or-tools 0.13.1 → 0.14.1
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 +10 -0
- data/README.md +34 -7
- data/ext/or-tools/constraint.cpp +30 -50
- data/ext/or-tools/ext.cpp +2 -0
- data/ext/or-tools/extconf.rb +8 -2
- data/ext/or-tools/linear.cpp +100 -12
- data/ext/or-tools/math_opt.cpp +180 -0
- data/ext/or-tools/routing.cpp +13 -27
- 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 +33 -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 +12 -15
- 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/ext/or-tools/routing.cpp
CHANGED
@@ -56,22 +56,9 @@ namespace Rice::detail
|
|
56
56
|
};
|
57
57
|
}
|
58
58
|
|
59
|
-
namespace Rice::detail
|
60
|
-
{
|
61
|
-
template<class T, class U>
|
62
|
-
class To_Ruby<std::pair<T, U>>
|
63
|
-
{
|
64
|
-
public:
|
65
|
-
VALUE convert(std::pair<T, U> const & x)
|
66
|
-
{
|
67
|
-
return rb_ary_new3(2, To_Ruby<T>().convert(x.first), To_Ruby<U>().convert(x.second));
|
68
|
-
}
|
69
|
-
};
|
70
|
-
}
|
71
|
-
|
72
59
|
void init_routing(Rice::Module& m) {
|
73
60
|
auto rb_cRoutingSearchParameters = Rice::define_class_under<RoutingSearchParameters>(m, "RoutingSearchParameters");
|
74
|
-
auto rb_cIntVar = Rice::define_class_under<operations_research::IntVar>(m, "
|
61
|
+
auto rb_cIntVar = Rice::define_class_under<operations_research::IntVar>(m, "RoutingIntVar");
|
75
62
|
|
76
63
|
m.define_singleton_function("default_routing_search_parameters", &DefaultRoutingSearchParameters);
|
77
64
|
|
@@ -213,7 +200,6 @@ void init_routing(Rice::Module& m) {
|
|
213
200
|
|
214
201
|
Rice::define_class_under<RoutingDimension>(m, "RoutingDimension")
|
215
202
|
.define_method("transit_value", &RoutingDimension::GetTransitValue)
|
216
|
-
// TODO GetTransitValueFromClass
|
217
203
|
.define_method("cumul_var", &RoutingDimension::CumulVar)
|
218
204
|
.define_method("transit_var", &RoutingDimension::TransitVar)
|
219
205
|
.define_method("fixed_transit_var", &RoutingDimension::FixedTransitVar)
|
@@ -239,7 +225,7 @@ void init_routing(Rice::Module& m) {
|
|
239
225
|
.define_method("post", &operations_research::Constraint::Post)
|
240
226
|
.define_method("debug_string", &operations_research::Constraint::DebugString);
|
241
227
|
|
242
|
-
Rice::define_class_under<operations_research::Solver>(m, "
|
228
|
+
Rice::define_class_under<operations_research::Solver>(m, "RoutingSolver")
|
243
229
|
.define_method(
|
244
230
|
"add",
|
245
231
|
[](operations_research::Solver& self, Object o) {
|
@@ -247,7 +233,7 @@ void init_routing(Rice::Module& m) {
|
|
247
233
|
if (o.respond_to("left")) {
|
248
234
|
operations_research::IntExpr* left(Rice::detail::From_Ruby<operations_research::IntVar*>().convert(o.call("left")));
|
249
235
|
operations_research::IntExpr* right(Rice::detail::From_Ruby<operations_research::IntVar*>().convert(o.call("right")));
|
250
|
-
auto op = o.call("
|
236
|
+
auto op = o.call("op").to_s().str();
|
251
237
|
if (op == "==") {
|
252
238
|
constraint = self.MakeEquality(left, right);
|
253
239
|
} else if (op == "<=") {
|
@@ -304,8 +290,13 @@ void init_routing(Rice::Module& m) {
|
|
304
290
|
.define_method(
|
305
291
|
"register_unary_transit_callback",
|
306
292
|
[](RoutingModel& self, Object callback) {
|
293
|
+
// TODO guard callback?
|
307
294
|
return self.RegisterUnaryTransitCallback(
|
308
295
|
[callback](int64_t from_index) -> int64_t {
|
296
|
+
if (!ruby_native_thread_p()) {
|
297
|
+
throw std::runtime_error("Non-Ruby thread");
|
298
|
+
}
|
299
|
+
|
309
300
|
return Rice::detail::From_Ruby<int64_t>().convert(callback.call("call", from_index));
|
310
301
|
}
|
311
302
|
);
|
@@ -314,8 +305,13 @@ void init_routing(Rice::Module& m) {
|
|
314
305
|
.define_method(
|
315
306
|
"register_transit_callback",
|
316
307
|
[](RoutingModel& self, Object callback) {
|
308
|
+
// TODO guard callback?
|
317
309
|
return self.RegisterTransitCallback(
|
318
310
|
[callback](int64_t from_index, int64_t to_index) -> int64_t {
|
311
|
+
if (!ruby_native_thread_p()) {
|
312
|
+
throw std::runtime_error("Non-Ruby thread");
|
313
|
+
}
|
314
|
+
|
319
315
|
return Rice::detail::From_Ruby<int64_t>().convert(callback.call("call", from_index, to_index));
|
320
316
|
}
|
321
317
|
);
|
@@ -328,13 +324,8 @@ void init_routing(Rice::Module& m) {
|
|
328
324
|
.define_method("add_constant_dimension", &RoutingModel::AddConstantDimension)
|
329
325
|
.define_method("add_vector_dimension", &RoutingModel::AddVectorDimension)
|
330
326
|
.define_method("add_matrix_dimension", &RoutingModel::AddMatrixDimension)
|
331
|
-
// TODO AddDimensionDependentDimensionWithVehicleCapacity
|
332
|
-
// .define_method("make_path_spans_and_total_slacks", &RoutingModel::MakePathSpansAndTotalSlacks)
|
333
327
|
.define_method("all_dimension_names", &RoutingModel::GetAllDimensionNames)
|
334
|
-
// .define_method("dimensions", &RoutingModel::GetDimensions)
|
335
|
-
// .define_method("dimensions_with_soft_or_span_costs", &RoutingModel::GetDimensionsWithSoftOrSpanCosts)
|
336
328
|
.define_method("dimension?", &RoutingModel::HasDimension)
|
337
|
-
// .define_method("dimension_or_die", &RoutingModel::GetDimensionOrDie)
|
338
329
|
.define_method("mutable_dimension", &RoutingModel::GetMutableDimension)
|
339
330
|
.define_method("set_primary_constrained_dimension", &RoutingModel::SetPrimaryConstrainedDimension)
|
340
331
|
.define_method("primary_constrained_dimension", &RoutingModel::GetPrimaryConstrainedDimension)
|
@@ -373,9 +364,6 @@ void init_routing(Rice::Module& m) {
|
|
373
364
|
}
|
374
365
|
return positions;
|
375
366
|
})
|
376
|
-
// TODO SetPickupAndDeliveryPolicyOfAllVehicles
|
377
|
-
// TODO SetPickupAndDeliveryPolicyOfVehicle
|
378
|
-
// TODO GetPickupAndDeliveryPolicyOfVehicle
|
379
367
|
.define_method("num_of_singleton_nodes", &RoutingModel::GetNumOfSingletonNodes)
|
380
368
|
.define_method("unperformed_penalty", &RoutingModel::UnperformedPenalty)
|
381
369
|
.define_method("unperformed_penalty_or_value", &RoutingModel::UnperformedPenaltyOrValue)
|
@@ -444,8 +432,6 @@ void init_routing(Rice::Module& m) {
|
|
444
432
|
.define_method("compact_and_check_assignment", &RoutingModel::CompactAndCheckAssignment)
|
445
433
|
.define_method("add_to_assignment", &RoutingModel::AddToAssignment)
|
446
434
|
.define_method("add_interval_to_assignment", &RoutingModel::AddIntervalToAssignment)
|
447
|
-
// TODO PackCumulsOfOptimizerDimensionsFromAssignment
|
448
|
-
// TODO AddLocalSearchFilter
|
449
435
|
.define_method("start", &RoutingModel::Start)
|
450
436
|
.define_method("end", &RoutingModel::End)
|
451
437
|
.define_method("start?", &RoutingModel::IsStart)
|
data/lib/or-tools.rb
CHANGED
@@ -1,39 +1,50 @@
|
|
1
1
|
# ext
|
2
2
|
require "or_tools/ext"
|
3
3
|
|
4
|
-
#
|
4
|
+
# expressions
|
5
|
+
require_relative "or_tools/expression"
|
5
6
|
require_relative "or_tools/comparison"
|
6
|
-
require_relative "or_tools/comparison_operators"
|
7
|
-
require_relative "or_tools/bool_var"
|
8
7
|
require_relative "or_tools/constant"
|
8
|
+
require_relative "or_tools/product"
|
9
|
+
require_relative "or_tools/variable"
|
10
|
+
|
11
|
+
# bin packing
|
12
|
+
require_relative "or_tools/knapsack_solver"
|
13
|
+
|
14
|
+
# constraint
|
9
15
|
require_relative "or_tools/cp_model"
|
10
16
|
require_relative "or_tools/cp_solver"
|
11
17
|
require_relative "or_tools/cp_solver_solution_callback"
|
12
|
-
require_relative "or_tools/int_var"
|
13
|
-
require_relative "or_tools/knapsack_solver"
|
14
|
-
require_relative "or_tools/linear_constraint"
|
15
|
-
require_relative "or_tools/linear_expr"
|
16
|
-
require_relative "or_tools/mp_variable"
|
17
|
-
require_relative "or_tools/product_cst"
|
18
|
-
require_relative "or_tools/routing_index_manager"
|
19
|
-
require_relative "or_tools/routing_model"
|
20
|
-
require_relative "or_tools/sat_linear_expr"
|
21
|
-
require_relative "or_tools/sat_int_var"
|
22
|
-
require_relative "or_tools/solver"
|
23
|
-
require_relative "or_tools/sum_array"
|
24
|
-
require_relative "or_tools/version"
|
25
|
-
|
26
|
-
# solution printers
|
27
18
|
require_relative "or_tools/objective_solution_printer"
|
28
19
|
require_relative "or_tools/var_array_solution_printer"
|
29
20
|
require_relative "or_tools/var_array_and_objective_solution_printer"
|
30
21
|
|
22
|
+
# linear
|
23
|
+
require_relative "or_tools/solver"
|
24
|
+
|
25
|
+
# math opt
|
26
|
+
require_relative "or_tools/math_opt/model"
|
27
|
+
require_relative "or_tools/math_opt/variable"
|
28
|
+
|
29
|
+
# routing
|
30
|
+
require_relative "or_tools/routing_index_manager"
|
31
|
+
require_relative "or_tools/routing_model"
|
32
|
+
|
31
33
|
# higher level interfaces
|
32
34
|
require_relative "or_tools/basic_scheduler"
|
33
35
|
require_relative "or_tools/seating"
|
34
36
|
require_relative "or_tools/sudoku"
|
35
37
|
require_relative "or_tools/tsp"
|
36
38
|
|
39
|
+
# modules
|
40
|
+
require_relative "or_tools/utils"
|
41
|
+
require_relative "or_tools/version"
|
42
|
+
|
37
43
|
module ORTools
|
38
44
|
class Error < StandardError; end
|
45
|
+
|
46
|
+
# previous names
|
47
|
+
Solver2 = RoutingSolver
|
48
|
+
IntVar = RoutingIntVar
|
49
|
+
BoolVar = SatBoolVar
|
39
50
|
end
|
data/lib/or_tools/comparison.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
module ORTools
|
2
2
|
class Comparison
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :left, :op, :right
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@right = right
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_s
|
12
|
-
"#{left} #{operator} #{right}"
|
5
|
+
def initialize(left, op, right)
|
6
|
+
@left = Expression.to_expression(left)
|
7
|
+
@op = op
|
8
|
+
@right = Expression.to_expression(right)
|
13
9
|
end
|
14
10
|
|
15
11
|
def inspect
|
16
|
-
"
|
12
|
+
"#{@left.inspect} #{@op} #{@right.inspect}"
|
17
13
|
end
|
14
|
+
alias_method :to_s, :inspect
|
18
15
|
end
|
19
16
|
end
|
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)
|
@@ -18,28 +34,30 @@ module ORTools
|
|
18
34
|
objective.set_minimization
|
19
35
|
end
|
20
36
|
|
37
|
+
def solve(parameters = nil)
|
38
|
+
parameters ||= MPSolverParameters.new
|
39
|
+
_solve(parameters)
|
40
|
+
end
|
41
|
+
|
21
42
|
private
|
22
43
|
|
23
44
|
def set_objective(expr)
|
45
|
+
coeffs = Utils.index_expression(expr, check_linear: true)
|
46
|
+
offset = coeffs.delete(nil)
|
47
|
+
|
24
48
|
objective.clear
|
25
|
-
coeffs = expr.coeffs
|
26
|
-
offset = coeffs.delete(OFFSET_KEY)
|
27
49
|
objective.set_offset(offset) if offset
|
28
|
-
coeffs.each do |
|
29
|
-
objective.set_coefficient(
|
50
|
+
coeffs.each do |var, c|
|
51
|
+
objective.set_coefficient(var, c)
|
30
52
|
end
|
31
53
|
end
|
32
54
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
else
|
39
|
-
super
|
40
|
-
end
|
55
|
+
def self.new(solver_id, *args)
|
56
|
+
if args.empty?
|
57
|
+
_create(solver_id)
|
58
|
+
else
|
59
|
+
_new(solver_id, *args)
|
41
60
|
end
|
42
61
|
end
|
43
|
-
singleton_class.prepend(m)
|
44
62
|
end
|
45
63
|
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
|