opl 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|