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.
@@ -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
@@ -0,0 +1,4 @@
1
+ require_relative 'gurobi/ext'
2
+
3
+ require_relative 'gurobi/env'
4
+ require_relative 'gurobi/model'