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,186 @@
1
+ module MIPPeR
2
+ class GLPKModel < Model
3
+ attr_reader :ptr
4
+
5
+ def initialize
6
+ super
7
+
8
+ @var_count = 0
9
+ @constr_count = 0
10
+
11
+ # Constraint matrix initialization (see #add_constraints)
12
+ @ia = [0]
13
+ @ja = [0]
14
+ @ar = [0]
15
+
16
+ # Disable terminal output
17
+ GLPK.glp_term_out GLPK::GLP_OFF
18
+
19
+ # Construct a new model
20
+ @ptr = FFI::AutoPointer.new GLPK.glp_create_prob,
21
+ GLPK.method(:glp_delete_prob)
22
+ end
23
+
24
+ # Write the model to a file
25
+ def write(filename)
26
+ ret = GLPK.glp_write_lp @ptr, 0, filename
27
+ fail if ret != 0
28
+ end
29
+
30
+ # Set the sense of the model
31
+ def sense=(sense)
32
+ @sense = sense
33
+ sense = sense == :min ? GLPK::GLP_MIN : GLPK::GLP_MAX
34
+ GLPK.glp_set_obj_dir @ptr, sense
35
+ end
36
+
37
+ # Optimize the model
38
+ def optimize
39
+ # Ensure pending variables and constraints are added
40
+ update
41
+
42
+ # Run the solver and save the status for later
43
+ iocp = GLPK::IOCP.new
44
+ GLPK.glp_init_iocp iocp
45
+ iocp[:presolve] = GLPK::GLP_ON
46
+ @status = GLPK.glp_intopt @ptr, iocp
47
+ end
48
+
49
+ # Get the status of the model
50
+ def status
51
+ case @status
52
+ when 0, GLPK::GLP_EMIPGAP
53
+ :optimized
54
+ when GLPK::GLP_EBOUND, GLPK::GLP_EROOT, GLPK::GLP_ENOPFS
55
+ :invalid
56
+ else
57
+ :unknown
58
+ end
59
+ end
60
+
61
+ # The value of the objective function
62
+ def objective_value
63
+ GLPK.glp_mip_obj_val @ptr
64
+ end
65
+
66
+ # Get the value of a variable from the model
67
+ def variable_value(var)
68
+ GLPK.glp_mip_col_val(@ptr, var.index)
69
+ end
70
+
71
+ def set_variable_bounds(var_index, lb, ub)
72
+ # Determine the type of the variable bounds
73
+ if lb == ub
74
+ type = GLPK::GLP_FX
75
+ elsif lb.finite?
76
+ type = ub.finite? ? GLPK::GLP_DB : GLPK::GLP_LO
77
+ else
78
+ type = ub.finite? ? GLPK::GLP_UP : GLPK::GLP_FR
79
+ end
80
+
81
+ # Set the bounds on the variable
82
+ GLPK.glp_set_col_bnds(@ptr, var_index, type, lb, ub)
83
+ end
84
+
85
+ protected
86
+
87
+ # Add multiple variables to the model simultaneously
88
+ def add_variables(vars)
89
+ # Store all the variables in the model
90
+ GLPK.glp_add_cols(@ptr, vars.length)
91
+ vars.each { |var| store_variable var }
92
+ end
93
+
94
+ # Add multiple constraints at once
95
+ def add_constraints(constrs)
96
+ GLPK.glp_add_rows(@ptr, constrs.length)
97
+
98
+ # Store each constraint and initialize a matrix used to hold
99
+ # the coefficients of each value in the constraint matrix
100
+ #
101
+ # * ia - row (constraint) index
102
+ # * ja - column (variable) index
103
+ # * ar - constraint coefficient
104
+ constrs.each do |constr|
105
+ store_constraint constr
106
+ constr.expression.terms.each do |var, coeff|
107
+ @ia << constr.instance_variable_get(:@index)
108
+ @ja << var.instance_variable_get(:@index)
109
+ @ar << coeff
110
+ end
111
+ end
112
+
113
+ ia_buffer = FFI::MemoryPointer.new :pointer, @ia.length
114
+ ia_buffer.write_array_of_int @ia
115
+ ja_buffer = FFI::MemoryPointer.new :pointer, @ja.length
116
+ ja_buffer.write_array_of_int @ja
117
+ ar_buffer = FFI::MemoryPointer.new :pointer, @ar.length
118
+ ar_buffer.write_array_of_double @ar
119
+
120
+ GLPK.glp_load_matrix(@ptr, @ar.length - 1, ia_buffer, ja_buffer,
121
+ ar_buffer)
122
+
123
+ @constraints.concat constrs
124
+ end
125
+
126
+ private
127
+
128
+ # Save the constraint to the model and update the constraint pointers
129
+ def store_constraint(constr)
130
+ # Update the constraint to track the index in the model
131
+ index = @constr_count + 1
132
+ constr.instance_variable_set :@model, self
133
+ constr.instance_variable_set :@index, index
134
+ @constr_count += 1
135
+
136
+ # Set constraint properties
137
+ GLPK.glp_set_row_name(@ptr, index, constr.name)
138
+
139
+ case constr.sense
140
+ when :==
141
+ lb = ub = constr.rhs
142
+ type = GLPK::GLP_FX
143
+ when :>=
144
+ lb = constr.rhs
145
+ ub = Float::INFINITY
146
+ type = GLPK::GLP_LO
147
+ when :<=
148
+ lb = -Float::INFINITY
149
+ ub = constr.rhs
150
+ type = GLPK::GLP_UP
151
+ end
152
+ GLPK.glp_set_row_bnds(@ptr, index, type, lb, ub)
153
+ end
154
+
155
+ # Save the variable to the model and update the variable pointers
156
+ def store_variable(var)
157
+ # Update the variable to track the index in the model
158
+ index = @var_count + 1
159
+ var.instance_variable_set :@model, self
160
+ var.instance_variable_set :@index, index
161
+ @var_count += 1
162
+
163
+ @variables << var
164
+
165
+ # Set variable properties
166
+ GLPK.glp_set_col_name(@ptr, index, var.name)
167
+ GLPK.glp_set_col_kind(@ptr, index, glpk_type(var.type))
168
+ GLPK.glp_set_obj_coef(@ptr, index, var.coefficient)
169
+ set_variable_bounds index, var.lower_bound, var.upper_bound
170
+ end
171
+
172
+ # Get the constant GLPK uses to represent our variable types
173
+ def glpk_type(type)
174
+ case type
175
+ when :integer
176
+ GLPK::GLP_IV
177
+ when :binary
178
+ GLPK::GLP_BV
179
+ when :continuous
180
+ GLPK::GLP_CV
181
+ else
182
+ fail type
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'glpk/ext'
2
+
3
+ require_relative 'glpk/model'
@@ -0,0 +1,25 @@
1
+ module MIPPeR
2
+ module Gurobi
3
+ class Environment
4
+ attr_reader :ptr
5
+
6
+ def initialize
7
+ # Create a new environment object
8
+ @ptr = FFI::MemoryPointer.new :pointer
9
+ Gurobi.GRBloadenv @ptr, nil
10
+ @ptr = @ptr.read_pointer
11
+
12
+ # Disable output
13
+ Gurobi.GRBsetintparam @ptr, Gurobi::GRB_INT_PAR_OUTPUTFLAG, 0
14
+
15
+ # Ensure the environment is freed
16
+ ObjectSpace.define_finalizer self, self.class.finalize(@ptr)
17
+ end
18
+
19
+ # Free the environment
20
+ def self.finalize(ptr)
21
+ proc { Gurobi.GRBfreeenv ptr }
22
+ end
23
+ end
24
+ end
25
+ end