ruby-cbc 0.3.16 → 0.3.19
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 +5 -5
- data/.rubocop.yml +3 -1
- data/.rubocop_todo.yml +51 -0
- data/.ruby-style.yml +6 -1346
- data/.travis.yml +3 -1
- data/Gemfile +1 -1
- data/README.md +53 -10
- data/Rakefile +6 -6
- data/lib/ruby-cbc/conflict_solver.rb +9 -8
- data/lib/ruby-cbc/ilp/constant.rb +49 -0
- data/lib/ruby-cbc/ilp/constraint.rb +2 -3
- data/lib/ruby-cbc/ilp/objective.rb +1 -1
- data/lib/ruby-cbc/ilp/term.rb +2 -10
- data/lib/ruby-cbc/ilp/term_array.rb +8 -5
- data/lib/ruby-cbc/ilp/var.rb +1 -1
- data/lib/ruby-cbc/model.rb +9 -9
- data/lib/ruby-cbc/problem.rb +26 -26
- data/lib/ruby-cbc/version.rb +1 -1
- data/lib/ruby-cbc.rb +3 -2
- data/ruby-cbc.gemspec +10 -9
- metadata +44 -15
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[](https://travis-ci.org/gverger/ruby-cbc)
|
2
|
+
|
2
3
|
# Ruby-Cbc
|
3
4
|
|
4
5
|
This gem is using Cbc, an Integer Linear Programming Library, to provide optimization problems solving
|
@@ -8,6 +9,13 @@ It uses the version 2.9.9 of Cbc, and requires the version 2.9.9 of gem cbc-wrap
|
|
8
9
|
|
9
10
|
## Installation
|
10
11
|
|
12
|
+
You need to have `cbc` installed for `ruby-cbc` to work.
|
13
|
+
|
14
|
+
- On a mac, you can execute `brew install cbc`
|
15
|
+
- On Debian and Ubuntu, use `apt-get install coinor-libcbc-dev`
|
16
|
+
- On Archlinux, use `pacman -S coin-or-cbc`
|
17
|
+
- On docker you can check the dockerfiles in test/installation-tests/
|
18
|
+
|
11
19
|
Add this line to your application's Gemfile:
|
12
20
|
|
13
21
|
```ruby
|
@@ -22,16 +30,25 @@ Or install it yourself as:
|
|
22
30
|
|
23
31
|
$ gem install ruby-cbc
|
24
32
|
|
25
|
-
|
26
|
-
|
27
|
-
|
33
|
+
### Heroku
|
34
|
+
|
35
|
+
On Heroku, you will need to tweak your installation a bit: you can install the cbc library with
|
36
|
+
the [Apt](https://github.com/heroku/heroku-buildpack-apt) buildpack with an Aptfile of:
|
37
|
+
|
38
|
+
```
|
39
|
+
coinor-libcbc-dev
|
40
|
+
```
|
41
|
+
|
42
|
+
You will need to set LD_LIBRARY_PATH so it can find LAPACK and BLAS (see this [issue](https://github.com/heroku/heroku-buildpack-apt/issues/35)).
|
28
43
|
|
29
|
-
|
44
|
+
```
|
45
|
+
heroku config:set LD_LIBRARY_PATH=/app/.apt/usr/lib/x86_64-linux-gnu/lapack:/app/.apt/usr/lib/x86_64-linux-gnu/blas
|
46
|
+
```
|
30
47
|
|
31
48
|
## Usage
|
32
49
|
|
33
50
|
```ruby
|
34
|
-
# The same Brief Example as found in section 1.3 of
|
51
|
+
# The same Brief Example as found in section 1.3 of
|
35
52
|
# glpk-4.44/doc/glpk.pdf.
|
36
53
|
#
|
37
54
|
# maximize
|
@@ -52,39 +69,45 @@ m.maximize(10 * x1 + 6 * x2 + 4 * x3)
|
|
52
69
|
|
53
70
|
m.enforce(x1 + x2 + x3 <= 100)
|
54
71
|
m.enforce(10 * x1 + 4 * x2 + 5 * x3 <= 600)
|
55
|
-
m.enforce(2 * x1 + 2 * x2 + 6* x3 <= 300)
|
72
|
+
m.enforce(2 * x1 + 2 * x2 + 6 * x3 <= 300)
|
56
73
|
|
57
74
|
p = m.to_problem
|
58
75
|
|
59
76
|
p.solve
|
60
77
|
|
61
|
-
|
78
|
+
unless p.proven_infeasible?
|
62
79
|
puts "x1 = #{p.value_of(x1)}"
|
63
80
|
puts "x2 = #{p.value_of(x2)}"
|
64
81
|
puts "x3 = #{p.value_of(x3)}"
|
65
82
|
end
|
66
83
|
```
|
84
|
+
|
67
85
|
### Modelling
|
68
86
|
|
69
87
|
Let's have a model :
|
88
|
+
|
70
89
|
```ruby
|
71
90
|
model = Cbc::Model.new
|
72
91
|
```
|
92
|
+
|
73
93
|
#### The variables
|
74
94
|
|
75
95
|
3 variable kinds are available:
|
96
|
+
|
76
97
|
```ruby
|
77
98
|
x = model.bin_var(name: "x") # a binary variable (i.e. can take values 0 and 1)
|
78
99
|
y = model.int_var(L..U, name: "y") # a integer variable, L <= y <= U
|
79
100
|
z = model.cont_var(L..U, name: "z") # a continuous variable, L <= z <= U
|
80
101
|
```
|
102
|
+
|
81
103
|
Name is optional and used only when displaying the model.
|
82
104
|
|
83
105
|
If you don't specify the range, the variables are free.
|
84
|
-
You can also use
|
106
|
+
You can also use `Cbc::INF` as the infinity bound.
|
85
107
|
|
86
108
|
Each one of these 3 kinds have also an array method that generate several variables.
|
87
109
|
For instance to generate 3 positive integer variables named x, y and z :
|
110
|
+
|
88
111
|
```ruby
|
89
112
|
x, y, z = model.int_var_array(3, 0..Cbc::INF, names: ["x", "y", "z"])
|
90
113
|
```
|
@@ -92,12 +115,15 @@ x, y, z = model.int_var_array(3, 0..Cbc::INF, names: ["x", "y", "z"])
|
|
92
115
|
#### The constraints
|
93
116
|
|
94
117
|
You can enforce constraints:
|
118
|
+
|
95
119
|
```ruby
|
96
120
|
model.enforce(x + y - z <= 10)
|
97
121
|
```
|
122
|
+
|
98
123
|
You are not restricted to usual linear programming rules when writing a constraint.
|
99
|
-
Usually you would have to write
|
124
|
+
Usually you would have to write `x - y = 0` to express `x = y`.
|
100
125
|
Ruby-Cbc allows you to put variables and constants on both sides of the comparison operator. You can write
|
126
|
+
|
101
127
|
```ruby
|
102
128
|
model.enforce(x - y == 0)
|
103
129
|
model.enforce(x == y)
|
@@ -107,12 +133,14 @@ model.enforce(0 == x - y)
|
|
107
133
|
|
108
134
|
Ruby-Cbc allows you to name your constraints. Beware that their name is not an unique id. It is only a helper
|
109
135
|
for human readability, and several constraints can share the same function name.
|
136
|
+
|
110
137
|
```ruby
|
111
138
|
model.enforce(my_function_name: x + y <= 50)
|
112
139
|
model.constraints.map(&:to_function_s) # => ["my_function_name(x, y)"]
|
113
140
|
```
|
114
141
|
|
115
142
|
Linear constraints are usually of the form
|
143
|
+
|
116
144
|
```ruby
|
117
145
|
a1 * x1 + a2 * x2 + ... + an * xn <= C
|
118
146
|
a1 * x1 + a2 * x2 + ... + an * xn >= C
|
@@ -120,14 +148,17 @@ a1 * x1 + a2 * x2 + ... + an * xn == C
|
|
120
148
|
```
|
121
149
|
|
122
150
|
With Ruby-Cbc you can write
|
151
|
+
|
123
152
|
```ruby
|
124
153
|
2 * (2 + 5 * x) + 4 * 5 + 1 == 1 + 4 * 5 * y
|
125
154
|
```
|
155
|
+
|
126
156
|
The (in)equation must still be a **linear** (in)equation, you cannot multiply two variables !
|
127
157
|
|
128
158
|
#### Objective
|
129
159
|
|
130
160
|
You can set the objective:
|
161
|
+
|
131
162
|
```ruby
|
132
163
|
model.maximize(3 * x + 2 * y)
|
133
164
|
model.minimize(3 * x + 2 * y)
|
@@ -136,12 +167,15 @@ model.minimize(3 * x + 2 * y)
|
|
136
167
|
#### Displaying the model
|
137
168
|
|
138
169
|
the `Model` instances have a `to_s` method. You can then run
|
170
|
+
|
139
171
|
```ruby
|
140
172
|
puts model
|
141
173
|
```
|
174
|
+
|
142
175
|
The model will be printed in LP format.
|
143
176
|
|
144
177
|
For instance:
|
178
|
+
|
145
179
|
```
|
146
180
|
Maximize
|
147
181
|
+ 10 x1 + 6 x2 + 4 x3
|
@@ -167,25 +201,30 @@ End
|
|
167
201
|
### Solving
|
168
202
|
|
169
203
|
To solve the model, you need to first transform it to a problem.
|
204
|
+
|
170
205
|
```ruby
|
171
206
|
problem = model.to_problem
|
172
207
|
```
|
173
208
|
|
174
209
|
You can define a time limit to the resolution
|
210
|
+
|
175
211
|
```ruby
|
176
212
|
problem.set_time_limit(nb_seconds)
|
177
213
|
```
|
178
214
|
|
179
215
|
You can solve the Linear Problem
|
216
|
+
|
180
217
|
```ruby
|
181
218
|
problem.solve
|
182
219
|
```
|
183
220
|
|
184
221
|
You can specify arguments that match the cbc command line
|
222
|
+
|
185
223
|
```ruby
|
186
224
|
problem.solve(sec: 60) # equivalent to $ cbc -sec 60
|
187
225
|
problem.solve(log: 1) # equivalent to $ cbc -log 1
|
188
226
|
```
|
227
|
+
|
189
228
|
For more examples of available options, if `coinor-cbc` is installed run
|
190
229
|
|
191
230
|
$ cbc
|
@@ -193,6 +232,7 @@ For more examples of available options, if `coinor-cbc` is installed run
|
|
193
232
|
then type `?`
|
194
233
|
|
195
234
|
Once `problem.solve` has finished you can query the status:
|
235
|
+
|
196
236
|
```ruby
|
197
237
|
problem.proven_infeasible? # will tell you if the problem has no solution
|
198
238
|
problem.proven_optimal? # will tell you if the problem is solved optimally
|
@@ -200,6 +240,7 @@ problem.time_limit_reached? # Will tell you if the solve timed out
|
|
200
240
|
```
|
201
241
|
|
202
242
|
To have the different values, do
|
243
|
+
|
203
244
|
```ruby
|
204
245
|
problem.objective_value # Will tell you the value of the best objective
|
205
246
|
problem.best_bound # Will tell you the best known bound
|
@@ -211,16 +252,19 @@ problem.value_of(var) # will tell you the computed value or a variable
|
|
211
252
|
|
212
253
|
Sometimes a problem has no feasible solution. In this case, one may wonder what is the minimum subset of conflicting
|
213
254
|
inequations. For this prupose, you can use
|
255
|
+
|
214
256
|
```ruby
|
215
257
|
problem.find_conflict # Will return an array of constraints that form an unsatifiable set
|
216
258
|
problem.find_conflict_vars # Will return all variables involved in the unsatisfiable minimum set of constraints
|
217
259
|
```
|
260
|
+
|
218
261
|
It finds a minimum subset of constraints that make the problem unsatisfiable. Note that there could be several of them,
|
219
262
|
but the solver only computes the first one it finds. Note also that it does so by solving several instances
|
220
263
|
of relaxed versions of the problem. It might take some time! It is based on QuickXplain
|
221
264
|
(http://dl.acm.org/citation.cfm?id=1597177).
|
222
265
|
|
223
266
|
One way to see the results nicely could be
|
267
|
+
|
224
268
|
```ruby
|
225
269
|
problem.find_conflict.map(&:to_function_s)
|
226
270
|
```
|
@@ -228,4 +272,3 @@ problem.find_conflict.map(&:to_function_s)
|
|
228
272
|
## Contributing
|
229
273
|
|
230
274
|
Bug reports and pull requests are welcome on GitHub at https://github.com/gverger/ruby-cbc.
|
231
|
-
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
|
-
require
|
3
|
+
require "rake/extensiontask"
|
4
4
|
|
5
5
|
def in_dir(path)
|
6
6
|
original_dir = Dir.pwd
|
@@ -12,14 +12,14 @@ end
|
|
12
12
|
|
13
13
|
RSpec::Core::RakeTask.new(:spec)
|
14
14
|
|
15
|
-
spec = Gem::Specification.load(
|
16
|
-
Rake::ExtensionTask.new(
|
17
|
-
ext.lib_dir =
|
15
|
+
spec = Gem::Specification.load("ruby-cbc.gemspec")
|
16
|
+
Rake::ExtensionTask.new("ruby-cbc", spec) do |ext|
|
17
|
+
ext.lib_dir = "lib/ruby-cbc"
|
18
18
|
ext.tmp_dir = "tmp"
|
19
|
-
ext.name =
|
19
|
+
ext.name = "cbc_wrapper"
|
20
20
|
end
|
21
21
|
|
22
|
-
task :
|
22
|
+
task default: [:spec]
|
23
23
|
|
24
24
|
task :benchmark do
|
25
25
|
ruby "test/benchmark.rb"
|
@@ -19,7 +19,8 @@ module Cbc
|
|
19
19
|
max_iter = 1
|
20
20
|
conflict_set_size = 0
|
21
21
|
loop do
|
22
|
-
range_idxs =
|
22
|
+
range_idxs =
|
23
|
+
first_failing(conflict_set_size, crs, continuous: continuous, max_iterations: max_iter)
|
23
24
|
break if range_idxs.nil?
|
24
25
|
crs = crs.restrict_to_n_constraints(range_idxs.max + 1)
|
25
26
|
crs.move_constraint_to_start(range_idxs)
|
@@ -43,7 +44,9 @@ module Cbc
|
|
43
44
|
nb_clusters_one_constraint += 1
|
44
45
|
end
|
45
46
|
if nb_clusters_one_constraint > 0
|
46
|
-
crs.move_constraint_to_start(
|
47
|
+
crs.move_constraint_to_start(
|
48
|
+
(crs.nb_constraints - nb_clusters_one_constraint)..(crs.nb_constraints - 1)
|
49
|
+
)
|
47
50
|
clusters[nb_clusters_one_constraint, clusters.size - nb_clusters_one_constraint] =
|
48
51
|
clusters[0, clusters.size - nb_clusters_one_constraint]
|
49
52
|
clusters[0, nb_clusters_one_constraint] = Array.new(nb_clusters_one_constraint, 1)
|
@@ -83,14 +86,12 @@ module Cbc
|
|
83
86
|
min_idx = half_constraint_idx + 1
|
84
87
|
# puts " FEAS"
|
85
88
|
end
|
86
|
-
if max_idx
|
87
|
-
|
88
|
-
|
89
|
-
return min_idx..max_idx
|
90
|
-
end
|
89
|
+
next if max_idx != min_idx
|
90
|
+
return nil if max_idx > crs.nb_constraints
|
91
|
+
return min_idx..max_idx
|
91
92
|
end
|
92
93
|
# Shouldn't come here if the whole problem is infeasible
|
93
|
-
|
94
|
+
nil
|
94
95
|
end
|
95
96
|
|
96
97
|
def infeasible?(problem)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ilp
|
2
|
+
class Constant
|
3
|
+
attr_accessor :value
|
4
|
+
def initialize(value)
|
5
|
+
raise ArgumentError, "Argument is not numeric" unless value.is_a? Numeric
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def <=(other)
|
10
|
+
other >= value
|
11
|
+
end
|
12
|
+
|
13
|
+
def <(other)
|
14
|
+
other > value
|
15
|
+
end
|
16
|
+
|
17
|
+
def >=(other)
|
18
|
+
other <= value
|
19
|
+
end
|
20
|
+
|
21
|
+
def >(other)
|
22
|
+
other < value
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
other == value
|
27
|
+
end
|
28
|
+
|
29
|
+
def *(other)
|
30
|
+
other * value
|
31
|
+
end
|
32
|
+
|
33
|
+
def +(other)
|
34
|
+
other + value
|
35
|
+
end
|
36
|
+
|
37
|
+
def -(other)
|
38
|
+
-1 * other + value
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
value.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def pretty_print
|
46
|
+
value.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -4,8 +4,7 @@ module Ilp
|
|
4
4
|
GREATER_OR_EQ = :greater_or_eq
|
5
5
|
EQUALS = :equals
|
6
6
|
|
7
|
-
|
8
|
-
attr_accessor :function_name
|
7
|
+
attr_accessor :terms, :type, :bound, :function_name
|
9
8
|
|
10
9
|
def initialize(terms, type, bound)
|
11
10
|
@terms = terms - bound
|
@@ -26,7 +25,7 @@ module Ilp
|
|
26
25
|
LESS_OR_EQ => "<=",
|
27
26
|
GREATER_OR_EQ => ">=",
|
28
27
|
EQUALS => "="
|
29
|
-
}
|
28
|
+
}.freeze
|
30
29
|
|
31
30
|
def to_s
|
32
31
|
sign = SIGN_TO_STRING[@type] || "??"
|
data/lib/ruby-cbc/ilp/term.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Ilp
|
2
2
|
class Term
|
3
|
-
|
4
|
-
attr_accessor :mult
|
3
|
+
attr_accessor :mult, :var
|
5
4
|
|
6
5
|
def initialize(var, mult = 1)
|
7
6
|
@mult = mult
|
@@ -28,20 +27,13 @@ module Ilp
|
|
28
27
|
Ilp::TermArray.new([self]) >= other
|
29
28
|
end
|
30
29
|
|
31
|
-
def combine_in(other_term)
|
32
|
-
return Term.new(var, mult) unless other_term
|
33
|
-
raise "Terms cannot be combined: #{self} and #{other_term}" unless var.equal? other_term.var
|
34
|
-
other_term.mult += mult
|
35
|
-
other_term
|
36
|
-
end
|
37
|
-
|
38
30
|
def *(other)
|
39
31
|
raise ArgumentError, "Argument is not numeric" unless other.is_a? Numeric
|
40
32
|
Ilp::Term.new(@var, @mult * other)
|
41
33
|
end
|
42
34
|
|
43
35
|
def coerce(num)
|
44
|
-
[Ilp::TermArray.new([self])
|
36
|
+
[Ilp::Constant.new(num), Ilp::TermArray.new([self])]
|
45
37
|
end
|
46
38
|
|
47
39
|
def to_s
|
@@ -1,8 +1,10 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module Ilp
|
2
4
|
class TermArray
|
3
5
|
extend Forwardable
|
4
6
|
|
5
|
-
|
7
|
+
attr_accessor :terms
|
6
8
|
def_delegators :@terms, :map, :each, :size
|
7
9
|
|
8
10
|
def initialize(terms)
|
@@ -31,7 +33,7 @@ module Ilp
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def *(other)
|
34
|
-
raise ArgumentError,
|
36
|
+
raise ArgumentError, "Argument is not numeric" unless other.is_a? Numeric
|
35
37
|
new_terms = terms.map { |term| term * other }
|
36
38
|
TermArray.new(new_terms)
|
37
39
|
end
|
@@ -45,8 +47,9 @@ module Ilp
|
|
45
47
|
when Numeric
|
46
48
|
constant += term
|
47
49
|
when Ilp::Term
|
48
|
-
|
49
|
-
hterms[
|
50
|
+
v = term.var
|
51
|
+
hterms[v] ||= Ilp::Term.new(v, 0)
|
52
|
+
hterms[v].mult += term.mult
|
50
53
|
end
|
51
54
|
end
|
52
55
|
reduced = hterms.map { |_, term| term unless term.mult.zero? }
|
@@ -68,7 +71,7 @@ module Ilp
|
|
68
71
|
end
|
69
72
|
|
70
73
|
def coerce(value)
|
71
|
-
[value, self]
|
74
|
+
[Ilp::Constant.new(value), self]
|
72
75
|
end
|
73
76
|
|
74
77
|
def to_s
|
data/lib/ruby-cbc/ilp/var.rb
CHANGED
data/lib/ruby-cbc/model.rb
CHANGED
@@ -91,7 +91,7 @@ module Cbc
|
|
91
91
|
constraints.each do |cons|
|
92
92
|
str << " #{cons}\n"
|
93
93
|
end
|
94
|
-
bounded_vars = vars.
|
94
|
+
bounded_vars = vars.reject { |v| v.kind == Ilp::Var::BINARY_KIND }
|
95
95
|
unless bounded_vars.empty?
|
96
96
|
str << "\nBounds\n"
|
97
97
|
bounded_vars.each do |v|
|
@@ -133,16 +133,16 @@ module Cbc
|
|
133
133
|
v
|
134
134
|
end
|
135
135
|
|
136
|
-
def lb_to_s(
|
137
|
-
return "-inf" if
|
138
|
-
return "+inf" if
|
139
|
-
|
136
|
+
def lb_to_s(lower_bound)
|
137
|
+
return "-inf" if lower_bound.nil? || lower_bound == -Cbc::INF
|
138
|
+
return "+inf" if lower_bound == Cbc::INF
|
139
|
+
lower_bound.to_s
|
140
140
|
end
|
141
141
|
|
142
|
-
def ub_to_s(
|
143
|
-
return "+inf" if
|
144
|
-
return "-inf" if
|
145
|
-
|
142
|
+
def ub_to_s(upper_bound)
|
143
|
+
return "+inf" if upper_bound.nil? || upper_bound == Cbc::INF
|
144
|
+
return "-inf" if upper_bound == -Cbc::INF
|
145
|
+
upper_bound.to_s
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
data/lib/ruby-cbc/problem.rb
CHANGED
@@ -56,9 +56,6 @@ module Cbc
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def create_cbc_problem(continuous: false)
|
59
|
-
@int_arrays = []
|
60
|
-
@double_arrays = []
|
61
|
-
|
62
59
|
ccs = self.class.crs_to_ccs(@crs)
|
63
60
|
objective = Array.new(ccs.nb_vars, 0)
|
64
61
|
if model.objective
|
@@ -69,10 +66,18 @@ module Cbc
|
|
69
66
|
|
70
67
|
@cbc_model = Cbc_wrapper.Cbc_newModel
|
71
68
|
Cbc_wrapper.Cbc_loadProblem(
|
72
|
-
@cbc_model,
|
73
|
-
|
74
|
-
|
75
|
-
|
69
|
+
@cbc_model,
|
70
|
+
ccs.nb_vars,
|
71
|
+
@crs.nb_constraints,
|
72
|
+
to_int_array(ccs.col_ptr),
|
73
|
+
to_int_array(ccs.row_idx),
|
74
|
+
to_double_array(ccs.values),
|
75
|
+
nil,
|
76
|
+
nil,
|
77
|
+
to_double_array(objective),
|
78
|
+
nil,
|
79
|
+
nil
|
80
|
+
)
|
76
81
|
|
77
82
|
# Segmentation errors when setting name
|
78
83
|
# Cbc_wrapper.Cbc_setProblemName(@cbc_model, model.name) if model.name
|
@@ -106,22 +111,20 @@ module Cbc
|
|
106
111
|
idx += 1
|
107
112
|
end
|
108
113
|
|
109
|
-
ObjectSpace.define_finalizer(self, self.class.finalizer(@cbc_model
|
114
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer(@cbc_model))
|
110
115
|
|
111
|
-
@default_solve_params = {
|
112
|
-
log: 0
|
113
|
-
}
|
116
|
+
@default_solve_params = { log: 0 }
|
114
117
|
end
|
115
118
|
|
116
|
-
def set_constraint_bounds(
|
117
|
-
case
|
119
|
+
def set_constraint_bounds(constraint, idx)
|
120
|
+
case constraint.type
|
118
121
|
when Ilp::Constraint::LESS_OR_EQ
|
119
|
-
Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx,
|
122
|
+
Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx, constraint.bound)
|
120
123
|
when Ilp::Constraint::GREATER_OR_EQ
|
121
|
-
Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx,
|
124
|
+
Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx, constraint.bound)
|
122
125
|
when Ilp::Constraint::EQUALS
|
123
|
-
Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx,
|
124
|
-
Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx,
|
126
|
+
Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx, constraint.bound)
|
127
|
+
Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx, constraint.bound)
|
125
128
|
end
|
126
129
|
end
|
127
130
|
|
@@ -131,8 +134,6 @@ module Cbc
|
|
131
134
|
end
|
132
135
|
Cbc_wrapper.Cbc_solve(@cbc_model)
|
133
136
|
@solution = Cbc_wrapper::DoubleArray.frompointer(Cbc_wrapper.Cbc_getColSolution(@cbc_model))
|
134
|
-
@double_arrays << @solution
|
135
|
-
@solution
|
136
137
|
end
|
137
138
|
|
138
139
|
def value_of(var)
|
@@ -145,9 +146,12 @@ module Cbc
|
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
149
|
+
# Keep this one for back compatibility
|
150
|
+
# rubocop:disable Naming/AccessorMethodName
|
148
151
|
def set_time_limit(seconds)
|
149
152
|
@default_solve_params[:sec] = seconds
|
150
153
|
end
|
154
|
+
# rubocop:enable Naming/AccessorMethodName
|
151
155
|
|
152
156
|
def proven_optimal?
|
153
157
|
Cbc_wrapper.Cbc_isProvenOptimal(@cbc_model) == 1
|
@@ -175,18 +179,16 @@ module Cbc
|
|
175
179
|
end
|
176
180
|
|
177
181
|
def find_conflict
|
178
|
-
@
|
182
|
+
@find_conflict ||= ConflictSolver.new(self).find_conflict
|
179
183
|
end
|
180
184
|
|
181
185
|
def find_conflict_vars
|
182
|
-
@
|
186
|
+
@find_conflict_vars ||= find_conflict.map(&:vars).flatten.uniq
|
183
187
|
end
|
184
188
|
|
185
|
-
def self.finalizer(cbc_model
|
189
|
+
def self.finalizer(cbc_model)
|
186
190
|
proc do
|
187
191
|
Cbc_wrapper.Cbc_deleteModel(cbc_model)
|
188
|
-
int_arrays.each { |ar| Cbc_wrapper.delete_intArray(ar) }
|
189
|
-
double_arrays.each { |ar| Cbc_wrapper.delete_doubleArray(ar) }
|
190
192
|
end
|
191
193
|
end
|
192
194
|
|
@@ -203,7 +205,6 @@ module Cbc
|
|
203
205
|
c_array[idx] = array[idx]
|
204
206
|
idx += 1
|
205
207
|
end
|
206
|
-
@int_arrays << c_array
|
207
208
|
c_array
|
208
209
|
end
|
209
210
|
|
@@ -214,7 +215,6 @@ module Cbc
|
|
214
215
|
c_array[idx] = array[idx]
|
215
216
|
idx += 1
|
216
217
|
end
|
217
|
-
@double_arrays << c_array
|
218
218
|
c_array
|
219
219
|
end
|
220
220
|
end
|
data/lib/ruby-cbc/version.rb
CHANGED
data/lib/ruby-cbc.rb
CHANGED
@@ -4,18 +4,19 @@ require "cbc-wrapper"
|
|
4
4
|
module Cbc
|
5
5
|
end
|
6
6
|
|
7
|
-
files = %w
|
7
|
+
files = %w[
|
8
8
|
conflict_solver
|
9
9
|
model
|
10
10
|
problem
|
11
11
|
version
|
12
|
+
ilp/constant
|
12
13
|
ilp/constraint
|
13
14
|
ilp/objective
|
14
15
|
ilp/term
|
15
16
|
ilp/term_array
|
16
17
|
ilp/var
|
17
18
|
utils/compressed_row_storage
|
18
|
-
|
19
|
+
]
|
19
20
|
|
20
21
|
files.each do |file|
|
21
22
|
require File.expand_path("../ruby-cbc/#{file}", __FILE__)
|