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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3df4957c76c02dc47894208b573de31ffb70d8e
|
4
|
+
data.tar.gz: d83b68ff194145a7dd6ab43f34df6480eef6901f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3c2fccdb2cfa9fd3796344c09b1335b460446d1a674034fd9143e3400cede2cc6483b386e297d01028ec64deff57d9a7886ea3fbe86a0863de047d266fb37014
|
7
|
+
data.tar.gz: f93a8c1ffa8e7ff786ebbcc7fd551deca62506e85bc99d2b5bcc0c14e40c2bf25363c42bb8864b095567c96319f0547491320d376b7329aa13c93842a44550e2
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module MIPPeR
|
4
|
+
module Cbc
|
5
|
+
extend FFI::Library
|
6
|
+
ffi_lib_flags :now, :global
|
7
|
+
ffi_lib 'z', 'bz2', 'CoinUtils', 'Osi', 'Clp', 'OsiClp', 'Cgl', 'Cbc',
|
8
|
+
'CbcSolver'
|
9
|
+
|
10
|
+
attach_function :Cbc_newModel, [], :pointer
|
11
|
+
attach_function :Cbc_deleteModel, [:pointer], :void
|
12
|
+
attach_function :Cbc_loadProblem, [:pointer, :int, :int, :pointer, :pointer,
|
13
|
+
:pointer, :pointer, :pointer, :pointer,
|
14
|
+
:pointer, :pointer], :void
|
15
|
+
attach_function :Cbc_setColName, [:pointer, :int, :string], :void
|
16
|
+
attach_function :Cbc_setRowName, [:pointer, :int, :string], :void
|
17
|
+
attach_function :Cbc_setObjSense, [:pointer, :double], :void
|
18
|
+
attach_function :Cbc_setRowLower, [:pointer, :int, :double], :void
|
19
|
+
attach_function :Cbc_setRowUpper, [:pointer, :int, :double], :void
|
20
|
+
attach_function :Cbc_setObjCoeff, [:pointer, :int, :double], :void
|
21
|
+
attach_function :Cbc_setColLower, [:pointer, :int, :double], :void
|
22
|
+
attach_function :Cbc_setColUpper, [:pointer, :int, :double], :void
|
23
|
+
attach_function :Cbc_setContinuous, [:pointer, :int], :void
|
24
|
+
attach_function :Cbc_setInteger, [:pointer, :int], :void
|
25
|
+
attach_function :Cbc_solve, [:pointer], :int
|
26
|
+
attach_function :Cbc_getColSolution, [:pointer], :pointer
|
27
|
+
attach_function :Cbc_getObjValue, [:pointer], :double
|
28
|
+
attach_function :Cbc_status, [:pointer], :int
|
29
|
+
attach_function :Cbc_secondaryStatus, [:pointer], :int
|
30
|
+
attach_function :Cbc_printModel, [:pointer, :string], :void
|
31
|
+
attach_function :Cbc_isProvenOptimal, [:pointer], :int
|
32
|
+
attach_function :Cbc_isProvenInfeasible, [:pointer], :int
|
33
|
+
attach_function :Cbc_isContinuousUnbounded, [:pointer], :int
|
34
|
+
attach_function :Cbc_setParameter, [:pointer, :string, :string], :void
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module MIPPeR
|
2
|
+
class CbcModel < Model
|
3
|
+
attr_reader :ptr
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
|
8
|
+
@var_count = 0
|
9
|
+
@constr_count = 0
|
10
|
+
|
11
|
+
# Construct a new model
|
12
|
+
@ptr = FFI::AutoPointer.new Cbc.Cbc_newModel,
|
13
|
+
Cbc.method(:Cbc_deleteModel)
|
14
|
+
Cbc.Cbc_setParameter @ptr, 'logLevel', '0'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Set the sense of the model
|
18
|
+
def sense=(sense)
|
19
|
+
@sense = sense
|
20
|
+
sense = sense == :min ? 1 : -1
|
21
|
+
Cbc.Cbc_setObjSense @ptr, sense
|
22
|
+
end
|
23
|
+
|
24
|
+
# Optimize the model
|
25
|
+
def optimize
|
26
|
+
# Ensure pending variables and constraints are added
|
27
|
+
update
|
28
|
+
|
29
|
+
# Run the solver and save the status for later
|
30
|
+
Cbc.Cbc_solve @ptr
|
31
|
+
fail if Cbc.Cbc_status(@ptr) != 0
|
32
|
+
|
33
|
+
# Check and store the model status
|
34
|
+
if Cbc.Cbc_isProvenOptimal(@ptr) == 1
|
35
|
+
@status = :optimized
|
36
|
+
elsif Cbc.Cbc_isProvenInfeasible(@ptr) == 1 or
|
37
|
+
Cbc.Cbc_isContinuousUnbounded(@ptr) == 1
|
38
|
+
@status = :invalid
|
39
|
+
else
|
40
|
+
@status = :unknown
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the status of the model
|
45
|
+
def status
|
46
|
+
@status
|
47
|
+
end
|
48
|
+
|
49
|
+
# The value of the objective function
|
50
|
+
def objective_value
|
51
|
+
Cbc.Cbc_getObjValue @ptr
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the value of a variable from the model
|
55
|
+
def variable_value(var)
|
56
|
+
dblptr = Cbc.Cbc_getColSolution @ptr
|
57
|
+
dblptr.read_array_of_double(@variables.length)[var.index]
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_variable_bounds(var_index, lb, ub)
|
61
|
+
Cbc.Cbc_setColLower @ptr, var_index, lb
|
62
|
+
Cbc.Cbc_setColUpper @ptr, var_index, ub
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
# Add multiple variables to the model simultaneously
|
68
|
+
def add_variables(vars)
|
69
|
+
# Store all the variables in the model
|
70
|
+
# Most of the work will be done when we add the constraints
|
71
|
+
vars.each do |var|
|
72
|
+
var.instance_variable_set :@model, self
|
73
|
+
@variables << var
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add multiple constraints at once
|
78
|
+
def add_constraints(constrs)
|
79
|
+
# Store the index which will be used for each constraint
|
80
|
+
constrs.each do |constr|
|
81
|
+
constr.instance_variable_set :@index, @constr_count
|
82
|
+
@constr_count += 1
|
83
|
+
end
|
84
|
+
|
85
|
+
# Construct a matrix of non-zero values in CSC format
|
86
|
+
start = []
|
87
|
+
index = []
|
88
|
+
value = []
|
89
|
+
col_start = 0
|
90
|
+
@variables.each do |var|
|
91
|
+
# Mark the start of this column
|
92
|
+
start << col_start
|
93
|
+
|
94
|
+
var.constraints.each do |constr|
|
95
|
+
col_start += 1
|
96
|
+
index << constr.instance_variable_get(:@index)
|
97
|
+
value << constr.expression.terms[var]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
start << col_start
|
101
|
+
|
102
|
+
start_buffer = FFI::MemoryPointer.new :int, start.length
|
103
|
+
start_buffer.write_array_of_int start
|
104
|
+
index_buffer = FFI::MemoryPointer.new :int, index.length
|
105
|
+
index_buffer.write_array_of_int index
|
106
|
+
value_buffer = FFI::MemoryPointer.new :double, value.length
|
107
|
+
value_buffer.write_array_of_double value
|
108
|
+
|
109
|
+
Cbc.Cbc_loadProblem @ptr, @variables.length, constrs.length,
|
110
|
+
start_buffer, index_buffer, value_buffer,
|
111
|
+
nil, nil, nil, nil, nil
|
112
|
+
|
113
|
+
constrs.each do |constr|
|
114
|
+
store_constraint constr
|
115
|
+
@constraints << constr
|
116
|
+
end
|
117
|
+
|
118
|
+
# We store variables now since they didn't exist earlier
|
119
|
+
@variables.each_with_index do |var, i|
|
120
|
+
var.instance_variable_set(:@index, i)
|
121
|
+
store_variable var
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
# Save the constraint to the model and update the constraint pointers
|
128
|
+
def store_constraint(constr)
|
129
|
+
# Update the constraint to track the index in the model
|
130
|
+
index = constr.instance_variable_get(:@index)
|
131
|
+
constr.instance_variable_set :@model, self
|
132
|
+
|
133
|
+
# Set constraint properties
|
134
|
+
Cbc.Cbc_setRowName(@ptr, index, constr.name) unless constr.name.nil?
|
135
|
+
|
136
|
+
case constr.sense
|
137
|
+
when :==
|
138
|
+
lb = ub = constr.rhs
|
139
|
+
when :>=
|
140
|
+
lb = constr.rhs
|
141
|
+
ub = Float::INFINITY
|
142
|
+
when :<=
|
143
|
+
lb = -Float::INFINITY
|
144
|
+
ub = constr.rhs
|
145
|
+
end
|
146
|
+
Cbc.Cbc_setRowLower @ptr, index, lb
|
147
|
+
Cbc.Cbc_setRowUpper @ptr, index, ub
|
148
|
+
end
|
149
|
+
|
150
|
+
# Set the properties of a variable in the model
|
151
|
+
def store_variable(var)
|
152
|
+
# Force the correct bounds since we can't explicitly specify binary
|
153
|
+
if var.type == :binary
|
154
|
+
var.instance_variable_set(:@lower_bound, 0)
|
155
|
+
var.instance_variable_set(:@upper_bound, 1)
|
156
|
+
end
|
157
|
+
|
158
|
+
Cbc.Cbc_setColLower @ptr, var.index, var.lower_bound
|
159
|
+
Cbc.Cbc_setColUpper @ptr, var.index, var.upper_bound
|
160
|
+
Cbc.Cbc_setObjCoeff @ptr, var.index, var.coefficient
|
161
|
+
|
162
|
+
case var.type
|
163
|
+
when :continuous
|
164
|
+
Cbc.Cbc_setContinuous @ptr, var.index
|
165
|
+
when :integer, :binary
|
166
|
+
Cbc.Cbc_setInteger @ptr, var.index
|
167
|
+
else
|
168
|
+
fail :type
|
169
|
+
end
|
170
|
+
|
171
|
+
Cbc.Cbc_setColName(@ptr, var.index, var.name) unless var.name.nil?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/mipper/cbc.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module MIPPeR
|
2
|
+
class Constraint
|
3
|
+
attr_reader :expression, :sense, :rhs, :name
|
4
|
+
|
5
|
+
def initialize(expr, sense, rhs, name = nil)
|
6
|
+
@expression = expr
|
7
|
+
@sense = sense
|
8
|
+
@rhs = rhs
|
9
|
+
@name = name
|
10
|
+
|
11
|
+
# Store this constraint for each associated variable
|
12
|
+
@expression.terms.each_key do |var|
|
13
|
+
var.constraints << self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module MIPPeR
|
2
|
+
class LinExpr
|
3
|
+
attr_reader :terms
|
4
|
+
|
5
|
+
def initialize(terms = {})
|
6
|
+
@terms = terms
|
7
|
+
end
|
8
|
+
|
9
|
+
# Add two {LinExpr}s
|
10
|
+
def +(other)
|
11
|
+
case other
|
12
|
+
when LinExpr
|
13
|
+
# Add the terms of two expressions
|
14
|
+
# For now, this assumes variables are not duplicated
|
15
|
+
LinExpr.new(@terms.merge(other.terms) { |_, c1, c2| c1 + c2 })
|
16
|
+
when Variable
|
17
|
+
# Add the variable to the expression
|
18
|
+
self + other * 1.0
|
19
|
+
else
|
20
|
+
fail TypeError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add terms from the other expression to this one
|
25
|
+
def add(other)
|
26
|
+
case other
|
27
|
+
when LinExpr
|
28
|
+
@terms.merge!(other.terms) { |_, c1, c2| c1 + c2 }
|
29
|
+
when Variable
|
30
|
+
if @terms.key? other
|
31
|
+
@terms[other] += 1.0
|
32
|
+
else
|
33
|
+
@terms[other] = 1.0
|
34
|
+
end
|
35
|
+
else
|
36
|
+
fail TypeError
|
37
|
+
end
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Produce a string representing the expression
|
43
|
+
def inspect
|
44
|
+
@terms.map do |var, coeff|
|
45
|
+
# Skip if the coefficient is zero or the value is zero
|
46
|
+
value = var.value
|
47
|
+
next if coeff == 0 || value == 0 || value == false
|
48
|
+
|
49
|
+
coeff == 1 ? var.name : "#{var.name} * #{coeff}"
|
50
|
+
end.compact.join(' + ')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|