Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing.h
Go to the documentation of this file.
1// Copyright 2010-2024 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
67// TODO(user): Add a section on costs (vehicle arc costs, span costs,
68// disjunctions costs).
69//
155
156#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
157#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
158
159#include <algorithm>
160#include <atomic>
161#include <cstddef>
162#include <cstdint>
163#include <deque>
164#include <functional>
165#include <limits>
166#include <memory>
167#include <set>
168#include <string>
169#include <tuple>
170#include <utility>
171#include <vector>
172
173#include "absl/container/flat_hash_map.h"
174#include "absl/container/flat_hash_set.h"
175#include "absl/container/inlined_vector.h"
176#include "absl/hash/hash.h"
177#include "absl/log/check.h"
178#include "absl/strings/string_view.h"
179#include "absl/time/time.h"
180#include "absl/types/span.h"
182#include "ortools/base/logging.h"
184#include "ortools/base/types.h"
187#include "ortools/constraint_solver/routing_enums.pb.h"
189#include "ortools/constraint_solver/routing_parameters.pb.h"
192#include "ortools/graph/graph.h"
194#include "ortools/util/bitset.h"
199
200namespace operations_research {
201
202class FinalizerVariables;
203class GlobalDimensionCumulOptimizer;
204class LocalDimensionCumulOptimizer;
205class LocalSearchPhaseParameters;
206#ifndef SWIG
207class IndexNeighborFinder;
208class IntVarFilteredDecisionBuilder;
209#endif
210class RoutingDimension;
211#ifndef SWIG
213class SweepArranger;
214#endif
215
216class PathsMetadata {
217 public:
218 explicit PathsMetadata(const RoutingIndexManager& manager) {
219 const int num_indices = manager.num_indices();
220 const int num_paths = manager.num_vehicles();
221 path_of_node_.resize(num_indices, -1);
222 is_start_.resize(num_indices, false);
223 is_end_.resize(num_indices, false);
224 start_of_path_.resize(num_paths);
225 end_of_path_.resize(num_paths);
226 for (int v = 0; v < num_paths; ++v) {
227 const int64_t start = manager.GetStartIndex(v);
228 start_of_path_[v] = start;
229 path_of_node_[start] = v;
230 is_start_[start] = true;
231 const int64_t end = manager.GetEndIndex(v);
232 end_of_path_[v] = end;
233 path_of_node_[end] = v;
234 is_end_[end] = true;
235 }
236 }
237
238 bool IsStart(int64_t node) const { return is_start_[node]; }
239 bool IsEnd(int64_t node) const { return is_end_[node]; }
240 int GetPath(int64_t start_or_end_node) const {
241 return path_of_node_[start_or_end_node];
242 }
243 int NumPaths() const { return start_of_path_.size(); }
244 const std::vector<int64_t>& Paths() const { return path_of_node_; }
245 const std::vector<int64_t>& Starts() const { return start_of_path_; }
246 const std::vector<int64_t>& Ends() const { return end_of_path_; }
247
248 private:
249 std::vector<bool> is_start_;
250 std::vector<bool> is_end_;
251 std::vector<int64_t> start_of_path_;
252 std::vector<int64_t> end_of_path_;
253 std::vector<int64_t> path_of_node_;
254};
255
256class RoutingModel {
257 public:
259 enum Status {
261 ROUTING_NOT_SOLVED,
263 ROUTING_SUCCESS,
267 ROUTING_PARTIAL_SUCCESS_LOCAL_OPTIMUM_NOT_REACHED,
269 ROUTING_FAIL,
271 ROUTING_FAIL_TIMEOUT,
273 ROUTING_INVALID,
275 ROUTING_INFEASIBLE,
277 ROUTING_OPTIMAL
278 };
279
281 enum PickupAndDeliveryPolicy {
283 PICKUP_AND_DELIVERY_NO_ORDER,
285 PICKUP_AND_DELIVERY_LIFO,
287 PICKUP_AND_DELIVERY_FIFO
288 };
289 typedef RoutingCostClassIndex CostClassIndex;
290 typedef RoutingDimensionIndex DimensionIndex;
291 typedef RoutingDisjunctionIndex DisjunctionIndex;
292 typedef RoutingVehicleClassIndex VehicleClassIndex;
293 typedef RoutingResourceClassIndex ResourceClassIndex;
294 typedef RoutingTransitCallback1 TransitCallback1;
295 typedef RoutingTransitCallback2 TransitCallback2;
296
297#if !defined(SWIG)
310 struct StateDependentTransit {
311 RangeIntToIntFunction* transit;
312 RangeMinMaxIndexFunction* transit_plus_identity;
313 };
314 typedef std::function<StateDependentTransit(int64_t, int64_t)>
315 VariableIndexEvaluator2;
316#endif // SWIG
317
318#if !defined(SWIG)
319 struct CostClass {
321 int evaluator_index = 0;
322
337
343 struct DimensionCost {
344 int64_t transit_evaluator_class;
345 // TODO(user): replace span_cost_coefficient by
346 // transit_cost_coefficient and add the span coefficient to the slack one.
347 int64_t span_cost_coefficient;
348 int64_t slack_cost_coefficient;
349 const RoutingDimension* dimension;
350 bool operator<(const DimensionCost& cost) const {
351 return std::tie(transit_evaluator_class, span_cost_coefficient,
352 slack_cost_coefficient) <
353 std::tie(cost.transit_evaluator_class,
354 cost.span_cost_coefficient,
355 cost.slack_cost_coefficient);
356 }
357
358 friend bool operator==(const DimensionCost& c1, const DimensionCost& c2) {
359 return c1.transit_evaluator_class == c2.transit_evaluator_class &&
360 c1.span_cost_coefficient == c2.span_cost_coefficient &&
361 c1.slack_cost_coefficient == c2.slack_cost_coefficient;
362 }
363 template <typename H>
364 friend H AbslHashValue(H h, const DimensionCost& cost) {
365 return H::combine(std::move(h), cost.transit_evaluator_class,
366 cost.span_cost_coefficient,
367 cost.slack_cost_coefficient);
368 }
369 };
370 std::vector<DimensionCost>
371 dimension_transit_evaluator_class_and_cost_coefficient;
372
373 explicit CostClass(int evaluator_index)
374 : evaluator_index(evaluator_index) {}
375
376 friend bool operator==(const CostClass& c1, const CostClass& c2) {
377 return c1.evaluator_index == c2.evaluator_index &&
378 c1.dimension_transit_evaluator_class_and_cost_coefficient ==
379 c2.dimension_transit_evaluator_class_and_cost_coefficient;
380 }
381 template <typename H>
382 friend H AbslHashValue(H h, const CostClass& c) {
383 return H::combine(
384 std::move(h), c.evaluator_index,
385 c.dimension_transit_evaluator_class_and_cost_coefficient);
386 }
387 };
388#endif // defined(SWIG)
389
393 struct VehicleTypeContainer {
394 struct VehicleClassEntry {
395 int vehicle_class;
396 int64_t fixed_cost;
397
398 bool operator<(const VehicleClassEntry& other) const {
399 return std::tie(fixed_cost, vehicle_class) <
400 std::tie(other.fixed_cost, other.vehicle_class);
401 }
402 };
403
404 int NumTypes() const { return sorted_vehicle_classes_per_type.size(); }
405
406 int Type(int vehicle) const {
407 DCHECK_LT(vehicle, type_index_of_vehicle.size());
408 return type_index_of_vehicle[vehicle];
409 }
410
411 std::vector<int> type_index_of_vehicle;
412 // clang-format off
413 std::vector<std::set<VehicleClassEntry> > sorted_vehicle_classes_per_type;
414 std::vector<std::deque<int> > vehicles_per_vehicle_class;
415 // clang-format on
416 };
417
429 class ResourceGroup {
430 public:
432 class Attributes {
433 public:
434 Attributes();
435 Attributes(Domain start_domain, Domain end_domain);
436
437 const Domain& start_domain() const { return start_domain_; }
438 const Domain& end_domain() const { return end_domain_; }
439
440 friend bool operator==(const Attributes& a, const Attributes& b) {
441 return a.start_domain_ == b.start_domain_ &&
442 a.end_domain_ == b.end_domain_;
443 }
444 template <typename H>
445 friend H AbslHashValue(H h, const Attributes& attributes) {
446 return H::combine(std::move(h), attributes.start_domain_,
447 attributes.end_domain_);
448 }
449
450 private:
454 Domain start_domain_;
456 Domain end_domain_;
457 };
458
460 class Resource {
461 public:
462 const ResourceGroup::Attributes& GetDimensionAttributes(
463 const RoutingDimension* dimension) const;
464
465 private:
466 explicit Resource(const RoutingModel* model) : model_(model) {}
467
468 void SetDimensionAttributes(ResourceGroup::Attributes attributes,
469 const RoutingDimension* dimension);
470 const ResourceGroup::Attributes& GetDefaultAttributes() const;
471
472 const RoutingModel* const model_;
473 absl::flat_hash_map<DimensionIndex, ResourceGroup::Attributes>
474 dimension_attributes_;
475
476 friend class ResourceGroup;
477 };
478
481 int AddResource(Attributes attributes, const RoutingDimension* dimension);
482
486 void NotifyVehicleRequiresAResource(int vehicle);
487
488 const std::vector<int>& GetVehiclesRequiringAResource() const {
489 return vehicles_requiring_resource_;
490 }
491
492 bool VehicleRequiresAResource(int vehicle) const {
493 return vehicle_requires_resource_[vehicle];
494 }
495
496 void SetAllowedResourcesForVehicle(
497 int vehicle, const std::vector<int>& allowed_resource_indices) {
498 DCHECK(!model_->closed_);
499 // NOTE: As of 12/2023, an empty set of allowed resources means "all
500 // resources allowed", so we make sure the empty set isn't used here
501 // explicitly by the user to mark a vehicle as "unusable".
502 DCHECK(!allowed_resource_indices.empty());
503 DCHECK(vehicle_requires_resource_[vehicle]);
504 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
505 vehicle_allowed_resources_[vehicle].clear();
506 vehicle_allowed_resources_[vehicle].insert(
507 allowed_resource_indices.begin(), allowed_resource_indices.end());
508 }
509 void ClearAllowedResourcesForVehicle(int vehicle) {
510 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
511 vehicle_allowed_resources_[vehicle].clear();
512 }
513 const absl::flat_hash_set<int>& GetResourcesMarkedAllowedForVehicle(
514 int vehicle) const {
515 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
516 return vehicle_allowed_resources_[vehicle];
517 }
518 bool IsResourceAllowedForVehicle(int resource, int vehicle) const {
519 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
520 return vehicle_allowed_resources_[vehicle].empty() ||
521 vehicle_allowed_resources_[vehicle].contains(resource);
522 }
523
524 const std::vector<Resource>& GetResources() const { return resources_; }
525 const Resource& GetResource(int resource_index) const {
526 DCHECK_LT(resource_index, resources_.size());
527 return resources_[resource_index];
528 }
529 const absl::flat_hash_set<DimensionIndex>& GetAffectedDimensionIndices()
530 const {
531 return affected_dimension_indices_;
532 }
533
534 int GetResourceClassesCount() const {
535 return resource_indices_per_class_.size();
536 }
537 const std::vector<int>& GetResourceIndicesInClass(
538 ResourceClassIndex resource_class) const {
539 DCHECK_LT(resource_class, resource_indices_per_class_.size());
540 return resource_indices_per_class_[resource_class];
541 }
542 // clang-format off
544 GetResourceIndicesPerClass() const {
545 return resource_indices_per_class_;
546 }
547 // clang-format on
548 ResourceClassIndex GetResourceClassIndex(int resource_index) const {
549 DCHECK_LT(resource_index, resource_class_indices_.size());
550 return resource_class_indices_[resource_index];
551 }
552
553 int Size() const { return resources_.size(); }
554 int Index() const { return index_; }
555
556 private:
557 explicit ResourceGroup(const RoutingModel* model)
558 : index_(model->GetResourceGroups().size()),
559 model_(model),
560 vehicle_requires_resource_(model->vehicles(), false),
561 vehicle_allowed_resources_(model->vehicles()) {}
562
563 void ComputeResourceClasses();
564
565 const int index_;
566 const RoutingModel* const model_;
567 std::vector<Resource> resources_;
568 // Stores the ResourceClassIndex of each resource (See implementation of
569 // ComputeResourceClasses()).
570 std::vector<ResourceClassIndex> resource_class_indices_;
571 // clang-format off
573 resource_indices_per_class_;
574 // clang-format on
575
576 std::vector<bool> vehicle_requires_resource_;
577 std::vector<int> vehicles_requiring_resource_;
578 // For each vehicle, stores the set of allowed resource indices if it's
579 // restricted for that vehicle. If the set is empty for a vehicle, then any
580 // resource from this group can be assigned to it.
581 // clang-format off
582 std::vector<absl::flat_hash_set<int> > vehicle_allowed_resources_;
583 // clang-format on
585 absl::flat_hash_set<DimensionIndex> affected_dimension_indices_;
586
587 friend class RoutingModel;
588 };
589
591 struct VariableValuePair {
592 int var_index;
593 int64_t value;
594 };
595
597 class SecondaryOptimizer {
598 public:
599 SecondaryOptimizer(RoutingModel* model,
600 RoutingSearchParameters* search_parameters,
601 int64_t solve_period);
602 bool Solve(const std::vector<RoutingModel::VariableValuePair>& in_state,
603 std::vector<RoutingModel::VariableValuePair>* out_state);
604
605 private:
606 RoutingModel* const model_;
607 const RoutingSearchParameters* const search_parameters_;
608 const int64_t solve_period_ = 0;
609 int64_t call_count_ = 0;
610 Assignment* state_ = nullptr;
611 absl::flat_hash_map<IntVar*, int> var_to_index_;
612 };
613
615 static const int64_t kNoPenalty;
616
619 static const DisjunctionIndex kNoDisjunction;
620
623 static const DimensionIndex kNoDimension;
624
628 explicit RoutingModel(const RoutingIndexManager& index_manager);
629 RoutingModel(const RoutingIndexManager& index_manager,
630 const RoutingModelParameters& parameters);
631
632 // This type is neither copyable nor movable.
633 RoutingModel(const RoutingModel&) = delete;
634 RoutingModel& operator=(const RoutingModel&) = delete;
635
636 ~RoutingModel();
637
639 enum TransitEvaluatorSign {
640 kTransitEvaluatorSignUnknown = 0,
641 kTransitEvaluatorSignPositiveOrZero = 1,
642 kTransitEvaluatorSignNegativeOrZero = 2,
643 };
649 int RegisterUnaryTransitVector(std::vector<int64_t> values);
650 int RegisterUnaryTransitCallback(
651 TransitCallback1 callback,
652 TransitEvaluatorSign sign = kTransitEvaluatorSignUnknown);
653
654 int RegisterTransitMatrix(
655 std::vector<std::vector<int64_t> /*needed_for_swig*/> values);
656 int RegisterTransitCallback(
657 TransitCallback2 callback,
658 TransitEvaluatorSign sign = kTransitEvaluatorSignUnknown);
659
660 int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback);
661 const TransitCallback2& TransitCallback(int callback_index) const {
662 CHECK_LT(callback_index, transit_evaluators_.size());
663 return transit_evaluators_[callback_index];
664 }
665 const TransitCallback1& UnaryTransitCallbackOrNull(int callback_index) const {
666 CHECK_LT(callback_index, unary_transit_evaluators_.size());
667 return unary_transit_evaluators_[callback_index];
668 }
669 const VariableIndexEvaluator2& StateDependentTransitCallback(
670 int callback_index) const {
671 CHECK_LT(callback_index, state_dependent_transit_evaluators_.size());
672 return state_dependent_transit_evaluators_[callback_index];
673 }
674
676
688
697 bool AddDimension(int evaluator_index, int64_t slack_max, int64_t capacity,
698 bool fix_start_cumul_to_zero, const std::string& name);
699 bool AddDimensionWithVehicleTransits(
700 const std::vector<int>& evaluator_indices, int64_t slack_max,
701 int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
702 bool AddDimensionWithVehicleCapacity(int evaluator_index, int64_t slack_max,
703 std::vector<int64_t> vehicle_capacities,
704 bool fix_start_cumul_to_zero,
705 const std::string& name);
706 bool AddDimensionWithVehicleTransitAndCapacity(
707 const std::vector<int>& evaluator_indices, int64_t slack_max,
708 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
709 const std::string& name);
718 std::pair<int, bool> AddConstantDimensionWithSlack(
719 int64_t value, int64_t capacity, int64_t slack_max,
720 bool fix_start_cumul_to_zero, const std::string& name);
721 std::pair<int, bool> AddConstantDimension(int64_t value, int64_t capacity,
722 bool fix_start_cumul_to_zero,
723 const std::string& name) {
724 return AddConstantDimensionWithSlack(value, capacity, 0,
725 fix_start_cumul_to_zero, name);
726 }
736 std::pair<int, bool> AddVectorDimension(std::vector<int64_t> values,
737 int64_t capacity,
738 bool fix_start_cumul_to_zero,
739 const std::string& name);
749 std::pair<int, bool> AddMatrixDimension(
750 std::vector<std::vector<int64_t> /*needed_for_swig*/> values,
751 int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
758 bool AddDimensionDependentDimensionWithVehicleCapacity(
759 const std::vector<int>& pure_transits,
760 const std::vector<int>& dependent_transits,
761 const RoutingDimension* base_dimension, int64_t slack_max,
762 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
763 const std::string& name) {
764 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
765 pure_transits, dependent_transits, base_dimension, slack_max,
766 std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
767 }
768
770 bool AddDimensionDependentDimensionWithVehicleCapacity(
771 const std::vector<int>& transits, const RoutingDimension* base_dimension,
772 int64_t slack_max, std::vector<int64_t> vehicle_capacities,
773 bool fix_start_cumul_to_zero, const std::string& name);
775 bool AddDimensionDependentDimensionWithVehicleCapacity(
776 int transit, const RoutingDimension* base_dimension, int64_t slack_max,
777 int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
778 const std::string& name);
779 bool AddDimensionDependentDimensionWithVehicleCapacity(
780 int pure_transit, int dependent_transit,
781 const RoutingDimension* base_dimension, int64_t slack_max,
782 int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
783 const std::string& name);
784
786 static RoutingModel::StateDependentTransit MakeStateDependentTransit(
787 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
788 int64_t domain_end);
789
791 // TODO(user): rename.
792 std::vector<std::string> GetAllDimensionNames() const;
794 const std::vector<RoutingDimension*>& GetDimensions() const {
795 return dimensions_.get();
796 }
798 std::vector<RoutingDimension*> GetDimensionsWithSoftOrSpanCosts() const;
800 std::vector<RoutingDimension*> GetUnaryDimensions() const;
801
803 std::vector<const RoutingDimension*> GetDimensionsWithGlobalCumulOptimizers()
804 const;
805 std::vector<const RoutingDimension*> GetDimensionsWithLocalCumulOptimizers()
806 const;
807
809 bool HasGlobalCumulOptimizer(const RoutingDimension& dimension) const {
810 return GetGlobalCumulOptimizerIndex(dimension) >= 0;
811 }
812 bool HasLocalCumulOptimizer(const RoutingDimension& dimension) const {
813 return GetLocalCumulOptimizerIndex(dimension) >= 0;
814 }
817 GlobalDimensionCumulOptimizer* GetMutableGlobalCumulLPOptimizer(
818 const RoutingDimension& dimension) const;
819 GlobalDimensionCumulOptimizer* GetMutableGlobalCumulMPOptimizer(
820 const RoutingDimension& dimension) const;
821 LocalDimensionCumulOptimizer* GetMutableLocalCumulLPOptimizer(
822 const RoutingDimension& dimension) const;
823 LocalDimensionCumulOptimizer* GetMutableLocalCumulMPOptimizer(
824 const RoutingDimension& dimension) const;
825
827 bool HasDimension(absl::string_view dimension_name) const;
829 const RoutingDimension& GetDimensionOrDie(
830 const std::string& dimension_name) const;
833 RoutingDimension* GetMutableDimension(
834 const std::string& dimension_name) const;
839 void SetPrimaryConstrainedDimension(const std::string& dimension_name) {
840 DCHECK(dimension_name.empty() || HasDimension(dimension_name));
841 primary_constrained_dimension_ = dimension_name;
842 }
844 const std::string& GetPrimaryConstrainedDimension() const {
845 return primary_constrained_dimension_;
846 }
847
849 ResourceGroup* AddResourceGroup();
850 // clang-format off
851 const std::vector<std::unique_ptr<ResourceGroup> >& GetResourceGroups()
852 const {
853 return resource_groups_;
854 }
855 // clang-format on
856 ResourceGroup* GetResourceGroup(int rg_index) const {
857 DCHECK_LT(rg_index, resource_groups_.size());
858 return resource_groups_[rg_index].get();
859 }
860
863 const std::vector<int>& GetDimensionResourceGroupIndices(
864 const RoutingDimension* dimension) const;
865
868 int GetDimensionResourceGroupIndex(const RoutingDimension* dimension) const {
869 DCHECK_EQ(GetDimensionResourceGroupIndices(dimension).size(), 1);
870 return GetDimensionResourceGroupIndices(dimension)[0];
871 }
872
889 DisjunctionIndex AddDisjunction(const std::vector<int64_t>& indices,
890 int64_t penalty = kNoPenalty,
891 int64_t max_cardinality = 1);
893 const std::vector<DisjunctionIndex>& GetDisjunctionIndices(
894 int64_t index) const {
895 return index_to_disjunctions_[index];
896 }
900 template <typename F>
901 void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(
902 int64_t index, int64_t max_cardinality, F f) const {
903 for (const DisjunctionIndex disjunction : GetDisjunctionIndices(index)) {
904 if (disjunctions_[disjunction].value.max_cardinality == max_cardinality) {
905 for (const int64_t d_index : disjunctions_[disjunction].indices) {
906 f(d_index);
907 }
908 }
909 }
910 }
911#if !defined(SWIGPYTHON)
914 const std::vector<int64_t>& GetDisjunctionNodeIndices(
915 DisjunctionIndex index) const {
916 return disjunctions_[index].indices;
917 }
918#endif // !defined(SWIGPYTHON)
920 int64_t GetDisjunctionPenalty(DisjunctionIndex index) const {
921 return disjunctions_[index].value.penalty;
922 }
925 int64_t GetDisjunctionMaxCardinality(DisjunctionIndex index) const {
926 return disjunctions_[index].value.max_cardinality;
927 }
929 int GetNumberOfDisjunctions() const { return disjunctions_.size(); }
932 bool HasMandatoryDisjunctions() const;
935 bool HasMaxCardinalityConstrainedDisjunctions() const;
940 std::vector<std::pair<int64_t, int64_t>> GetPerfectBinaryDisjunctions() const;
946 void IgnoreDisjunctionsAlreadyForcedToZero();
947
951 void AddSoftSameVehicleConstraint(const std::vector<int64_t>& indices,
952 int64_t cost);
953
958 void SetAllowedVehiclesForIndex(const std::vector<int>& vehicles,
959 int64_t index);
960
962 bool IsVehicleAllowedForIndex(int vehicle, int64_t index) const {
963 return allowed_vehicles_[index].empty() ||
964 allowed_vehicles_[index].find(vehicle) !=
965 allowed_vehicles_[index].end();
966 }
967
982 // TODO(user): Remove this when model introspection detects linked nodes.
983 void AddPickupAndDelivery(int64_t pickup, int64_t delivery);
987 void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction,
988 DisjunctionIndex delivery_disjunction);
989
991 struct PickupDeliveryPosition {
993 int pd_pair_index;
996 int alternative_index;
997 };
999 const std::vector<PickupDeliveryPosition>& GetPickupPositions(
1000 int64_t node_index) const;
1002 const std::vector<PickupDeliveryPosition>& GetDeliveryPositions(
1003 int64_t node_index) const;
1005 bool IsPickup(int64_t node_index) const {
1006 return !GetPickupPositions(node_index).empty();
1007 }
1008 bool IsDelivery(int64_t node_index) const {
1009 return !GetDeliveryPositions(node_index).empty();
1010 }
1011
1014 void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy);
1015 void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy,
1016 int vehicle);
1017 PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(
1018 int vehicle) const;
1021
1022 int GetNumOfSingletonNodes() const;
1023
1024#ifndef SWIG
1026 const std::vector<PickupDeliveryPair>& GetPickupAndDeliveryPairs() const {
1027 return pickup_delivery_pairs_;
1028 }
1029 const std::vector<std::pair<DisjunctionIndex, DisjunctionIndex>>&
1030 GetPickupAndDeliveryDisjunctions() const {
1031 return pickup_delivery_disjunctions_;
1032 }
1037 const std::vector<PickupDeliveryPair>&
1038 GetImplicitUniquePickupAndDeliveryPairs() const {
1039 DCHECK(closed_);
1040 return implicit_pickup_delivery_pairs_without_alternatives_;
1041 }
1042#endif // SWIG
1054 enum VisitTypePolicy {
1056 TYPE_ADDED_TO_VEHICLE,
1061 ADDED_TYPE_REMOVED_FROM_VEHICLE,
1064 TYPE_ON_VEHICLE_UP_TO_VISIT,
1069 TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
1070 };
1071 // TODO(user): Support multiple visit types per node?
1072 void SetVisitType(int64_t index, int type, VisitTypePolicy type_policy);
1073 int GetVisitType(int64_t index) const;
1074 const std::vector<int>& GetSingleNodesOfType(int type) const;
1075 const std::vector<int>& GetPairIndicesOfType(int type) const;
1076 VisitTypePolicy GetVisitTypePolicy(int64_t index) const;
1079 // TODO(user): Reconsider the logic and potentially remove the need to
1081 void CloseVisitTypes();
1082 int GetNumberOfVisitTypes() const { return num_visit_types_; }
1083#ifndef SWIG
1084 const std::vector<std::vector<int>>& GetTopologicallySortedVisitTypes()
1085 const {
1086 DCHECK(closed_);
1087 return topologically_sorted_visit_types_;
1088 }
1089#endif // SWIG
1094 void AddHardTypeIncompatibility(int type1, int type2);
1095 void AddTemporalTypeIncompatibility(int type1, int type2);
1097 const absl::flat_hash_set<int>& GetHardTypeIncompatibilitiesOfType(
1098 int type) const;
1099 const absl::flat_hash_set<int>& GetTemporalTypeIncompatibilitiesOfType(
1100 int type) const;
1103 bool HasHardTypeIncompatibilities() const {
1104 return has_hard_type_incompatibilities_;
1105 }
1106 bool HasTemporalTypeIncompatibilities() const {
1107 return has_temporal_type_incompatibilities_;
1108 }
1119 void AddSameVehicleRequiredTypeAlternatives(
1120 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1125 void AddRequiredTypeAlternativesWhenAddingType(
1126 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1132 void AddRequiredTypeAlternativesWhenRemovingType(
1133 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1134 // clang-format off
1137 const std::vector<absl::flat_hash_set<int> >&
1138 GetSameVehicleRequiredTypeAlternativesOfType(int type) const;
1140 const std::vector<absl::flat_hash_set<int> >&
1141 GetRequiredTypeAlternativesWhenAddingType(int type) const;
1143 const std::vector<absl::flat_hash_set<int> >&
1144 GetRequiredTypeAlternativesWhenRemovingType(int type) const;
1145 // clang-format on
1148 bool HasSameVehicleTypeRequirements() const {
1149 return has_same_vehicle_type_requirements_;
1150 }
1151 bool HasTemporalTypeRequirements() const {
1152 return has_temporal_type_requirements_;
1153 }
1154
1157 bool HasTypeRegulations() const {
1158 return HasTemporalTypeIncompatibilities() ||
1159 HasHardTypeIncompatibilities() || HasSameVehicleTypeRequirements() ||
1160 HasTemporalTypeRequirements();
1161 }
1162
1167 int64_t UnperformedPenalty(int64_t var_index) const;
1171 int64_t UnperformedPenaltyOrValue(int64_t default_value,
1172 int64_t var_index) const;
1176 int64_t GetDepot() const;
1177
1182 void SetMaximumNumberOfActiveVehicles(int max_active_vehicles) {
1183 max_active_vehicles_ = max_active_vehicles;
1184 }
1186 int GetMaximumNumberOfActiveVehicles() const { return max_active_vehicles_; }
1190 void SetArcCostEvaluatorOfAllVehicles(int evaluator_index);
1192 void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle);
1195 void SetFixedCostOfAllVehicles(int64_t cost);
1197 void SetFixedCostOfVehicle(int64_t cost, int vehicle);
1201 int64_t GetFixedCostOfVehicle(int vehicle) const;
1202 // Sets the energy cost of a vehicle.
1203 // The energy used by a vehicle is the integral of the force dimension over
1204 // the distance dimension: it is the sum over nodes visited by the vehicle of
1205 // force.CumulVar(Next(node)) * distance.TransitVar(node).
1206 // The energy cost of a vehicle is linear in the energy used by the vehicle,
1207 // this call sets the coefficient to unit_cost, it is zero if unset.
1208 void SetPathEnergyCostOfVehicle(const std::string& force,
1209 const std::string& distance,
1210 int64_t unit_cost, int vehicle);
1211
1227 void SetAmortizedCostFactorsOfAllVehicles(int64_t linear_cost_factor,
1228 int64_t quadratic_cost_factor);
1230 void SetAmortizedCostFactorsOfVehicle(int64_t linear_cost_factor,
1231 int64_t quadratic_cost_factor,
1232 int vehicle);
1233
1234 const std::vector<int64_t>& GetAmortizedLinearCostFactorOfVehicles() const {
1235 return linear_cost_factor_of_vehicle_;
1236 }
1237 const std::vector<int64_t>& GetAmortizedQuadraticCostFactorOfVehicles()
1238 const {
1239 return quadratic_cost_factor_of_vehicle_;
1240 }
1241
1242 void SetVehicleUsedWhenEmpty(bool is_used, int vehicle) {
1243 DCHECK_LT(vehicle, vehicles_);
1244 vehicle_used_when_empty_[vehicle] = is_used;
1245 }
1246
1247 bool IsVehicleUsedWhenEmpty(int vehicle) const {
1248 DCHECK_LT(vehicle, vehicles_);
1249 return vehicle_used_when_empty_[vehicle];
1250 }
1251
1254#ifndef SWIG
1255 const Solver::IndexEvaluator2& first_solution_evaluator() const {
1256 return first_solution_evaluator_;
1257 }
1258#endif
1260 void SetFirstSolutionEvaluator(Solver::IndexEvaluator2 evaluator) {
1261 first_solution_evaluator_ = std::move(evaluator);
1262 }
1265 void AddLocalSearchOperator(LocalSearchOperator* ls_operator);
1267 void AddSearchMonitor(SearchMonitor* monitor);
1274 void AddAtSolutionCallback(std::function<void()> callback,
1275 bool track_unchecked_neighbors = false);
1280 void AddVariableMinimizedByFinalizer(IntVar* var);
1283 void AddVariableMaximizedByFinalizer(IntVar* var);
1286 void AddWeightedVariableMinimizedByFinalizer(IntVar* var, int64_t cost);
1289 void AddWeightedVariableMaximizedByFinalizer(IntVar* var, int64_t cost);
1292 void AddVariableTargetToFinalizer(IntVar* var, int64_t target);
1295 void AddWeightedVariableTargetToFinalizer(IntVar* var, int64_t target,
1296 int64_t cost);
1303 void CloseModel();
1306 void CloseModelWithParameters(
1307 const RoutingSearchParameters& search_parameters);
1314 const Assignment* Solve(const Assignment* assignment = nullptr);
1322 const Assignment* SolveWithParameters(
1323 const RoutingSearchParameters& search_parameters,
1324 std::vector<const Assignment*>* solutions = nullptr);
1327 const Assignment* SolveFromAssignmentWithParameters(
1328 const Assignment* assignment,
1329 const RoutingSearchParameters& search_parameters,
1330 std::vector<const Assignment*>* solutions = nullptr);
1335 const Assignment* FastSolveFromAssignmentWithParameters(
1336 const Assignment* assignment,
1337 const RoutingSearchParameters& search_parameters,
1338 bool check_solution_in_cp,
1339 absl::flat_hash_set<IntVar*>* touched = nullptr);
1342 const Assignment* SolveFromAssignmentsWithParameters(
1343 const std::vector<const Assignment*>& assignments,
1344 const RoutingSearchParameters& search_parameters,
1345 std::vector<const Assignment*>* solutions = nullptr);
1348 const Assignment* SolveWithIteratedLocalSearch(
1349 const RoutingSearchParameters& search_parameters);
1355 void SetAssignmentFromOtherModelAssignment(
1356 Assignment* target_assignment, const RoutingModel* source_model,
1357 const Assignment* source_assignment);
1363 // TODO(user): Add support for non-homogeneous costs and disjunctions.
1364 int64_t ComputeLowerBound();
1367 int64_t objective_lower_bound() const { return objective_lower_bound_; }
1369 Status status() const { return status_; }
1371 bool enable_deep_serialization() const { return enable_deep_serialization_; }
1380 IntVar* ApplyLocks(const std::vector<int64_t>& locks);
1389 bool ApplyLocksToAllVehicles(const std::vector<std::vector<int64_t>>& locks,
1390 bool close_routes);
1395 const Assignment* PreAssignment() const { return preassignment_; }
1396 Assignment* MutablePreAssignment() { return preassignment_; }
1400 bool WriteAssignment(const std::string& file_name) const;
1404 Assignment* ReadAssignment(const std::string& file_name);
1407 Assignment* RestoreAssignment(const Assignment& solution);
1413 Assignment* ReadAssignmentFromRoutes(
1414 const std::vector<std::vector<int64_t>>& routes,
1415 bool ignore_inactive_indices);
1432 bool RoutesToAssignment(const std::vector<std::vector<int64_t>>& routes,
1433 bool ignore_inactive_indices, bool close_routes,
1434 Assignment* assignment) const;
1438 void AssignmentToRoutes(const Assignment& assignment,
1439 std::vector<std::vector<int64_t>>* routes) const;
1444#ifndef SWIG
1445 std::vector<std::vector<int64_t>> GetRoutesFromAssignment(
1446 const Assignment& assignment);
1447#endif
1465 Assignment* CompactAssignment(const Assignment& assignment) const;
1469 Assignment* CompactAndCheckAssignment(const Assignment& assignment) const;
1471 void AddToAssignment(IntVar* var);
1472 void AddIntervalToAssignment(IntervalVar* interval);
1483 const Assignment* PackCumulsOfOptimizerDimensionsFromAssignment(
1484 const Assignment* original_assignment, absl::Duration duration_limit,
1485 bool* time_limit_was_reached = nullptr);
1488 struct RouteDimensionTravelInfo {
1490 struct TransitionInfo {
1494 // TODO(user): Adjust the inlined vector sizes based on experiments.
1495 struct PiecewiseLinearFormulation {
1497 absl::InlinedVector<int64_t, 8> x_anchors;
1503 absl::InlinedVector<int64_t, 8> y_anchors;
1504
1505 std::string DebugString(std::string line_prefix = "") const;
1506 };
1507
1510 PiecewiseLinearFormulation travel_start_dependent_travel;
1511
1515 PiecewiseLinearFormulation travel_compression_cost;
1516
1520 int64_t pre_travel_transit_value;
1521 int64_t post_travel_transit_value;
1522
1525 int64_t compressed_travel_value_lower_bound;
1526
1531 int64_t travel_value_upper_bound;
1532
1533 std::string DebugString(std::string line_prefix = "") const;
1534 };
1535
1538 std::vector<TransitionInfo> transition_info;
1540 int64_t travel_cost_coefficient;
1541
1542 std::string DebugString(std::string line_prefix = "") const;
1543 };
1544
1545#ifndef SWIG
1546 // TODO(user): Revisit if coordinates are added to the RoutingModel class.
1547 void SetSweepArranger(SweepArranger* sweep_arranger);
1549 SweepArranger* sweep_arranger() const;
1550#endif
1551 class NodeNeighborsByCostClass {
1552 public:
1553 NodeNeighborsByCostClass() = default;
1554
1557 void ComputeNeighbors(const RoutingModel& routing_model, int num_neighbors,
1558 bool add_vehicle_starts_to_neighbors);
1560 const std::vector<int>& GetNeighborsOfNodeForCostClass(
1561 int cost_class, int node_index) const {
1562 return all_nodes_.empty() ? node_index_to_neighbors_by_cost_class_
1563 [node_index][cost_class]
1564 ->PositionsSetAtLeastOnce()
1565 : all_nodes_;
1566 }
1567
1568 private:
1569 std::vector<std::vector<std::unique_ptr<SparseBitset<int>>>>
1570 node_index_to_neighbors_by_cost_class_;
1571 std::vector<int> all_nodes_;
1572 };
1573
1578 const NodeNeighborsByCostClass* GetOrCreateNodeNeighborsByCostClass(
1579 double neighbors_ratio, int64_t min_neighbors,
1580 double& neighbors_ratio_used,
1581 bool add_vehicle_starts_to_neighbors = true);
1584 const NodeNeighborsByCostClass* GetOrCreateNodeNeighborsByCostClass(
1585 int num_neighbors, bool add_vehicle_starts_to_neighbors = true);
1591 void AddLocalSearchFilter(LocalSearchFilter* filter) {
1592 CHECK(filter != nullptr);
1593 if (closed_) {
1594 LOG(WARNING) << "Model is closed, filter addition will be ignored.";
1595 }
1596 extra_filters_.push_back({filter, LocalSearchFilterManager::kRelax});
1597 extra_filters_.push_back({filter, LocalSearchFilterManager::kAccept});
1598 }
1599
1602 int64_t Start(int vehicle) const { return paths_metadata_.Starts()[vehicle]; }
1604 int64_t End(int vehicle) const { return paths_metadata_.Ends()[vehicle]; }
1606 bool IsStart(int64_t index) const { return paths_metadata_.IsStart(index); }
1608 bool IsEnd(int64_t index) const { return paths_metadata_.IsEnd(index); }
1611 int VehicleIndex(int64_t index) const {
1612 return paths_metadata_.GetPath(index);
1613 }
1617 int64_t Next(const Assignment& assignment, int64_t index) const;
1619 bool IsVehicleUsed(const Assignment& assignment, int vehicle) const;
1620
1621#if !defined(SWIGPYTHON)
1624 const std::vector<IntVar*>& Nexts() const { return nexts_; }
1627 const std::vector<IntVar*>& VehicleVars() const { return vehicle_vars_; }
1631 const std::vector<IntVar*>& ResourceVars(int resource_group) const {
1632 return resource_vars_[resource_group];
1633 }
1634#endif
1637 IntVar* NextVar(int64_t index) const { return nexts_[index]; }
1639 IntVar* ActiveVar(int64_t index) const { return active_[index]; }
1642 IntVar* ActiveVehicleVar(int vehicle) const {
1643 return vehicle_active_[vehicle];
1644 }
1648 IntVar* VehicleRouteConsideredVar(int vehicle) const {
1649 return vehicle_route_considered_[vehicle];
1650 }
1653 IntVar* VehicleVar(int64_t index) const { return vehicle_vars_[index]; }
1657 IntVar* ResourceVar(int vehicle, int resource_group) const {
1658 DCHECK_LT(resource_group, resource_vars_.size());
1659 DCHECK_LT(vehicle, resource_vars_[resource_group].size());
1660 return resource_vars_[resource_group][vehicle];
1661 }
1663 IntVar* CostVar() const { return cost_; }
1664
1667 int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index,
1668 int64_t vehicle) const;
1670 bool CostsAreHomogeneousAcrossVehicles() const {
1671 return costs_are_homogeneous_across_vehicles_;
1672 }
1675 int64_t GetHomogeneousCost(int64_t from_index, int64_t to_index) const {
1676 return GetArcCostForVehicle(from_index, to_index, /*vehicle=*/0);
1677 }
1680 int64_t GetArcCostForFirstSolution(int64_t from_index,
1681 int64_t to_index) const;
1688 int64_t GetArcCostForClass(int64_t from_index, int64_t to_index,
1689 int64_t /*CostClassIndex*/ cost_class_index) const;
1691 CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const {
1692 DCHECK(closed_);
1693 DCHECK_GE(vehicle, 0);
1694 DCHECK_LT(vehicle, cost_class_index_of_vehicle_.size());
1695 DCHECK_GE(cost_class_index_of_vehicle_[vehicle], 0);
1696 return cost_class_index_of_vehicle_[vehicle];
1697 }
1700 bool HasVehicleWithCostClassIndex(CostClassIndex cost_class_index) const {
1701 DCHECK(closed_);
1702 if (cost_class_index == kCostClassIndexOfZeroCost) {
1703 return has_vehicle_with_zero_cost_class_;
1704 }
1705 return cost_class_index < cost_classes_.size();
1706 }
1708 int GetCostClassesCount() const { return cost_classes_.size(); }
1710 int GetNonZeroCostClassesCount() const {
1711 return std::max(0, GetCostClassesCount() - 1);
1712 }
1713 VehicleClassIndex GetVehicleClassIndexOfVehicle(int64_t vehicle) const {
1714 DCHECK(closed_);
1715 return vehicle_class_index_of_vehicle_[vehicle];
1716 }
1719 int GetVehicleOfClass(VehicleClassIndex vehicle_class) const {
1720 DCHECK(closed_);
1721 const RoutingModel::VehicleTypeContainer& vehicle_type_container =
1722 GetVehicleTypeContainer();
1723 if (vehicle_class.value() >= GetVehicleClassesCount() ||
1724 vehicle_type_container.vehicles_per_vehicle_class[vehicle_class.value()]
1725 .empty()) {
1726 return -1;
1727 }
1728 return vehicle_type_container
1729 .vehicles_per_vehicle_class[vehicle_class.value()]
1730 .front();
1731 }
1733 int GetVehicleClassesCount() const { return num_vehicle_classes_; }
1735 const std::vector<int>& GetSameVehicleIndicesOfIndex(int node) const {
1736 DCHECK(closed_);
1737 return same_vehicle_groups_[same_vehicle_group_[node]];
1738 }
1739
1740 const VehicleTypeContainer& GetVehicleTypeContainer() const {
1741 DCHECK(closed_);
1742 return vehicle_type_container_;
1743 }
1744
1763 bool ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1, int64_t to2);
1768 std::string DebugOutputAssignment(
1769 const Assignment& solution_assignment,
1770 const std::string& dimension_to_print) const;
1776#ifndef SWIG
1777 std::vector<std::vector<std::pair<int64_t, int64_t>>> GetCumulBounds(
1778 const Assignment& solution_assignment, const RoutingDimension& dimension);
1779#endif
1781 bool CheckIfAssignmentIsFeasible(const Assignment& assignment,
1782 bool call_at_solution_monitors);
1785 Solver* solver() const { return solver_.get(); }
1786
1789 bool CheckLimit(absl::Duration offset = absl::ZeroDuration()) {
1790 DCHECK(limit_ != nullptr);
1791 return limit_->CheckWithOffset(offset);
1792 }
1793
1795 absl::Duration RemainingTime() const {
1796 DCHECK(limit_ != nullptr);
1797 return limit_->AbsoluteSolverDeadline() - solver_->Now();
1798 }
1799
1801 void UpdateTimeLimit(absl::Duration time_limit) {
1802 RegularLimit* limit = GetOrCreateLimit();
1803 limit->UpdateLimits(time_limit, std::numeric_limits<int64_t>::max(),
1804 std::numeric_limits<int64_t>::max(),
1805 limit->solutions());
1806 }
1807
1809 absl::Duration TimeBuffer() const { return time_buffer_; }
1810
1812 std::atomic<bool>* GetMutableCPSatInterrupt() { return &interrupt_cp_sat_; }
1814 std::atomic<bool>* GetMutableCPInterrupt() { return &interrupt_cp_; }
1816 void CancelSearch() {
1817 interrupt_cp_sat_ = true;
1818 interrupt_cp_ = true;
1819 }
1820
1823 int nodes() const { return nodes_; }
1825 int vehicles() const { return vehicles_; }
1827 int64_t Size() const { return nodes_ + vehicles_ - start_end_count_; }
1828
1831 int64_t GetNumberOfDecisionsInFirstSolution(
1832 const RoutingSearchParameters& search_parameters) const;
1833 int64_t GetNumberOfRejectsInFirstSolution(
1834 const RoutingSearchParameters& search_parameters) const;
1836 operations_research::FirstSolutionStrategy::Value
1837 GetAutomaticFirstSolutionStrategy() const {
1838 return automatic_first_solution_strategy_;
1839 }
1840
1842 bool IsMatchingModel() const;
1843
1846 bool AreRoutesInterdependent(const RoutingSearchParameters& parameters) const;
1847
1848#ifndef SWIG
1851 using GetTabuVarsCallback =
1852 std::function<std::vector<operations_research::IntVar*>(RoutingModel*)>;
1853
1854 void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback);
1855#endif // SWIG
1856
1858 // TODO(user): Find a way to test and restrict the access at the same time.
1870 DecisionBuilder* MakeGuidedSlackFinalizer(
1871 const RoutingDimension* dimension,
1872 std::function<int64_t(int64_t)> initializer);
1873#ifndef SWIG
1874 // TODO(user): MakeGreedyDescentLSOperator is too general for routing.h.
1879 static std::unique_ptr<LocalSearchOperator> MakeGreedyDescentLSOperator(
1880 std::vector<IntVar*> variables);
1881 // Read access to currently registered search monitors.
1882 const std::vector<SearchMonitor*>& GetSearchMonitors() const {
1883 return monitors_;
1884 }
1885#endif
1899 DecisionBuilder* MakeSelfDependentDimensionFinalizer(
1900 const RoutingDimension* dimension);
1901
1902 const PathsMetadata& GetPathsMetadata() const { return paths_metadata_; }
1903#ifndef SWIG
1904 BinCapacities* GetBinCapacities() { return bin_capacities_.get(); }
1905
1908 void SetSecondaryModel(RoutingModel* secondary_model,
1909 RoutingSearchParameters secondary_parameters) {
1910 DCHECK(!closed_);
1911 secondary_model_ = secondary_model;
1912 secondary_parameters_ = std::move(secondary_parameters);
1913 }
1914#endif // SWIG
1915
1916 private:
1918 enum RoutingLocalSearchOperator {
1919 RELOCATE = 0,
1920 RELOCATE_PAIR,
1921 LIGHT_RELOCATE_PAIR,
1922 RELOCATE_NEIGHBORS,
1923 EXCHANGE,
1924 EXCHANGE_PAIR,
1925 CROSS,
1926 CROSS_EXCHANGE,
1927 TWO_OPT,
1928 OR_OPT,
1929 GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
1930 LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
1931 GLOBAL_CHEAPEST_INSERTION_PATH_LNS,
1932 LOCAL_CHEAPEST_INSERTION_PATH_LNS,
1933 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
1934 GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
1935 LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
1936 RELOCATE_EXPENSIVE_CHAIN,
1937 LIN_KERNIGHAN,
1938 TSP_OPT,
1939 MAKE_ACTIVE,
1940 RELOCATE_AND_MAKE_ACTIVE,
1941 MAKE_ACTIVE_AND_RELOCATE,
1942 MAKE_INACTIVE,
1943 MAKE_CHAIN_INACTIVE,
1944 SWAP_ACTIVE,
1945 EXTENDED_SWAP_ACTIVE,
1946 SHORTEST_PATH_SWAP_ACTIVE,
1947 NODE_PAIR_SWAP,
1948 PATH_LNS,
1949 FULL_PATH_LNS,
1950 TSP_LNS,
1951 INACTIVE_LNS,
1952 EXCHANGE_RELOCATE_PAIR,
1953 RELOCATE_SUBTRIP,
1954 EXCHANGE_SUBTRIP,
1955 LOCAL_SEARCH_OPERATOR_COUNTER
1956 };
1957
1961 template <typename T>
1962 struct ValuedNodes {
1963 std::vector<int64_t> indices;
1964 T value;
1965 };
1966 struct DisjunctionValues {
1967 int64_t penalty;
1968 int64_t max_cardinality;
1969 };
1970 typedef ValuedNodes<DisjunctionValues> Disjunction;
1971
1974 struct CostCacheElement {
1980 int index;
1981 CostClassIndex cost_class_index;
1982 int64_t cost;
1983 };
1984
1987 template <class DimensionCumulOptimizer>
1988 struct DimensionCumulOptimizers {
1989 std::unique_ptr<DimensionCumulOptimizer> lp_optimizer;
1990 std::unique_ptr<DimensionCumulOptimizer> mp_optimizer;
1991 };
1992
1994 void Initialize();
1995 void AddNoCycleConstraintInternal();
1996 bool AddDimensionWithCapacityInternal(
1997 const std::vector<int>& evaluator_indices, int64_t slack_max,
1998 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1999 const std::string& name);
2000 bool AddDimensionDependentDimensionWithVehicleCapacityInternal(
2001 const std::vector<int>& pure_transits,
2002 const std::vector<int>& dependent_transits,
2003 const RoutingDimension* base_dimension, int64_t slack_max,
2004 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
2005 const std::string& name);
2006 bool InitializeDimensionInternal(
2007 const std::vector<int>& evaluator_indices,
2008 const std::vector<int>& state_dependent_evaluator_indices,
2009 int64_t slack_max, bool fix_start_cumul_to_zero,
2010 RoutingDimension* dimension);
2011 DimensionIndex GetDimensionIndex(const std::string& dimension_name) const;
2012
2040 void StoreDimensionCumulOptimizers(const RoutingSearchParameters& parameters);
2041
2047 void FinalizeAllowedVehicles();
2048
2049 void ComputeCostClasses(const RoutingSearchParameters& parameters);
2050 void ComputeVehicleClasses();
2058 void ComputeVehicleTypes();
2060 void ComputeResourceClasses();
2070 void FinalizeVisitTypes();
2071 // Called by FinalizeVisitTypes() to setup topologically_sorted_visit_types_.
2072 void TopologicallySortVisitTypes();
2073 int64_t GetArcCostForClassInternal(int64_t from_index, int64_t to_index,
2074 CostClassIndex cost_class_index) const;
2075 void AppendHomogeneousArcCosts(const RoutingSearchParameters& parameters,
2076 int node_index,
2077 std::vector<IntVar*>* cost_elements);
2078 void AppendArcCosts(const RoutingSearchParameters& parameters, int node_index,
2079 std::vector<IntVar*>* cost_elements);
2080 Assignment* DoRestoreAssignment();
2081 static const CostClassIndex kCostClassIndexOfZeroCost;
2082 int64_t SafeGetCostClassInt64OfVehicle(int64_t vehicle) const {
2083 DCHECK_LT(0, vehicles_);
2084 return (vehicle >= 0 ? GetCostClassIndexOfVehicle(vehicle)
2085 : kCostClassIndexOfZeroCost)
2086 .value();
2087 }
2088 int64_t GetDimensionTransitCostSum(int64_t i, int64_t j,
2089 const CostClass& cost_class) const;
2091 IntVar* CreateDisjunction(DisjunctionIndex disjunction);
2093 void AddPickupAndDeliverySetsInternal(const std::vector<int64_t>& pickups,
2094 const std::vector<int64_t>& deliveries);
2097 IntVar* CreateSameVehicleCost(int vehicle_index);
2100 int FindNextActive(int index, const std::vector<int64_t>& indices) const;
2101
2104 bool RouteCanBeUsedByVehicle(const Assignment& assignment, int start_index,
2105 int vehicle) const;
2113 bool ReplaceUnusedVehicle(int unused_vehicle, int active_vehicle,
2114 Assignment* compact_assignment) const;
2115
2116 void QuietCloseModel();
2117 void QuietCloseModelWithParameters(
2118 const RoutingSearchParameters& parameters) {
2119 if (!closed_) {
2120 CloseModelWithParameters(parameters);
2121 }
2122 }
2123
2125 bool SolveMatchingModel(Assignment* assignment,
2126 const RoutingSearchParameters& parameters);
2127#ifndef SWIG
2129 bool AppendAssignmentIfFeasible(
2130 const Assignment& assignment,
2131 std::vector<std::unique_ptr<Assignment>>* assignments,
2132 bool call_at_solution_monitors = true);
2133#endif
2135 void LogSolution(const RoutingSearchParameters& parameters,
2136 absl::string_view description, int64_t solution_cost,
2137 int64_t start_time_ms);
2140 Assignment* CompactAssignmentInternal(const Assignment& assignment,
2141 bool check_compact_assignment) const;
2144 std::string FindErrorInSearchParametersForModel(
2145 const RoutingSearchParameters& search_parameters) const;
2147 void SetupSearch(const RoutingSearchParameters& search_parameters);
2149 // TODO(user): Document each auxiliary method.
2150 Assignment* GetOrCreateAssignment();
2151 Assignment* GetOrCreateTmpAssignment();
2152 RegularLimit* GetOrCreateLimit();
2153 RegularLimit* GetOrCreateCumulativeLimit();
2154 RegularLimit* GetOrCreateLocalSearchLimit();
2155 RegularLimit* GetOrCreateLargeNeighborhoodSearchLimit();
2156 RegularLimit* GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
2157 LocalSearchOperator* CreateInsertionOperator();
2158 LocalSearchOperator* CreateMakeInactiveOperator();
2159#ifndef SWIG
2160 template <class T>
2161 LocalSearchOperator* CreateCPOperator(const T& operator_factory) {
2162 return operator_factory(solver_.get(), nexts_,
2163 CostsAreHomogeneousAcrossVehicles()
2164 ? std::vector<IntVar*>()
2165 : vehicle_vars_,
2166 vehicle_start_class_callback_);
2167 }
2168 template <class T>
2169 LocalSearchOperator* CreateCPOperator() {
2170 return CreateCPOperator(MakeLocalSearchOperator<T>);
2171 }
2172 using NeighborAccessor = std::function<const std::vector<int>&(int, int)>;
2173 template <class T>
2174 LocalSearchOperator* CreateCPOperatorWithNeighbors(
2175 NeighborAccessor get_neighbors) {
2176 return CreateCPOperatorWithNeighbors(
2177 MakeLocalSearchOperatorWithNeighbors<T>, std::move(get_neighbors));
2178 }
2179 template <class T>
2180 LocalSearchOperator* CreateOperatorWithNeighborsRatio(
2181 int neighbors_ratio_used, NeighborAccessor get_neighbors) {
2182 return neighbors_ratio_used == 1
2183 ? CreateCPOperator<T>()
2184 : CreateCPOperatorWithNeighbors<T>(std::move(get_neighbors));
2185 }
2186 template <class T>
2187 LocalSearchOperator* CreateCPOperatorWithNeighbors(
2188 const T& operator_factory, NeighborAccessor get_neighbors) {
2189 return operator_factory(
2190 solver_.get(), nexts_,
2191 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
2192 : vehicle_vars_,
2193 vehicle_start_class_callback_, std::move(get_neighbors));
2194 }
2195 template <class T, class Arg>
2196 LocalSearchOperator* CreateOperator(const Arg& arg) {
2197 return solver_->RevAlloc(new T(nexts_,
2198 CostsAreHomogeneousAcrossVehicles()
2199 ? std::vector<IntVar*>()
2200 : vehicle_vars_,
2201 vehicle_start_class_callback_, arg));
2202 }
2203 template <class T, class Arg>
2204 LocalSearchOperator* CreateOperatorWithNeighbors(
2205 NeighborAccessor get_neighbors, const Arg& arg) {
2206 return solver_->RevAlloc(
2207 new T(nexts_,
2208 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
2209 : vehicle_vars_,
2210 vehicle_start_class_callback_, std::move(get_neighbors), arg));
2211 }
2212 template <class T, class Arg>
2213 LocalSearchOperator* CreateOperatorWithNeighborsRatio(
2214 int neighbors_ratio_used, NeighborAccessor get_neighbors,
2215 const Arg& arg) {
2216 return neighbors_ratio_used == 1
2217 ? CreateOperator<T>(arg)
2218 : CreateOperatorWithNeighbors<T>(std::move(get_neighbors), arg);
2219 }
2220 template <class T, class Arg1, class MoveableArg2>
2221 LocalSearchOperator* CreateOperator(const Arg1& arg1, MoveableArg2 arg2) {
2222 return solver_->RevAlloc(
2223 new T(nexts_,
2224 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
2225 : vehicle_vars_,
2226 vehicle_start_class_callback_, arg1, std::move(arg2)));
2227 }
2228 template <class T, class Arg1, class MoveableArg2>
2229 LocalSearchOperator* CreateOperatorWithNeighborsRatio(
2230 int neighbors_ratio_used, NeighborAccessor get_neighbors,
2231 const Arg1& arg1, MoveableArg2 arg2) {
2232 return neighbors_ratio_used == 1
2233 ? CreateOperator<T>(arg1, std::move(arg2))
2234 : solver_->RevAlloc(new T(nexts_,
2235 CostsAreHomogeneousAcrossVehicles()
2236 ? std::vector<IntVar*>()
2237 : vehicle_vars_,
2238 vehicle_start_class_callback_,
2239 std::move(get_neighbors), arg1,
2240 std::move(arg2)));
2241 }
2242 template <class T>
2243 LocalSearchOperator* CreatePairOperator() {
2244 return CreateOperator<T>(pickup_delivery_pairs_);
2245 }
2246 template <class T>
2247 LocalSearchOperator* CreatePairOperator(int neighbors_ratio_used,
2248 NeighborAccessor get_neighbors) {
2249 return neighbors_ratio_used == 1
2250 ? CreateOperator<T>(pickup_delivery_pairs_)
2251 : CreateOperatorWithNeighbors<T>(std::move(get_neighbors),
2252 pickup_delivery_pairs_);
2253 }
2254#endif // SWIG
2255 void CreateNeighborhoodOperators(const RoutingSearchParameters& parameters);
2256 LocalSearchOperator* ConcatenateOperators(
2257 const RoutingSearchParameters& search_parameters,
2258 const std::vector<LocalSearchOperator*>& operators) const;
2259 LocalSearchOperator* GetNeighborhoodOperators(
2260 const RoutingSearchParameters& search_parameters,
2261 const absl::flat_hash_set<RoutingLocalSearchOperator>&
2262 operators_to_consider) const;
2263
2264 struct FilterOptions {
2265 bool filter_objective;
2266 bool filter_with_cp_solver;
2267
2268 bool operator==(const FilterOptions& other) const {
2269 return other.filter_objective == filter_objective &&
2270 other.filter_with_cp_solver == filter_with_cp_solver;
2271 }
2272 template <typename H>
2273 friend H AbslHashValue(H h, const FilterOptions& options) {
2274 return H::combine(std::move(h), options.filter_objective,
2275 options.filter_with_cp_solver);
2276 }
2277 };
2278 std::vector<LocalSearchFilterManager::FilterEvent> CreateLocalSearchFilters(
2279 const RoutingSearchParameters& parameters, const FilterOptions& options);
2280 LocalSearchFilterManager* GetOrCreateLocalSearchFilterManager(
2281 const RoutingSearchParameters& parameters, const FilterOptions& options);
2282 DecisionBuilder* CreateSolutionFinalizer(
2283 const RoutingSearchParameters& parameters, SearchLimit* lns_limit);
2284 void CreateFirstSolutionDecisionBuilders(
2285 const RoutingSearchParameters& search_parameters);
2286 DecisionBuilder* GetFirstSolutionDecisionBuilder(
2287 const RoutingSearchParameters& search_parameters) const;
2288 IntVarFilteredDecisionBuilder* GetFilteredFirstSolutionDecisionBuilderOrNull(
2289 const RoutingSearchParameters& parameters) const;
2290#ifndef SWIG
2291 template <typename Heuristic, typename... Args>
2292 IntVarFilteredDecisionBuilder* CreateIntVarFilteredDecisionBuilder(
2293 const Args&... args);
2294#endif
2295 LocalSearchPhaseParameters* CreateLocalSearchParameters(
2296 const RoutingSearchParameters& search_parameters, bool secondary_ls);
2297 DecisionBuilder* CreatePrimaryLocalSearchDecisionBuilder(
2298 const RoutingSearchParameters& search_parameters);
2299 void SetupDecisionBuilders(const RoutingSearchParameters& search_parameters);
2300 void SetupMetaheuristics(const RoutingSearchParameters& search_parameters);
2301 void SetupAssignmentCollector(
2302 const RoutingSearchParameters& search_parameters);
2303 void SetupTrace(const RoutingSearchParameters& search_parameters);
2304 void SetupImprovementLimit(const RoutingSearchParameters& search_parameters);
2305 void SetupSearchMonitors(const RoutingSearchParameters& search_parameters);
2306 bool UsesLightPropagation(
2307 const RoutingSearchParameters& search_parameters) const;
2308 GetTabuVarsCallback tabu_var_callback_;
2309
2310 // Detects implicit pickup delivery pairs. These pairs are
2311 // non-pickup/delivery pairs for which there exists a unary dimension such
2312 // that the demand d of the implicit pickup is positive and the demand of the
2313 // implicit delivery is equal to -d.
2314 void DetectImplicitPickupAndDeliveries();
2315
2316 int GetVehicleStartClass(int64_t start) const;
2317
2318 void InitSameVehicleGroups(int number_of_groups) {
2319 same_vehicle_group_.assign(Size(), 0);
2320 same_vehicle_groups_.assign(number_of_groups, {});
2321 }
2322 void SetSameVehicleGroup(int index, int group) {
2323 same_vehicle_group_[index] = group;
2324 same_vehicle_groups_[group].push_back(index);
2325 }
2326
2329 int GetGlobalCumulOptimizerIndex(const RoutingDimension& dimension) const;
2330 int GetLocalCumulOptimizerIndex(const RoutingDimension& dimension) const;
2331
2333 std::unique_ptr<Solver> solver_;
2334 int nodes_;
2335 int vehicles_;
2336 int max_active_vehicles_;
2337 Constraint* no_cycle_constraint_ = nullptr;
2339 std::vector<IntVar*> nexts_;
2340 std::vector<IntVar*> vehicle_vars_;
2341 std::vector<IntVar*> active_;
2347 // clang-format off
2348 std::vector<std::vector<IntVar*> > resource_vars_;
2349 // clang-format on
2350 // The following vectors are indexed by vehicle index.
2351 std::vector<IntVar*> vehicle_active_;
2352 std::vector<IntVar*> vehicle_route_considered_;
2357 std::vector<IntVar*> is_bound_to_end_;
2358 mutable RevSwitch is_bound_to_end_ct_added_;
2360 absl::flat_hash_map<std::string, DimensionIndex> dimension_name_to_index_;
2366 // clang-format off
2367 std::vector<std::unique_ptr<ResourceGroup> > resource_groups_;
2370 dimension_resource_group_indices_;
2371
2375 std::vector<DimensionCumulOptimizers<GlobalDimensionCumulOptimizer> >
2376 global_dimension_optimizers_;
2377 util_intops::StrongVector<DimensionIndex, int> global_optimizer_index_;
2378 std::vector<DimensionCumulOptimizers<LocalDimensionCumulOptimizer> >
2379 local_dimension_optimizers_;
2381 // clang-format on
2382 std::string primary_constrained_dimension_;
2384 IntVar* cost_ = nullptr;
2385 std::vector<int> vehicle_to_transit_cost_;
2386 std::vector<int64_t> fixed_cost_of_vehicle_;
2387 std::vector<CostClassIndex> cost_class_index_of_vehicle_;
2388 bool has_vehicle_with_zero_cost_class_;
2389 std::vector<int64_t> linear_cost_factor_of_vehicle_;
2390 std::vector<int64_t> quadratic_cost_factor_of_vehicle_;
2391 bool vehicle_amortized_cost_factors_set_;
2403 std::vector<bool> vehicle_used_when_empty_;
2404#ifndef SWIG
2405 absl::flat_hash_map<std::pair<std::string, std::string>, std::vector<int64_t>,
2406 absl::Hash<std::pair<std::string, std::string>>>
2407 force_distance_to_vehicle_unit_costs_;
2409#endif // SWIG
2410 bool costs_are_homogeneous_across_vehicles_;
2411 bool cache_callbacks_;
2412 mutable std::vector<CostCacheElement> cost_cache_;
2413 std::vector<VehicleClassIndex> vehicle_class_index_of_vehicle_;
2414 int num_vehicle_classes_;
2415
2416 VehicleTypeContainer vehicle_type_container_;
2417 std::function<int(int64_t)> vehicle_start_class_callback_;
2420 // clang-format off
2421 std::vector<std::vector<DisjunctionIndex> > index_to_disjunctions_;
2423 std::vector<ValuedNodes<int64_t> > same_vehicle_costs_;
2425#ifndef SWIG
2426 std::vector<absl::flat_hash_set<int>> allowed_vehicles_;
2427#endif // SWIG
2429 std::vector<PickupDeliveryPair> pickup_delivery_pairs_;
2430 std::vector<PickupDeliveryPair>
2431 implicit_pickup_delivery_pairs_without_alternatives_;
2432 std::vector<std::pair<DisjunctionIndex, DisjunctionIndex> >
2433 pickup_delivery_disjunctions_;
2434 // If node_index is a pickup, index_to_pickup_positions_[node_index] contains
2435 // all the PickupDeliveryPosition {pickup_delivery_index, alternative_index}
2436 // such that (pickup_delivery_pairs_[pickup_delivery_index]
2437 // .pickup_alternatives)[alternative_index] == node_index
2438 std::vector<std::vector<PickupDeliveryPosition>> index_to_pickup_positions_;
2439 // Same as above for deliveries.
2440 std::vector<std::vector<PickupDeliveryPosition>> index_to_delivery_positions_;
2441 // clang-format on
2442 std::vector<PickupAndDeliveryPolicy> vehicle_pickup_delivery_policy_;
2443 // Same vehicle group to which a node belongs.
2444 std::vector<int> same_vehicle_group_;
2445 // Same vehicle node groups.
2446 std::vector<std::vector<int>> same_vehicle_groups_;
2447 // Node visit types
2448 // Variable index to visit type index.
2449 std::vector<int> index_to_visit_type_;
2450 // Variable index to VisitTypePolicy.
2451 std::vector<VisitTypePolicy> index_to_type_policy_;
2452 // clang-format off
2453 std::vector<std::vector<int> > single_nodes_of_type_;
2454 std::vector<std::vector<int> > pair_indices_of_type_;
2455
2456 std::vector<absl::flat_hash_set<int> >
2457 hard_incompatible_types_per_type_index_;
2458 bool has_hard_type_incompatibilities_;
2459 std::vector<absl::flat_hash_set<int> >
2460 temporal_incompatible_types_per_type_index_;
2461 bool has_temporal_type_incompatibilities_;
2462
2463 std::vector<std::vector<absl::flat_hash_set<int> > >
2464 same_vehicle_required_type_alternatives_per_type_index_;
2465 bool has_same_vehicle_type_requirements_;
2466 std::vector<std::vector<absl::flat_hash_set<int> > >
2467 required_type_alternatives_when_adding_type_index_;
2468 std::vector<std::vector<absl::flat_hash_set<int> > >
2469 required_type_alternatives_when_removing_type_index_;
2470 bool has_temporal_type_requirements_;
2471 absl::flat_hash_map</*type*/int, absl::flat_hash_set<VisitTypePolicy> >
2472 trivially_infeasible_visit_types_to_policies_;
2473
2474 // Visit types sorted topologically based on required-->dependent requirement
2475 // arcs between the types (if the requirement/dependency graph is acyclic).
2476 // Visit types of the same topological level are sorted in each sub-vector
2477 // by decreasing requirement "tightness", computed as the pair of the two
2478 // following criteria:
2479 //
2480 // 1) How highly *dependent* this type is, determined by
2481 // (total number of required alternative sets for that type)
2482 // / (average number of types in the required alternative sets)
2483 // 2) How highly *required* this type t is, computed as
2484 // SUM_{S required set containing t} ( 1 / |S| ),
2485 // i.e. the sum of reverse number of elements of all required sets
2486 // containing the type t.
2487 //
2488 // The higher these two numbers, the tighter the type is wrt requirements.
2489 std::vector<std::vector<int> > topologically_sorted_visit_types_;
2490 // clang-format on
2491 int num_visit_types_;
2492 // Two indices are equivalent if they correspond to the same node (as given
2493 // to the constructors taking a RoutingIndexManager).
2494 std::vector<int> index_to_equivalence_class_;
2495 const PathsMetadata paths_metadata_;
2496 // TODO(user): b/62478706 Once the port is done, this shouldn't be needed
2497 // anymore.
2498 RoutingIndexManager manager_;
2499 int start_end_count_;
2500 // Model status
2501 bool closed_ = false;
2502 Status status_ = ROUTING_NOT_SOLVED;
2503 bool enable_deep_serialization_ = true;
2504
2505 // Secondary routing solver
2506 RoutingModel* secondary_model_ = nullptr;
2507 RoutingSearchParameters secondary_parameters_;
2508 std::unique_ptr<SecondaryOptimizer> secondary_optimizer_;
2509
2510 // Search data
2511 std::vector<DecisionBuilder*> first_solution_decision_builders_;
2512 std::vector<IntVarFilteredDecisionBuilder*>
2513 first_solution_filtered_decision_builders_;
2514 Solver::IndexEvaluator2 first_solution_evaluator_;
2515 FirstSolutionStrategy::Value automatic_first_solution_strategy_ =
2516 FirstSolutionStrategy::UNSET;
2517 std::vector<LocalSearchOperator*> local_search_operators_;
2518 std::vector<SearchMonitor*> monitors_;
2519 std::vector<SearchMonitor*> secondary_ls_monitors_;
2520 std::vector<SearchMonitor*> at_solution_monitors_;
2521 SearchMonitor* metaheuristic_ = nullptr;
2522 SearchMonitor* search_log_ = nullptr;
2523 bool local_optimum_reached_ = false;
2524 // Best lower bound found during the search.
2525 int64_t objective_lower_bound_ = kint64min;
2526 SolutionCollector* collect_assignments_ = nullptr;
2527 SolutionCollector* collect_secondary_ls_assignments_ = nullptr;
2528 SolutionCollector* collect_one_assignment_ = nullptr;
2529 SolutionCollector* optimized_dimensions_assignment_collector_ = nullptr;
2530 DecisionBuilder* solve_db_ = nullptr;
2531 DecisionBuilder* improve_db_ = nullptr;
2532 DecisionBuilder* secondary_ls_db_ = nullptr;
2533 DecisionBuilder* restore_assignment_ = nullptr;
2534 DecisionBuilder* restore_tmp_assignment_ = nullptr;
2535 Assignment* assignment_ = nullptr;
2536 Assignment* preassignment_ = nullptr;
2537 Assignment* tmp_assignment_ = nullptr;
2538 LocalSearchOperator* primary_ls_operator_ = nullptr;
2539 LocalSearchOperator* secondary_ls_operator_ = nullptr;
2540 std::vector<IntVar*> extra_vars_;
2541 std::vector<IntervalVar*> extra_intervals_;
2542 std::vector<LocalSearchOperator*> extra_operators_;
2543 absl::flat_hash_map<FilterOptions, LocalSearchFilterManager*>
2544 local_search_filter_managers_;
2545 std::vector<LocalSearchFilterManager::FilterEvent> extra_filters_;
2546 struct NodeNeighborsParameters {
2547 int num_neighbors;
2548 bool add_vehicle_starts_to_neighbors;
2549
2550 bool operator==(const NodeNeighborsParameters& other) const {
2551 return num_neighbors == other.num_neighbors &&
2552 add_vehicle_starts_to_neighbors ==
2553 other.add_vehicle_starts_to_neighbors;
2554 }
2555 template <typename H>
2556 friend H AbslHashValue(H h, const NodeNeighborsParameters& params) {
2557 return H::combine(std::move(h), params.num_neighbors,
2558 params.add_vehicle_starts_to_neighbors);
2559 }
2560 };
2561 absl::flat_hash_map<NodeNeighborsParameters,
2562 std::unique_ptr<NodeNeighborsByCostClass>>
2563 node_neighbors_by_cost_class_per_size_;
2564 std::unique_ptr<FinalizerVariables> finalizer_variables_;
2565#ifndef SWIG
2566 std::unique_ptr<SweepArranger> sweep_arranger_;
2567#endif
2568
2569 RegularLimit* limit_ = nullptr;
2570 RegularLimit* cumulative_limit_ = nullptr;
2571 RegularLimit* ls_limit_ = nullptr;
2572 RegularLimit* lns_limit_ = nullptr;
2573 RegularLimit* first_solution_lns_limit_ = nullptr;
2574 absl::Duration time_buffer_;
2575
2576 std::atomic<bool> interrupt_cp_sat_;
2577 std::atomic<bool> interrupt_cp_;
2578
2579 typedef std::pair<int64_t, int64_t> CacheKey;
2580 typedef absl::flat_hash_map<CacheKey, int64_t> TransitCallbackCache;
2581 typedef absl::flat_hash_map<CacheKey, StateDependentTransit>
2582 StateDependentTransitCallbackCache;
2583
2584 // All transit callbacks are stored in transit_evaluators_,
2585 // we refer to callbacks by the index in this vector.
2586 // We maintain unary_transit_evaluators_[] (with the same size) to store
2587 // callbacks that are unary:
2588 // - if a callback is unary, it is in unary_transit_evaluators_[i],
2589 // and a binary version is stored at transit_evaluators_[i].
2590 // - if a callback is binary, it is stored at transit_evaluators_[i],
2591 // and unary_transit_evaluators_[i] is nullptr.
2592 std::vector<TransitCallback1> unary_transit_evaluators_;
2593 std::vector<TransitCallback2> transit_evaluators_;
2594 std::vector<TransitEvaluatorSign> transit_evaluator_sign_;
2595
2596 std::vector<VariableIndexEvaluator2> state_dependent_transit_evaluators_;
2597 std::vector<std::unique_ptr<StateDependentTransitCallbackCache>>
2598 state_dependent_transit_evaluators_cache_;
2599
2600 // Returns global BinCapacities state, may be nullptr.
2601 std::unique_ptr<BinCapacities> bin_capacities_;
2602
2603 friend class RoutingDimension;
2604 friend class RoutingModelInspector;
2605 friend class ResourceGroup::Resource;
2606};
2607
2609class RoutingModelVisitor : public BaseObject {
2610 public:
2612 static const char kLightElement[];
2613 static const char kLightElement2[];
2614 static const char kRemoveValues[];
2615};
2616
2617#if !defined(SWIG)
2620class DisjunctivePropagator {
2621 public:
2627 struct Tasks {
2628 int num_chain_tasks = 0;
2629 std::vector<int64_t> start_min;
2630 std::vector<int64_t> start_max;
2631 std::vector<int64_t> duration_min;
2632 std::vector<int64_t> duration_max;
2633 std::vector<int64_t> end_min;
2634 std::vector<int64_t> end_max;
2635 std::vector<bool> is_preemptible;
2636 std::vector<const SortedDisjointIntervalList*> forbidden_intervals;
2637 std::vector<std::pair<int64_t, int64_t>> distance_duration;
2638 int64_t span_min = 0;
2639 int64_t span_max = kint64max;
2640
2641 void Clear() {
2642 start_min.clear();
2643 start_max.clear();
2644 duration_min.clear();
2645 duration_max.clear();
2646 end_min.clear();
2647 end_max.clear();
2648 is_preemptible.clear();
2649 forbidden_intervals.clear();
2650 distance_duration.clear();
2651 span_min = 0;
2652 span_max = kint64max;
2653 num_chain_tasks = 0;
2654 }
2655 };
2656
2659 bool Propagate(Tasks* tasks);
2660
2662 bool Precedences(Tasks* tasks);
2665 bool MirrorTasks(Tasks* tasks);
2667 bool EdgeFinding(Tasks* tasks);
2670 bool DetectablePrecedencesWithChain(Tasks* tasks);
2672 bool ForbiddenIntervals(Tasks* tasks);
2674 bool DistanceDuration(Tasks* tasks);
2677 bool ChainSpanMin(Tasks* tasks);
2682 bool ChainSpanMinDynamic(Tasks* tasks);
2683
2684 private:
2687 sat::ThetaLambdaTree<int64_t> theta_lambda_tree_;
2689 std::vector<int> tasks_by_start_min_;
2690 std::vector<int> tasks_by_end_max_;
2691 std::vector<int> event_of_task_;
2692 std::vector<int> nonchain_tasks_by_start_max_;
2694 std::vector<int64_t> total_duration_before_;
2695};
2696
2697struct TravelBounds {
2698 std::vector<int64_t> min_travels;
2699 std::vector<int64_t> max_travels;
2700 std::vector<int64_t> pre_travels;
2701 std::vector<int64_t> post_travels;
2702};
2703
2704void AppendTasksFromPath(absl::Span<const int64_t> path,
2705 const TravelBounds& travel_bounds,
2706 const RoutingDimension& dimension,
2707 DisjunctivePropagator::Tasks* tasks);
2708void AppendTasksFromIntervals(const std::vector<IntervalVar*>& intervals,
2709 DisjunctivePropagator::Tasks* tasks);
2710void FillPathEvaluation(const std::vector<int64_t>& path,
2711 const RoutingModel::TransitCallback2& evaluator,
2712 std::vector<int64_t>* values);
2713void FillTravelBoundsOfVehicle(int vehicle, const std::vector<int64_t>& path,
2714 const RoutingDimension& dimension,
2715 TravelBounds* travel_bounds);
2716#endif // !defined(SWIG)
2717
2728class GlobalVehicleBreaksConstraint : public Constraint {
2729 public:
2730 explicit GlobalVehicleBreaksConstraint(const RoutingDimension* dimension);
2731 std::string DebugString() const override {
2732 return "GlobalVehicleBreaksConstraint";
2733 }
2734
2735 void Post() override;
2736 void InitialPropagate() override;
2737
2738 private:
2739 void PropagateNode(int node);
2740 void PropagateVehicle(int vehicle);
2741
2742 const RoutingModel* model_;
2743 const RoutingDimension* const dimension_;
2744 std::vector<Demon*> vehicle_demons_;
2745 std::vector<int64_t> path_;
2746
2751 void FillPartialPathOfVehicle(int vehicle);
2752 void FillPathTravels(absl::Span<const int64_t> path);
2753
2764 class TaskTranslator {
2765 public:
2766 TaskTranslator(IntVar* start, int64_t before_start, int64_t after_start)
2767 : start_(start),
2768 before_start_(before_start),
2769 after_start_(after_start) {}
2770 explicit TaskTranslator(IntervalVar* interval) : interval_(interval) {}
2771 TaskTranslator() = default;
2772
2773 void SetStartMin(int64_t value) {
2774 if (start_ != nullptr) {
2775 start_->SetMin(CapAdd(before_start_, value));
2776 } else if (interval_ != nullptr) {
2777 interval_->SetStartMin(value);
2778 }
2779 }
2780 void SetStartMax(int64_t value) {
2781 if (start_ != nullptr) {
2782 start_->SetMax(CapAdd(before_start_, value));
2783 } else if (interval_ != nullptr) {
2784 interval_->SetStartMax(value);
2785 }
2786 }
2787 void SetDurationMin(int64_t value) {
2788 if (interval_ != nullptr) {
2789 interval_->SetDurationMin(value);
2790 }
2791 }
2792 void SetEndMin(int64_t value) {
2793 if (start_ != nullptr) {
2794 start_->SetMin(CapSub(value, after_start_));
2795 } else if (interval_ != nullptr) {
2796 interval_->SetEndMin(value);
2797 }
2798 }
2799 void SetEndMax(int64_t value) {
2800 if (start_ != nullptr) {
2801 start_->SetMax(CapSub(value, after_start_));
2802 } else if (interval_ != nullptr) {
2803 interval_->SetEndMax(value);
2804 }
2805 }
2806
2807 private:
2808 IntVar* start_ = nullptr;
2809 int64_t before_start_;
2810 int64_t after_start_;
2811 IntervalVar* interval_ = nullptr;
2812 };
2813
2815 std::vector<TaskTranslator> task_translators_;
2816
2818 DisjunctivePropagator disjunctive_propagator_;
2819 DisjunctivePropagator::Tasks tasks_;
2820
2822 TravelBounds travel_bounds_;
2823};
2824
2825class TypeRegulationsChecker {
2826 public:
2827 explicit TypeRegulationsChecker(const RoutingModel& model);
2828 virtual ~TypeRegulationsChecker() = default;
2829
2830 bool CheckVehicle(int vehicle,
2831 const std::function<int64_t(int64_t)>& next_accessor);
2832
2833 protected:
2834#ifndef SWIG
2835 using VisitTypePolicy = RoutingModel::VisitTypePolicy;
2836#endif // SWIG
2837
2838 struct TypePolicyOccurrence {
2842 int num_type_added_to_vehicle = 0;
2848 int num_type_removed_from_vehicle = 0;
2853 int position_of_last_type_on_vehicle_up_to_visit = -1;
2854 };
2855
2860 bool TypeOccursOnRoute(int type) const;
2867 bool TypeCurrentlyOnRoute(int type, int pos) const;
2868
2869 void InitializeCheck(int vehicle,
2870 const std::function<int64_t(int64_t)>& next_accessor);
2871 virtual void OnInitializeCheck() {}
2872 virtual bool HasRegulationsToCheck() const = 0;
2873 virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy,
2874 int pos) = 0;
2875 virtual bool FinalizeCheck() const { return true; }
2876
2877 const RoutingModel& model_;
2878
2879 private:
2880 std::vector<TypePolicyOccurrence> occurrences_of_type_;
2881 std::vector<int64_t> current_route_visits_;
2882};
2883
2885class TypeIncompatibilityChecker : public TypeRegulationsChecker {
2886 public:
2887 TypeIncompatibilityChecker(const RoutingModel& model,
2888 bool check_hard_incompatibilities);
2889 ~TypeIncompatibilityChecker() override = default;
2890
2891 private:
2892 bool HasRegulationsToCheck() const override;
2893 bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos) override;
2897 bool check_hard_incompatibilities_;
2898};
2899
2901class TypeRequirementChecker : public TypeRegulationsChecker {
2902 public:
2903 explicit TypeRequirementChecker(const RoutingModel& model)
2904 : TypeRegulationsChecker(model) {}
2905 ~TypeRequirementChecker() override = default;
2906
2907 private:
2908 bool HasRegulationsToCheck() const override;
2909 void OnInitializeCheck() override {
2910 types_with_same_vehicle_requirements_on_route_.clear();
2911 }
2912 // clang-format off
2915 bool CheckRequiredTypesCurrentlyOnRoute(
2916 const std::vector<absl::flat_hash_set<int> >& required_type_alternatives,
2917 int pos);
2918 // clang-format on
2919 bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos) override;
2920 bool FinalizeCheck() const override;
2921
2922 absl::flat_hash_set<int> types_with_same_vehicle_requirements_on_route_;
2923};
2924
2965class TypeRegulationsConstraint : public Constraint {
2966 public:
2967 explicit TypeRegulationsConstraint(const RoutingModel& model);
2968
2969 void Post() override;
2970 void InitialPropagate() override;
2971
2972 private:
2973 void PropagateNodeRegulations(int node);
2974 void CheckRegulationsOnVehicle(int vehicle);
2975
2976 const RoutingModel& model_;
2977 TypeIncompatibilityChecker incompatibility_checker_;
2978 TypeRequirementChecker requirement_checker_;
2979 std::vector<Demon*> vehicle_demons_;
2980};
2981
2994struct BoundCost {
2995 int64_t bound;
2996 int64_t cost;
2997 BoundCost() : bound(0), cost(0) {}
2998 BoundCost(int64_t bound, int64_t cost) : bound(bound), cost(cost) {}
2999};
3000
3001class SimpleBoundCosts {
3002 public:
3003 SimpleBoundCosts(int num_bounds, BoundCost default_bound_cost)
3004 : bound_costs_(num_bounds, default_bound_cost) {}
3005#ifndef SWIG
3006 BoundCost& bound_cost(int element) { return bound_costs_[element]; }
3007#endif
3008 BoundCost bound_cost(int element) const { return bound_costs_[element]; }
3009 int Size() { return bound_costs_.size(); }
3010 SimpleBoundCosts(const SimpleBoundCosts&) = delete;
3011 SimpleBoundCosts operator=(const SimpleBoundCosts&) = delete;
3012
3013 private:
3014 std::vector<BoundCost> bound_costs_;
3015};
3016
3034// TODO(user): Break constraints need to know the service time of nodes
3037class RoutingDimension {
3038 public:
3039 // This type is neither copyable nor movable.
3040 RoutingDimension(const RoutingDimension&) = delete;
3041 RoutingDimension& operator=(const RoutingDimension&) = delete;
3042
3043 ~RoutingDimension();
3045 RoutingModel* model() const { return model_; }
3049 int64_t GetTransitValue(int64_t from_index, int64_t to_index,
3050 int64_t vehicle) const;
3053 int64_t GetTransitValueFromClass(int64_t from_index, int64_t to_index,
3054 int64_t vehicle_class) const {
3055 return model_->TransitCallback(class_evaluators_[vehicle_class])(from_index,
3056 to_index);
3057 }
3060 IntVar* CumulVar(int64_t index) const { return cumuls_[index]; }
3061 IntVar* TransitVar(int64_t index) const { return transits_[index]; }
3062 IntVar* FixedTransitVar(int64_t index) const {
3063 return fixed_transits_[index];
3064 }
3065 IntVar* SlackVar(int64_t index) const { return slacks_[index]; }
3066
3067#if !defined(SWIGPYTHON)
3070 const std::vector<IntVar*>& cumuls() const { return cumuls_; }
3071 const std::vector<IntVar*>& fixed_transits() const { return fixed_transits_; }
3072 const std::vector<IntVar*>& transits() const { return transits_; }
3073 const std::vector<IntVar*>& slacks() const { return slacks_; }
3074#if !defined(SWIGCSHARP) && !defined(SWIGJAVA)
3076 const std::vector<SortedDisjointIntervalList>& forbidden_intervals() const {
3077 return forbidden_intervals_;
3078 }
3080 SortedDisjointIntervalList GetAllowedIntervalsInRange(
3081 int64_t index, int64_t min_value, int64_t max_value) const;
3084 int64_t GetFirstPossibleGreaterOrEqualValueForNode(int64_t index,
3085 int64_t min_value) const {
3086 DCHECK_LT(index, forbidden_intervals_.size());
3087 const SortedDisjointIntervalList& forbidden_intervals =
3088 forbidden_intervals_[index];
3089 const auto first_forbidden_interval_it =
3090 forbidden_intervals.FirstIntervalGreaterOrEqual(min_value);
3091 if (first_forbidden_interval_it != forbidden_intervals.end() &&
3092 min_value >= first_forbidden_interval_it->start) {
3094 return CapAdd(first_forbidden_interval_it->end, 1);
3095 }
3097 return min_value;
3098 }
3103 int64_t GetLastPossibleLessOrEqualValueForNode(int64_t index,
3104 int64_t max_value) const {
3105 DCHECK_LT(index, forbidden_intervals_.size());
3106 const SortedDisjointIntervalList& forbidden_intervals =
3107 forbidden_intervals_[index];
3108 const auto last_forbidden_interval_it =
3109 forbidden_intervals.LastIntervalLessOrEqual(max_value);
3110 if (last_forbidden_interval_it != forbidden_intervals.end() &&
3111 max_value <= last_forbidden_interval_it->end) {
3113 return CapSub(last_forbidden_interval_it->start, 1);
3114 }
3116 return max_value;
3117 }
3119 const std::vector<int64_t>& vehicle_capacities() const {
3120 return vehicle_capacities_;
3121 }
3124 const RoutingModel::TransitCallback2& transit_evaluator(int vehicle) const {
3125 return model_->TransitCallback(
3126 class_evaluators_[vehicle_to_class_[vehicle]]);
3127 }
3128
3131 const RoutingModel::TransitCallback2& class_transit_evaluator(
3132 RoutingVehicleClassIndex vehicle_class) const {
3133 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3134 DCHECK_NE(vehicle, -1);
3135 return transit_evaluator(vehicle);
3136 }
3137
3139 bool IsUnary() const {
3140 for (int evaluator_index : class_evaluators_) {
3141 if (model_->UnaryTransitCallbackOrNull(evaluator_index) == nullptr) {
3142 return false;
3143 }
3144 }
3145 return true;
3146 }
3147
3151 const RoutingModel::TransitCallback1& GetUnaryTransitEvaluator(
3152 int vehicle) const {
3153 return model_->UnaryTransitCallbackOrNull(
3154 class_evaluators_[vehicle_to_class_[vehicle]]);
3155 }
3156 const RoutingModel::TransitCallback2& GetBinaryTransitEvaluator(
3157 int vehicle) const {
3158 return model_->TransitCallback(
3159 class_evaluators_[vehicle_to_class_[vehicle]]);
3160 }
3163 bool AreVehicleTransitsPositive(int vehicle) const {
3164 const int evaluator_index = class_evaluators_[vehicle_to_class_[vehicle]];
3165 return model()->transit_evaluator_sign_[evaluator_index] ==
3166 RoutingModel::kTransitEvaluatorSignPositiveOrZero;
3167 }
3168 bool AllTransitEvaluatorSignsAreUnknown() const;
3169 RoutingModel::TransitEvaluatorSign GetTransitEvaluatorSign(
3170 int vehicle) const {
3171 const int evaluator_index = class_evaluators_[vehicle_to_class_[vehicle]];
3172 return model()->transit_evaluator_sign_[evaluator_index];
3173 }
3174 int vehicle_to_class(int vehicle) const { return vehicle_to_class_[vehicle]; }
3175#endif
3176#endif
3180 void SetSpanUpperBoundForVehicle(int64_t upper_bound, int vehicle);
3187 void SetSpanCostCoefficientForVehicle(int64_t coefficient, int vehicle);
3188 void SetSpanCostCoefficientForAllVehicles(int64_t coefficient);
3196 void SetSlackCostCoefficientForVehicle(int64_t coefficient, int vehicle);
3197 void SetSlackCostCoefficientForAllVehicles(int64_t coefficient);
3204 void SetGlobalSpanCostCoefficient(int64_t coefficient);
3205
3206#ifndef SWIG
3211 void SetCumulVarPiecewiseLinearCost(int64_t index,
3212 const PiecewiseLinearFunction& cost);
3215 bool HasCumulVarPiecewiseLinearCost(int64_t index) const;
3218 const PiecewiseLinearFunction* GetCumulVarPiecewiseLinearCost(
3219 int64_t index) const;
3220#endif
3221
3230 void SetCumulVarSoftUpperBound(int64_t index, int64_t upper_bound,
3231 int64_t coefficient);
3234 bool HasCumulVarSoftUpperBound(int64_t index) const;
3238 int64_t GetCumulVarSoftUpperBound(int64_t index) const;
3242 int64_t GetCumulVarSoftUpperBoundCoefficient(int64_t index) const;
3243
3253 void SetCumulVarSoftLowerBound(int64_t index, int64_t lower_bound,
3254 int64_t coefficient);
3257 bool HasCumulVarSoftLowerBound(int64_t index) const;
3261 int64_t GetCumulVarSoftLowerBound(int64_t index) const;
3265 int64_t GetCumulVarSoftLowerBoundCoefficient(int64_t index) const;
3281 // TODO(user): Remove if !defined when routing.i is repaired.
3282#if !defined(SWIGPYTHON)
3283 void SetBreakIntervalsOfVehicle(std::vector<IntervalVar*> breaks, int vehicle,
3284 int pre_travel_evaluator,
3285 int post_travel_evaluator);
3286#endif // !defined(SWIGPYTHON)
3287
3289 void SetBreakIntervalsOfVehicle(std::vector<IntervalVar*> breaks, int vehicle,
3290 std::vector<int64_t> node_visit_transits);
3291
3296 void SetBreakDistanceDurationOfVehicle(int64_t distance, int64_t duration,
3297 int vehicle);
3300 void InitializeBreaks();
3302 bool HasBreakConstraints() const;
3303#if !defined(SWIGPYTHON)
3306 void SetBreakIntervalsOfVehicle(
3307 std::vector<IntervalVar*> breaks, int vehicle,
3308 std::vector<int64_t> node_visit_transits,
3309 std::function<int64_t(int64_t, int64_t)> delays);
3310
3312 const std::vector<IntervalVar*>& GetBreakIntervalsOfVehicle(
3313 int vehicle) const;
3316 // clang-format off
3317 const std::vector<std::pair<int64_t, int64_t> >&
3318 GetBreakDistanceDurationOfVehicle(int vehicle) const;
3319 // clang-format on
3320#endif
3321 int GetPreTravelEvaluatorOfVehicle(int vehicle) const;
3322 int GetPostTravelEvaluatorOfVehicle(int vehicle) const;
3323
3325 const RoutingDimension* base_dimension() const { return base_dimension_; }
3333 int64_t ShortestTransitionSlack(int64_t node) const;
3334
3336 const std::string& name() const { return name_; }
3337
3339#ifndef SWIG
3340 const ReverseArcListGraph<int, int>& GetPathPrecedenceGraph() const {
3341 return path_precedence_graph_;
3342 }
3343#endif // SWIG
3344
3354 typedef std::function<int64_t(int, int)> PickupToDeliveryLimitFunction;
3355
3356 void SetPickupToDeliveryLimitFunctionForPair(
3357 PickupToDeliveryLimitFunction limit_function, int pair_index);
3358
3359 bool HasPickupToDeliveryLimits() const;
3360#ifndef SWIG
3361 int64_t GetPickupToDeliveryLimitForPair(int pair_index,
3362 int pickup_alternative_index,
3363 int delivery_alternative_index) const;
3364
3365 struct NodePrecedence {
3366 int64_t first_node;
3367 int64_t second_node;
3368 int64_t offset;
3369 };
3370
3371 void AddNodePrecedence(NodePrecedence precedence) {
3372 node_precedences_.push_back(precedence);
3373 }
3374 const std::vector<NodePrecedence>& GetNodePrecedences() const {
3375 return node_precedences_;
3376 }
3377#endif // SWIG
3378
3379 void AddNodePrecedence(int64_t first_node, int64_t second_node,
3380 int64_t offset) {
3381 AddNodePrecedence({first_node, second_node, offset});
3382 }
3383
3384 int64_t GetSpanUpperBoundForVehicle(int vehicle) const {
3385 return vehicle_span_upper_bounds_[vehicle];
3386 }
3387#ifndef SWIG
3388 const std::vector<int64_t>& vehicle_span_upper_bounds() const {
3389 return vehicle_span_upper_bounds_;
3390 }
3391#endif // SWIG
3392 int64_t GetSpanCostCoefficientForVehicle(int vehicle) const {
3393 return vehicle_span_cost_coefficients_[vehicle];
3394 }
3395#ifndef SWIG
3396 int64_t GetSpanCostCoefficientForVehicleClass(
3397 RoutingVehicleClassIndex vehicle_class) const {
3398 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3399 DCHECK_NE(vehicle, -1);
3400 return GetSpanCostCoefficientForVehicle(vehicle);
3401 }
3402#endif // SWIG
3403#ifndef SWIG
3404 const std::vector<int64_t>& vehicle_span_cost_coefficients() const {
3405 return vehicle_span_cost_coefficients_;
3406 }
3407#endif // SWIG
3408#ifndef SWIG
3409 const std::vector<int64_t>& vehicle_slack_cost_coefficients() const {
3410 return vehicle_slack_cost_coefficients_;
3411 }
3412#endif // SWIG
3413 int64_t GetSlackCostCoefficientForVehicle(int vehicle) const {
3414 return vehicle_slack_cost_coefficients_[vehicle];
3415 }
3416#ifndef SWIG
3417 int64_t GetSlackCostCoefficientForVehicleClass(
3418 RoutingVehicleClassIndex vehicle_class) const {
3419 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3420 DCHECK_NE(vehicle, -1);
3421 return GetSlackCostCoefficientForVehicle(vehicle);
3422 }
3423#endif // SWIG
3424 int64_t global_span_cost_coefficient() const {
3425 return global_span_cost_coefficient_;
3426 }
3427
3428 int64_t GetGlobalOptimizerOffset() const {
3429 DCHECK_GE(global_optimizer_offset_, 0);
3430 return global_optimizer_offset_;
3431 }
3432 int64_t GetLocalOptimizerOffsetForVehicle(int vehicle) const {
3433 if (vehicle >= local_optimizer_offset_for_vehicle_.size()) {
3434 return 0;
3435 }
3436 DCHECK_GE(local_optimizer_offset_for_vehicle_[vehicle], 0);
3437 return local_optimizer_offset_for_vehicle_[vehicle];
3438 }
3439
3442 void SetSoftSpanUpperBoundForVehicle(BoundCost bound_cost, int vehicle) {
3443 if (!HasSoftSpanUpperBounds()) {
3444 vehicle_soft_span_upper_bound_ = std::make_unique<SimpleBoundCosts>(
3445 model_->vehicles(), BoundCost{kint64max, 0});
3446 }
3447 vehicle_soft_span_upper_bound_->bound_cost(vehicle) = bound_cost;
3448 }
3449 bool HasSoftSpanUpperBounds() const {
3450 return vehicle_soft_span_upper_bound_ != nullptr;
3451 }
3452 BoundCost GetSoftSpanUpperBoundForVehicle(int vehicle) const {
3453 DCHECK(HasSoftSpanUpperBounds());
3454 return vehicle_soft_span_upper_bound_->bound_cost(vehicle);
3455 }
3458 void SetQuadraticCostSoftSpanUpperBoundForVehicle(BoundCost bound_cost,
3459 int vehicle) {
3460 if (!HasQuadraticCostSoftSpanUpperBounds()) {
3461 vehicle_quadratic_cost_soft_span_upper_bound_ =
3462 std::make_unique<SimpleBoundCosts>(model_->vehicles(),
3463 BoundCost{kint64max, 0});
3464 }
3465 vehicle_quadratic_cost_soft_span_upper_bound_->bound_cost(vehicle) =
3466 bound_cost;
3467 }
3468 bool HasQuadraticCostSoftSpanUpperBounds() const {
3469 return vehicle_quadratic_cost_soft_span_upper_bound_ != nullptr;
3470 }
3471 BoundCost GetQuadraticCostSoftSpanUpperBoundForVehicle(int vehicle) const {
3472 DCHECK(HasQuadraticCostSoftSpanUpperBounds());
3473 return vehicle_quadratic_cost_soft_span_upper_bound_->bound_cost(vehicle);
3474 }
3475
3476 private:
3477 struct SoftBound {
3478 IntVar* var;
3479 int64_t bound;
3480 int64_t coefficient;
3481 };
3482
3483 struct PiecewiseLinearCost {
3484 PiecewiseLinearCost() : var(nullptr), cost(nullptr) {}
3485 IntVar* var;
3486 std::unique_ptr<PiecewiseLinearFunction> cost;
3487 };
3488
3489 class SelfBased {};
3490 RoutingDimension(RoutingModel* model, std::vector<int64_t> vehicle_capacities,
3491 const std::string& name,
3492 const RoutingDimension* base_dimension);
3493 RoutingDimension(RoutingModel* model, std::vector<int64_t> vehicle_capacities,
3494 const std::string& name, SelfBased);
3495 void Initialize(const std::vector<int>& transit_evaluators,
3496 const std::vector<int>& state_dependent_transit_evaluators,
3497 int64_t slack_max);
3498 void InitializeCumuls();
3499 void InitializeTransits(
3500 const std::vector<int>& transit_evaluators,
3501 const std::vector<int>& state_dependent_transit_evaluators,
3502 int64_t slack_max);
3503 void InitializeTransitVariables(int64_t slack_max);
3505 void SetupCumulVarSoftUpperBoundCosts(
3506 std::vector<IntVar*>* cost_elements) const;
3508 void SetupCumulVarSoftLowerBoundCosts(
3509 std::vector<IntVar*>* cost_elements) const;
3510 void SetupCumulVarPiecewiseLinearCosts(
3511 std::vector<IntVar*>* cost_elements) const;
3514 void SetupGlobalSpanCost(std::vector<IntVar*>* cost_elements) const;
3515 void SetupSlackAndDependentTransitCosts() const;
3517 void CloseModel(bool use_light_propagation);
3518
3519 void SetOffsetForGlobalOptimizer(int64_t offset) {
3520 global_optimizer_offset_ = std::max(Zero(), offset);
3521 }
3523 void SetVehicleOffsetsForLocalOptimizer(std::vector<int64_t> offsets) {
3525 std::transform(offsets.begin(), offsets.end(), offsets.begin(),
3526 [](int64_t offset) { return std::max(Zero(), offset); });
3527 local_optimizer_offset_for_vehicle_ = std::move(offsets);
3528 }
3529
3530 std::vector<IntVar*> cumuls_;
3531 std::vector<SortedDisjointIntervalList> forbidden_intervals_;
3532 std::vector<IntVar*> capacity_vars_;
3533 const std::vector<int64_t> vehicle_capacities_;
3534 std::vector<IntVar*> transits_;
3535 std::vector<IntVar*> fixed_transits_;
3538 std::vector<int> class_evaluators_;
3539 std::vector<int64_t> vehicle_to_class_;
3540#ifndef SWIG
3541 ReverseArcListGraph<int, int> path_precedence_graph_;
3542#endif
3543 // For every {first_node, second_node, offset} element in node_precedences_,
3544 // if both first_node and second_node are performed, then
3545 // cumuls_[second_node] must be greater than (or equal to)
3546 // cumuls_[first_node] + offset.
3547 std::vector<NodePrecedence> node_precedences_;
3548
3549 // The transits of a dimension may depend on its cumuls or the cumuls of
3550 // another dimension. There can be no cycles, except for self loops, a
3551 // typical example for this is a time dimension.
3552 const RoutingDimension* const base_dimension_;
3553
3554 // Values in state_dependent_class_evaluators_ correspond to the evaluators
3555 // in RoutingModel::state_dependent_transit_evaluators_ for each vehicle
3556 // class.
3557 std::vector<int> state_dependent_class_evaluators_;
3558 std::vector<int64_t> state_dependent_vehicle_to_class_;
3559
3560 // For each pickup/delivery pair_index for which limits have been set,
3561 // pickup_to_delivery_limits_per_pair_index_[pair_index] contains the
3562 // PickupToDeliveryLimitFunction for the pickup and deliveries in this pair.
3563 std::vector<PickupToDeliveryLimitFunction>
3564 pickup_to_delivery_limits_per_pair_index_;
3565
3566 // Used if some vehicle has breaks in this dimension, typically time.
3567 bool break_constraints_are_initialized_ = false;
3568 // clang-format off
3569 std::vector<std::vector<IntervalVar*> > vehicle_break_intervals_;
3570 std::vector<std::vector<std::pair<int64_t, int64_t> > >
3571 vehicle_break_distance_duration_;
3572 // clang-format on
3573 // For each vehicle, stores the part of travel that is made directly
3574 // after (before) the departure (arrival) node of the travel.
3575 // These parts of the travel are non-interruptible, in particular by a break.
3576 std::vector<int> vehicle_pre_travel_evaluators_;
3577 std::vector<int> vehicle_post_travel_evaluators_;
3578
3579 std::vector<IntVar*> slacks_;
3580 std::vector<IntVar*> dependent_transits_;
3581 std::vector<int64_t> vehicle_span_upper_bounds_;
3582 int64_t global_span_cost_coefficient_;
3583 std::vector<int64_t> vehicle_span_cost_coefficients_;
3584 std::vector<int64_t> vehicle_slack_cost_coefficients_;
3585 std::vector<SoftBound> cumul_var_soft_upper_bound_;
3586 std::vector<SoftBound> cumul_var_soft_lower_bound_;
3587 std::vector<PiecewiseLinearCost> cumul_var_piecewise_linear_cost_;
3588 RoutingModel* const model_;
3589 const std::string name_;
3590 int64_t global_optimizer_offset_;
3591 std::vector<int64_t> local_optimizer_offset_for_vehicle_;
3593 std::unique_ptr<SimpleBoundCosts> vehicle_soft_span_upper_bound_;
3594 std::unique_ptr<SimpleBoundCosts>
3595 vehicle_quadratic_cost_soft_span_upper_bound_;
3596 friend class RoutingModel;
3597 friend class RoutingModelInspector;
3598};
3599
3604bool SolveModelWithSat(RoutingModel* model,
3605 const RoutingSearchParameters& search_parameters,
3606 const Assignment* initial_solution,
3607 Assignment* solution);
3608
3609#if !defined(SWIG)
3610IntVarLocalSearchFilter* MakeVehicleBreaksFilter(
3611 const RoutingModel& routing_model, const RoutingDimension& dimension);
3612#endif
3613
3614} // namespace operations_research
3615#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
IntegerValue size
NodeIndexType size() const
Definition graph.h:212
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
SatParameters parameters
const std::string name
A name for logging purposes.
int64_t value
IntVar * var
const int64_t limit_
absl::Status status
Definition g_gurobi.cc:44
const std::vector< IntVar * > cumuls_
double upper_bound
double lower_bound
GRBmodel * model
MPCallback * callback
time_limit
Definition solve.cc:22
int index
double solution
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
Definition matchers.h:468
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition solve.cc:62
H AbslHashValue(H h, const IndicatorConstraint &constraint)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const SatParameters &params)
Solves the given CpModelProto with the given parameters.
In SWIG mode, we don't want anything besides these top-level includes.
LinearRange operator==(const LinearExpr &lhs, const LinearExpr &rhs)
int64_t CapAdd(int64_t x, int64_t y)
void FillTravelBoundsOfVehicle(int vehicle, const std::vector< int64_t > &path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
std::function< int64_t(int64_t)> RoutingTransitCallback1
void AppendTasksFromPath(absl::Span< const int64_t > path, const TravelBounds &travel_bounds, const RoutingDimension &dimension, DisjunctivePropagator::Tasks *tasks)
void FillPathEvaluation(const std::vector< int64_t > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
Definition routing.cc:6095
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
bool SolveModelWithSat(RoutingModel *model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
void AppendTasksFromIntervals(const std::vector< IntervalVar * > &intervals, DisjunctivePropagator::Tasks *tasks)
STL namespace.
bool Next()
IntervalVar * interval
Definition resource.cc:101
int64_t fixed_cost
Contrarily to CostClass, here we need strict equivalence.
Definition routing.cc:1338
RoutingModel::CostClassIndex cost_class_index
The cost class of the vehicle.
Definition routing.cc:1336
int64_t bound
int64_t coefficient
int vehicle_class
int nodes
double distance
Rev< int64_t > start_max
Rev< int64_t > end_max
Rev< int64_t > start_min
Rev< int64_t > end_min
int var_index
Definition search.cc:3268
std::optional< int64_t > end
int64_t start
static const int64_t kint64max
Definition types.h:32
static const int64_t kint64min
Definition types.h:31