opl 2.4.0 → 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/array.rb +59 -0
- data/lib/opl.rb +211 -168
- data/lib/string.rb +83 -0
- metadata +12 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a11eb865eed8b1f280c8dd764f76fa4916357d3
|
4
|
+
data.tar.gz: 78ec8f5196e4d90f2d6f237c21571262cab92dd0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5124341d93c8438663e4c2244c7abad2f51036ea6278b0f6e9f2ae092823cf29a821cc4d29541a3ee64ee668a4bad1068e424a2c39f950a0d0ff85f468ec7eee
|
7
|
+
data.tar.gz: fd1490b7e159ab31361fe0e748bafb0dfd8f03a2c578e732f79d0eaea7a25309d37cbb302b4ca11e2f8b84dd7a1671e34c75f2e59bc8adea02fdda441783597f
|
data/lib/array.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
class Array
|
2
|
+
def dimension
|
3
|
+
a = self
|
4
|
+
return 0 if a.class != Array
|
5
|
+
result = 1
|
6
|
+
a.each do |sub_a|
|
7
|
+
if sub_a.class == Array
|
8
|
+
dim = sub_a.dimension
|
9
|
+
result = dim + 1 if dim + 1 > result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
return result
|
13
|
+
end
|
14
|
+
|
15
|
+
def values_at_a(indices, current_array=self)
|
16
|
+
#in: self = [3,4,[6,5,[3,4]],3], indices = [2,2,0]
|
17
|
+
#out: 3
|
18
|
+
if indices.size == 1
|
19
|
+
return(current_array[indices[0]])
|
20
|
+
else
|
21
|
+
values_at_a(indices[1..-1], current_array[indices[0]])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def inject_dim(int)
|
26
|
+
arr = self
|
27
|
+
int.times do
|
28
|
+
arr << []
|
29
|
+
end
|
30
|
+
arr
|
31
|
+
end
|
32
|
+
|
33
|
+
def matrix(int_arr, current_arr=[])
|
34
|
+
int = int_arr[0]
|
35
|
+
new_int_arr = int_arr[1..-1]
|
36
|
+
if int_arr.empty?
|
37
|
+
return(current_arr)
|
38
|
+
else
|
39
|
+
if current_arr.empty?
|
40
|
+
new_arr = current_arr.inject_dim(int)
|
41
|
+
self.matrix(new_int_arr, new_arr)
|
42
|
+
else
|
43
|
+
current_arr.each do |arr|
|
44
|
+
arr.matrix(int_arr, arr)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert_at(position_arr, value)
|
51
|
+
arr = self
|
52
|
+
if position_arr.size == 1
|
53
|
+
arr[position_arr[0]] = value
|
54
|
+
return(arr)
|
55
|
+
else
|
56
|
+
arr[position_arr[0]].insert_at(position_arr[1..-1], value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/opl.rb
CHANGED
@@ -1,158 +1,77 @@
|
|
1
1
|
require "rglpk"
|
2
|
+
require_relative "string.rb"
|
3
|
+
require_relative "array.rb"
|
2
4
|
|
3
|
-
#
|
4
|
-
#
|
5
|
+
# Notes for future functionality
|
6
|
+
#
|
7
|
+
# Implement more advanced TSPs in order to
|
5
8
|
#make sure extreme cases of foralls and sums
|
6
9
|
#are handled
|
10
|
+
# Make an ERRORS : ON option
|
11
|
+
#
|
12
|
+
# need to handle multiple abs() in one constraint:
|
13
|
+
#
|
14
|
+
# 4 + abs(x - y) + abs(z) <= 4
|
15
|
+
#
|
16
|
+
# still need to implement abs() in objective
|
17
|
+
# I am not sure how to handle arithmetic inside
|
18
|
+
# an abs() in an objective function
|
19
|
+
#
|
20
|
+
# perhaps I should split into two LPs,
|
21
|
+
# one with +objective and one with -objective,
|
22
|
+
# solve both, and then take the more optimal solution
|
23
|
+
#
|
24
|
+
# maximize(abs(x))
|
25
|
+
# subject to:
|
26
|
+
# x < 3
|
27
|
+
# x > -7
|
28
|
+
#
|
29
|
+
# we set x = x1 - x2, x1,x2>=0
|
30
|
+
# so abs(x) = x1 + x2
|
31
|
+
# the lp should become
|
32
|
+
#
|
33
|
+
# maximize(x1 + x2)
|
34
|
+
# subject to:
|
35
|
+
# x1 - x2 < 3
|
36
|
+
# x1 - x2 > -7
|
37
|
+
# NONNEGATIVE: x1, x2
|
7
38
|
|
8
|
-
#3.
|
9
|
-
#
|
39
|
+
# 3.2
|
40
|
+
# or statements
|
41
|
+
# in order to do this I first have to do some refactoring:
|
42
|
+
# constraints need to be immediately turned in to objects
|
43
|
+
# step 1: turn constraints into objects and make current code work
|
44
|
+
# step 2: add a property to Constraint called or_index
|
45
|
+
# The or_index property represents the m[i] that belongs to that constraint
|
46
|
+
# step 3: after parsing is done, add the m[i] code
|
47
|
+
# Turning objects into constraints will be useful for any processing
|
48
|
+
# that I want to leave for later - i.e. if-->then, piecewise
|
10
49
|
|
11
|
-
#3.
|
12
|
-
#if --> then statements
|
50
|
+
# 3.3
|
51
|
+
# if --> then statements
|
13
52
|
|
14
|
-
#3.
|
15
|
-
#
|
53
|
+
# 3.4
|
54
|
+
# piecewise statements
|
16
55
|
|
17
|
-
#3.
|
18
|
-
#
|
19
|
-
|
20
|
-
#3.5
|
21
|
-
#duals, sensitivity, etc. - I could simply allow
|
56
|
+
# 3.5
|
57
|
+
# duals, sensitivity, etc. - I could simply allow
|
22
58
|
#access to the rglpk object wrapper
|
23
59
|
|
24
|
-
#4.0
|
25
|
-
#import excel sheets as data
|
26
|
-
|
27
|
-
$default_epsilon = 0.01
|
28
|
-
|
29
|
-
class String
|
30
|
-
def paren_to_array
|
31
|
-
#in: "(2..5)"
|
32
|
-
#out: "[2,3,4,5]"
|
33
|
-
text = self
|
34
|
-
start = text[1].to_i
|
35
|
-
stop = text[-2].to_i
|
36
|
-
(start..stop).map{|i|i}.to_s
|
37
|
-
end
|
38
|
-
|
39
|
-
def sub_paren_with_array
|
40
|
-
text = self
|
41
|
-
targets = text.scan(/\([\d]+\.\.[\d]+\)/)
|
42
|
-
targets.each do |target|
|
43
|
-
text = text.gsub(target, target.paren_to_array)
|
44
|
-
end
|
45
|
-
return(text)
|
46
|
-
end
|
47
|
-
|
48
|
-
def to_array(current_array=[self])
|
49
|
-
#in: "[1,2,[3,4],[4,2,[3,2,[4,2]]],2,[4,2]]"
|
50
|
-
#out: [1,2,[3,4],[4,2,[3,2,[4,2]]],2,[4,2]]
|
51
|
-
def current_level_information(b)
|
52
|
-
b = b.gsub(" ","")
|
53
|
-
stripped_array = b[1..-2]
|
54
|
-
in_array = 0
|
55
|
-
inside_arrays_string = ""
|
56
|
-
inside_values_string = ""
|
57
|
-
stripped_array.split("").each do |char|
|
58
|
-
if char == "["
|
59
|
-
in_array += 1
|
60
|
-
elsif char == "]"
|
61
|
-
in_array += -1
|
62
|
-
end
|
63
|
-
if (in_array > 0) || (char == "]")
|
64
|
-
inside_arrays_string += char
|
65
|
-
end
|
66
|
-
end
|
67
|
-
stripped_array_without_arrays = stripped_array
|
68
|
-
inside_arrays_string.gsub("][","],,,[").split(",,,").each do |str|
|
69
|
-
stripped_array_without_arrays = stripped_array_without_arrays.gsub(str,"")
|
70
|
-
end
|
71
|
-
inside_values_string = stripped_array_without_arrays.split(",").find_all{|e|e!=""}.join(",")
|
72
|
-
return {:values => inside_values_string, :arrays => inside_arrays_string}
|
73
|
-
end
|
74
|
-
if !current_array.join(",").include?("[")
|
75
|
-
return(current_array)
|
76
|
-
else
|
77
|
-
a = []
|
78
|
-
element = current_array.find_all{|e|e.include?("[")}.first
|
79
|
-
i = current_array.index(element)
|
80
|
-
info = current_level_information(element)
|
81
|
-
info[:values].split(",").each do |v|
|
82
|
-
a << v
|
83
|
-
end
|
84
|
-
info[:arrays].gsub("][","],,,[").split(",,,").each do |v|
|
85
|
-
a << v.to_array
|
86
|
-
end
|
87
|
-
current_array[i] = a
|
88
|
-
return(current_array[0])
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def to_a
|
93
|
-
self.to_array
|
94
|
-
end
|
95
|
-
end
|
60
|
+
# 4.0
|
61
|
+
# import excel sheets as data
|
96
62
|
|
97
|
-
|
98
|
-
|
99
|
-
a = self
|
100
|
-
return 0 if a.class != Array
|
101
|
-
result = 1
|
102
|
-
a.each do |sub_a|
|
103
|
-
if sub_a.class == Array
|
104
|
-
dim = sub_a.dimension
|
105
|
-
result = dim + 1 if dim + 1 > result
|
106
|
-
end
|
107
|
-
end
|
108
|
-
return result
|
109
|
-
end
|
110
|
-
|
111
|
-
def values_at_a(indices, current_array=self)
|
112
|
-
#in: self = [3,4,[6,5,[3,4]],3], indices = [2,2,0]
|
113
|
-
#out: 3
|
114
|
-
if indices.size == 1
|
115
|
-
return(current_array[indices[0]])
|
116
|
-
else
|
117
|
-
values_at_a(indices[1..-1], current_array[indices[0]])
|
118
|
-
end
|
119
|
-
end
|
63
|
+
# 4.1
|
64
|
+
# add a SUBTOURS: option to eliminate subtours in a TSP
|
120
65
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
66
|
+
# 4.2
|
67
|
+
# have an option where you can pass in a function.
|
68
|
+
#the function takes the resulting lp as input.
|
69
|
+
#you can add constraints based on the lp and re-run it.
|
70
|
+
#this will be useful for adding sub-tours to a problem
|
71
|
+
#without having to look at the output manually every time
|
128
72
|
|
129
|
-
|
130
|
-
|
131
|
-
new_int_arr = int_arr[1..-1]
|
132
|
-
if int_arr.empty?
|
133
|
-
return(current_arr)
|
134
|
-
else
|
135
|
-
if current_arr.empty?
|
136
|
-
new_arr = current_arr.inject_dim(int)
|
137
|
-
self.matrix(new_int_arr, new_arr)
|
138
|
-
else
|
139
|
-
current_arr.each do |arr|
|
140
|
-
arr.matrix(int_arr, arr)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def insert_at(position_arr, value)
|
147
|
-
arr = self
|
148
|
-
if position_arr.size == 1
|
149
|
-
arr[position_arr[0]] = value
|
150
|
-
return(arr)
|
151
|
-
else
|
152
|
-
arr[position_arr[0]].insert_at(position_arr[1..-1], value)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
73
|
+
$default_epsilon = 0.01
|
74
|
+
$default_m = 1000000000000.0
|
156
75
|
|
157
76
|
class OPL
|
158
77
|
class Helper
|
@@ -274,6 +193,10 @@ class OPL
|
|
274
193
|
#in: "i in [0,1], j in [4,-5], 3x[i][j]"
|
275
194
|
#out: "3x[0][4] + 3x[0][-5] + 3x[1][4] + 3x[1][-5]"
|
276
195
|
text = text.sub_paren_with_array
|
196
|
+
if text.scan(/\(\d+\+\d+\)/).size > 0
|
197
|
+
text.scan(/\(\d+\+\d+\)/).each {|e| text = text.gsub(e,eval(e.gsub("(","").gsub(")","")).to_s) }
|
198
|
+
end
|
199
|
+
text = text.sub_paren_with_array
|
277
200
|
if (text.gsub(" ","")).scan(/\]\,/).size != text.scan(/in/).size
|
278
201
|
raise "The following sum() constraint is incorrectly formatted: #{text}. Please see the examples in test.rb for sum() constraints. I suspect you are missing a comma somewhere."
|
279
202
|
elsif (text.gsub(" ","").include?("=") || text.gsub(" ","").include?("<") || text.gsub(" ","").include?(">"))
|
@@ -335,32 +258,34 @@ class OPL
|
|
335
258
|
def self.sub_sum(equation, lp, indexvalues={:indices => [], :values => []})
|
336
259
|
#in: "sum(i in (0..3), x[i]) <= 100"
|
337
260
|
#out: "x[0]+x[1]+x[2]+x[3] <= 100"
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
indexvalues[:indices].
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
261
|
+
if equation.include?("sum(")
|
262
|
+
sums = (equation+"#").split("sum(").map{|ee|ee.split(")")[0..-2].join(")")}.find_all{|eee|eee!=""}.find_all{|eeee|!eeee.include?("forall")}
|
263
|
+
sums.each do |text|
|
264
|
+
e = text
|
265
|
+
unless indexvalues[:indices].empty?
|
266
|
+
indexvalues[:indices].each_index do |i|
|
267
|
+
index = indexvalues[:indices][i]
|
268
|
+
value = indexvalues[:values][i].to_s
|
269
|
+
e = e.gsub("("+index, "("+value)
|
270
|
+
e = e.gsub(index+")", value+")")
|
271
|
+
e = e.gsub("["+index, "["+value)
|
272
|
+
e = e.gsub(index+"]", value+"]")
|
273
|
+
e = e.gsub("=>"+index, "=>"+value)
|
274
|
+
e = e.gsub("<="+index, "<="+value)
|
275
|
+
e = e.gsub(">"+index, ">"+value)
|
276
|
+
e = e.gsub("<"+index, "<"+value)
|
277
|
+
e = e.gsub("="+index, "="+value)
|
278
|
+
e = e.gsub("=> "+index, "=> "+value)
|
279
|
+
e = e.gsub("<= "+index, "<= "+value)
|
280
|
+
e = e.gsub("> "+index, "> "+value)
|
281
|
+
e = e.gsub("< "+index, "< "+value)
|
282
|
+
e = e.gsub("= "+index, "= "+value)
|
283
|
+
end
|
359
284
|
end
|
285
|
+
equation = equation.gsub(text, e)
|
286
|
+
result = self.sum(text, lp)
|
287
|
+
equation = equation.gsub("sum("+text+")", result)
|
360
288
|
end
|
361
|
-
equation = equation.gsub(text, e)
|
362
|
-
result = self.sum(text, lp)
|
363
|
-
equation = equation.gsub("sum("+text+")", result)
|
364
289
|
end
|
365
290
|
return(equation)
|
366
291
|
end
|
@@ -379,8 +304,12 @@ class OPL
|
|
379
304
|
end
|
380
305
|
|
381
306
|
def self.variables(text, lp)#parameter is one side of the equation
|
382
|
-
|
383
|
-
|
307
|
+
text = self.add_ones(text, lp)
|
308
|
+
text = text.gsub("abs","").gsub("(","").gsub(")","")
|
309
|
+
variables = text.scan(/[a-z][\[\]\d]*/)
|
310
|
+
raise("The variable letter a is reserved for special processes. Please rename your variable to something other than a.") if variables.join.include?("a")
|
311
|
+
raise("The variable letter m is reserved for special processes. Please rename your variable to something other than m.") if variables.join.include?("m")
|
312
|
+
return variables
|
384
313
|
end
|
385
314
|
|
386
315
|
def self.get_all_vars(constraints, lp)
|
@@ -728,6 +657,88 @@ class OPL
|
|
728
657
|
end
|
729
658
|
end
|
730
659
|
end
|
660
|
+
|
661
|
+
def self.negate(text, explicit=false)
|
662
|
+
# text is one side of an equation
|
663
|
+
# there will be no foralls, no sums, and no abs
|
664
|
+
# in: "z - 3"
|
665
|
+
# out: "-z + 3"
|
666
|
+
working_text = text = text.gsub(" ","")
|
667
|
+
#!("a".."z").to_a.include?(text[0]) &&
|
668
|
+
if !["-","+"].include?(text[0])
|
669
|
+
working_text = "+"+working_text
|
670
|
+
end
|
671
|
+
indices_of_negatives = working_text.index_array("-")
|
672
|
+
indices_of_positives = working_text.index_array("+")
|
673
|
+
indices_of_negatives.each {|i| working_text[i] = "+"}
|
674
|
+
indices_of_positives.each {|i| working_text[i] = "-"}
|
675
|
+
if !explicit && working_text[0] == "+"
|
676
|
+
working_text = working_text[1..-1]
|
677
|
+
end
|
678
|
+
return(working_text)
|
679
|
+
end
|
680
|
+
|
681
|
+
def self.replace_absolute_value(text)
|
682
|
+
# text is a constraint
|
683
|
+
# there will be no foralls and no sums
|
684
|
+
# in: "abs(x) <= 1"
|
685
|
+
# out: "x <= 1", "-x <= 1"
|
686
|
+
# in: "4x + 3y - 6abs(z - 3) <= 4"
|
687
|
+
# out: "4x + 3y - 6z + 18 <= 4", "4x + 3y + 6z - 18 <= 4"
|
688
|
+
if text.include?("abs")
|
689
|
+
helper = self
|
690
|
+
constraints_to_delete = [text]
|
691
|
+
text = text.gsub(" ","")
|
692
|
+
constraints_to_delete << text
|
693
|
+
working_text = "#"+text
|
694
|
+
absolute_value_elements = working_text.scan(/[\-\+\#]\d*abs\([a-z\-\+\*\d\[\]]+\)/)
|
695
|
+
constraints_to_add = []
|
696
|
+
absolute_value_elements.each do |ave|
|
697
|
+
ave = ave.gsub("#","")
|
698
|
+
inside_value = ave.split("(")[1].split(")")[0]
|
699
|
+
positive_ave = inside_value
|
700
|
+
negative_ave = helper.negate(inside_value)
|
701
|
+
if ave[0] == "-" || ave[1] == "-"
|
702
|
+
positive_ave = helper.negate(positive_ave, true)
|
703
|
+
negative_ave = helper.negate(negative_ave, true)
|
704
|
+
elsif ave[0] == "+"
|
705
|
+
positive_ave = "+"+positive_ave
|
706
|
+
end
|
707
|
+
positive_constraint = text.gsub(ave, positive_ave)
|
708
|
+
negative_constraint = text.gsub(ave, negative_ave)
|
709
|
+
constraints_to_add << positive_constraint
|
710
|
+
constraints_to_add << negative_constraint
|
711
|
+
end
|
712
|
+
return {:constraints_to_delete => constraints_to_delete, :constraints_to_add => constraints_to_add}
|
713
|
+
else
|
714
|
+
return {:constraints_to_delete => [], :constraints_to_add => []}
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
def self.strip_abs(text)
|
719
|
+
# in: "3 + abs(x[1] - y) + abs(x[3]) <= 3*abs(-x[2])"
|
720
|
+
# out: {:positive => "3 + x[1] - y + x[3] <= -3*x[2]",
|
721
|
+
# :negative => "3 - x[1] + y - x[3] <= 3*x[2]"}
|
722
|
+
text = text.gsub(" ","")
|
723
|
+
working_text = "#"+text
|
724
|
+
|
725
|
+
end
|
726
|
+
|
727
|
+
def self.either_or(lp, constraint1, constraint2, i)
|
728
|
+
# in: lp, "10.0*x1+4.0*x2+5.0*x3<=600", "2.0*x1+2.0*x2+6.0*x3<=300"
|
729
|
+
# out: "10.0*x1+4.0*x2+5.0*x3<=600+#{$default_m}", "2.0*x1+2.0*x2+6.0*x3<=300+#{$default_m}"
|
730
|
+
index1 = lp.constraints.index(constraint1)
|
731
|
+
lp.constraints[index1] = lp.constraints[index1]+"#{$default_m}*m[#{i}]"
|
732
|
+
# add "+M[n]y[n]" to the rhs of the constraint
|
733
|
+
end
|
734
|
+
|
735
|
+
# make a default OPTION that m is BOOLEAN
|
736
|
+
def self.split_ors(lp, constraints)
|
737
|
+
# in: ["x <= 10", "y <= 24 or y + x <= 14"]
|
738
|
+
# out: ["x <= 10", "y - 1000000000000.0*m[0] <= 24", "y + x - 1000000000000.0 + 1000000000000.0*m[0] <= 14"]
|
739
|
+
|
740
|
+
# add m[i+1] to left side of constraints
|
741
|
+
end
|
731
742
|
end
|
732
743
|
|
733
744
|
class LinearProgram
|
@@ -750,6 +761,8 @@ class OPL
|
|
750
761
|
attr_accessor :error_message
|
751
762
|
attr_accessor :stop_processing
|
752
763
|
attr_accessor :solution_type
|
764
|
+
attr_accessor :negated_objective_lp
|
765
|
+
attr_accessor :m_index
|
753
766
|
|
754
767
|
def keys
|
755
768
|
[:objective, :constraints, :rows, :solution, :formatted_constraints, :rglpk_object, :solver, :matrix, :simplex_message, :mip_message, :data]
|
@@ -792,6 +805,20 @@ class OPL
|
|
792
805
|
end
|
793
806
|
return(matrix_solution)
|
794
807
|
end
|
808
|
+
|
809
|
+
def recreate_with_objective_abs(objective)
|
810
|
+
#in: "abs(x)"
|
811
|
+
#out: {:objective => "x1 + x2", :constraints => "x1 * x2 = 0"}
|
812
|
+
#this is a really tough problem - first time I am considering
|
813
|
+
#abandoning the string parsing approach. Really need to think
|
814
|
+
#about how to attack this
|
815
|
+
lp_class = self
|
816
|
+
helper = OPL::Helper
|
817
|
+
variabes = helper.variables(objective, lp_class.new)
|
818
|
+
new_objective = ""
|
819
|
+
constraints_to_add = []
|
820
|
+
#return(self)
|
821
|
+
end
|
795
822
|
end
|
796
823
|
|
797
824
|
class Objective
|
@@ -875,6 +902,7 @@ def subject_to(constraints, options=[])
|
|
875
902
|
end
|
876
903
|
lp.epsilon = epsilon
|
877
904
|
constraints = constraints.flatten
|
905
|
+
#constraints = constraints.split_ors(constraints)
|
878
906
|
constraints = OPL::Helper.split_equals_a(constraints)
|
879
907
|
data_names = lp.data.map{|d|d.name}
|
880
908
|
constraints = constraints.map do |constraint|
|
@@ -889,6 +917,16 @@ def subject_to(constraints, options=[])
|
|
889
917
|
constraints = constraints.map do |constraint|
|
890
918
|
OPL::Helper.sum_indices(constraint)
|
891
919
|
end
|
920
|
+
new_constraints = []
|
921
|
+
constraints.each do |constraint|
|
922
|
+
replace_absolute_value_results = OPL::Helper.replace_absolute_value(constraint)
|
923
|
+
if replace_absolute_value_results[:constraints_to_add].empty?
|
924
|
+
new_constraints << constraint
|
925
|
+
else
|
926
|
+
new_constraints += replace_absolute_value_results[:constraints_to_add]
|
927
|
+
end
|
928
|
+
end
|
929
|
+
constraints = new_constraints
|
892
930
|
constraints = constraints.map do |constraint|
|
893
931
|
OPL::Helper.put_constants_on_rhs(constraint)
|
894
932
|
end
|
@@ -983,6 +1021,11 @@ end
|
|
983
1021
|
|
984
1022
|
def optimize(optimization, objective, lp)
|
985
1023
|
original_objective = objective
|
1024
|
+
while original_objective.include?("abs")
|
1025
|
+
#need to add some constraints, change the objective,
|
1026
|
+
#and reprocess the constraints
|
1027
|
+
#lp = lp.recreate_with_objective_abs(original_objective)
|
1028
|
+
end
|
986
1029
|
objective = OPL::Helper.sub_sum(objective, lp)
|
987
1030
|
objective = OPL::Helper.sum_indices(objective)
|
988
1031
|
objective = OPL::Helper.substitute_data(objective, lp)
|
data/lib/string.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
class String
|
2
|
+
def paren_to_array
|
3
|
+
#in: "(2..5)"
|
4
|
+
#out: "[2,3,4,5]"
|
5
|
+
start = self[1].to_i
|
6
|
+
stop = self[-2].to_i
|
7
|
+
(start..stop).map{|i|i}.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def sub_paren_with_array
|
11
|
+
text = self
|
12
|
+
targets = text.scan(/\([\d]+\.\.[\d]+\)/)
|
13
|
+
targets.each do |target|
|
14
|
+
text = text.gsub(target, target.paren_to_array)
|
15
|
+
end
|
16
|
+
return(text)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_array(current_array=[self])
|
20
|
+
#in: "[1,2,[3,4],[4,2,[3,2,[4,2]]],2,[4,2]]"
|
21
|
+
#out: [1,2,[3,4],[4,2,[3,2,[4,2]]],2,[4,2]]
|
22
|
+
def current_level_information(b)
|
23
|
+
b = b.gsub(" ","")
|
24
|
+
stripped_array = b[1..-2]
|
25
|
+
in_array = 0
|
26
|
+
inside_arrays_string = ""
|
27
|
+
inside_values_string = ""
|
28
|
+
stripped_array.split("").each do |char|
|
29
|
+
if char == "["
|
30
|
+
in_array += 1
|
31
|
+
elsif char == "]"
|
32
|
+
in_array += -1
|
33
|
+
end
|
34
|
+
if (in_array > 0) || (char == "]")
|
35
|
+
inside_arrays_string += char
|
36
|
+
end
|
37
|
+
end
|
38
|
+
stripped_array_without_arrays = stripped_array
|
39
|
+
inside_arrays_string.gsub("][","],,,[").split(",,,").each do |str|
|
40
|
+
stripped_array_without_arrays = stripped_array_without_arrays.gsub(str,"")
|
41
|
+
end
|
42
|
+
inside_values_string = stripped_array_without_arrays.split(",").find_all{|e|e!=""}.join(",")
|
43
|
+
return {:values => inside_values_string, :arrays => inside_arrays_string}
|
44
|
+
end
|
45
|
+
if !current_array.join(",").include?("[")
|
46
|
+
return(current_array)
|
47
|
+
else
|
48
|
+
a = []
|
49
|
+
element = current_array.find_all{|e|e.include?("[")}.first
|
50
|
+
i = current_array.index(element)
|
51
|
+
info = current_level_information(element)
|
52
|
+
info[:values].split(",").each do |v|
|
53
|
+
a << v
|
54
|
+
end
|
55
|
+
info[:arrays].gsub("][","],,,[").split(",,,").each do |v|
|
56
|
+
a << v.to_array
|
57
|
+
end
|
58
|
+
current_array[i] = a
|
59
|
+
return(current_array[0])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_a
|
64
|
+
self.to_array
|
65
|
+
end
|
66
|
+
|
67
|
+
def index_array(str)
|
68
|
+
indices = []
|
69
|
+
string = self
|
70
|
+
ignore_indices = []
|
71
|
+
search_length = str.size
|
72
|
+
[*(0..string.size-1)].each do |i|
|
73
|
+
if !ignore_indices.include?(i)
|
74
|
+
compare_str = string[i..(i+search_length-1)]
|
75
|
+
if compare_str == str
|
76
|
+
indices << i
|
77
|
+
ignore_indices = ignore_indices + [i..(i+search_length-1)]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
return(indices)
|
82
|
+
end
|
83
|
+
end
|
metadata
CHANGED
@@ -1,49 +1,48 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
5
|
-
prerelease:
|
4
|
+
version: 2.4.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Benjamin Godlove
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-02-15 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
|
-
description:
|
15
|
-
|
16
|
-
|
17
|
-
software, but the license is quite expensive.
|
13
|
+
description: This gem gives you a beautifully simple way to formulate your linear
|
14
|
+
or mixed integer program. The syntax is inspired by OPL Studio, which remains my
|
15
|
+
favorite linear programming software, but the license is quite expensive.
|
18
16
|
email: bgodlove88@gmail.com
|
19
17
|
executables: []
|
20
18
|
extensions: []
|
21
19
|
extra_rdoc_files: []
|
22
20
|
files:
|
21
|
+
- lib/array.rb
|
23
22
|
- lib/opl.rb
|
23
|
+
- lib/string.rb
|
24
24
|
homepage: http://github.com/brg8/opl
|
25
25
|
licenses:
|
26
26
|
- GNU
|
27
|
+
metadata: {}
|
27
28
|
post_install_message:
|
28
29
|
rdoc_options: []
|
29
30
|
require_paths:
|
30
31
|
- lib
|
31
32
|
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
-
none: false
|
33
33
|
requirements:
|
34
|
-
- -
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '0'
|
37
37
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
-
none: false
|
39
38
|
requirements:
|
40
|
-
- -
|
39
|
+
- - ">="
|
41
40
|
- !ruby/object:Gem::Version
|
42
41
|
version: '0'
|
43
42
|
requirements: []
|
44
43
|
rubyforge_project:
|
45
|
-
rubygems_version:
|
44
|
+
rubygems_version: 2.2.2
|
46
45
|
signing_key:
|
47
|
-
specification_version:
|
46
|
+
specification_version: 4
|
48
47
|
summary: Linear Or Mixed Integer Program Solver
|
49
48
|
test_files: []
|