or-tools 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c21f77bdb14226464938b4407e6399e8d60247b519df13ae3671643eef4ca47
4
- data.tar.gz: e056e127a5ffae6aba598210df051e7d5bee7484b1415415054464cc115a0742
3
+ metadata.gz: 3d0010ac9fa141621f67cee4eb00ba077a5bafcec58c65202d477c1576ba797e
4
+ data.tar.gz: '0328a9b9f0fa296df875e0756b40dc16fdbf756725d5ecc4e3bc23c600245974'
5
5
  SHA512:
6
- metadata.gz: aab9c9e218da811b49805587642e19432d3a5356eb3a5bb75aacea7f2ef8a4370ae8a2d7821d34f15a728a6c03710e50bd10f21f2531bb2309d4b9e953a9efbb
7
- data.tar.gz: 79cfe7ee894e35272d3cf0f09f936e66ced59d84b9c6ce2096434ac30887db4c95f6836b12387859f2e089efabea5cb9fa8bdee2d488b00a56da2f5328f6dab2
6
+ metadata.gz: dd66a479521af3f036ed14f639f957cd6b608efed729a4e7ed1eb5fde749fd779ec471750b93ab5d48e0162116e9865c0763355fd72b610a0b74cf6747dad220
7
+ data.tar.gz: 41af8a12b7aa110baae1f62029c4bde6e291ea1b870f9435ba3463a1a9e52d41dc069ef049671a95802d3942e29402f7096709c6a8fd659382a372923e03002a
@@ -1,3 +1,8 @@
1
+ ## 0.1.2 (2020-02-18)
2
+
3
+ - Added support for scheduling
4
+ - Added `lib_version` method
5
+
1
6
  ## 0.1.1 (2020-02-16)
2
7
 
3
8
  - Added `RoutingModel`
data/README.md CHANGED
@@ -31,7 +31,7 @@ Constraint Optimization
31
31
 
32
32
  Integer Optimization
33
33
 
