mipper 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|