OR-Tools  8.2
routing.cc
Go to the documentation of this file.
1// Copyright 2010-2018 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
15
16#include <algorithm>
17#include <cmath>
18#include <cstddef>
19#include <cstring>
20#include <functional>
21#include <initializer_list>
22#include <map>
23#include <memory>
24#include <numeric>
25#include <tuple>
26#include <utility>
27
28#include "absl/base/casts.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/memory/memory.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_format.h"
34#include "absl/time/time.h"
35#include "google/protobuf/duration.pb.h"
36#include "google/protobuf/text_format.h"
38#include "ortools/base/hash.h"
48#include "ortools/constraint_solver/routing_enums.pb.h"
58#include "ortools/util/bitset.h"
59#include "ortools/util/optional_boolean.pb.h"
61#include "ortools/util/stats.h"
62
63namespace operations_research {
64class LocalSearchPhaseParameters;
65} // namespace operations_research
66
67ABSL_FLAG(int64, sweep_sectors, 1,
68 "The number of sectors the space is divided before it is sweeped "
69 "by the ray.");
70
71// Trace settings
72
73// TODO(user): Move most of the following settings to a model parameter
74// proto.
75
76namespace operations_research {
77
78namespace {
79// A decision builder which tries to assign values to variables as close as
80// possible to target values first.
81// TODO(user): Move to CP solver.
82class SetValuesFromTargets : public DecisionBuilder {
83 public:
84 SetValuesFromTargets(std::vector<IntVar*> variables,
85 std::vector<int64> targets)
86 : variables_(std::move(variables)),
87 targets_(std::move(targets)),
88 index_(0),
89 steps_(variables_.size(), 0) {
90 DCHECK_EQ(variables_.size(), targets_.size());
91 }
92 Decision* Next(Solver* const solver) override {
93 int index = index_.Value();
94 while (index < variables_.size() && variables_[index]->Bound()) {
95 ++index;
96 }
97 index_.SetValue(solver, index);
98 if (index >= variables_.size()) return nullptr;
99 const int64 variable_min = variables_[index]->Min();
100 const int64 variable_max = variables_[index]->Max();
101 // Target can be before, inside, or after the variable range.
102 // We do a trichotomy on this for clarity.
103 if (targets_[index] <= variable_min) {
104 return solver->MakeAssignVariableValue(variables_[index], variable_min);
105 } else if (targets_[index] >= variable_max) {
106 return solver->MakeAssignVariableValue(variables_[index], variable_max);
107 } else {
108 int64 step = steps_[index];
109 int64 value = CapAdd(targets_[index], step);
110 // If value is out of variable's range, we can remove the interval of
111 // values already explored (which can make the solver fail) and
112 // recall Next() to get back into the trichotomy above.
113 if (value < variable_min || variable_max < value) {
114 step = GetNextStep(step);
115 value = CapAdd(targets_[index], step);
116 if (step > 0) {
117 // Values in [variable_min, value) were already explored.
118 variables_[index]->SetMin(value);
119 } else {
120 // Values in (value, variable_max] were already explored.
121 variables_[index]->SetMax(value);
122 }
123 return Next(solver);
124 }
125 steps_.SetValue(solver, index, GetNextStep(step));
126 return solver->MakeAssignVariableValueOrDoNothing(variables_[index],
127 value);
128 }
129 }
130
131 private:
132 int64 GetNextStep(int64 step) const {
133 return (step > 0) ? -step : CapSub(1, step);
134 }
135 const std::vector<IntVar*> variables_;
136 const std::vector<int64> targets_;
137 Rev<int> index_;
138 RevArray<int64> steps_;
139};
140
141} // namespace
142
144 std::vector<IntVar*> variables,
145 std::vector<int64> targets) {
146 return solver->RevAlloc(
147 new SetValuesFromTargets(std::move(variables), std::move(targets)));
148}
149
150namespace {
151
152bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
153 const RoutingDimension& dimension, int vehicle) {
154 const RoutingModel* const model = dimension.model();
155 int node = model->Start(vehicle);
156 while (!model->IsEnd(node)) {
157 if (!model->NextVar(node)->Bound()) {
158 return false;
159 }
160 const int next = model->NextVar(node)->Value();
161 if (dimension.transit_evaluator(vehicle)(node, next) !=
162 dimension.FixedTransitVar(node)->Value()) {
163 return false;
164 }
165 node = next;
166 }
167 return true;
168}
169
170bool DimensionFixedTransitsEqualTransitEvaluators(
171 const RoutingDimension& dimension) {
172 for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
173 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
174 vehicle)) {
175 return false;
176 }
177 }
178 return true;
179}
180
181class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
182 public:
183 SetCumulsFromLocalDimensionCosts(
184 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
185 local_optimizers,
186 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
187 local_mp_optimizers,
188 SearchMonitor* monitor, bool optimize_and_pack = false)
189 : local_optimizers_(*local_optimizers),
190 local_mp_optimizers_(*local_mp_optimizers),
191 monitor_(monitor),
192 optimize_and_pack_(optimize_and_pack) {}
193 Decision* Next(Solver* const solver) override {
194 // The following boolean variable indicates if the solver should fail, in
195 // order to postpone the Fail() call until after the internal for loop, so
196 // there are no memory leaks related to the cumul_values vector.
197 bool should_fail = false;
198 for (int i = 0; i < local_optimizers_.size(); ++i) {
199 const auto& local_optimizer = local_optimizers_[i];
200 const RoutingDimension* const dimension = local_optimizer->dimension();
201 RoutingModel* const model = dimension->model();
202 const auto next = [model](int64 i) { return model->NextVar(i)->Value(); };
203 const auto compute_cumul_values =
204 [this, &next](LocalDimensionCumulOptimizer* optimizer, int vehicle,
205 std::vector<int64>* cumul_values,
206 std::vector<int64>* break_start_end_values) {
207 if (optimize_and_pack_) {
208 return optimizer->ComputePackedRouteCumuls(
209 vehicle, next, cumul_values, break_start_end_values);
210 } else {
211 return optimizer->ComputeRouteCumuls(vehicle, next, cumul_values,
212 break_start_end_values);
213 }
214 };
215 for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) {
216 // TODO(user): Investigate if we should skip unused vehicles.
217 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(*dimension,
218 vehicle));
219 const bool vehicle_has_break_constraint =
220 dimension->HasBreakConstraints() &&
221 !dimension->GetBreakIntervalsOfVehicle(vehicle).empty();
222 LocalDimensionCumulOptimizer* const optimizer =
223 vehicle_has_break_constraint ? local_mp_optimizers_[i].get()
224 : local_optimizer.get();
225 DCHECK(optimizer != nullptr);
226 std::vector<int64> cumul_values;
227 std::vector<int64> break_start_end_values;
228 const DimensionSchedulingStatus status = compute_cumul_values(
229 optimizer, vehicle, &cumul_values, &break_start_end_values);
231 should_fail = true;
232 break;
233 }
234 // If relaxation is not feasible, try the MILP optimizer.
236 cumul_values.clear();
237 break_start_end_values.clear();
238 DCHECK(local_mp_optimizers_[i] != nullptr);
239 if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
240 &cumul_values, &break_start_end_values) ==
242 should_fail = true;
243 break;
244 }
245 } else {
247 }
248 // Concatenate cumul_values and break_start_end_values into cp_values,
249 // generate corresponding cp_variables vector.
250 std::vector<IntVar*> cp_variables;
251 std::vector<int64> cp_values;
252 std::swap(cp_values, cumul_values);
253 {
254 int current = model->Start(vehicle);
255 while (true) {
256 cp_variables.push_back(dimension->CumulVar(current));
257 if (!model->IsEnd(current)) {
258 current = model->NextVar(current)->Value();
259 } else {
260 break;
261 }
262 }
263 }
264 // Setting the cumuls of path start/end first is more efficient than
265 // setting the cumuls in order of path appearance, because setting start
266 // and end cumuls gives an opportunity to fix all cumuls with two
267 // decisions instead of |path| decisions.
268 // To this effect, we put end cumul just after the start cumul.
269 std::swap(cp_variables[1], cp_variables.back());
270 std::swap(cp_values[1], cp_values.back());
271 if (dimension->HasBreakConstraints()) {
272 for (IntervalVar* interval :
273 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
274 cp_variables.push_back(interval->SafeStartExpr(0)->Var());
275 cp_variables.push_back(interval->SafeEndExpr(0)->Var());
276 }
277 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
278 break_start_end_values.end());
279 }
280 // Value kint64min signals an unoptimized variable, set to min instead.
281 for (int i = 0; i < cp_values.size(); ++i) {
282 if (cp_values[i] == kint64min) {
283 cp_values[i] = cp_variables[i]->Min();
284 }
285 }
286 if (!solver->SolveAndCommit(
287 MakeSetValuesFromTargets(solver, std::move(cp_variables),
288 std::move(cp_values)),
289 monitor_)) {
290 should_fail = true;
291 break;
292 }
293 }
294 if (should_fail) {
295 solver->Fail();
296 }
297 }
298 return nullptr;
299 }
300
301 private:
302 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
303 local_optimizers_;
304 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
305 local_mp_optimizers_;
306 SearchMonitor* const monitor_;
307 const bool optimize_and_pack_;
308};
309
310class SetCumulsFromGlobalDimensionCosts : public DecisionBuilder {
311 public:
312 SetCumulsFromGlobalDimensionCosts(
313 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
314 global_optimizers,
315 SearchMonitor* monitor, bool optimize_and_pack = false)
316 : global_optimizers_(*global_optimizers),
317 monitor_(monitor),
318 optimize_and_pack_(optimize_and_pack) {}
319 Decision* Next(Solver* const solver) override {
320 // The following boolean variable indicates if the solver should fail, in
321 // order to postpone the Fail() call until after the for loop, so there are
322 // no memory leaks related to the cumul_values vector.
323 bool should_fail = false;
324 for (const auto& global_optimizer : global_optimizers_) {
325 const RoutingDimension* dimension = global_optimizer->dimension();
326 RoutingModel* const model = dimension->model();
327
328 const auto next = [model](int64 i) { return model->NextVar(i)->Value(); };
329
330 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
331
332 std::vector<int64> cumul_values;
333 std::vector<int64> break_start_end_values;
334 const bool cumuls_optimized =
335 optimize_and_pack_
336 ? global_optimizer->ComputePackedCumuls(next, &cumul_values,
337 &break_start_end_values)
338 : global_optimizer->ComputeCumuls(next, &cumul_values,
339 &break_start_end_values);
340 if (!cumuls_optimized) {
341 should_fail = true;
342 break;
343 }
344 // Concatenate cumul_values and break_start_end_values into cp_values,
345 // generate corresponding cp_variables vector.
346 std::vector<IntVar*> cp_variables = dimension->cumuls();
347 std::vector<int64> cp_values;
348 std::swap(cp_values, cumul_values);
349 if (dimension->HasBreakConstraints()) {
350 const int num_vehicles = model->vehicles();
351 for (int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
352 for (IntervalVar* interval :
353 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
354 cp_variables.push_back(interval->SafeStartExpr(0)->Var());
355 cp_variables.push_back(interval->SafeEndExpr(0)->Var());
356 }
357 }
358 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
359 break_start_end_values.end());
360 }
361 // Value kint64min signals an unoptimized variable, set to min instead.
362 for (int i = 0; i < cp_values.size(); ++i) {
363 if (cp_values[i] == kint64min) {
364 cp_values[i] = cp_variables[i]->Min();
365 }
366 }
367 if (!solver->SolveAndCommit(
368 MakeSetValuesFromTargets(solver, std::move(cp_variables),
369 std::move(cp_values)),
370 monitor_)) {
371 should_fail = true;
372 break;
373 }
374 }
375 if (should_fail) {
376 solver->Fail();
377 }
378 return nullptr;
379 }
380
381 private:
382 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
383 global_optimizers_;
384 SearchMonitor* const monitor_;
385 const bool optimize_and_pack_;
386};
387
388} // namespace
389
391 const Assignment* original_assignment, absl::Duration duration_limit) {
392 CHECK(closed_);
393 if (original_assignment == nullptr) return nullptr;
394 if (duration_limit <= absl::ZeroDuration()) return original_assignment;
395 if (global_dimension_optimizers_.empty() &&
396 local_dimension_optimizers_.empty()) {
397 DCHECK(local_dimension_mp_optimizers_.empty());
398 return original_assignment;
399 }
400 RegularLimit* const limit = GetOrCreateLimit();
401 limit->UpdateLimits(duration_limit, kint64max, kint64max, kint64max);
402
403 // Initialize the packed_assignment with the Next values in the
404 // original_assignment.
405 Assignment* packed_assignment = solver_->MakeAssignment();
406 packed_assignment->Add(Nexts());
407 packed_assignment->CopyIntersection(original_assignment);
408
409 std::vector<DecisionBuilder*> decision_builders;
410 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
411 decision_builders.push_back(
412 solver_->MakeRestoreAssignment(packed_assignment));
413 decision_builders.push_back(
414 solver_->RevAlloc(new SetCumulsFromLocalDimensionCosts(
415 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
416 GetOrCreateLargeNeighborhoodSearchLimit(),
417 /*optimize_and_pack=*/true)));
418 decision_builders.push_back(
419 solver_->RevAlloc(new SetCumulsFromGlobalDimensionCosts(
420 &global_dimension_optimizers_,
421 GetOrCreateLargeNeighborhoodSearchLimit(),
422 /*optimize_and_pack=*/true)));
423 decision_builders.push_back(
424 CreateFinalizerForMinimizedAndMaximizedVariables());
425
426 DecisionBuilder* restore_pack_and_finalize =
427 solver_->Compose(decision_builders);
428 solver_->Solve(restore_pack_and_finalize,
429 packed_dimensions_assignment_collector_, limit);
430
431 if (packed_dimensions_assignment_collector_->solution_count() != 1) {
432 LOG(ERROR) << "The given assignment is not valid for this model, or cannot "
433 "be packed.";
434 return nullptr;
435 }
436
437 packed_assignment->Copy(original_assignment);
438 packed_assignment->CopyIntersection(
439 packed_dimensions_assignment_collector_->solution(0));
440
441 return packed_assignment;
442}
443
444namespace {
445// Constraint which ensures that var != values.
446class DifferentFromValues : public Constraint {
447 public:
448 DifferentFromValues(Solver* solver, IntVar* var, std::vector<int64> values)
449 : Constraint(solver), var_(var), values_(std::move(values)) {}
450 void Post() override {}
451 void InitialPropagate() override { var_->RemoveValues(values_); }
452 std::string DebugString() const override { return "DifferentFromValues"; }
453 void Accept(ModelVisitor* const visitor) const override {
454 visitor->BeginVisitConstraint(RoutingModelVisitor::kRemoveValues, this);
455 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
456 {var_});
457 visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
458 visitor->EndVisitConstraint(RoutingModelVisitor::kRemoveValues, this);
459 }
460
461 private:
462 IntVar* const var_;
463 const std::vector<int64> values_;
464};
465
466// Set of "light" constraints, well-suited for use within Local Search.
467// These constraints are "checking" constraints, only triggered on WhenBound
468// events. The provide very little (or no) domain filtering.
469// TODO(user): Move to core constraintsolver library.
470
471// Light one-dimension function-based element constraint ensuring:
472// var == values(index).
473// Doesn't perform bound reduction of the resulting variable until the index
474// variable is bound.
475// If deep_serialize returns false, the model visitor will not extract all
476// possible values from the values function.
477template <typename F>
478class LightFunctionElementConstraint : public Constraint {
479 public:
480 LightFunctionElementConstraint(Solver* const solver, IntVar* const var,
481 IntVar* const index, F values,
482 std::function<bool()> deep_serialize)
483 : Constraint(solver),
484 var_(var),
485 index_(index),
486 values_(std::move(values)),
487 deep_serialize_(std::move(deep_serialize)) {}
488 ~LightFunctionElementConstraint() override {}
489
490 void Post() override {
491 Demon* demon = MakeConstraintDemon0(
492 solver(), this, &LightFunctionElementConstraint::IndexBound,
493 "IndexBound");
494 index_->WhenBound(demon);
495 }
496
497 void InitialPropagate() override {
498 if (index_->Bound()) {
499 IndexBound();
500 }
501 }
502
503 std::string DebugString() const override {
504 return "LightFunctionElementConstraint";
505 }
506
507 void Accept(ModelVisitor* const visitor) const override {
508 visitor->BeginVisitConstraint(RoutingModelVisitor::kLightElement, this);
509 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
510 var_);
511 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
512 index_);
513 // Warning: This will expand all values into a vector.
514 if (deep_serialize_()) {
515 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
516 index_->Max());
517 }
518 visitor->EndVisitConstraint(RoutingModelVisitor::kLightElement, this);
519 }
520
521 private:
522 void IndexBound() { var_->SetValue(values_(index_->Min())); }
523
524 IntVar* const var_;
525 IntVar* const index_;
526 F values_;
527 std::function<bool()> deep_serialize_;
528};
529
530template <typename F>
531Constraint* MakeLightElement(Solver* const solver, IntVar* const var,
532 IntVar* const index, F values,
533 std::function<bool()> deep_serialize) {
534 return solver->RevAlloc(new LightFunctionElementConstraint<F>(
535 solver, var, index, std::move(values), std::move(deep_serialize)));
536}
537
538// Light two-dimension function-based element constraint ensuring:
539// var == values(index1, index2).
540// Doesn't perform bound reduction of the resulting variable until the index
541// variables are bound.
542// Ownership of the 'values' callback is taken by the constraint.
543template <typename F>
544class LightFunctionElement2Constraint : public Constraint {
545 public:
546 LightFunctionElement2Constraint(Solver* const solver, IntVar* const var,
547 IntVar* const index1, IntVar* const index2,
548 F values,
549 std::function<bool()> deep_serialize)
550 : Constraint(solver),
551 var_(var),
552 index1_(index1),
553 index2_(index2),
554 values_(std::move(values)),
555 deep_serialize_(std::move(deep_serialize)) {}
556 ~LightFunctionElement2Constraint() override {}
557 void Post() override {
558 Demon* demon = MakeConstraintDemon0(
559 solver(), this, &LightFunctionElement2Constraint::IndexBound,
560 "IndexBound");
561 index1_->WhenBound(demon);
562 index2_->WhenBound(demon);
563 }
564 void InitialPropagate() override { IndexBound(); }
565
566 std::string DebugString() const override {
567 return "LightFunctionElement2Constraint";
568 }
569
570 void Accept(ModelVisitor* const visitor) const override {
571 visitor->BeginVisitConstraint(RoutingModelVisitor::kLightElement2, this);
572 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
573 var_);
574 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
575 index1_);
576 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndex2Argument,
577 index2_);
578 // Warning: This will expand all values into a vector.
579 const int64 index1_min = index1_->Min();
580 const int64 index1_max = index1_->Max();
581 visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, index1_min);
582 visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, index1_max);
583 if (deep_serialize_()) {
584 for (int i = index1_min; i <= index1_max; ++i) {
585 visitor->VisitInt64ToInt64Extension(
586 [this, i](int64 j) { return values_(i, j); }, index2_->Min(),
587 index2_->Max());
588 }
589 }
590 visitor->EndVisitConstraint(RoutingModelVisitor::kLightElement2, this);
591 }
592
593 private:
594 void IndexBound() {
595 if (index1_->Bound() && index2_->Bound()) {
596 var_->SetValue(values_(index1_->Min(), index2_->Min()));
597 }
598 }
599
600 IntVar* const var_;
601 IntVar* const index1_;
602 IntVar* const index2_;
604 std::function<bool()> deep_serialize_;
605};
606
607template <typename F>
608Constraint* MakeLightElement2(Solver* const solver, IntVar* const var,
609 IntVar* const index1, IntVar* const index2,
610 F values, std::function<bool()> deep_serialize) {
611 return solver->RevAlloc(new LightFunctionElement2Constraint<F>(
612 solver, var, index1, index2, std::move(values),
613 std::move(deep_serialize)));
614}
615
616// Evaluators
617template <class A, class B>
618static int64 ReturnZero(A a, B b) {
619 return 0;
620}
621
622bool TransitCallbackPositive(const RoutingTransitCallback2& callback, int size1,
623 int size2) {
624 for (int i = 0; i < size1; i++) {
625 for (int j = 0; j < size2; j++) {
626 if (callback(i, j) < 0) {
627 return false;
628 }
629 }
630 }
631 return true;
632}
633
634} // namespace
635
636// ----- Routing model -----
637
638static const int kUnassigned = -1;
640
642
644
646 : RoutingModel(index_manager, DefaultRoutingModelParameters()) {}
647
649 const RoutingModelParameters& parameters)
650 : nodes_(index_manager.num_nodes()),
651 vehicles_(index_manager.num_vehicles()),
652 max_active_vehicles_(vehicles_),
653 fixed_cost_of_vehicle_(vehicles_, 0),
654 cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)),
655 linear_cost_factor_of_vehicle_(vehicles_, 0),
656 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
657 vehicle_amortized_cost_factors_set_(false),
658 consider_empty_route_costs_(vehicles_, false),
659 cost_classes_(),
660 costs_are_homogeneous_across_vehicles_(
661 parameters.reduce_vehicle_cost_model()),
662 cache_callbacks_(false),
663 vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)),
664 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
665 has_hard_type_incompatibilities_(false),
666 has_temporal_type_incompatibilities_(false),
667 has_same_vehicle_type_requirements_(false),
668 has_temporal_type_requirements_(false),
669 num_visit_types_(0),
670 starts_(vehicles_),
671 ends_(vehicles_),
672 manager_(index_manager) {
673 // Initialize vehicle costs to the zero evaluator.
674 vehicle_to_transit_cost_.assign(
675 vehicles_, RegisterTransitCallback(ReturnZero<int64, int64>));
676 // Active caching after initializing vehicle_to_transit_cost_ to avoid
677 // uselessly caching ReturnZero.
678 cache_callbacks_ = (nodes_ <= parameters.max_callback_cache_size());
679
680 VLOG(1) << "Model parameters:\n" << parameters.DebugString();
681 ConstraintSolverParameters solver_parameters =
682 parameters.has_solver_parameters() ? parameters.solver_parameters()
684 solver_ = absl::make_unique<Solver>("Routing", solver_parameters);
685 // TODO(user): Remove when removal of NodeIndex is complete.
686 start_end_count_ = index_manager.num_unique_depots();
687 Initialize();
688
689 const int64 size = Size();
690 index_to_pickup_index_pairs_.resize(size);
691 index_to_delivery_index_pairs_.resize(size);
692 index_to_visit_type_.resize(index_manager.num_indices(), kUnassigned);
693 index_to_type_policy_.resize(index_manager.num_indices());
694
695 index_to_vehicle_.resize(index_manager.num_indices(), kUnassigned);
696 for (int v = 0; v < index_manager.num_vehicles(); ++v) {
697 starts_[v] = index_manager.GetStartIndex(v);
698 index_to_vehicle_[starts_[v]] = v;
699 ends_[v] = index_manager.GetEndIndex(v);
700 index_to_vehicle_[ends_[v]] = v;
701 }
702
703 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
704 index_manager.GetIndexToNodeMap();
705 index_to_equivalence_class_.resize(index_manager.num_indices());
706 for (int i = 0; i < index_to_node.size(); ++i) {
707 index_to_equivalence_class_[i] = index_to_node[i].value();
708 }
709 allowed_vehicles_.resize(Size() + vehicles_);
710}
711
712void RoutingModel::Initialize() {
713 const int size = Size();
714 // Next variables
715 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1, "Nexts", &nexts_);
716 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_, false));
717 index_to_disjunctions_.resize(size + vehicles_);
718 // Vehicle variables. In case that node i is not active, vehicle_vars_[i] is
719 // bound to -1.
720 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1, "Vehicles",
721 &vehicle_vars_);
722 // Active variables
723 solver_->MakeBoolVarArray(size, "Active", &active_);
724 // Active vehicle variables
725 solver_->MakeBoolVarArray(vehicles_, "ActiveVehicle", &vehicle_active_);
726 // Variables representing vehicles contributing to cost.
727 solver_->MakeBoolVarArray(vehicles_, "VehicleCostsConsidered",
728 &vehicle_costs_considered_);
729 // Is-bound-to-end variables.
730 solver_->MakeBoolVarArray(size + vehicles_, "IsBoundToEnd",
731 &is_bound_to_end_);
732 // Cost cache
733 cost_cache_.clear();
734 cost_cache_.resize(size + vehicles_, {kUnassigned, CostClassIndex(-1), 0});
735 preassignment_ = solver_->MakeAssignment();
736}
737
739 gtl::STLDeleteElements(&dimensions_);
740
741 // State dependent transit callbacks.
742 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
743 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
744 for (const auto& cache_line : state_dependent_transit_evaluators_cache_) {
745 for (const auto& key_transit : *cache_line) {
746 value_functions_delete.insert(key_transit.second.transit);
747 index_functions_delete.insert(key_transit.second.transit_plus_identity);
748 }
749 }
750 gtl::STLDeleteElements(&value_functions_delete);
751 gtl::STLDeleteElements(&index_functions_delete);
752}
753
754namespace {
755int RegisterCallback(RoutingTransitCallback2 callback, bool is_positive,
757 if (is_positive) {
758 return model->RegisterPositiveTransitCallback(std::move(callback));
759 }
760 return model->RegisterTransitCallback(std::move(callback));
761}
762
763int RegisterUnaryCallback(RoutingTransitCallback1 callback, bool is_positive,
764 RoutingModel* model) {
765 if (is_positive) {
766 return model->RegisterPositiveUnaryTransitCallback(std::move(callback));
767 }
768 return model->RegisterUnaryTransitCallback(std::move(callback));
769}
770} // namespace
771
772int RoutingModel::RegisterUnaryTransitVector(std::vector<int64> values) {
773 return RegisterUnaryCallback(
774 [this, values = std::move(values)](int64 i) {
775 return values[manager_.IndexToNode(i).value()];
776 },
777 /*is_positive=*/
778 std::all_of(std::cbegin(values), std::cend(values),
779 [](int64 transit) { return transit >= 0; }),
780 this);
781}
782
784 const int index = unary_transit_evaluators_.size();
785 unary_transit_evaluators_.push_back(std::move(callback));
786 return RegisterTransitCallback([this, index](int i, int j) {
787 return unary_transit_evaluators_[index](i);
788 });
789}
790
792 std::vector<std::vector<int64> /*needed_for_swig*/> values) {
793 bool all_transits_positive = true;
794 for (const std::vector<int64>& transit_values : values) {
795 all_transits_positive =
796 std::all_of(std::cbegin(transit_values), std::cend(transit_values),
797 [](int64 transit) { return transit >= 0; });
798 if (!all_transits_positive) {
799 break;
800 }
801 }
802 return RegisterCallback(
803 [this, values = std::move(values)](int64 i, int64 j) {
804 return values[manager_.IndexToNode(i).value()]
805 [manager_.IndexToNode(j).value()];
806 },
807 all_transits_positive, this);
808}
809
812 is_transit_evaluator_positive_.push_back(true);
813 DCHECK(TransitCallbackPositive(
814 [&callback](int i, int) { return callback(i); }, Size() + vehicles(), 1));
815 return RegisterUnaryTransitCallback(std::move(callback));
816}
817
819 if (cache_callbacks_) {
820 const int size = Size() + vehicles();
821 std::vector<int64> cache(size * size, 0);
822 for (int i = 0; i < size; ++i) {
823 for (int j = 0; j < size; ++j) {
824 cache[i * size + j] = callback(i, j);
825 }
826 }
827 transit_evaluators_.push_back(
828 [cache, size](int64 i, int64 j) { return cache[i * size + j]; });
829 } else {
830 transit_evaluators_.push_back(std::move(callback));
831 }
832 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
833 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
834 unary_transit_evaluators_.push_back(nullptr);
835 }
836 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
837 DCHECK_EQ(transit_evaluators_.size(),
838 is_transit_evaluator_positive_.size() + 1);
839 is_transit_evaluator_positive_.push_back(false);
840 }
841 return transit_evaluators_.size() - 1;
842}
843
845 is_transit_evaluator_positive_.push_back(true);
846 DCHECK(TransitCallbackPositive(callback, Size() + vehicles(),
847 Size() + vehicles()));
848 return RegisterTransitCallback(std::move(callback));
849}
850
853 state_dependent_transit_evaluators_cache_.push_back(
854 absl::make_unique<StateDependentTransitCallbackCache>());
855 StateDependentTransitCallbackCache* const cache =
856 state_dependent_transit_evaluators_cache_.back().get();
857 state_dependent_transit_evaluators_.push_back(
858 [cache, callback](int64 i, int64 j) {
860 if (gtl::FindCopy(*cache, CacheKey(i, j), &value)) return value;
861 value = callback(i, j);
862 cache->insert({CacheKey(i, j), value});
863 return value;
864 });
865 return state_dependent_transit_evaluators_.size() - 1;
866}
867
868void RoutingModel::AddNoCycleConstraintInternal() {
869 if (no_cycle_constraint_ == nullptr) {
870 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
871 solver_->AddConstraint(no_cycle_constraint_);
872 }
873}
874
875bool RoutingModel::AddDimension(int evaluator_index, int64 slack_max,
876 int64 capacity, bool fix_start_cumul_to_zero,
877 const std::string& name) {
878 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
879 std::vector<int64> capacities(vehicles_, capacity);
880 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
881 std::move(capacities),
882 fix_start_cumul_to_zero, name);
883}
884
886 const std::vector<int>& evaluator_indices, int64 slack_max, int64 capacity,
887 bool fix_start_cumul_to_zero, const std::string& name) {
888 std::vector<int64> capacities(vehicles_, capacity);
889 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
890 std::move(capacities),
891 fix_start_cumul_to_zero, name);
892}
893
895 int evaluator_index, int64 slack_max, std::vector<int64> vehicle_capacities,
896 bool fix_start_cumul_to_zero, const std::string& name) {
897 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
898 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
899 std::move(vehicle_capacities),
900 fix_start_cumul_to_zero, name);
901}
902
904 const std::vector<int>& evaluator_indices, int64 slack_max,
905 std::vector<int64> vehicle_capacities, bool fix_start_cumul_to_zero,
906 const std::string& name) {
907 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
908 std::move(vehicle_capacities),
909 fix_start_cumul_to_zero, name);
910}
911
912bool RoutingModel::AddDimensionWithCapacityInternal(
913 const std::vector<int>& evaluator_indices, int64 slack_max,
914 std::vector<int64> vehicle_capacities, bool fix_start_cumul_to_zero,
915 const std::string& name) {
916 CHECK_EQ(vehicles_, vehicle_capacities.size());
917 return InitializeDimensionInternal(
918 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
919 new RoutingDimension(this, std::move(vehicle_capacities), name, nullptr));
920}
921
922bool RoutingModel::InitializeDimensionInternal(
923 const std::vector<int>& evaluator_indices,
924 const std::vector<int>& state_dependent_evaluator_indices, int64 slack_max,
925 bool fix_start_cumul_to_zero, RoutingDimension* dimension) {
926 CHECK(dimension != nullptr);
927 CHECK_EQ(vehicles_, evaluator_indices.size());
928 CHECK((dimension->base_dimension_ == nullptr &&
929 state_dependent_evaluator_indices.empty()) ||
930 vehicles_ == state_dependent_evaluator_indices.size());
931 if (!HasDimension(dimension->name())) {
932 const DimensionIndex dimension_index(dimensions_.size());
933 dimension_name_to_index_[dimension->name()] = dimension_index;
934 dimensions_.push_back(dimension);
935 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
936 slack_max);
937 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
938 nexts_, active_, dimension->cumuls(), dimension->transits()));
939 if (fix_start_cumul_to_zero) {
940 for (int i = 0; i < vehicles_; ++i) {
941 IntVar* const start_cumul = dimension->CumulVar(Start(i));
942 CHECK_EQ(0, start_cumul->Min());
943 start_cumul->SetValue(0);
944 }
945 }
946 return true;
947 }
948 delete dimension;
949 return false;
950}
951
953 int64 value, int64 capacity, int64 slack_max, bool fix_start_cumul_to_zero,
954 const std::string& dimension_name) {
955 const int evaluator_index =
956 RegisterUnaryCallback([value](int64) { return value; },
957 /*is_positive=*/value >= 0, this);
958 return std::make_pair(evaluator_index,
959 AddDimension(evaluator_index, slack_max, capacity,
960 fix_start_cumul_to_zero, dimension_name));
961}
962
964 std::vector<int64> values, int64 capacity, bool fix_start_cumul_to_zero,
965 const std::string& dimension_name) {
966 const int evaluator_index = RegisterUnaryTransitVector(std::move(values));
967 return std::make_pair(evaluator_index,
968 AddDimension(evaluator_index, 0, capacity,
969 fix_start_cumul_to_zero, dimension_name));
970}
971
973 std::vector<std::vector<int64>> values, int64 capacity,
974 bool fix_start_cumul_to_zero, const std::string& dimension_name) {
975 const int evaluator_index = RegisterTransitMatrix(std::move(values));
976 return std::make_pair(evaluator_index,
977 AddDimension(evaluator_index, 0, capacity,
978 fix_start_cumul_to_zero, dimension_name));
979}
980
981namespace {
982// RangeMakeElementExpr is an IntExpr that corresponds to a
983// RangeIntToIntFunction indexed by an IntVar.
984// Do not create this class dicretly, but rather use MakeRangeMakeElementExpr.
985class RangeMakeElementExpr : public BaseIntExpr {
986 public:
987 RangeMakeElementExpr(const RangeIntToIntFunction* callback, IntVar* index,
988 Solver* s)
989 : BaseIntExpr(s), callback_(ABSL_DIE_IF_NULL(callback)), index_(index) {
990 CHECK(callback_ != nullptr);
991 CHECK(index != nullptr);
992 }
993
994 int64 Min() const override {
995 // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max).
996 const int idx_min = index_->Min();
997 const int idx_max = index_->Max() + 1;
998 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
999 : kint64max;
1000 }
1001 void SetMin(int64 new_min) override {
1002 const int64 old_min = Min();
1003 const int64 old_max = Max();
1004 if (old_min < new_min && new_min <= old_max) {
1005 const int64 old_idx_min = index_->Min();
1006 const int64 old_idx_max = index_->Max() + 1;
1007 if (old_idx_min < old_idx_max) {
1008 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1009 old_idx_min, old_idx_max, new_min, old_max + 1);
1010 index_->SetMin(new_idx_min);
1011 if (new_idx_min < old_idx_max) {
1012 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1013 new_idx_min, old_idx_max, new_min, old_max + 1);
1014 index_->SetMax(new_idx_max);
1015 }
1016 }
1017 }
1018 }
1019 int64 Max() const override {
1020 // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max).
1021 const int idx_min = index_->Min();
1022 const int idx_max = index_->Max() + 1;
1023 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1024 : kint64min;
1025 }
1026 void SetMax(int64 new_max) override {
1027 const int64 old_min = Min();
1028 const int64 old_max = Max();
1029 if (old_min <= new_max && new_max < old_max) {
1030 const int64 old_idx_min = index_->Min();
1031 const int64 old_idx_max = index_->Max() + 1;
1032 if (old_idx_min < old_idx_max) {
1033 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1034 old_idx_min, old_idx_max, old_min, new_max + 1);
1035 index_->SetMin(new_idx_min);
1036 if (new_idx_min < old_idx_max) {
1037 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1038 new_idx_min, old_idx_max, old_min, new_max + 1);
1039 index_->SetMax(new_idx_max);
1040 }
1041 }
1042 }
1043 }
1044 void WhenRange(Demon* d) override { index_->WhenRange(d); }
1045
1046 private:
1047 const RangeIntToIntFunction* const callback_;
1048 IntVar* const index_;
1049};
1050
1051IntExpr* MakeRangeMakeElementExpr(const RangeIntToIntFunction* callback,
1052 IntVar* index, Solver* s) {
1053 return s->RegisterIntExpr(
1054 s->RevAlloc(new RangeMakeElementExpr(callback, index, s)));
1055}
1056} // namespace
1057
1059 const std::vector<int>& dependent_transits,
1060 const RoutingDimension* base_dimension, int64 slack_max,
1061 std::vector<int64> vehicle_capacities, bool fix_start_cumul_to_zero,
1062 const std::string& name) {
1063 const std::vector<int> pure_transits(vehicles_, /*zero_evaluator*/ 0);
1065 pure_transits, dependent_transits, base_dimension, slack_max,
1066 std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
1067}
1068
1070 int transit, const RoutingDimension* dimension, int64 slack_max,
1071 int64 vehicle_capacity, bool fix_start_cumul_to_zero,
1072 const std::string& name) {
1074 /*zero_evaluator*/ 0, transit, dimension, slack_max, vehicle_capacity,
1075 fix_start_cumul_to_zero, name);
1076}
1077
1078bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1079 const std::vector<int>& pure_transits,
1080 const std::vector<int>& dependent_transits,
1081 const RoutingDimension* base_dimension, int64 slack_max,
1082 std::vector<int64> vehicle_capacities, bool fix_start_cumul_to_zero,
1083 const std::string& name) {
1084 CHECK_EQ(vehicles_, vehicle_capacities.size());
1085 RoutingDimension* new_dimension = nullptr;
1086 if (base_dimension == nullptr) {
1087 new_dimension = new RoutingDimension(this, std::move(vehicle_capacities),
1088 name, RoutingDimension::SelfBased());
1089 } else {
1090 new_dimension = new RoutingDimension(this, std::move(vehicle_capacities),
1091 name, base_dimension);
1092 }
1093 return InitializeDimensionInternal(pure_transits, dependent_transits,
1094 slack_max, fix_start_cumul_to_zero,
1095 new_dimension);
1096}
1097
1099 int pure_transit, int dependent_transit,
1100 const RoutingDimension* base_dimension, int64 slack_max,
1101 int64 vehicle_capacity, bool fix_start_cumul_to_zero,
1102 const std::string& name) {
1103 std::vector<int> pure_transits(vehicles_, pure_transit);
1104 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1105 std::vector<int64> vehicle_capacities(vehicles_, vehicle_capacity);
1106 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1107 pure_transits, dependent_transits, base_dimension, slack_max,
1108 std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
1109}
1110
1112 const std::function<int64(int64)>& f, int64 domain_start,
1113 int64 domain_end) {
1114 const std::function<int64(int64)> g = [&f](int64 x) { return f(x) + x; };
1115 // The next line is safe, because MakeCachedIntToIntFunction does not count
1116 // on keeping the closure of its first argument alive.
1117 return {MakeCachedIntToIntFunction(f, domain_start, domain_end),
1118 MakeCachedRangeMinMaxIndexFunction(g, domain_start, domain_end)};
1119}
1120
1121std::vector<std::string> RoutingModel::GetAllDimensionNames() const {
1122 std::vector<std::string> dimension_names;
1123 for (const auto& dimension_name_index : dimension_name_to_index_) {
1124 dimension_names.push_back(dimension_name_index.first);
1125 }
1126 std::sort(dimension_names.begin(), dimension_names.end());
1127 return dimension_names;
1128}
1129
1131 const RoutingDimension& dimension) const {
1132 const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1133 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1134 global_optimizer_index_[dim_index] < 0) {
1135 return nullptr;
1136 }
1137 const int optimizer_index = global_optimizer_index_[dim_index];
1138 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1139 return global_dimension_optimizers_[optimizer_index].get();
1140}
1141
1143 const RoutingDimension& dimension) const {
1144 const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1145 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1146 local_optimizer_index_[dim_index] < 0) {
1147 return nullptr;
1148 }
1149 const int optimizer_index = local_optimizer_index_[dim_index];
1150 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1151 return local_dimension_optimizers_[optimizer_index].get();
1152}
1153
1155 const RoutingDimension& dimension) const {
1156 const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1157 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1158 local_optimizer_index_[dim_index] < 0) {
1159 return nullptr;
1160 }
1161 const int optimizer_index = local_optimizer_index_[dim_index];
1162 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1163 return local_dimension_mp_optimizers_[optimizer_index].get();
1164}
1165
1166bool RoutingModel::HasDimension(const std::string& dimension_name) const {
1167 return gtl::ContainsKey(dimension_name_to_index_, dimension_name);
1168}
1169
1170RoutingModel::DimensionIndex RoutingModel::GetDimensionIndex(
1171 const std::string& dimension_name) const {
1172 return gtl::FindWithDefault(dimension_name_to_index_, dimension_name,
1173 kNoDimension);
1174}
1175
1177 const std::string& dimension_name) const {
1178 return *dimensions_[gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1179}
1180
1182 const std::string& dimension_name) const {
1183 const DimensionIndex index = GetDimensionIndex(dimension_name);
1184 if (index != kNoDimension) {
1185 return dimensions_[index];
1186 }
1187 return nullptr;
1188}
1189
1191 CHECK_LT(0, vehicles_);
1192 for (int i = 0; i < vehicles_; ++i) {
1193 SetArcCostEvaluatorOfVehicle(evaluator_index, i);
1194 }
1195}
1196
1198 int vehicle) {
1199 CHECK_LT(vehicle, vehicles_);
1200 CHECK_LT(evaluator_index, transit_evaluators_.size());
1201 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1202}
1203
1205 for (int i = 0; i < vehicles_; ++i) {
1207 }
1208}
1209
1211 CHECK_LT(vehicle, vehicles_);
1212 return fixed_cost_of_vehicle_[vehicle];
1213}
1214
1216 CHECK_LT(vehicle, vehicles_);
1217 DCHECK_GE(cost, 0);
1218 fixed_cost_of_vehicle_[vehicle] = cost;
1219}
1220
1222 int64 linear_cost_factor, int64 quadratic_cost_factor) {
1223 for (int v = 0; v < vehicles_; v++) {
1224 SetAmortizedCostFactorsOfVehicle(linear_cost_factor, quadratic_cost_factor,
1225 v);
1226 }
1227}
1228
1230 int64 quadratic_cost_factor,
1231 int vehicle) {
1232 CHECK_LT(vehicle, vehicles_);
1233 DCHECK_GE(linear_cost_factor, 0);
1234 DCHECK_GE(quadratic_cost_factor, 0);
1235 if (linear_cost_factor + quadratic_cost_factor > 0) {
1236 vehicle_amortized_cost_factors_set_ = true;
1237 }
1238 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1239 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1240}
1241
1242namespace {
1243// Some C++ versions used in the open-source export don't support comparison
1244// functors for STL containers; so we need a comparator class instead.
1245struct CostClassComparator {
1246 bool operator()(const RoutingModel::CostClass& a,
1247 const RoutingModel::CostClass& b) const {
1249 }
1250};
1251
1252struct VehicleClassComparator {
1253 bool operator()(const RoutingModel::VehicleClass& a,
1254 const RoutingModel::VehicleClass& b) const {
1256 }
1257};
1258} // namespace
1259
1260// static
1261const RoutingModel::CostClassIndex RoutingModel::kCostClassIndexOfZeroCost =
1262 CostClassIndex(0);
1263
1264void RoutingModel::ComputeCostClasses(
1265 const RoutingSearchParameters& parameters) {
1266 // Create and reduce the cost classes.
1267 cost_classes_.reserve(vehicles_);
1268 cost_classes_.clear();
1269 cost_class_index_of_vehicle_.assign(vehicles_, CostClassIndex(-1));
1270 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1271
1272 // Pre-insert the built-in cost class 'zero cost' with index 0.
1273 const CostClass zero_cost_class(0);
1274 cost_classes_.push_back(zero_cost_class);
1275 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1276 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1277
1278 // Determine the canonicalized cost class for each vehicle, and insert it as
1279 // a new cost class if it doesn't exist already. Building cached evaluators
1280 // on the way.
1281 has_vehicle_with_zero_cost_class_ = false;
1282 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1283 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1284
1285 // Insert the dimension data in a canonical way.
1286 for (const RoutingDimension* const dimension : dimensions_) {
1287 const int64 coeff = dimension->vehicle_span_cost_coefficients()[vehicle];
1288 if (coeff == 0) continue;
1289 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1290 .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1291 }
1292 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1293 .begin(),
1294 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1295 .end());
1296 // Try inserting the CostClass, if it's not already present.
1297 const CostClassIndex num_cost_classes(cost_classes_.size());
1298 const CostClassIndex cost_class_index =
1299 gtl::LookupOrInsert(&cost_class_map, cost_class, num_cost_classes);
1300 if (cost_class_index == kCostClassIndexOfZeroCost) {
1301 has_vehicle_with_zero_cost_class_ = true;
1302 } else if (cost_class_index == num_cost_classes) { // New cost class.
1303 cost_classes_.push_back(cost_class);
1304 }
1305 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1306 }
1307
1308 // TRICKY:
1309 // If some vehicle had the "zero" cost class, then we'll have homogeneous
1310 // vehicles iff they all have that cost class (i.e. cost class count = 1).
1311 // If none of them have it, then we have homogeneous costs iff there are two
1312 // cost classes: the unused "zero" cost class and the one used by all
1313 // vehicles.
1314 // Note that we always need the zero cost class, even if no vehicle uses it,
1315 // because we use it in the vehicle_var = -1 scenario (i.e. unperformed).
1316 //
1317 // Fixed costs are simply ignored for computing these cost classes. They are
1318 // attached to start nodes directly.
1319 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1320 ? GetCostClassesCount() == 1
1321 : GetCostClassesCount() <= 2;
1322}
1323
1325 const VehicleClass& b) {
1326 return std::tie(a.cost_class_index, a.fixed_cost, a.start_equivalence_class,
1327 a.end_equivalence_class, a.unvisitable_nodes_fprint,
1328 a.dimension_start_cumuls_min, a.dimension_start_cumuls_max,
1329 a.dimension_end_cumuls_min, a.dimension_end_cumuls_max,
1330 a.dimension_capacities, a.dimension_evaluator_classes) <
1331 std::tie(b.cost_class_index, b.fixed_cost, b.start_equivalence_class,
1332 b.end_equivalence_class, b.unvisitable_nodes_fprint,
1333 b.dimension_start_cumuls_min, b.dimension_start_cumuls_max,
1334 b.dimension_end_cumuls_min, b.dimension_end_cumuls_max,
1335 b.dimension_capacities, b.dimension_evaluator_classes);
1336}
1337
1338void RoutingModel::ComputeVehicleClasses() {
1339 vehicle_classes_.reserve(vehicles_);
1340 vehicle_classes_.clear();
1341 vehicle_class_index_of_vehicle_.assign(vehicles_, VehicleClassIndex(-1));
1342 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1343 vehicle_class_map;
1344 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1345 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1346 new char[nodes_unvisitability_num_bytes]);
1347 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1348 VehicleClass vehicle_class;
1349 vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1350 vehicle_class.fixed_cost = fixed_cost_of_vehicle_[vehicle];
1351 vehicle_class.start_equivalence_class =
1352 index_to_equivalence_class_[Start(vehicle)];
1353 vehicle_class.end_equivalence_class =
1354 index_to_equivalence_class_[End(vehicle)];
1355 for (const RoutingDimension* const dimension : dimensions_) {
1356 IntVar* const start_cumul_var = dimension->cumuls()[Start(vehicle)];
1358 start_cumul_var->Min());
1360 start_cumul_var->Max());
1361 IntVar* const end_cumul_var = dimension->cumuls()[End(vehicle)];
1362 vehicle_class.dimension_end_cumuls_min.push_back(end_cumul_var->Min());
1363 vehicle_class.dimension_end_cumuls_max.push_back(end_cumul_var->Max());
1364 vehicle_class.dimension_capacities.push_back(
1365 dimension->vehicle_capacities()[vehicle]);
1367 dimension->vehicle_to_class(vehicle));
1368 }
1369 memset(nodes_unvisitability_bitmask.get(), 0,
1370 nodes_unvisitability_num_bytes);
1371 for (int index = 0; index < vehicle_vars_.size(); ++index) {
1372 IntVar* const vehicle_var = vehicle_vars_[index];
1373 if (!IsStart(index) && !IsEnd(index) &&
1374 (!vehicle_var->Contains(vehicle) ||
1375 !IsVehicleAllowedForIndex(vehicle, index))) {
1376 nodes_unvisitability_bitmask[index / CHAR_BIT] |= 1U
1377 << (index % CHAR_BIT);
1378 }
1379 }
1380 vehicle_class.unvisitable_nodes_fprint = ThoroughHash(
1381 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1382 const VehicleClassIndex num_vehicle_classes(vehicle_classes_.size());
1383 const VehicleClassIndex vehicle_class_index = gtl::LookupOrInsert(
1384 &vehicle_class_map, vehicle_class, num_vehicle_classes);
1385 if (vehicle_class_index == num_vehicle_classes) { // New vehicle class
1386 vehicle_classes_.push_back(vehicle_class);
1387 }
1388 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1389 }
1390}
1391
1392void RoutingModel::ComputeVehicleTypes() {
1393 const int nodes_squared = nodes_ * nodes_;
1394 std::vector<int>& type_index_of_vehicle =
1395 vehicle_type_container_.type_index_of_vehicle;
1396 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1397 sorted_vehicle_classes_per_type =
1398 vehicle_type_container_.sorted_vehicle_classes_per_type;
1399 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1400 vehicle_type_container_.vehicles_per_vehicle_class;
1401
1402 type_index_of_vehicle.resize(vehicles_);
1403 sorted_vehicle_classes_per_type.clear();
1404 sorted_vehicle_classes_per_type.reserve(vehicles_);
1405 vehicles_per_vehicle_class.clear();
1406 vehicles_per_vehicle_class.resize(GetVehicleClassesCount());
1407
1408 absl::flat_hash_map<int64, int> type_to_type_index;
1409
1410 for (int v = 0; v < vehicles_; v++) {
1411 const int start = manager_.IndexToNode(Start(v)).value();
1412 const int end = manager_.IndexToNode(End(v)).value();
1413 const int cost_class = GetCostClassIndexOfVehicle(v).value();
1414 const int64 type = cost_class * nodes_squared + start * nodes_ + end;
1415
1416 const auto& vehicle_type_added = type_to_type_index.insert(
1417 std::make_pair(type, type_to_type_index.size()));
1418
1419 const int index = vehicle_type_added.first->second;
1420
1421 const int vehicle_class = GetVehicleClassIndexOfVehicle(v).value();
1422 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1423 vehicle_class, GetFixedCostOfVehicle(v)};
1424
1425 if (vehicle_type_added.second) {
1426 // Type was not indexed yet.
1427 DCHECK_EQ(sorted_vehicle_classes_per_type.size(), index);
1428 sorted_vehicle_classes_per_type.push_back({class_entry});
1429 } else {
1430 // Type already indexed.
1431 DCHECK_LT(index, sorted_vehicle_classes_per_type.size());
1432 sorted_vehicle_classes_per_type[index].insert(class_entry);
1433 }
1434 vehicles_per_vehicle_class[vehicle_class].push_back(v);
1435 type_index_of_vehicle[v] = index;
1436 }
1437}
1438
1439void RoutingModel::FinalizeVisitTypes() {
1440 // NOTE(user): This is necessary if CloseVisitTypes() was not called
1441 // explicitly before. This will be removed when the TODO regarding this logic
1442 // is addressed.
1444
1445 single_nodes_of_type_.clear();
1446 single_nodes_of_type_.resize(num_visit_types_);
1447 pair_indices_of_type_.clear();
1448 pair_indices_of_type_.resize(num_visit_types_);
1449 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1450 num_visit_types_);
1451
1452 for (int index = 0; index < index_to_visit_type_.size(); index++) {
1453 const int visit_type = GetVisitType(index);
1454 if (visit_type < 0) {
1455 continue;
1456 }
1457 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1458 index_to_pickup_index_pairs_[index];
1459 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1460 index_to_delivery_index_pairs_[index];
1461 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1462 single_nodes_of_type_[visit_type].push_back(index);
1463 }
1464 for (const std::vector<std::pair<int, int>>* index_pairs :
1465 {&pickup_index_pairs, &delivery_index_pairs}) {
1466 for (const std::pair<int, int>& index_pair : *index_pairs) {
1467 const int pair_index = index_pair.first;
1468 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1469 pair_indices_of_type_[visit_type].push_back(pair_index);
1470 }
1471 }
1472 }
1473 }
1474
1475 TopologicallySortVisitTypes();
1476}
1477
1478void RoutingModel::TopologicallySortVisitTypes() {
1479 if (!has_same_vehicle_type_requirements_ &&
1480 !has_temporal_type_requirements_) {
1481 return;
1482 }
1483 std::vector<std::pair<double, double>> type_requirement_tightness(
1484 num_visit_types_, {0, 0});
1485 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1486 num_visit_types_);
1487 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1488 std::vector<int> in_degree(num_visit_types_, 0);
1489 for (int type = 0; type < num_visit_types_; type++) {
1490 int num_alternative_required_types = 0;
1491 int num_required_sets = 0;
1492 for (const std::vector<absl::flat_hash_set<int>>*
1493 required_type_alternatives :
1494 {&required_type_alternatives_when_adding_type_index_[type],
1495 &required_type_alternatives_when_removing_type_index_[type],
1496 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1497 for (const absl::flat_hash_set<int>& alternatives :
1498 *required_type_alternatives) {
1499 types_in_requirement_graph.Set(type);
1500 num_required_sets++;
1501 for (int required_type : alternatives) {
1502 type_requirement_tightness[required_type].second +=
1503 1.0 / alternatives.size();
1504 types_in_requirement_graph.Set(required_type);
1505 num_alternative_required_types++;
1506 if (type_to_dependent_types[required_type].insert(type).second) {
1507 in_degree[type]++;
1508 }
1509 }
1510 }
1511 }
1512 if (num_alternative_required_types > 0) {
1513 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1514 num_required_sets /
1515 num_alternative_required_types;
1516 }
1517 }
1518
1519 // Compute topological order of visit types.
1520 topologically_sorted_visit_types_.clear();
1521 std::vector<int> current_types_with_zero_indegree;
1522 for (int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1523 DCHECK(type_requirement_tightness[type].first > 0 ||
1524 type_requirement_tightness[type].second > 0);
1525 if (in_degree[type] == 0) {
1526 current_types_with_zero_indegree.push_back(type);
1527 }
1528 }
1529
1530 int num_types_added = 0;
1531 while (!current_types_with_zero_indegree.empty()) {
1532 // Add all zero-degree nodes to the same topological order group, while
1533 // also marking their dependent types that become part of the next group.
1534 topologically_sorted_visit_types_.push_back({});
1535 std::vector<int>& topological_group =
1536 topologically_sorted_visit_types_.back();
1537 std::vector<int> next_types_with_zero_indegree;
1538 for (int type : current_types_with_zero_indegree) {
1539 topological_group.push_back(type);
1540 num_types_added++;
1541 for (int dependent_type : type_to_dependent_types[type]) {
1542 DCHECK_GT(in_degree[dependent_type], 0);
1543 if (--in_degree[dependent_type] == 0) {
1544 next_types_with_zero_indegree.push_back(dependent_type);
1545 }
1546 }
1547 }
1548 // Sort the types in the current topological group based on their
1549 // requirement tightness.
1550 // NOTE: For a deterministic order, types with equal tightness are sorted by
1551 // increasing type.
1552 // TODO(user): Put types of the same topological order and same
1553 // requirement tightness in a single group (so that they all get inserted
1554 // simultaneously by the GlobalCheapestInsertion heuristic, for instance).
1555 std::sort(topological_group.begin(), topological_group.end(),
1556 [&type_requirement_tightness](int type1, int type2) {
1557 const auto& tightness1 = type_requirement_tightness[type1];
1558 const auto& tightness2 = type_requirement_tightness[type2];
1559 return tightness1 > tightness2 ||
1560 (tightness1 == tightness2 && type1 < type2);
1561 });
1562 // Swap the current types with zero in-degree with the next ones.
1563 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1564 }
1565
1566 const int num_types_in_requirement_graph =
1567 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1568 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1569 if (num_types_added < num_types_in_requirement_graph) {
1570 // Requirement graph is cyclic, no topological order.
1571 topologically_sorted_visit_types_.clear();
1572 }
1573}
1574
1576 const std::vector<int64>& indices, int64 penalty, int64 max_cardinality) {
1577 CHECK_GE(max_cardinality, 1);
1578 for (int i = 0; i < indices.size(); ++i) {
1579 CHECK_NE(kUnassigned, indices[i]);
1580 }
1581
1582 const DisjunctionIndex disjunction_index(disjunctions_.size());
1583 disjunctions_.push_back({indices, {penalty, max_cardinality}});
1584 for (const int64 index : indices) {
1585 index_to_disjunctions_[index].push_back(disjunction_index);
1586 }
1587 return disjunction_index;
1588}
1589
1590std::vector<std::pair<int64, int64>>
1592 std::vector<std::pair<int64, int64>> var_index_pairs;
1593 for (const Disjunction& disjunction : disjunctions_) {
1594 const std::vector<int64>& var_indices = disjunction.indices;
1595 if (var_indices.size() != 2) continue;
1596 const int64 v0 = var_indices[0];
1597 const int64 v1 = var_indices[1];
1598 if (index_to_disjunctions_[v0].size() == 1 &&
1599 index_to_disjunctions_[v1].size() == 1) {
1600 // We output sorted pairs.
1601 var_index_pairs.push_back({std::min(v0, v1), std::max(v0, v1)});
1602 }
1603 }
1604 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1605 return var_index_pairs;
1606}
1607
1609 CHECK(!closed_);
1610 for (Disjunction& disjunction : disjunctions_) {
1611 bool has_one_potentially_active_var = false;
1612 for (const int64 var_index : disjunction.indices) {
1613 if (ActiveVar(var_index)->Max() > 0) {
1614 has_one_potentially_active_var = true;
1615 break;
1616 }
1617 }
1618 if (!has_one_potentially_active_var) {
1619 disjunction.value.max_cardinality = 0;
1620 }
1621 }
1622}
1623
1624IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) {
1625 const std::vector<int64>& indices = disjunctions_[disjunction].indices;
1626 const int indices_size = indices.size();
1627 std::vector<IntVar*> disjunction_vars(indices_size);
1628 for (int i = 0; i < indices_size; ++i) {
1629 const int64 index = indices[i];
1630 CHECK_LT(index, Size());
1631 disjunction_vars[i] = ActiveVar(index);
1632 }
1633 const int64 max_cardinality =
1634 disjunctions_[disjunction].value.max_cardinality;
1635 IntVar* no_active_var = solver_->MakeBoolVar();
1636 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1637 solver_->AddConstraint(
1638 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1639 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1640 number_active_vars, max_cardinality, no_active_var));
1641 const int64 penalty = disjunctions_[disjunction].value.penalty;
1642 if (penalty < 0) {
1643 no_active_var->SetMax(0);
1644 return nullptr;
1645 } else {
1646 return solver_->MakeProd(no_active_var, penalty)->Var();
1647 }
1648}
1649
1651 const std::vector<int64>& indices, int64 cost) {
1652 if (!indices.empty()) {
1653 ValuedNodes<int64> same_vehicle_cost;
1654 for (const int64 index : indices) {
1655 same_vehicle_cost.indices.push_back(index);
1656 }
1657 same_vehicle_cost.value = cost;
1658 same_vehicle_costs_.push_back(same_vehicle_cost);
1659 }
1660}
1661
1663 int64 index) {
1664 auto& allowed_vehicles = allowed_vehicles_[index];
1665 allowed_vehicles.clear();
1666 for (int vehicle : vehicles) {
1667 allowed_vehicles.insert(vehicle);
1668 }
1669}
1670
1672 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1673 pickup_delivery_disjunctions_.push_back({kNoDisjunction, kNoDisjunction});
1674}
1675
1677 DisjunctionIndex pickup_disjunction,
1678 DisjunctionIndex delivery_disjunction) {
1679 AddPickupAndDeliverySetsInternal(GetDisjunctionIndices(pickup_disjunction),
1680 GetDisjunctionIndices(delivery_disjunction));
1681 pickup_delivery_disjunctions_.push_back(
1682 {pickup_disjunction, delivery_disjunction});
1683}
1684
1685void RoutingModel::AddPickupAndDeliverySetsInternal(
1686 const std::vector<int64>& pickups, const std::vector<int64>& deliveries) {
1687 if (pickups.empty() || deliveries.empty()) {
1688 return;
1689 }
1690 const int64 size = Size();
1691 const int pair_index = pickup_delivery_pairs_.size();
1692 for (int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1693 const int64 pickup = pickups[pickup_index];
1694 CHECK_LT(pickup, size);
1695 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1696 }
1697 for (int delivery_index = 0; delivery_index < deliveries.size();
1698 delivery_index++) {
1699 const int64 delivery = deliveries[delivery_index];
1700 CHECK_LT(delivery, size);
1701 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1702 delivery_index);
1703 }
1704 pickup_delivery_pairs_.push_back({pickups, deliveries});
1705}
1706
1707const std::vector<std::pair<int, int>>& RoutingModel::GetPickupIndexPairs(
1708 int64 node_index) const {
1709 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1710 return index_to_pickup_index_pairs_[node_index];
1711}
1712
1713const std::vector<std::pair<int, int>>& RoutingModel::GetDeliveryIndexPairs(
1714 int64 node_index) const {
1715 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1716 return index_to_delivery_index_pairs_[node_index];
1717}
1718
1720 PickupAndDeliveryPolicy policy, int vehicle) {
1721 CHECK_LT(vehicle, vehicles_);
1722 vehicle_pickup_delivery_policy_[vehicle] = policy;
1723}
1724
1726 PickupAndDeliveryPolicy policy) {
1727 CHECK_LT(0, vehicles_);
1728 for (int i = 0; i < vehicles_; ++i) {
1730 }
1731}
1732
1735 CHECK_LT(vehicle, vehicles_);
1736 return vehicle_pickup_delivery_policy_[vehicle];
1737}
1738
1740 int count = 0;
1741 for (int i = 0; i < Nexts().size(); ++i) {
1742 // End nodes have no next variables.
1743 if (!IsStart(i) && GetPickupIndexPairs(i).empty() &&
1744 GetDeliveryIndexPairs(i).empty()) {
1745 ++count;
1746 }
1747 }
1748 return count;
1749}
1750
1751IntVar* RoutingModel::CreateSameVehicleCost(int vehicle_index) {
1752 const std::vector<int64>& indices =
1753 same_vehicle_costs_[vehicle_index].indices;
1754 CHECK(!indices.empty());
1755 std::vector<IntVar*> vehicle_counts;
1756 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1757 &vehicle_counts);
1758 std::vector<int64> vehicle_values(vehicle_vars_.size() + 1);
1759 for (int i = 0; i < vehicle_vars_.size(); ++i) {
1760 vehicle_values[i] = i;
1761 }
1762 vehicle_values[vehicle_vars_.size()] = -1;
1763 std::vector<IntVar*> vehicle_vars;
1764 vehicle_vars.reserve(indices.size());
1765 for (const int64 index : indices) {
1766 vehicle_vars.push_back(vehicle_vars_[index]);
1767 }
1768 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1769 std::vector<IntVar*> vehicle_used;
1770 for (int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1771 vehicle_used.push_back(
1772 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1773 }
1774 vehicle_used.push_back(solver_->MakeIntConst(-1));
1775 return solver_
1776 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1777 same_vehicle_costs_[vehicle_index].value)
1778 ->Var();
1779}
1780
1782 extra_operators_.push_back(ls_operator);
1783}
1784
1785int64 RoutingModel::GetDepot() const { return vehicles() > 0 ? Start(0) : -1; }
1786
1787// TODO(user): Remove the need for the homogeneous version once the
1788// vehicle var to cost class element constraint is fast enough.
1789void RoutingModel::AppendHomogeneousArcCosts(
1790 const RoutingSearchParameters& parameters, int node_index,
1791 std::vector<IntVar*>* cost_elements) {
1792 CHECK(cost_elements != nullptr);
1793 const auto arc_cost_evaluator = [this, node_index](int64 next_index) {
1794 return GetHomogeneousCost(node_index, next_index);
1795 };
1796 if (UsesLightPropagation(parameters)) {
1797 // Only supporting positive costs.
1798 // TODO(user): Detect why changing lower bound to kint64min stalls
1799 // the search in GLS in some cases (Solomon instances for instance).
1800 IntVar* const base_cost_var = solver_->MakeIntVar(0, kint64max);
1801 solver_->AddConstraint(MakeLightElement(
1802 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1803 [this]() { return enable_deep_serialization_; }));
1804 IntVar* const var =
1805 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1806 cost_elements->push_back(var);
1807 } else {
1808 IntExpr* const expr =
1809 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1810 IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var();
1811 cost_elements->push_back(var);
1812 }
1813}
1814
1815void RoutingModel::AppendArcCosts(const RoutingSearchParameters& parameters,
1816 int node_index,
1817 std::vector<IntVar*>* cost_elements) {
1818 CHECK(cost_elements != nullptr);
1819 DCHECK_GT(vehicles_, 0);
1820 if (UsesLightPropagation(parameters)) {
1821 // Only supporting positive costs.
1822 // TODO(user): Detect why changing lower bound to kint64min stalls
1823 // the search in GLS in some cases (Solomon instances for instance).
1824 IntVar* const base_cost_var = solver_->MakeIntVar(0, kint64max);
1825 solver_->AddConstraint(MakeLightElement2(
1826 solver_.get(), base_cost_var, nexts_[node_index],
1827 vehicle_vars_[node_index],
1828 [this, node_index](int64 to, int64 vehicle) {
1829 return GetArcCostForVehicle(node_index, to, vehicle);
1830 },
1831 [this]() { return enable_deep_serialization_; }));
1832 IntVar* const var =
1833 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1834 cost_elements->push_back(var);
1835 } else {
1836 IntVar* const vehicle_class_var =
1837 solver_
1838 ->MakeElement(
1839 [this](int64 index) {
1840 return SafeGetCostClassInt64OfVehicle(index);
1841 },
1842 vehicle_vars_[node_index])
1843 ->Var();
1844 IntExpr* const expr = solver_->MakeElement(
1845 [this, node_index](int64 next, int64 vehicle_class) {
1846 return GetArcCostForClass(node_index, next, vehicle_class);
1847 },
1848 nexts_[node_index], vehicle_class_var);
1849 IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var();
1850 cost_elements->push_back(var);
1851 }
1852}
1853
1854int RoutingModel::GetVehicleStartClass(int64 start_index) const {
1855 const int vehicle = index_to_vehicle_[start_index];
1856 if (vehicle != kUnassigned) {
1857 return GetVehicleClassIndexOfVehicle(vehicle).value();
1858 }
1859 return kUnassigned;
1860}
1861
1862std::string RoutingModel::FindErrorInSearchParametersForModel(
1863 const RoutingSearchParameters& search_parameters) const {
1864 const FirstSolutionStrategy::Value first_solution_strategy =
1865 search_parameters.first_solution_strategy();
1866 if (GetFirstSolutionDecisionBuilder(search_parameters) == nullptr) {
1867 return absl::StrCat(
1868 "Undefined first solution strategy: ",
1869 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1870 " (int value: ", first_solution_strategy, ")");
1871 }
1872 if (search_parameters.first_solution_strategy() ==
1873 FirstSolutionStrategy::SWEEP &&
1874 sweep_arranger() == nullptr) {
1875 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1876 }
1877 return "";
1878}
1879
1880void RoutingModel::QuietCloseModel() {
1881 QuietCloseModelWithParameters(DefaultRoutingSearchParameters());
1882}
1883
1886}
1887
1889 public:
1891 same_vehicle_components_.SetNumberOfNodes(model->Size());
1892 for (const std::string& name : model->GetAllDimensionNames()) {
1893 RoutingDimension* const dimension = model->GetMutableDimension(name);
1894 const std::vector<IntVar*>& cumuls = dimension->cumuls();
1895 for (int i = 0; i < cumuls.size(); ++i) {
1896 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
1897 }
1898 }
1899 const std::vector<IntVar*>& vehicle_vars = model->VehicleVars();
1900 for (int i = 0; i < vehicle_vars.size(); ++i) {
1901 vehicle_var_to_indices_[vehicle_vars[i]] = i;
1902 }
1903 RegisterInspectors();
1904 }
1906 void EndVisitModel(const std::string& solver_name) override {
1907 const std::vector<int> node_to_same_vehicle_component_id =
1908 same_vehicle_components_.GetComponentIds();
1909 model_->InitSameVehicleGroups(
1910 same_vehicle_components_.GetNumberOfComponents());
1911 for (int node = 0; node < model_->Size(); ++node) {
1912 model_->SetSameVehicleGroup(node,
1913 node_to_same_vehicle_component_id[node]);
1914 }
1915 // TODO(user): Perform transitive closure of dimension precedence graphs.
1916 // TODO(user): Have a single annotated precedence graph.
1917 }
1918 void EndVisitConstraint(const std::string& type_name,
1919 const Constraint* const constraint) override {
1920 gtl::FindWithDefault(constraint_inspectors_, type_name, []() {})();
1921 }
1922 void VisitIntegerExpressionArgument(const std::string& type_name,
1923 IntExpr* const expr) override {
1924 gtl::FindWithDefault(expr_inspectors_, type_name,
1925 [](const IntExpr* expr) {})(expr);
1926 }
1927 void VisitIntegerArrayArgument(const std::string& arg_name,
1928 const std::vector<int64>& values) override {
1929 gtl::FindWithDefault(array_inspectors_, arg_name,
1930 [](const std::vector<int64>& int_array) {})(values);
1931 }
1932
1933 private:
1934 using ExprInspector = std::function<void(const IntExpr*)>;
1935 using ArrayInspector = std::function<void(const std::vector<int64>&)>;
1936 using ConstraintInspector = std::function<void()>;
1937
1938 void RegisterInspectors() {
1939 expr_inspectors_[kExpressionArgument] = [this](const IntExpr* expr) {
1940 expr_ = expr;
1941 };
1942 expr_inspectors_[kLeftArgument] = [this](const IntExpr* expr) {
1943 left_ = expr;
1944 };
1945 expr_inspectors_[kRightArgument] = [this](const IntExpr* expr) {
1946 right_ = expr;
1947 };
1948 array_inspectors_[kStartsArgument] =
1949 [this](const std::vector<int64>& int_array) {
1950 starts_argument_ = int_array;
1951 };
1952 array_inspectors_[kEndsArgument] =
1953 [this](const std::vector<int64>& int_array) {
1954 ends_argument_ = int_array;
1955 };
1956 constraint_inspectors_[kNotMember] = [this]() {
1957 std::pair<RoutingDimension*, int> dim_index;
1958 if (gtl::FindCopy(cumul_to_dim_indices_, expr_, &dim_index)) {
1959 RoutingDimension* const dimension = dim_index.first;
1960 const int index = dim_index.second;
1961 dimension->forbidden_intervals_[index].InsertIntervals(starts_argument_,
1962 ends_argument_);
1963 VLOG(2) << dimension->name() << " " << index << ": "
1964 << dimension->forbidden_intervals_[index].DebugString();
1965 }
1966 expr_ = nullptr;
1967 starts_argument_.clear();
1968 ends_argument_.clear();
1969 };
1970 constraint_inspectors_[kEquality] = [this]() {
1971 int left_index = 0;
1972 int right_index = 0;
1973 if (gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
1974 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
1975 VLOG(2) << "Vehicle variables for " << left_index << " and "
1976 << right_index << " are equal.";
1977 same_vehicle_components_.AddEdge(left_index, right_index);
1978 }
1979 left_ = nullptr;
1980 right_ = nullptr;
1981 };
1982 constraint_inspectors_[kLessOrEqual] = [this]() {
1983 std::pair<RoutingDimension*, int> left_index;
1984 std::pair<RoutingDimension*, int> right_index;
1985 if (gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
1986 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
1987 RoutingDimension* const dimension = left_index.first;
1988 if (dimension == right_index.first) {
1989 VLOG(2) << "For dimension " << dimension->name() << ", cumul for "
1990 << left_index.second << " is less than " << right_index.second
1991 << ".";
1992 dimension->path_precedence_graph_.AddArc(left_index.second,
1993 right_index.second);
1994 }
1995 }
1996 left_ = nullptr;
1997 right_ = nullptr;
1998 };
1999 }
2000
2001 RoutingModel* const model_;
2002 DenseConnectedComponentsFinder same_vehicle_components_;
2003 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2004 cumul_to_dim_indices_;
2005 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2006 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2007 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2008 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2009 const IntExpr* expr_ = nullptr;
2010 const IntExpr* left_ = nullptr;
2011 const IntExpr* right_ = nullptr;
2012 std::vector<int64> starts_argument_;
2013 std::vector<int64> ends_argument_;
2014};
2015
2016void RoutingModel::DetectImplicitPickupAndDeliveries() {
2017 std::vector<int> non_pickup_delivery_nodes;
2018 for (int node = 0; node < Size(); ++node) {
2019 if (!IsStart(node) && GetPickupIndexPairs(node).empty() &&
2020 GetDeliveryIndexPairs(node).empty()) {
2021 non_pickup_delivery_nodes.push_back(node);
2022 }
2023 }
2024 // Needs to be sorted for stability.
2025 std::set<std::pair<int64, int64>> implicit_pickup_deliveries;
2026 for (const RoutingDimension* const dimension : dimensions_) {
2027 if (dimension->class_evaluators_.size() != 1) {
2028 continue;
2029 }
2030 const TransitCallback1& transit =
2031 UnaryTransitCallbackOrNull(dimension->class_evaluators_[0]);
2032 if (transit == nullptr) continue;
2033 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_positive_demand;
2034 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_negative_demand;
2035 for (int node : non_pickup_delivery_nodes) {
2036 const int64 demand = transit(node);
2037 if (demand > 0) {
2038 nodes_by_positive_demand[demand].push_back(node);
2039 } else if (demand < 0) {
2040 nodes_by_negative_demand[-demand].push_back(node);
2041 }
2042 }
2043 for (const auto& [demand, positive_nodes] : nodes_by_positive_demand) {
2044 const std::vector<int64>* const negative_nodes =
2045 gtl::FindOrNull(nodes_by_negative_demand, demand);
2046 if (negative_nodes != nullptr) {
2047 for (int64 positive_node : positive_nodes) {
2048 for (int64 negative_node : *negative_nodes) {
2049 implicit_pickup_deliveries.insert({positive_node, negative_node});
2050 }
2051 }
2052 }
2053 }
2054 }
2055 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2056 for (auto [pickup, delivery] : implicit_pickup_deliveries) {
2057 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2058 std::vector<int64>({pickup}), std::vector<int64>({delivery}));
2059 }
2060}
2061
2063 const RoutingSearchParameters& parameters) {
2065 if (!error.empty()) {
2066 status_ = ROUTING_INVALID;
2067 LOG(ERROR) << "Invalid RoutingSearchParameters: " << error;
2068 return;
2069 }
2070 if (closed_) {
2071 LOG(WARNING) << "Model already closed";
2072 return;
2073 }
2074 closed_ = true;
2075
2076 for (RoutingDimension* const dimension : dimensions_) {
2077 dimension->CloseModel(UsesLightPropagation(parameters));
2078 }
2079 ComputeCostClasses(parameters);
2080 ComputeVehicleClasses();
2081 ComputeVehicleTypes();
2082 FinalizeVisitTypes();
2083 vehicle_start_class_callback_ = [this](int64 start) {
2084 return GetVehicleStartClass(start);
2085 };
2086
2087 AddNoCycleConstraintInternal();
2088
2089 const int size = Size();
2090
2091 // Vehicle variable constraints
2092 for (int i = 0; i < vehicles_; ++i) {
2093 const int64 start = starts_[i];
2094 const int64 end = ends_[i];
2095 solver_->AddConstraint(
2096 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2097 solver_->AddConstraint(
2098 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2099 solver_->AddConstraint(
2100 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2101 if (consider_empty_route_costs_[i]) {
2102 vehicle_costs_considered_[i]->SetMin(1);
2103 } else {
2104 solver_->AddConstraint(solver_->MakeEquality(
2105 vehicle_active_[i], vehicle_costs_considered_[i]));
2106 }
2107 }
2108
2109 // Limit the number of vehicles with non-empty routes.
2110 if (vehicles_ > max_active_vehicles_) {
2111 solver_->AddConstraint(
2112 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2113 }
2114
2115 // If there is only one vehicle in the model the vehicle variables will have
2116 // a maximum domain of [-1, 0]. If a node is performed/active then its vehicle
2117 // variable will be reduced to [0] making the path-cumul constraint below
2118 // useless. If the node is unperformed/unactive then its vehicle variable will
2119 // be reduced to [-1] in any case.
2120 if (vehicles_ > 1) {
2121 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2122 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2123 nexts_, active_, vehicle_vars_, zero_transit));
2124 }
2125
2126 // Nodes which are not in a disjunction are mandatory, and those with a
2127 // trivially infeasible type are necessarily unperformed
2128 for (int i = 0; i < size; ++i) {
2129 if (GetDisjunctionIndices(i).empty() && active_[i]->Max() != 0) {
2130 active_[i]->SetValue(1);
2131 }
2132 const int type = GetVisitType(i);
2133 if (type == kUnassigned) {
2134 continue;
2135 }
2136 const absl::flat_hash_set<VisitTypePolicy>* const infeasible_policies =
2137 gtl::FindOrNull(trivially_infeasible_visit_types_to_policies_, type);
2138 if (infeasible_policies != nullptr &&
2139 gtl::ContainsKey(*infeasible_policies, index_to_type_policy_[i])) {
2140 active_[i]->SetValue(0);
2141 }
2142 }
2143
2144 // Reduce domains of vehicle variables
2145 for (int i = 0; i < allowed_vehicles_.size(); ++i) {
2146 const auto& allowed_vehicles = allowed_vehicles_[i];
2147 if (!allowed_vehicles.empty()) {
2148 std::vector<int64> vehicles;
2149 vehicles.reserve(allowed_vehicles.size() + 1);
2150 vehicles.push_back(-1);
2151 for (int vehicle : allowed_vehicles) {
2152 vehicles.push_back(vehicle);
2153 }
2154 solver_->AddConstraint(solver_->MakeMemberCt(VehicleVar(i), vehicles));
2155 }
2156 }
2157
2158 // Reduce domain of next variables.
2159 for (int i = 0; i < size; ++i) {
2160 // No variable can point back to a start.
2161 solver_->AddConstraint(solver_->RevAlloc(
2162 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2163 // Extra constraint to state an active node can't point to itself.
2164 solver_->AddConstraint(
2165 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2166 }
2167
2168 // Add constraints to bind vehicle_vars_[i] to -1 in case that node i is not
2169 // active.
2170 for (int i = 0; i < size; ++i) {
2171 solver_->AddConstraint(
2172 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2173 }
2174
2175 if (HasTypeRegulations()) {
2176 solver_->AddConstraint(
2177 solver_->RevAlloc(new TypeRegulationsConstraint(*this)));
2178 }
2179
2180 // Associate first and "logical" last nodes
2181 for (int i = 0; i < vehicles_; ++i) {
2182 std::vector<int64> forbidden_ends;
2183 forbidden_ends.reserve(vehicles_ - 1);
2184 for (int j = 0; j < vehicles_; ++j) {
2185 if (i != j) {
2186 forbidden_ends.push_back(ends_[j]);
2187 }
2188 }
2189 solver_->AddConstraint(solver_->RevAlloc(new DifferentFromValues(
2190 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2191 }
2192
2193 // Constraining is_bound_to_end_ variables.
2194 for (const int64 end : ends_) {
2195 is_bound_to_end_[end]->SetValue(1);
2196 }
2197
2198 std::vector<IntVar*> cost_elements;
2199 // Arc and dimension costs.
2200 if (vehicles_ > 0) {
2201 for (int node_index = 0; node_index < size; ++node_index) {
2203 AppendHomogeneousArcCosts(parameters, node_index, &cost_elements);
2204 } else {
2205 AppendArcCosts(parameters, node_index, &cost_elements);
2206 }
2207 }
2208 if (vehicle_amortized_cost_factors_set_) {
2209 std::vector<IntVar*> route_lengths;
2210 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2211 solver_->AddConstraint(
2212 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2213 std::vector<IntVar*> vehicle_used;
2214 for (int i = 0; i < vehicles_; i++) {
2215 // The start/end of the vehicle are always on the route.
2216 vehicle_used.push_back(
2217 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2218 IntVar* const var =
2219 solver_
2220 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2221 solver_->MakeSum(route_lengths[i], -2))),
2222 quadratic_cost_factor_of_vehicle_[i])
2223 ->Var();
2224 cost_elements.push_back(var);
2225 }
2226 IntVar* const vehicle_usage_cost =
2227 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2228 ->Var();
2229 cost_elements.push_back(vehicle_usage_cost);
2230 }
2231 }
2232 // Dimension span constraints: cost and limits.
2233 for (const RoutingDimension* dimension : dimensions_) {
2234 dimension->SetupGlobalSpanCost(&cost_elements);
2235 dimension->SetupSlackAndDependentTransitCosts();
2236 const std::vector<int64>& span_costs =
2237 dimension->vehicle_span_cost_coefficients();
2238 const std::vector<int64>& span_ubs = dimension->vehicle_span_upper_bounds();
2239 const bool has_span_constraint =
2240 std::any_of(span_costs.begin(), span_costs.end(),
2241 [](int64 coeff) { return coeff != 0; }) ||
2242 std::any_of(span_ubs.begin(), span_ubs.end(),
2243 [](int64 value) { return value < kint64max; }) ||
2244 dimension->HasSoftSpanUpperBounds() ||
2245 dimension->HasQuadraticCostSoftSpanUpperBounds();
2246 if (has_span_constraint) {
2247 std::vector<IntVar*> spans(vehicles(), nullptr);
2248 std::vector<IntVar*> total_slacks(vehicles(), nullptr);
2249 // Generate variables only where needed.
2250 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2251 if (span_ubs[vehicle] < kint64max) {
2252 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle], "");
2253 }
2254 if (span_costs[vehicle] != 0) {
2255 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle], "");
2256 }
2257 }
2258 if (dimension->HasSoftSpanUpperBounds()) {
2259 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2260 if (spans[vehicle]) continue;
2261 const SimpleBoundCosts::BoundCost bound_cost =
2262 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2263 if (bound_cost.cost == 0) continue;
2264 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2265 }
2266 }
2267 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2268 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2269 if (spans[vehicle]) continue;
2270 const SimpleBoundCosts::BoundCost bound_cost =
2271 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2272 if (bound_cost.cost == 0) continue;
2273 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2274 }
2275 }
2276 solver_->AddConstraint(
2277 MakePathSpansAndTotalSlacks(dimension, spans, total_slacks));
2278 // If a vehicle's span is constrained, its start/end cumuls must be
2279 // instantiated.
2280 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2281 if (!spans[vehicle] && !total_slacks[vehicle]) continue;
2282 if (spans[vehicle]) {
2283 AddVariableTargetToFinalizer(spans[vehicle], kint64min);
2284 }
2285 AddVariableTargetToFinalizer(dimension->CumulVar(End(vehicle)),
2286 kint64min);
2287 AddVariableTargetToFinalizer(dimension->CumulVar(Start(vehicle)),
2288 kint64max);
2289 }
2290 // Add costs of variables.
2291 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2292 if (span_costs[vehicle] == 0) continue;
2293 DCHECK(total_slacks[vehicle] != nullptr);
2294 IntVar* const slack_amount =
2295 solver_
2296 ->MakeProd(vehicle_costs_considered_[vehicle],
2297 total_slacks[vehicle])
2298 ->Var();
2299 IntVar* const slack_cost =
2300 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2301 cost_elements.push_back(slack_cost);
2303 span_costs[vehicle]);
2304 }
2305 if (dimension->HasSoftSpanUpperBounds()) {
2306 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2307 const auto bound_cost =
2308 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2309 if (bound_cost.cost == 0 || bound_cost.bound == kint64max) continue;
2310 DCHECK(spans[vehicle] != nullptr);
2311 // Additional cost is vehicle_cost_considered_[vehicle] *
2312 // max(0, spans[vehicle] - bound_cost.bound) * bound_cost.cost.
2313 IntVar* const span_violation_amount =
2314 solver_
2315 ->MakeProd(
2316 vehicle_costs_considered_[vehicle],
2317 solver_->MakeMax(
2318 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2319 0))
2320 ->Var();
2321 IntVar* const span_violation_cost =
2322 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2323 cost_elements.push_back(span_violation_cost);
2324 AddWeightedVariableMinimizedByFinalizer(span_violation_amount,
2325 bound_cost.cost);
2326 }
2327 }
2328 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2329 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2330 const auto bound_cost =
2331 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2332 if (bound_cost.cost == 0 || bound_cost.bound == kint64max) continue;
2333 DCHECK(spans[vehicle] != nullptr);
2334 // Additional cost is vehicle_cost_considered_[vehicle] *
2335 // max(0, spans[vehicle] - bound_cost.bound)^2 * bound_cost.cost.
2336 IntExpr* max0 = solver_->MakeMax(
2337 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2338 IntVar* const squared_span_violation_amount =
2339 solver_
2340 ->MakeProd(vehicle_costs_considered_[vehicle],
2341 solver_->MakeSquare(max0))
2342 ->Var();
2343 IntVar* const span_violation_cost =
2344 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2345 ->Var();
2346 cost_elements.push_back(span_violation_cost);
2347 AddWeightedVariableMinimizedByFinalizer(squared_span_violation_amount,
2348 bound_cost.cost);
2349 }
2350 }
2351 }
2352 }
2353 // Penalty costs
2354 for (DisjunctionIndex i(0); i < disjunctions_.size(); ++i) {
2355 IntVar* penalty_var = CreateDisjunction(i);
2356 if (penalty_var != nullptr) {
2357 cost_elements.push_back(penalty_var);
2358 }
2359 }
2360 // Soft cumul lower/upper bound costs
2361 for (const RoutingDimension* dimension : dimensions_) {
2362 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2363 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2364 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2365 }
2366 // Same vehicle costs
2367 for (int i = 0; i < same_vehicle_costs_.size(); ++i) {
2368 cost_elements.push_back(CreateSameVehicleCost(i));
2369 }
2370 cost_ = solver_->MakeSum(cost_elements)->Var();
2371 cost_->set_name("Cost");
2372
2373 // Pickup-delivery precedences
2374 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2375 for (const auto& pair : pickup_delivery_pairs_) {
2376 DCHECK(!pair.first.empty() && !pair.second.empty());
2377 for (int pickup : pair.first) {
2378 for (int delivery : pair.second) {
2379 pickup_delivery_precedences.emplace_back(pickup, delivery);
2380 }
2381 }
2382 }
2383 std::vector<int> lifo_vehicles;
2384 std::vector<int> fifo_vehicles;
2385 for (int i = 0; i < vehicles_; ++i) {
2386 switch (vehicle_pickup_delivery_policy_[i]) {
2388 break;
2390 lifo_vehicles.push_back(Start(i));
2391 break;
2393 fifo_vehicles.push_back(Start(i));
2394 break;
2395 }
2396 }
2397 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2398 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2399
2400 // Detect constraints
2401 enable_deep_serialization_ = false;
2402 std::unique_ptr<RoutingModelInspector> inspector(
2403 new RoutingModelInspector(this));
2404 solver_->Accept(inspector.get());
2405 enable_deep_serialization_ = true;
2406
2407 for (const RoutingDimension* const dimension : dimensions_) {
2408 // Dimension path precedences, discovered by model inspection (which must be
2409 // performed before adding path transit precedences).
2410 const ReverseArcListGraph<int, int>& graph =
2411 dimension->GetPathPrecedenceGraph();
2412 std::vector<std::pair<int, int>> path_precedences;
2413 for (const auto tail : graph.AllNodes()) {
2414 for (const auto head : graph[tail]) {
2415 path_precedences.emplace_back(tail, head);
2416 }
2417 }
2418 if (!path_precedences.empty()) {
2419 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2420 nexts_, dimension->transits(), path_precedences));
2421 }
2422
2423 // Dimension node precedences.
2424 for (const RoutingDimension::NodePrecedence& node_precedence :
2425 dimension->GetNodePrecedences()) {
2426 const int64 first_node = node_precedence.first_node;
2427 const int64 second_node = node_precedence.second_node;
2428 IntExpr* const nodes_are_selected =
2429 solver_->MakeMin(active_[first_node], active_[second_node]);
2430 IntExpr* const cumul_difference = solver_->MakeDifference(
2431 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2432 IntVar* const cumul_difference_is_ge_offset =
2433 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2434 node_precedence.offset);
2435 // Forces the implication: both nodes are active => cumul difference
2436 // constraint is active.
2437 solver_->AddConstraint(solver_->MakeLessOrEqual(
2438 nodes_are_selected->Var(), cumul_difference_is_ge_offset));
2439 }
2440 }
2441
2442 DetectImplicitPickupAndDeliveries();
2443
2444 // Store the local/global cumul optimizers, along with their offsets.
2445 StoreDimensionCumulOptimizers(parameters);
2446
2447 // Keep this out of SetupSearch as this contains static search objects.
2448 // This will allow calling SetupSearch multiple times with different search
2449 // parameters.
2450 CreateNeighborhoodOperators(parameters);
2451 CreateFirstSolutionDecisionBuilders(parameters);
2452 error = FindErrorInSearchParametersForModel(parameters);
2453 if (!error.empty()) {
2454 status_ = ROUTING_INVALID;
2455 LOG(ERROR) << "Invalid RoutingSearchParameters for this model: " << error;
2456 return;
2457 }
2458 SetupSearch(parameters);
2459}
2460
2461struct Link {
2462 Link(std::pair<int, int> link, double value, int vehicle_class,
2463 int64 start_depot, int64 end_depot)
2464 : link(link),
2465 value(value),
2466 vehicle_class(vehicle_class),
2467 start_depot(start_depot),
2468 end_depot(end_depot) {}
2470
2471 std::pair<int, int> link;
2476};
2477
2478struct LinkSort {
2479 bool operator()(const Link& link1, const Link& link2) const {
2480 return (link1.value > link2.value);
2481 }
2483
2484// The RouteConstructor creates the routes of a VRP instance subject to its
2485// constraints by iterating on a list of arcs appearing in descending order
2486// of priority.
2487// TODO(user): Use the dimension class in this class.
2488// TODO(user): Add support for vehicle-dependent dimension transits.
2490 public:
2492 bool check_assignment, int64 num_indices,
2493 const std::vector<Link>& links_list)
2494 : assignment_(assignment),
2495 model_(model),
2496 check_assignment_(check_assignment),
2497 solver_(model_->solver()),
2498 num_indices_(num_indices),
2499 links_list_(links_list),
2500 nexts_(model_->Nexts()),
2501 in_route_(num_indices_, -1),
2502 final_routes_(),
2503 index_to_chain_index_(num_indices, -1),
2504 index_to_vehicle_class_index_(num_indices, -1) {
2505 {
2506 const std::vector<std::string> dimension_names =
2507 model_->GetAllDimensionNames();
2508 dimensions_.assign(dimension_names.size(), nullptr);
2509 for (int i = 0; i < dimension_names.size(); ++i) {
2510 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
2511 }
2512 }
2513 cumuls_.resize(dimensions_.size());
2514 for (std::vector<int64>& cumuls : cumuls_) {
2515 cumuls.resize(num_indices_);
2516 }
2517 new_possible_cumuls_.resize(dimensions_.size());
2518 }
2519
2521
2522 void Construct() {
2523 model_->solver()->TopPeriodicCheck();
2524 // Initial State: Each order is served by its own vehicle.
2525 for (int index = 0; index < num_indices_; ++index) {
2526 if (!model_->IsStart(index) && !model_->IsEnd(index)) {
2527 std::vector<int> route(1, index);
2528 routes_.push_back(route);
2529 in_route_[index] = routes_.size() - 1;
2530 }
2531 }
2532
2533 for (const Link& link : links_list_) {
2534 model_->solver()->TopPeriodicCheck();
2535 const int index1 = link.link.first;
2536 const int index2 = link.link.second;
2537 const int vehicle_class = link.vehicle_class;
2538 const int64 start_depot = link.start_depot;
2539 const int64 end_depot = link.end_depot;
2540
2541 // Initialisation of cumuls_ if the indices are encountered for first time
2542 if (index_to_vehicle_class_index_[index1] < 0) {
2543 for (int dimension_index = 0; dimension_index < dimensions_.size();
2544 ++dimension_index) {
2545 cumuls_[dimension_index][index1] =
2546 std::max(dimensions_[dimension_index]->GetTransitValue(
2547 start_depot, index1, 0),
2548 dimensions_[dimension_index]->CumulVar(index1)->Min());
2549 }
2550 }
2551 if (index_to_vehicle_class_index_[index2] < 0) {
2552 for (int dimension_index = 0; dimension_index < dimensions_.size();
2553 ++dimension_index) {
2554 cumuls_[dimension_index][index2] =
2555 std::max(dimensions_[dimension_index]->GetTransitValue(
2556 start_depot, index2, 0),
2557 dimensions_[dimension_index]->CumulVar(index2)->Min());
2558 }
2559 }
2560
2561 const int route_index1 = in_route_[index1];
2562 const int route_index2 = in_route_[index2];
2563 const bool merge =
2564 route_index1 >= 0 && route_index2 >= 0 &&
2565 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
2566 index2, route_index1, route_index2, vehicle_class,
2567 start_depot, end_depot);
2568 if (Merge(merge, route_index1, route_index2)) {
2569 index_to_vehicle_class_index_[index1] = vehicle_class;
2570 index_to_vehicle_class_index_[index2] = vehicle_class;
2571 }
2572 }
2573
2574 model_->solver()->TopPeriodicCheck();
2575 // Beyond this point not checking limits anymore as the rest of the code is
2576 // linear and that given we managed to build a solution would be stupid to
2577 // drop it now.
2578 for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2579 if (!gtl::ContainsKey(deleted_chains_, chain_index)) {
2580 final_chains_.push_back(chains_[chain_index]);
2581 }
2582 }
2583 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
2584 for (int route_index = 0; route_index < routes_.size(); ++route_index) {
2585 if (!gtl::ContainsKey(deleted_routes_, route_index)) {
2586 final_routes_.push_back(routes_[route_index]);
2587 }
2588 }
2589 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
2590
2591 const int extra_vehicles = std::max(
2592 0, static_cast<int>(final_chains_.size()) - model_->vehicles());
2593 // Bind the Start and End of each chain
2594 int chain_index = 0;
2595 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
2596 ++chain_index) {
2597 if (chain_index - extra_vehicles >= model_->vehicles()) {
2598 break;
2599 }
2600 const int start = final_chains_[chain_index].head;
2601 const int end = final_chains_[chain_index].tail;
2602 assignment_->Add(
2603 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2604 assignment_->SetValue(
2605 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
2606 assignment_->Add(nexts_[end]);
2607 assignment_->SetValue(nexts_[end],
2608 model_->End(chain_index - extra_vehicles));
2609 }
2610
2611 // Create the single order routes
2612 for (int route_index = 0; route_index < final_routes_.size();
2613 ++route_index) {
2614 if (chain_index - extra_vehicles >= model_->vehicles()) {
2615 break;
2616 }
2617 DCHECK_LT(route_index, final_routes_.size());
2618 const int head = final_routes_[route_index].front();
2619 const int tail = final_routes_[route_index].back();
2620 if (head == tail && head < model_->Size()) {
2621 assignment_->Add(
2622 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2623 assignment_->SetValue(
2624 model_->NextVar(model_->Start(chain_index - extra_vehicles)), head);
2625 assignment_->Add(nexts_[tail]);
2626 assignment_->SetValue(nexts_[tail],
2627 model_->End(chain_index - extra_vehicles));
2628 ++chain_index;
2629 }
2630 }
2631
2632 // Unperformed
2633 for (int index = 0; index < model_->Size(); ++index) {
2634 IntVar* const next = nexts_[index];
2635 if (!assignment_->Contains(next)) {
2636 assignment_->Add(next);
2637 if (next->Contains(index)) {
2638 assignment_->SetValue(next, index);
2639 }
2640 }
2641 }
2642 }
2643
2644 const std::vector<std::vector<int>>& final_routes() const {
2645 return final_routes_;
2646 }
2647
2648 private:
2649 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
2650
2651 struct RouteSort {
2652 bool operator()(const std::vector<int>& route1,
2653 const std::vector<int>& route2) const {
2654 return (route1.size() < route2.size());
2655 }
2656 } RouteComparator;
2657
2658 struct Chain {
2659 int head;
2660 int tail;
2661 int nodes;
2662 };
2663
2664 struct ChainSort {
2665 bool operator()(const Chain& chain1, const Chain& chain2) const {
2666 return (chain1.nodes < chain2.nodes);
2667 }
2668 } ChainComparator;
2669
2670 bool Head(int node) const {
2671 return (node == routes_[in_route_[node]].front());
2672 }
2673
2674 bool Tail(int node) const {
2675 return (node == routes_[in_route_[node]].back());
2676 }
2677
2678 bool FeasibleRoute(const std::vector<int>& route, int64 route_cumul,
2679 int dimension_index) {
2680 const RoutingDimension& dimension = *dimensions_[dimension_index];
2681 std::vector<int>::const_iterator it = route.begin();
2682 int64 cumul = route_cumul;
2683 while (it != route.end()) {
2684 const int previous = *it;
2685 const int64 cumul_previous = cumul;
2686 gtl::InsertOrDie(&(new_possible_cumuls_[dimension_index]), previous,
2687 cumul_previous);
2688 ++it;
2689 if (it == route.end()) {
2690 return true;
2691 }
2692 const int next = *it;
2693 int64 available_from_previous =
2694 cumul_previous + dimension.GetTransitValue(previous, next, 0);
2695 int64 available_cumul_next =
2696 std::max(cumuls_[dimension_index][next], available_from_previous);
2697
2698 const int64 slack = available_cumul_next - available_from_previous;
2699 if (slack > dimension.SlackVar(previous)->Max()) {
2700 available_cumul_next =
2701 available_from_previous + dimension.SlackVar(previous)->Max();
2702 }
2703
2704 if (available_cumul_next > dimension.CumulVar(next)->Max()) {
2705 return false;
2706 }
2707 if (available_cumul_next <= cumuls_[dimension_index][next]) {
2708 return true;
2709 }
2710 cumul = available_cumul_next;
2711 }
2712 return true;
2713 }
2714
2715 bool CheckRouteConnection(const std::vector<int>& route1,
2716 const std::vector<int>& route2, int dimension_index,
2717 int64 start_depot, int64 end_depot) {
2718 const int tail1 = route1.back();
2719 const int head2 = route2.front();
2720 const int tail2 = route2.back();
2721 const RoutingDimension& dimension = *dimensions_[dimension_index];
2722 int non_depot_node = -1;
2723 for (int node = 0; node < num_indices_; ++node) {
2724 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
2725 non_depot_node = node;
2726 break;
2727 }
2728 }
2729 CHECK_GE(non_depot_node, 0);
2730 const int64 depot_threshold =
2731 std::max(dimension.SlackVar(non_depot_node)->Max(),
2732 dimension.CumulVar(non_depot_node)->Max());
2733
2734 int64 available_from_tail1 = cumuls_[dimension_index][tail1] +
2735 dimension.GetTransitValue(tail1, head2, 0);
2736 int64 new_available_cumul_head2 =
2737 std::max(cumuls_[dimension_index][head2], available_from_tail1);
2738
2739 const int64 slack = new_available_cumul_head2 - available_from_tail1;
2740 if (slack > dimension.SlackVar(tail1)->Max()) {
2741 new_available_cumul_head2 =
2742 available_from_tail1 + dimension.SlackVar(tail1)->Max();
2743 }
2744
2745 bool feasible_route = true;
2746 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
2747 return false;
2748 }
2749 if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
2750 return true;
2751 }
2752
2753 feasible_route =
2754 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
2755 const int64 new_possible_cumul_tail2 =
2756 gtl::ContainsKey(new_possible_cumuls_[dimension_index], tail2)
2757 ? new_possible_cumuls_[dimension_index][tail2]
2758 : cumuls_[dimension_index][tail2];
2759
2760 if (!feasible_route || (new_possible_cumul_tail2 +
2761 dimension.GetTransitValue(tail2, end_depot, 0) >
2762 depot_threshold)) {
2763 return false;
2764 }
2765 return true;
2766 }
2767
2768 bool FeasibleMerge(const std::vector<int>& route1,
2769 const std::vector<int>& route2, int node1, int node2,
2770 int route_index1, int route_index2, int vehicle_class,
2771 int64 start_depot, int64 end_depot) {
2772 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
2773 return false;
2774 }
2775
2776 // Vehicle Class Check
2777 if (!((index_to_vehicle_class_index_[node1] == -1 &&
2778 index_to_vehicle_class_index_[node2] == -1) ||
2779 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2780 index_to_vehicle_class_index_[node2] == -1) ||
2781 (index_to_vehicle_class_index_[node1] == -1 &&
2782 index_to_vehicle_class_index_[node2] == vehicle_class) ||
2783 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2784 index_to_vehicle_class_index_[node2] == vehicle_class))) {
2785 return false;
2786 }
2787
2788 // Check Route1 -> Route2 connection for every dimension
2789 bool merge = true;
2790 for (int dimension_index = 0; dimension_index < dimensions_.size();
2791 ++dimension_index) {
2792 new_possible_cumuls_[dimension_index].clear();
2793 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
2794 start_depot, end_depot);
2795 if (!merge) {
2796 return false;
2797 }
2798 }
2799 return true;
2800 }
2801
2802 bool CheckTempAssignment(Assignment* const temp_assignment,
2803 int new_chain_index, int old_chain_index, int head1,
2804 int tail1, int head2, int tail2) {
2805 // TODO(user): If the chain index is greater than the number of vehicles,
2806 // use another vehicle instead.
2807 if (new_chain_index >= model_->vehicles()) return false;
2808 const int start = head1;
2809 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
2810 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
2811 start);
2812 temp_assignment->Add(nexts_[tail1]);
2813 temp_assignment->SetValue(nexts_[tail1], head2);
2814 temp_assignment->Add(nexts_[tail2]);
2815 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
2816 for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2817 if ((chain_index != new_chain_index) &&
2818 (chain_index != old_chain_index) &&
2819 (!gtl::ContainsKey(deleted_chains_, chain_index))) {
2820 const int start = chains_[chain_index].head;
2821 const int end = chains_[chain_index].tail;
2822 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
2823 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
2824 start);
2825 temp_assignment->Add(nexts_[end]);
2826 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
2827 }
2828 }
2829 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
2830 }
2831
2832 bool UpdateAssignment(const std::vector<int>& route1,
2833 const std::vector<int>& route2) {
2834 bool feasible = true;
2835 const int head1 = route1.front();
2836 const int tail1 = route1.back();
2837 const int head2 = route2.front();
2838 const int tail2 = route2.back();
2839 const int chain_index1 = index_to_chain_index_[head1];
2840 const int chain_index2 = index_to_chain_index_[head2];
2841 if (chain_index1 < 0 && chain_index2 < 0) {
2842 const int chain_index = chains_.size();
2843 if (check_assignment_) {
2844 Assignment* const temp_assignment =
2845 solver_->MakeAssignment(assignment_);
2846 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
2847 tail1, head2, tail2);
2848 }
2849 if (feasible) {
2850 Chain chain;
2851 chain.head = head1;
2852 chain.tail = tail2;
2853 chain.nodes = 2;
2854 index_to_chain_index_[head1] = chain_index;
2855 index_to_chain_index_[tail2] = chain_index;
2856 chains_.push_back(chain);
2857 }
2858 } else if (chain_index1 >= 0 && chain_index2 < 0) {
2859 if (check_assignment_) {
2860 Assignment* const temp_assignment =
2861 solver_->MakeAssignment(assignment_);
2862 feasible =
2863 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2864 head1, tail1, head2, tail2);
2865 }
2866 if (feasible) {
2867 index_to_chain_index_[tail2] = chain_index1;
2868 chains_[chain_index1].head = head1;
2869 chains_[chain_index1].tail = tail2;
2870 ++chains_[chain_index1].nodes;
2871 }
2872 } else if (chain_index1 < 0 && chain_index2 >= 0) {
2873 if (check_assignment_) {
2874 Assignment* const temp_assignment =
2875 solver_->MakeAssignment(assignment_);
2876 feasible =
2877 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
2878 head1, tail1, head2, tail2);
2879 }
2880 if (feasible) {
2881 index_to_chain_index_[head1] = chain_index2;
2882 chains_[chain_index2].head = head1;
2883 chains_[chain_index2].tail = tail2;
2884 ++chains_[chain_index2].nodes;
2885 }
2886 } else {
2887 if (check_assignment_) {
2888 Assignment* const temp_assignment =
2889 solver_->MakeAssignment(assignment_);
2890 feasible =
2891 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2892 head1, tail1, head2, tail2);
2893 }
2894 if (feasible) {
2895 index_to_chain_index_[tail2] = chain_index1;
2896 chains_[chain_index1].head = head1;
2897 chains_[chain_index1].tail = tail2;
2898 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
2899 deleted_chains_.insert(chain_index2);
2900 }
2901 }
2902 if (feasible) {
2903 assignment_->Add(nexts_[tail1]);
2904 assignment_->SetValue(nexts_[tail1], head2);
2905 }
2906 return feasible;
2907 }
2908
2909 bool Merge(bool merge, int index1, int index2) {
2910 if (merge) {
2911 if (UpdateAssignment(routes_[index1], routes_[index2])) {
2912 // Connection Route1 -> Route2
2913 for (const int node : routes_[index2]) {
2914 in_route_[node] = index1;
2915 routes_[index1].push_back(node);
2916 }
2917 for (int dimension_index = 0; dimension_index < dimensions_.size();
2918 ++dimension_index) {
2919 for (const std::pair<int, int64> new_possible_cumul :
2920 new_possible_cumuls_[dimension_index]) {
2921 cumuls_[dimension_index][new_possible_cumul.first] =
2922 new_possible_cumul.second;
2923 }
2924 }
2925 deleted_routes_.insert(index2);
2926 return true;
2927 }
2928 }
2929 return false;
2930 }
2931
2932 Assignment* const assignment_;
2933 RoutingModel* const model_;
2934 const bool check_assignment_;
2935 Solver* const solver_;
2936 const int64 num_indices_;
2937 const std::vector<Link> links_list_;
2938 std::vector<IntVar*> nexts_;
2939 std::vector<const RoutingDimension*> dimensions_; // Not owned.
2940 std::vector<std::vector<int64>> cumuls_;
2941 std::vector<absl::flat_hash_map<int, int64>> new_possible_cumuls_;
2942 std::vector<std::vector<int>> routes_;
2943 std::vector<int> in_route_;
2944 absl::flat_hash_set<int> deleted_routes_;
2945 std::vector<std::vector<int>> final_routes_;
2946 std::vector<Chain> chains_;
2947 absl::flat_hash_set<int> deleted_chains_;
2948 std::vector<Chain> final_chains_;
2949 std::vector<int> index_to_chain_index_;
2950 std::vector<int> index_to_vehicle_class_index_;
2951};
2952
2953#ifndef SWIG
2955 SweepIndex(const int64 index, const double angle, const double distance)
2956 : index(index), angle(angle), distance(distance) {}
2958
2960 double angle;
2961 double distance;
2962};
2963
2965 bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
2966 return (node1.angle < node2.angle);
2967 }
2969
2971 bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
2972 return (node1.distance < node2.distance);
2973 }
2975
2976SweepArranger::SweepArranger(const std::vector<std::pair<int64, int64>>& points)
2977 : coordinates_(2 * points.size(), 0), sectors_(1) {
2978 for (int64 i = 0; i < points.size(); ++i) {
2979 coordinates_[2 * i] = points[i].first;
2980 coordinates_[2 * i + 1] = points[i].second;
2981 }
2982}
2983
2984// Splits the space of the indices into sectors and sorts the indices of each
2985// sector with ascending angle from the depot.
2986void SweepArranger::ArrangeIndices(std::vector<int64>* indices) {
2987 const double pi_rad = 3.14159265;
2988 // Suppose that the center is at x0, y0.
2989 const int x0 = coordinates_[0];
2990 const int y0 = coordinates_[1];
2991
2992 std::vector<SweepIndex> sweep_indices;
2993 for (int64 index = 0; index < static_cast<int>(coordinates_.size()) / 2;
2994 ++index) {
2995 const int x = coordinates_[2 * index];
2996 const int y = coordinates_[2 * index + 1];
2997 const double x_delta = x - x0;
2998 const double y_delta = y - y0;
2999 double square_distance = x_delta * x_delta + y_delta * y_delta;
3000 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3001 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
3002 SweepIndex sweep_index(index, angle, square_distance);
3003 sweep_indices.push_back(sweep_index);
3004 }
3005 std::sort(sweep_indices.begin(), sweep_indices.end(),
3007
3008 const int size = static_cast<int>(sweep_indices.size()) / sectors_;
3009 for (int sector = 0; sector < sectors_; ++sector) {
3010 std::vector<SweepIndex> cluster;
3011 std::vector<SweepIndex>::iterator begin =
3012 sweep_indices.begin() + sector * size;
3013 std::vector<SweepIndex>::iterator end =
3014 sector == sectors_ - 1 ? sweep_indices.end()
3015 : sweep_indices.begin() + (sector + 1) * size;
3016 std::sort(begin, end, SweepIndexAngleComparator);
3017 }
3018 for (const SweepIndex& sweep_index : sweep_indices) {
3019 indices->push_back(sweep_index.index);
3020 }
3021}
3022
3023// Decision Builder building a first solution based on Sweep heuristic for
3024// Vehicle Routing Problem.
3025// Suitable only when distance is considered as the cost.
3027 public:
3028 SweepBuilder(RoutingModel* const model, bool check_assignment)
3029 : model_(model), check_assignment_(check_assignment) {}
3030 ~SweepBuilder() override {}
3031
3032 Decision* Next(Solver* const solver) override {
3033 // Setup the model of the instance for the Sweep Algorithm
3034 ModelSetup();
3035
3036 // Build the assignment routes for the model
3037 Assignment* const assignment = solver->MakeAssignment();
3038 route_constructor_ = absl::make_unique<RouteConstructor>(
3039 assignment, model_, check_assignment_, num_indices_, links_);
3040 // This call might cause backtracking if the search limit is reached.
3041 route_constructor_->Construct();
3042 route_constructor_.reset(nullptr);
3043 // This call might cause backtracking if the solution is not feasible.
3044 assignment->Restore();
3045
3046 return nullptr;
3047 }
3048
3049 private:
3050 void ModelSetup() {
3051 const int depot = model_->GetDepot();
3052 num_indices_ = model_->Size() + model_->vehicles();
3053 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
3054 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
3055 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
3056 }
3057 std::vector<int64> indices;
3058 model_->sweep_arranger()->ArrangeIndices(&indices);
3059 for (int i = 0; i < indices.size() - 1; ++i) {
3060 const int64 first = indices[i];
3061 const int64 second = indices[i + 1];
3062 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
3063 (model_->IsStart(second) || !model_->IsEnd(second))) {
3064 if (first != depot && second != depot) {
3065 Link link(std::make_pair(first, second), 0, 0, depot, depot);
3066 links_.push_back(link);
3067 }
3068 }
3069 }
3070 }
3071
3072 RoutingModel* const model_;
3073 std::unique_ptr<RouteConstructor> route_constructor_;
3074 const bool check_assignment_;
3075 int64 num_indices_;
3076 std::vector<Link> links_;
3077};
3078#endif
3079
3080namespace {
3081// Decision builder to build a solution with all nodes inactive. It does no
3082// branching and may fail if some nodes cannot be made inactive.
3083
3084class AllUnperformed : public DecisionBuilder {
3085 public:
3086 // Does not take ownership of model.
3087 explicit AllUnperformed(RoutingModel* const model) : model_(model) {}
3088 ~AllUnperformed() override {}
3089 Decision* Next(Solver* const solver) override {
3090 // Solver::(Un)FreezeQueue is private, passing through the public API
3091 // on PropagationBaseObject.
3092 model_->CostVar()->FreezeQueue();
3093 for (int i = 0; i < model_->Size(); ++i) {
3094 if (!model_->IsStart(i)) {
3095 model_->ActiveVar(i)->SetValue(0);
3096 }
3097 }
3098 model_->CostVar()->UnfreezeQueue();
3099 return nullptr;
3100 }
3101
3102 private:
3103 RoutingModel* const model_;
3104};
3105} // namespace
3106
3108 monitors_.push_back(monitor);
3109}
3110
3111namespace {
3112class AtSolutionCallbackMonitor : public SearchMonitor {
3113 public:
3114 AtSolutionCallbackMonitor(Solver* solver, std::function<void()> callback)
3115 : SearchMonitor(solver), callback_(std::move(callback)) {}
3116 bool AtSolution() override {
3117 callback_();
3118 return false;
3119 }
3120
3121 private:
3122 std::function<void()> callback_;
3123};
3124} // namespace
3125
3127 AddSearchMonitor(solver_->RevAlloc(
3128 new AtSolutionCallbackMonitor(solver_.get(), std::move(callback))));
3129}
3130
3131const Assignment* RoutingModel::Solve(const Assignment* assignment) {
3132 return SolveFromAssignmentWithParameters(assignment,
3134}
3135
3137 const RoutingSearchParameters& parameters,
3138 std::vector<const Assignment*>* solutions) {
3139 return SolveFromAssignmentWithParameters(nullptr, parameters, solutions);
3140}
3141
3142namespace {
3143absl::Duration GetTimeLimit(const RoutingSearchParameters& parameters) {
3144 if (!parameters.has_time_limit()) return absl::InfiniteDuration();
3145 return util_time::DecodeGoogleApiProto(parameters.time_limit()).value();
3146}
3147
3148absl::Duration GetLnsTimeLimit(const RoutingSearchParameters& parameters) {
3149 if (!parameters.has_lns_time_limit()) return absl::InfiniteDuration();
3150 return util_time::DecodeGoogleApiProto(parameters.lns_time_limit()).value();
3151}
3152
3153} // namespace
3154
3155namespace {
3156void MakeAllUnperformed(const RoutingModel* model, Assignment* assignment) {
3157 assignment->Clear();
3158 for (int i = 0; i < model->Nexts().size(); ++i) {
3159 if (!model->IsStart(i)) {
3160 assignment->Add(model->NextVar(i))->SetValue(i);
3161 }
3162 }
3163 for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) {
3164 assignment->Add(model->NextVar(model->Start(vehicle)))
3165 ->SetValue(model->End(vehicle));
3166 }
3167}
3168} // namespace
3169
3170bool RoutingModel::AppendAssignmentIfFeasible(
3171 const Assignment& assignment,
3172 std::vector<std::unique_ptr<Assignment>>* assignments) {
3173 tmp_assignment_->CopyIntersection(&assignment);
3174 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3175 GetOrCreateLimit());
3176 if (collect_one_assignment_->solution_count() == 1) {
3177 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3178 assignments->back()->Copy(collect_one_assignment_->solution(0));
3179 return true;
3180 }
3181 return false;
3182}
3183
3184void RoutingModel::LogSolution(const RoutingSearchParameters& parameters,
3185 const std::string& description,
3186 int64 solution_cost, int64 start_time_ms) {
3187 const std::string memory_str = MemoryUsage();
3188 const double cost_scaling_factor = parameters.log_cost_scaling_factor();
3189 const double cost_offset = parameters.log_cost_offset();
3190 const std::string cost_string =
3191 cost_scaling_factor == 1.0 && cost_offset == 0.0
3192 ? absl::StrCat(solution_cost)
3193 : absl::StrFormat(
3194 "%d (%.8lf)", solution_cost,
3195 cost_scaling_factor * (solution_cost + cost_offset));
3196 LOG(INFO) << absl::StrFormat(
3197 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3198 solver_->wall_time() - start_time_ms, memory_str);
3199}
3200
3202 const Assignment* assignment, const RoutingSearchParameters& parameters,
3203 std::vector<const Assignment*>* solutions) {
3204 const int64 start_time_ms = solver_->wall_time();
3205 QuietCloseModelWithParameters(parameters);
3206 VLOG(1) << "Search parameters:\n" << parameters.DebugString();
3207 if (solutions != nullptr) solutions->clear();
3208 if (status_ == ROUTING_INVALID) {
3209 return nullptr;
3210 }
3211 limit_->UpdateLimits(GetTimeLimit(parameters), kint64max, kint64max,
3212 parameters.solution_limit());
3213 ls_limit_->UpdateLimits(GetTimeLimit(parameters), kint64max, kint64max, 1);
3214 lns_limit_->UpdateLimits(GetLnsTimeLimit(parameters), kint64max, kint64max,
3215 kint64max);
3216 // NOTE: Allow more time for the first solution's scheduling, since if it
3217 // fails, we won't have anything to build upon.
3218 // We set this time limit based on whether local/global dimension optimizers
3219 // are used in the finalizer to avoid going over the general time limit.
3220 // TODO(user): Adapt this when absolute timeouts are given to the model.
3221 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3222 !local_dimension_optimizers_.empty();
3223 const absl::Duration first_solution_lns_time_limit =
3224 std::max(GetTimeLimit(parameters) / time_limit_shares,
3225 GetLnsTimeLimit(parameters));
3226 first_solution_lns_limit_->UpdateLimits(first_solution_lns_time_limit,
3228
3229 std::vector<std::unique_ptr<Assignment>> solution_pool;
3230 if (parameters.use_cp() == BOOL_TRUE) {
3231 if (nullptr == assignment) {
3232 bool solution_found = false;
3233 Assignment matching(solver_.get());
3234 if (IsMatchingModel() && SolveMatchingModel(&matching, parameters) &&
3235 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3236 if (parameters.log_search()) {
3237 LogSolution(parameters, "Min-Cost Flow Solution",
3238 solution_pool.back()->ObjectiveValue(), start_time_ms);
3239 }
3240 solution_found = true;
3241 }
3242 if (!solution_found) {
3243 // Build trivial solutions to which we can come back too in case the
3244 // solver does not manage to build something better.
3245 Assignment unperformed(solver_.get());
3246 MakeAllUnperformed(this, &unperformed);
3247 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3248 parameters.log_search()) {
3249 LogSolution(parameters, "All Unperformed Solution",
3250 solution_pool.back()->ObjectiveValue(), start_time_ms);
3251 }
3252 const absl::Duration elapsed_time =
3253 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3254 const absl::Duration time_left =
3255 GetTimeLimit(parameters) - elapsed_time;
3256 if (time_left >= absl::ZeroDuration()) {
3257 limit_->UpdateLimits(time_left, kint64max, kint64max,
3258 parameters.solution_limit());
3259 ls_limit_->UpdateLimits(time_left, kint64max, kint64max, 1);
3260 solver_->Solve(solve_db_, monitors_);
3261 }
3262 }
3263 } else {
3264 assignment_->CopyIntersection(assignment);
3265 solver_->Solve(improve_db_, monitors_);
3266 }
3267 }
3268
3269 if (parameters.use_cp_sat() == BOOL_TRUE) {
3270 const int solution_count = collect_assignments_->solution_count();
3271 Assignment* const cp_solution =
3272 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3273 : nullptr;
3274 Assignment sat_solution(solver_.get());
3275 if (SolveModelWithSat(*this, parameters, cp_solution, &sat_solution) &&
3276 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3277 parameters.log_search()) {
3278 LogSolution(parameters, "SAT", solution_pool.back()->ObjectiveValue(),
3279 start_time_ms);
3280 }
3281 }
3282
3283 const absl::Duration elapsed_time =
3284 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3285 const int solution_count = collect_assignments_->solution_count();
3286 if (solution_count >= 1 || !solution_pool.empty()) {
3287 status_ = ROUTING_SUCCESS;
3288 if (solutions != nullptr) {
3289 for (int i = 0; i < solution_count; ++i) {
3290 solutions->push_back(
3291 solver_->MakeAssignment(collect_assignments_->solution(i)));
3292 }
3293 for (const auto& solution : solution_pool) {
3294 if (solutions->empty() ||
3295 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3296 solutions->push_back(solver_->MakeAssignment(solution.get()));
3297 }
3298 }
3299 return solutions->back();
3300 }
3301 Assignment* best_assignment =
3302 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3303 : nullptr;
3304 for (const auto& solution : solution_pool) {
3305 if (best_assignment == nullptr ||
3306 solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3307 best_assignment = solution.get();
3308 }
3309 }
3310 return solver_->MakeAssignment(best_assignment);
3311 } else {
3312 if (elapsed_time >= GetTimeLimit(parameters)) {
3313 status_ = ROUTING_FAIL_TIMEOUT;
3314 } else {
3315 status_ = ROUTING_FAIL;
3316 }
3317 return nullptr;
3318 }
3319}
3320
3322 Assignment* target_assignment, const RoutingModel* source_model,
3323 const Assignment* source_assignment) {
3324 const int size = Size();
3325 DCHECK_EQ(size, source_model->Size());
3326 CHECK_EQ(target_assignment->solver(), solver_.get());
3327
3329 SetAssignmentFromAssignment(target_assignment, Nexts(), source_assignment,
3330 source_model->Nexts());
3331 } else {
3332 std::vector<IntVar*> source_vars(size + size + vehicles_);
3333 std::vector<IntVar*> target_vars(size + size + vehicles_);
3334 for (int index = 0; index < size; index++) {
3335 source_vars[index] = source_model->NextVar(index);
3336 target_vars[index] = NextVar(index);
3337 }
3338 for (int index = 0; index < size + vehicles_; index++) {
3339 source_vars[size + index] = source_model->VehicleVar(index);
3340 target_vars[size + index] = VehicleVar(index);
3341 }
3342 SetAssignmentFromAssignment(target_assignment, target_vars,
3343 source_assignment, source_vars);
3344 }
3345
3346 target_assignment->AddObjective(cost_);
3347}
3348
3349// Computing a lower bound to the cost of a vehicle routing problem solving a
3350// a linear assignment problem (minimum-cost perfect bipartite matching).
3351// A bipartite graph is created with left nodes representing the nodes of the
3352// routing problem and right nodes representing possible node successors; an
3353// arc between a left node l and a right node r is created if r can be the
3354// node folowing l in a route (Next(l) = r); the cost of the arc is the transit
3355// cost between l and r in the routing problem.
3356// This is a lower bound given the solution to assignment problem does not
3357// necessarily produce a (set of) closed route(s) from a starting node to an
3358// ending node.
3360 if (!closed_) {
3361 LOG(WARNING) << "Non-closed model not supported.";
3362 return 0;
3363 }
3365 LOG(WARNING) << "Non-homogeneous vehicle costs not supported";
3366 return 0;
3367 }
3368 if (!disjunctions_.empty()) {
3369 LOG(WARNING)
3370 << "Node disjunction constraints or optional nodes not supported.";
3371 return 0;
3372 }
3373 const int num_nodes = Size() + vehicles_;
3374 ForwardStarGraph graph(2 * num_nodes, num_nodes * num_nodes);
3375 LinearSumAssignment<ForwardStarGraph> linear_sum_assignment(graph, num_nodes);
3376 // Adding arcs for non-end nodes, based on possible values of next variables.
3377 // Left nodes in the bipartite are indexed from 0 to num_nodes - 1; right
3378 // nodes are indexed from num_nodes to 2 * num_nodes - 1.
3379 for (int tail = 0; tail < Size(); ++tail) {
3380 std::unique_ptr<IntVarIterator> iterator(
3381 nexts_[tail]->MakeDomainIterator(false));
3382 for (const int64 head : InitAndGetValues(iterator.get())) {
3383 // Given there are no disjunction constraints, a node cannot point to
3384 // itself. Doing this explicitly given that outside the search,
3385 // propagation hasn't removed this value from next variables yet.
3386 if (head == tail) {
3387 continue;
3388 }
3389 // The index of a right node in the bipartite graph is the index
3390 // of the successor offset by the number of nodes.
3391 const ArcIndex arc = graph.AddArc(tail, num_nodes + head);
3393 linear_sum_assignment.SetArcCost(arc, cost);
3394 }
3395 }
3396 // The linear assignment library requires having as many left and right nodes.
3397 // Therefore we are creating fake assignments for end nodes, forced to point
3398 // to the equivalent start node with a cost of 0.
3399 for (int tail = Size(); tail < num_nodes; ++tail) {
3400 const ArcIndex arc = graph.AddArc(tail, num_nodes + starts_[tail - Size()]);
3401 linear_sum_assignment.SetArcCost(arc, 0);
3402 }
3403 if (linear_sum_assignment.ComputeAssignment()) {
3404 return linear_sum_assignment.GetCost();
3405 }
3406 return 0;
3407}
3408
3409bool RoutingModel::RouteCanBeUsedByVehicle(const Assignment& assignment,
3410 int start_index, int vehicle) const {
3411 int current_index =
3412 IsStart(start_index) ? Next(assignment, start_index) : start_index;
3413 while (!IsEnd(current_index)) {
3414 const IntVar* const vehicle_var = VehicleVar(current_index);
3415 if (!vehicle_var->Contains(vehicle)) {
3416 return false;
3417 }
3418 const int next_index = Next(assignment, current_index);
3419 CHECK_NE(next_index, current_index) << "Inactive node inside a route";
3420 current_index = next_index;
3421 }
3422 return true;
3423}
3424
3425bool RoutingModel::ReplaceUnusedVehicle(
3426 int unused_vehicle, int active_vehicle,
3427 Assignment* const compact_assignment) const {
3428 CHECK(compact_assignment != nullptr);
3429 CHECK(!IsVehicleUsed(*compact_assignment, unused_vehicle));
3430 CHECK(IsVehicleUsed(*compact_assignment, active_vehicle));
3431 // Swap NextVars at start nodes.
3432 const int unused_vehicle_start = Start(unused_vehicle);
3433 IntVar* const unused_vehicle_start_var = NextVar(unused_vehicle_start);
3434 const int unused_vehicle_end = End(unused_vehicle);
3435 const int active_vehicle_start = Start(active_vehicle);
3436 const int active_vehicle_end = End(active_vehicle);
3437 IntVar* const active_vehicle_start_var = NextVar(active_vehicle_start);
3438 const int active_vehicle_next =
3439 compact_assignment->Value(active_vehicle_start_var);
3440 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3441 compact_assignment->SetValue(active_vehicle_start_var, End(active_vehicle));
3442
3443 // Update VehicleVars along the route, update the last NextVar.
3444 int current_index = active_vehicle_next;
3445 while (!IsEnd(current_index)) {
3446 IntVar* const vehicle_var = VehicleVar(current_index);
3447 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3448 const int next_index = Next(*compact_assignment, current_index);
3449 if (IsEnd(next_index)) {
3450 IntVar* const last_next_var = NextVar(current_index);
3451 compact_assignment->SetValue(last_next_var, End(unused_vehicle));
3452 }
3453 current_index = next_index;
3454 }
3455
3456 // Update dimensions: update transits at the start.
3457 for (const RoutingDimension* const dimension : dimensions_) {
3458 const std::vector<IntVar*>& transit_variables = dimension->transits();
3459 IntVar* const unused_vehicle_transit_var =
3460 transit_variables[unused_vehicle_start];
3461 IntVar* const active_vehicle_transit_var =
3462 transit_variables[active_vehicle_start];
3463 const bool contains_unused_vehicle_transit_var =
3464 compact_assignment->Contains(unused_vehicle_transit_var);
3465 const bool contains_active_vehicle_transit_var =
3466 compact_assignment->Contains(active_vehicle_transit_var);
3467 if (contains_unused_vehicle_transit_var !=
3468 contains_active_vehicle_transit_var) {
3469 // TODO(user): clarify the expected trigger rate of this LOG.
3470 LOG(INFO) << "The assignment contains transit variable for dimension '"
3471 << dimension->name() << "' for some vehicles, but not for all";
3472 return false;
3473 }
3474 if (contains_unused_vehicle_transit_var) {
3475 const int64 old_unused_vehicle_transit =
3476 compact_assignment->Value(unused_vehicle_transit_var);
3477 const int64 old_active_vehicle_transit =
3478 compact_assignment->Value(active_vehicle_transit_var);
3479 compact_assignment->SetValue(unused_vehicle_transit_var,
3480 old_active_vehicle_transit);
3481 compact_assignment->SetValue(active_vehicle_transit_var,
3482 old_unused_vehicle_transit);
3483 }
3484
3485 // Update dimensions: update cumuls at the end.
3486 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3487 IntVar* const unused_vehicle_cumul_var =
3488 cumul_variables[unused_vehicle_end];
3489 IntVar* const active_vehicle_cumul_var =
3490 cumul_variables[active_vehicle_end];
3491 const int64 old_unused_vehicle_cumul =
3492 compact_assignment->Value(unused_vehicle_cumul_var);
3493 const int64 old_active_vehicle_cumul =
3494 compact_assignment->Value(active_vehicle_cumul_var);
3495 compact_assignment->SetValue(unused_vehicle_cumul_var,
3496 old_active_vehicle_cumul);
3497 compact_assignment->SetValue(active_vehicle_cumul_var,
3498 old_unused_vehicle_cumul);
3499 }
3500 return true;
3501}
3502
3504 const Assignment& assignment) const {
3505 return CompactAssignmentInternal(assignment, false);
3506}
3507
3509 const Assignment& assignment) const {
3510 return CompactAssignmentInternal(assignment, true);
3511}
3512
3513Assignment* RoutingModel::CompactAssignmentInternal(
3514 const Assignment& assignment, bool check_compact_assignment) const {
3515 CHECK_EQ(assignment.solver(), solver_.get());
3517 LOG(WARNING)
3518 << "The costs are not homogeneous, routes cannot be rearranged";
3519 return nullptr;
3520 }
3521
3522 std::unique_ptr<Assignment> compact_assignment(new Assignment(&assignment));
3523 for (int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3524 if (IsVehicleUsed(*compact_assignment, vehicle)) {
3525 continue;
3526 }
3527 const int vehicle_start = Start(vehicle);
3528 const int vehicle_end = End(vehicle);
3529 // Find the last vehicle, that can swap routes with this one.
3530 int swap_vehicle = vehicles_ - 1;
3531 bool has_more_vehicles_with_route = false;
3532 for (; swap_vehicle > vehicle; --swap_vehicle) {
3533 // If a vehicle was already swapped, it will appear in compact_assignment
3534 // as unused.
3535 if (!IsVehicleUsed(*compact_assignment, swap_vehicle) ||
3536 !IsVehicleUsed(*compact_assignment, swap_vehicle)) {
3537 continue;
3538 }
3539 has_more_vehicles_with_route = true;
3540 const int swap_vehicle_start = Start(swap_vehicle);
3541 const int swap_vehicle_end = End(swap_vehicle);
3542 if (manager_.IndexToNode(vehicle_start) !=
3543 manager_.IndexToNode(swap_vehicle_start) ||
3544 manager_.IndexToNode(vehicle_end) !=
3545 manager_.IndexToNode(swap_vehicle_end)) {
3546 continue;
3547 }
3548
3549 // Check that updating VehicleVars is OK.
3550 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3551 vehicle)) {
3552 break;
3553 }
3554 }
3555
3556 if (swap_vehicle == vehicle) {
3557 if (has_more_vehicles_with_route) {
3558 // No route can be assigned to this vehicle, but there are more vehicles
3559 // with a route left. This would leave a gap in the indices.
3560 // TODO(user): clarify the expected trigger rate of this LOG.
3561 LOG(INFO) << "No vehicle that can be swapped with " << vehicle
3562 << " was found";
3563 return nullptr;
3564 } else {
3565 break;
3566 }
3567 } else {
3568 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3569 compact_assignment.get())) {
3570 return nullptr;
3571 }
3572 }
3573 }
3574 if (check_compact_assignment &&
3575 !solver_->CheckAssignment(compact_assignment.get())) {
3576 // TODO(user): clarify the expected trigger rate of this LOG.
3577 LOG(WARNING) << "The compacted assignment is not a valid solution";
3578 return nullptr;
3579 }
3580 return compact_assignment.release();
3581}
3582
3583int RoutingModel::FindNextActive(int index,
3584 const std::vector<int64>& indices) const {
3585 ++index;
3586 CHECK_LE(0, index);
3587 const int size = indices.size();
3588 while (index < size && ActiveVar(indices[index])->Max() == 0) {
3589 ++index;
3590 }
3591 return index;
3592}
3593
3594IntVar* RoutingModel::ApplyLocks(const std::vector<int64>& locks) {
3595 // TODO(user): Replace calls to this method with calls to
3596 // ApplyLocksToAllVehicles and remove this method?
3597 CHECK_EQ(vehicles_, 1);
3598 preassignment_->Clear();
3599 IntVar* next_var = nullptr;
3600 int lock_index = FindNextActive(-1, locks);
3601 const int size = locks.size();
3602 if (lock_index < size) {
3603 next_var = NextVar(locks[lock_index]);
3604 preassignment_->Add(next_var);
3605 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3606 lock_index = FindNextActive(lock_index, locks)) {
3607 preassignment_->SetValue(next_var, locks[lock_index]);
3608 next_var = NextVar(locks[lock_index]);
3609 preassignment_->Add(next_var);
3610 }
3611 }
3612 return next_var;
3613}
3614
3616 const std::vector<std::vector<int64>>& locks, bool close_routes) {
3617 preassignment_->Clear();
3618 return RoutesToAssignment(locks, true, close_routes, preassignment_);
3619}
3620
3622 const RoutingSearchParameters& parameters) const {
3623 IntVarFilteredDecisionBuilder* const decision_builder =
3624 GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3625 return decision_builder != nullptr ? decision_builder->number_of_decisions()
3626 : 0;
3627}
3628
3630 const RoutingSearchParameters& parameters) const {
3631 IntVarFilteredDecisionBuilder* const decision_builder =
3632 GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3633 return decision_builder != nullptr ? decision_builder->number_of_rejects()
3634 : 0;
3635}
3636
3637bool RoutingModel::WriteAssignment(const std::string& file_name) const {
3638 if (collect_assignments_->solution_count() == 1 && assignment_ != nullptr) {
3639 assignment_->CopyIntersection(collect_assignments_->solution(0));
3640 return assignment_->Save(file_name);
3641 } else {
3642 return false;
3643 }
3644}
3645
3646Assignment* RoutingModel::ReadAssignment(const std::string& file_name) {
3647 QuietCloseModel();
3648 CHECK(assignment_ != nullptr);
3649 if (assignment_->Load(file_name)) {
3650 return DoRestoreAssignment();
3651 }
3652 return nullptr;
3653}
3654
3656 QuietCloseModel();
3657 CHECK(assignment_ != nullptr);
3658 assignment_->CopyIntersection(&solution);
3659 return DoRestoreAssignment();
3660}
3661
3662Assignment* RoutingModel::DoRestoreAssignment() {
3663 if (status_ == ROUTING_INVALID) {
3664 return nullptr;
3665 }
3666 solver_->Solve(restore_assignment_, monitors_);
3667 if (collect_assignments_->solution_count() == 1) {
3668 status_ = ROUTING_SUCCESS;
3669 return collect_assignments_->solution(0);
3670 } else {
3671 status_ = ROUTING_FAIL;
3672 return nullptr;
3673 }
3674 return nullptr;
3675}
3676
3678 const std::vector<std::vector<int64>>& routes, bool ignore_inactive_indices,
3679 bool close_routes, Assignment* const assignment) const {
3680 CHECK(assignment != nullptr);
3681 if (!closed_) {
3682 LOG(ERROR) << "The model is not closed yet";
3683 return false;
3684 }
3685 const int num_routes = routes.size();
3686 if (num_routes > vehicles_) {
3687 LOG(ERROR) << "The number of vehicles in the assignment (" << routes.size()
3688 << ") is greater than the number of vehicles in the model ("
3689 << vehicles_ << ")";
3690 return false;
3691 }
3692
3693 absl::flat_hash_set<int> visited_indices;
3694 // Set value to NextVars based on the routes.
3695 for (int vehicle = 0; vehicle < num_routes; ++vehicle) {
3696 const std::vector<int64>& route = routes[vehicle];
3697 int from_index = Start(vehicle);
3698 std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3699 visited_indices.insert(from_index);
3700 if (!insert_result.second) {
3701 LOG(ERROR) << "Index " << from_index << " (start node for vehicle "
3702 << vehicle << ") was already used";
3703 return false;
3704 }
3705
3706 for (const int64 to_index : route) {
3707 if (to_index < 0 || to_index >= Size()) {
3708 LOG(ERROR) << "Invalid index: " << to_index;
3709 return false;
3710 }
3711
3712 IntVar* const active_var = ActiveVar(to_index);
3713 if (active_var->Max() == 0) {
3714 if (ignore_inactive_indices) {
3715 continue;
3716 } else {
3717 LOG(ERROR) << "Index " << to_index << " is not active";
3718 return false;
3719 }
3720 }
3721
3722 insert_result = visited_indices.insert(to_index);
3723 if (!insert_result.second) {
3724 LOG(ERROR) << "Index " << to_index << " is used multiple times";
3725 return false;
3726 }
3727
3728 const IntVar* const vehicle_var = VehicleVar(to_index);
3729 if (!vehicle_var->Contains(vehicle)) {
3730 LOG(ERROR) << "Vehicle " << vehicle << " is not allowed at index "
3731 << to_index;
3732 return false;
3733 }
3734
3735 IntVar* const from_var = NextVar(from_index);
3736 if (!assignment->Contains(from_var)) {
3737 assignment->Add(from_var);
3738 }
3739 assignment->SetValue(from_var, to_index);
3740
3741 from_index = to_index;
3742 }
3743
3744 if (close_routes) {
3745 IntVar* const last_var = NextVar(from_index);
3746 if (!assignment->Contains(last_var)) {
3747 assignment->Add(last_var);
3748 }
3749 assignment->SetValue(last_var, End(vehicle));
3750 }
3751 }
3752
3753 // Do not use the remaining vehicles.
3754 for (int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3755 const int start_index = Start(vehicle);
3756 // Even if close_routes is false, we still need to add the start index to
3757 // visited_indices so that deactivating other nodes works correctly.
3758 std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3759 visited_indices.insert(start_index);
3760 if (!insert_result.second) {
3761 LOG(ERROR) << "Index " << start_index << " is used multiple times";
3762 return false;
3763 }
3764 if (close_routes) {
3765 IntVar* const start_var = NextVar(start_index);
3766 if (!assignment->Contains(start_var)) {
3767 assignment->Add(start_var);
3768 }
3769 assignment->SetValue(start_var, End(vehicle));
3770 }
3771 }
3772
3773 // Deactivate other nodes (by pointing them to themselves).
3774 if (close_routes) {
3775 for (int index = 0; index < Size(); ++index) {
3776 if (!gtl::ContainsKey(visited_indices, index)) {
3777 IntVar* const next_var = NextVar(index);
3778 if (!assignment->Contains(next_var)) {
3779 assignment->Add(next_var);
3780 }
3781 assignment->SetValue(next_var, index);
3782 }
3783 }
3784 }
3785
3786 return true;
3787}
3788
3790 const std::vector<std::vector<int64>>& routes,
3791 bool ignore_inactive_indices) {
3792 QuietCloseModel();
3793 if (!RoutesToAssignment(routes, ignore_inactive_indices, true, assignment_)) {
3794 return nullptr;
3795 }
3796 // DoRestoreAssignment() might still fail when checking constraints (most
3797 // constraints are not verified by RoutesToAssignment) or when filling in
3798 // dimension variables.
3799 return DoRestoreAssignment();
3800}
3801
3803 const Assignment& assignment,
3804 std::vector<std::vector<int64>>* const routes) const {
3805 CHECK(closed_);
3806 CHECK(routes != nullptr);
3807
3808 const int model_size = Size();
3809 routes->resize(vehicles_);
3810 for (int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3811 std::vector<int64>* const vehicle_route = &routes->at(vehicle);
3812 vehicle_route->clear();
3813
3814 int num_visited_indices = 0;
3815 const int first_index = Start(vehicle);
3816 const IntVar* const first_var = NextVar(first_index);
3817 CHECK(assignment.Contains(first_var));
3818 CHECK(assignment.Bound(first_var));
3819 int current_index = assignment.Value(first_var);
3820 while (!IsEnd(current_index)) {
3821 vehicle_route->push_back(current_index);
3822
3823 const IntVar* const next_var = NextVar(current_index);
3824 CHECK(assignment.Contains(next_var));
3825 CHECK(assignment.Bound(next_var));
3826 current_index = assignment.Value(next_var);
3827
3828 ++num_visited_indices;
3829 CHECK_LE(num_visited_indices, model_size)
3830 << "The assignment contains a cycle";
3831 }
3832 }
3833}
3834
3835#ifndef SWIG
3836std::vector<std::vector<int64>> RoutingModel::GetRoutesFromAssignment(
3837 const Assignment& assignment) {
3838 std::vector<std::vector<int64>> route_indices(vehicles());
3839 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3840 if (!assignment.Bound(NextVar(vehicle))) {
3841 LOG(DFATAL) << "GetRoutesFromAssignment() called on incomplete solution:"
3842 << " NextVar(" << vehicle << ") is unbound.";
3843 }
3844 }
3845 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3846 int64 index = Start(vehicle);
3847 route_indices[vehicle].push_back(index);
3848 while (!IsEnd(index)) {
3849 index = assignment.Value(NextVar(index));
3850 route_indices[vehicle].push_back(index);
3851 }
3852 }
3853 return route_indices;
3854}
3855#endif
3856
3857int64 RoutingModel::GetArcCostForClassInternal(
3858 int64 from_index, int64 to_index, CostClassIndex cost_class_index) const {
3859 DCHECK(closed_);
3860 DCHECK_GE(cost_class_index, 0);
3861 DCHECK_LT(cost_class_index, cost_classes_.size());
3862 CostCacheElement* const cache = &cost_cache_[from_index];
3863 // See the comment in CostCacheElement in the .h for the int64->int cast.
3864 if (cache->index == static_cast<int>(to_index) &&
3865 cache->cost_class_index == cost_class_index) {
3866 return cache->cost;
3867 }
3868 int64 cost = 0;
3869 const CostClass& cost_class = cost_classes_[cost_class_index];
3870 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3871 if (!IsStart(from_index)) {
3872 cost = CapAdd(evaluator(from_index, to_index),
3873 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3874 } else if (!IsEnd(to_index)) {
3875 // Apply route fixed cost on first non-first/last node, in other words on
3876 // the arc from the first node to its next node if it's not the last node.
3877 cost = CapAdd(
3878 evaluator(from_index, to_index),
3879 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3880 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3881 } else {
3882 // If there's only the first and last nodes on the route, it is considered
3883 // as an empty route.
3884 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3885 cost =
3886 CapAdd(evaluator(from_index, to_index),
3887 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3888 } else {
3889 cost = 0;
3890 }
3891 }
3892 *cache = {static_cast<int>(to_index), cost_class_index, cost};
3893 return cost;
3894}
3895
3897 return !IsEnd(index) && index_to_vehicle_[index] != kUnassigned;
3898}
3899
3901 int vehicle) const {
3902 CHECK_GE(vehicle, 0);
3903 CHECK_LT(vehicle, vehicles_);
3904 CHECK_EQ(solver_.get(), assignment.solver());
3905 IntVar* const start_var = NextVar(Start(vehicle));
3906 CHECK(assignment.Contains(start_var));
3907 return !IsEnd(assignment.Value(start_var));
3908}
3909
3910int64 RoutingModel::Next(const Assignment& assignment, int64 index) const {
3911 CHECK_EQ(solver_.get(), assignment.solver());
3912 IntVar* const next_var = NextVar(index);
3913 CHECK(assignment.Contains(next_var));
3914 CHECK(assignment.Bound(next_var));
3915 return assignment.Value(next_var);
3916}
3917
3919 int64 vehicle) const {
3920 if (from_index != to_index && vehicle >= 0) {
3921 return GetArcCostForClassInternal(from_index, to_index,
3923 } else {
3924 return 0;
3925 }
3926}
3927
3929 int64 from_index, int64 to_index,
3930 int64 /*CostClassIndex*/ cost_class_index) const {
3931 if (from_index != to_index) {
3932 return GetArcCostForClassInternal(from_index, to_index,
3933 CostClassIndex(cost_class_index));
3934 } else {
3935 return 0;
3936 }
3937}
3938
3940 int64 to_index) const {
3941 // Return high cost if connecting to an end (or bound-to-end) node;
3942 // this is used in the cost-based first solution strategies to avoid closing
3943 // routes too soon.
3944 if (!is_bound_to_end_ct_added_.Switched()) {
3945 // Lazily adding path-cumul constraint propagating connection to route end,
3946 // as it can be pretty costly in the general case.
3947 std::vector<IntVar*> zero_transit(Size(), solver_->MakeIntConst(0));
3948 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3949 nexts_, active_, is_bound_to_end_, zero_transit));
3950 is_bound_to_end_ct_added_.Switch(solver_.get());
3951 }
3952 if (is_bound_to_end_[to_index]->Min() == 1) return kint64max;
3953 // TODO(user): Take vehicle into account.
3954 return GetHomogeneousCost(from_index, to_index);
3955}
3956
3957int64 RoutingModel::GetDimensionTransitCostSum(
3958 int64 i, int64 j, const CostClass& cost_class) const {
3959 int64 cost = 0;
3960 for (const auto& evaluator_and_coefficient :
3961 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3962 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3963 cost = CapAdd(
3964 cost,
3965 CapProd(evaluator_and_coefficient.cost_coefficient,
3966 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3967 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3968 }
3969 return cost;
3970}
3971
3973 int64 to2) {
3974 // Deal with end nodes: never pick an end node over a non-end node.
3975 if (IsEnd(to1) || IsEnd(to2)) {
3976 if (IsEnd(to1) != IsEnd(to2)) return IsEnd(to2);
3977 // If both are end nodes, we don't care; the right end node will be picked
3978 // by constraint propagation. Break the tie by index.
3979 return to1 < to2;
3980 }
3981
3982 // Look whether they are mandatory (must be performed) or optional.
3983 const bool mandatory1 = active_[to1]->Min() == 1;
3984 const bool mandatory2 = active_[to2]->Min() == 1;
3985 // Always pick a mandatory node over a non-mandatory one.
3986 if (mandatory1 != mandatory2) return mandatory1;
3987
3988 // Look at the vehicle variables.
3989 IntVar* const src_vehicle_var = VehicleVar(from);
3990 // In case the source vehicle is bound, "src_vehicle" will be it.
3991 // Otherwise, it'll be set to some possible source vehicle that
3992 // isn't -1 (if possible).
3993 const int64 src_vehicle = src_vehicle_var->Max();
3994 if (src_vehicle_var->Bound()) {
3995 IntVar* const to1_vehicle_var = VehicleVar(to1);
3996 IntVar* const to2_vehicle_var = VehicleVar(to2);
3997 // Subtle: non-mandatory node have kNoVehicle as possible value for
3998 // their vehicle variable. So they're effectively "bound" when their domain
3999 // size is 2.
4000 const bool bound1 =
4001 mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
4002 const bool bound2 =
4003 mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
4004 // Prefer a destination bound to a given vehicle, even if it's not
4005 // bound to the right one (the propagation will quickly rule it out).
4006 if (bound1 != bound2) return bound1;
4007 if (bound1) { // same as bound1 && bound2.
4008 // Min() will return kNoVehicle for optional nodes. Thus we use Max().
4009 const int64 vehicle1 = to1_vehicle_var->Max();
4010 const int64 vehicle2 = to2_vehicle_var->Max();
4011 // Prefer a destination bound to the right vehicle.
4012 // TODO(user): cover this clause in a unit test.
4013 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4014 return vehicle1 == src_vehicle;
4015 }
4016 // If no destination is bound to the right vehicle, whatever we
4017 // return doesn't matter: both are infeasible. To be consistent, we
4018 // just break the tie.
4019 if (vehicle1 != src_vehicle) return to1 < to2;
4020 }
4021 }
4022 // At this point, either both destinations are bound to the source vehicle,
4023 // or none of them is bound, or the source vehicle isn't bound.
4024 // We don't bother inspecting the domains of the vehicle variables further.
4025
4026 // Inspect the primary constrained dimension, if any.
4027 // TODO(user): try looking at all the dimensions, not just the primary one,
4028 // and reconsider the need for a "primary" dimension.
4029 if (!GetPrimaryConstrainedDimension().empty()) {
4030 const std::vector<IntVar*>& cumul_vars =
4032 IntVar* const dim1 = cumul_vars[to1];
4033 IntVar* const dim2 = cumul_vars[to2];
4034 // Prefer the destination that has a lower upper bound for the constrained
4035 // dimension.
4036 if (dim1->Max() != dim2->Max()) return dim1->Max() < dim2->Max();
4037 // TODO(user): evaluate the *actual* Min() of each cumul variable in the
4038 // scenario where the corresponding arc from->to is performed, and pick
4039 // the destination with the lowest value.
4040 }
4041
4042 // Break ties on equally constrained nodes with the (cost - unperformed
4043 // penalty).
4044 {
4045 const /*CostClassIndex*/ int64 cost_class_index =
4046 SafeGetCostClassInt64OfVehicle(src_vehicle);
4047 const int64 cost1 = CapSub(GetArcCostForClass(from, to1, cost_class_index),
4048 UnperformedPenalty(to1));
4049 const int64 cost2 = CapSub(GetArcCostForClass(from, to2, cost_class_index),
4050 UnperformedPenalty(to2));
4051 if (cost1 != cost2) return cost1 < cost2;
4052 }
4053
4054 // Further break ties by looking at the size of the VehicleVar.
4055 {
4056 const int64 num_vehicles1 = VehicleVar(to1)->Size();
4057 const int64 num_vehicles2 = VehicleVar(to2)->Size();
4058 if (num_vehicles1 != num_vehicles2) return num_vehicles1 < num_vehicles2;
4059 }
4060
4061 // Break perfect ties by value.
4062 return to1 < to2;
4063}
4064
4066 CHECK_LT(index, index_to_visit_type_.size());
4067 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4068 index_to_visit_type_[index] = type;
4069 index_to_type_policy_[index] = policy;
4070 num_visit_types_ = std::max(num_visit_types_, type + 1);
4071}
4072
4074 CHECK_LT(index, index_to_visit_type_.size());
4075 return index_to_visit_type_[index];
4076}
4077
4078const std::vector<int>& RoutingModel::GetSingleNodesOfType(int type) const {
4079 DCHECK_LT(type, single_nodes_of_type_.size());
4080 return single_nodes_of_type_[type];
4081}
4082
4083const std::vector<int>& RoutingModel::GetPairIndicesOfType(int type) const {
4084 DCHECK_LT(type, pair_indices_of_type_.size());
4085 return pair_indices_of_type_[type];
4086}
4087
4089 int64 index) const {
4090 CHECK_LT(index, index_to_type_policy_.size());
4091 return index_to_type_policy_[index];
4092}
4093
4095 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4096 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4097 same_vehicle_required_type_alternatives_per_type_index_.resize(
4098 num_visit_types_);
4099 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4100 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4101}
4102
4104 DCHECK_LT(std::max(type1, type2),
4105 hard_incompatible_types_per_type_index_.size());
4106 has_hard_type_incompatibilities_ = true;
4107
4108 hard_incompatible_types_per_type_index_[type1].insert(type2);
4109 hard_incompatible_types_per_type_index_[type2].insert(type1);
4110}
4111
4113 DCHECK_LT(std::max(type1, type2),
4114 temporal_incompatible_types_per_type_index_.size());
4115 has_temporal_type_incompatibilities_ = true;
4116
4117 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4118 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4119}
4120
4121const absl::flat_hash_set<int>&
4123 DCHECK_GE(type, 0);
4124 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4125 return hard_incompatible_types_per_type_index_[type];
4126}
4127
4128const absl::flat_hash_set<int>&
4130 DCHECK_GE(type, 0);
4131 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4132 return temporal_incompatible_types_per_type_index_[type];
4133}
4134
4135// TODO(user): Consider if an empty "required_type_alternatives" should mean
4136// trivially feasible requirement, as there are no required type alternatives?
4138 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4139 DCHECK_LT(dependent_type,
4140 same_vehicle_required_type_alternatives_per_type_index_.size());
4141
4142 if (required_type_alternatives.empty()) {
4143 // The dependent_type requires an infeasible (empty) set of types.
4144 // Nodes of this type and all policies except
4145 // ADDED_TYPE_REMOVED_FROM_VEHICLE are trivially infeasible.
4146 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4147 trivially_infeasible_visit_types_to_policies_[dependent_type];
4148 infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4149 infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4150 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4151 return;
4152 }
4153
4154 has_same_vehicle_type_requirements_ = true;
4155 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4156 .push_back(std::move(required_type_alternatives));
4157}
4158
4160 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4161 DCHECK_LT(dependent_type,
4162 required_type_alternatives_when_adding_type_index_.size());
4163
4164 if (required_type_alternatives.empty()) {
4165 // The dependent_type requires an infeasible (empty) set of types.
4166 // Nodes of this type and policy TYPE_ADDED_TO_VEHICLE or
4167 // TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED are trivially infeasible.
4168 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4169 trivially_infeasible_visit_types_to_policies_[dependent_type];
4170 infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4171 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4172 return;
4173 }
4174
4175 has_temporal_type_requirements_ = true;
4176 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4177 std::move(required_type_alternatives));
4178}
4179
4181 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4182 DCHECK_LT(dependent_type,
4183 required_type_alternatives_when_removing_type_index_.size());
4184
4185 if (required_type_alternatives.empty()) {
4186 // The dependent_type requires an infeasible (empty) set of types.
4187 // Nodes of this type and all policies except TYPE_ADDED_TO_VEHICLE are
4188 // trivially infeasible.
4189 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4190 trivially_infeasible_visit_types_to_policies_[dependent_type];
4191 infeasible_policies.insert(ADDED_TYPE_REMOVED_FROM_VEHICLE);
4192 infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4193 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4194 return;
4195 }
4196
4197 has_temporal_type_requirements_ = true;
4198 required_type_alternatives_when_removing_type_index_[dependent_type]
4199 .push_back(std::move(required_type_alternatives));
4200}
4201
4202const std::vector<absl::flat_hash_set<int>>&
4204 DCHECK_GE(type, 0);
4205 DCHECK_LT(type,
4206 same_vehicle_required_type_alternatives_per_type_index_.size());
4207 return same_vehicle_required_type_alternatives_per_type_index_[type];
4208}
4209
4210const std::vector<absl::flat_hash_set<int>>&
4212 DCHECK_GE(type, 0);
4213 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4214 return required_type_alternatives_when_adding_type_index_[type];
4215}
4216
4217const std::vector<absl::flat_hash_set<int>>&
4219 DCHECK_GE(type, 0);
4220 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4221 return required_type_alternatives_when_removing_type_index_[type];
4222}
4223
4225 return UnperformedPenaltyOrValue(0, var_index);
4226}
4227
4229 int64 var_index) const {
4230 if (active_[var_index]->Min() == 1) return kint64max; // Forced active.
4231 const std::vector<DisjunctionIndex>& disjunction_indices =
4232 GetDisjunctionIndices(var_index);
4233 if (disjunction_indices.size() != 1) return default_value;
4234 const DisjunctionIndex disjunction_index = disjunction_indices[0];
4235 // The disjunction penalty can be kNoPenalty iff there is more than one node
4236 // in the disjunction; otherwise we would have caught it earlier (the node
4237 // would be forced active).
4238 return std::max(int64{0}, disjunctions_[disjunction_index].value.penalty);
4239}
4240
4242 const Assignment& solution_assignment,
4243 const std::string& dimension_to_print) const {
4244 for (int i = 0; i < Size(); ++i) {
4245 if (!solution_assignment.Bound(NextVar(i))) {
4246 LOG(DFATAL)
4247 << "DebugOutputVehicleSchedules() called on incomplete solution:"
4248 << " NextVar(" << i << ") is unbound.";
4249 return "";
4250 }
4251 }
4252 std::string output;
4253 absl::flat_hash_set<std::string> dimension_names;
4254 if (dimension_to_print.empty()) {
4255 const std::vector<std::string> all_dimension_names = GetAllDimensionNames();
4256 dimension_names.insert(all_dimension_names.begin(),
4257 all_dimension_names.end());
4258 } else {
4259 dimension_names.insert(dimension_to_print);
4260 }
4261 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4262 int empty_vehicle_range_start = vehicle;
4263 while (vehicle < vehicles() &&
4264 IsEnd(solution_assignment.Value(NextVar(Start(vehicle))))) {
4265 vehicle++;
4266 }
4267 if (empty_vehicle_range_start != vehicle) {
4268 if (empty_vehicle_range_start == vehicle - 1) {
4269 absl::StrAppendFormat(&output, "Vehicle %d: empty",
4270 empty_vehicle_range_start);
4271 } else {
4272 absl::StrAppendFormat(&output, "Vehicles %d-%d: empty",
4273 empty_vehicle_range_start, vehicle - 1);
4274 }
4275 output.append("\n");
4276 }
4277 if (vehicle < vehicles()) {
4278 absl::StrAppendFormat(&output, "Vehicle %d:", vehicle);
4279 int64 index = Start(vehicle);
4280 for (;;) {
4281 const IntVar* vehicle_var = VehicleVar(index);
4282 absl::StrAppendFormat(&output, "%d Vehicle(%d) ", index,
4283 solution_assignment.Value(vehicle_var));
4284 for (const RoutingDimension* const dimension : dimensions_) {
4285 if (gtl::ContainsKey(dimension_names, dimension->name())) {
4286 const IntVar* const var = dimension->CumulVar(index);
4287 absl::StrAppendFormat(&output, "%s(%d..%d) ", dimension->name(),
4288 solution_assignment.Min(var),
4289 solution_assignment.Max(var));
4290 }
4291 }
4292 if (IsEnd(index)) break;
4293 index = solution_assignment.Value(NextVar(index));
4294 if (IsEnd(index)) output.append("Route end ");
4295 }
4296 output.append("\n");
4297 }
4298 }
4299 output.append("Unperformed nodes: ");
4300 bool has_unperformed = false;
4301 for (int i = 0; i < Size(); ++i) {
4302 if (!IsEnd(i) && !IsStart(i) &&
4303 solution_assignment.Value(NextVar(i)) == i) {
4304 absl::StrAppendFormat(&output, "%d ", i);
4305 has_unperformed = true;
4306 }
4307 }
4308 if (!has_unperformed) output.append("None");
4309 output.append("\n");
4310 return output;
4311}
4312
4313#ifndef SWIG
4314std::vector<std::vector<std::pair<int64, int64>>> RoutingModel::GetCumulBounds(
4315 const Assignment& solution_assignment, const RoutingDimension& dimension) {
4316 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(vehicles());
4317 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4318 if (!solution_assignment.Bound(NextVar(vehicle))) {
4319 LOG(DFATAL) << "GetCumulBounds() called on incomplete solution:"
4320 << " NextVar(" << vehicle << ") is unbound.";
4321 }
4322 }
4323
4324 for (int vehicle_id = 0; vehicle_id < vehicles(); ++vehicle_id) {
4325 int64 index = Start(vehicle_id);
4326 IntVar* dim_var = dimension.CumulVar(index);
4327 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4328 solution_assignment.Max(dim_var));
4329 while (!IsEnd(index)) {
4330 index = solution_assignment.Value(NextVar(index));
4331 IntVar* dim_var = dimension.CumulVar(index);
4332 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4333 solution_assignment.Max(dim_var));
4334 }
4335 }
4336 return cumul_bounds;
4337}
4338#endif
4339
4340Assignment* RoutingModel::GetOrCreateAssignment() {
4341 if (assignment_ == nullptr) {
4342 assignment_ = solver_->MakeAssignment();
4343 assignment_->Add(nexts_);
4345 assignment_->Add(vehicle_vars_);
4346 }
4347 assignment_->AddObjective(cost_);
4348 }
4349 return assignment_;
4350}
4351
4352Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4353 if (tmp_assignment_ == nullptr) {
4354 tmp_assignment_ = solver_->MakeAssignment();
4355 tmp_assignment_->Add(nexts_);
4356 }
4357 return tmp_assignment_;
4358}
4359
4360RegularLimit* RoutingModel::GetOrCreateLimit() {
4361 if (limit_ == nullptr) {
4362 limit_ = solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4363 kint64max, /*smart_time_check=*/true);
4364 }
4365 return limit_;
4366}
4367
4368RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4369 if (ls_limit_ == nullptr) {
4370 ls_limit_ =
4371 solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4372 /*solutions=*/1, /*smart_time_check=*/true);
4373 }
4374 return ls_limit_;
4375}
4376
4377RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4378 if (lns_limit_ == nullptr) {
4379 lns_limit_ =
4380 solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4381 kint64max, /*smart_time_check=*/false);
4382 }
4383 return lns_limit_;
4384}
4385
4386RegularLimit*
4387RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4388 if (first_solution_lns_limit_ == nullptr) {
4389 first_solution_lns_limit_ =
4390 solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4391 kint64max, /*smart_time_check=*/false);
4392 }
4393 return first_solution_lns_limit_;
4394}
4395
4396LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4397 LocalSearchOperator* insertion_operator =
4398 CreateCPOperator<MakeActiveOperator>();
4399 if (!pickup_delivery_pairs_.empty()) {
4400 insertion_operator = solver_->ConcatenateOperators(
4401 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4402 }
4403 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4404 insertion_operator = solver_->ConcatenateOperators(
4405 {CreateOperator<MakePairActiveOperator>(
4406 implicit_pickup_delivery_pairs_without_alternatives_),
4407 insertion_operator});
4408 }
4409 return insertion_operator;
4410}
4411
4412LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4413 LocalSearchOperator* make_inactive_operator =
4414 CreateCPOperator<MakeInactiveOperator>();
4415 if (!pickup_delivery_pairs_.empty()) {
4416 make_inactive_operator = solver_->ConcatenateOperators(
4417 {CreatePairOperator<MakePairInactiveOperator>(),
4418 make_inactive_operator});
4419 }
4420 return make_inactive_operator;
4421}
4422
4423void RoutingModel::CreateNeighborhoodOperators(
4424 const RoutingSearchParameters& parameters) {
4425 local_search_operators_.clear();
4426 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER, nullptr);
4427 {
4428 // Operators defined by Solver::LocalSearchOperators.
4429 const std::vector<
4430 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4431 operator_by_type = {{OR_OPT, Solver::OROPT},
4432 {PATH_LNS, Solver::PATHLNS},
4433 {FULL_PATH_LNS, Solver::FULLPATHLNS},
4434 {INACTIVE_LNS, Solver::UNACTIVELNS}};
4435 for (const auto [type, op] : operator_by_type) {
4436 local_search_operators_[type] =
4438 ? solver_->MakeOperator(nexts_, op)
4439 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4440 }
4441 }
4442 {
4443 // Operators defined by Solver::EvaluatorLocalSearchOperators.
4444 const std::vector<std::pair<RoutingLocalSearchOperator,
4446 operator_by_type = {{LIN_KERNIGHAN, Solver::LK},
4447 {TSP_OPT, Solver::TSPOPT},
4448 {TSP_LNS, Solver::TSPLNS}};
4449 for (const auto [type, op] : operator_by_type) {
4450 auto arc_cost =
4451 absl::bind_front(&RoutingModel::GetArcCostForVehicle, this);
4452 local_search_operators_[type] =
4454 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4455 : solver_->MakeOperator(nexts_, vehicle_vars_,
4456 std::move(arc_cost), op);
4457 }
4458 }
4459
4460 // Other operators defined in the CP solver.
4461 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4462 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4463 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4464 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4465 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4466 CreateCPOperator<RelocateAndMakeActiveOperator>();
4467 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4468 CreateCPOperator<MakeActiveAndRelocate>();
4469 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4470 CreateCPOperator<MakeChainInactiveOperator>();
4471 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4472 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4473 CreateCPOperator<ExtendedSwapActiveOperator>();
4474
4475 // Routing-specific operators.
4476 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4477 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4478 local_search_operators_[RELOCATE_PAIR] =
4479 CreatePairOperator<PairRelocateOperator>();
4480 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4481 light_relocate_pair_operators.push_back(
4482 CreatePairOperator<LightPairRelocateOperator>());
4483 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4484 solver_->ConcatenateOperators(light_relocate_pair_operators);
4485 local_search_operators_[EXCHANGE_PAIR] =
4486 CreatePairOperator<PairExchangeOperator>();
4487 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4488 CreatePairOperator<PairExchangeRelocateOperator>();
4489 local_search_operators_[RELOCATE_NEIGHBORS] =
4490 CreateOperator<MakeRelocateNeighborsOperator>(
4491 absl::bind_front(&RoutingModel::GetHomogeneousCost, this));
4492 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4493 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4494 CreatePairOperator<SwapIndexPairOperator>(),
4495 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4496 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4497 local_search_operators_[RELOCATE_SUBTRIP] =
4498 CreatePairOperator<RelocateSubtrip>();
4499 local_search_operators_[EXCHANGE_SUBTRIP] =
4500 CreatePairOperator<ExchangeSubtrip>();
4501
4502 const auto arc_cost_for_path_start =
4503 [this](int64 before_node, int64 after_node, int64 start_index) {
4504 const int vehicle = index_to_vehicle_[start_index];
4505 const int64 arc_cost =
4506 GetArcCostForVehicle(before_node, after_node, vehicle);
4507 return (before_node != start_index || IsEnd(after_node))
4508 ? arc_cost
4509 : CapSub(arc_cost, GetFixedCostOfVehicle(vehicle));
4510 };
4511 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4512 solver_->RevAlloc(new RelocateExpensiveChain(
4513 nexts_,
4514 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
4515 : vehicle_vars_,
4516 vehicle_start_class_callback_,
4517 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4518 arc_cost_for_path_start));
4519
4520 // Insertion-based LNS neighborhoods.
4521 const auto make_global_cheapest_insertion_filtered_heuristic =
4522 [this, &parameters]() {
4523 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4524 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4525 ls_gci_parameters.is_sequential = false;
4526 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4527 ls_gci_parameters.neighbors_ratio =
4528 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4529 ls_gci_parameters.min_neighbors =
4530 parameters.cheapest_insertion_ls_operator_min_neighbors();
4531 ls_gci_parameters.use_neighbors_ratio_for_initialization = true;
4532 ls_gci_parameters.add_unperformed_entries =
4533 parameters.cheapest_insertion_add_unperformed_entries();
4534 return absl::make_unique<Heuristic>(
4535 this, absl::bind_front(&RoutingModel::GetArcCostForVehicle, this),
4536 absl::bind_front(&RoutingModel::UnperformedPenaltyOrValue, this, 0),
4537 GetOrCreateFeasibilityFilterManager(parameters), ls_gci_parameters);
4538 };
4539 const auto make_local_cheapest_insertion_filtered_heuristic =
4540 [this, &parameters]() {
4541 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4542 this, absl::bind_front(&RoutingModel::GetArcCostForVehicle, this),
4543 GetOrCreateFeasibilityFilterManager(parameters));
4544 };
4545 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4546 solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4547 make_global_cheapest_insertion_filtered_heuristic(),
4548 parameters.heuristic_close_nodes_lns_num_nodes()));
4549
4550 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4551 solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4552 make_local_cheapest_insertion_filtered_heuristic(),
4553 parameters.heuristic_close_nodes_lns_num_nodes()));
4554
4555 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4556 solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4557 make_global_cheapest_insertion_filtered_heuristic()));
4558
4559 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4560 solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4561 make_local_cheapest_insertion_filtered_heuristic()));
4562
4563 local_search_operators_
4564 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4565 solver_->RevAlloc(
4566 new RelocatePathAndHeuristicInsertUnperformedOperator(
4567 make_global_cheapest_insertion_filtered_heuristic()));
4568
4569 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4570 solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4571 make_global_cheapest_insertion_filtered_heuristic(),
4572 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4573 arc_cost_for_path_start));
4574
4575 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4576 solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4577 make_local_cheapest_insertion_filtered_heuristic(),
4578 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4579 arc_cost_for_path_start));
4580}
4581
4582#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4583 if (search_parameters.local_search_operators().use_##operator_method() == \
4584 BOOL_TRUE) { \
4585 operators.push_back(local_search_operators_[operator_type]); \
4586 }
4587
4588LocalSearchOperator* RoutingModel::ConcatenateOperators(
4589 const RoutingSearchParameters& search_parameters,
4590 const std::vector<LocalSearchOperator*>& operators) const {
4591 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4592 return solver_->MultiArmedBanditConcatenateOperators(
4593 operators,
4594 search_parameters
4595 .multi_armed_bandit_compound_operator_memory_coefficient(),
4596 search_parameters
4597 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4598 /*maximize=*/false);
4599 }
4600 return solver_->ConcatenateOperators(operators);
4601}
4602
4603LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4604 const RoutingSearchParameters& search_parameters) const {
4605 std::vector<LocalSearchOperator*> operator_groups;
4606 std::vector<LocalSearchOperator*> operators = extra_operators_;
4607 if (!pickup_delivery_pairs_.empty()) {
4608 CP_ROUTING_PUSH_OPERATOR(RELOCATE_PAIR, relocate_pair, operators);
4609 // Only add the light version of relocate pair if the normal version has not
4610 // already been added as it covers a subset of its neighborhood.
4611 if (search_parameters.local_search_operators().use_relocate_pair() ==
4612 BOOL_FALSE) {
4613 CP_ROUTING_PUSH_OPERATOR(LIGHT_RELOCATE_PAIR, light_relocate_pair,
4614 operators);
4615 }
4616 CP_ROUTING_PUSH_OPERATOR(EXCHANGE_PAIR, exchange_pair, operators);
4617 CP_ROUTING_PUSH_OPERATOR(NODE_PAIR_SWAP, node_pair_swap_active, operators);
4618 CP_ROUTING_PUSH_OPERATOR(RELOCATE_SUBTRIP, relocate_subtrip, operators);
4619 CP_ROUTING_PUSH_OPERATOR(EXCHANGE_SUBTRIP, exchange_subtrip, operators);
4620 }
4621 if (vehicles_ > 1) {
4622 if (GetNumOfSingletonNodes() > 0) {
4623 // If there are only pairs in the model the only case where Relocate will
4624 // work is for intra-route moves, already covered by OrOpt.
4625 // We are not disabling Exchange and Cross because there are no
4626 // intra-route equivalents.
4627 CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators);
4628 }
4629 CP_ROUTING_PUSH_OPERATOR(EXCHANGE, exchange, operators);
4630 CP_ROUTING_PUSH_OPERATOR(CROSS, cross, operators);
4631 }
4632 if (!pickup_delivery_pairs_.empty() ||
4633 search_parameters.local_search_operators().use_relocate_neighbors() ==
4634 BOOL_TRUE) {
4635 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4636 }
4637 const LocalSearchMetaheuristic::Value local_search_metaheuristic =
4638 search_parameters.local_search_metaheuristic();
4639 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4640 local_search_metaheuristic !=
4641 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4642 local_search_metaheuristic !=
4643 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4644 CP_ROUTING_PUSH_OPERATOR(LIN_KERNIGHAN, lin_kernighan, operators);
4645 }
4646 CP_ROUTING_PUSH_OPERATOR(TWO_OPT, two_opt, operators);
4647 CP_ROUTING_PUSH_OPERATOR(OR_OPT, or_opt, operators);
4648 CP_ROUTING_PUSH_OPERATOR(RELOCATE_EXPENSIVE_CHAIN, relocate_expensive_chain,
4649 operators);
4650 if (!disjunctions_.empty()) {
4651 CP_ROUTING_PUSH_OPERATOR(MAKE_INACTIVE, make_inactive, operators);
4652 CP_ROUTING_PUSH_OPERATOR(MAKE_CHAIN_INACTIVE, make_chain_inactive,
4653 operators);
4654 CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE, make_active, operators);
4655
4656 // The relocate_and_make_active parameter activates all neighborhoods
4657 // relocating a node together with making another active.
4658 CP_ROUTING_PUSH_OPERATOR(RELOCATE_AND_MAKE_ACTIVE, relocate_and_make_active,
4659 operators);
4660 CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE_AND_RELOCATE, relocate_and_make_active,
4661 operators);
4662
4663 CP_ROUTING_PUSH_OPERATOR(SWAP_ACTIVE, swap_active, operators);
4664 CP_ROUTING_PUSH_OPERATOR(EXTENDED_SWAP_ACTIVE, extended_swap_active,
4665 operators);
4666 }
4667 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4668
4669 // Second local search loop: LNS-like operators.
4670 operators.clear();
4671 if (vehicles() > 1) {
4672 // NOTE: The following heuristic path LNS with a single vehicle are
4673 // equivalent to using the heuristic as first solution strategy, so we only
4674 // add these moves if we have at least 2 vehicles in the model.
4675 CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_PATH_LNS,
4676 global_cheapest_insertion_path_lns, operators);
4677 CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_PATH_LNS,
4678 local_cheapest_insertion_path_lns, operators);
4680 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4681 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4682 }
4683 CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4684 global_cheapest_insertion_expensive_chain_lns,
4685 operators);
4686 CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4687 local_cheapest_insertion_expensive_chain_lns,
4688 operators);
4689 CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4690 global_cheapest_insertion_close_nodes_lns,
4691 operators);
4692 CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4693 local_cheapest_insertion_close_nodes_lns, operators);
4694 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4695
4696 // Third local search loop: Expensive LNS operators.
4697 operators.clear();
4698 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4699 local_search_metaheuristic !=
4700 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4701 local_search_metaheuristic !=
4702 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4703 CP_ROUTING_PUSH_OPERATOR(TSP_OPT, tsp_opt, operators);
4704 }
4705 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4706 local_search_metaheuristic !=
4707 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4708 local_search_metaheuristic !=
4709 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4710 CP_ROUTING_PUSH_OPERATOR(TSP_LNS, tsp_lns, operators);
4711 }
4712 CP_ROUTING_PUSH_OPERATOR(FULL_PATH_LNS, full_path_lns, operators);
4713 CP_ROUTING_PUSH_OPERATOR(PATH_LNS, path_lns, operators);
4714 if (!disjunctions_.empty()) {
4715 CP_ROUTING_PUSH_OPERATOR(INACTIVE_LNS, inactive_lns, operators);
4716 }
4717 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4718
4719 return solver_->ConcatenateOperators(operator_groups);
4720}
4721
4722#undef CP_ROUTING_PUSH_OPERATOR
4723
4724bool HasUnaryDimension(const std::vector<RoutingDimension*>& dimensions) {
4725 for (const RoutingDimension* dimension : dimensions) {
4726 if (dimension->GetUnaryTransitEvaluator(0) != nullptr) return true;
4727 }
4728 return false;
4729}
4730
4731namespace {
4732
4733void ConvertVectorInt64ToVectorInt(const std::vector<int64>& input,
4734 std::vector<int>* output) {
4735 const int n = input.size();
4736 output->resize(n);
4737 int* data = output->data();
4738 for (int i = 0; i < n; ++i) {
4739 const int element = static_cast<int>(input[i]);
4740 DCHECK_EQ(input[i], static_cast<int64>(element));
4741 data[i] = element;
4742 }
4743}
4744
4745} // namespace
4746
4747std::vector<LocalSearchFilterManager::FilterEvent>
4748RoutingModel::GetOrCreateLocalSearchFilters(
4749 const RoutingSearchParameters& parameters, bool filter_cost) {
4750 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4751 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4752 // As of 2013/01, three filters evaluate sub-parts of the objective
4753 // function:
4754 // - NodeDisjunctionFilter: takes disjunction penalty costs into account,
4755 // - PathCumulFilter: takes dimension span costs into account,
4756 // - ObjectiveFilter:
4757 // - VehicleAmortizedCostFilter, which considers the part of the cost
4758 // related to amortized linear and quadratic vehicle cost factors.
4759 // - LocalSearchObjectiveFilter, which takes dimension "arc" costs into
4760 // account.
4761 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4762 // VehicleAmortizedCostFilter can have a negative value, so it must be first.
4763 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4764 filters.push_back({MakeVehicleAmortizedCostFilter(*this), kAccept});
4765 }
4766
4767 // The SumObjectiveFilter has the best reject/second ratio in practice,
4768 // so it is the earliest.
4769 if (filter_cost) {
4771 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4772 nexts_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
4773 Solver::LE);
4774 filters.push_back({sum, kAccept});
4775 } else {
4776 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4777 nexts_, vehicle_vars_,
4778 [this](int64 i, int64 j, int64 k) {
4779 return GetArcCostForVehicle(i, j, k);
4780 },
4781 Solver::LE);
4782 filters.push_back({sum, kAccept});
4783 }
4784 }
4785
4786 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4787
4788 if (vehicles_ > max_active_vehicles_) {
4789 filters.push_back({MakeMaxActiveVehiclesFilter(*this), kAccept});
4790 }
4791
4792 if (!disjunctions_.empty()) {
4793 filters.push_back({MakeNodeDisjunctionFilter(*this), kAccept});
4794 }
4795
4796 if (!pickup_delivery_pairs_.empty()) {
4797 filters.push_back(
4798 {MakePickupDeliveryFilter(*this, pickup_delivery_pairs_,
4799 vehicle_pickup_delivery_policy_),
4800 kAccept});
4801 }
4802
4803 if (HasTypeRegulations()) {
4804 filters.push_back({MakeTypeRegulationsFilter(*this), kAccept});
4805 }
4806
4807 filters.push_back({MakeVehicleVarFilter(*this), kAccept});
4808
4809 const PathState* path_state_reference = nullptr;
4811 std::vector<int> path_starts;
4812 std::vector<int> path_ends;
4813 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4814 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4815
4816 auto path_state = absl::make_unique<PathState>(
4817 Size() + vehicles(), std::move(path_starts), std::move(path_ends));
4818 path_state_reference = path_state.get();
4819 filters.push_back(
4820 {MakePathStateFilter(solver_.get(), std::move(path_state), Nexts()),
4821 kRelax});
4822 AppendLightWeightDimensionFilters(path_state_reference, GetDimensions(),
4823 &filters);
4824 }
4825
4827 &filters);
4828
4829 for (const RoutingDimension* dimension : dimensions_) {
4830 if (!dimension->HasBreakConstraints()) continue;
4831 filters.push_back({MakeVehicleBreaksFilter(*this, *dimension), kAccept});
4832 }
4833 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4834 return filters;
4835}
4836
4837LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4838 const RoutingSearchParameters& parameters) {
4839 if (!local_search_filter_manager_) {
4840 local_search_filter_manager_ =
4841 solver_->RevAlloc(new LocalSearchFilterManager(
4842 GetOrCreateLocalSearchFilters(parameters)));
4843 }
4844 return local_search_filter_manager_;
4845}
4846
4847std::vector<LocalSearchFilterManager::FilterEvent>
4848RoutingModel::GetOrCreateFeasibilityFilters(
4849 const RoutingSearchParameters& parameters) {
4850 return GetOrCreateLocalSearchFilters(parameters, false);
4851}
4852
4853LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4854 const RoutingSearchParameters& parameters) {
4855 if (!feasibility_filter_manager_) {
4856 feasibility_filter_manager_ =
4857 solver_->RevAlloc(new LocalSearchFilterManager(
4858 GetOrCreateFeasibilityFilters(parameters)));
4859 }
4860 return feasibility_filter_manager_;
4861}
4862
4863LocalSearchFilterManager*
4864RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4865 const RoutingSearchParameters& parameters) {
4866 if (!strong_feasibility_filter_manager_) {
4867 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4868 GetOrCreateFeasibilityFilters(parameters);
4869 filters.push_back({MakeCPFeasibilityFilter(this),
4870 LocalSearchFilterManager::FilterEventType::kAccept});
4871 strong_feasibility_filter_manager_ =
4872 solver_->RevAlloc(new LocalSearchFilterManager(std::move(filters)));
4873 }
4874 return strong_feasibility_filter_manager_;
4875}
4876
4877namespace {
4878bool AllTransitsPositive(const RoutingDimension& dimension) {
4879 for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4880 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4881 return false;
4882 }
4883 }
4884 return true;
4885}
4886} // namespace
4887
4888void RoutingModel::StoreDimensionCumulOptimizers(
4889 const RoutingSearchParameters& parameters) {
4890 Assignment* packed_dimensions_collector_assignment =
4891 solver_->MakeAssignment();
4892 packed_dimensions_collector_assignment->AddObjective(CostVar());
4893 const int num_dimensions = dimensions_.size();
4894 local_optimizer_index_.resize(num_dimensions, -1);
4895 global_optimizer_index_.resize(num_dimensions, -1);
4896 for (DimensionIndex dim = DimensionIndex(0); dim < num_dimensions; dim++) {
4897 RoutingDimension* dimension = dimensions_[dim];
4898 if (dimension->global_span_cost_coefficient() > 0 ||
4899 !dimension->GetNodePrecedences().empty()) {
4900 // Use global optimizer.
4901 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4902 global_dimension_optimizers_.push_back(
4903 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4904 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4905 if (!AllTransitsPositive(*dimension)) {
4906 dimension->SetOffsetForGlobalOptimizer(0);
4907 continue;
4908 }
4909 int64 offset = vehicles() == 0 ? 0 : kint64max;
4910 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4911 DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4912 offset =
4913 std::min(offset, dimension->CumulVar(Start(vehicle))->Min() - 1);
4914 }
4915 dimension->SetOffsetForGlobalOptimizer(std::max(Zero(), offset));
4916 } else {
4917 bool has_span_cost = false;
4918 bool has_span_limit = false;
4919 std::vector<int64> vehicle_offsets(vehicles());
4920 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4921 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4922 has_span_cost = true;
4923 }
4924 if (dimension->GetSpanUpperBoundForVehicle(vehicle) < kint64max) {
4925 has_span_limit = true;
4926 }
4927 DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4928 vehicle_offsets[vehicle] =
4929 dimension->AreVehicleTransitsPositive(vehicle)
4930 ? std::max(Zero(),
4931 dimension->CumulVar(Start(vehicle))->Min() - 1)
4932 : 0;
4933 }
4934 bool has_soft_lower_bound = false;
4935 bool has_soft_upper_bound = false;
4936 for (int i = 0; i < dimension->cumuls().size(); ++i) {
4937 if (dimension->HasCumulVarSoftLowerBound(i)) {
4938 has_soft_lower_bound = true;
4939 }
4940 if (dimension->HasCumulVarSoftUpperBound(i)) {
4941 has_soft_upper_bound = true;
4942 }
4943 }
4944 int num_linear_constraints = 0;
4945 if (has_span_cost) ++num_linear_constraints;
4946 if (has_span_limit) ++num_linear_constraints;
4947 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4948 if (has_soft_lower_bound) ++num_linear_constraints;
4949 if (has_soft_upper_bound) ++num_linear_constraints;
4950 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4951 if (num_linear_constraints >= 2) {
4952 dimension->SetVehicleOffsetsForLocalOptimizer(
4953 std::move(vehicle_offsets));
4954 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4955 local_dimension_optimizers_.push_back(
4956 absl::make_unique<LocalDimensionCumulOptimizer>(
4957 dimension, parameters.continuous_scheduling_solver()));
4958 bool has_intervals = false;
4959 for (const SortedDisjointIntervalList& intervals :
4960 dimension->forbidden_intervals()) {
4961 // TODO(user): Change the following test to check intervals within
4962 // the domain of the corresponding variables.
4963 if (intervals.NumIntervals() > 0) {
4964 has_intervals = true;
4965 break;
4966 }
4967 }
4968 if (dimension->HasBreakConstraints() || has_intervals) {
4969 local_dimension_mp_optimizers_.push_back(
4970 absl::make_unique<LocalDimensionCumulOptimizer>(
4971 dimension, parameters.mixed_integer_scheduling_solver()));
4972 } else {
4973 local_dimension_mp_optimizers_.push_back(nullptr);
4974 }
4975 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4976 }
4977 }
4978 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4979 local_dimension_optimizers_.size());
4980 }
4981
4982 // NOTE(b/129252839): We also add all other extra variables to the
4983 // packed_dimensions_collector_assignment to make sure the necessary
4984 // propagations on these variables after packing are correctly stored.
4985 for (IntVar* const extra_var : extra_vars_) {
4986 packed_dimensions_collector_assignment->Add(extra_var);
4987 }
4988 for (IntervalVar* const extra_interval : extra_intervals_) {
4989 packed_dimensions_collector_assignment->Add(extra_interval);
4990 }
4991
4992 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4993 packed_dimensions_collector_assignment);
4994}
4995
4997 const {
4998 std::vector<RoutingDimension*> dimensions;
4999 for (RoutingDimension* dimension : dimensions_) {
5000 bool has_soft_or_span_cost = false;
5001 for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
5002 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5003 has_soft_or_span_cost = true;
5004 break;
5005 }
5006 }
5007 if (!has_soft_or_span_cost) {
5008 for (int i = 0; i < dimension->cumuls().size(); ++i) {
5009 if (dimension->HasCumulVarSoftUpperBound(i) ||
5010 dimension->HasCumulVarSoftLowerBound(i)) {
5011 has_soft_or_span_cost = true;
5012 break;
5013 }
5014 }
5015 }
5016 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5017 }
5018 return dimensions;
5019}
5020
5022RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5023 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5024 finalizer_variable_cost_pairs_.end(),
5025 [](const std::pair<IntVar*, int64>& var_cost1,
5026 const std::pair<IntVar*, int64>& var_cost2) {
5027 return var_cost1.second > var_cost2.second;
5028 });
5029 const int num_variables = finalizer_variable_cost_pairs_.size() +
5030 finalizer_variable_target_pairs_.size();
5031 std::vector<IntVar*> variables;
5032 std::vector<int64> targets;
5033 variables.reserve(num_variables);
5034 targets.reserve(num_variables);
5035 for (const auto& variable_cost : finalizer_variable_cost_pairs_) {
5036 variables.push_back(variable_cost.first);
5037 targets.push_back(kint64min);
5038 }
5039 for (const auto& variable_target : finalizer_variable_target_pairs_) {
5040 variables.push_back(variable_target.first);
5041 targets.push_back(variable_target.second);
5042 }
5043 return MakeSetValuesFromTargets(solver(), std::move(variables),
5044 std::move(targets));
5045}
5046
5047DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5048 std::vector<DecisionBuilder*> decision_builders;
5049 decision_builders.push_back(solver_->MakePhase(
5051
5052 if (!local_dimension_optimizers_.empty()) {
5053 decision_builders.push_back(
5054 solver_->RevAlloc(new SetCumulsFromLocalDimensionCosts(
5055 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5056 lns_limit)));
5057 }
5058 if (!global_dimension_optimizers_.empty()) {
5059 decision_builders.push_back(
5060 solver_->RevAlloc(new SetCumulsFromGlobalDimensionCosts(
5061 &global_dimension_optimizers_, lns_limit)));
5062 }
5063 decision_builders.push_back(
5064 CreateFinalizerForMinimizedAndMaximizedVariables());
5065
5066 return solver_->Compose(decision_builders);
5067}
5068
5069void RoutingModel::CreateFirstSolutionDecisionBuilders(
5070 const RoutingSearchParameters& search_parameters) {
5071 first_solution_decision_builders_.resize(
5072 FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr);
5073 first_solution_filtered_decision_builders_.resize(
5074 FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr);
5075 DecisionBuilder* const finalize_solution =
5076 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5077 // Default heuristic
5078 first_solution_decision_builders_
5079 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5080 // Global cheapest addition heuristic.
5081 first_solution_decision_builders_
5082 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5083 nexts_,
5084 [this](int64 i, int64 j) { return GetArcCostForFirstSolution(i, j); },
5086 // Cheapest addition heuristic.
5087 Solver::IndexEvaluator2 eval = [this](int64 i, int64 j) {
5088 return GetArcCostForFirstSolution(i, j);
5089 };
5090 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5091 solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5092 // Path-based cheapest addition heuristic.
5093 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5094 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5095 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5096 first_solution_filtered_decision_builders_
5097 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5098 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5099 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5100 this,
5101 [this](int64 i, int64 j) {
5102 return GetArcCostForFirstSolution(i, j);
5103 },
5104 GetOrCreateFeasibilityFilterManager(search_parameters))));
5105 first_solution_decision_builders_
5106 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5107 solver_->Try(first_solution_filtered_decision_builders_
5108 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5109 first_solution_decision_builders_
5110 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5111 }
5112 // Path-based most constrained arc addition heuristic.
5113 Solver::VariableValueComparator comp = [this](int64 i, int64 j, int64 k) {
5114 return ArcIsMoreConstrainedThanArc(i, j, k);
5115 };
5116
5117 first_solution_decision_builders_
5118 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5119 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5120 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5121 first_solution_filtered_decision_builders_
5122 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5123 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5124 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5125 this, comp,
5126 GetOrCreateFeasibilityFilterManager(search_parameters))));
5127 first_solution_decision_builders_
5128 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5129 first_solution_filtered_decision_builders_
5130 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5131 first_solution_decision_builders_
5132 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5133 }
5134 // Evaluator-based path heuristic.
5135 if (first_solution_evaluator_ != nullptr) {
5136 first_solution_decision_builders_
5137 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5138 nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5139 } else {
5140 first_solution_decision_builders_
5141 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = nullptr;
5142 }
5143 // All unperformed heuristic.
5144 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5145 solver_->RevAlloc(new AllUnperformed(this));
5146 // Best insertion heuristic.
5147 RegularLimit* const ls_limit =
5148 solver_->MakeLimit(GetTimeLimit(search_parameters), kint64max, kint64max,
5149 kint64max, /*smart_time_check=*/true);
5150 DecisionBuilder* const finalize = solver_->MakeSolveOnce(
5151 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5152 LocalSearchPhaseParameters* const insertion_parameters =
5153 solver_->MakeLocalSearchPhaseParameters(
5154 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5155 GetOrCreateLocalSearchFilterManager(search_parameters));
5156 std::vector<IntVar*> decision_vars = nexts_;
5158 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5159 vehicle_vars_.end());
5160 }
5161 const int64 optimization_step = std::max(
5162 MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5163 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5164 solver_->MakeNestedOptimize(
5165 solver_->MakeLocalSearchPhase(
5166 decision_vars, solver_->RevAlloc(new AllUnperformed(this)),
5167 insertion_parameters),
5168 GetOrCreateAssignment(), false, optimization_step);
5169 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5170 solver_->Compose(first_solution_decision_builders_
5171 [FirstSolutionStrategy::BEST_INSERTION],
5172 finalize);
5173
5174 // Parallel/Sequential Global cheapest insertion
5175 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5176 gci_parameters;
5177 gci_parameters.is_sequential = false;
5178 gci_parameters.farthest_seeds_ratio =
5179 search_parameters.cheapest_insertion_farthest_seeds_ratio();
5180 gci_parameters.neighbors_ratio =
5181 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5182 gci_parameters.min_neighbors =
5183 search_parameters.cheapest_insertion_first_solution_min_neighbors();
5184 gci_parameters.use_neighbors_ratio_for_initialization = false;
5185 gci_parameters.add_unperformed_entries =
5186 search_parameters.cheapest_insertion_add_unperformed_entries();
5187 for (bool is_sequential : {false, true}) {
5188 FirstSolutionStrategy::Value first_solution_strategy =
5189 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5190 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5191 gci_parameters.is_sequential = is_sequential;
5192
5193 first_solution_filtered_decision_builders_[first_solution_strategy] =
5194 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5195 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5196 this,
5197 [this](int64 i, int64 j, int64 vehicle) {
5198 return GetArcCostForVehicle(i, j, vehicle);
5199 },
5200 [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
5201 GetOrCreateFeasibilityFilterManager(search_parameters),
5202 gci_parameters)));
5203 IntVarFilteredDecisionBuilder* const strong_gci =
5204 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5205 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5206 this,
5207 [this](int64 i, int64 j, int64 vehicle) {
5208 return GetArcCostForVehicle(i, j, vehicle);
5209 },
5210 [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
5211 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5212 gci_parameters)));
5213 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5214 first_solution_filtered_decision_builders_[first_solution_strategy],
5215 solver_->Try(strong_gci, first_solution_decision_builders_
5216 [FirstSolutionStrategy::BEST_INSERTION]));
5217 }
5218
5219 // Local cheapest insertion
5220 first_solution_filtered_decision_builders_
5221 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5222 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5223 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5224 this,
5225 [this](int64 i, int64 j, int64 vehicle) {
5226 return GetArcCostForVehicle(i, j, vehicle);
5227 },
5228 GetOrCreateFeasibilityFilterManager(search_parameters))));
5229 IntVarFilteredDecisionBuilder* const strong_lci =
5230 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5231 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5232 this,
5233 [this](int64 i, int64 j, int64 vehicle) {
5234 return GetArcCostForVehicle(i, j, vehicle);
5235 },
5236 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5237 first_solution_decision_builders_
5238 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5239 first_solution_filtered_decision_builders_
5240 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5241 solver_->Try(strong_lci,
5242 first_solution_decision_builders_
5243 [FirstSolutionStrategy::BEST_INSERTION]));
5244 // Savings
5245 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5246 savings_parameters.neighbors_ratio =
5247 search_parameters.savings_neighbors_ratio();
5248 savings_parameters.max_memory_usage_bytes =
5249 search_parameters.savings_max_memory_usage_bytes();
5250 savings_parameters.add_reverse_arcs =
5251 search_parameters.savings_add_reverse_arcs();
5252 savings_parameters.arc_coefficient =
5253 search_parameters.savings_arc_coefficient();
5254 LocalSearchFilterManager* filter_manager = nullptr;
5255 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5256 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5257 }
5258
5259 if (search_parameters.savings_parallel_routes()) {
5260 IntVarFilteredDecisionBuilder* savings_db =
5261 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5262 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5263 this, &manager_, savings_parameters, filter_manager)));
5264 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5265 first_solution_filtered_decision_builders_
5266 [FirstSolutionStrategy::SAVINGS] = savings_db;
5267 }
5268
5269 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5270 solver_->Try(savings_db,
5271 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5272 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5273 this, &manager_, savings_parameters,
5274 GetOrCreateStrongFeasibilityFilterManager(
5275 search_parameters)))));
5276 } else {
5277 IntVarFilteredDecisionBuilder* savings_db =
5278 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5279 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5280 this, &manager_, savings_parameters, filter_manager)));
5281 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5282 first_solution_filtered_decision_builders_
5283 [FirstSolutionStrategy::SAVINGS] = savings_db;
5284 }
5285
5286 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5287 solver_->Try(savings_db,
5288 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5289 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5290 this, &manager_, savings_parameters,
5291 GetOrCreateStrongFeasibilityFilterManager(
5292 search_parameters)))));
5293 }
5294 // Sweep
5295 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5296 solver_->RevAlloc(new SweepBuilder(this, true));
5297 DecisionBuilder* sweep_builder =
5298 solver_->RevAlloc(new SweepBuilder(this, false));
5299 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5300 solver_->Try(
5301 sweep_builder,
5302 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5303 // Christofides
5304 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5305 solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5306 absl::make_unique<ChristofidesFilteredHeuristic>(
5307 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5308 search_parameters.christofides_use_minimum_matching())));
5309 // Automatic
5310 // TODO(user): make this smarter.
5311 const bool has_precedences = std::any_of(
5312 dimensions_.begin(), dimensions_.end(),
5313 [](RoutingDimension* dim) { return !dim->GetNodePrecedences().empty(); });
5314 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5315 automatic_first_solution_strategy_ =
5316 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5317 } else {
5318 automatic_first_solution_strategy_ =
5319 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5320 }
5321 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5322 first_solution_decision_builders_[automatic_first_solution_strategy_];
5323 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5324 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5325}
5326
5327DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5328 const RoutingSearchParameters& search_parameters) const {
5329 const FirstSolutionStrategy::Value first_solution_strategy =
5330 search_parameters.first_solution_strategy();
5331 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5332 return first_solution_decision_builders_[first_solution_strategy];
5333 } else {
5334 return nullptr;
5335 }
5336}
5337
5338IntVarFilteredDecisionBuilder*
5339RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5340 const RoutingSearchParameters& search_parameters) const {
5341 const FirstSolutionStrategy::Value first_solution_strategy =
5342 search_parameters.first_solution_strategy();
5343 return first_solution_filtered_decision_builders_[first_solution_strategy];
5344}
5345
5346LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5347 const RoutingSearchParameters& search_parameters) {
5348 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5349 return solver_->MakeLocalSearchPhaseParameters(
5350 CostVar(), GetNeighborhoodOperators(search_parameters),
5351 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5352 GetOrCreateLocalSearchLimit(),
5353 GetOrCreateLocalSearchFilterManager(search_parameters));
5354}
5355
5356DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5357 const RoutingSearchParameters& search_parameters) {
5358 const int size = Size();
5359 DecisionBuilder* first_solution =
5360 GetFirstSolutionDecisionBuilder(search_parameters);
5361 LocalSearchPhaseParameters* const parameters =
5362 CreateLocalSearchParameters(search_parameters);
5363 SearchLimit* first_solution_lns_limit =
5364 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5365 DecisionBuilder* const first_solution_sub_decision_builder =
5366 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5367 first_solution_lns_limit);
5369 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5370 first_solution_sub_decision_builder,
5371 parameters);
5372 } else {
5373 const int all_size = size + size + vehicles_;
5374 std::vector<IntVar*> all_vars(all_size);
5375 for (int i = 0; i < size; ++i) {
5376 all_vars[i] = nexts_[i];
5377 }
5378 for (int i = size; i < all_size; ++i) {
5379 all_vars[i] = vehicle_vars_[i - size];
5380 }
5381 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5382 first_solution_sub_decision_builder,
5383 parameters);
5384 }
5385}
5386
5387void RoutingModel::SetupDecisionBuilders(
5388 const RoutingSearchParameters& search_parameters) {
5389 if (search_parameters.use_depth_first_search()) {
5390 SearchLimit* first_lns_limit =
5391 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5392 solve_db_ = solver_->Compose(
5393 GetFirstSolutionDecisionBuilder(search_parameters),
5394 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5395 first_lns_limit));
5396 } else {
5397 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5398 }
5399 CHECK(preassignment_ != nullptr);
5400 DecisionBuilder* restore_preassignment =
5401 solver_->MakeRestoreAssignment(preassignment_);
5402 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5403 improve_db_ =
5404 solver_->Compose(restore_preassignment,
5405 solver_->MakeLocalSearchPhase(
5406 GetOrCreateAssignment(),
5407 CreateLocalSearchParameters(search_parameters)));
5408 restore_assignment_ = solver_->Compose(
5409 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5410 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5411 restore_tmp_assignment_ = solver_->Compose(
5412 restore_preassignment,
5413 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5414 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5415}
5416
5417void RoutingModel::SetupMetaheuristics(
5418 const RoutingSearchParameters& search_parameters) {
5419 SearchMonitor* optimize;
5420 const LocalSearchMetaheuristic::Value metaheuristic =
5421 search_parameters.local_search_metaheuristic();
5422 // Some metaheuristics will effectively never terminate; warn
5423 // user if they fail to set a time limit.
5424 bool limit_too_long = !search_parameters.has_time_limit() &&
5425 search_parameters.solution_limit() == kint64max;
5426 const int64 optimization_step = std::max(
5427 MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5428 switch (metaheuristic) {
5429 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5431 optimize = solver_->MakeGuidedLocalSearch(
5432 false, cost_,
5433 [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
5434 optimization_step, nexts_,
5435 search_parameters.guided_local_search_lambda_coefficient());
5436 } else {
5437 optimize = solver_->MakeGuidedLocalSearch(
5438 false, cost_,
5439 [this](int64 i, int64 j, int64 k) {
5440 return GetArcCostForVehicle(i, j, k);
5441 },
5442 optimization_step, nexts_, vehicle_vars_,
5443 search_parameters.guided_local_search_lambda_coefficient());
5444 }
5445 break;
5446 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5447 optimize =
5448 solver_->MakeSimulatedAnnealing(false, cost_, optimization_step, 100);
5449 break;
5450 case LocalSearchMetaheuristic::TABU_SEARCH:
5451 optimize = solver_->MakeTabuSearch(false, cost_, optimization_step,
5452 nexts_, 10, 10, .8);
5453 break;
5454 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5455 std::vector<operations_research::IntVar*> tabu_vars;
5456 if (tabu_var_callback_) {
5457 tabu_vars = tabu_var_callback_(this);
5458 } else {
5459 tabu_vars.push_back(cost_);
5460 }
5461 optimize = solver_->MakeGenericTabuSearch(false, cost_, optimization_step,
5462 tabu_vars, 100);
5463 break;
5464 }
5465 default:
5466 limit_too_long = false;
5467 optimize = solver_->MakeMinimize(cost_, optimization_step);
5468 }
5469 if (limit_too_long) {
5470 LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5471 << " specified without sane timeout: solve may run forever.";
5472 }
5473 monitors_.push_back(optimize);
5474}
5475
5477 tabu_var_callback_ = std::move(tabu_var_callback);
5478}
5479
5480void RoutingModel::SetupAssignmentCollector(
5481 const RoutingSearchParameters& search_parameters) {
5482 Assignment* full_assignment = solver_->MakeAssignment();
5483 for (const RoutingDimension* const dimension : dimensions_) {
5484 full_assignment->Add(dimension->cumuls());
5485 }
5486 for (IntVar* const extra_var : extra_vars_) {
5487 full_assignment->Add(extra_var);
5488 }
5489 for (IntervalVar* const extra_interval : extra_intervals_) {
5490 full_assignment->Add(extra_interval);
5491 }
5492 full_assignment->Add(nexts_);
5493 full_assignment->Add(active_);
5494 full_assignment->Add(vehicle_vars_);
5495 full_assignment->AddObjective(cost_);
5496
5497 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5498 full_assignment, search_parameters.number_of_solutions_to_collect(),
5499 false);
5500 collect_one_assignment_ =
5501 solver_->MakeFirstSolutionCollector(full_assignment);
5502 monitors_.push_back(collect_assignments_);
5503}
5504
5505void RoutingModel::SetupTrace(
5506 const RoutingSearchParameters& search_parameters) {
5507 if (search_parameters.log_search()) {
5508 Solver::SearchLogParameters search_log_parameters;
5509 search_log_parameters.branch_period = 10000;
5510 search_log_parameters.objective = nullptr;
5511 search_log_parameters.variable = cost_;
5512 search_log_parameters.scaling_factor =
5513 search_parameters.log_cost_scaling_factor();
5514 search_log_parameters.offset = search_parameters.log_cost_offset();
5515 if (!search_parameters.log_tag().empty()) {
5516 const std::string& tag = search_parameters.log_tag();
5517 search_log_parameters.display_callback = [tag]() { return tag; };
5518 } else {
5519 search_log_parameters.display_callback = nullptr;
5520 }
5521 search_log_parameters.display_on_new_solutions_only = false;
5522 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5523 }
5524}
5525
5526void RoutingModel::SetupImprovementLimit(
5527 const RoutingSearchParameters& search_parameters) {
5528 if (search_parameters.has_improvement_limit_parameters()) {
5529 monitors_.push_back(solver_->MakeImprovementLimit(
5530 cost_, /*maximize=*/false, search_parameters.log_cost_scaling_factor(),
5531 search_parameters.log_cost_offset(),
5532 search_parameters.improvement_limit_parameters()
5533 .improvement_rate_coefficient(),
5534 search_parameters.improvement_limit_parameters()
5535 .improvement_rate_solutions_distance()));
5536 }
5537}
5538
5539void RoutingModel::SetupSearchMonitors(
5540 const RoutingSearchParameters& search_parameters) {
5541 monitors_.push_back(GetOrCreateLimit());
5542 SetupImprovementLimit(search_parameters);
5543 SetupMetaheuristics(search_parameters);
5544 SetupAssignmentCollector(search_parameters);
5545 SetupTrace(search_parameters);
5546}
5547
5548bool RoutingModel::UsesLightPropagation(
5549 const RoutingSearchParameters& search_parameters) const {
5550 return !search_parameters.use_full_propagation() &&
5551 !search_parameters.use_depth_first_search() &&
5552 search_parameters.first_solution_strategy() !=
5553 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5554}
5555
5557 int64 cost) {
5558 CHECK(var != nullptr);
5559 const int index = gtl::LookupOrInsert(&finalizer_variable_cost_index_, var,
5560 finalizer_variable_cost_pairs_.size());
5561 if (index < finalizer_variable_cost_pairs_.size()) {
5562 const int64 old_cost = finalizer_variable_cost_pairs_[index].second;
5563 finalizer_variable_cost_pairs_[index].second = CapAdd(old_cost, cost);
5564 } else {
5565 finalizer_variable_cost_pairs_.emplace_back(var, cost);
5566 }
5567}
5568
5570 CHECK(var != nullptr);
5571 if (finalizer_variable_target_set_.contains(var)) return;
5572 finalizer_variable_target_set_.insert(var);
5573 finalizer_variable_target_pairs_.emplace_back(var, target);
5574}
5575
5578}
5579
5582}
5583
5584void RoutingModel::SetupSearch(
5585 const RoutingSearchParameters& search_parameters) {
5586 SetupDecisionBuilders(search_parameters);
5587 SetupSearchMonitors(search_parameters);
5588}
5589
5591 extra_vars_.push_back(var);
5592}
5593
5595 extra_intervals_.push_back(interval);
5596}
5597
5598namespace {
5599
5600class PathSpansAndTotalSlacks : public Constraint {
5601 public:
5602 PathSpansAndTotalSlacks(const RoutingModel* model,
5603 const RoutingDimension* dimension,
5604 std::vector<IntVar*> spans,
5605 std::vector<IntVar*> total_slacks)
5606 : Constraint(model->solver()),
5607 model_(model),
5608 dimension_(dimension),
5609 spans_(std::move(spans)),
5610 total_slacks_(std::move(total_slacks)) {
5611 CHECK_EQ(spans_.size(), model_->vehicles());
5612 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5613 vehicle_demons_.resize(model_->vehicles());
5614 }
5615
5616 std::string DebugString() const override { return "PathSpansAndTotalSlacks"; }
5617
5618 void Post() override {
5619 const int num_nodes = model_->VehicleVars().size();
5620 const int num_transits = model_->Nexts().size();
5621 for (int node = 0; node < num_nodes; ++node) {
5622 auto* demon = MakeConstraintDemon1(
5623 model_->solver(), this, &PathSpansAndTotalSlacks::PropagateNode,
5624 "PathSpansAndTotalSlacks::PropagateNode", node);
5625 dimension_->CumulVar(node)->WhenRange(demon);
5626 model_->VehicleVar(node)->WhenBound(demon);
5627 if (node < num_transits) {
5628 dimension_->TransitVar(node)->WhenRange(demon);
5629 dimension_->FixedTransitVar(node)->WhenBound(demon);
5630 model_->NextVar(node)->WhenBound(demon);
5631 }
5632 }
5633 for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5634 if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5635 auto* demon = MakeDelayedConstraintDemon1(
5636 solver(), this, &PathSpansAndTotalSlacks::PropagateVehicle,
5637 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5638 vehicle_demons_[vehicle] = demon;
5639 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5640 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5641 if (dimension_->HasBreakConstraints()) {
5642 for (IntervalVar* b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5643 b->WhenAnything(demon);
5644 }
5645 }
5646 }
5647 }
5648
5649 // Call propagator on all vehicles.
5650 void InitialPropagate() override {
5651 for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5652 if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5653 PropagateVehicle(vehicle);
5654 }
5655 }
5656
5657 private:
5658 // Called when a path/dimension variables of the node changes,
5659 // this delays propagator calls until path variables (Next and VehicleVar)
5660 // are instantiated, which saves fruitless and multiple identical calls.
5661 void PropagateNode(int node) {
5662 if (!model_->VehicleVar(node)->Bound()) return;
5663 const int vehicle = model_->VehicleVar(node)->Min();
5664 if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return;
5665 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5666 }
5667
5668 // In order to make reasoning on span and total_slack of a vehicle uniform,
5669 // we rely on the fact that span == sum_fixed_transits + total_slack
5670 // to present both span and total_slack in terms of span and fixed transit.
5671 // This allows to use the same code whether there actually are variables
5672 // for span and total_slack or not.
5673 int64 SpanMin(int vehicle, int64 sum_fixed_transits) {
5674 DCHECK_GE(sum_fixed_transits, 0);
5675 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() : kint64max;
5676 const int64 total_slack_min =
5677 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() : kint64max;
5678 return std::min(span_min, CapAdd(total_slack_min, sum_fixed_transits));
5679 }
5680 int64 SpanMax(int vehicle, int64 sum_fixed_transits) {
5681 DCHECK_GE(sum_fixed_transits, 0);
5682 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() : kint64min;
5683 const int64 total_slack_max =
5684 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() : kint64min;
5685 return std::max(span_max, CapAdd(total_slack_max, sum_fixed_transits));
5686 }
5687 void SetSpanMin(int vehicle, int64 min, int64 sum_fixed_transits) {
5688 DCHECK_GE(sum_fixed_transits, 0);
5689 if (spans_[vehicle]) {
5690 spans_[vehicle]->SetMin(min);
5691 }
5692 if (total_slacks_[vehicle]) {
5693 total_slacks_[vehicle]->SetMin(CapSub(min, sum_fixed_transits));
5694 }
5695 }
5696 void SetSpanMax(int vehicle, int64 max, int64 sum_fixed_transits) {
5697 DCHECK_GE(sum_fixed_transits, 0);
5698 if (spans_[vehicle]) {
5699 spans_[vehicle]->SetMax(max);
5700 }
5701 if (total_slacks_[vehicle]) {
5702 total_slacks_[vehicle]->SetMax(CapSub(max, sum_fixed_transits));
5703 }
5704 }
5705 // Propagates span == sum_fixed_transits + total_slack.
5706 // This should be called at least once during PropagateVehicle().
5707 void SynchronizeSpanAndTotalSlack(int vehicle, int64 sum_fixed_transits) {
5708 DCHECK_GE(sum_fixed_transits, 0);
5709 IntVar* span = spans_[vehicle];
5710 IntVar* total_slack = total_slacks_[vehicle];
5711 if (!span || !total_slack) return;
5712 span->SetMin(CapAdd(total_slack->Min(), sum_fixed_transits));
5713 span->SetMax(CapAdd(total_slack->Max(), sum_fixed_transits));
5714 total_slack->SetMin(CapSub(span->Min(), sum_fixed_transits));
5715 total_slack->SetMax(CapSub(span->Max(), sum_fixed_transits));
5716 }
5717
5718 void PropagateVehicle(int vehicle) {
5719 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5720 const int start = model_->Start(vehicle);
5721 const int end = model_->End(vehicle);
5722 // Record path, if it is not fixed from start to end, stop here.
5723 // TRICKY: do not put end node yet, we look only at transits in the next
5724 // reasonings, we will append the end when we look at cumuls.
5725 {
5726 path_.clear();
5727 int curr_node = start;
5728 while (!model_->IsEnd(curr_node)) {
5729 const IntVar* next_var = model_->NextVar(curr_node);
5730 if (!next_var->Bound()) return;
5731 path_.push_back(curr_node);
5732 curr_node = next_var->Value();
5733 }
5734 }
5735 // Compute the sum of fixed transits. Fixed transit variables should all be
5736 // fixed, otherwise we wait to get called later when propagation does it.
5737 int64 sum_fixed_transits = 0;
5738 for (const int node : path_) {
5739 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5740 if (!fixed_transit_var->Bound()) return;
5741 sum_fixed_transits =
5742 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5743 }
5744
5745 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5746
5747 // The amount of break time that must occur during the route must be smaller
5748 // than span max - sum_fixed_transits. A break must occur on the route if it
5749 // must be after the route's start and before the route's end.
5750 // Propagate lower bound on span, then filter out values
5751 // that would force more breaks in route than possible.
5752 if (dimension_->HasBreakConstraints() &&
5753 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5754 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5755 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5756 // Compute and propagate lower bound.
5757 int64 min_break_duration = 0;
5758 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5759 if (!br->MustBePerformed()) continue;
5760 if (vehicle_start_max < br->EndMin() &&
5761 br->StartMax() < vehicle_end_min) {
5762 min_break_duration = CapAdd(min_break_duration, br->DurationMin());
5763 }
5764 }
5765 SetSpanMin(vehicle, CapAdd(min_break_duration, sum_fixed_transits),
5766 sum_fixed_transits);
5767 // If a break that is not inside the route may violate slack_max,
5768 // we can propagate in some cases: when the break must be before or
5769 // must be after the route.
5770 // In the other cases, we cannot deduce a better bound on a CumulVar or
5771 // on a break, so we do nothing.
5772 const int64 slack_max =
5773 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5774 const int64 max_additional_slack = CapSub(slack_max, min_break_duration);
5775 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5776 if (!br->MustBePerformed()) continue;
5777 // Break must be before end, detect whether it must be before start.
5778 if (vehicle_start_max >= br->EndMin() &&
5779 br->StartMax() < vehicle_end_min) {
5780 if (br->DurationMin() > max_additional_slack) {
5781 // Having the break inside would violate max_additional_slack..
5782 // Thus, it must be outside the route, in this case, before.
5783 br->SetEndMax(vehicle_start_max);
5784 dimension_->CumulVar(start)->SetMin(br->EndMin());
5785 }
5786 }
5787 // Break must be after start, detect whether it must be after end.
5788 // Same reasoning, in the case where the break is after.
5789 if (vehicle_start_max < br->EndMin() &&
5790 br->StartMax() >= vehicle_end_min) {
5791 if (br->DurationMin() > max_additional_slack) {
5792 br->SetStartMin(vehicle_end_min);
5793 dimension_->CumulVar(end)->SetMax(br->StartMax());
5794 }
5795 }
5796 }
5797 }
5798
5799 // Propagate span == cumul(end) - cumul(start).
5800 {
5801 IntVar* start_cumul = dimension_->CumulVar(start);
5802 IntVar* end_cumul = dimension_->CumulVar(end);
5803 const int64 start_min = start_cumul->Min();
5804 const int64 start_max = start_cumul->Max();
5805 const int64 end_min = end_cumul->Min();
5806 const int64 end_max = end_cumul->Max();
5807 // Propagate from cumuls to span.
5808 const int64 span_lb = CapSub(end_min, start_max);
5809 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5810 const int64 span_ub = CapSub(end_max, start_min);
5811 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5812 // Propagate from span to cumuls.
5813 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5814 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5815 const int64 slack_from_lb = CapSub(span_max, span_lb);
5816 const int64 slack_from_ub = CapSub(span_ub, span_min);
5817 // start >= start_max - (span_max - span_lb).
5818 start_cumul->SetMin(CapSub(start_max, slack_from_lb));
5819 // end <= end_min + (span_max - span_lb).
5820 end_cumul->SetMax(CapAdd(end_min, slack_from_lb));
5821 // // start <= start_min + (span_ub - span_min)
5822 start_cumul->SetMax(CapAdd(start_min, slack_from_ub));
5823 // // end >= end_max - (span_ub - span_min)
5824 end_cumul->SetMin(CapSub(end_max, slack_from_ub));
5825 }
5826
5827 // Propagate sum transits == span.
5828 {
5829 // Propagate from transits to span.
5830 int64 span_lb = 0;
5831 int64 span_ub = 0;
5832 for (const int node : path_) {
5833 span_lb = CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5834 span_ub = CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5835 }
5836 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5837 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5838 // Propagate from span to transits.
5839 // transit[i] <= transit_i_min + (span_max - span_lb)
5840 // transit[i] >= transit_i_max - (span_ub - span_min)
5841 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5842 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5843 const int64 slack_from_lb = CapSub(span_max, span_lb);
5844 const int64 slack_from_ub =
5845 span_ub < kint64max ? CapSub(span_ub, span_min) : kint64max;
5846 for (const int node : path_) {
5847 IntVar* transit_var = dimension_->TransitVar(node);
5848 const int64 transit_i_min = transit_var->Min();
5849 const int64 transit_i_max = transit_var->Max();
5850 // TRICKY: the first propagation might change transit_var->Max(),
5851 // but we must use the same value of transit_i_max in the computation
5852 // of transit[i]'s lower bound that was used for span_ub.
5853 transit_var->SetMax(CapAdd(transit_i_min, slack_from_lb));
5854 transit_var->SetMin(CapSub(transit_i_max, slack_from_ub));
5855 }
5856 }
5857
5858 // TRICKY: add end node now, we will look at cumuls.
5859 path_.push_back(end);
5860
5861 // A stronger bound: from start min of the route, go to node i+1 with time
5862 // max(cumul[i] + fixed_transit, cumul[i+1].Min()).
5863 // Record arrival time (should be the same as end cumul min).
5864 // Then do the reverse route, going to time
5865 // min(cumul[i+1] - fixed_transit, cumul[i].Max())
5866 // Record final time as departure time.
5867 // Then arrival time - departure time is a valid lower bound of span.
5868 // First reasoning: start - end - start
5869 {
5870 int64 arrival_time = dimension_->CumulVar(start)->Min();
5871 for (int i = 1; i < path_.size(); ++i) {
5872 arrival_time =
5873 std::max(CapAdd(arrival_time,
5874 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5875 dimension_->CumulVar(path_[i])->Min());
5876 }
5877 int64 departure_time = arrival_time;
5878 for (int i = path_.size() - 2; i >= 0; --i) {
5879 departure_time =
5880 std::min(CapSub(departure_time,
5881 dimension_->FixedTransitVar(path_[i])->Min()),
5882 dimension_->CumulVar(path_[i])->Max());
5883 }
5884 const int64 span_lb = CapSub(arrival_time, departure_time);
5885 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5886 const int64 maximum_deviation =
5887 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5888 const int64 start_lb = CapSub(departure_time, maximum_deviation);
5889 dimension_->CumulVar(start)->SetMin(start_lb);
5890 }
5891 // Second reasoning: end - start - end
5892 {
5893 int64 departure_time = dimension_->CumulVar(end)->Max();
5894 for (int i = path_.size() - 2; i >= 0; --i) {
5895 const int curr_node = path_[i];
5896 departure_time =
5897 std::min(CapSub(departure_time,
5898 dimension_->FixedTransitVar(curr_node)->Min()),
5899 dimension_->CumulVar(curr_node)->Max());
5900 }
5901 int arrival_time = departure_time;
5902 for (int i = 1; i < path_.size(); ++i) {
5903 arrival_time =
5904 std::max(CapAdd(arrival_time,
5905 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5906 dimension_->CumulVar(path_[i])->Min());
5907 }
5908 const int64 span_lb = CapSub(arrival_time, departure_time);
5909 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5910 const int64 maximum_deviation =
5911 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5912 dimension_->CumulVar(end)->SetMax(
5913 CapAdd(arrival_time, maximum_deviation));
5914 }
5915 }
5916
5917 const RoutingModel* const model_;
5918 const RoutingDimension* const dimension_;
5919 std::vector<IntVar*> spans_;
5920 std::vector<IntVar*> total_slacks_;
5921 std::vector<int> path_;
5922 std::vector<Demon*> vehicle_demons_;
5923};
5924
5925} // namespace
5926
5928 const RoutingDimension* dimension, std::vector<IntVar*> spans,
5929 std::vector<IntVar*> total_slacks) {
5930 CHECK_EQ(vehicles_, spans.size());
5931 CHECK_EQ(vehicles_, total_slacks.size());
5932 return solver()->RevAlloc(
5933 new PathSpansAndTotalSlacks(this, dimension, spans, total_slacks));
5934}
5935
5936const char RoutingModelVisitor::kLightElement[] = "LightElement";
5937const char RoutingModelVisitor::kLightElement2[] = "LightElement2";
5938const char RoutingModelVisitor::kRemoveValues[] = "RemoveValues";
5939
5940RoutingDimension::RoutingDimension(RoutingModel* model,
5941 std::vector<int64> vehicle_capacities,
5942 const std::string& name,
5943 const RoutingDimension* base_dimension)
5944 : vehicle_capacities_(std::move(vehicle_capacities)),
5945 base_dimension_(base_dimension),
5946 global_span_cost_coefficient_(0),
5947 model_(model),
5948 name_(name),
5949 global_optimizer_offset_(0) {
5950 CHECK(model != nullptr);
5951 vehicle_span_upper_bounds_.assign(model->vehicles(), kint64max);
5952 vehicle_span_cost_coefficients_.assign(model->vehicles(), 0);
5953}
5954
5955RoutingDimension::RoutingDimension(RoutingModel* model,
5956 std::vector<int64> vehicle_capacities,
5957 const std::string& name, SelfBased)
5958 : RoutingDimension(model, std::move(vehicle_capacities), name, this) {}
5959
5961 cumul_var_piecewise_linear_cost_.clear();
5962}
5963
5964void RoutingDimension::Initialize(
5965 const std::vector<int>& transit_evaluators,
5966 const std::vector<int>& state_dependent_transit_evaluators,
5967 int64 slack_max) {
5968 InitializeCumuls();
5969 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5970 slack_max);
5971}
5972
5973namespace {
5974// Very light version of the RangeLessOrEqual constraint (see ./range_cst.cc).
5975// Only performs initial propagation and then checks the compatibility of the
5976// variable domains without domain pruning.
5977// This is useful when to avoid ping-pong effects with costly constraints
5978// such as the PathCumul constraint.
5979// This constraint has not been added to the cp library (in range_cst.cc) given
5980// it only does checking and no propagation (except the initial propagation)
5981// and is only fit for local search, in particular in the context of vehicle
5982// routing.
5983class LightRangeLessOrEqual : public Constraint {
5984 public:
5985 LightRangeLessOrEqual(Solver* const s, IntExpr* const l, IntExpr* const r);
5986 ~LightRangeLessOrEqual() override {}
5987 void Post() override;
5988 void InitialPropagate() override;
5989 std::string DebugString() const override;
5990 IntVar* Var() override {
5991 return solver()->MakeIsLessOrEqualVar(left_, right_);
5992 }
5993 // TODO(user): introduce a kLightLessOrEqual tag.
5994 void Accept(ModelVisitor* const visitor) const override {
5995 visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual, this);
5996 visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
5997 visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
5998 right_);
5999 visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual, this);
6000 }
6001
6002 private:
6003 void CheckRange();
6004
6005 IntExpr* const left_;
6006 IntExpr* const right_;
6007 Demon* demon_;
6008};
6009
6010LightRangeLessOrEqual::LightRangeLessOrEqual(Solver* const s, IntExpr* const l,
6011 IntExpr* const r)
6012 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6013
6014void LightRangeLessOrEqual::Post() {
6015 demon_ = MakeConstraintDemon0(
6016 solver(), this, &LightRangeLessOrEqual::CheckRange, "CheckRange");
6017 left_->WhenRange(demon_);
6018 right_->WhenRange(demon_);
6019}
6020
6021void LightRangeLessOrEqual::InitialPropagate() {
6022 left_->SetMax(right_->Max());
6023 right_->SetMin(left_->Min());
6024 if (left_->Max() <= right_->Min()) {
6025 demon_->inhibit(solver());
6026 }
6027}
6028
6029void LightRangeLessOrEqual::CheckRange() {
6030 if (left_->Min() > right_->Max()) {
6031 solver()->Fail();
6032 }
6033 if (left_->Max() <= right_->Min()) {
6034 demon_->inhibit(solver());
6035 }
6036}
6037
6038std::string LightRangeLessOrEqual::DebugString() const {
6039 return left_->DebugString() + " < " + right_->DebugString();
6040}
6041
6042} // namespace
6043
6044void RoutingDimension::InitializeCumuls() {
6045 Solver* const solver = model_->solver();
6046 const int size = model_->Size() + model_->vehicles();
6047 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6048 vehicle_capacities_.end());
6049 const int64 min_capacity = *capacity_range.first;
6050 CHECK_GE(min_capacity, 0);
6051 const int64 max_capacity = *capacity_range.second;
6052 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6053 // Refine the min/max for vehicle start/ends based on vehicle capacities.
6054 for (int v = 0; v < model_->vehicles(); v++) {
6055 const int64 vehicle_capacity = vehicle_capacities_[v];
6056 cumuls_[model_->Start(v)]->SetMax(vehicle_capacity);
6057 cumuls_[model_->End(v)]->SetMax(vehicle_capacity);
6058 }
6059
6060 forbidden_intervals_.resize(size);
6061 capacity_vars_.clear();
6062 if (min_capacity != max_capacity) {
6063 solver->MakeIntVarArray(size, 0, kint64max, &capacity_vars_);
6064 for (int i = 0; i < size; ++i) {
6065 IntVar* const capacity_var = capacity_vars_[i];
6066 if (i < model_->Size()) {
6067 IntVar* const capacity_active = solver->MakeBoolVar();
6068 solver->AddConstraint(
6069 solver->MakeLessOrEqual(model_->ActiveVar(i), capacity_active));
6070 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6071 cumuls_[i], capacity_var, capacity_active));
6072 } else {
6073 solver->AddConstraint(
6074 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6075 }
6076 }
6077 }
6078}
6079
6080namespace {
6081template <int64 value>
6082int64 IthElementOrValue(const std::vector<int64>& v, int64 index) {
6083 return index >= 0 ? v[index] : value;
6084}
6085
6086void ComputeTransitClasses(const std::vector<int>& evaluator_indices,
6087 std::vector<int>* class_evaluators,
6088 std::vector<int64>* vehicle_to_class) {
6089 CHECK(class_evaluators != nullptr);
6090 CHECK(vehicle_to_class != nullptr);
6091 class_evaluators->clear();
6092 vehicle_to_class->resize(evaluator_indices.size(), -1);
6093 absl::flat_hash_map<int, int64> evaluator_to_class;
6094 for (int i = 0; i < evaluator_indices.size(); ++i) {
6095 const int evaluator_index = evaluator_indices[i];
6096 int evaluator_class = -1;
6097 if (!gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6098 evaluator_class = class_evaluators->size();
6099 evaluator_to_class[evaluator_index] = evaluator_class;
6100 class_evaluators->push_back(evaluator_index);
6101 }
6102 (*vehicle_to_class)[i] = evaluator_class;
6103 }
6104}
6105} // namespace
6106
6107void RoutingDimension::InitializeTransitVariables(int64 slack_max) {
6108 CHECK(!class_evaluators_.empty());
6109 CHECK(base_dimension_ == nullptr ||
6110 !state_dependent_class_evaluators_.empty());
6111
6112 Solver* const solver = model_->solver();
6113 const int size = model_->Size();
6114 const Solver::IndexEvaluator1 dependent_vehicle_class_function =
6115 [this](int index) {
6116 return (0 <= index && index < state_dependent_vehicle_to_class_.size())
6117 ? state_dependent_vehicle_to_class_[index]
6118 : state_dependent_class_evaluators_.size();
6119 };
6120 const std::string slack_name = name_ + " slack";
6121 const std::string transit_name = name_ + " fixed transit";
6122
6123 for (int64 i = 0; i < size; ++i) {
6124 fixed_transits_[i] =
6125 solver->MakeIntVar(kint64min, kint64max, absl::StrCat(transit_name, i));
6126 // Setting dependent_transits_[i].
6127 if (base_dimension_ != nullptr) {
6128 if (state_dependent_class_evaluators_.size() == 1) {
6129 std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6130 for (int64 j = 0; j < cumuls_.size(); ++j) {
6131 transition_variables[j] =
6132 MakeRangeMakeElementExpr(
6133 model_
6134 ->StateDependentTransitCallback(
6135 state_dependent_class_evaluators_[0])(i, j)
6136 .transit,
6137 base_dimension_->CumulVar(i), solver)
6138 ->Var();
6139 }
6140 dependent_transits_[i] =
6141 solver->MakeElement(transition_variables, model_->NextVar(i))
6142 ->Var();
6143 } else {
6144 IntVar* const vehicle_class_var =
6145 solver
6146 ->MakeElement(dependent_vehicle_class_function,
6147 model_->VehicleVar(i))
6148 ->Var();
6149 std::vector<IntVar*> transit_for_vehicle;
6150 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6151 1);
6152 for (int evaluator : state_dependent_class_evaluators_) {
6153 std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6154 for (int64 j = 0; j < cumuls_.size(); ++j) {
6155 transition_variables[j] =
6156 MakeRangeMakeElementExpr(
6157 model_->StateDependentTransitCallback(evaluator)(i, j)
6158 .transit,
6159 base_dimension_->CumulVar(i), solver)
6160 ->Var();
6161 }
6162 transit_for_vehicle.push_back(
6163 solver->MakeElement(transition_variables, model_->NextVar(i))
6164 ->Var());
6165 }
6166 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6167 dependent_transits_[i] =
6168 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6169 }
6170 } else {
6171 dependent_transits_[i] = solver->MakeIntConst(0);
6172 }
6173
6174 // Summing fixed transits, dependent transits and the slack.
6175 IntExpr* transit_expr = fixed_transits_[i];
6176 if (dependent_transits_[i]->Min() != 0 ||
6177 dependent_transits_[i]->Max() != 0) {
6178 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6179 }
6180
6181 if (slack_max == 0) {
6182 slacks_[i] = solver->MakeIntConst(0);
6183 } else {
6184 slacks_[i] =
6185 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6186 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6187 }
6188 transits_[i] = transit_expr->Var();
6189 }
6190}
6191
6192void RoutingDimension::InitializeTransits(
6193 const std::vector<int>& transit_evaluators,
6194 const std::vector<int>& state_dependent_transit_evaluators,
6195 int64 slack_max) {
6196 CHECK_EQ(model_->vehicles(), transit_evaluators.size());
6197 CHECK(base_dimension_ == nullptr ||
6198 model_->vehicles() == state_dependent_transit_evaluators.size());
6199 const int size = model_->Size();
6200 transits_.resize(size, nullptr);
6201 fixed_transits_.resize(size, nullptr);
6202 slacks_.resize(size, nullptr);
6203 dependent_transits_.resize(size, nullptr);
6204 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6205 &vehicle_to_class_);
6206 if (base_dimension_ != nullptr) {
6207 ComputeTransitClasses(state_dependent_transit_evaluators,
6208 &state_dependent_class_evaluators_,
6209 &state_dependent_vehicle_to_class_);
6210 }
6211
6212 InitializeTransitVariables(slack_max);
6213}
6214
6215void FillPathEvaluation(const std::vector<int64>& path,
6216 const RoutingModel::TransitCallback2& evaluator,
6217 std::vector<int64>* values) {
6218 const int num_nodes = path.size();
6219 values->resize(num_nodes - 1);
6220 for (int i = 0; i < num_nodes - 1; ++i) {
6221 (*values)[i] = evaluator(path[i], path[i + 1]);
6222 }
6223}
6224
6226 : model_(model), occurrences_of_type_(model.GetNumberOfVisitTypes()) {}
6227
6229 int vehicle, const std::function<int64(int64)>& next_accessor) {
6230 if (!HasRegulationsToCheck()) {
6231 return true;
6232 }
6233
6234 InitializeCheck(vehicle, next_accessor);
6235
6236 for (int pos = 0; pos < current_route_visits_.size(); pos++) {
6237 const int64 current_visit = current_route_visits_[pos];
6238 const int type = model_.GetVisitType(current_visit);
6239 if (type < 0) {
6240 continue;
6241 }
6242 const VisitTypePolicy policy = model_.GetVisitTypePolicy(current_visit);
6243
6244 DCHECK_LT(type, occurrences_of_type_.size());
6245 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6246 int& num_type_removed =
6247 occurrences_of_type_[type].num_type_removed_from_vehicle;
6248 DCHECK_LE(num_type_removed, num_type_added);
6250 num_type_removed == num_type_added) {
6251 // The type is not actually being removed as all added types have already
6252 // been removed.
6253 continue;
6254 }
6255
6256 if (!CheckTypeRegulations(type, policy, pos)) {
6257 return false;
6258 }
6259 // Update count of type based on the visit policy.
6260 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6261 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6262 num_type_added++;
6263 }
6264 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6265 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6266 num_type_removed++;
6267 }
6268 }
6269 return FinalizeCheck();
6270}
6271
6273 int vehicle, const std::function<int64(int64)>& next_accessor) {
6274 // Accumulates the count of types before the current node.
6275 // {0, 0, -1} does not compile on or-tools.
6276 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6278
6279 // TODO(user): Optimize the filter to avoid scanning the route an extra
6280 // time when there are no TYPE_ON_VEHICLE_UP_TO_VISIT policies on the route,
6281 // by passing a boolean to CheckVehicle() passed to InitializeCheck().
6282 current_route_visits_.clear();
6283 for (int64 current = model_.Start(vehicle); !model_.IsEnd(current);
6284 current = next_accessor(current)) {
6285 const int type = model_.GetVisitType(current);
6286 if (type >= 0 && model_.GetVisitTypePolicy(current) ==
6287 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6288 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6289 current_route_visits_.size();
6290 }
6291 current_route_visits_.push_back(current);
6292 }
6293
6295}
6296
6298 const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6299 return occurrences.num_type_added_to_vehicle > 0 ||
6301}
6302
6304 const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6305 return occurrences.num_type_removed_from_vehicle <
6306 occurrences.num_type_added_to_vehicle ||
6308}
6309
6311 const RoutingModel& model, bool check_hard_incompatibilities)
6313 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6314
6315bool TypeIncompatibilityChecker::HasRegulationsToCheck() const {
6317 (check_hard_incompatibilities_ &&
6319}
6320
6321// TODO(user): Remove the check_hard_incompatibilities_ boolean and always
6322// check both incompatibilities to simplify the code?
6323// TODO(user): Improve algorithm by only checking a given type if necessary?
6324// - For temporal incompatibilities, only check if NonDeliveredType(count) == 1.
6325// - For hard incompatibilities, only if NonDeliveryType(type) == 1.
6326bool TypeIncompatibilityChecker::CheckTypeRegulations(int type,
6327 VisitTypePolicy policy,
6328 int pos) {
6329 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6330 // NOTE: We don't need to check incompatibilities when the type is being
6331 // removed from the route.
6332 return true;
6333 }
6334 for (int incompatible_type :
6336 if (TypeCurrentlyOnRoute(incompatible_type, pos)) {
6337 return false;
6338 }
6339 }
6340 if (check_hard_incompatibilities_) {
6341 for (int incompatible_type :
6343 if (TypeOccursOnRoute(incompatible_type)) {
6344 return false;
6345 }
6346 }
6347 }
6348 return true;
6349}
6350
6351bool TypeRequirementChecker::HasRegulationsToCheck() const {
6354}
6355
6356bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6357 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6358 int pos) {
6359 for (const absl::flat_hash_set<int>& requirement_alternatives :
6360 required_type_alternatives) {
6361 bool has_one_of_alternatives = false;
6362 for (int type_alternative : requirement_alternatives) {
6363 if (TypeCurrentlyOnRoute(type_alternative, pos)) {
6364 has_one_of_alternatives = true;
6365 break;
6366 }
6367 }
6368 if (!has_one_of_alternatives) {
6369 return false;
6370 }
6371 }
6372 return true;
6373}
6374
6375bool TypeRequirementChecker::CheckTypeRegulations(int type,
6376 VisitTypePolicy policy,
6377 int pos) {
6380 if (!CheckRequiredTypesCurrentlyOnRoute(
6382 return false;
6383 }
6384 }
6386 if (!CheckRequiredTypesCurrentlyOnRoute(
6388 return false;
6389 }
6390 }
6393 types_with_same_vehicle_requirements_on_route_.insert(type);
6394 }
6395 return true;
6396}
6397
6398bool TypeRequirementChecker::FinalizeCheck() const {
6399 for (int type : types_with_same_vehicle_requirements_on_route_) {
6400 for (const absl::flat_hash_set<int>& requirement_alternatives :
6402 bool has_one_of_alternatives = false;
6403 for (const int type_alternative : requirement_alternatives) {
6404 if (TypeOccursOnRoute(type_alternative)) {
6405 has_one_of_alternatives = true;
6406 break;
6407 }
6408 }
6409 if (!has_one_of_alternatives) {
6410 return false;
6411 }
6412 }
6413 }
6414 return true;
6415}
6416
6418 : Constraint(model.solver()),
6419 model_(model),
6420 incompatibility_checker_(model, /*check_hard_incompatibilities*/ true),
6421 requirement_checker_(model),
6422 vehicle_demons_(model.vehicles()) {}
6423
6424void TypeRegulationsConstraint::PropagateNodeRegulations(int node) {
6425 DCHECK_LT(node, model_.Size());
6426 if (!model_.VehicleVar(node)->Bound() || !model_.NextVar(node)->Bound()) {
6427 // Vehicle var or Next var not bound.
6428 return;
6429 }
6430 const int vehicle = model_.VehicleVar(node)->Min();
6431 if (vehicle < 0) return;
6432 DCHECK(vehicle_demons_[vehicle] != nullptr);
6433 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6434}
6435
6436void TypeRegulationsConstraint::CheckRegulationsOnVehicle(int vehicle) {
6437 const auto next_accessor = [this, vehicle](int64 node) {
6438 if (model_.NextVar(node)->Bound()) {
6439 return model_.NextVar(node)->Value();
6440 }
6441 // Node not bound, skip to the end of the vehicle.
6442 return model_.End(vehicle);
6443 };
6444 if (!incompatibility_checker_.CheckVehicle(vehicle, next_accessor) ||
6445 !requirement_checker_.CheckVehicle(vehicle, next_accessor)) {
6446 model_.solver()->Fail();
6447 }
6448}
6449
6451 for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6452 vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1(
6453 solver(), this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6454 "CheckRegulationsOnVehicle", vehicle);
6455 }
6456 for (int node = 0; node < model_.Size(); node++) {
6457 Demon* node_demon = MakeConstraintDemon1(
6458 solver(), this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6459 "PropagateNodeRegulations", node);
6460 model_.NextVar(node)->WhenBound(node_demon);
6461 model_.VehicleVar(node)->WhenBound(node_demon);
6462 }
6463}
6464
6466 for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6467 CheckRegulationsOnVehicle(vehicle);
6468 }
6469}
6470
6471void RoutingDimension::CloseModel(bool use_light_propagation) {
6472 Solver* const solver = model_->solver();
6473 const auto capacity_lambda = [this](int64 vehicle) {
6474 return vehicle >= 0 ? vehicle_capacities_[vehicle] : kint64max;
6475 };
6476 for (int i = 0; i < capacity_vars_.size(); ++i) {
6477 IntVar* const vehicle_var = model_->VehicleVar(i);
6478 IntVar* const capacity_var = capacity_vars_[i];
6479 if (use_light_propagation) {
6480 solver->AddConstraint(MakeLightElement(
6481 solver, capacity_var, vehicle_var, capacity_lambda,
6482 [this]() { return model_->enable_deep_serialization_; }));
6483 } else {
6484 solver->AddConstraint(solver->MakeEquality(
6485 capacity_var,
6486 solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6487 }
6488 }
6489 const Solver::IndexEvaluator1 vehicle_class_function = [this](int index) {
6490 return IthElementOrValue<-1>(vehicle_to_class_, index);
6491 };
6492 for (int i = 0; i < fixed_transits_.size(); ++i) {
6493 IntVar* const next_var = model_->NextVar(i);
6494 IntVar* const fixed_transit = fixed_transits_[i];
6495 const auto transit_vehicle_evaluator = [this, i](int64 to,
6496 int64 eval_index) {
6497 return eval_index >= 0 ? transit_evaluator(eval_index)(i, to) : 0;
6498 };
6499 if (use_light_propagation) {
6500 if (class_evaluators_.size() == 1) {
6501 const int class_evaluator_index = class_evaluators_[0];
6502 const auto& unary_callback =
6503 model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6504 if (unary_callback == nullptr) {
6505 solver->AddConstraint(MakeLightElement(
6506 solver, fixed_transit, next_var,
6507 [this, i](int64 to) {
6508 return model_->TransitCallback(class_evaluators_[0])(i, to);
6509 },
6510 [this]() { return model_->enable_deep_serialization_; }));
6511 } else {
6512 fixed_transit->SetValue(unary_callback(i));
6513 }
6514 } else {
6515 solver->AddConstraint(MakeLightElement2(
6516 solver, fixed_transit, next_var, model_->VehicleVar(i),
6517 transit_vehicle_evaluator,
6518 [this]() { return model_->enable_deep_serialization_; }));
6519 }
6520 } else {
6521 if (class_evaluators_.size() == 1) {
6522 const int class_evaluator_index = class_evaluators_[0];
6523 const auto& unary_callback =
6524 model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6525 if (unary_callback == nullptr) {
6526 solver->AddConstraint(solver->MakeEquality(
6527 fixed_transit, solver
6528 ->MakeElement(
6529 [this, i](int64 to) {
6530 return model_->TransitCallback(
6531 class_evaluators_[0])(i, to);
6532 },
6533 model_->NextVar(i))
6534 ->Var()));
6535 } else {
6536 fixed_transit->SetValue(unary_callback(i));
6537 }
6538 } else {
6539 IntVar* const vehicle_class_var =
6540 solver->MakeElement(vehicle_class_function, model_->VehicleVar(i))
6541 ->Var();
6542 solver->AddConstraint(solver->MakeEquality(
6543 fixed_transit, solver
6544 ->MakeElement(transit_vehicle_evaluator,
6545 next_var, vehicle_class_var)
6546 ->Var()));
6547 }
6548 }
6549 }
6550 if (HasBreakConstraints()) {
6551 GlobalVehicleBreaksConstraint* constraint =
6552 model()->solver()->RevAlloc(new GlobalVehicleBreaksConstraint(this));
6553 solver->AddConstraint(constraint);
6554 }
6555}
6556
6558 int64 vehicle) const {
6559 DCHECK(transit_evaluator(vehicle) != nullptr);
6560 return transit_evaluator(vehicle)(from_index, to_index);
6561}
6562
6564 int64 index, int64 min_value, int64 max_value) const {
6566 const SortedDisjointIntervalList& forbidden = forbidden_intervals_[index];
6567 IntVar* const cumul_var = cumuls_[index];
6568 const int64 min = std::max(min_value, cumul_var->Min());
6569 const int64 max = std::min(max_value, cumul_var->Max());
6570 int64 next_start = min;
6573 interval != forbidden.end(); ++interval) {
6574 if (next_start > max) break;
6575 if (next_start < interval->start) {
6576 allowed.InsertInterval(next_start, CapSub(interval->start, 1));
6577 }
6578 next_start = CapAdd(interval->end, 1);
6579 }
6580 if (next_start <= max) {
6581 allowed.InsertInterval(next_start, max);
6582 }
6583 return allowed;
6584}
6585
6587 int vehicle) {
6588 CHECK_GE(vehicle, 0);
6589 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6590 CHECK_GE(upper_bound, 0);
6591 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6592}
6593
6595 int vehicle) {
6596 CHECK_GE(vehicle, 0);
6597 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6599 vehicle_span_cost_coefficients_[vehicle] = coefficient;
6600}
6601
6604 vehicle_span_cost_coefficients_.assign(model_->vehicles(), coefficient);
6605}
6606
6609 global_span_cost_coefficient_ = coefficient;
6610}
6611
6614 if (!cost.IsNonDecreasing()) {
6615 LOG(WARNING) << "Only non-decreasing cost functions are supported.";
6616 return;
6617 }
6618 if (cost.Value(0) < 0) {
6619 LOG(WARNING) << "Only positive cost functions are supported.";
6620 return;
6621 }
6622 if (index >= cumul_var_piecewise_linear_cost_.size()) {
6623 cumul_var_piecewise_linear_cost_.resize(index + 1);
6624 }
6625 PiecewiseLinearCost& piecewise_linear_cost =
6626 cumul_var_piecewise_linear_cost_[index];
6627 piecewise_linear_cost.var = cumuls_[index];
6628 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(cost);
6629}
6630
6632 return (index < cumul_var_piecewise_linear_cost_.size() &&
6633 cumul_var_piecewise_linear_cost_[index].var != nullptr);
6634}
6635
6637 int64 index) const {
6638 if (index < cumul_var_piecewise_linear_cost_.size() &&
6639 cumul_var_piecewise_linear_cost_[index].var != nullptr) {
6640 return cumul_var_piecewise_linear_cost_[index].cost.get();
6641 }
6642 return nullptr;
6643}
6644
6645namespace {
6646IntVar* BuildVarFromExprAndIndexActiveState(const RoutingModel* model,
6647 IntExpr* expr, int index) {
6648 Solver* const solver = model->solver();
6649 if (model->IsStart(index) || model->IsEnd(index)) {
6650 const int vehicle = model->VehicleIndex(index);
6651 DCHECK_GE(vehicle, 0);
6652 return solver->MakeProd(expr, model->VehicleCostsConsideredVar(vehicle))
6653 ->Var();
6654 }
6655 return solver->MakeProd(expr, model->ActiveVar(index))->Var();
6656}
6657} // namespace
6658
6659void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6660 std::vector<IntVar*>* cost_elements) const {
6661 CHECK(cost_elements != nullptr);
6662 Solver* const solver = model_->solver();
6663 for (int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6664 const PiecewiseLinearCost& piecewise_linear_cost =
6665 cumul_var_piecewise_linear_cost_[i];
6666 if (piecewise_linear_cost.var != nullptr) {
6667 IntExpr* const expr = solver->MakePiecewiseLinearExpr(
6668 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6669 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6670 cost_elements->push_back(cost_var);
6671 // TODO(user): Check if it wouldn't be better to minimize
6672 // piecewise_linear_cost.var here.
6673 model_->AddWeightedVariableMinimizedByFinalizer(cost_var, 0);
6674 }
6675 }
6676}
6677
6680 if (index >= cumul_var_soft_upper_bound_.size()) {
6681 cumul_var_soft_upper_bound_.resize(index + 1, {nullptr, 0, 0});
6682 }
6683 cumul_var_soft_upper_bound_[index] = {cumuls_[index], upper_bound,
6684 coefficient};
6685}
6686
6688 return (index < cumul_var_soft_upper_bound_.size() &&
6689 cumul_var_soft_upper_bound_[index].var != nullptr);
6690}
6691
6693 if (index < cumul_var_soft_upper_bound_.size() &&
6694 cumul_var_soft_upper_bound_[index].var != nullptr) {
6695 return cumul_var_soft_upper_bound_[index].bound;
6696 }
6697 return cumuls_[index]->Max();
6698}
6699
6701 int64 index) const {
6702 if (index < cumul_var_soft_upper_bound_.size() &&
6703 cumul_var_soft_upper_bound_[index].var != nullptr) {
6704 return cumul_var_soft_upper_bound_[index].coefficient;
6705 }
6706 return 0;
6707}
6708
6709void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6710 std::vector<IntVar*>* cost_elements) const {
6711 CHECK(cost_elements != nullptr);
6712 Solver* const solver = model_->solver();
6713 for (int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6714 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6715 if (soft_bound.var != nullptr) {
6716 IntExpr* const expr = solver->MakeSemiContinuousExpr(
6717 solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6718 soft_bound.coefficient);
6719 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6720 cost_elements->push_back(cost_var);
6721 // NOTE: We minimize the cost here instead of minimizing the cumul
6722 // variable, to avoid setting the cumul to earlier than necessary.
6724 soft_bound.coefficient);
6725 }
6726 }
6727}
6728
6731 if (index >= cumul_var_soft_lower_bound_.size()) {
6732 cumul_var_soft_lower_bound_.resize(index + 1, {nullptr, 0, 0});
6733 }
6734 cumul_var_soft_lower_bound_[index] = {cumuls_[index], lower_bound,
6735 coefficient};
6736}
6737
6739 return (index < cumul_var_soft_lower_bound_.size() &&
6740 cumul_var_soft_lower_bound_[index].var != nullptr);
6741}
6742
6744 if (index < cumul_var_soft_lower_bound_.size() &&
6745 cumul_var_soft_lower_bound_[index].var != nullptr) {
6746 return cumul_var_soft_lower_bound_[index].bound;
6747 }
6748 return cumuls_[index]->Min();
6749}
6750
6752 int64 index) const {
6753 if (index < cumul_var_soft_lower_bound_.size() &&
6754 cumul_var_soft_lower_bound_[index].var != nullptr) {
6755 return cumul_var_soft_lower_bound_[index].coefficient;
6756 }
6757 return 0;
6758}
6759
6760void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6761 std::vector<IntVar*>* cost_elements) const {
6762 CHECK(cost_elements != nullptr);
6763 Solver* const solver = model_->solver();
6764 for (int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6765 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6766 if (soft_bound.var != nullptr) {
6767 IntExpr* const expr = solver->MakeSemiContinuousExpr(
6768 solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6769 soft_bound.coefficient);
6770 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6771 cost_elements->push_back(cost_var);
6772 // NOTE: We minimize the cost here instead of maximizing the cumul
6773 // variable, to avoid setting the cumul to later than necessary.
6775 soft_bound.coefficient);
6776 }
6777 }
6778}
6779
6780void RoutingDimension::SetupGlobalSpanCost(
6781 std::vector<IntVar*>* cost_elements) const {
6782 CHECK(cost_elements != nullptr);
6783 Solver* const solver = model_->solver();
6784 if (global_span_cost_coefficient_ != 0) {
6785 std::vector<IntVar*> end_cumuls;
6786 for (int i = 0; i < model_->vehicles(); ++i) {
6787 end_cumuls.push_back(solver
6788 ->MakeProd(model_->vehicle_costs_considered_[i],
6789 cumuls_[model_->End(i)])
6790 ->Var());
6791 }
6792 IntVar* const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6794 max_end_cumul, global_span_cost_coefficient_);
6795 std::vector<IntVar*> start_cumuls;
6796 for (int i = 0; i < model_->vehicles(); ++i) {
6797 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0, kint64max);
6798 solver->AddConstraint(solver->MakeIfThenElseCt(
6799 model_->vehicle_costs_considered_[i], cumuls_[model_->Start(i)],
6800 max_end_cumul, global_span_cost_start_cumul));
6801 start_cumuls.push_back(global_span_cost_start_cumul);
6802 }
6803 IntVar* const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6805 min_start_cumul, global_span_cost_coefficient_);
6806 // If there is a single vehicle, model the cost as the sum of its transits
6807 // to avoid slow (infinite) propagation loops.
6808 // TODO(user): Avoid slow propagation in the path constraints.
6809 if (model_->vehicles() == 1) {
6810 for (int var_index = 0; var_index < model_->Size(); ++var_index) {
6812 slacks_[var_index], global_span_cost_coefficient_);
6813 cost_elements->push_back(
6814 solver
6815 ->MakeProd(
6816 model_->vehicle_costs_considered_[0],
6817 solver->MakeProd(
6818 solver->MakeProd(
6819 solver->MakeSum(transits_[var_index],
6820 dependent_transits_[var_index]),
6821 global_span_cost_coefficient_),
6822 model_->ActiveVar(var_index)))
6823 ->Var());
6824 }
6825 } else {
6826 IntVar* const end_range =
6827 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6828 end_range->SetMin(0);
6829 cost_elements->push_back(
6830 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6831 }
6832 }
6833}
6834
6836 std::vector<IntervalVar*> breaks, int vehicle,
6837 std::vector<int64> node_visit_transits) {
6838 if (breaks.empty()) return;
6839 const int visit_evaluator = model()->RegisterTransitCallback(
6840 [node_visit_transits = std::move(node_visit_transits)](int64 from, int64 to) {
6841 return node_visit_transits[from];
6842 });
6843 SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator, -1);
6844}
6845
6847 std::vector<IntervalVar*> breaks, int vehicle,
6848 std::vector<int64> node_visit_transits,
6849 std::function<int64(int64, int64)> delays) {
6850 if (breaks.empty()) return;
6851 const int visit_evaluator = model()->RegisterTransitCallback(
6852 [node_visit_transits](int64 from, int64 to) {
6853 return node_visit_transits[from];
6854 });
6855 const int delay_evaluator =
6856 model()->RegisterTransitCallback(std::move(delays));
6857 SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator,
6858 delay_evaluator);
6859}
6860
6862 std::vector<IntervalVar*> breaks, int vehicle, int pre_travel_evaluator,
6863 int post_travel_evaluator) {
6864 DCHECK_LE(0, vehicle);
6865 DCHECK_LT(vehicle, model_->vehicles());
6866 if (breaks.empty()) return;
6867 if (!break_constraints_are_initialized_) InitializeBreaks();
6868 vehicle_break_intervals_[vehicle] = std::move(breaks);
6869 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6870 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6871 // Breaks intervals must be fixed by search.
6872 for (IntervalVar* const interval : vehicle_break_intervals_[vehicle]) {
6874 if (interval->MayBePerformed() && !interval->MustBePerformed()) {
6875 model_->AddVariableTargetToFinalizer(interval->PerformedExpr()->Var(), 0);
6876 }
6877 model_->AddVariableTargetToFinalizer(interval->SafeStartExpr(0)->Var(),
6878 kint64min);
6879 model_->AddVariableTargetToFinalizer(interval->SafeDurationExpr(0)->Var(),
6880 kint64min);
6881 }
6882 // When a vehicle has breaks, if its start and end are fixed,
6883 // then propagation keeps the cumuls min and max on its path feasible.
6884 model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6885 kint64min);
6886 model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6887 kint64max);
6888}
6889
6891 DCHECK(!break_constraints_are_initialized_);
6892 const int num_vehicles = model_->vehicles();
6893 vehicle_break_intervals_.resize(num_vehicles);
6894 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6895 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6896 vehicle_break_distance_duration_.resize(num_vehicles);
6897 break_constraints_are_initialized_ = true;
6898}
6899
6901 return break_constraints_are_initialized_;
6902}
6903
6904const std::vector<IntervalVar*>& RoutingDimension::GetBreakIntervalsOfVehicle(
6905 int vehicle) const {
6906 DCHECK_LE(0, vehicle);
6907 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6908 return vehicle_break_intervals_[vehicle];
6909}
6910
6912 DCHECK_LE(0, vehicle);
6913 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6914 return vehicle_pre_travel_evaluators_[vehicle];
6915}
6916
6918 DCHECK_LE(0, vehicle);
6919 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6920 return vehicle_post_travel_evaluators_[vehicle];
6921}
6922
6924 int64 duration,
6925 int vehicle) {
6926 DCHECK_LE(0, vehicle);
6927 DCHECK_LT(vehicle, model_->vehicles());
6928 if (!break_constraints_are_initialized_) InitializeBreaks();
6929 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6930 // When a vehicle has breaks, if its start and end are fixed,
6931 // then propagation keeps the cumuls min and max on its path feasible.
6932 model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6933 kint64min);
6934 model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6935 kint64max);
6936}
6937
6938const std::vector<std::pair<int64, int64>>&
6940 DCHECK_LE(0, vehicle);
6941 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6942 return vehicle_break_distance_duration_[vehicle];
6943}
6944
6946 PickupToDeliveryLimitFunction limit_function, int pair_index) {
6947 CHECK_GE(pair_index, 0);
6948 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6949 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6950 }
6951 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6952 std::move(limit_function);
6953}
6954
6956 return !pickup_to_delivery_limits_per_pair_index_.empty();
6957}
6958
6960 int pickup,
6961 int delivery) const {
6962 DCHECK_GE(pair_index, 0);
6963
6964 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6965 return kint64max;
6966 }
6967 const PickupToDeliveryLimitFunction& pickup_to_delivery_limit_function =
6968 pickup_to_delivery_limits_per_pair_index_[pair_index];
6969 if (!pickup_to_delivery_limit_function) {
6970 // No limit function set for this pair.
6971 return kint64max;
6972 }
6973 DCHECK_GE(pickup, 0);
6974 DCHECK_GE(delivery, 0);
6975 return pickup_to_delivery_limit_function(pickup, delivery);
6976}
6977
6978void RoutingDimension::SetupSlackAndDependentTransitCosts() const {
6979 if (model_->vehicles() == 0) return;
6980 // Figure out whether all vehicles have the same span cost coefficient or not.
6981 bool all_vehicle_span_costs_are_equal = true;
6982 for (int i = 1; i < model_->vehicles(); ++i) {
6983 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6984 vehicle_span_cost_coefficients_[0];
6985 }
6986
6987 if (all_vehicle_span_costs_are_equal &&
6988 vehicle_span_cost_coefficients_[0] == 0) {
6989 return; // No vehicle span cost.
6990 }
6991
6992 // Make sure that the vehicle's start cumul will be maximized in the end;
6993 // and that the vehicle's end cumul and the node's slacks will be minimized.
6994 // Note that we don't do that if there was no span cost (see the return
6995 // clause above), because in that case we want the dimension cumul to
6996 // remain unconstrained. Since transitions depend on base dimensions, we
6997 // have to make sure the slacks of base dimensions are taken care of.
6998 // Also, it makes more sense to make decisions from the root of the tree
6999 // towards to leaves, and hence the slacks are pushed in reverse order.
7000 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {this};
7001 while (true) {
7002 const RoutingDimension* next =
7003 dimensions_with_relevant_slacks.back()->base_dimension_;
7004 if (next == nullptr || next == dimensions_with_relevant_slacks.back()) {
7005 break;
7006 }
7007 dimensions_with_relevant_slacks.push_back(next);
7008 }
7009
7010 for (auto it = dimensions_with_relevant_slacks.rbegin();
7011 it != dimensions_with_relevant_slacks.rend(); ++it) {
7012 for (int i = 0; i < model_->vehicles(); ++i) {
7013 model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->End(i)],
7014 kint64min);
7015 model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->Start(i)],
7016 kint64max);
7017 }
7018 for (IntVar* const slack : (*it)->slacks_) {
7020 }
7021 }
7022}
7023} // namespace operations_research
int64 min
Definition: alldiff_cst.cc:138
int64 max
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:887
#define CHECK_LT(val1, val2)
Definition: base/logging.h:700
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
#define CHECK_GE(val1, val2)
Definition: base/logging.h:701
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:889
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:890
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:884
#define CHECK_LE(val1, val2)
Definition: base/logging.h:699
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:885
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:39
#define VLOG(verboselevel)
Definition: base/logging.h:978
void resize(size_type new_size)
size_type size() const
bool empty() const
void push_back(const value_type &x)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
int64 Value(const IntVar *const var) const
bool Load(const std::string &filename)
Loads an assignment from a file; does not add variables to the assignment (only the variables contain...
int64 Min(const IntVar *const var) const
bool Contains(const IntVar *const var) const
bool Save(const std::string &filename) const
Saves the assignment to a file.
int64 Max(const IntVar *const var) const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
void SetValue(const IntVar *const var, int64 value)
void Copy(const Assignment *assignment)
Copies 'assignment' to the current assignment, clearing its previous content.
IntVarElement * Add(IntVar *const var)
bool Bound(const IntVar *const var) const
This is the base class for all expressions that are not variables.
A constraint is the main modeling object.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
A Demon is the base element of a propagation queue.
void inhibit(Solver *const s)
This method inhibits the demon in the search tree below the current position.
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition: ebert_graph.h:1001
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
The class IntExpr is the base of all integer expressions in constraint programming.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64 Max() const =0
virtual IntVar * Var()=0
Creates a variable from the expression.
virtual int64 Min() const =0
Decision builder building a solution using heuristics with local search filters to evaluate its feasi...
Definition: routing.h:2977
int64 number_of_decisions() const
Returns statistics from its underlying heuristic.
The class IntVar is a subset of IntExpr.
virtual void WhenBound(Demon *d)=0
This method attaches a demon that will be awakened when the variable is bound.
virtual bool Contains(int64 v) const =0
This method returns whether the value 'v' is in the domain of the variable.
virtual int64 Value() const =0
This method returns the value of the variable.
virtual uint64 Size() const =0
This method returns the number of values in the domain of the variable.
Interval variables are often used in scheduling.
void SetArcCost(ArcIndex arc, CostValue cost)
The base class for all local search operators.
static int64 FastInt64Round(double x)
Definition: mathutil.h:138
void EnqueueDelayedDemon(Demon *const d)
This method pushes the demon onto the propagation queue.
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
void UpdateLimits(absl::Duration time, int64 branches, int64 failures, int64 solutions)
Definition: search.cc:4031
void Switch(Solver *const solver)
RouteConstructor(Assignment *const assignment, RoutingModel *const model, bool check_assignment, int64 num_indices, const std::vector< Link > &links_list)
Definition: routing.cc:2491
const std::vector< std::vector< int > > & final_routes() const
Definition: routing.cc:2644
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2368
void SetCumulVarPiecewiseLinearCost(int64 index, const PiecewiseLinearFunction &cost)
Sets a piecewise linear cost on the cumul variable of a given variable index.
Definition: routing.cc:6612
IntVar * CumulVar(int64 index) const
Get the cumul, transit and slack variables for the given node (given as int64 var index).
Definition: routing.h:2386
const std::vector< std::pair< int64, int64 > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
Definition: routing.cc:6939
const std::string & name() const
Returns the name of the dimension.
Definition: routing.h:2619
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
Definition: routing.h:2394
void SetSpanUpperBoundForVehicle(int64 upper_bound, int vehicle)
!defined(SWIGCSHARP) && !defined(SWIGJAVA) !defined(SWIGPYTHON)
Definition: routing.cc:6586
int64 GetCumulVarSoftLowerBoundCoefficient(int64 index) const
Returns the cost coefficient of the soft lower bound of a cumul variable for a given variable index.
Definition: routing.cc:6751
bool HasCumulVarSoftLowerBound(int64 index) const
Returns true if a soft lower bound has been set for a given variable index.
Definition: routing.cc:6738
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
Definition: routing.h:2449
void SetSpanCostCoefficientForAllVehicles(int64 coefficient)
Definition: routing.cc:6602
int64 GetCumulVarSoftLowerBound(int64 index) const
Returns the soft lower bound of a cumul variable for a given variable index.
Definition: routing.cc:6743
std::function< int64(int, int)> PickupToDeliveryLimitFunction
Limits, in terms of maximum difference between the cumul variables, between the pickup and delivery a...
Definition: routing.h:2637
void SetBreakDistanceDurationOfVehicle(int64 distance, int64 duration, int vehicle)
With breaks supposed to be consecutive, this forces the distance between breaks of size at least mini...
Definition: routing.cc:6923
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
Definition: routing.cc:6900
void InitializeBreaks()
Sets up vehicle_break_intervals_, vehicle_break_distance_duration_, pre_travel_evaluators and post_tr...
Definition: routing.cc:6890
int64 GetTransitValue(int64 from_index, int64 to_index, int64 vehicle) const
Returns the transition value for a given pair of nodes (as var index); this value is the one taken by...
Definition: routing.cc:6557
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
Definition: routing.cc:6911
RoutingModel * model() const
Returns the model on which the dimension was created.
Definition: routing.h:2372
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
Definition: routing.cc:6904
void SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function, int pair_index)
Definition: routing.cc:6945
int64 GetPickupToDeliveryLimitForPair(int pair_index, int pickup, int delivery) const
Definition: routing.cc:6959
void SetSpanCostCoefficientForVehicle(int64 coefficient, int vehicle)
Sets a cost proportional to the dimension span on a given vehicle, or on all vehicles at once.
Definition: routing.cc:6594
int64 GetCumulVarSoftUpperBound(int64 index) const
Returns the soft upper bound of a cumul variable for a given variable index.
Definition: routing.cc:6692
void SetCumulVarSoftUpperBound(int64 index, int64 upper_bound, int64 coefficient)
Sets a soft upper bound to the cumul variable of a given variable index.
Definition: routing.cc:6678
const PiecewiseLinearFunction * GetCumulVarPiecewiseLinearCost(int64 index) const
Returns the piecewise linear cost of a cumul variable for a given variable index.
Definition: routing.cc:6636
SortedDisjointIntervalList GetAllowedIntervalsInRange(int64 index, int64 min_value, int64 max_value) const
Returns allowed intervals for a given node in a given interval.
Definition: routing.cc:6563
void SetCumulVarSoftLowerBound(int64 index, int64 lower_bound, int64 coefficient)
Sets a soft lower bound to the cumul variable of a given variable index.
Definition: routing.cc:6729
void SetBreakIntervalsOfVehicle(std::vector< IntervalVar * > breaks, int vehicle, int pre_travel_evaluator, int post_travel_evaluator)
Sets the breaks for a given vehicle.
Definition: routing.cc:6861
bool HasCumulVarPiecewiseLinearCost(int64 index) const
Returns true if a piecewise linear cost has been set for a given variable index.
Definition: routing.cc:6631
void SetGlobalSpanCostCoefficient(int64 coefficient)
Sets a cost proportional to the global dimension span, that is the difference between the largest val...
Definition: routing.cc:6607
bool HasCumulVarSoftUpperBound(int64 index) const
Returns true if a soft upper bound has been set for a given variable index.
Definition: routing.cc:6687
int64 GetCumulVarSoftUpperBoundCoefficient(int64 index) const
Returns the cost coefficient of the soft upper bound of a cumul variable for a given variable index.
Definition: routing.cc:6700
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
Definition: routing.cc:6917
Manager for any NodeIndex <-> variable index conversion.
std::vector< NodeIndex > GetIndexToNodeMap() const
bool AddDimensionWithVehicleTransitAndCapacity(const std::vector< int > &evaluator_indices, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Definition: routing.cc:903
int64 GetNumberOfDecisionsInFirstSolution(const RoutingSearchParameters &search_parameters) const
Returns statistics on first solution search, number of decisions sent to filters, number of decisions...
Definition: routing.cc:3621
void SetFixedCostOfAllVehicles(int64 cost)
Sets the fixed cost of all vehicle routes.
Definition: routing.cc:1204
void AddAtSolutionCallback(std::function< void()> callback)
Adds a callback called each time a solution is found during the search.
Definition: routing.cc:3126
std::pair< int, bool > AddConstantDimensionWithSlack(int64 value, int64 capacity, int64 slack_max, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'value'; 'capacity' is t...
Definition: routing.cc:952
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1327
const TransitCallback2 & TransitCallback(int callback_index) const
Definition: routing.h:413
Assignment * RestoreAssignment(const Assignment &solution)
Restores an assignment as a solution in the routing model and returns the new solution.
Definition: routing.cc:3655
std::function< std::vector< operations_research::IntVar * >(RoutingModel *)> GetTabuVarsCallback
Sets the callback returning the variable to use for the Tabu Search metaheuristic.
Definition: routing.h:1368
void AddSearchMonitor(SearchMonitor *const monitor)
Adds a search monitor to the search used to solve the routing model.
Definition: routing.cc:3107
int nodes() const
Sizes and indices Returns the number of nodes in the model.
Definition: routing.h:1343
bool ArcIsMoreConstrainedThanArc(int64 from, int64 to1, int64 to2)
Returns whether the arc from->to1 is more constrained than from->to2, taking into account,...
Definition: routing.cc:3972
void AddLocalSearchOperator(LocalSearchOperator *ls_operator)
Adds a local search operator to the set of operators used to solve the vehicle routing problem.
Definition: routing.cc:1781
void AddVariableTargetToFinalizer(IntVar *var, int64 target)
Add a variable to set the closest possible to the target value in the solution finalizer.
Definition: routing.cc:5569
int64 End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1182
const std::vector< int > & GetPairIndicesOfType(int type) const
Definition: routing.cc:4083
RoutingTransitCallback1 TransitCallback1
Definition: routing.h:242
const absl::flat_hash_set< int > & GetTemporalTypeIncompatibilitiesOfType(int type) const
Definition: routing.cc:4129
void AddPickupAndDelivery(int64 pickup, int64 delivery)
Notifies that index1 and index2 form a pair of nodes which should belong to the same route.
Definition: routing.cc:1671
std::string DebugOutputAssignment(const Assignment &solution_assignment, const std::string &dimension_to_print) const
Print some debugging information about an assignment, including the feasible intervals of the CumulVa...
Definition: routing.cc:4241
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenRemovingType(int type) const
Returns the set of requirement alternatives when removing the given type.
Definition: routing.cc:4218
CostClassIndex GetCostClassIndexOfVehicle(int64 vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1251
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
Definition: routing.h:1278
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const
Definition: routing.cc:1734
int64 Size() const
Returns the number of next variables in the model.
Definition: routing.h:1347
int64 UnperformedPenalty(int64 var_index) const
Get the "unperformed" penalty of a node.
Definition: routing.cc:4224
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)
Sets the Pickup and delivery policy of all vehicles.
Definition: routing.cc:1725
IntVar * ActiveVar(int64 index) const
Returns the active variable of the node corresponding to index.
Definition: routing.h:1209
Assignment * ReadAssignment(const std::string &file_name)
Reads an assignment from a file and returns the current solution.
Definition: routing.cc:3646
Assignment * CompactAssignment(const Assignment &assignment) const
Returns a compacted version of the given assignment, in which all vehicles with id lower or equal to ...
Definition: routing.cc:3503
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
Definition: routing.cc:851
void AddToAssignment(IntVar *const var)
Adds an extra variable to the vehicle routing assignment.
Definition: routing.cc:5590
bool IsVehicleAllowedForIndex(int vehicle, int64 index)
Returns true if a vehicle is allowed to visit a given node.
Definition: routing.h:694
bool RoutesToAssignment(const std::vector< std::vector< int64 > > &routes, bool ignore_inactive_indices, bool close_routes, Assignment *const assignment) const
Fills an assignment from a specification of the routes of the vehicles.
Definition: routing.cc:3677
int64 Next(const Assignment &assignment, int64 index) const
Assignment inspection Returns the variable index of the node directly after the node corresponding to...
Definition: routing.cc:3910
IntVar * NextVar(int64 index) const
!defined(SWIGPYTHON)
Definition: routing.h:1207
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64 index) const
Returns the indices of the disjunctions to which an index belongs.
Definition: routing.h:631
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
Definition: routing.cc:5580
VisitTypePolicy
Set the node visit types and incompatibilities/requirements between the types (see below).
Definition: routing.h:773
@ TYPE_ADDED_TO_VEHICLE
When visited, the number of types 'T' on the vehicle increases by one.
Definition: routing.h:775
@ ADDED_TYPE_REMOVED_FROM_VEHICLE
When visited, one instance of type 'T' previously added to the route (TYPE_ADDED_TO_VEHICLE),...
Definition: routing.h:780
@ TYPE_ON_VEHICLE_UP_TO_VISIT
With the following policy, the visit enforces that type 'T' is considered on the route from its start...
Definition: routing.h:783
@ TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
The visit doesn't have an impact on the number of types 'T' on the route, as it's (virtually) added a...
Definition: routing.h:788
bool AddDimensionDependentDimensionWithVehicleCapacity(const std::vector< int > &pure_transits, const std::vector< int > &dependent_transits, const RoutingDimension *base_dimension, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension with transits depending on the cumuls of another dimension.
Definition: routing.h:510
static RoutingModel::StateDependentTransit MakeStateDependentTransit(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
Creates a cached StateDependentTransit from an std::function.
Definition: routing.cc:1111
int64 GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
Definition: routing.cc:1210
void SetFixedCostOfVehicle(int64 cost, int vehicle)
Sets the fixed cost of one vehicle route.
Definition: routing.cc:1215
Constraint * MakePathSpansAndTotalSlacks(const RoutingDimension *dimension, std::vector< IntVar * > spans, std::vector< IntVar * > total_slacks)
For every vehicle of the routing model:
Definition: routing.cc:5927
int64 GetArcCostForVehicle(int64 from_index, int64 to_index, int64 vehicle) const
Returns the cost of the transit arc between two nodes for a given vehicle.
Definition: routing.cc:3918
LocalDimensionCumulOptimizer * GetMutableLocalCumulOptimizer(const RoutingDimension &dimension) const
Definition: routing.cc:1142
const std::vector< absl::flat_hash_set< int > > & GetSameVehicleRequiredTypeAlternativesOfType(int type) const
Returns the set of same-vehicle requirement alternatives for the given type.
Definition: routing.cc:4203
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
Definition: routing.cc:1181
Assignment * ReadAssignmentFromRoutes(const std::vector< std::vector< int64 > > &routes, bool ignore_inactive_indices)
Restores the routes as the current solution.
Definition: routing.cc:3789
std::vector< std::vector< int64 > > GetRoutesFromAssignment(const Assignment &assignment)
Converts the solution in the given assignment to routes for all vehicles.
Definition: routing.cc:3836
bool HasTemporalTypeRequirements() const
Definition: routing.h:870
void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction, DisjunctionIndex delivery_disjunction)
Same as AddPickupAndDelivery but notifying that the performed node from the disjunction of index 'pic...
Definition: routing.cc:1676
RoutingTransitCallback2 TransitCallback2
Definition: routing.h:243
SweepArranger * sweep_arranger() const
Returns the sweep arranger to be used by routing heuristics.
Definition: routing.h:1162
std::vector< std::string > GetAllDimensionNames() const
Outputs the names of all dimensions added to the routing engine.
Definition: routing.cc:1121
int64 GetArcCostForFirstSolution(int64 from_index, int64 to_index) const
Returns the cost of the arc in the context of the first solution strategy.
Definition: routing.cc:3939
bool AddDimensionWithVehicleTransits(const std::vector< int > &evaluator_indices, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Definition: routing.cc:885
std::function< StateDependentTransit(int64, int64)> VariableIndexEvaluator2
Definition: routing.h:269
const Assignment * SolveFromAssignmentWithParameters(const Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Definition: routing.cc:3201
@ ROUTING_SUCCESS
Problem solved successfully after calling RoutingModel::Solve().
Definition: routing.h:220
@ ROUTING_FAIL
No solution found to the problem after calling RoutingModel::Solve().
Definition: routing.h:222
@ ROUTING_INVALID
Model, model parameters or flags are not valid.
Definition: routing.h:226
@ ROUTING_FAIL_TIMEOUT
Time limit reached before finding a solution with RoutingModel::Solve().
Definition: routing.h:224
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
Definition: routing.cc:4996
void AddTemporalTypeIncompatibility(int type1, int type2)
Definition: routing.cc:4112
std::vector< std::pair< int64, int64 > > GetPerfectBinaryDisjunctions() const
Returns the list of all perfect binary disjunctions, as pairs of variable indices: a disjunction is "...
Definition: routing.cc:1591
const std::vector< int > & GetSingleNodesOfType(int type) const
Definition: routing.cc:4078
void SetVisitType(int64 index, int type, VisitTypePolicy type_policy)
Definition: routing.cc:4065
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenAddingType(int type) const
Returns the set of requirement alternatives when adding the given type.
Definition: routing.cc:4211
void AddHardTypeIncompatibility(int type1, int type2)
Incompatibilities: Two nodes with "hard" incompatible types cannot share the same route at all,...
Definition: routing.cc:4103
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64 vehicle) const
Definition: routing.h:1273
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
Definition: routing.h:417
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)
Definition: routing.cc:810
void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)
Definition: routing.cc:5476
void CloseVisitTypes()
This function should be called once all node visit types have been set and prior to adding any incomp...
Definition: routing.cc:4094
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
Definition: routing.h:559
std::pair< int, bool > AddVectorDimension(std::vector< int64 > values, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'values[i]' for node i; ...
Definition: routing.cc:963
static const int64 kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
Definition: routing.h:384
void IgnoreDisjunctionsAlreadyForcedToZero()
SPECIAL: Makes the solver ignore all the disjunctions whose active variables are all trivially zero (...
Definition: routing.cc:1608
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy, int vehicle)
Definition: routing.cc:1719
const Assignment * SolveWithParameters(const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Solves the current routing model with the given parameters.
Definition: routing.cc:3136
int RegisterTransitCallback(TransitCallback2 callback)
Definition: routing.cc:818
const VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
Definition: routing.h:421
const Assignment * PackCumulsOfOptimizerDimensionsFromAssignment(const Assignment *original_assignment, absl::Duration duration_limit)
For every dimension in the model with an optimizer in local/global_dimension_optimizers_,...
Definition: routing.cc:390
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
Definition: routing.h:1200
int64 GetArcCostForClass(int64 from_index, int64 to_index, int64 cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
Definition: routing.cc:3928
void AddWeightedVariableMinimizedByFinalizer(IntVar *var, int64 cost)
Adds a variable to minimize in the solution finalizer, with a weighted priority: the higher the more ...
Definition: routing.cc:5556
int GetVisitType(int64 index) const
Definition: routing.cc:4073
RoutingDimensionIndex DimensionIndex
Definition: routing.h:239
std::pair< int, bool > AddMatrixDimension(std::vector< std::vector< int64 > > values, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'values[i][next(i)]' for...
Definition: routing.cc:972
IntVar * ApplyLocks(const std::vector< int64 > &locks)
Applies a lock chain to the next search.
Definition: routing.cc:3594
DisjunctionIndex AddDisjunction(const std::vector< int64 > &indices, int64 penalty=kNoPenalty, int64 max_cardinality=1)
Adds a disjunction constraint on the indices: exactly 'max_cardinality' of the indices are active.
Definition: routing.cc:1575
Assignment * CompactAndCheckAssignment(const Assignment &assignment) const
Same as CompactAssignment() but also checks the validity of the final compact solution; if it is not ...
Definition: routing.cc:3508
void AddSoftSameVehicleConstraint(const std::vector< int64 > &indices, int64 cost)
Adds a soft constraint to force a set of variable indices to be on the same vehicle.
Definition: routing.cc:1650
LocalDimensionCumulOptimizer * GetMutableLocalCumulMPOptimizer(const RoutingDimension &dimension) const
Definition: routing.cc:1154
bool HasHardTypeIncompatibilities() const
Returns true iff any hard (resp.
Definition: routing.h:822
void AssignmentToRoutes(const Assignment &assignment, std::vector< std::vector< int64 > > *const routes) const
Converts the solution in the given assignment to routes for all vehicles.
Definition: routing.cc:3802
void AddRequiredTypeAlternativesWhenRemovingType(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
The following requirements apply when visiting dependent nodes that remove their type from the route,...
Definition: routing.cc:4180
std::vector< std::vector< std::pair< int64, int64 > > > GetCumulBounds(const Assignment &solution_assignment, const RoutingDimension &dimension)
Returns a vector cumul_bounds, for which cumul_bounds[i][j] is a pair containing the minimum and maxi...
Definition: routing.cc:4314
int64 GetHomogeneousCost(int64 from_index, int64 to_index) const
Returns the cost of the segment between two nodes supposing all vehicle costs are the same (returns t...
Definition: routing.h:1236
int RegisterPositiveTransitCallback(TransitCallback2 callback)
Definition: routing.cc:844
PickupAndDeliveryPolicy
Types of precedence policy applied to pickup and delivery pairs.
Definition: routing.h:230
@ PICKUP_AND_DELIVERY_LIFO
Deliveries must be performed in reverse order of pickups.
Definition: routing.h:234
@ PICKUP_AND_DELIVERY_NO_ORDER
Any precedence is accepted.
Definition: routing.h:232
@ PICKUP_AND_DELIVERY_FIFO
Deliveries must be performed in the same order as pickups.
Definition: routing.h:236
void CloseModelWithParameters(const RoutingSearchParameters &search_parameters)
Same as above taking search parameters (as of 10/2015 some the parameters have to be set when closing...
Definition: routing.cc:2062
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1345
void AddVariableMaximizedByFinalizer(IntVar *var)
Adds a variable to maximize in the solution finalizer (see above for information on the solution fina...
Definition: routing.cc:5576
IntVar * VehicleVar(int64 index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1222
bool HasTypeRegulations() const
Returns true iff the model has any incompatibilities or requirements set on node types.
Definition: routing.h:876
RoutingVehicleClassIndex VehicleClassIndex
Definition: routing.h:241
int RegisterUnaryTransitVector(std::vector< int64 > values)
Registers 'callback' and returns its index.
Definition: routing.cc:772
bool AddDimension(int evaluator_index, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Model creation.
Definition: routing.cc:875
void AddIntervalToAssignment(IntervalVar *const interval)
Definition: routing.cc:5594
void SetArcCostEvaluatorOfAllVehicles(int evaluator_index)
Sets the cost function of the model such that the cost of a segment of a route between node 'from' an...
Definition: routing.cc:1190
void SetAmortizedCostFactorsOfAllVehicles(int64 linear_cost_factor, int64 quadratic_cost_factor)
The following methods set the linear and quadratic cost factors of vehicles (must be positive values)...
Definition: routing.cc:1221
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulOptimizer(const RoutingDimension &dimension) const
Returns the global/local dimension cumul optimizer for a given dimension, or nullptr if there is none...
Definition: routing.cc:1130
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1224
bool HasSameVehicleTypeRequirements() const
Returns true iff any same-route (resp.
Definition: routing.h:867
void SetAssignmentFromOtherModelAssignment(Assignment *target_assignment, const RoutingModel *source_model, const Assignment *source_assignment)
Given a "source_model" and its "source_assignment", resets "target_assignment" with the IntVar variab...
Definition: routing.cc:3321
void AddSameVehicleRequiredTypeAlternatives(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
Requirements: NOTE: As of 2019-04, cycles in the requirement graph are not supported,...
Definition: routing.cc:4137
const absl::flat_hash_set< int > & GetHardTypeIncompatibilitiesOfType(int type) const
Returns visit types incompatible with a given type.
Definition: routing.cc:4122
int RegisterTransitMatrix(std::vector< std::vector< int64 > > values)
Definition: routing.cc:791
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
Definition: routing_flow.cc:34
bool AddDimensionWithVehicleCapacity(int evaluator_index, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Definition: routing.cc:894
int RegisterUnaryTransitCallback(TransitCallback1 callback)
Definition: routing.cc:783
int64 Start(int vehicle) const
Model inspection.
Definition: routing.h:1180
int64 GetDepot() const
Returns the variable index of the first starting or ending node of all routes.
Definition: routing.cc:1785
bool WriteAssignment(const std::string &file_name) const
Writes the current solution to a file containing an AssignmentProto.
Definition: routing.cc:3637
RoutingCostClassIndex CostClassIndex
Definition: routing.h:238
bool HasTemporalTypeIncompatibilities() const
Definition: routing.h:825
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1268
int GetNumOfSingletonNodes() const
Returns the number of non-start/end nodes which do not appear in a pickup/delivery pair.
Definition: routing.cc:1739
void AddRequiredTypeAlternativesWhenAddingType(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
If type_D depends on type_R when adding type_D, any node_D of type_D and VisitTypePolicy TYPE_ADDED_T...
Definition: routing.cc:4159
void CloseModel()
Closes the current routing model; after this method is called, no modification to the model can be do...
Definition: routing.cc:1884
static const DimensionIndex kNoDimension
Constant used to express the "no dimension" index, returned when a dimension name does not correspond...
Definition: routing.h:392
bool CostsAreHomogeneousAcrossVehicles() const
Whether costs are homogeneous across all vehicles.
Definition: routing.h:1231
void SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor, int64 quadratic_cost_factor, int vehicle)
Sets the linear and quadratic cost factor of the given vehicle.
Definition: routing.cc:1229
bool IsStart(int64 index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.cc:3896
bool IsEnd(int64 index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1186
const Assignment * Solve(const Assignment *assignment=nullptr)
Solves the current routing model; closes the current model.
Definition: routing.cc:3131
static const DisjunctionIndex kNoDisjunction
Constant used to express the "no disjunction" index, returned when a node does not appear in any disj...
Definition: routing.h:388
void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle)
Sets the cost function for a given vehicle route.
Definition: routing.cc:1197
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64 node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
Definition: routing.cc:1707
int64 UnperformedPenaltyOrValue(int64 default_value, int64 var_index) const
Same as above except that it returns default_value instead of 0 when penalty is not well defined (def...
Definition: routing.cc:4228
bool ApplyLocksToAllVehicles(const std::vector< std::vector< int64 > > &locks, bool close_routes)
Applies lock chains to all vehicles to the next search, such that locks[p] is the lock chain for rout...
Definition: routing.cc:3615
int64 ComputeLowerBound()
Computes a lower bound to the routing problem solving a linear assignment problem.
Definition: routing.cc:3359
bool HasDimension(const std::string &dimension_name) const
Returns true if a dimension exists for a given dimension name.
Definition: routing.cc:1166
bool IsVehicleUsed(const Assignment &assignment, int vehicle) const
Returns true if the route of 'vehicle' is non empty in 'assignment'.
Definition: routing.cc:3900
int64 GetNumberOfRejectsInFirstSolution(const RoutingSearchParameters &search_parameters) const
Definition: routing.cc:3629
const std::string & GetPrimaryConstrainedDimension() const
Get the primary constrained dimension, or an empty string if it is unset.
Definition: routing.h:608
RoutingModel(const RoutingIndexManager &index_manager)
Constructor taking an index manager.
Definition: routing.cc:645
RoutingDisjunctionIndex DisjunctionIndex
Definition: routing.h:240
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64 node_index) const
Same as above for deliveries.
Definition: routing.cc:1713
VisitTypePolicy GetVisitTypePolicy(int64 index) const
Definition: routing.cc:4088
void SetAllowedVehiclesForIndex(const std::vector< int > &vehicles, int64 index)
Sets the vehicles which can visit a given node.
Definition: routing.cc:1662
const RoutingDimension & GetDimensionOrDie(const std::string &dimension_name) const
Returns a dimension from its name. Dies if the dimension does not exist.
Definition: routing.cc:1176
RoutingModelInspector(RoutingModel *model)
Definition: routing.cc:1890
void EndVisitConstraint(const std::string &type_name, const Constraint *const constraint) override
Definition: routing.cc:1918
void EndVisitModel(const std::string &solver_name) override
Definition: routing.cc:1906
void VisitIntegerArrayArgument(const std::string &arg_name, const std::vector< int64 > &values) override
Definition: routing.cc:1927
void VisitIntegerExpressionArgument(const std::string &type_name, IntExpr *const expr) override
Visit integer expression argument.
Definition: routing.cc:1922
static const char kLightElement[]
Constraint types.
Definition: routing.h:1946
A search monitor is a simple set of callbacks to monitor all search events.
int solution_count() const
Returns how many solutions were stored during the search.
Definition: search.cc:2325
Assignment * solution(int n) const
Returns the nth solution.
Definition: search.cc:2320
IntExpr * MakeElement(const std::vector< int64 > &values, IntVar *const index)
values[index]
Definition: element.cc:647
Constraint * MakeEquality(IntExpr *const left, IntExpr *const right)
left == right
Definition: range_cst.cc:512
std::function< bool(int64, int64, int64)> VariableValueComparator
@ ASSIGN_MIN_VALUE
Selects the min value of the selected variable.
void AddConstraint(Constraint *const c)
Adds the constraint 'c' to the model.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
@ CHOOSE_STATIC_GLOBAL_BEST
Pairs are compared at the first call of the selector, and results are cached.
IntExpr * MakeMax(const std::vector< IntVar * > &vars)
std::max(vars)
Definition: expr_array.cc:3321
IntExpr * MakeDifference(IntExpr *const left, IntExpr *const right)
left - right
std::function< int64(int64)> IndexEvaluator1
Callback typedefs.
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
@ CHOOSE_FIRST_UNBOUND
Select the first unbound variable.
@ CHOOSE_PATH
Selects the next unbound variable on a path, the path being defined by the variables: var[i] correspo...
std::function< int64(int64, int64)> IndexEvaluator2
IntExpr * MakeSemiContinuousExpr(IntExpr *const expr, int64 fixed_charge, int64 step)
Semi continuous Expression (x <= 0 -> f(x) = 0; x > 0 -> f(x) = ax + b) a >= 0 and b >= 0.
IntExpr * MakeSum(IntExpr *const left, IntExpr *const right)
left + right.
Assignment * MakeAssignment()
This method creates an empty assignment.
IntExpr * MakeProd(IntExpr *const left, IntExpr *const right)
left * right
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
T * RevAlloc(T *object)
Registers the given object as being reversible.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
@ LE
Move is accepted when the current objective value <= objective.Max.
This class represents a sorted list of disjoint, closed intervals.
Iterator InsertInterval(int64 start, int64 end)
Adds the interval [start..end] to the list, and merges overlapping or immediately adjacent intervals ...
Iterator FirstIntervalGreaterOrEqual(int64 value) const
Returns an iterator to either:
void ArrangeIndices(std::vector< int64 > *indices)
Definition: routing.cc:2986
SweepArranger(const std::vector< std::pair< int64, int64 > > &points)
Definition: routing.cc:2976
void SetSectors(int sectors)
Definition: routing.h:2874
SweepBuilder(RoutingModel *const model, bool check_assignment)
Definition: routing.cc:3028
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
Definition: routing.cc:3032
TypeIncompatibilityChecker(const RoutingModel &model, bool check_hard_incompatibilities)
Definition: routing.cc:6310
virtual bool HasRegulationsToCheck() const =0
virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos)=0
bool CheckVehicle(int vehicle, const std::function< int64(int64)> &next_accessor)
Definition: routing.cc:6228
TypeRegulationsChecker(const RoutingModel &model)
Definition: routing.cc:6225
bool TypeCurrentlyOnRoute(int type, int pos) const
Returns true iff there's at least one instance of the given type on the route when scanning the route...
Definition: routing.cc:6303
void InitializeCheck(int vehicle, const std::function< int64(int64)> &next_accessor)
Definition: routing.cc:6272
bool TypeOccursOnRoute(int type) const
Returns true iff any occurrence of the given type was seen on the route, i.e.
Definition: routing.cc:6297
The following constraint ensures that incompatibilities and requirements between types are respected.
Definition: routing.h:2300
void Post() override
This method is called when the constraint is processed by the solver.
Definition: routing.cc:6450
void InitialPropagate() override
This method performs the initial propagation of the constraint.
Definition: routing.cc:6465
TypeRegulationsConstraint(const RoutingModel &model)
Definition: routing.cc:6417
IntegerRange< NodeIndex > AllNodes() const
Definition: graph.h:935
Block * next
SatParameters parameters
const std::string name
int64 value
IntVar *const expr_
Definition: element.cc:85
IntVar * var
Definition: expr_array.cc:1858
const std::vector< IntVar * > cumuls_
GRBmodel * model
MPCallback * callback
static const int64 kint64max
int64_t int64
static const int64 kint64min
const int WARNING
Definition: log_severity.h:31
const int INFO
Definition: log_severity.h:31
const int ERROR
Definition: log_severity.h:32
Definition: cleanup.h:22
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:176
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:207
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:135
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:26
void STLDeleteElements(T *container)
Definition: stl_util.h:372
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Definition: map_util.h:155
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:41
std::function< int64(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1487
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
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:505
int64 CapAdd(int64 x, int64 y)
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters &parameters, bool filter_objective_cost, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
int64 CapProd(int64 x, int64 y)
RoutingModelParameters DefaultRoutingModelParameters()
uint64 ThoroughHash(const char *bytes, size_t len)
Definition: thorough_hash.h:33
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
int64 CapSub(int64 x, int64 y)
std::string FindErrorInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
Returns an empty std::string if the routing search parameters are valid, and a non-empty,...
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
struct operations_research::SweepIndexSortAngle SweepIndexAngleComparator
void SetAssignmentFromAssignment(Assignment *target_assignment, const std::vector< IntVar * > &target_vars, const Assignment *source_assignment, const std::vector< IntVar * > &source_vars)
NOLINT.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
bool HasUnaryDimension(const std::vector< RoutingDimension * > &dimensions)
Definition: routing.cc:4724
struct operations_research::SweepIndexSortDistance SweepIndexDistanceComparator
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
struct operations_research::LinkSort LinkComparator
std::function< int64(int64, int64)> RoutingTransitCallback2
Definition: routing_types.h:42
RoutingSearchParameters DefaultRoutingSearchParameters()
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
std::function< int64(int64)> RoutingTransitCallback1
Definition: routing_types.h:41
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
std::string MemoryUsage()
Definition: stats.cc:25
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const RoutingModel::IndexPairs &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
int64 One()
This method returns 1.
void FillPathEvaluation(const std::vector< int64 > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64 > *values)
Definition: routing.cc:6215
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
static const int kUnassigned
Definition: routing.cc:638
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model)
DecisionBuilder * MakeSetValuesFromTargets(Solver *solver, std::vector< IntVar * > variables, std::vector< int64 > targets)
A decision builder which tries to assign values to variables as close as possible to target values fi...
Definition: routing.cc:143
STL namespace.
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:40
int index
Definition: pack.cc:508
static int input(yyscan_t yyscanner)
int64 demand
Definition: resource.cc:123
IntervalVar * interval
Definition: resource.cc:98
ABSL_FLAG(int64, sweep_sectors, 1, "The number of sectors the space is divided before it is sweeped " "by the ray.")
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators)
Definition: routing.cc:4582
int64 tail
int64 cost
int64 head
int64 capacity
int64 coefficient
std::vector< int > var_indices
Rev< int64 > end_min
Rev< int64 > end_max
Rev< int64 > start_max
Rev< int64 > start_min
static bool LessThan(const CostClass &a, const CostClass &b)
Comparator for STL containers and algorithms.
Definition: routing.h:315
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:264
int64 fixed_cost
Contrarily to CostClass, here we need strict equivalence.
Definition: routing.h:328
absl::StrongVector< DimensionIndex, int64 > dimension_capacities
Definition: routing.h:343
absl::StrongVector< DimensionIndex, int64 > dimension_evaluator_classes
dimension_evaluators[d]->Run(from, to) is the transit value of arc from->to for a dimension d.
Definition: routing.h:346
int start_equivalence_class
Vehicle start and end equivalence classes.
Definition: routing.h:335
absl::StrongVector< DimensionIndex, int64 > dimension_start_cumuls_min
Bounds of cumul variables at start and end vehicle nodes.
Definition: routing.h:339
absl::StrongVector< DimensionIndex, int64 > dimension_end_cumuls_min
Definition: routing.h:341
absl::StrongVector< DimensionIndex, int64 > dimension_end_cumuls_max
Definition: routing.h:342
uint64 unvisitable_nodes_fprint
Fingerprint of unvisitable non-start/end nodes.
Definition: routing.h:348
static bool LessThan(const VehicleClass &a, const VehicleClass &b)
Comparator for STL containers and algorithms.
Definition: routing.cc:1324
absl::StrongVector< DimensionIndex, int64 > dimension_start_cumuls_max
Definition: routing.h:340
CostClassIndex cost_class_index
The cost class of the vehicle.
Definition: routing.h:326
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
Definition: routing.h:378
std::vector< std::deque< int > > vehicles_per_vehicle_class
Definition: routing.h:379
SweepIndex(const int64 index, const double angle, const double distance)
Definition: routing.cc:2955
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
Definition: routing.cc:2965
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
Definition: routing.cc:2971
int position_of_last_type_on_vehicle_up_to_visit
Position of the last node of policy TYPE_ON_VEHICLE_UP_TO_VISIT visited on the route.
Definition: routing.h:2188
int num_type_added_to_vehicle
Number of TYPE_ADDED_TO_VEHICLE and TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED node type policies seen on ...
Definition: routing.h:2177
int num_type_removed_from_vehicle
Number of ADDED_TYPE_REMOVED_FROM_VEHICLE (effectively removing a type from the route) and TYPE_SIMUL...
Definition: routing.h:2183