34
- - [Mixed-Integer Programming](mixed-integer-programming)
34
+ - [Mixed-Integer Programming](#mixed-integer-programming)
35
35
 
36
36
  Routing
37
37
 
@@ -56,6 +56,10 @@ Assignment
56
56
  - [Assignment as a Min Cost Problem](#assignment-as-a-min-cost-problem)
57
57
  - [Assignment as a MIP Problem](#assignment-as-a-mip-problem)
58
58
 
59
+ Scheduling
60
+
61
+ - [Employee Scheduling](#employee-scheduling)
62
+
59
63
  ### The Glop Linear Solver
60
64
 
61
65
  [Guide](https://developers.google.com/optimization/lp/glop)
@@ -964,6 +968,123 @@ puts
964
968
  puts "Time = #{solver.wall_time} milliseconds"
965
969
  ```
966
970
 
971
+ ## Employee Scheduling
972
+
973
+ [Guide](https://developers.google.com/optimization/scheduling/employee_scheduling)
974
+
975
+ Define the data
976
+
977
+ ```ruby
978
+ num_nurses = 4
979
+ num_shifts = 3
980
+ num_days = 3
981
+ all_nurses = num_nurses.times.to_a
982
+ all_shifts = num_shifts.times.to_a
983
+ all_days = num_days.times.to_a
984
+ ```
985
+
986
+ Create the variables
987
+
988
+ ```ruby
989
+ model = ORTools::CpModel.new
990
+
991
+ shifts = {}
992
+ all_nurses.each do |n|
993
+ all_days.each do |d|
994
+ all_shifts.each do |s|
995
+ shifts[[n, d, s]] = model.new_bool_var("shift_n%id%is%i" % [n, d, s])
996
+ end
997
+ end
998
+ end
999
+ ```
1000
+
1001
+ Assign nurses to shifts
1002
+
1003
+ ```ruby
1004
+ all_days.each do |d|
1005
+ all_shifts.each do |s|
1006
+ model.add(model.sum(all_nurses.map { |n| shifts[[n, d, s]] }) == 1)
1007
+ end
1008
+ end
1009
+
1010
+ all_nurses.each do |n|
1011
+ all_days.each do |d|
1012
+ model.add(model.sum(all_shifts.map { |s| shifts[[n, d, s]] }) <= 1)
1013
+ end
1014
+ end
1015
+ ```
1016
+
1017
+ Assign shifts evenly
1018
+
1019
+ ```ruby
1020
+ min_shifts_per_nurse = (num_shifts * num_days) / num_nurses
1021
+ max_shifts_per_nurse = min_shifts_per_nurse + 1
1022
+ all_nurses.each do |n|
1023
+ num_shifts_worked = model.sum(all_days.flat_map { |d| all_shifts.map { |s| shifts[[n, d, s]] } })
1024
+ model.add(num_shifts_worked >= min_shifts_per_nurse)
1025
+ model.add(num_shifts_worked <= max_shifts_per_nurse)
1026
+ end
1027
+ ```
1028
+
1029
+ Create a printer
1030
+
1031
+ ```ruby
1032
+ class NursesPartialSolutionPrinter < ORTools::CpSolverSolutionCallback
1033
+ attr_reader :solution_count
1034
+
1035
+ def initialize(shifts, num_nurses, num_days, num_shifts, sols)
1036
+ super()
1037
+ @shifts = shifts
1038
+ @num_nurses = num_nurses
1039
+ @num_days = num_days
1040
+ @num_shifts = num_shifts
1041
+ @solutions = sols
1042
+ @solution_count = 0
1043
+ end
1044
+
1045
+ def on_solution_callback
1046
+ if @solutions.include?(@solution_count)
1047
+ puts "Solution #{@solution_count}"
1048
+ @num_days.times do |d|
1049
+ puts "Day #{d}"
1050
+ @num_nurses.times do |n|
1051
+ working = false
1052
+ @num_shifts.times do |s|
1053
+ if value(@shifts[[n, d, s]])
1054
+ working = true
1055
+ puts " Nurse %i works shift %i" % [n, s]
1056
+ end
1057
+ end
1058
+ unless working
1059
+ puts " Nurse #{n} does not work"
1060
+ end
1061
+ end
1062
+ end
1063
+ puts
1064
+ end
1065
+ @solution_count += 1
1066
+ end
1067
+ end
1068
+ ```
1069
+
1070
+ Call the solver and display the results
1071
+
1072
+ ```ruby
1073
+ solver = ORTools::CpSolver.new
1074
+ a_few_solutions = 5.times.to_a
1075
+ solution_printer = NursesPartialSolutionPrinter.new(
1076
+ shifts, num_nurses, num_days, num_shifts, a_few_solutions
1077
+ )
1078
+ solver.search_for_all_solutions(model, solution_printer)
1079
+
1080
+ puts
1081
+ puts "Statistics"
1082
+ puts " - conflicts : %i" % solver.num_conflicts
1083
+ puts " - branches : %i" % solver.num_branches
1084
+ puts " - wall time : %f s" % solver.wall_time
1085
+ puts " - solutions found : %i" % solution_printer.solution_count
1086
+ ```
1087
+
967
1088
  ## History
968
1089
 
969
1090
  View the [changelog](https://github.com/ankane/or-tools/blob/master/CHANGELOG.md)
@@ -1,5 +1,6 @@
1
1
  // or-tools
2
2
  #include <ortools/algorithms/knapsack_solver.h>
3
+ #include <ortools/base/version.h>
3
4
  #include <ortools/constraint_solver/routing.h>
4
5
  #include <ortools/constraint_solver/routing_parameters.h>
5
6
  #include <ortools/graph/assignment.h>
@@ -14,6 +15,7 @@
14
15
  #include <rice/Constructor.hpp>
15
16
  #include <rice/Hash.hpp>
16
17
  #include <rice/Module.hpp>
18
+ #include <rice/String.hpp>
17
19
  #include <rice/Symbol.hpp>
18
20
 
19
21
  using operations_research::ArcIndex;
@@ -43,6 +45,8 @@ using operations_research::sat::BoolVar;
43
45
  using operations_research::sat::CpModelBuilder;
44
46
  using operations_research::sat::CpSolverResponse;
45
47
  using operations_research::sat::CpSolverStatus;
48
+ using operations_research::sat::NewFeasibleSolutionObserver;
49
+ using operations_research::sat::SatParameters;
46
50
  using operations_research::sat::SolutionIntegerValue;
47
51
 
48
52
  using Rice::Array;
@@ -51,6 +55,7 @@ using Rice::Constructor;
51
55
  using Rice::Hash;
52
56
  using Rice::Module;
53
57
  using Rice::Object;
58
+ using Rice::String;
54
59
  using Rice::Symbol;
55
60
  using Rice::define_module;
56
61
  using Rice::define_class_under;
@@ -111,7 +116,7 @@ operations_research::sat::LinearExpr from_ruby<operations_research::sat::LinearE
111
116
  // TODO clean up
112
117
  Object o = cvar[0];
113
118
  if (((Rice::String) o.call("class").call("name")).str() == "ORTools::BoolVar") {
114
- expr.AddTerm(from_ruby<operations_research::sat::BoolVar>(cvar[0]), from_ruby<int64>(cvar[1]));
119
+ expr.AddVar(from_ruby<operations_research::sat::BoolVar>(cvar[0]));
115
120
  } else {
116
121
  expr.AddTerm(from_ruby<operations_research::sat::IntVar>(cvar[0]), from_ruby<int64>(cvar[1]));
117
122
  }
@@ -183,7 +188,13 @@ extern "C"
183
188
  void Init_ext()
184
189
  {
185
190
  Module rb_mORTools = define_module("ORTools")
186
- .define_singleton_method("default_routing_search_parameters", &DefaultRoutingSearchParameters);
191
+ .define_singleton_method("default_routing_search_parameters", &DefaultRoutingSearchParameters)
192
+ .define_singleton_method(
193
+ "lib_version",
194
+ *[]() {
195
+ return std::to_string(operations_research::OrToolsMajorVersion()) + "."
196
+ + std::to_string(operations_research::OrToolsMinorVersion());
197
+ });
187
198
 
188
199
  define_class_under<RoutingSearchParameters>(rb_mORTools, "RoutingSearchParameters")
189
200
  .define_method(
@@ -392,7 +403,15 @@ void Init_ext()
392
403
 
393
404
  // not to be confused with operations_research::IntVar
394
405
  define_class_under<operations_research::sat::IntVar>(rb_mORTools, "SatIntVar");
395
- define_class_under<BoolVar>(rb_mORTools, "BoolVar");
406
+ define_class_under<BoolVar>(rb_mORTools, "BoolVar")
407
+ .define_method("name", &BoolVar::Name)
408
+ .define_method("index", &BoolVar::index)
409
+ .define_method(
410
+ "inspect",
411
+ *[](BoolVar& self) {
412
+ String name(self.Name());
413
+ return "#<ORTools::BoolVar @name=" + name.inspect().str() + ">";
414
+ });
396
415
 
397
416
  define_class_under<CpModelBuilder>(rb_mORTools, "CpModel")
398
417
  .define_constructor(Constructor<CpModelBuilder>())
@@ -449,6 +468,25 @@ void Init_ext()
449
468
  });
450
469
 
451
470
  define_class_under(rb_mORTools, "CpSolver")
471
+ .define_method(
472
+ "_solve_with_observer",
473
+ *[](Object self, CpModelBuilder& model, Object callback) {
474
+ operations_research::sat::Model m;
475
+
476
+ // set parameters for SearchForAllSolutions
477
+ SatParameters parameters;
478
+ parameters.set_enumerate_all_solutions(true);
479
+ m.Add(NewSatParameters(parameters));
480
+
481
+ m.Add(NewFeasibleSolutionObserver(
482
+ [callback](const CpSolverResponse& r) {
483
+ // TODO find a better way to do this
484
+ callback.call("response=", r);
485
+ callback.call("on_solution_callback");
486
+ })
487
+ );
488
+ return SolveCpModel(model.Build(), &m);
489
+ })
452
490
  .define_method(
453
491
  "_solve",
454
492
  *[](Object self, CpModelBuilder& model) {
@@ -462,6 +500,11 @@ void Init_ext()
462
500
 
463
501
  define_class_under<CpSolverResponse>(rb_mORTools, "CpSolverResponse")
464
502
  .define_method("objective_value", &CpSolverResponse::objective_value)
503
+ .define_method("num_conflicts", &CpSolverResponse::num_conflicts)
504
+ .define_method("num_branches", &CpSolverResponse::num_branches)
505
+ .define_method("wall_time", &CpSolverResponse::wall_time)
506
+ // .define_method("solution_integer_value", &SolutionIntegerValue)
507
+ .define_method("solution_boolean_value", &operations_research::sat::SolutionBooleanValue)
465
508
  .define_method(
466
509
  "status",
467
510
  *[](CpSolverResponse& self) {
@@ -6,6 +6,7 @@ require "or_tools/comparison"
6
6
  require "or_tools/comparison_operators"
7
7
  require "or_tools/cp_model"
8
8
  require "or_tools/cp_solver"
9
+ require "or_tools/cp_solver_solution_callback"
9
10
  require "or_tools/knapsack_solver"
10
11
  require "or_tools/linear_expr"
11
12
  require "or_tools/routing_model"
@@ -7,5 +7,9 @@ module ORTools
7
7
  @left = left
8
8
  @right = right
9
9
  end
10
+
11
+ def inspect
12
+ "#{left.inspect} #{operator} #{right.inspect}"
13
+ end
10
14
  end
11
15
  end
@@ -1,5 +1,11 @@
1
+ require "forwardable"
2
+
1
3
  module ORTools
2
4
  class CpSolver
5
+ extend Forwardable
6
+
7
+ def_delegators :@response, :objective_value, :num_conflicts, :num_branches, :wall_time
8
+
3
9
  def solve(model)
4
10
  @response = _solve(model)
5
11
  @response.status
@@ -9,8 +15,9 @@ module ORTools
9
15
  _solution_integer_value(@response, var)
10
16
  end
11
17
 
12
- def objective_value
13
- @response.objective_value
18
+ def search_for_all_solutions(model, observer)
19
+ @response = _solve_with_observer(model, observer)
20
+ @response.status
14
21
  end
15
22
  end
16
23
  end
@@ -0,0 +1,9 @@
1
+ module ORTools
2
+ class CpSolverSolutionCallback
3
+ attr_writer :response
4
+
5
+ def value(expr)
6
+ @response.solution_boolean_value(expr)
7
+ end
8
+ end
9
+ end
Binary file
@@ -16,6 +16,10 @@ module ORTools
16
16
  add(other, -1)
17
17
  end
18
18
 
19
+ def inspect
20
+ vars.map { |v| v[0].is_a?(BoolVar) ? v[0].name : v[0].name + " * " + v[1] }.join(" + ")
21
+ end
22
+
19
23
  private
20
24
 
21
25
  def add(other, sign)
@@ -1,3 +1,3 @@
1
1
  module ORTools
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
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.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-16 00:00:00.000000000 Z
11
+ date: 2020-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rice
@@ -98,6 +98,7 @@ files:
98
98
  - lib/or_tools/comparison_operators.rb
99
99
  - lib/or_tools/cp_model.rb
100
100
  - lib/or_tools/cp_solver.rb
101
+ - lib/or_tools/cp_solver_solution_callback.rb
101
102
  - lib/or_tools/ext.bundle
102
103
  - lib/or_tools/knapsack_solver.rb
103
104
  - lib/or_tools/linear_expr.rb