mipper 0.0.4
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/mipper/cbc/ext.rb +36 -0
- data/lib/mipper/cbc/model.rb +174 -0
- data/lib/mipper/cbc.rb +3 -0
- data/lib/mipper/constraint.rb +17 -0
- data/lib/mipper/expression.rb +53 -0
- data/lib/mipper/glpk/ext/constants.rb +587 -0
- data/lib/mipper/glpk/ext/structs.rb +28 -0
- data/lib/mipper/glpk/ext.rb +43 -0
- data/lib/mipper/glpk/model.rb +186 -0
- data/lib/mipper/glpk.rb +3 -0
- data/lib/mipper/gurobi/env.rb +25 -0
- data/lib/mipper/gurobi/ext/constants.rb +1899 -0
- data/lib/mipper/gurobi/ext.rb +47 -0
- data/lib/mipper/gurobi/model.rb +246 -0
- data/lib/mipper/gurobi.rb +4 -0
- data/lib/mipper/lp_solve/ext/constants.rb +2075 -0
- data/lib/mipper/lp_solve/ext.rb +35 -0
- data/lib/mipper/lp_solve/model.rb +173 -0
- data/lib/mipper/lp_solve.rb +4 -0
- data/lib/mipper/model.rb +92 -0
- data/lib/mipper/util.rb +5 -0
- data/lib/mipper/variable.rb +79 -0
- data/lib/mipper.rb +11 -0
- metadata +81 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module MIPPeR
|
4
|
+
module Gurobi
|
5
|
+
# Hide the constants inside the MIPPeR::Gurobi module
|
6
|
+
module_eval File.read(File.expand_path './ext/constants.rb',
|
7
|
+
File.dirname(__FILE__))
|
8
|
+
|
9
|
+
extend FFI::Library
|
10
|
+
ffi_lib 'gurobi'
|
11
|
+
|
12
|
+
attach_function :GRBloadenv, [:pointer, :string], :int
|
13
|
+
attach_function :GRBsetintparam, [:pointer, :string, :int], :int
|
14
|
+
attach_function :GRBgeterrormsg, [:pointer], :string
|
15
|
+
attach_function :GRBfreeenv, [:pointer], :void
|
16
|
+
|
17
|
+
attach_function :GRBnewmodel, [:pointer, :pointer, :string, :int,
|
18
|
+
:pointer, :pointer, :pointer, :pointer,
|
19
|
+
:pointer], :int
|
20
|
+
attach_function :GRBupdatemodel, [:pointer], :int
|
21
|
+
attach_function :GRBfreemodel, [:pointer], :int
|
22
|
+
|
23
|
+
attach_function :GRBaddvar, [:pointer, :int, :pointer, :pointer,
|
24
|
+
:double, :double, :double, :char, :string],
|
25
|
+
:int
|
26
|
+
attach_function :GRBaddvars, [:pointer, :int, :int, :pointer, :pointer,
|
27
|
+
:pointer, :pointer, :pointer, :pointer,
|
28
|
+
:pointer, :pointer], :int
|
29
|
+
attach_function :GRBaddconstr, [:pointer, :int, :pointer, :pointer, :char,
|
30
|
+
:double, :string], :int
|
31
|
+
attach_function :GRBaddconstrs, [:pointer, :int, :int, :pointer, :pointer,
|
32
|
+
:pointer, :pointer, :pointer,
|
33
|
+
:pointer], :int
|
34
|
+
attach_function :GRBoptimize, [:pointer], :int
|
35
|
+
attach_function :GRBcomputeIIS, [:pointer], :int
|
36
|
+
attach_function :GRBwrite, [:pointer, :string], :int
|
37
|
+
|
38
|
+
attach_function :GRBsetintattr, [:pointer, :string, :int], :int
|
39
|
+
|
40
|
+
attach_function :GRBgetintattr, [:pointer, :string, :pointer], :int
|
41
|
+
attach_function :GRBgetdblattr, [:pointer, :string, :pointer], :int
|
42
|
+
attach_function :GRBgetdblattrarray, [:pointer, :string, :int, :int,
|
43
|
+
:pointer], :int
|
44
|
+
attach_function :GRBsetdblattrarray, [:pointer, :string, :int, :int,
|
45
|
+
:pointer], :int
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
module MIPPeR
|
2
|
+
class GurobiModel < Model
|
3
|
+
attr_reader :ptr, :environment
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
|
8
|
+
@var_count = 0
|
9
|
+
|
10
|
+
@environment = Gurobi::Environment.new
|
11
|
+
|
12
|
+
@ptr = FFI::MemoryPointer.new :pointer
|
13
|
+
Gurobi.GRBnewmodel @environment.ptr, @ptr, 'model',
|
14
|
+
0, nil, nil, nil, nil, nil
|
15
|
+
@ptr = @ptr.read_pointer
|
16
|
+
|
17
|
+
# Ensure the model is freed
|
18
|
+
ObjectSpace.define_finalizer self, self.class.finalize(@ptr)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Update the model
|
22
|
+
def update
|
23
|
+
super
|
24
|
+
|
25
|
+
ret = Gurobi.GRBupdatemodel @ptr
|
26
|
+
fail if ret != 0
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write the model to a file
|
30
|
+
def write(filename)
|
31
|
+
Gurobi.GRBwrite @ptr, filename
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set the sense of the model
|
35
|
+
def sense=(sense)
|
36
|
+
@sense = sense
|
37
|
+
sense = sense == :min ? Gurobi::GRB_MINIMIZE : Gurobi::GRB_MAXIMIZE
|
38
|
+
ret = Gurobi.GRBsetintattr @ptr, Gurobi::GRB_INT_ATTR_MODELSENSE, sense
|
39
|
+
fail if ret != 0
|
40
|
+
end
|
41
|
+
|
42
|
+
# Optimize the model
|
43
|
+
def optimize
|
44
|
+
# Ensure pending variables and constraints are added
|
45
|
+
update
|
46
|
+
|
47
|
+
ret = Gurobi.GRBoptimize @ptr
|
48
|
+
fail if ret != 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the status of the model
|
52
|
+
def status
|
53
|
+
intptr = FFI::MemoryPointer.new :pointer
|
54
|
+
ret = Gurobi.GRBgetintattr @ptr, Gurobi::GRB_INT_ATTR_STATUS, intptr
|
55
|
+
fail if ret != 0
|
56
|
+
|
57
|
+
case intptr.read_int
|
58
|
+
when Gurobi::GRB_OPTIMAL
|
59
|
+
:optimized
|
60
|
+
when Gurobi::GRB_INFEASIBLE, Gurobi::GRB_INF_OR_UNBD,
|
61
|
+
Gurobi::GRB_UNBOUNDED
|
62
|
+
:invalid
|
63
|
+
else
|
64
|
+
:unknown
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Compute an irreducible inconsistent subsytem for the model
|
69
|
+
def compute_IIS
|
70
|
+
ret = Gurobi.GRBcomputeIIS @ptr
|
71
|
+
fail if ret != 0
|
72
|
+
end
|
73
|
+
|
74
|
+
# The value of the objective function
|
75
|
+
def objective_value
|
76
|
+
dblptr = FFI::MemoryPointer.new :pointer
|
77
|
+
ret = Gurobi.GRBgetdblattr @ptr, Gurobi::GRB_DBL_ATTR_OBJVAL, dblptr
|
78
|
+
fail if ret != 0
|
79
|
+
dblptr.read_double
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_variable_bounds(var_index, lb, ub)
|
83
|
+
set_double_attribute Gurobi::GRB_DBL_ATTR_LB, var_index, lb
|
84
|
+
set_double_attribute Gurobi::GRB_DBL_ATTR_UB, var_index, ub
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get the value of a variable from the model
|
88
|
+
def variable_value(var)
|
89
|
+
dblptr = FFI::MemoryPointer.new :pointer
|
90
|
+
Gurobi.GRBgetdblattrarray @ptr, Gurobi::GRB_DBL_ATTR_X,
|
91
|
+
var.index, 1, dblptr
|
92
|
+
dblptr.read_array_of_double(1)[0]
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
# Add multiple variables to the model simultaneously
|
98
|
+
def add_variables(vars)
|
99
|
+
objective_buffer = FFI::MemoryPointer.new :double, vars.length
|
100
|
+
objective_buffer.write_array_of_double vars.map(&:coefficient)
|
101
|
+
|
102
|
+
lb_buffer = FFI::MemoryPointer.new :double, vars.length
|
103
|
+
lb_buffer.write_array_of_double vars.map(&:lower_bound)
|
104
|
+
|
105
|
+
ub_buffer = FFI::MemoryPointer.new :double, vars.length
|
106
|
+
ub_buffer.write_array_of_double vars.map(&:upper_bound)
|
107
|
+
|
108
|
+
type_buffer = FFI::MemoryPointer.new :char, vars.length
|
109
|
+
type_buffer.write_array_of_char vars.map { |var| gurobi_type(var.type) }
|
110
|
+
|
111
|
+
names = array_to_pointers_to_names vars
|
112
|
+
names_buffer = FFI::MemoryPointer.new :pointer, vars.length
|
113
|
+
names_buffer.write_array_of_pointer names
|
114
|
+
|
115
|
+
ret = Gurobi.GRBaddvars @ptr, vars.length, 0, nil, nil, nil,
|
116
|
+
objective_buffer, lb_buffer, ub_buffer,
|
117
|
+
type_buffer, names_buffer
|
118
|
+
|
119
|
+
fail if ret != 0
|
120
|
+
|
121
|
+
# Store all the variables in the model
|
122
|
+
vars.each { |var| store_variable var }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Add a new variable to the model
|
126
|
+
def add_variable(var)
|
127
|
+
ret = Gurobi.GRBaddvar @ptr, 0, nil, nil, var.coefficient,
|
128
|
+
var.lower_bound, var.upper_bound,
|
129
|
+
gurobi_type(var.type), var.name
|
130
|
+
fail if ret != 0
|
131
|
+
|
132
|
+
store_variable var
|
133
|
+
end
|
134
|
+
|
135
|
+
# Add multiple constraints at once
|
136
|
+
def add_constraints(constrs)
|
137
|
+
cbeg = []
|
138
|
+
cind = []
|
139
|
+
cval = []
|
140
|
+
constrs.each_with_index.map do |constr, i|
|
141
|
+
cbeg << cind.length
|
142
|
+
constr.expression.terms.each do |var, coeff|
|
143
|
+
cind << var.instance_variable_get(:@index)
|
144
|
+
cval << coeff
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
cbeg_buffer = FFI::MemoryPointer.new :pointer, cbeg.length
|
149
|
+
cbeg_buffer.write_array_of_int cbeg
|
150
|
+
|
151
|
+
cind_buffer = FFI::MemoryPointer.new :pointer, cind.length
|
152
|
+
cind_buffer.write_array_of_int cind
|
153
|
+
|
154
|
+
cval_buffer = FFI::MemoryPointer.new :pointer, cval.length
|
155
|
+
cval_buffer.write_array_of_double cval
|
156
|
+
|
157
|
+
sense_buffer = FFI::MemoryPointer.new :pointer, constrs.length
|
158
|
+
sense_buffer.write_array_of_char(constrs.map do |c|
|
159
|
+
gurobi_sense(c.sense)
|
160
|
+
end)
|
161
|
+
|
162
|
+
rhs_buffer = FFI::MemoryPointer.new :pointer, constrs.length
|
163
|
+
rhs_buffer.write_array_of_double constrs.map(&:rhs)
|
164
|
+
|
165
|
+
names = array_to_pointers_to_names constrs
|
166
|
+
names_buffer = FFI::MemoryPointer.new :pointer, constrs.length
|
167
|
+
names_buffer.write_array_of_pointer names
|
168
|
+
|
169
|
+
ret = Gurobi.GRBaddconstrs @ptr, constrs.length, cind.length,
|
170
|
+
cbeg_buffer, cind_buffer, cval_buffer,
|
171
|
+
sense_buffer, rhs_buffer, names_buffer
|
172
|
+
fail if ret != 0
|
173
|
+
|
174
|
+
@constraints.concat constrs
|
175
|
+
end
|
176
|
+
|
177
|
+
# Add a new constraint to the model
|
178
|
+
def add_constraint(constr)
|
179
|
+
terms = constr.expression.terms
|
180
|
+
indexes_buffer = FFI::MemoryPointer.new :int, terms.length
|
181
|
+
indexes_buffer.write_array_of_int(terms.each_key.map do |var|
|
182
|
+
var.instance_variable_get(:@index)
|
183
|
+
end)
|
184
|
+
|
185
|
+
values_buffer = FFI::MemoryPointer.new :double, terms.length
|
186
|
+
values_buffer.write_array_of_double terms.values
|
187
|
+
|
188
|
+
ret = Gurobi.GRBaddconstr @ptr, terms.length,
|
189
|
+
indexes_buffer, values_buffer,
|
190
|
+
gurobi_sense(constr.sense),
|
191
|
+
constr.rhs, constr.name
|
192
|
+
fail if ret != 0
|
193
|
+
|
194
|
+
@constraints << constr
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
# Free the model
|
200
|
+
def self.finalize(ptr)
|
201
|
+
proc { Gurobi.GRBfreemodel ptr }
|
202
|
+
end
|
203
|
+
|
204
|
+
# Save the variable to the model and update the variable pointers
|
205
|
+
def store_variable(var)
|
206
|
+
# Update the variable to track the index in the model
|
207
|
+
var.instance_variable_set :@model, self
|
208
|
+
var.instance_variable_set :@index, @var_count
|
209
|
+
@var_count += 1
|
210
|
+
|
211
|
+
@variables << var
|
212
|
+
end
|
213
|
+
|
214
|
+
def set_double_attribute(name, var_index, value)
|
215
|
+
buffer = FFI::MemoryPointer.new :double, 1
|
216
|
+
buffer.write_array_of_double [value]
|
217
|
+
ret = Gurobi.GRBsetdblattrarray @ptr, name, var_index, 1, buffer
|
218
|
+
fail if ret != 0
|
219
|
+
end
|
220
|
+
|
221
|
+
# Convert an array of objects to an FFI array of
|
222
|
+
# memory pointers to the names of each object
|
223
|
+
def array_to_pointers_to_names(arr)
|
224
|
+
arr.map do |obj|
|
225
|
+
obj.name.nil? ? nil : FFI::MemoryPointer.from_string(obj.name)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def gurobi_sense(sense)
|
230
|
+
sense.to_s[0].ord
|
231
|
+
end
|
232
|
+
|
233
|
+
def gurobi_type(type)
|
234
|
+
case type
|
235
|
+
when :integer
|
236
|
+
Gurobi::GRB_INTEGER.ord
|
237
|
+
when :binary
|
238
|
+
Gurobi::GRB_BINARY.ord
|
239
|
+
when :continuous
|
240
|
+
Gurobi::GRB_CONTINUOUS.ord
|
241
|
+
else
|
242
|
+
fail type
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|