mipper 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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,3 @@
1
+ require_relative 'cbc/ext'
2
+
3
+ require_relative 'cbc/model'
@@ -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