or-tools 0.13.0 → 0.14.0
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +27 -0
- data/ext/or-tools/constraint.cpp +29 -50
- data/ext/or-tools/ext.cpp +2 -0
- data/ext/or-tools/extconf.rb +8 -2
- data/ext/or-tools/linear.cpp +10 -9
- data/ext/or-tools/math_opt.cpp +179 -0
- data/ext/or-tools/routing.cpp +13 -27
- data/ext/or-tools/vendor.rb +3 -0
- data/lib/or-tools.rb +29 -18
- data/lib/or_tools/comparison.rb +7 -10
- data/lib/or_tools/constant.rb +16 -13
- data/lib/or_tools/cp_model.rb +8 -8
- data/lib/or_tools/cp_solver_solution_callback.rb +3 -3
- data/lib/or_tools/expression.rb +85 -0
- data/lib/or_tools/math_opt/model.rb +54 -0
- data/lib/or_tools/math_opt/variable.rb +15 -0
- data/lib/or_tools/product.rb +32 -0
- data/lib/or_tools/solver.rb +28 -15
- data/lib/or_tools/utils.rb +107 -0
- data/lib/or_tools/variable.rb +29 -0
- data/lib/or_tools/version.rb +1 -1
- metadata +11 -14
- data/lib/or_tools/bool_var.rb +0 -9
- data/lib/or_tools/comparison_operators.rb +0 -9
- data/lib/or_tools/int_var.rb +0 -5
- data/lib/or_tools/linear_constraint.rb +0 -50
- data/lib/or_tools/linear_expr.rb +0 -85
- data/lib/or_tools/mp_variable.rb +0 -11
- data/lib/or_tools/product_cst.rb +0 -35
- data/lib/or_tools/sat_int_var.rb +0 -29
- data/lib/or_tools/sat_linear_expr.rb +0 -59
- data/lib/or_tools/sum_array.rb +0 -23
data/lib/or_tools/constant.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
1
|
module ORTools
|
2
|
-
class Constant <
|
3
|
-
|
4
|
-
|
2
|
+
class Constant < Expression
|
3
|
+
attr_reader :value
|
4
|
+
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
5
7
|
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
# simplify Ruby sum
|
10
|
+
def +(other)
|
11
|
+
@value == 0 ? other : super
|
9
12
|
end
|
10
13
|
|
11
|
-
def
|
12
|
-
|
14
|
+
def inspect
|
15
|
+
@value.to_s
|
13
16
|
end
|
14
|
-
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
1
|
18
|
+
def -@
|
19
|
+
Constant.new(-value)
|
19
20
|
end
|
20
|
-
end
|
21
21
|
|
22
|
-
|
22
|
+
def vars
|
23
|
+
@vars ||= []
|
24
|
+
end
|
25
|
+
end
|
23
26
|
end
|
data/lib/or_tools/cp_model.rb
CHANGED
@@ -4,18 +4,18 @@ module ORTools
|
|
4
4
|
case comparison
|
5
5
|
when Comparison
|
6
6
|
method_name =
|
7
|
-
case comparison.
|
8
|
-
when
|
7
|
+
case comparison.op
|
8
|
+
when :==
|
9
9
|
:add_equality
|
10
|
-
when
|
10
|
+
when :!=
|
11
11
|
:add_not_equal
|
12
|
-
when
|
12
|
+
when :>
|
13
13
|
:add_greater_than
|
14
|
-
when
|
14
|
+
when :>=
|
15
15
|
:add_greater_or_equal
|
16
|
-
when
|
16
|
+
when :<
|
17
17
|
:add_less_than
|
18
|
-
when
|
18
|
+
when :<=
|
19
19
|
:add_less_or_equal
|
20
20
|
else
|
21
21
|
raise ArgumentError, "Unknown operator: #{comparison.operator}"
|
@@ -32,7 +32,7 @@ module ORTools
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def sum(arr)
|
35
|
-
|
35
|
+
Expression.new(arr)
|
36
36
|
end
|
37
37
|
|
38
38
|
def inspect
|
@@ -5,16 +5,16 @@ module ORTools
|
|
5
5
|
def value(expr)
|
6
6
|
case expr
|
7
7
|
when SatIntVar
|
8
|
-
@response
|
8
|
+
@response&.solution_integer_value(expr)
|
9
9
|
when BoolVar
|
10
|
-
@response
|
10
|
+
@response&.solution_boolean_value(expr)
|
11
11
|
else
|
12
12
|
raise "Unsupported type: #{expr.class.name}"
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def objective_value
|
17
|
-
@response
|
17
|
+
@response&.objective_value
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ORTools
|
2
|
+
module ExpressionMethods
|
3
|
+
attr_reader :parts
|
4
|
+
|
5
|
+
def +(other)
|
6
|
+
Expression.new((parts || [self]) + [Expression.to_expression(other)])
|
7
|
+
end
|
8
|
+
|
9
|
+
def -(other)
|
10
|
+
Expression.new((parts || [self]) + [-Expression.to_expression(other)])
|
11
|
+
end
|
12
|
+
|
13
|
+
def -@
|
14
|
+
-1 * self
|
15
|
+
end
|
16
|
+
|
17
|
+
def *(other)
|
18
|
+
Expression.new([Product.new(self, Expression.to_expression(other))])
|
19
|
+
end
|
20
|
+
|
21
|
+
def >(other)
|
22
|
+
Comparison.new(self, :>, other)
|
23
|
+
end
|
24
|
+
|
25
|
+
def <(other)
|
26
|
+
Comparison.new(self, :<, other)
|
27
|
+
end
|
28
|
+
|
29
|
+
def >=(other)
|
30
|
+
Comparison.new(self, :>=, other)
|
31
|
+
end
|
32
|
+
|
33
|
+
def <=(other)
|
34
|
+
Comparison.new(self, :<=, other)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other)
|
38
|
+
Comparison.new(self, :==, other)
|
39
|
+
end
|
40
|
+
|
41
|
+
def !=(other)
|
42
|
+
Comparison.new(self, :!=, other)
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
@parts.reject { |v| v.is_a?(Constant) && v.value == 0 }.map(&:inspect).join(" + ").gsub(" + -", " - ")
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
# keep order
|
54
|
+
def coerce(other)
|
55
|
+
if other.is_a?(Numeric)
|
56
|
+
[Constant.new(other), self]
|
57
|
+
else
|
58
|
+
raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def vars
|
63
|
+
@vars ||= @parts.flat_map(&:vars)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Expression
|
68
|
+
include ExpressionMethods
|
69
|
+
|
70
|
+
def initialize(parts = [])
|
71
|
+
@parts = parts
|
72
|
+
end
|
73
|
+
|
74
|
+
# private
|
75
|
+
def self.to_expression(other)
|
76
|
+
if other.is_a?(Numeric)
|
77
|
+
Constant.new(other)
|
78
|
+
elsif other.is_a?(Variable) || other.is_a?(Expression)
|
79
|
+
other
|
80
|
+
else
|
81
|
+
raise TypeError, "can't cast #{other.class.name} to Expression"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ORTools
|
2
|
+
module MathOpt
|
3
|
+
class Model
|
4
|
+
def add_linear_constraint(expr)
|
5
|
+
left, op, const = Utils.index_constraint(expr)
|
6
|
+
|
7
|
+
constraint = _add_linear_constraint
|
8
|
+
left.each do |var, c|
|
9
|
+
_set_coefficient(constraint, var, c)
|
10
|
+
end
|
11
|
+
case op
|
12
|
+
when :<=
|
13
|
+
_set_upper_bound(constraint, const)
|
14
|
+
when :>=
|
15
|
+
_set_lower_bound(constraint, const)
|
16
|
+
when :==
|
17
|
+
_set_lower_bound(constraint, const)
|
18
|
+
_set_upper_bound(constraint, const)
|
19
|
+
else
|
20
|
+
raise ArgumentError, "Supported operations are ==, <=, and >="
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def maximize(objective)
|
26
|
+
set_objective(objective)
|
27
|
+
_set_maximize
|
28
|
+
end
|
29
|
+
|
30
|
+
def minimize(objective)
|
31
|
+
set_objective(objective)
|
32
|
+
_set_minimize
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO change default for MIP
|
36
|
+
def solve(solver_type = :glop)
|
37
|
+
_solve(solver_type)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def set_objective(objective)
|
43
|
+
objective = Expression.to_expression(objective)
|
44
|
+
coeffs = Utils.index_expression(objective, check_linear: true)
|
45
|
+
offset = coeffs.delete(nil)
|
46
|
+
|
47
|
+
objective.set_offset(offset) if offset
|
48
|
+
coeffs.each do |var, c|
|
49
|
+
_set_objective_coefficient(var, c)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ORTools
|
2
|
+
class Product < Expression
|
3
|
+
attr_reader :left, :right
|
4
|
+
|
5
|
+
def initialize(left, right)
|
6
|
+
@left = left
|
7
|
+
@right = right
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
if @left.is_a?(Constant) && @right.is_a?(Variable) && left.value == -1
|
12
|
+
"-#{inspect_part(@right)}"
|
13
|
+
else
|
14
|
+
"#{inspect_part(@left)} * #{inspect_part(@right)}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def vars
|
19
|
+
@vars ||= (@left.vars + @right.vars).uniq
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def inspect_part(var)
|
25
|
+
if var.instance_of?(Expression)
|
26
|
+
"(#{var.inspect})"
|
27
|
+
else
|
28
|
+
var.inspect
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/or_tools/solver.rb
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
module ORTools
|
2
2
|
class Solver
|
3
3
|
def sum(arr)
|
4
|
-
|
4
|
+
Expression.new(arr)
|
5
5
|
end
|
6
6
|
|
7
7
|
def add(expr)
|
8
|
-
|
8
|
+
left, op, const = Utils.index_constraint(expr)
|
9
|
+
|
10
|
+
constraint =
|
11
|
+
case op
|
12
|
+
when :<=
|
13
|
+
self.constraint(-infinity, const)
|
14
|
+
when :>=
|
15
|
+
self.constraint(const, infinity)
|
16
|
+
when :==
|
17
|
+
self.constraint(const, const)
|
18
|
+
else
|
19
|
+
raise ArgumentError, "Supported operations are ==, <=, and >="
|
20
|
+
end
|
21
|
+
left.each do |var, c|
|
22
|
+
constraint.set_coefficient(var, c)
|
23
|
+
end
|
24
|
+
nil
|
9
25
|
end
|
10
26
|
|
11
27
|
def maximize(expr)
|
@@ -21,25 +37,22 @@ module ORTools
|
|
21
37
|
private
|
22
38
|
|
23
39
|
def set_objective(expr)
|
40
|
+
coeffs = Utils.index_expression(expr, check_linear: true)
|
41
|
+
offset = coeffs.delete(nil)
|
42
|
+
|
24
43
|
objective.clear
|
25
|
-
coeffs = expr.coeffs
|
26
|
-
offset = coeffs.delete(OFFSET_KEY)
|
27
44
|
objective.set_offset(offset) if offset
|
28
|
-
coeffs.each do |
|
29
|
-
objective.set_coefficient(
|
45
|
+
coeffs.each do |var, c|
|
46
|
+
objective.set_coefficient(var, c)
|
30
47
|
end
|
31
48
|
end
|
32
49
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
else
|
39
|
-
super
|
40
|
-
end
|
50
|
+
def self.new(solver_id, *args)
|
51
|
+
if args.empty?
|
52
|
+
_create(solver_id)
|
53
|
+
else
|
54
|
+
_new(solver_id, *args)
|
41
55
|
end
|
42
56
|
end
|
43
|
-
singleton_class.prepend(m)
|
44
57
|
end
|
45
58
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module ORTools
|
2
|
+
module Utils
|
3
|
+
def self.index_constraint(constraint)
|
4
|
+
raise ArgumentError, "Expected Comparison" unless constraint.is_a?(Comparison)
|
5
|
+
|
6
|
+
left = index_expression(constraint.left, check_linear: true)
|
7
|
+
right = index_expression(constraint.right, check_linear: true)
|
8
|
+
|
9
|
+
const = right.delete(nil).to_f - left.delete(nil).to_f
|
10
|
+
right.each do |k, v|
|
11
|
+
left[k] -= v
|
12
|
+
end
|
13
|
+
|
14
|
+
[left, constraint.op, const]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.index_expression(expression, check_linear: true)
|
18
|
+
vars = Hash.new(0)
|
19
|
+
case expression
|
20
|
+
when Numeric
|
21
|
+
vars[nil] += expression
|
22
|
+
when Constant
|
23
|
+
vars[nil] += expression.value
|
24
|
+
when Variable
|
25
|
+
vars[expression] += 1
|
26
|
+
when Product
|
27
|
+
if check_linear && expression.left.vars.any? && expression.right.vars.any?
|
28
|
+
raise ArgumentError, "Nonlinear"
|
29
|
+
end
|
30
|
+
vars = index_product(expression.left, expression.right)
|
31
|
+
when Expression
|
32
|
+
expression.parts.each do |part|
|
33
|
+
index_expression(part, check_linear: check_linear).each do |k, v|
|
34
|
+
vars[k] += v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise TypeError, "Unsupported type"
|
39
|
+
end
|
40
|
+
vars
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.index_product(left, right)
|
44
|
+
# normalize
|
45
|
+
types = [Constant, Variable, Product, Expression]
|
46
|
+
if types.index { |t| left.is_a?(t) } > types.index { |t| right.is_a?(t) }
|
47
|
+
left, right = right, left
|
48
|
+
end
|
49
|
+
|
50
|
+
vars = Hash.new(0)
|
51
|
+
case left
|
52
|
+
when Constant
|
53
|
+
vars = index_expression(right)
|
54
|
+
vars.transform_values! { |v| v * left.value }
|
55
|
+
when Variable
|
56
|
+
case right
|
57
|
+
when Variable
|
58
|
+
vars[quad_key(left, right)] = 1
|
59
|
+
when Product
|
60
|
+
index_expression(right).each do |k, v|
|
61
|
+
case k
|
62
|
+
when Array
|
63
|
+
raise Error, "Non-quadratic"
|
64
|
+
when Variable
|
65
|
+
vars[quad_key(left, k)] = v
|
66
|
+
else # nil
|
67
|
+
raise "Bug?"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
right.parts.each do |part|
|
72
|
+
index_product(left, part).each do |k, v|
|
73
|
+
vars[k] += v
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
when Product
|
78
|
+
index_expression(left).each do |lk, lv|
|
79
|
+
index_expression(right).each do |rk, rv|
|
80
|
+
if lk.is_a?(Variable) && rk.is_a?(Variable)
|
81
|
+
vars[quad_key(lk, rk)] = lv * rv
|
82
|
+
else
|
83
|
+
raise "todo"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
else # Expression
|
88
|
+
left.parts.each do |lp|
|
89
|
+
right.parts.each do |rp|
|
90
|
+
index_product(lp, rp).each do |k, v|
|
91
|
+
vars[k] += v
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
vars
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.quad_key(left, right)
|
100
|
+
if left.object_id <= right.object_id
|
101
|
+
[left, right]
|
102
|
+
else
|
103
|
+
[right, left]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ORTools
|
2
|
+
module Variable
|
3
|
+
include ExpressionMethods
|
4
|
+
|
5
|
+
def inspect
|
6
|
+
name
|
7
|
+
end
|
8
|
+
|
9
|
+
def vars
|
10
|
+
@vars ||= [self]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class MPVariable
|
15
|
+
include Variable
|
16
|
+
end
|
17
|
+
|
18
|
+
class SatIntVar
|
19
|
+
include Variable
|
20
|
+
end
|
21
|
+
|
22
|
+
class SatBoolVar
|
23
|
+
include Variable
|
24
|
+
end
|
25
|
+
|
26
|
+
class RoutingIntVar
|
27
|
+
include Variable
|
28
|
+
end
|
29
|
+
end
|
data/lib/or_tools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: or-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rice
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 4.3.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.3.3
|
27
27
|
description:
|
28
28
|
email: andrew@ankane.org
|
29
29
|
executables: []
|
@@ -42,36 +42,33 @@ files:
|
|
42
42
|
- ext/or-tools/ext.h
|
43
43
|
- ext/or-tools/extconf.rb
|
44
44
|
- ext/or-tools/linear.cpp
|
45
|
+
- ext/or-tools/math_opt.cpp
|
45
46
|
- ext/or-tools/network_flows.cpp
|
46
47
|
- ext/or-tools/routing.cpp
|
47
48
|
- ext/or-tools/vendor.rb
|
48
49
|
- lib/or-tools.rb
|
49
50
|
- lib/or_tools/basic_scheduler.rb
|
50
|
-
- lib/or_tools/bool_var.rb
|
51
51
|
- lib/or_tools/comparison.rb
|
52
|
-
- lib/or_tools/comparison_operators.rb
|
53
52
|
- lib/or_tools/constant.rb
|
54
53
|
- lib/or_tools/cp_model.rb
|
55
54
|
- lib/or_tools/cp_solver.rb
|
56
55
|
- lib/or_tools/cp_solver_solution_callback.rb
|
57
|
-
- lib/or_tools/
|
56
|
+
- lib/or_tools/expression.rb
|
58
57
|
- lib/or_tools/knapsack_solver.rb
|
59
|
-
- lib/or_tools/
|
60
|
-
- lib/or_tools/
|
61
|
-
- lib/or_tools/mp_variable.rb
|
58
|
+
- lib/or_tools/math_opt/model.rb
|
59
|
+
- lib/or_tools/math_opt/variable.rb
|
62
60
|
- lib/or_tools/objective_solution_printer.rb
|
63
|
-
- lib/or_tools/
|
61
|
+
- lib/or_tools/product.rb
|
64
62
|
- lib/or_tools/routing_index_manager.rb
|
65
63
|
- lib/or_tools/routing_model.rb
|
66
|
-
- lib/or_tools/sat_int_var.rb
|
67
|
-
- lib/or_tools/sat_linear_expr.rb
|
68
64
|
- lib/or_tools/seating.rb
|
69
65
|
- lib/or_tools/solver.rb
|
70
66
|
- lib/or_tools/sudoku.rb
|
71
|
-
- lib/or_tools/sum_array.rb
|
72
67
|
- lib/or_tools/tsp.rb
|
68
|
+
- lib/or_tools/utils.rb
|
73
69
|
- lib/or_tools/var_array_and_objective_solution_printer.rb
|
74
70
|
- lib/or_tools/var_array_solution_printer.rb
|
71
|
+
- lib/or_tools/variable.rb
|
75
72
|
- lib/or_tools/version.rb
|
76
73
|
homepage: https://github.com/ankane/or-tools-ruby
|
77
74
|
licenses:
|
data/lib/or_tools/bool_var.rb
DELETED
data/lib/or_tools/int_var.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
module ORTools
|
2
|
-
class LinearConstraint
|
3
|
-
attr_reader :expr, :lb, :ub
|
4
|
-
|
5
|
-
def initialize(expr, lb, ub)
|
6
|
-
@expr = expr
|
7
|
-
@lb = lb
|
8
|
-
@ub = ub
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_s
|
12
|
-
if @lb > -Float::INFINITY && @ub < Float::INFINITY
|
13
|
-
if @lb == @ub
|
14
|
-
"#{@expr} == #{@lb}"
|
15
|
-
else
|
16
|
-
"#{@lb} <= #{@expr} <= #{@ub}"
|
17
|
-
end
|
18
|
-
elsif @lb > -Float::INFINITY
|
19
|
-
"#{@expr} >= #{@lb}"
|
20
|
-
elsif @ub < Float::INFINITY
|
21
|
-
"#{@expr} <= #{@ub}"
|
22
|
-
else
|
23
|
-
"Trivial inequality (always true)"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def inspect
|
28
|
-
"#<#{self.class.name} #{to_s}>"
|
29
|
-
end
|
30
|
-
|
31
|
-
def extract(solver)
|
32
|
-
coeffs = @expr.coeffs
|
33
|
-
constant = coeffs.delete(OFFSET_KEY) || 0.0
|
34
|
-
lb = -solver.infinity
|
35
|
-
ub = solver.infinity
|
36
|
-
if @lb > -Float::INFINITY
|
37
|
-
lb = @lb - constant
|
38
|
-
end
|
39
|
-
if @ub < Float::INFINITY
|
40
|
-
ub = @ub - constant
|
41
|
-
end
|
42
|
-
|
43
|
-
constraint = solver.constraint(lb, ub)
|
44
|
-
coeffs.each do |v, c|
|
45
|
-
constraint.set_coefficient(v, c.to_f)
|
46
|
-
end
|
47
|
-
constraint
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|