OR-Tools  9.0
routing_sat.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #include <algorithm>
15 #include <cstdint>
16 #include <functional>
17 #include <limits>
18 #include <map>
19 #include <memory>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/time/time.h"
25 #include "ortools/base/logging.h"
26 #include "ortools/base/map_util.h"
32 #include "ortools/sat/integer.h"
33 #include "ortools/sat/model.h"
36 
37 namespace operations_research {
38 namespace sat {
39 namespace {
40 
41 // As of 07/2019, TSPs and VRPs with homogeneous fleets of vehicles are
42 // supported.
43 // TODO(user): Support any type of constraints.
44 // TODO(user): Make VRPs properly support optional nodes.
45 bool RoutingModelCanBeSolvedBySat(const RoutingModel& model) {
46  return model.GetVehicleClassesCount() == 1;
47 }
48 
49 // Adds an integer variable to a CpModelProto, returning its index in the proto.
50 int AddVariable(CpModelProto* cp_model, int64_t lb, int64_t ub) {
51  const int index = cp_model->variables_size();
52  IntegerVariableProto* const var = cp_model->add_variables();
53  var->add_domain(lb);
54  var->add_domain(ub);
55  return index;
56 }
57 
58 // Returns the unique depot node used in the CP-SAT models (as of 01/2020).
59 int64_t GetDepotFromModel(const RoutingModel& model) { return model.Start(0); }
60 
61 // Structure to keep track of arcs created.
62 struct Arc {
63  int tail;
64  int head;
65 
66  friend bool operator==(const Arc& a, const Arc& b) {
67  return a.tail == b.tail && a.head == b.head;
68  }
69  friend bool operator!=(const Arc& a, const Arc& b) { return !(a == b); }
70  friend bool operator<(const Arc& a, const Arc& b) {
71  return a.tail == b.tail ? a.head < b.head : a.tail < b.tail;
72  }
73  friend std::ostream& operator<<(std::ostream& strm, const Arc& arc) {
74  return strm << "{" << arc.tail << ", " << arc.head << "}";
75  }
76  template <typename H>
77  friend H AbslHashValue(H h, const Arc& a) {
78  return H::combine(std::move(h), a.tail, a.head);
79  }
80 };
81 
82 using ArcVarMap = std::map<Arc, int>; // needs to be stable when iterating
83 
84 // Adds all dimensions to a CpModelProto. Only adds path cumul constraints and
85 // cumul bounds.
86 void AddDimensions(const RoutingModel& model, const ArcVarMap& arc_vars,
87  CpModelProto* cp_model) {
88  for (const RoutingDimension* dimension : model.GetDimensions()) {
89  // Only a single vehicle class.
90  const RoutingModel::TransitCallback2& transit =
91  dimension->transit_evaluator(0);
92  std::vector<int> cumuls(dimension->cumuls().size(), -1);
93  const int64_t min_start = dimension->cumuls()[model.Start(0)]->Min();
94  const int64_t max_end = std::min(dimension->cumuls()[model.End(0)]->Max(),
95  dimension->vehicle_capacities()[0]);
96  for (int i = 0; i < cumuls.size(); ++i) {
97  if (model.IsStart(i) || model.IsEnd(i)) continue;
98  // Reducing bounds supposing the triangular inequality.
99  const int64_t cumul_min =
101  std::max(dimension->cumuls()[i]->Min(),
102  CapAdd(transit(model.Start(0), i), min_start)));
103  const int64_t cumul_max =
105  std::min(dimension->cumuls()[i]->Max(),
106  CapSub(max_end, transit(i, model.End(0)))));
107  cumuls[i] = AddVariable(cp_model, cumul_min, cumul_max);
108  }
109  for (const auto arc_var : arc_vars) {
110  const int tail = arc_var.first.tail;
111  const int head = arc_var.first.head;
112  if (tail == head || model.IsStart(tail) || model.IsStart(head)) continue;
113  // arc[tail][head] -> cumuls[head] >= cumuls[tail] + transit.
114  // This is a relaxation of the model as it does not consider slack max.
115  ConstraintProto* ct = cp_model->add_constraints();
116  ct->add_enforcement_literal(arc_var.second);
117  LinearConstraintProto* arg = ct->mutable_linear();
118  arg->add_domain(transit(tail, head));
119  arg->add_domain(std::numeric_limits<int64_t>::max());
120  arg->add_vars(cumuls[tail]);
121  arg->add_coeffs(-1);
122  arg->add_vars(cumuls[head]);
123  arg->add_coeffs(1);
124  }
125  }
126 }
127 
128 std::vector<int> CreateRanks(const RoutingModel& model,
129  const ArcVarMap& arc_vars,
130  CpModelProto* cp_model) {
131  const int depot = GetDepotFromModel(model);
132  const int size = model.Size() + model.vehicles();
133  const int rank_size = model.Size() - model.vehicles();
134  std::vector<int> ranks(size, -1);
135  for (int i = 0; i < size; ++i) {
136  if (model.IsStart(i) || model.IsEnd(i)) continue;
137  ranks[i] = AddVariable(cp_model, 0, rank_size);
138  }
139  ranks[depot] = AddVariable(cp_model, 0, 0);
140  for (const auto arc_var : arc_vars) {
141  const int tail = arc_var.first.tail;
142  const int head = arc_var.first.head;
143  if (tail == head || head == depot) continue;
144  // arc[tail][head] -> ranks[head] == ranks[tail] + 1.
145  ConstraintProto* ct = cp_model->add_constraints();
146  ct->add_enforcement_literal(arc_var.second);
147  LinearConstraintProto* arg = ct->mutable_linear();
148  arg->add_domain(1);
149  arg->add_domain(1);
150  arg->add_vars(ranks[tail]);
151  arg->add_coeffs(-1);
152  arg->add_vars(ranks[head]);
153  arg->add_coeffs(1);
154  }
155  return ranks;
156 }
157 
158 // Vehicle variables do not actually represent the index of the vehicle
159 // performing a node, but we ensure that the values of two vehicle variables
160 // are the same if and only if the corresponding nodes are served by the same
161 // vehicle.
162 std::vector<int> CreateVehicleVars(const RoutingModel& model,
163  const ArcVarMap& arc_vars,
164  CpModelProto* cp_model) {
165  const int depot = GetDepotFromModel(model);
166  const int size = model.Size() + model.vehicles();
167  std::vector<int> vehicles(size, -1);
168  for (int i = 0; i < size; ++i) {
169  if (model.IsStart(i) || model.IsEnd(i)) continue;
170  vehicles[i] = AddVariable(cp_model, 0, size - 1);
171  }
172  for (const auto arc_var : arc_vars) {
173  const int tail = arc_var.first.tail;
174  const int head = arc_var.first.head;
175  if (tail == head || head == depot) continue;
176  if (tail == depot) {
177  // arc[depot][head] -> vehicles[head] == head.
178  ConstraintProto* ct = cp_model->add_constraints();
179  ct->add_enforcement_literal(arc_var.second);
180  LinearConstraintProto* arg = ct->mutable_linear();
181  arg->add_domain(head);
182  arg->add_domain(head);
183  arg->add_vars(vehicles[head]);
184  arg->add_coeffs(1);
185  continue;
186  }
187  // arc[tail][head] -> vehicles[head] == vehicles[tail].
188  ConstraintProto* ct = cp_model->add_constraints();
189  ct->add_enforcement_literal(arc_var.second);
190  LinearConstraintProto* arg = ct->mutable_linear();
191  arg->add_domain(0);
192  arg->add_domain(0);
193  arg->add_vars(vehicles[tail]);
194  arg->add_coeffs(-1);
195  arg->add_vars(vehicles[head]);
196  arg->add_coeffs(1);
197  }
198  return vehicles;
199 }
200 
201 void AddPickupDeliveryConstraints(const RoutingModel& model,
202  const ArcVarMap& arc_vars,
203  CpModelProto* cp_model) {
204  if (model.GetPickupAndDeliveryPairs().empty()) return;
205  const std::vector<int> ranks = CreateRanks(model, arc_vars, cp_model);
206  const std::vector<int> vehicles =
207  CreateVehicleVars(model, arc_vars, cp_model);
208  for (const auto& pairs : model.GetPickupAndDeliveryPairs()) {
209  const int64_t pickup = pairs.first[0];
210  const int64_t delivery = pairs.second[0];
211  {
212  // ranks[pickup] + 1 <= ranks[delivery].
213  ConstraintProto* ct = cp_model->add_constraints();
214  LinearConstraintProto* arg = ct->mutable_linear();
215  arg->add_domain(1);
216  arg->add_domain(std::numeric_limits<int64_t>::max());
217  arg->add_vars(ranks[delivery]);
218  arg->add_coeffs(1);
219  arg->add_vars(ranks[pickup]);
220  arg->add_coeffs(-1);
221  }
222  {
223  // vehicles[pickup] == vehicles[delivery]
224  ConstraintProto* ct = cp_model->add_constraints();
225  LinearConstraintProto* arg = ct->mutable_linear();
226  arg->add_domain(0);
227  arg->add_domain(0);
228  arg->add_vars(vehicles[delivery]);
229  arg->add_coeffs(1);
230  arg->add_vars(vehicles[pickup]);
231  arg->add_coeffs(-1);
232  }
233  }
234 }
235 
236 // Converts a RoutingModel to CpModelProto for models with multiple vehicles.
237 // All non-start/end nodes have the same index in both models. Start/end nodes
238 // map to a single depot index; its value is arbitrarly the index of the start
239 // node of the first vehicle in the RoutingModel.
240 // The map between CPModelProto arcs and their corresponding arc variable is
241 // returned.
242 ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
243  CpModelProto* cp_model) {
244  ArcVarMap arc_vars;
245  const int num_nodes = model.Nexts().size();
246  const int depot = GetDepotFromModel(model);
247 
248  // Create "arc" variables and set their cost.
249  for (int tail = 0; tail < num_nodes; ++tail) {
250  const int tail_index = model.IsStart(tail) ? depot : tail;
251  std::unique_ptr<IntVarIterator> iter(
252  model.NextVar(tail)->MakeDomainIterator(false));
253  for (int head : InitAndGetValues(iter.get())) {
254  // Vehicle start and end nodes are represented as a single node in the
255  // CP-SAT model. We choose the start index of the first vehicle to
256  // represent both. We can also skip any head representing a vehicle start
257  // as the CP solver will reject those.
258  if (model.IsStart(head)) continue;
259  const int head_index = model.IsEnd(head) ? depot : head;
260  if (head_index == tail_index) continue;
261  const int64_t cost = tail != head ? model.GetHomogeneousCost(tail, head)
262  : model.UnperformedPenalty(tail);
263  if (cost == std::numeric_limits<int64_t>::max()) continue;
264  const Arc arc = {tail_index, head_index};
265  if (gtl::ContainsKey(arc_vars, arc)) continue;
266  const int index = AddVariable(cp_model, 0, 1);
267  gtl::InsertOrDie(&arc_vars, arc, index);
268  cp_model->mutable_objective()->add_vars(index);
269  cp_model->mutable_objective()->add_coeffs(cost);
270  }
271  }
272 
273  // The following flow constraints seem to be necessary with the Route
274  // constraint, greatly improving performance due to stronger LP relaxation
275  // (supposedly).
276  // TODO(user): Remove these constraints when the Route constraint handles
277  // LP relaxations properly.
278  {
279  LinearConstraintProto* ct = cp_model->add_constraints()->mutable_linear();
280  ct->add_domain(0);
281  ct->add_domain(0);
282  for (int node = 0; node < num_nodes; ++node) {
283  if (model.IsStart(node) || model.IsEnd(node)) continue;
284  int* const depot_node_var = gtl::FindOrNull(arc_vars, {depot, node});
285  if (depot_node_var == nullptr) continue;
286  ct->add_vars(*depot_node_var);
287  ct->add_coeffs(1);
288  int* const node_depot_var = gtl::FindOrNull(arc_vars, {node, depot});
289  if (node_depot_var == nullptr) continue;
290  ct->add_vars(*node_depot_var);
291  ct->add_coeffs(-1);
292  }
293  }
294 
295  {
296  LinearConstraintProto* ct = cp_model->add_constraints()->mutable_linear();
297  ct->add_domain(0);
298  // Taking the min since as of 04/2020 fleet is homogeneous.
299  ct->add_domain(
300  std::min(model.vehicles(), model.GetMaximumNumberOfActiveVehicles()));
301  for (int node = 0; node < num_nodes; ++node) {
302  if (model.IsStart(node) || model.IsEnd(node)) continue;
303  int* const var = gtl::FindOrNull(arc_vars, {depot, node});
304  if (var == nullptr) continue;
305  ct->add_vars(*var);
306  ct->add_coeffs(1);
307  }
308  }
309 
310  for (int tail = 0; tail < num_nodes; ++tail) {
311  if (model.IsStart(tail) || model.IsEnd(tail)) continue;
312  LinearConstraintProto* ct = cp_model->add_constraints()->mutable_linear();
313  ct->add_domain(1);
314  ct->add_domain(1);
315  std::unique_ptr<IntVarIterator> iter(
316  model.NextVar(tail)->MakeDomainIterator(false));
317  bool depot_added = false;
318  for (int head : InitAndGetValues(iter.get())) {
319  if (model.IsStart(head)) continue;
320  if (tail == head) continue;
321  if (model.IsEnd(head)) {
322  if (depot_added) continue;
323  head = depot;
324  depot_added = true;
325  }
326  int* const var = gtl::FindOrNull(arc_vars, {tail, head});
327  if (var == nullptr) continue;
328  ct->add_vars(*var);
329  ct->add_coeffs(1);
330  }
331  }
332 
333  for (int head = 0; head < num_nodes; ++head) {
334  if (model.IsStart(head) || model.IsEnd(head)) continue;
335  LinearConstraintProto* ct = cp_model->add_constraints()->mutable_linear();
336  ct->add_domain(1);
337  ct->add_domain(1);
338  for (int tail = 0; tail < num_nodes; ++tail) {
339  if (model.IsEnd(head)) continue;
340  if (tail == head) continue;
341  if (model.IsStart(tail) && tail != depot) continue;
342  int* const var = gtl::FindOrNull(arc_vars, {tail, head});
343  if (var == nullptr) continue;
344  ct->add_vars(*var);
345  ct->add_coeffs(1);
346  }
347  }
348 
349  AddPickupDeliveryConstraints(model, arc_vars, cp_model);
350 
351  AddDimensions(model, arc_vars, cp_model);
352 
353  // Create Routes constraint, ensuring circuits from and to the depot.
354  // This one is a bit tricky, because we need to remap the depot to zero.
355  // TODO(user): Make Routes constraints support optional nodes.
356  RoutesConstraintProto* routes_ct =
357  cp_model->add_constraints()->mutable_routes();
358  for (const auto arc_var : arc_vars) {
359  const int tail = arc_var.first.tail;
360  const int head = arc_var.first.head;
361  routes_ct->add_tails(tail == 0 ? depot : tail == depot ? 0 : tail);
362  routes_ct->add_heads(head == 0 ? depot : head == depot ? 0 : head);
363  routes_ct->add_literals(arc_var.second);
364  }
365 
366  // Add demands and capacities to improve the LP relaxation and cuts. These are
367  // based on the first "unary" dimension in the model if it exists.
368  // TODO(user): We might want to try to get demand lower bounds from
369  // non-unary dimensions if no unary exist.
370  const RoutingDimension* master_dimension = nullptr;
371  for (const RoutingDimension* dimension : model.GetDimensions()) {
372  // Only a single vehicle class is supported.
373  if (dimension->GetUnaryTransitEvaluator(0) != nullptr) {
374  master_dimension = dimension;
375  break;
376  }
377  }
378  if (master_dimension != nullptr) {
379  const RoutingModel::TransitCallback1& transit =
380  master_dimension->GetUnaryTransitEvaluator(0);
381  for (int node = 0; node < num_nodes; ++node) {
382  // Tricky: demand is added for all nodes in the sat model; this means
383  // start/end nodes other than the one used for the depot must be ignored.
384  if (!model.IsEnd(node) && (!model.IsStart(node) || node == depot)) {
385  routes_ct->add_demands(transit(node));
386  }
387  }
388  DCHECK_EQ(routes_ct->demands_size(), num_nodes + 1 - model.vehicles());
389  routes_ct->set_capacity(master_dimension->vehicle_capacities()[0]);
390  }
391  return arc_vars;
392 }
393 
394 // Converts a RoutingModel with a single vehicle to a CpModelProto.
395 // The mapping between CPModelProto arcs and their corresponding arc variables
396 // is returned.
397 ArcVarMap PopulateSingleRouteModelFromRoutingModel(const RoutingModel& model,
398  CpModelProto* cp_model) {
399  ArcVarMap arc_vars;
400  const int num_nodes = model.Nexts().size();
401  CircuitConstraintProto* circuit =
402  cp_model->add_constraints()->mutable_circuit();
403  for (int tail = 0; tail < num_nodes; ++tail) {
404  std::unique_ptr<IntVarIterator> iter(
405  model.NextVar(tail)->MakeDomainIterator(false));
406  for (int head : InitAndGetValues(iter.get())) {
407  // Vehicle start and end nodes are represented as a single node in the
408  // CP-SAT model. We choose the start index to represent both. We can also
409  // skip any head representing a vehicle start as the CP solver will reject
410  // those.
411  if (model.IsStart(head)) continue;
412  if (model.IsEnd(head)) head = model.Start(0);
413  const int64_t cost = tail != head ? model.GetHomogeneousCost(tail, head)
414  : model.UnperformedPenalty(tail);
415  if (cost == std::numeric_limits<int64_t>::max()) continue;
416  const int index = AddVariable(cp_model, 0, 1);
417  circuit->add_literals(index);
418  circuit->add_tails(tail);
419  circuit->add_heads(head);
420  cp_model->mutable_objective()->add_vars(index);
421  cp_model->mutable_objective()->add_coeffs(cost);
422  gtl::InsertOrDie(&arc_vars, {tail, head}, index);
423  }
424  }
425  AddPickupDeliveryConstraints(model, arc_vars, cp_model);
426  AddDimensions(model, arc_vars, cp_model);
427  return arc_vars;
428 }
429 
430 // Converts a RoutingModel to a CpModelProto.
431 // The mapping between CPModelProto arcs and their corresponding arc variables
432 // is returned.
433 ArcVarMap PopulateModelFromRoutingModel(const RoutingModel& model,
434  CpModelProto* cp_model) {
435  if (model.vehicles() == 1) {
436  return PopulateSingleRouteModelFromRoutingModel(model, cp_model);
437  }
438  return PopulateMultiRouteModelFromRoutingModel(model, cp_model);
439 }
440 
441 // Converts a CpSolverResponse to an Assignment containing next variables.
442 bool ConvertToSolution(const CpSolverResponse& response,
443  const RoutingModel& model, const ArcVarMap& arc_vars,
444  Assignment* solution) {
445  if (response.status() != CpSolverStatus::OPTIMAL &&
447  return false;
448  const int depot = GetDepotFromModel(model);
449  int vehicle = 0;
450  for (const auto& arc_var : arc_vars) {
451  if (response.solution(arc_var.second) != 0) {
452  const int tail = arc_var.first.tail;
453  const int head = arc_var.first.head;
454  if (head == depot) continue;
455  if (tail != depot) {
456  solution->Add(model.NextVar(tail))->SetValue(head);
457  } else {
458  solution->Add(model.NextVar(model.Start(vehicle)))->SetValue(head);
459  ++vehicle;
460  }
461  }
462  }
463  // Close open routes.
464  for (int v = 0; v < model.vehicles(); ++v) {
465  int current = model.Start(v);
466  while (solution->Contains(model.NextVar(current))) {
467  current = solution->Value(model.NextVar(current));
468  }
469  solution->Add(model.NextVar(current))->SetValue(model.End(v));
470  }
471  return true;
472 }
473 
474 void AddSolutionAsHintToModel(const Assignment* solution,
475  const RoutingModel& model,
476  const ArcVarMap& arc_vars,
477  CpModelProto* cp_model) {
478  if (solution == nullptr) return;
479  PartialVariableAssignment* const hint = cp_model->mutable_solution_hint();
480  hint->Clear();
481  const int depot = GetDepotFromModel(model);
482  const int num_nodes = model.Nexts().size();
483  for (int tail = 0; tail < num_nodes; ++tail) {
484  const int tail_index = model.IsStart(tail) ? depot : tail;
485  const int head = solution->Value(model.NextVar(tail));
486  const int head_index = model.IsEnd(head) ? depot : head;
487  if (tail_index == depot && head_index == depot) continue;
488  const int* const var_index =
489  gtl::FindOrNull(arc_vars, {tail_index, head_index});
490  // Arcs with a cost of kint64max are not added to the model (considered as
491  // infeasible). In some rare cases CP solutions might contain such arcs in
492  // which case they are skipped here and a partial solution is used as a
493  // hint.
494  if (var_index == nullptr) continue;
495  hint->add_vars(*var_index);
496  hint->add_values(1);
497  }
498 }
499 
500 // Configures a CP-SAT solver and solves the given (routing) model using it.
501 // Returns the response of the search.
502 CpSolverResponse SolveRoutingModel(
503  const CpModelProto& cp_model, absl::Duration remaining_time,
504  const std::function<void(const CpSolverResponse& response)>& observer) {
505  // TODO(user): Add CP-SAT parameters to routing parameters.
506  SatParameters parameters;
507  parameters.set_linearization_level(2);
508  parameters.set_max_time_in_seconds(absl::ToDoubleSeconds(remaining_time));
509  parameters.set_num_search_workers(1);
510  Model model;
512  if (observer != nullptr) {
513  model.Add(NewFeasibleSolutionObserver(observer));
514  }
515  // TODO(user): Add an option to dump the CP-SAT model or check if the
516  // cp_model_dump_file flag in cp_model_solver.cc is good enough.
517  return SolveCpModel(cp_model, &model);
518 }
519 
520 } // namespace
521 } // namespace sat
522 
523 // Solves a RoutingModel using the CP-SAT solver. Returns false if no solution
524 // was found.
526  const RoutingSearchParameters& search_parameters,
527  const Assignment* initial_solution,
528  Assignment* solution) {
529  if (!sat::RoutingModelCanBeSolvedBySat(model)) return false;
530  sat::CpModelProto cp_model;
531  cp_model.mutable_objective()->set_scaling_factor(
532  search_parameters.log_cost_scaling_factor());
533  cp_model.mutable_objective()->set_offset(search_parameters.log_cost_offset());
534  const sat::ArcVarMap arc_vars =
535  sat::PopulateModelFromRoutingModel(model, &cp_model);
536  sat::AddSolutionAsHintToModel(initial_solution, model, arc_vars, &cp_model);
537  return sat::ConvertToSolution(
538  sat::SolveRoutingModel(cp_model, model.RemainingTime(), nullptr), model,
539  arc_vars, solution);
540 }
541 
542 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
An Assignment is a variable -> domains mapping, used to report solutions to the user.
RoutingTransitCallback1 TransitCallback1
Definition: routing.h:237
RoutingTransitCallback2 TransitCallback2
Definition: routing.h:238
int64_t b
int64_t a
SatParameters parameters
SharedResponseManager * response
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:60
bool operator!=(const STLCountingAllocator< T, A > &a, const STLCountingAllocator< T, A > &b)
Definition: stl_util.h:985
H AbslHashValue(H h, const LinearConstraint &linear_constraint)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:68
std::function< SatParameters(Model *)> NewSatParameters(const std::string &params)
Creates parameters for the solver, which you can add to the model with.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
Collection of objects used to extend the Constraint Solver library.
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
Definition: routing_sat.cc:525
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
std::pair< int64_t, int64_t > Arc
Definition: search.cc:3383
LinearRange operator==(const LinearExpr &lhs, const LinearExpr &rhs)
Definition: linear_expr.cc:180
int index
Definition: pack.cc:509
int64_t cost
int head
Definition: routing_sat.cc:64
int tail
Definition: routing_sat.cc:63