Google OR-Tools v9.12
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-2025 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 <cstdint>
162#include <deque>
163#include <functional>
164#include <limits>
165#include <memory>
166#include <optional>
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/hash/hash.h"
176#include "absl/log/check.h"
177#include "absl/strings/string_view.h"
178#include "absl/time/time.h"
179#include "absl/types/span.h"
181#include "ortools/base/logging.h"
183#include "ortools/base/types.h"
186#include "ortools/constraint_solver/routing_enums.pb.h"
188#include "ortools/constraint_solver/routing_parameters.pb.h"
191#include "ortools/graph/graph.h"
197
198namespace operations_research {
199
203#ifndef SWIG
205#endif
206class RoutingDimension;
207#ifndef SWIG
208using util::ReverseArcListGraph;
209class SweepArranger;
210#endif
211
212class PathsMetadata {
213 public:
214 explicit PathsMetadata(const RoutingIndexManager& manager) {
215 const int num_indices = manager.num_indices();
216 const int num_paths = manager.num_vehicles();
217 path_of_node_.resize(num_indices, -1);
218 is_start_.resize(num_indices, false);
219 is_end_.resize(num_indices, false);
220 start_of_path_.resize(num_paths);
221 end_of_path_.resize(num_paths);
222 for (int v = 0; v < num_paths; ++v) {
223 const int64_t start = manager.GetStartIndex(v);
224 start_of_path_[v] = start;
225 path_of_node_[start] = v;
226 is_start_[start] = true;
227 const int64_t end = manager.GetEndIndex(v);
228 end_of_path_[v] = end;
229 path_of_node_[end] = v;
230 is_end_[end] = true;
231 }
232 }
233
234 bool IsStart(int64_t node) const { return is_start_[node]; }
235 bool IsEnd(int64_t node) const { return is_end_[node]; }
236 int GetPath(int64_t start_or_end_node) const {
237 return path_of_node_[start_or_end_node];
238 }
239 int NumPaths() const { return start_of_path_.size(); }
240 const std::vector<int64_t>& Paths() const { return path_of_node_; }
241 const std::vector<int64_t>& Starts() const { return start_of_path_; }
242 int64_t Start(int path) const { return start_of_path_[path]; }
243 int64_t End(int path) const { return end_of_path_[path]; }
244 const std::vector<int64_t>& Ends() const { return end_of_path_; }
245
246 private:
247 std::vector<bool> is_start_;
248 std::vector<bool> is_end_;
249 std::vector<int64_t> start_of_path_;
250 std::vector<int64_t> end_of_path_;
251 std::vector<int64_t> path_of_node_;
252};
253
254class OR_DLL RoutingModel {
255 public:
257 enum PickupAndDeliveryPolicy {
259 PICKUP_AND_DELIVERY_NO_ORDER,
261 PICKUP_AND_DELIVERY_LIFO,
263 PICKUP_AND_DELIVERY_FIFO
264 };
265 typedef RoutingCostClassIndex CostClassIndex;
266 typedef RoutingDimensionIndex DimensionIndex;
267 typedef RoutingDisjunctionIndex DisjunctionIndex;
268 typedef RoutingVehicleClassIndex VehicleClassIndex;
269 typedef RoutingResourceClassIndex ResourceClassIndex;
270 typedef RoutingTransitCallback1 TransitCallback1;
271 typedef RoutingTransitCallback2 TransitCallback2;
272 typedef RoutingCumulDependentTransitCallback2 CumulDependentTransitCallback2;
273
274#if !defined(SWIG)
287 struct StateDependentTransit {
288 RangeIntToIntFunction* transit;
289 RangeMinMaxIndexFunction* transit_plus_identity;
290 };
291 typedef std::function<StateDependentTransit(int64_t, int64_t)>
292 VariableIndexEvaluator2;
293#endif // SWIG
294
295#if !defined(SWIG)
296 struct CostClass {
298 int evaluator_index = 0;
299
314
320 struct DimensionCost {
321 int64_t transit_evaluator_class;
322 // TODO(user): replace span_cost_coefficient by
323 // transit_cost_coefficient and add the span coefficient to the slack one.
324 int64_t span_cost_coefficient;
325 int64_t slack_cost_coefficient;
326 const RoutingDimension* dimension;
327 bool operator<(const DimensionCost& cost) const {
328 return std::tie(transit_evaluator_class, span_cost_coefficient,
329 slack_cost_coefficient) <
330 std::tie(cost.transit_evaluator_class,
331 cost.span_cost_coefficient,
332 cost.slack_cost_coefficient);
333 }
334
335 friend bool operator==(const DimensionCost& c1, const DimensionCost& c2) {
336 return c1.transit_evaluator_class == c2.transit_evaluator_class &&
337 c1.span_cost_coefficient == c2.span_cost_coefficient &&
338 c1.slack_cost_coefficient == c2.slack_cost_coefficient;
339 }
340 template <typename H>
341 friend H AbslHashValue(H h, const DimensionCost& cost) {
342 return H::combine(std::move(h), cost.transit_evaluator_class,
343 cost.span_cost_coefficient,
344 cost.slack_cost_coefficient);
345 }
346 };
347 std::vector<DimensionCost>
348 dimension_transit_evaluator_class_and_cost_coefficient;
349
350 explicit CostClass(int evaluator_index)
351 : evaluator_index(evaluator_index) {}
352
353 friend bool operator==(const CostClass& c1, const CostClass& c2) {
354 return c1.evaluator_index == c2.evaluator_index &&
355 c1.dimension_transit_evaluator_class_and_cost_coefficient ==
356 c2.dimension_transit_evaluator_class_and_cost_coefficient;
357 }
358 template <typename H>
359 friend H AbslHashValue(H h, const CostClass& c) {
360 return H::combine(
361 std::move(h), c.evaluator_index,
362 c.dimension_transit_evaluator_class_and_cost_coefficient);
363 }
364 };
365#endif // defined(SWIG)
366
370 struct VehicleTypeContainer {
371 struct VehicleClassEntry {
372 int vehicle_class;
373 int64_t fixed_cost;
374
375 bool operator<(const VehicleClassEntry& other) const {
376 return std::tie(fixed_cost, vehicle_class) <
377 std::tie(other.fixed_cost, other.vehicle_class);
378 }
379 };
380
381 int NumTypes() const { return sorted_vehicle_classes_per_type.size(); }
382
383 int Type(int vehicle) const {
384 DCHECK_LT(vehicle, type_index_of_vehicle.size());
385 return type_index_of_vehicle[vehicle];
386 }
387
388 std::vector<int> type_index_of_vehicle;
389 // clang-format off
390 std::vector<std::set<VehicleClassEntry> > sorted_vehicle_classes_per_type;
391 std::vector<std::deque<int> > vehicles_per_vehicle_class;
392 // clang-format on
393 };
394
406 class ResourceGroup {
407 public:
409 class Attributes {
410 public:
411 Attributes();
412 Attributes(Domain start_domain, Domain end_domain);
413
414 const Domain& start_domain() const { return start_domain_; }
415 const Domain& end_domain() const { return end_domain_; }
416
417 friend bool operator==(const Attributes& a, const Attributes& b) {
418 return a.start_domain_ == b.start_domain_ &&
419 a.end_domain_ == b.end_domain_;
420 }
421 template <typename H>
422 friend H AbslHashValue(H h, const Attributes& attributes) {
423 return H::combine(std::move(h), attributes.start_domain_,
424 attributes.end_domain_);
425 }
426
427 private:
431 Domain start_domain_;
433 Domain end_domain_;
434 };
435
437 class Resource {
438 public:
439 const ResourceGroup::Attributes& GetDimensionAttributes(
440 const RoutingDimension* dimension) const;
441
442 private:
443 explicit Resource(const RoutingModel* model) : model_(model) {}
444
445 void SetDimensionAttributes(ResourceGroup::Attributes attributes,
446 const RoutingDimension* dimension);
447 const ResourceGroup::Attributes& GetDefaultAttributes() const;
448
449 const RoutingModel* const model_;
450 absl::flat_hash_map<DimensionIndex, ResourceGroup::Attributes>
451 dimension_attributes_;
452
453 friend class ResourceGroup;
454 };
455
458 int AddResource(Attributes attributes, const RoutingDimension* dimension);
459
463 void NotifyVehicleRequiresAResource(int vehicle);
464
465 const std::vector<int>& GetVehiclesRequiringAResource() const {
466 return vehicles_requiring_resource_;
467 }
468
469 bool VehicleRequiresAResource(int vehicle) const {
470 return vehicle_requires_resource_[vehicle];
471 }
472
473 void SetAllowedResourcesForVehicle(
474 int vehicle, const std::vector<int>& allowed_resource_indices) {
475 DCHECK(!model_->closed_);
476 // NOTE: As of 12/2023, an empty set of allowed resources means "all
477 // resources allowed", so we make sure the empty set isn't used here
478 // explicitly by the user to mark a vehicle as "unusable".
479 DCHECK(!allowed_resource_indices.empty());
480 DCHECK(vehicle_requires_resource_[vehicle]);
481 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
482 vehicle_allowed_resources_[vehicle].clear();
483 vehicle_allowed_resources_[vehicle].insert(
484 allowed_resource_indices.begin(), allowed_resource_indices.end());
485 }
486 void ClearAllowedResourcesForVehicle(int vehicle) {
487 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
488 vehicle_allowed_resources_[vehicle].clear();
489 }
490 const absl::flat_hash_set<int>& GetResourcesMarkedAllowedForVehicle(
491 int vehicle) const {
492 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
493 return vehicle_allowed_resources_[vehicle];
494 }
495 bool IsResourceAllowedForVehicle(int resource, int vehicle) const {
496 DCHECK_LT(vehicle, vehicle_allowed_resources_.size());
497 return vehicle_allowed_resources_[vehicle].empty() ||
498 vehicle_allowed_resources_[vehicle].contains(resource);
499 }
500
501 const std::vector<Resource>& GetResources() const { return resources_; }
502 const Resource& GetResource(int resource_index) const {
503 DCHECK_LT(resource_index, resources_.size());
504 return resources_[resource_index];
505 }
506 const absl::flat_hash_set<DimensionIndex>& GetAffectedDimensionIndices()
507 const {
508 return affected_dimension_indices_;
509 }
510
511 int GetResourceClassesCount() const {
512 return resource_indices_per_class_.size();
513 }
514 const std::vector<int>& GetResourceIndicesInClass(
515 ResourceClassIndex resource_class) const {
516 DCHECK_LT(resource_class, resource_indices_per_class_.size());
517 return resource_indices_per_class_[resource_class];
518 }
519 // clang-format off
520 const util_intops::StrongVector<ResourceClassIndex, std::vector<int> >&
521 GetResourceIndicesPerClass() const {
522 return resource_indices_per_class_;
523 }
524 // clang-format on
525 ResourceClassIndex GetResourceClassIndex(int resource_index) const {
526 DCHECK_LT(resource_index, resource_class_indices_.size());
527 return resource_class_indices_[resource_index];
528 }
529 const Attributes& GetDimensionAttributesForClass(
530 const RoutingDimension* dimension, ResourceClassIndex rc_index) const {
531 DCHECK_LT(rc_index, resource_indices_per_class_.size());
532 const std::vector<int>& resource_indices =
533 resource_indices_per_class_[rc_index];
534 DCHECK(!resource_indices.empty());
535 return resources_[resource_indices[0]].GetDimensionAttributes(dimension);
536 }
537
538 int Size() const { return resources_.size(); }
539 int Index() const { return index_; }
540
541 private:
542 explicit ResourceGroup(const RoutingModel* model)
543 : index_(model->GetResourceGroups().size()),
544 model_(model),
545 vehicle_requires_resource_(model->vehicles(), false),
546 vehicle_allowed_resources_(model->vehicles()) {}
547
548 void ComputeResourceClasses();
549
550 const int index_;
551 const RoutingModel* const model_;
552 std::vector<Resource> resources_;
553 // Stores the ResourceClassIndex of each resource (See implementation of
554 // ComputeResourceClasses()).
555 std::vector<ResourceClassIndex> resource_class_indices_;
556 // clang-format off
557 util_intops::StrongVector<ResourceClassIndex, std::vector<int> >
558 resource_indices_per_class_;
559 // clang-format on
560
561 std::vector<bool> vehicle_requires_resource_;
562 std::vector<int> vehicles_requiring_resource_;
563 // For each vehicle, stores the set of allowed resource indices if it's
564 // restricted for that vehicle. If the set is empty for a vehicle, then any
565 // resource from this group can be assigned to it.
566 // clang-format off
567 std::vector<absl::flat_hash_set<int> > vehicle_allowed_resources_;
568 // clang-format on
570 absl::flat_hash_set<DimensionIndex> affected_dimension_indices_;
571
572 friend class RoutingModel;
573 };
574
576 struct VariableValuePair {
577 int var_index;
578 int64_t value;
579 };
580
582 class SecondaryOptimizer {
583 public:
584 SecondaryOptimizer(RoutingModel* model,
585 RoutingSearchParameters* search_parameters,
586 int64_t solve_period);
587 bool Solve(const std::vector<RoutingModel::VariableValuePair>& in_state,
588 std::vector<RoutingModel::VariableValuePair>* out_state);
589
590 private:
591 RoutingModel* const model_;
592 const RoutingSearchParameters* const search_parameters_;
593 const int64_t solve_period_ = 0;
594 int64_t call_count_ = 0;
595 Assignment* state_ = nullptr;
596 absl::flat_hash_map<IntVar*, int> var_to_index_;
597 };
598
600 static const int64_t kNoPenalty;
601
604 static const DisjunctionIndex kNoDisjunction;
605
608 static const DimensionIndex kNoDimension;
609
613 explicit RoutingModel(const RoutingIndexManager& index_manager);
614 RoutingModel(const RoutingIndexManager& index_manager,
615 const RoutingModelParameters& parameters);
616
617 // This type is neither copyable nor movable.
618 RoutingModel(const RoutingModel&) = delete;
619 RoutingModel& operator=(const RoutingModel&) = delete;
620
621 ~RoutingModel();
622
624 enum TransitEvaluatorSign {
625 kTransitEvaluatorSignUnknown = 0,
626 kTransitEvaluatorSignPositiveOrZero = 1,
627 kTransitEvaluatorSignNegativeOrZero = 2,
628 };
634 int RegisterUnaryTransitVector(std::vector<int64_t> values);
635 int RegisterUnaryTransitCallback(
636 TransitCallback1 callback,
637 TransitEvaluatorSign sign = kTransitEvaluatorSignUnknown);
638
639 int RegisterTransitMatrix(
640 std::vector<std::vector<int64_t> /*needed_for_swig*/> values);
641 int RegisterTransitCallback(
642 TransitCallback2 callback,
643 TransitEvaluatorSign sign = kTransitEvaluatorSignUnknown);
644 int RegisterCumulDependentTransitCallback(
645 CumulDependentTransitCallback2 callback);
646 int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback);
647
648 const TransitCallback2& TransitCallback(int callback_index) const {
649 CHECK_LT(callback_index, transit_evaluators_.size());
650 return transit_evaluators_[callback_index];
651 }
652 const TransitCallback1& UnaryTransitCallbackOrNull(int callback_index) const {
653 CHECK_LT(callback_index, unary_transit_evaluators_.size());
654 return unary_transit_evaluators_[callback_index];
655 }
656 const CumulDependentTransitCallback2& CumulDependentTransitCallback(
657 int callback_index) const {
658 CHECK_LT(callback_index, cumul_dependent_transit_evaluators_.size());
659 return cumul_dependent_transit_evaluators_[callback_index];
660 }
661 const VariableIndexEvaluator2& StateDependentTransitCallback(
662 int callback_index) const {
663 CHECK_LT(callback_index, state_dependent_transit_evaluators_.size());
664 return state_dependent_transit_evaluators_[callback_index];
665 }
666
668
680
689 bool AddDimension(int evaluator_index, int64_t slack_max, int64_t capacity,
690 bool fix_start_cumul_to_zero, const std::string& name);
691 bool AddDimensionWithVehicleTransits(
692 const std::vector<int>& evaluator_indices, int64_t slack_max,
693 int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
694 bool AddDimensionWithVehicleCapacity(int evaluator_index, int64_t slack_max,
695 std::vector<int64_t> vehicle_capacities,
696 bool fix_start_cumul_to_zero,
697 const std::string& name);
698 bool AddDimensionWithVehicleTransitAndCapacity(
699 const std::vector<int>& evaluator_indices, int64_t slack_max,
700 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
701 const std::string& name);
708 bool AddDimensionWithCumulDependentVehicleTransitAndCapacity(
709 const std::vector<int>& fixed_evaluator_indices,
710 const std::vector<int>& cumul_dependent_evaluator_indices,
711 int64_t slack_max, std::vector<int64_t> vehicle_capacities,
712 bool fix_start_cumul_to_zero, const std::string& name);
713
722 std::pair<int, bool> AddConstantDimensionWithSlack(
723 int64_t value, int64_t capacity, int64_t slack_max,
724 bool fix_start_cumul_to_zero, const std::string& name);
725 std::pair<int, bool> AddConstantDimension(int64_t value, int64_t capacity,
726 bool fix_start_cumul_to_zero,
727 const std::string& name) {
728 return AddConstantDimensionWithSlack(value, capacity, 0,
729 fix_start_cumul_to_zero, name);
730 }
740 std::pair<int, bool> AddVectorDimension(std::vector<int64_t> values,
741 int64_t capacity,
742 bool fix_start_cumul_to_zero,
743 const std::string& name);
753 std::pair<int, bool> AddMatrixDimension(
754 std::vector<std::vector<int64_t> /*needed_for_swig*/> values,
755 int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
762 bool AddDimensionDependentDimensionWithVehicleCapacity(
763 const std::vector<int>& pure_transits,
764 const std::vector<int>& dependent_transits,
765 const RoutingDimension* base_dimension, int64_t slack_max,
766 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
767 const std::string& name) {
768 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
769 pure_transits, dependent_transits, base_dimension, slack_max,
770 std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
771 }
772
774 bool AddDimensionDependentDimensionWithVehicleCapacity(
775 const std::vector<int>& transits, const RoutingDimension* base_dimension,
776 int64_t slack_max, std::vector<int64_t> vehicle_capacities,
777 bool fix_start_cumul_to_zero, const std::string& name);
779 bool AddDimensionDependentDimensionWithVehicleCapacity(
780 int transit, const RoutingDimension* base_dimension, int64_t slack_max,
781 int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
782 const std::string& name);
783 bool AddDimensionDependentDimensionWithVehicleCapacity(
784 int pure_transit, int dependent_transit,
785 const RoutingDimension* base_dimension, int64_t slack_max,
786 int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
787 const std::string& name);
788
790 static RoutingModel::StateDependentTransit MakeStateDependentTransit(
791 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
792 int64_t domain_end);
793
795 // TODO(user): rename.
796 std::vector<std::string> GetAllDimensionNames() const;
798 const std::vector<RoutingDimension*>& GetDimensions() const {
799 return dimensions_.get();
800 }
802 std::vector<RoutingDimension*> GetDimensionsWithSoftOrSpanCosts() const;
804 std::vector<RoutingDimension*> GetUnaryDimensions() const;
805
807 std::vector<const RoutingDimension*> GetDimensionsWithGlobalCumulOptimizers()
808 const;
809 std::vector<const RoutingDimension*> GetDimensionsWithLocalCumulOptimizers()
810 const;
811
813 bool HasGlobalCumulOptimizer(const RoutingDimension& dimension) const {
814 return GetGlobalCumulOptimizerIndex(dimension) >= 0;
815 }
816 bool HasLocalCumulOptimizer(const RoutingDimension& dimension) const {
817 return GetLocalCumulOptimizerIndex(dimension) >= 0;
818 }
821 GlobalDimensionCumulOptimizer* GetMutableGlobalCumulLPOptimizer(
822 const RoutingDimension& dimension) const;
823 GlobalDimensionCumulOptimizer* GetMutableGlobalCumulMPOptimizer(
824 const RoutingDimension& dimension) const;
825 LocalDimensionCumulOptimizer* GetMutableLocalCumulLPOptimizer(
826 const RoutingDimension& dimension) const;
827 LocalDimensionCumulOptimizer* GetMutableLocalCumulMPOptimizer(
828 const RoutingDimension& dimension) const;
829
831 bool HasDimension(absl::string_view dimension_name) const;
833 const RoutingDimension& GetDimensionOrDie(
834 const std::string& dimension_name) const;
837 RoutingDimension* GetMutableDimension(
838 const std::string& dimension_name) const;
843 void SetPrimaryConstrainedDimension(absl::string_view dimension_name) {
844 DCHECK(dimension_name.empty() || HasDimension(dimension_name));
845 primary_constrained_dimension_ = dimension_name;
846 }
848 const std::string& GetPrimaryConstrainedDimension() const {
849 return primary_constrained_dimension_;
850 }
851
853 ResourceGroup* AddResourceGroup();
854 // clang-format off
855 const std::vector<std::unique_ptr<ResourceGroup> >& GetResourceGroups()
856 const {
857 return resource_groups_;
858 }
859 // clang-format on
860 ResourceGroup* GetResourceGroup(int rg_index) const {
861 DCHECK_LT(rg_index, resource_groups_.size());
862 return resource_groups_[rg_index].get();
863 }
864
867 const std::vector<int>& GetDimensionResourceGroupIndices(
868 const RoutingDimension* dimension) const;
869
872 int GetDimensionResourceGroupIndex(const RoutingDimension* dimension) const {
873 DCHECK_EQ(GetDimensionResourceGroupIndices(dimension).size(), 1);
874 return GetDimensionResourceGroupIndices(dimension)[0];
875 }
876
879 enum PenaltyCostBehavior { PENALIZE_ONCE, PENALIZE_PER_INACTIVE };
880
902 DisjunctionIndex AddDisjunction(const std::vector<int64_t>& indices,
903 int64_t penalty = kNoPenalty,
904 int64_t max_cardinality = 1,
905 PenaltyCostBehavior penalty_cost_behavior =
906 PenaltyCostBehavior::PENALIZE_ONCE);
908 const std::vector<DisjunctionIndex>& GetDisjunctionIndices(
909 int64_t index) const {
910 return index_to_disjunctions_[index];
911 }
915 template <typename F>
916 void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(
917 int64_t index, int64_t max_cardinality, F f) const {
918 for (const DisjunctionIndex disjunction : GetDisjunctionIndices(index)) {
919 if (disjunctions_[disjunction].value.max_cardinality == max_cardinality) {
920 for (const int64_t d_index : disjunctions_[disjunction].indices) {
921 f(d_index);
922 }
923 }
924 }
925 }
926#if !defined(SWIGPYTHON)
929 const std::vector<int64_t>& GetDisjunctionNodeIndices(
930 DisjunctionIndex index) const {
931 return disjunctions_[index].indices;
932 }
933#endif // !defined(SWIGPYTHON)
935 int64_t GetDisjunctionPenalty(DisjunctionIndex index) const {
936 return disjunctions_[index].value.penalty;
937 }
940 int64_t GetDisjunctionMaxCardinality(DisjunctionIndex index) const {
941 return disjunctions_[index].value.max_cardinality;
942 }
945 PenaltyCostBehavior GetDisjunctionPenaltyCostBehavior(
946 DisjunctionIndex index) const {
947 return disjunctions_[index].value.penalty_cost_behavior;
948 }
950 int GetNumberOfDisjunctions() const { return disjunctions_.size(); }
953 bool HasMandatoryDisjunctions() const;
956 bool HasMaxCardinalityConstrainedDisjunctions() const;
961 std::vector<std::pair<int64_t, int64_t>> GetPerfectBinaryDisjunctions() const;
967 void IgnoreDisjunctionsAlreadyForcedToZero();
968
972 void AddSoftSameVehicleConstraint(std::vector<int64_t> indices, int64_t cost);
973
978 void SetAllowedVehiclesForIndex(const std::vector<int>& vehicles,
979 int64_t index);
980
982 bool IsVehicleAllowedForIndex(int vehicle, int64_t index) const {
983 return allowed_vehicles_[index].empty() ||
984 allowed_vehicles_[index].find(vehicle) !=
985 allowed_vehicles_[index].end();
986 }
987
1002 // TODO(user): Remove this when model introspection detects linked nodes.
1003 void AddPickupAndDelivery(int64_t pickup, int64_t delivery);
1007 void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction,
1008 DisjunctionIndex delivery_disjunction);
1009
1011 struct PickupDeliveryPosition {
1013 int pd_pair_index = -1;
1016 int alternative_index = -1;
1017 };
1019 std::optional<PickupDeliveryPosition> GetPickupPosition(
1020 int64_t node_index) const;
1022 std::optional<PickupDeliveryPosition> GetDeliveryPosition(
1023 int64_t node_index) const;
1025 bool IsPickup(int64_t node_index) const {
1026 return index_to_pickup_position_[node_index].pd_pair_index != -1;
1027 }
1028 bool IsDelivery(int64_t node_index) const {
1029 return index_to_delivery_position_[node_index].pd_pair_index != -1;
1030 }
1031
1034 void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy);
1035 void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy,
1036 int vehicle);
1037 PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(
1038 int vehicle) const;
1041
1042 int GetNumOfSingletonNodes() const;
1043
1044#ifndef SWIG
1046 const std::vector<PickupDeliveryPair>& GetPickupAndDeliveryPairs() const {
1047 return pickup_delivery_pairs_;
1048 }
1049 const std::vector<std::pair<DisjunctionIndex, DisjunctionIndex>>&
1050 GetPickupAndDeliveryDisjunctions() const {
1051 return pickup_delivery_disjunctions_;
1052 }
1057 const std::vector<PickupDeliveryPair>&
1058 GetImplicitUniquePickupAndDeliveryPairs() const {
1059 DCHECK(closed_);
1060 return implicit_pickup_delivery_pairs_without_alternatives_;
1061 }
1062#endif // SWIG
1063
1064 // Returns the first pickup or delivery sibling of the given node matching
1065 // the given predicate.
1066 std::optional<int64_t> GetFirstMatchingPickupDeliverySibling(
1067 int64_t node, const std::function<bool(int64_t)>& is_match) const;
1068
1080 enum VisitTypePolicy {
1082 TYPE_ADDED_TO_VEHICLE,
1087 ADDED_TYPE_REMOVED_FROM_VEHICLE,
1090 TYPE_ON_VEHICLE_UP_TO_VISIT,
1095 TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
1096 };
1097 // TODO(user): Support multiple visit types per node?
1098 void SetVisitType(int64_t index, int type, VisitTypePolicy type_policy);
1099 int GetVisitType(int64_t index) const;
1100 const std::vector<int>& GetSingleNodesOfType(int type) const;
1101 const std::vector<int>& GetPairIndicesOfType(int type) const;
1102 VisitTypePolicy GetVisitTypePolicy(int64_t index) const;
1103
1104 int GetNumberOfVisitTypes() const { return num_visit_types_; }
1105#ifndef SWIG
1106 const std::vector<std::vector<int>>& GetTopologicallySortedVisitTypes()
1107 const {
1108 DCHECK(closed_);
1109 return topologically_sorted_visit_types_;
1110 }
1111#endif // SWIG
1119 void AddHardTypeIncompatibility(int type1, int type2);
1120 void AddTemporalTypeIncompatibility(int type1, int type2);
1122 const absl::flat_hash_set<int>& GetHardTypeIncompatibilitiesOfType(
1123 int type) const;
1124 const absl::flat_hash_set<int>& GetTemporalTypeIncompatibilitiesOfType(
1125 int type) const;
1128 bool HasHardTypeIncompatibilities() const {
1129 return !hard_incompatible_types_per_type_index_.empty();
1130 }
1131 bool HasTemporalTypeIncompatibilities() const {
1132 return !temporal_incompatible_types_per_type_index_.empty();
1133 }
1147 void AddSameVehicleRequiredTypeAlternatives(
1148 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1153 void AddRequiredTypeAlternativesWhenAddingType(
1154 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1160 void AddRequiredTypeAlternativesWhenRemovingType(
1161 int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
1162 // clang-format off
1165 const std::vector<absl::flat_hash_set<int> >&
1166 GetSameVehicleRequiredTypeAlternativesOfType(int type) const;
1168 const std::vector<absl::flat_hash_set<int> >&
1169 GetRequiredTypeAlternativesWhenAddingType(int type) const;
1171 const std::vector<absl::flat_hash_set<int> >&
1172 GetRequiredTypeAlternativesWhenRemovingType(int type) const;
1173 // clang-format on
1176 bool HasSameVehicleTypeRequirements() const {
1177 return !same_vehicle_required_type_alternatives_per_type_index_.empty();
1178 }
1179 bool HasTemporalTypeRequirements() const {
1180 return !required_type_alternatives_when_adding_type_index_.empty() ||
1181 !required_type_alternatives_when_removing_type_index_.empty();
1182 }
1183
1186 bool HasTypeRegulations() const {
1187 return HasTemporalTypeIncompatibilities() ||
1188 HasHardTypeIncompatibilities() || HasSameVehicleTypeRequirements() ||
1189 HasTemporalTypeRequirements();
1190 }
1191
1196 int64_t UnperformedPenalty(int64_t var_index) const;
1200 int64_t UnperformedPenaltyOrValue(int64_t default_value,
1201 int64_t var_index) const;
1205 int64_t GetDepot() const;
1206
1211 void SetMaximumNumberOfActiveVehicles(int max_active_vehicles) {
1212 max_active_vehicles_ = max_active_vehicles;
1213 }
1215 int GetMaximumNumberOfActiveVehicles() const { return max_active_vehicles_; }
1219 void SetArcCostEvaluatorOfAllVehicles(int evaluator_index);
1221 void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle);
1224 void SetFixedCostOfAllVehicles(int64_t cost);
1226 void SetFixedCostOfVehicle(int64_t cost, int vehicle);
1230 int64_t GetFixedCostOfVehicle(int vehicle) const;
1231 // Sets the energy cost of a vehicle.
1232 // The energy used by a vehicle is defined as the integral of the
1233 // force dimension over the distance dimension:
1234 // it is the sum over nodes visited by the vehicle of
1235 // force.CumulVar(Next(node)) * distance.TransitVar(node).
1236 // The energy cost of a vehicle is linear in the energy used by the vehicle,
1237 // this call sets the coefficient to cost_per_unit. The cost is zero if unset.
1238 void SetPathEnergyCostOfVehicle(const std::string& force,
1239 const std::string& distance,
1240 int64_t cost_per_unit, int vehicle);
1241 // Sets the energy cost of a vehicle, relative to a threshold.
1242 // The cost per unit of energy is cost_per_unit_below_threshold until the
1243 // force reaches the threshold, then it is cost_per_unit_above_threshold:
1244 // min(threshold, force.CumulVar(Next(node))) * distance.TransitVar(node) *
1245 // cost_per_unit_below_threshold + max(0, force.CumulVar(Next(node)) -
1246 // threshold) * distance.TransitVar(node) * cost_per_unit_above_threshold.
1247 void SetPathEnergyCostsOfVehicle(const std::string& force,
1248 const std::string& distance,
1249 int64_t threshold,
1250 int64_t cost_per_unit_below_threshold,
1251 int64_t cost_per_unit_above_threshold,
1252 int vehicle);
1253
1269 void SetAmortizedCostFactorsOfAllVehicles(int64_t linear_cost_factor,
1270 int64_t quadratic_cost_factor);
1272 void SetAmortizedCostFactorsOfVehicle(int64_t linear_cost_factor,
1273 int64_t quadratic_cost_factor,
1274 int vehicle);
1275
1276 const std::vector<int64_t>& GetAmortizedLinearCostFactorOfVehicles() const {
1277 return linear_cost_factor_of_vehicle_;
1278 }
1279 const std::vector<int64_t>& GetAmortizedQuadraticCostFactorOfVehicles()
1280 const {
1281 return quadratic_cost_factor_of_vehicle_;
1282 }
1283
1284 // Adds a custom route constraint based on a route evaluation callback. The
1285 // callback must not return a value if the route vector is invalid, and
1286 // returns the value of the route otherwise.
1287 // The callback must always return the same value for a given route.
1288 void AddRouteConstraint(
1289 std::function<std::optional<int64_t>(const std::vector<int64_t>&)>
1290 route_evaluator,
1291 bool costs_are_homogeneous_across_vehicles = false);
1292 std::optional<int64_t> GetRouteCost(const std::vector<int64_t>& route) const {
1293 int64_t route_cost = 0;
1294 for (const auto& evaluator : route_evaluators_) {
1295 std::optional<int64_t> cost = evaluator(route);
1296 if (!cost.has_value()) return std::nullopt;
1297 CapAddTo(cost.value(), &route_cost);
1298 }
1299 return route_cost;
1300 }
1301
1302 void SetVehicleUsedWhenEmpty(bool is_used, int vehicle) {
1303 DCHECK_LT(vehicle, vehicles_);
1304 vehicle_used_when_empty_[vehicle] = is_used;
1305 }
1306
1307 bool IsVehicleUsedWhenEmpty(int vehicle) const {
1308 DCHECK_LT(vehicle, vehicles_);
1309 return vehicle_used_when_empty_[vehicle];
1310 }
1311
1314#ifndef SWIG
1315 const Solver::IndexEvaluator2& first_solution_evaluator() const {
1316 return first_solution_evaluator_;
1317 }
1318#endif
1320 void SetFirstSolutionEvaluator(Solver::IndexEvaluator2 evaluator) {
1321 first_solution_evaluator_ = std::move(evaluator);
1322 }
1327 void SetFirstSolutionHint(const Assignment* hint) { hint_ = hint; }
1329 const Assignment* GetFirstSolutionHint() const { return hint_; }
1332 void AddLocalSearchOperator(LocalSearchOperator* ls_operator);
1334 void AddSearchMonitor(SearchMonitor* monitor);
1335 // Adds a callback called at the beginning of the search.
1336 void AddEnterSearchCallback(std::function<void()> callback);
1343 void AddAtSolutionCallback(std::function<void()> callback,
1344 bool track_unchecked_neighbors = false);
1345 // Internal-only: Adds a callback to reset
1346 // RestoreDimensionValuesForUnchangedRoutes at the beginning of the search.
1347 void AddRestoreDimensionValuesResetCallback(std::function<void()> callback);
1352 void AddVariableMinimizedByFinalizer(IntVar* var);
1355 void AddVariableMaximizedByFinalizer(IntVar* var);
1358 void AddWeightedVariableMinimizedByFinalizer(IntVar* var, int64_t cost);
1361 void AddWeightedVariableMaximizedByFinalizer(IntVar* var, int64_t cost);
1364 void AddVariableTargetToFinalizer(IntVar* var, int64_t target);
1367 void AddWeightedVariableTargetToFinalizer(IntVar* var, int64_t target,
1368 int64_t cost);
1375 void CloseModel();
1378 void CloseModelWithParameters(
1379 const RoutingSearchParameters& search_parameters);
1386 const Assignment* Solve(const Assignment* assignment = nullptr);
1395 const RoutingSearchParameters& search_parameters,
1396 std::vector<const Assignment*>* solutions = nullptr);
1399 const Assignment* SolveFromAssignmentWithParameters(
1400 const Assignment* assignment,
1401 const RoutingSearchParameters& search_parameters,
1402 std::vector<const Assignment*>* solutions = nullptr);
1407 const Assignment* FastSolveFromAssignmentWithParameters(
1408 const Assignment* assignment,
1409 const RoutingSearchParameters& search_parameters,
1410 bool check_solution_in_cp,
1411 absl::flat_hash_set<IntVar*>* touched = nullptr);
1414 const Assignment* SolveFromAssignmentsWithParameters(
1415 const std::vector<const Assignment*>& assignments,
1416 const RoutingSearchParameters& search_parameters,
1417 std::vector<const Assignment*>* solutions = nullptr);
1420 const Assignment* SolveWithIteratedLocalSearch(
1421 const RoutingSearchParameters& search_parameters);
1427 void SetAssignmentFromOtherModelAssignment(
1428 Assignment* target_assignment, const RoutingModel* source_model,
1429 const Assignment* source_assignment);
1435 // TODO(user): Add support for non-homogeneous costs and disjunctions.
1436 int64_t ComputeLowerBound();
1439 int64_t objective_lower_bound() const { return objective_lower_bound_; }
1441 RoutingSearchStatus::Value status() const { return status_; }
1443 bool enable_deep_serialization() const { return enable_deep_serialization_; }
1452 IntVar* ApplyLocks(const std::vector<int64_t>& locks);
1461 bool ApplyLocksToAllVehicles(const std::vector<std::vector<int64_t>>& locks,
1462 bool close_routes);
1467 const Assignment* PreAssignment() const { return preassignment_; }
1468 Assignment* MutablePreAssignment() { return preassignment_; }
1472 bool WriteAssignment(const std::string& file_name) const;
1476 Assignment* ReadAssignment(const std::string& file_name);
1479 Assignment* RestoreAssignment(const Assignment& solution);
1485 Assignment* ReadAssignmentFromRoutes(
1486 const std::vector<std::vector<int64_t>>& routes,
1487 bool ignore_inactive_indices);
1504 bool RoutesToAssignment(const std::vector<std::vector<int64_t>>& routes,
1505 bool ignore_inactive_indices, bool close_routes,
1506 Assignment* assignment) const;
1510 void AssignmentToRoutes(const Assignment& assignment,
1511 std::vector<std::vector<int64_t>>* routes) const;
1516#ifndef SWIG
1517 std::vector<std::vector<int64_t>> GetRoutesFromAssignment(
1518 const Assignment& assignment);
1519#endif
1537 Assignment* CompactAssignment(const Assignment& assignment) const;
1541 Assignment* CompactAndCheckAssignment(const Assignment& assignment) const;
1543 void AddToAssignment(IntVar* var);
1544 void AddIntervalToAssignment(IntervalVar* interval);
1555 const Assignment* PackCumulsOfOptimizerDimensionsFromAssignment(
1556 const Assignment* original_assignment, absl::Duration duration_limit,
1557 bool* time_limit_was_reached = nullptr);
1558
1559#ifndef SWIG
1562 struct RouteDimensionTravelInfo {
1564 struct TransitionInfo {
1567 FloatSlopePiecewiseLinearFunction travel_start_dependent_travel;
1568
1572 FloatSlopePiecewiseLinearFunction travel_compression_cost;
1573
1577 int64_t pre_travel_transit_value;
1578 int64_t post_travel_transit_value;
1579
1582 int64_t compressed_travel_value_lower_bound;
1583
1588 int64_t travel_value_upper_bound;
1589
1590 std::string DebugString(std::string line_prefix = "") const;
1591 };
1592
1595 std::vector<TransitionInfo> transition_info;
1597 int64_t travel_cost_coefficient;
1598
1599 std::string DebugString(std::string line_prefix = "") const;
1600 };
1601
1602#endif // SWIG
1603
1604#ifndef SWIG
1605 // TODO(user): Revisit if coordinates are added to the RoutingModel class.
1606 void SetSweepArranger(SweepArranger* sweep_arranger);
1608 SweepArranger* sweep_arranger() const;
1609#endif
1610 struct NodeNeighborsParameters {
1611 int num_neighbors;
1612 bool add_vehicle_starts_to_neighbors = true;
1613 bool add_vehicle_ends_to_neighbors = false;
1614 // In NodeNeighborsByCostClass, neighbors for each node are sorted by
1615 // increasing "cost" for each node. The following parameter determines if
1616 // neighbors are sorted based on distance only when the neighborhood is
1617 // partial, i.e. when num_neighbors entails that not all nodes are
1618 // neighbors.
1619 bool only_sort_neighbors_for_partial_neighborhoods = true;
1620
1621 bool operator==(const NodeNeighborsParameters& other) const {
1622 return num_neighbors == other.num_neighbors &&
1623 add_vehicle_starts_to_neighbors ==
1624 other.add_vehicle_starts_to_neighbors &&
1625 add_vehicle_ends_to_neighbors ==
1626 other.add_vehicle_ends_to_neighbors &&
1627 only_sort_neighbors_for_partial_neighborhoods ==
1628 other.only_sort_neighbors_for_partial_neighborhoods;
1629 }
1630 template <typename H>
1631 friend H AbslHashValue(H h, const NodeNeighborsParameters& params) {
1632 return H::combine(std::move(h), params.num_neighbors,
1633 params.add_vehicle_starts_to_neighbors,
1634 params.add_vehicle_ends_to_neighbors,
1635 params.only_sort_neighbors_for_partial_neighborhoods);
1636 }
1637 };
1638 class NodeNeighborsByCostClass {
1639 public:
1640 explicit NodeNeighborsByCostClass(const RoutingModel* routing_model)
1641 : routing_model_(*routing_model) {};
1642
1645 void ComputeNeighbors(const NodeNeighborsParameters& params);
1649 const std::vector<int>& GetIncomingNeighborsOfNodeForCostClass(
1650 int cost_class, int node_index) const {
1651 if (routing_model_.IsStart(node_index)) return empty_neighbors_;
1652
1653 if (node_index_to_incoming_neighbors_by_cost_class_.empty()) {
1654 return all_incoming_nodes_;
1655 }
1656 const std::vector<std::vector<int>>& node_index_to_incoming_neighbors =
1657 node_index_to_incoming_neighbors_by_cost_class_[cost_class];
1658 if (node_index_to_incoming_neighbors.empty()) {
1659 return empty_neighbors_;
1660 }
1661 return node_index_to_incoming_neighbors[node_index];
1662 }
1663
1667 const std::vector<int>& GetOutgoingNeighborsOfNodeForCostClass(
1668 int cost_class, int node_index) const {
1669 if (routing_model_.IsEnd(node_index)) return empty_neighbors_;
1670
1671 if (node_index_to_outgoing_neighbors_by_cost_class_.empty()) {
1672 return all_outgoing_nodes_;
1673 }
1674 const std::vector<std::vector<int>>& node_index_to_outgoing_neighbors =
1675 node_index_to_outgoing_neighbors_by_cost_class_[cost_class];
1676 if (node_index_to_outgoing_neighbors.empty()) {
1677 return empty_neighbors_;
1678 }
1679 return node_index_to_outgoing_neighbors[node_index];
1680 }
1684 bool IsNeighborhoodArcForCostClass(int cost_class, int64_t from,
1685 int64_t to) const {
1686 if (node_index_to_outgoing_neighbor_indicator_by_cost_class_.empty()) {
1687 return true;
1688 }
1689 if (routing_model_.IsEnd(from)) {
1690 return false;
1691 }
1692 return node_index_to_outgoing_neighbor_indicator_by_cost_class_
1693 [cost_class][from][to];
1694 }
1695
1696 private:
1697 const RoutingModel& routing_model_;
1698#if __cplusplus >= 202002L
1699 static constexpr std::vector<int> empty_neighbors_ = {};
1700#else
1701 inline static const std::vector<int> empty_neighbors_ = {};
1702#endif
1703
1704 std::vector<std::vector<std::vector<int>>>
1705 node_index_to_incoming_neighbors_by_cost_class_;
1706 std::vector<std::vector<std::vector<int>>>
1707 node_index_to_outgoing_neighbors_by_cost_class_;
1708 std::vector<std::vector<std::vector<bool>>>
1709 node_index_to_outgoing_neighbor_indicator_by_cost_class_;
1710
1711 std::vector<int> all_outgoing_nodes_;
1712 std::vector<int> all_incoming_nodes_;
1713 };
1714
1719 const NodeNeighborsByCostClass* GetOrCreateNodeNeighborsByCostClass(
1720 double neighbors_ratio, int64_t min_neighbors,
1721 double& neighbors_ratio_used, bool add_vehicle_starts_to_neighbors = true,
1722 bool add_vehicle_ends_to_neighbors = false,
1723 bool only_sort_neighbors_for_partial_neighborhoods = true);
1726 const NodeNeighborsByCostClass* GetOrCreateNodeNeighborsByCostClass(
1727 const NodeNeighborsParameters& params);
1733 void AddLocalSearchFilter(LocalSearchFilter* filter) {
1734 CHECK(filter != nullptr);
1735 if (closed_) {
1736 LOG(WARNING) << "Model is closed, filter addition will be ignored.";
1737 }
1738 extra_filters_.push_back({filter, LocalSearchFilterManager::kRelax});
1739 extra_filters_.push_back({filter, LocalSearchFilterManager::kAccept});
1740 }
1741
1744 int64_t Start(int vehicle) const { return paths_metadata_.Starts()[vehicle]; }
1746 int64_t End(int vehicle) const { return paths_metadata_.Ends()[vehicle]; }
1748 bool IsStart(int64_t index) const { return paths_metadata_.IsStart(index); }
1750 bool IsEnd(int64_t index) const { return paths_metadata_.IsEnd(index); }
1753 int VehicleIndex(int64_t index) const {
1754 return paths_metadata_.GetPath(index);
1755 }
1759 int64_t Next(const Assignment& assignment, int64_t index) const;
1761 bool IsVehicleUsed(const Assignment& assignment, int vehicle) const;
1762
1763#if !defined(SWIGPYTHON)
1766 const std::vector<IntVar*>& Nexts() const { return nexts_; }
1769 const std::vector<IntVar*>& VehicleVars() const { return vehicle_vars_; }
1773 const std::vector<IntVar*>& ResourceVars(int resource_group) const {
1774 return resource_vars_[resource_group];
1775 }
1776#endif
1779 IntVar* NextVar(int64_t index) const { return nexts_[index]; }
1781 IntVar* ActiveVar(int64_t index) const { return active_[index]; }
1784 IntVar* ActiveVehicleVar(int vehicle) const {
1785 return vehicle_active_[vehicle];
1786 }
1790 IntVar* VehicleRouteConsideredVar(int vehicle) const {
1791 return vehicle_route_considered_[vehicle];
1792 }
1795 IntVar* VehicleVar(int64_t index) const { return vehicle_vars_[index]; }
1799 IntVar* ResourceVar(int vehicle, int resource_group) const {
1800 DCHECK_LT(resource_group, resource_vars_.size());
1801 DCHECK_LT(vehicle, resource_vars_[resource_group].size());
1802 return resource_vars_[resource_group][vehicle];
1803 }
1805 IntVar* CostVar() const { return cost_; }
1806
1809 int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index,
1810 int64_t vehicle) const;
1812 bool CostsAreHomogeneousAcrossVehicles() const {
1813 return costs_are_homogeneous_across_vehicles_;
1814 }
1817 int64_t GetHomogeneousCost(int64_t from_index, int64_t to_index) const {
1818 return GetArcCostForVehicle(from_index, to_index, /*vehicle=*/0);
1819 }
1822 int64_t GetArcCostForFirstSolution(int64_t from_index,
1823 int64_t to_index) const;
1830 int64_t GetArcCostForClass(int64_t from_index, int64_t to_index,
1831 int64_t /*CostClassIndex*/ cost_class_index) const;
1833 CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const {
1834 DCHECK(closed_);
1835 DCHECK_GE(vehicle, 0);
1836 DCHECK_LT(vehicle, cost_class_index_of_vehicle_.size());
1837 DCHECK_GE(cost_class_index_of_vehicle_[vehicle], 0);
1838 return cost_class_index_of_vehicle_[vehicle];
1839 }
1842 bool HasVehicleWithCostClassIndex(CostClassIndex cost_class_index) const {
1843 DCHECK(closed_);
1844 if (cost_class_index == kCostClassIndexOfZeroCost) {
1845 return has_vehicle_with_zero_cost_class_;
1846 }
1847 return cost_class_index < cost_classes_.size();
1848 }
1850 int GetCostClassesCount() const { return cost_classes_.size(); }
1852 int GetNonZeroCostClassesCount() const {
1853 return std::max(0, GetCostClassesCount() - 1);
1854 }
1855 VehicleClassIndex GetVehicleClassIndexOfVehicle(int64_t vehicle) const {
1856 DCHECK(closed_);
1857 return vehicle_class_index_of_vehicle_[vehicle];
1858 }
1861 int GetVehicleOfClass(VehicleClassIndex vehicle_class) const {
1862 DCHECK(closed_);
1863 const RoutingModel::VehicleTypeContainer& vehicle_type_container =
1864 GetVehicleTypeContainer();
1865 if (vehicle_class.value() >= GetVehicleClassesCount() ||
1866 vehicle_type_container.vehicles_per_vehicle_class[vehicle_class.value()]
1867 .empty()) {
1868 return -1;
1869 }
1870 return vehicle_type_container
1871 .vehicles_per_vehicle_class[vehicle_class.value()]
1872 .front();
1873 }
1875 int GetVehicleClassesCount() const { return num_vehicle_classes_; }
1877 const std::vector<int>& GetSameVehicleIndicesOfIndex(int node) const {
1878 DCHECK(closed_);
1879 return same_vehicle_groups_[same_vehicle_group_[node]];
1880 }
1882 const std::vector<int>& GetSameActivityIndicesOfIndex(int node) const {
1883 DCHECK(closed_);
1884 return same_active_var_groups_[same_active_var_group_[node]];
1885 }
1887 int GetSameActivityGroupOfIndex(int node) const {
1888 DCHECK(closed_);
1889 return same_active_var_group_[node];
1890 }
1892 int GetSameActivityGroupsCount() const {
1893 DCHECK(closed_);
1894 return same_active_var_groups_.size();
1895 }
1897 const std::vector<int>& GetSameActivityIndicesOfGroup(int group) const {
1898 DCHECK(closed_);
1899 return same_active_var_groups_[group];
1900 }
1901
1902 const VehicleTypeContainer& GetVehicleTypeContainer() const {
1903 DCHECK(closed_);
1904 return vehicle_type_container_;
1905 }
1906
1925 bool ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1, int64_t to2);
1930 std::string DebugOutputAssignment(
1931 const Assignment& solution_assignment,
1932 const std::string& dimension_to_print) const;
1938#ifndef SWIG
1939 std::vector<std::vector<std::pair<int64_t, int64_t>>> GetCumulBounds(
1940 const Assignment& solution_assignment, const RoutingDimension& dimension);
1941#endif
1943 bool CheckIfAssignmentIsFeasible(const Assignment& assignment,
1944 bool call_at_solution_monitors);
1947 Solver* solver() const { return solver_.get(); }
1948
1951 bool CheckLimit(absl::Duration offset = absl::ZeroDuration()) {
1952 DCHECK(limit_ != nullptr);
1953 return limit_->CheckWithOffset(offset);
1954 }
1955
1957 absl::Duration RemainingTime() const {
1958 DCHECK(limit_ != nullptr);
1959 return limit_->AbsoluteSolverDeadline() - solver_->Now();
1960 }
1961
1963 void UpdateTimeLimit(absl::Duration time_limit) {
1964 RegularLimit* limit = GetOrCreateLimit();
1965 limit->UpdateLimits(time_limit, std::numeric_limits<int64_t>::max(),
1966 std::numeric_limits<int64_t>::max(),
1967 limit->solutions());
1968 }
1969
1971 absl::Duration TimeBuffer() const { return time_buffer_; }
1972
1974 std::atomic<bool>* GetMutableCPSatInterrupt() { return &interrupt_cp_sat_; }
1976 std::atomic<bool>* GetMutableCPInterrupt() { return &interrupt_cp_; }
1978 void CancelSearch() {
1979 interrupt_cp_sat_ = true;
1980 interrupt_cp_ = true;
1981 }
1982
1985 int nodes() const { return nodes_; }
1987 int vehicles() const { return vehicles_; }
1989 int64_t Size() const { return nodes_ + vehicles_ - start_end_count_; }
1990
1993 int64_t GetNumberOfDecisionsInFirstSolution(
1994 const RoutingSearchParameters& search_parameters) const;
1995 int64_t GetNumberOfRejectsInFirstSolution(
1996 const RoutingSearchParameters& search_parameters) const;
1998 operations_research::FirstSolutionStrategy::Value
1999 GetAutomaticFirstSolutionStrategy() const {
2000 return automatic_first_solution_strategy_;
2001 }
2002
2004 bool IsMatchingModel() const;
2005
2008 bool AreRoutesInterdependent(const RoutingSearchParameters& parameters) const;
2009
2010#ifndef SWIG
2013 using GetTabuVarsCallback =
2014 std::function<std::vector<operations_research::IntVar*>(RoutingModel*)>;
2015
2016 void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback);
2017#endif // SWIG
2018
2020 // TODO(user): Find a way to test and restrict the access at the same time.
2032 DecisionBuilder* MakeGuidedSlackFinalizer(
2033 const RoutingDimension* dimension,
2034 std::function<int64_t(int64_t)> initializer);
2035#ifndef SWIG
2036 // TODO(user): MakeGreedyDescentLSOperator is too general for routing.h.
2041 static std::unique_ptr<LocalSearchOperator> MakeGreedyDescentLSOperator(
2042 std::vector<IntVar*> variables);
2043 // Read access to currently registered search monitors.
2044 const std::vector<SearchMonitor*>& GetSearchMonitors() const {
2045 return monitors_;
2046 }
2047#endif
2061 DecisionBuilder* MakeSelfDependentDimensionFinalizer(
2062 const RoutingDimension* dimension);
2063
2064 const PathsMetadata& GetPathsMetadata() const { return paths_metadata_; }
2065#ifndef SWIG
2066 BinCapacities* GetBinCapacities() { return bin_capacities_.get(); }
2067
2070 void SetSecondaryModel(RoutingModel* secondary_model,
2071 RoutingSearchParameters secondary_parameters) {
2072 DCHECK(!closed_);
2073 secondary_model_ = secondary_model;
2074 secondary_parameters_ = std::move(secondary_parameters);
2075 }
2076#endif // SWIG
2077
2080 const std::deque<int>& GetVehiclesOfSameClass(int64_t start_end_index) const;
2081
2087 std::vector<std::pair<int64_t, int64_t>> GetSameVehicleClassArcs(
2088 int64_t from_index, int64_t to_index) const;
2089
2090 private:
2092 enum RoutingLocalSearchOperator {
2093 RELOCATE = 0,
2094 RELOCATE_PAIR,
2095 LIGHT_RELOCATE_PAIR,
2096 RELOCATE_NEIGHBORS,
2097 EXCHANGE,
2098 EXCHANGE_PAIR,
2099 CROSS,
2100 CROSS_EXCHANGE,
2101 TWO_OPT,
2102 OR_OPT,
2103 GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
2104 LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
2105 GLOBAL_CHEAPEST_INSERTION_PATH_LNS,
2106 LOCAL_CHEAPEST_INSERTION_PATH_LNS,
2107 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
2108 GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
2109 LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
2110 RELOCATE_EXPENSIVE_CHAIN,
2111 LIN_KERNIGHAN,
2112 TSP_OPT,
2113 MAKE_ACTIVE,
2114 RELOCATE_AND_MAKE_ACTIVE,
2115 MAKE_ACTIVE_AND_RELOCATE,
2116 EXCHANGE_AND_MAKE_ACTIVE,
2117 EXCHANGE_PATH_START_ENDS_AND_MAKE_ACTIVE,
2118 MAKE_INACTIVE,
2119 MAKE_CHAIN_INACTIVE,
2120 SWAP_ACTIVE,
2121 SWAP_ACTIVE_CHAIN,
2122 EXTENDED_SWAP_ACTIVE,
2123 SHORTEST_PATH_SWAP_ACTIVE,
2124 SHORTEST_PATH_TWO_OPT,
2125 NODE_PAIR_SWAP,
2126 PATH_LNS,
2127 FULL_PATH_LNS,
2128 TSP_LNS,
2129 INACTIVE_LNS,
2130 EXCHANGE_RELOCATE_PAIR,
2131 RELOCATE_SUBTRIP,
2132 EXCHANGE_SUBTRIP,
2133 LOCAL_SEARCH_OPERATOR_COUNTER
2134 };
2135
2139 template <typename T>
2140 struct ValuedNodes {
2141 std::vector<int64_t> indices;
2142 T value;
2143 };
2144 struct DisjunctionValues {
2145 int64_t penalty;
2146 int64_t max_cardinality;
2147 PenaltyCostBehavior penalty_cost_behavior;
2148 };
2149 typedef ValuedNodes<DisjunctionValues> Disjunction;
2150
2153 struct CostCacheElement {
2159 int index;
2160 CostClassIndex cost_class_index;
2161 int64_t cost;
2162 };
2163
2166 template <class DimensionCumulOptimizer>
2167 struct DimensionCumulOptimizers {
2168 std::unique_ptr<DimensionCumulOptimizer> lp_optimizer;
2169 std::unique_ptr<DimensionCumulOptimizer> mp_optimizer;
2170 };
2171
2173 void Initialize();
2174 void AddNoCycleConstraintInternal();
2175 bool AddDimensionWithCapacityInternal(
2176 const std::vector<int>& evaluator_indices,
2177 const std::vector<int>& cumul_dependent_evaluator_indices,
2178 int64_t slack_max, std::vector<int64_t> vehicle_capacities,
2179 bool fix_start_cumul_to_zero, const std::string& name);
2180 bool AddDimensionDependentDimensionWithVehicleCapacityInternal(
2181 const std::vector<int>& pure_transits,
2182 const std::vector<int>& dependent_transits,
2183 const RoutingDimension* base_dimension, int64_t slack_max,
2184 std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
2185 const std::string& name);
2186 bool InitializeDimensionInternal(
2187 const std::vector<int>& evaluator_indices,
2188 const std::vector<int>& cumul_dependent_evaluator_indices,
2189 const std::vector<int>& state_dependent_evaluator_indices,
2190 int64_t slack_max, bool fix_start_cumul_to_zero,
2191 RoutingDimension* dimension);
2192 DimensionIndex GetDimensionIndex(const std::string& dimension_name) const;
2193
2221 void StoreDimensionCumulOptimizers(const RoutingSearchParameters& parameters);
2222
2228 void FinalizeAllowedVehicles();
2229
2230 void ComputeCostClasses(const RoutingSearchParameters& parameters);
2231 void ComputeVehicleClasses();
2239 void ComputeVehicleTypes();
2241 void ComputeResourceClasses();
2251 void FinalizeVisitTypes();
2252 // Called by FinalizeVisitTypes() to setup topologically_sorted_visit_types_.
2253 void TopologicallySortVisitTypes();
2254 int64_t GetArcCostForClassInternal(int64_t from_index, int64_t to_index,
2255 CostClassIndex cost_class_index) const;
2256 int64_t GetArcCostWithGuidedLocalSearchPenalties(int64_t from_index,
2257 int64_t to_index,
2258 int64_t vehicle) const {
2259 return CapAdd(
2260 GetArcCostForVehicle(from_index, to_index, vehicle),
2261 solver()->GetGuidedLocalSearchPenalty(from_index, to_index, vehicle));
2262 }
2263 std::function<int64_t(int64_t, int64_t, int64_t)>
2264 GetLocalSearchArcCostCallback(
2265 const RoutingSearchParameters& parameters) const;
2266 int64_t GetHomogeneousArcCostWithGuidedLocalSearchPenalties(
2267 int64_t from_index, int64_t to_index) const {
2268 return GetArcCostWithGuidedLocalSearchPenalties(from_index, to_index,
2269 /*vehicle=*/0);
2270 }
2271 std::function<int64_t(int64_t, int64_t)>
2272 GetLocalSearchHomogeneousArcCostCallback(
2273 const RoutingSearchParameters& parameters) const;
2274 void AppendHomogeneousArcCosts(const RoutingSearchParameters& parameters,
2275 int node_index,
2276 std::vector<IntVar*>* cost_elements);
2277 void AppendArcCosts(const RoutingSearchParameters& parameters, int node_index,
2278 std::vector<IntVar*>* cost_elements);
2279 Assignment* DoRestoreAssignment();
2280 static const CostClassIndex kCostClassIndexOfZeroCost;
2281 int64_t SafeGetCostClassInt64OfVehicle(int64_t vehicle) const {
2282 DCHECK_LT(0, vehicles_);
2283 return (vehicle >= 0 ? GetCostClassIndexOfVehicle(vehicle)
2284 : kCostClassIndexOfZeroCost)
2285 .value();
2286 }
2287 int64_t GetDimensionTransitCostSum(int64_t i, int64_t j,
2288 const CostClass& cost_class) const;
2290 IntVar* CreateDisjunction(DisjunctionIndex disjunction);
2292 void AddPickupAndDeliverySetsInternal(const std::vector<int64_t>& pickups,
2293 const std::vector<int64_t>& deliveries);
2296 IntVar* CreateSameVehicleCost(int vehicle_index);
2299 int FindNextActive(int index, absl::Span<const int64_t> indices) const;
2300
2303 bool RouteCanBeUsedByVehicle(const Assignment& assignment, int start_index,
2304 int vehicle) const;
2312 bool ReplaceUnusedVehicle(int unused_vehicle, int active_vehicle,
2313 Assignment* compact_assignment) const;
2314
2315 void QuietCloseModel();
2316 void QuietCloseModelWithParameters(
2317 const RoutingSearchParameters& parameters) {
2318 if (!closed_) {
2319 CloseModelWithParameters(parameters);
2320 }
2321 }
2322
2324 bool SolveMatchingModel(Assignment* assignment,
2325 const RoutingSearchParameters& parameters);
2326#ifndef SWIG
2328 bool AppendAssignmentIfFeasible(
2329 const Assignment& assignment,
2330 std::vector<std::unique_ptr<Assignment>>* assignments,
2331 bool call_at_solution_monitors = true);
2332#endif
2334 void LogSolution(const RoutingSearchParameters& parameters,
2335 absl::string_view description, int64_t solution_cost,
2336 int64_t start_time_ms);
2339 Assignment* CompactAssignmentInternal(const Assignment& assignment,
2340 bool check_compact_assignment) const;
2343 std::string FindErrorInSearchParametersForModel(
2344 const RoutingSearchParameters& search_parameters) const;
2346 void SetupSearch(const RoutingSearchParameters& search_parameters);
2348 void UpdateSearchFromParametersIfNeeded(
2349 const RoutingSearchParameters& search_parameters);
2351 // TODO(user): Document each auxiliary method.
2352 Assignment* GetOrCreateAssignment();
2353 Assignment* GetOrCreateTmpAssignment();
2354 RegularLimit* GetOrCreateLimit();
2355 RegularLimit* GetOrCreateCumulativeLimit();
2356 RegularLimit* GetOrCreateLocalSearchLimit();
2357 RegularLimit* GetOrCreateLargeNeighborhoodSearchLimit();
2358 RegularLimit* GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
2359 LocalSearchOperator* CreateInsertionOperator();
2360 LocalSearchOperator* CreateMakeInactiveOperator();
2361 void CreateNeighborhoodOperators(const RoutingSearchParameters& parameters);
2362 LocalSearchOperator* ConcatenateOperators(
2363 const RoutingSearchParameters& search_parameters,
2364 const std::vector<LocalSearchOperator*>& operators) const;
2365 LocalSearchOperator* GetNeighborhoodOperators(
2366 const RoutingSearchParameters& search_parameters,
2367 const absl::flat_hash_set<RoutingLocalSearchOperator>&
2368 operators_to_consider) const;
2369
2370 struct FilterOptions {
2371 bool filter_objective;
2372 bool filter_with_cp_solver;
2373
2374 bool operator==(const FilterOptions& other) const {
2375 return other.filter_objective == filter_objective &&
2376 other.filter_with_cp_solver == filter_with_cp_solver;
2377 }
2378 template <typename H>
2379 friend H AbslHashValue(H h, const FilterOptions& options) {
2380 return H::combine(std::move(h), options.filter_objective,
2381 options.filter_with_cp_solver);
2382 }
2383 };
2384 std::vector<LocalSearchFilterManager::FilterEvent> CreateLocalSearchFilters(
2385 const RoutingSearchParameters& parameters, const FilterOptions& options);
2386 LocalSearchFilterManager* GetOrCreateLocalSearchFilterManager(
2387 const RoutingSearchParameters& parameters, const FilterOptions& options);
2388 DecisionBuilder* CreateSolutionFinalizer(
2389 const RoutingSearchParameters& parameters, SearchLimit* lns_limit);
2390 void CreateFirstSolutionDecisionBuilders(
2391 const RoutingSearchParameters& search_parameters);
2392 DecisionBuilder* GetFirstSolutionDecisionBuilder(
2393 const RoutingSearchParameters& search_parameters) const;
2394 IntVarFilteredDecisionBuilder* GetFilteredFirstSolutionDecisionBuilderOrNull(
2395 const RoutingSearchParameters& parameters) const;
2396#ifndef SWIG
2397 template <typename Heuristic, typename... Args>
2398 IntVarFilteredDecisionBuilder* CreateIntVarFilteredDecisionBuilder(
2399 const Args&... args);
2400#endif
2401 LocalSearchPhaseParameters* CreateLocalSearchParameters(
2402 const RoutingSearchParameters& search_parameters, bool secondary_ls);
2403 DecisionBuilder* CreatePrimaryLocalSearchDecisionBuilder(
2404 const RoutingSearchParameters& search_parameters);
2405 void SetupDecisionBuilders(const RoutingSearchParameters& search_parameters);
2406 void SetupMetaheuristics(const RoutingSearchParameters& search_parameters);
2407 void SetupAssignmentCollector(
2408 const RoutingSearchParameters& search_parameters);
2409 void SetupTrace(const RoutingSearchParameters& search_parameters);
2410 void SetupImprovementLimit(const RoutingSearchParameters& search_parameters);
2411 void SetupSearchMonitors(const RoutingSearchParameters& search_parameters);
2412 bool UsesLightPropagation(
2413 const RoutingSearchParameters& search_parameters) const;
2414 GetTabuVarsCallback tabu_var_callback_;
2415
2416 // Detects implicit pickup delivery pairs. These pairs are
2417 // non-pickup/delivery pairs for which there exists a unary dimension such
2418 // that the demand d of the implicit pickup is positive and the demand of the
2419 // implicit delivery is equal to -d.
2420 void DetectImplicitPickupAndDeliveries();
2421
2422 int GetVehicleStartClass(int64_t start) const;
2423
2424 void InitSameVehicleGroups(int number_of_groups) {
2425 same_vehicle_group_.assign(Size(), 0);
2426 same_vehicle_groups_.assign(number_of_groups, {});
2427 }
2428 void SetSameVehicleGroup(int index, int group) {
2429 same_vehicle_group_[index] = group;
2430 same_vehicle_groups_[group].push_back(index);
2431 }
2432
2433 void InitSameActiveVarGroups(int number_of_groups) {
2434 same_active_var_group_.assign(Size(), 0);
2435 same_active_var_groups_.assign(number_of_groups, {});
2436 }
2437 void SetSameActiveVarGroup(int index, int group) {
2438 same_active_var_group_[index] = group;
2439 same_active_var_groups_[group].push_back(index);
2440 }
2441
2444 int GetGlobalCumulOptimizerIndex(const RoutingDimension& dimension) const;
2445 int GetLocalCumulOptimizerIndex(const RoutingDimension& dimension) const;
2446
2448 std::unique_ptr<Solver> solver_;
2449 int nodes_;
2450 int vehicles_;
2451 int max_active_vehicles_;
2452 Constraint* no_cycle_constraint_ = nullptr;
2454 std::vector<IntVar*> nexts_;
2455 std::vector<IntVar*> vehicle_vars_;
2456 std::vector<IntVar*> active_;
2462 // clang-format off
2463 std::vector<std::vector<IntVar*> > resource_vars_;
2464 // clang-format on
2465 // The following vectors are indexed by vehicle index.
2466 std::vector<IntVar*> vehicle_active_;
2467 std::vector<IntVar*> vehicle_route_considered_;
2472 std::vector<IntVar*> is_bound_to_end_;
2473 mutable RevSwitch is_bound_to_end_ct_added_;
2475 absl::flat_hash_map<std::string, DimensionIndex> dimension_name_to_index_;
2476 util_intops::StrongVector<DimensionIndex, RoutingDimension*> dimensions_;
2481 // clang-format off
2482 std::vector<std::unique_ptr<ResourceGroup> > resource_groups_;
2484 util_intops::StrongVector<DimensionIndex, std::vector<int> >
2485 dimension_resource_group_indices_;
2486
2490 std::vector<DimensionCumulOptimizers<GlobalDimensionCumulOptimizer> >
2491 global_dimension_optimizers_;
2492 util_intops::StrongVector<DimensionIndex, int> global_optimizer_index_;
2493 std::vector<DimensionCumulOptimizers<LocalDimensionCumulOptimizer> >
2494 local_dimension_optimizers_;
2495 util_intops::StrongVector<DimensionIndex, int> local_optimizer_index_;
2496 // clang-format on
2497 std::string primary_constrained_dimension_;
2499 IntVar* cost_ = nullptr;
2500 std::vector<int> vehicle_to_transit_cost_;
2501 std::vector<int64_t> fixed_cost_of_vehicle_;
2502 std::vector<CostClassIndex> cost_class_index_of_vehicle_;
2503 bool has_vehicle_with_zero_cost_class_;
2504 std::vector<int64_t> linear_cost_factor_of_vehicle_;
2505 std::vector<int64_t> quadratic_cost_factor_of_vehicle_;
2506 bool vehicle_amortized_cost_factors_set_;
2507 std::vector<
2508 std::function<std::optional<int64_t>(const std::vector<int64_t>&)>>
2509 route_evaluators_;
2521 std::vector<bool> vehicle_used_when_empty_;
2522#ifndef SWIG
2523 absl::flat_hash_map<
2524 std::pair<std::string, std::string>,
2525 std::vector<Solver::PathEnergyCostConstraintSpecification::EnergyCost>,
2526 absl::Hash<std::pair<std::string, std::string>>>
2527 force_distance_to_energy_costs_;
2528 util_intops::StrongVector<CostClassIndex, CostClass> cost_classes_;
2529#endif // SWIG
2530 bool costs_are_homogeneous_across_vehicles_;
2531 bool cache_callbacks_;
2532 mutable std::vector<CostCacheElement> cost_cache_;
2533 std::vector<VehicleClassIndex> vehicle_class_index_of_vehicle_;
2534 int num_vehicle_classes_;
2535
2536 VehicleTypeContainer vehicle_type_container_;
2537 std::function<int(int64_t)> vehicle_start_class_callback_;
2539 util_intops::StrongVector<DisjunctionIndex, Disjunction> disjunctions_;
2540 // clang-format off
2541 std::vector<std::vector<DisjunctionIndex> > index_to_disjunctions_;
2543 std::vector<ValuedNodes<int64_t> > same_vehicle_costs_;
2545#ifndef SWIG
2546 std::vector<absl::flat_hash_set<int>> allowed_vehicles_;
2547#endif // SWIG
2549 std::vector<PickupDeliveryPair> pickup_delivery_pairs_;
2550 std::vector<PickupDeliveryPair>
2551 implicit_pickup_delivery_pairs_without_alternatives_;
2552 std::vector<std::pair<DisjunctionIndex, DisjunctionIndex> >
2553 pickup_delivery_disjunctions_;
2554 // If node_index is a pickup, index_to_pickup_position_[node_index] contains
2555 // the PickupDeliveryPosition {pickup_delivery_index, alternative_index}
2556 // such that (pickup_delivery_pairs_[pickup_delivery_index]
2557 // .pickup_alternatives)[alternative_index] == node_index
2558 std::vector<PickupDeliveryPosition> index_to_pickup_position_;
2559 // Same as above for deliveries.
2560 std::vector<PickupDeliveryPosition> index_to_delivery_position_;
2561 // clang-format on
2562 std::vector<PickupAndDeliveryPolicy> vehicle_pickup_delivery_policy_;
2563 // Same vehicle group to which a node belongs.
2564 std::vector<int> same_vehicle_group_;
2565 // Same vehicle node groups.
2566 std::vector<std::vector<int>> same_vehicle_groups_;
2567 // Same active var group to which a node belongs.
2568 std::vector<int> same_active_var_group_;
2569 // Same active var groups.
2570 std::vector<std::vector<int>> same_active_var_groups_;
2571 // Node visit types
2572 // Variable index to visit type index.
2573 std::vector<int> index_to_visit_type_;
2574 // Variable index to VisitTypePolicy.
2575 std::vector<VisitTypePolicy> index_to_type_policy_;
2576 // clang-format off
2577 std::vector<std::vector<int> > single_nodes_of_type_;
2578 std::vector<std::vector<int> > pair_indices_of_type_;
2579
2580 std::vector<absl::flat_hash_set<int> >
2581 hard_incompatible_types_per_type_index_;
2582 std::vector<absl::flat_hash_set<int> >
2583 temporal_incompatible_types_per_type_index_;
2584 const absl::flat_hash_set<int> empty_incompatibility_set_;
2585
2586 std::vector<std::vector<absl::flat_hash_set<int> > >
2587 same_vehicle_required_type_alternatives_per_type_index_;
2588 std::vector<std::vector<absl::flat_hash_set<int> > >
2589 required_type_alternatives_when_adding_type_index_;
2590 std::vector<std::vector<absl::flat_hash_set<int> > >
2591 required_type_alternatives_when_removing_type_index_;
2592 const std::vector<absl::flat_hash_set<int>> empty_required_type_alternatives_;
2593 absl::flat_hash_map</*type*/int, absl::flat_hash_set<VisitTypePolicy> >
2594 trivially_infeasible_visit_types_to_policies_;
2595
2596 // Visit types sorted topologically based on required-->dependent requirement
2597 // arcs between the types (if the requirement/dependency graph is acyclic).
2598 // Visit types of the same topological level are sorted in each sub-vector
2599 // by decreasing requirement "tightness", computed as the pair of the two
2600 // following criteria:
2601 //
2602 // 1) How highly *dependent* this type is, determined by
2603 // (total number of required alternative sets for that type)
2604 // / (average number of types in the required alternative sets)
2605 // 2) How highly *required* this type t is, computed as
2606 // SUM_{S required set containing t} ( 1 / |S| ),
2607 // i.e. the sum of reverse number of elements of all required sets
2608 // containing the type t.
2609 //
2610 // The higher these two numbers, the tighter the type is wrt requirements.
2611 std::vector<std::vector<int> > topologically_sorted_visit_types_;
2612 // clang-format on
2613 int num_visit_types_;
2614 // Two indices are equivalent if they correspond to the same node (as given
2615 // to the constructors taking a RoutingIndexManager).
2616 std::vector<int> index_to_equivalence_class_;
2617 const PathsMetadata paths_metadata_;
2618 // TODO(user): b/62478706 Once the port is done, this shouldn't be needed
2619 // anymore.
2620 RoutingIndexManager manager_;
2621 int start_end_count_;
2622 // Model status
2623 bool closed_ = false;
2624 RoutingSearchStatus::Value status_ = RoutingSearchStatus::ROUTING_NOT_SOLVED;
2625 bool enable_deep_serialization_ = true;
2626
2627 // Secondary routing solver
2628 RoutingModel* secondary_model_ = nullptr;
2629 RoutingSearchParameters secondary_parameters_;
2630 std::unique_ptr<SecondaryOptimizer> secondary_optimizer_;
2631
2632 // Search data
2633 std::vector<DecisionBuilder*> first_solution_decision_builders_;
2634 std::vector<IntVarFilteredDecisionBuilder*>
2635 first_solution_filtered_decision_builders_;
2636 Solver::IndexEvaluator2 first_solution_evaluator_;
2637 FirstSolutionStrategy::Value automatic_first_solution_strategy_ =
2638 FirstSolutionStrategy::UNSET;
2639 const Assignment* hint_ = nullptr;
2640 std::vector<LocalSearchOperator*> local_search_operators_;
2641 std::vector<SearchMonitor*> monitors_;
2642 std::vector<SearchMonitor*> secondary_ls_monitors_;
2643 std::vector<SearchMonitor*> at_solution_monitors_;
2644 std::vector<std::function<void()>> restore_dimension_values_reset_callbacks_;
2645 int monitors_before_setup_ = 0;
2646 int monitors_after_setup_ = 0;
2647 SearchMonitor* metaheuristic_ = nullptr;
2648 SearchMonitor* search_log_ = nullptr;
2649 bool local_optimum_reached_ = false;
2650 // Best lower bound found during the search.
2651 int64_t objective_lower_bound_ = kint64min;
2652 SolutionCollector* collect_assignments_ = nullptr;
2653 SolutionCollector* collect_secondary_ls_assignments_ = nullptr;
2654 SolutionCollector* collect_one_assignment_ = nullptr;
2655 SolutionCollector* optimized_dimensions_assignment_collector_ = nullptr;
2656 RoutingSearchParameters search_parameters_;
2657 DecisionBuilder* solve_db_ = nullptr;
2658 DecisionBuilder* improve_db_ = nullptr;
2659 DecisionBuilder* secondary_ls_db_ = nullptr;
2660 DecisionBuilder* restore_assignment_ = nullptr;
2661 DecisionBuilder* restore_tmp_assignment_ = nullptr;
2662 Assignment* assignment_ = nullptr;
2663 Assignment* preassignment_ = nullptr;
2664 Assignment* tmp_assignment_ = nullptr;
2665 LocalSearchOperator* primary_ls_operator_ = nullptr;
2666 LocalSearchOperator* secondary_ls_operator_ = nullptr;
2667 std::vector<IntVar*> extra_vars_;
2668 std::vector<IntervalVar*> extra_intervals_;
2669 std::vector<LocalSearchOperator*> extra_operators_;
2670 absl::flat_hash_map<FilterOptions, LocalSearchFilterManager*>
2671 local_search_filter_managers_;
2672 std::vector<LocalSearchFilterManager::FilterEvent> extra_filters_;
2673 absl::flat_hash_map<NodeNeighborsParameters,
2674 std::unique_ptr<NodeNeighborsByCostClass>>
2675 node_neighbors_by_cost_class_per_size_;
2676 std::unique_ptr<FinalizerVariables> finalizer_variables_;
2677#ifndef SWIG
2678 std::unique_ptr<SweepArranger> sweep_arranger_;
2679#endif
2680
2681 RegularLimit* limit_ = nullptr;
2682 RegularLimit* cumulative_limit_ = nullptr;
2683 RegularLimit* ls_limit_ = nullptr;
2684 RegularLimit* lns_limit_ = nullptr;
2685 RegularLimit* first_solution_lns_limit_ = nullptr;
2686 absl::Duration time_buffer_;
2687
2688 std::atomic<bool> interrupt_cp_sat_;
2689 std::atomic<bool> interrupt_cp_;
2690
2691 typedef std::pair<int64_t, int64_t> CacheKey;
2692 typedef absl::flat_hash_map<CacheKey, int64_t> TransitCallbackCache;
2693 typedef absl::flat_hash_map<CacheKey, StateDependentTransit>
2694 StateDependentTransitCallbackCache;
2695
2696 // All transit callbacks are stored in transit_evaluators_,
2697 // we refer to callbacks by the index in this vector.
2698 // We maintain unary_transit_evaluators_[] (with the same size) to store
2699 // callbacks that are unary:
2700 // - if a callback is unary, it is in unary_transit_evaluators_[i],
2701 // and a binary version is stored at transit_evaluators_[i].
2702 // - if a callback is binary, it is stored at transit_evaluators_[i],
2703 // and unary_transit_evaluators_[i] is nullptr.
2704 std::vector<TransitCallback1> unary_transit_evaluators_;
2705 std::vector<TransitCallback2> transit_evaluators_;
2706 std::vector<TransitEvaluatorSign> transit_evaluator_sign_;
2707
2708 std::vector<VariableIndexEvaluator2> state_dependent_transit_evaluators_;
2709 std::vector<std::unique_ptr<StateDependentTransitCallbackCache>>
2710 state_dependent_transit_evaluators_cache_;
2711
2712 std::vector<CumulDependentTransitCallback2>
2713 cumul_dependent_transit_evaluators_;
2714
2715 // Returns global BinCapacities state, may be nullptr.
2716 std::unique_ptr<BinCapacities> bin_capacities_;
2717
2718 friend class RoutingDimension;
2719 friend class RoutingModelInspector;
2720 friend class ResourceGroup::Resource;
2721};
2722
2724class OR_DLL RoutingModelVisitor : public BaseObject {
2725 public:
2727 static const char kLightElement[];
2728 static const char kLightElement2[];
2729 static const char kRemoveValues[];
2730};
2731
2732#if !defined(SWIG)
2735class DisjunctivePropagator {
2736 public:
2742 struct Tasks {
2743 int num_chain_tasks = 0;
2744 std::vector<int64_t> start_min;
2745 std::vector<int64_t> start_max;
2746 std::vector<int64_t> duration_min;
2747 std::vector<int64_t> duration_max;
2748 std::vector<int64_t> end_min;
2749 std::vector<int64_t> end_max;
2750 std::vector<bool> is_preemptible;
2751 std::vector<const SortedDisjointIntervalList*> forbidden_intervals;
2752 std::vector<std::pair<int64_t, int64_t>> distance_duration;
2753 int64_t span_min = 0;
2754 int64_t span_max = kint64max;
2755
2756 void Clear() {
2757 start_min.clear();
2758 start_max.clear();
2759 duration_min.clear();
2760 duration_max.clear();
2761 end_min.clear();
2762 end_max.clear();
2763 is_preemptible.clear();
2764 forbidden_intervals.clear();
2765 distance_duration.clear();
2766 span_min = 0;
2767 span_max = kint64max;
2768 num_chain_tasks = 0;
2769 }
2770 };
2771
2774 bool Propagate(Tasks* tasks);
2775
2777 bool Precedences(Tasks* tasks);
2780 bool MirrorTasks(Tasks* tasks);
2782 bool EdgeFinding(Tasks* tasks);
2785 bool DetectablePrecedencesWithChain(Tasks* tasks);
2787 bool ForbiddenIntervals(Tasks* tasks);
2789 bool DistanceDuration(Tasks* tasks);
2792 bool ChainSpanMin(Tasks* tasks);
2797 bool ChainSpanMinDynamic(Tasks* tasks);
2798
2799 private:
2802 sat::ThetaLambdaTree<int64_t> theta_lambda_tree_;
2804 std::vector<int> tasks_by_start_min_;
2805 std::vector<int> tasks_by_end_max_;
2806 std::vector<int> event_of_task_;
2807 std::vector<int> nonchain_tasks_by_start_max_;
2809 std::vector<int64_t> total_duration_before_;
2810};
2811
2812struct TravelBounds {
2813 std::vector<int64_t> min_travels;
2814 std::vector<int64_t> max_travels;
2815 std::vector<int64_t> pre_travels;
2816 std::vector<int64_t> post_travels;
2817};
2818
2819void AppendTasksFromPath(absl::Span<const int64_t> path,
2820 const TravelBounds& travel_bounds,
2821 const RoutingDimension& dimension,
2822 DisjunctivePropagator::Tasks* tasks);
2823void AppendTasksFromIntervals(const std::vector<IntervalVar*>& intervals,
2824 DisjunctivePropagator::Tasks* tasks);
2825void FillPathEvaluation(absl::Span<const int64_t> path,
2826 const RoutingModel::TransitCallback2& evaluator,
2827 std::vector<int64_t>* values);
2828void FillTravelBoundsOfVehicle(int vehicle, absl::Span<const int64_t> path,
2829 const RoutingDimension& dimension,
2830 TravelBounds* travel_bounds);
2831#endif // !defined(SWIG)
2832
2843class GlobalVehicleBreaksConstraint : public Constraint {
2844 public:
2845 explicit GlobalVehicleBreaksConstraint(const RoutingDimension* dimension);
2846 std::string DebugString() const override {
2847 return "GlobalVehicleBreaksConstraint";
2848 }
2849
2850 void Post() override;
2851 void InitialPropagate() override;
2852
2853 private:
2854 void PropagateNode(int node);
2855 void PropagateVehicle(int vehicle);
2856
2857 const RoutingModel* model_;
2858 const RoutingDimension* const dimension_;
2859 std::vector<Demon*> vehicle_demons_;
2860 std::vector<int64_t> path_;
2861
2866 void FillPartialPathOfVehicle(int vehicle);
2867 void FillPathTravels(absl::Span<const int64_t> path);
2868
2879 class TaskTranslator {
2880 public:
2881 TaskTranslator(IntVar* start, int64_t before_start, int64_t after_start)
2882 : start_(start),
2883 before_start_(before_start),
2884 after_start_(after_start) {}
2885 explicit TaskTranslator(IntervalVar* interval) : interval_(interval) {}
2886 TaskTranslator() = default;
2887
2888 void SetStartMin(int64_t value) {
2889 if (start_ != nullptr) {
2890 start_->SetMin(CapAdd(before_start_, value));
2891 } else if (interval_ != nullptr) {
2892 interval_->SetStartMin(value);
2893 }
2894 }
2895 void SetStartMax(int64_t value) {
2896 if (start_ != nullptr) {
2897 start_->SetMax(CapAdd(before_start_, value));
2898 } else if (interval_ != nullptr) {
2899 interval_->SetStartMax(value);
2900 }
2901 }
2902 void SetDurationMin(int64_t value) {
2903 if (interval_ != nullptr) {
2904 interval_->SetDurationMin(value);
2905 }
2906 }
2907 void SetEndMin(int64_t value) {
2908 if (start_ != nullptr) {
2909 start_->SetMin(CapSub(value, after_start_));
2910 } else if (interval_ != nullptr) {
2911 interval_->SetEndMin(value);
2912 }
2913 }
2914 void SetEndMax(int64_t value) {
2915 if (start_ != nullptr) {
2916 start_->SetMax(CapSub(value, after_start_));
2917 } else if (interval_ != nullptr) {
2918 interval_->SetEndMax(value);
2919 }
2920 }
2921
2922 private:
2923 IntVar* start_ = nullptr;
2924 int64_t before_start_;
2925 int64_t after_start_;
2926 IntervalVar* interval_ = nullptr;
2927 };
2928
2930 std::vector<TaskTranslator> task_translators_;
2931
2933 DisjunctivePropagator disjunctive_propagator_;
2934 DisjunctivePropagator::Tasks tasks_;
2935
2937 TravelBounds travel_bounds_;
2938};
2939
2940class TypeRegulationsChecker {
2941 public:
2942 explicit TypeRegulationsChecker(const RoutingModel& model);
2943 virtual ~TypeRegulationsChecker() = default;
2944
2945 bool CheckVehicle(int vehicle,
2946 const std::function<int64_t(int64_t)>& next_accessor);
2947
2948 protected:
2949#ifndef SWIG
2950 using VisitTypePolicy = RoutingModel::VisitTypePolicy;
2951#endif // SWIG
2952
2953 struct TypePolicyOccurrence {
2957 int num_type_added_to_vehicle = 0;
2963 int num_type_removed_from_vehicle = 0;
2968 int position_of_last_type_on_vehicle_up_to_visit = -1;
2969 };
2970
2975 bool TypeOccursOnRoute(int type) const;
2982 bool TypeCurrentlyOnRoute(int type, int pos) const;
2983
2984 void InitializeCheck(int vehicle,
2985 const std::function<int64_t(int64_t)>& next_accessor);
2986 virtual void OnInitializeCheck() {}
2987 virtual bool HasRegulationsToCheck() const = 0;
2988 virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy,
2989 int pos) = 0;
2990 virtual bool FinalizeCheck() const { return true; }
2991
2992 const RoutingModel& model_;
2993
2994 private:
2995 std::vector<TypePolicyOccurrence> occurrences_of_type_;
2996 std::vector<int64_t> current_route_visits_;
2997};
2998
3000class TypeIncompatibilityChecker : public TypeRegulationsChecker {
3001 public:
3002 TypeIncompatibilityChecker(const RoutingModel& model,
3003 bool check_hard_incompatibilities);
3004 ~TypeIncompatibilityChecker() override = default;
3005
3006 private:
3007 bool HasRegulationsToCheck() const override;
3008 bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos) override;
3012 bool check_hard_incompatibilities_;
3013};
3014
3016class TypeRequirementChecker : public TypeRegulationsChecker {
3017 public:
3018 explicit TypeRequirementChecker(const RoutingModel& model)
3019 : TypeRegulationsChecker(model) {}
3020 ~TypeRequirementChecker() override = default;
3021
3022 private:
3023 bool HasRegulationsToCheck() const override;
3024 void OnInitializeCheck() override {
3025 types_with_same_vehicle_requirements_on_route_.clear();
3026 }
3027 // clang-format off
3030 bool CheckRequiredTypesCurrentlyOnRoute(
3031 const std::vector<absl::flat_hash_set<int> >& required_type_alternatives,
3032 int pos);
3033 // clang-format on
3034 bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos) override;
3035 bool FinalizeCheck() const override;
3036
3037 absl::flat_hash_set<int> types_with_same_vehicle_requirements_on_route_;
3038};
3039
3080class TypeRegulationsConstraint : public Constraint {
3081 public:
3082 explicit TypeRegulationsConstraint(const RoutingModel& model);
3083
3084 void Post() override;
3085 void InitialPropagate() override;
3086
3087 private:
3088 void PropagateNodeRegulations(int node);
3089 void CheckRegulationsOnVehicle(int vehicle);
3090
3091 const RoutingModel& model_;
3092 TypeIncompatibilityChecker incompatibility_checker_;
3093 TypeRequirementChecker requirement_checker_;
3094 std::vector<Demon*> vehicle_demons_;
3095};
3096
3109struct BoundCost {
3110 int64_t bound;
3111 int64_t cost;
3112 BoundCost() : bound(0), cost(0) {}
3113 BoundCost(int64_t bound, int64_t cost) : bound(bound), cost(cost) {}
3114};
3115
3116class SimpleBoundCosts {
3117 public:
3118 SimpleBoundCosts(int num_bounds, BoundCost default_bound_cost)
3119 : bound_costs_(num_bounds, default_bound_cost) {}
3120#ifndef SWIG
3121 BoundCost& bound_cost(int element) { return bound_costs_[element]; }
3122#endif
3123 BoundCost bound_cost(int element) const { return bound_costs_[element]; }
3124 int Size() { return bound_costs_.size(); }
3125 SimpleBoundCosts(const SimpleBoundCosts&) = delete;
3126 SimpleBoundCosts operator=(const SimpleBoundCosts&) = delete;
3127
3128 private:
3129 std::vector<BoundCost> bound_costs_;
3130};
3131
3149// TODO(user): Break constraints need to know the service time of nodes
3152class RoutingDimension {
3153 public:
3154 // This type is neither copyable nor movable.
3155 RoutingDimension(const RoutingDimension&) = delete;
3156 RoutingDimension& operator=(const RoutingDimension&) = delete;
3157
3158 ~RoutingDimension();
3160 RoutingModel* model() const { return model_; }
3164 int64_t GetTransitValue(int64_t from_index, int64_t to_index,
3165 int64_t vehicle) const;
3168 int64_t GetTransitValueFromClass(int64_t from_index, int64_t to_index,
3169 int64_t vehicle_class) const {
3170 return model_->TransitCallback(class_evaluators_[vehicle_class])(from_index,
3171 to_index);
3172 }
3175 IntVar* CumulVar(int64_t index) const { return cumuls_[index]; }
3176 IntVar* TransitVar(int64_t index) const { return transits_[index]; }
3177 IntVar* FixedTransitVar(int64_t index) const {
3178 return fixed_transits_[index];
3179 }
3180 IntVar* SlackVar(int64_t index) const { return slacks_[index]; }
3181
3184 // TODO(user): Routing should not store its data in a CP model.
3185
3187 void SetCumulVarRange(int64_t index, int64_t min, int64_t max) {
3188 CumulVar(index)->SetRange(min, max);
3189 }
3191 int64_t GetCumulVarMin(int64_t index) const { return CumulVar(index)->Min(); }
3193 int64_t GetCumulVarMax(int64_t index) const { return CumulVar(index)->Max(); }
3194
3195#if !defined(SWIGPYTHON)
3198 const std::vector<IntVar*>& cumuls() const { return cumuls_; }
3199 const std::vector<IntVar*>& fixed_transits() const { return fixed_transits_; }
3200 const std::vector<IntVar*>& transits() const { return transits_; }
3201 const std::vector<IntVar*>& slacks() const { return slacks_; }
3202#if !defined(SWIGCSHARP) && !defined(SWIGJAVA)
3204 const std::vector<SortedDisjointIntervalList>& forbidden_intervals() const {
3205 return forbidden_intervals_;
3206 }
3208 SortedDisjointIntervalList GetAllowedIntervalsInRange(
3209 int64_t index, int64_t min_value, int64_t max_value) const;
3212 int64_t GetFirstPossibleGreaterOrEqualValueForNode(int64_t index,
3213 int64_t min_value) const {
3214 DCHECK_LT(index, forbidden_intervals_.size());
3215 const SortedDisjointIntervalList& forbidden_intervals =
3216 forbidden_intervals_[index];
3217 const auto first_forbidden_interval_it =
3218 forbidden_intervals.FirstIntervalGreaterOrEqual(min_value);
3219 if (first_forbidden_interval_it != forbidden_intervals.end() &&
3220 min_value >= first_forbidden_interval_it->start) {
3222 return CapAdd(first_forbidden_interval_it->end, 1);
3223 }
3225 return min_value;
3226 }
3231 int64_t GetLastPossibleLessOrEqualValueForNode(int64_t index,
3232 int64_t max_value) const {
3233 DCHECK_LT(index, forbidden_intervals_.size());
3234 const SortedDisjointIntervalList& forbidden_intervals =
3235 forbidden_intervals_[index];
3236 const auto last_forbidden_interval_it =
3237 forbidden_intervals.LastIntervalLessOrEqual(max_value);
3238 if (last_forbidden_interval_it != forbidden_intervals.end() &&
3239 max_value <= last_forbidden_interval_it->end) {
3241 return CapSub(last_forbidden_interval_it->start, 1);
3242 }
3244 return max_value;
3245 }
3247 const std::vector<int64_t>& vehicle_capacities() const {
3248 return vehicle_capacities_;
3249 }
3252 const RoutingModel::TransitCallback2& transit_evaluator(int vehicle) const {
3253 return model_->TransitCallback(
3254 class_evaluators_[vehicle_to_class_[vehicle]]);
3255 }
3256
3259 const RoutingModel::TransitCallback2& class_transit_evaluator(
3260 RoutingVehicleClassIndex vehicle_class) const {
3261 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3262 DCHECK_NE(vehicle, -1);
3263 return transit_evaluator(vehicle);
3264 }
3265
3267 bool IsUnary() const {
3268 for (int evaluator_index : class_evaluators_) {
3269 if (model_->UnaryTransitCallbackOrNull(evaluator_index) == nullptr) {
3270 return false;
3271 }
3272 }
3273 return true;
3274 }
3275
3279 const RoutingModel::TransitCallback1& GetUnaryTransitEvaluator(
3280 int vehicle) const {
3281 return model_->UnaryTransitCallbackOrNull(
3282 class_evaluators_[vehicle_to_class_[vehicle]]);
3283 }
3284 const RoutingModel::TransitCallback2& GetBinaryTransitEvaluator(
3285 int vehicle) const {
3286 return model_->TransitCallback(
3287 class_evaluators_[vehicle_to_class_[vehicle]]);
3288 }
3291 bool AreVehicleTransitsPositive(int vehicle) const {
3292 const int evaluator_index = class_evaluators_[vehicle_to_class_[vehicle]];
3293 return model()->transit_evaluator_sign_[evaluator_index] ==
3294 RoutingModel::kTransitEvaluatorSignPositiveOrZero;
3295 }
3296 bool AllTransitEvaluatorSignsAreUnknown() const;
3297 RoutingModel::TransitEvaluatorSign GetTransitEvaluatorSign(
3298 int vehicle) const {
3299 const int evaluator_index = class_evaluators_[vehicle_to_class_[vehicle]];
3300 return model()->transit_evaluator_sign_[evaluator_index];
3301 }
3302 int vehicle_to_class(int vehicle) const { return vehicle_to_class_[vehicle]; }
3303 int vehicle_to_cumul_dependent_class(int vehicle) const {
3304 if (vehicle_to_cumul_dependent_class_.empty()) {
3305 return -1;
3306 }
3307 DCHECK_LT(vehicle, vehicle_to_cumul_dependent_class_.size());
3308 return vehicle_to_cumul_dependent_class_[vehicle];
3309 }
3310#endif
3311#endif
3315 void SetSpanUpperBoundForVehicle(int64_t upper_bound, int vehicle);
3322 void SetSpanCostCoefficientForVehicle(int64_t coefficient, int vehicle);
3323 void SetSpanCostCoefficientForAllVehicles(int64_t coefficient);
3331 void SetSlackCostCoefficientForVehicle(int64_t coefficient, int vehicle);
3332 void SetSlackCostCoefficientForAllVehicles(int64_t coefficient);
3339 void SetGlobalSpanCostCoefficient(int64_t coefficient);
3340
3341#ifndef SWIG
3346 void SetCumulVarPiecewiseLinearCost(int64_t index,
3347 const PiecewiseLinearFunction& cost);
3350 bool HasCumulVarPiecewiseLinearCost(int64_t index) const;
3353 const PiecewiseLinearFunction* GetCumulVarPiecewiseLinearCost(
3354 int64_t index) const;
3355#endif
3356
3365 void SetCumulVarSoftUpperBound(int64_t index, int64_t upper_bound,
3366 int64_t coefficient);
3369 bool HasCumulVarSoftUpperBound(int64_t index) const;
3373 int64_t GetCumulVarSoftUpperBound(int64_t index) const;
3377 int64_t GetCumulVarSoftUpperBoundCoefficient(int64_t index) const;
3378
3388 void SetCumulVarSoftLowerBound(int64_t index, int64_t lower_bound,
3389 int64_t coefficient);
3392 bool HasCumulVarSoftLowerBound(int64_t index) const;
3396 int64_t GetCumulVarSoftLowerBound(int64_t index) const;
3400 int64_t GetCumulVarSoftLowerBoundCoefficient(int64_t index) const;
3416 // TODO(user): Remove if !defined when routing.i is repaired.
3417#if !defined(SWIGPYTHON)
3418 void SetBreakIntervalsOfVehicle(std::vector<IntervalVar*> breaks, int vehicle,
3419 int pre_travel_evaluator,
3420 int post_travel_evaluator);
3421#endif // !defined(SWIGPYTHON)
3422
3424 void SetBreakIntervalsOfVehicle(std::vector<IntervalVar*> breaks, int vehicle,
3425 std::vector<int64_t> node_visit_transits);
3426
3431 void SetBreakDistanceDurationOfVehicle(int64_t distance, int64_t duration,
3432 int vehicle);
3435 void InitializeBreaks();
3437 bool HasBreakConstraints() const;
3438#if !defined(SWIGPYTHON)
3441 void SetBreakIntervalsOfVehicle(
3442 std::vector<IntervalVar*> breaks, int vehicle,
3443 std::vector<int64_t> node_visit_transits,
3444 std::function<int64_t(int64_t, int64_t)> delays);
3445
3447 const std::vector<IntervalVar*>& GetBreakIntervalsOfVehicle(
3448 int vehicle) const;
3451 // clang-format off
3452 const std::vector<std::pair<int64_t, int64_t> >&
3453 GetBreakDistanceDurationOfVehicle(int vehicle) const;
3454 // clang-format on
3455#endif
3456 int GetPreTravelEvaluatorOfVehicle(int vehicle) const;
3457 int GetPostTravelEvaluatorOfVehicle(int vehicle) const;
3458
3460 const RoutingDimension* base_dimension() const { return base_dimension_; }
3468 int64_t ShortestTransitionSlack(int64_t node) const;
3469
3471 const std::string& name() const { return name_; }
3472
3474#ifndef SWIG
3475 const ReverseArcListGraph<int, int>& GetPathPrecedenceGraph() const {
3476 return path_precedence_graph_;
3477 }
3478#endif // SWIG
3479
3489 typedef std::function<int64_t(int, int)> PickupToDeliveryLimitFunction;
3490
3491 void SetPickupToDeliveryLimitFunctionForPair(
3492 PickupToDeliveryLimitFunction limit_function, int pair_index);
3493
3494 bool HasPickupToDeliveryLimits() const;
3495#ifndef SWIG
3496 int64_t GetPickupToDeliveryLimitForPair(int pair_index,
3497 int pickup_alternative_index,
3498 int delivery_alternative_index) const;
3499
3500 struct NodePrecedence {
3501 int64_t first_node;
3502 int64_t second_node;
3503 int64_t offset;
3504 enum class PerformedConstraint {
3505 // first_node and/or second_node can be unperformed.
3506 kFirstAndSecondIndependent,
3507 // if second_node is performed, first_node must be performed.
3508 kSecondImpliesFirst,
3509 // first_node is performed iff second_node is performed.
3510 kFirstAndSecondEqual,
3511 };
3512 PerformedConstraint performed_constraint;
3513 };
3514
3515 void AddNodePrecedence(NodePrecedence precedence) {
3516 node_precedences_.push_back(precedence);
3517 }
3518 const std::vector<NodePrecedence>& GetNodePrecedences() const {
3519 return node_precedences_;
3520 }
3523 enum class PrecedenceStatus {
3524 kActive,
3525 kInactive,
3526 kInvalid,
3527 };
3528 static PrecedenceStatus GetPrecedenceStatus(
3529 bool first_unperformed, bool second_unperformed,
3530 NodePrecedence::PerformedConstraint performed_constraint) {
3531 switch (performed_constraint) {
3532 case NodePrecedence::PerformedConstraint::kFirstAndSecondIndependent:
3533 if (first_unperformed || second_unperformed) {
3534 return PrecedenceStatus::kInactive;
3535 }
3536 break;
3537 case NodePrecedence::PerformedConstraint::kSecondImpliesFirst:
3538 if (first_unperformed) {
3539 if (!second_unperformed) return PrecedenceStatus::kInvalid;
3540 return PrecedenceStatus::kInactive;
3541 }
3542 if (second_unperformed) return PrecedenceStatus::kInactive;
3543 break;
3544 case NodePrecedence::PerformedConstraint::kFirstAndSecondEqual:
3545 if (first_unperformed != second_unperformed) {
3546 return PrecedenceStatus::kInvalid;
3547 }
3548 if (first_unperformed) return PrecedenceStatus::kInactive;
3549 break;
3550 }
3551 return PrecedenceStatus::kActive;
3552 }
3553 void AddNodePrecedence(
3554 int64_t first_node, int64_t second_node, int64_t offset,
3555 NodePrecedence::PerformedConstraint performed_constraint =
3556 NodePrecedence::PerformedConstraint::kFirstAndSecondIndependent) {
3557 AddNodePrecedence({first_node, second_node, offset, performed_constraint});
3558 }
3559#else
3560 void AddNodePrecedence(int64_t first_node, int64_t second_node,
3561 int64_t offset) {
3562 AddNodePrecedence(
3563 {first_node, second_node, offset,
3564 NodePrecedence::PerformedConstraint::kFirstAndSecondIndependent});
3565 }
3566#endif // SWIG
3567
3568 int64_t GetSpanUpperBoundForVehicle(int vehicle) const {
3569 return vehicle_span_upper_bounds_[vehicle];
3570 }
3571#ifndef SWIG
3572 const std::vector<int64_t>& vehicle_span_upper_bounds() const {
3573 return vehicle_span_upper_bounds_;
3574 }
3575#endif // SWIG
3576 int64_t GetSpanCostCoefficientForVehicle(int vehicle) const {
3577 return vehicle_span_cost_coefficients_[vehicle];
3578 }
3579#ifndef SWIG
3580 int64_t GetSpanCostCoefficientForVehicleClass(
3581 RoutingVehicleClassIndex vehicle_class) const {
3582 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3583 DCHECK_NE(vehicle, -1);
3584 return GetSpanCostCoefficientForVehicle(vehicle);
3585 }
3586#endif // SWIG
3587#ifndef SWIG
3588 const std::vector<int64_t>& vehicle_span_cost_coefficients() const {
3589 return vehicle_span_cost_coefficients_;
3590 }
3591#endif // SWIG
3592#ifndef SWIG
3593 const std::vector<int64_t>& vehicle_slack_cost_coefficients() const {
3594 return vehicle_slack_cost_coefficients_;
3595 }
3596#endif // SWIG
3597 int64_t GetSlackCostCoefficientForVehicle(int vehicle) const {
3598 return vehicle_slack_cost_coefficients_[vehicle];
3599 }
3600#ifndef SWIG
3601 int64_t GetSlackCostCoefficientForVehicleClass(
3602 RoutingVehicleClassIndex vehicle_class) const {
3603 const int vehicle = model_->GetVehicleOfClass(vehicle_class);
3604 DCHECK_NE(vehicle, -1);
3605 return GetSlackCostCoefficientForVehicle(vehicle);
3606 }
3607#endif // SWIG
3608 int64_t global_span_cost_coefficient() const {
3609 return global_span_cost_coefficient_;
3610 }
3611
3612 int64_t GetGlobalOptimizerOffset() const {
3613 DCHECK_GE(global_optimizer_offset_, 0);
3614 return global_optimizer_offset_;
3615 }
3616 int64_t GetLocalOptimizerOffsetForVehicle(int vehicle) const {
3617 if (vehicle >= local_optimizer_offset_for_vehicle_.size()) {
3618 return 0;
3619 }
3620 DCHECK_GE(local_optimizer_offset_for_vehicle_[vehicle], 0);
3621 return local_optimizer_offset_for_vehicle_[vehicle];
3622 }
3623
3626 void SetSoftSpanUpperBoundForVehicle(BoundCost bound_cost, int vehicle) {
3627 if (!HasSoftSpanUpperBounds()) {
3628 vehicle_soft_span_upper_bound_ = std::make_unique<SimpleBoundCosts>(
3629 model_->vehicles(), BoundCost{kint64max, 0});
3630 }
3631 vehicle_soft_span_upper_bound_->bound_cost(vehicle) = bound_cost;
3632 }
3633 bool HasSoftSpanUpperBounds() const {
3634 return vehicle_soft_span_upper_bound_ != nullptr;
3635 }
3636 BoundCost GetSoftSpanUpperBoundForVehicle(int vehicle) const {
3637 DCHECK(HasSoftSpanUpperBounds());
3638 return vehicle_soft_span_upper_bound_->bound_cost(vehicle);
3639 }
3642 void SetQuadraticCostSoftSpanUpperBoundForVehicle(BoundCost bound_cost,
3643 int vehicle) {
3644 if (!HasQuadraticCostSoftSpanUpperBounds()) {
3645 vehicle_quadratic_cost_soft_span_upper_bound_ =
3646 std::make_unique<SimpleBoundCosts>(model_->vehicles(),
3647 BoundCost{kint64max, 0});
3648 }
3649 vehicle_quadratic_cost_soft_span_upper_bound_->bound_cost(vehicle) =
3650 bound_cost;
3651 }
3652 bool HasQuadraticCostSoftSpanUpperBounds() const {
3653 return vehicle_quadratic_cost_soft_span_upper_bound_ != nullptr;
3654 }
3655 BoundCost GetQuadraticCostSoftSpanUpperBoundForVehicle(int vehicle) const {
3656 DCHECK(HasQuadraticCostSoftSpanUpperBounds());
3657 return vehicle_quadratic_cost_soft_span_upper_bound_->bound_cost(vehicle);
3658 }
3659
3660 private:
3661 struct SoftBound {
3662 IntVar* var;
3663 int64_t bound;
3664 int64_t coefficient;
3665 };
3666
3667 struct PiecewiseLinearCost {
3668 PiecewiseLinearCost() : var(nullptr), cost(nullptr) {}
3669 IntVar* var;
3670 std::unique_ptr<PiecewiseLinearFunction> cost;
3671 };
3672
3673 class SelfBased {};
3674 RoutingDimension(RoutingModel* model, std::vector<int64_t> vehicle_capacities,
3675 const std::string& name,
3676 const RoutingDimension* base_dimension);
3677 RoutingDimension(RoutingModel* model, std::vector<int64_t> vehicle_capacities,
3678 const std::string& name, SelfBased);
3679 void Initialize(const std::vector<int>& transit_evaluators,
3680 const std::vector<int>& cumul_dependent_transit_evaluators,
3681 const std::vector<int>& state_dependent_transit_evaluators,
3682 int64_t slack_max);
3683 void InitializeCumuls();
3684 void InitializeTransits(
3685 absl::Span<const int> transit_evaluators,
3686 absl::Span<const int> cumul_dependent_transit_evaluators,
3687 absl::Span<const int> state_dependent_transit_evaluators,
3688 int64_t slack_max);
3689 void InitializeTransitVariables(int64_t slack_max);
3691 void SetupCumulVarSoftUpperBoundCosts(
3692 std::vector<IntVar*>* cost_elements) const;
3694 void SetupCumulVarSoftLowerBoundCosts(
3695 std::vector<IntVar*>* cost_elements) const;
3696 void SetupCumulVarPiecewiseLinearCosts(
3697 std::vector<IntVar*>* cost_elements) const;
3700 void SetupGlobalSpanCost(std::vector<IntVar*>* cost_elements) const;
3701 void SetupSlackAndDependentTransitCosts() const;
3703 void CloseModel(bool use_light_propagation);
3704
3705 void SetOffsetForGlobalOptimizer(int64_t offset) {
3706 global_optimizer_offset_ = std::max(Zero(), offset);
3707 }
3709 void SetVehicleOffsetsForLocalOptimizer(std::vector<int64_t> offsets) {
3711 std::transform(offsets.begin(), offsets.end(), offsets.begin(),
3712 [](int64_t offset) { return std::max(Zero(), offset); });
3713 local_optimizer_offset_for_vehicle_ = std::move(offsets);
3714 }
3715
3716 std::vector<IntVar*> cumuls_;
3717 std::vector<SortedDisjointIntervalList> forbidden_intervals_;
3718 std::vector<IntVar*> capacity_vars_;
3719 const std::vector<int64_t> vehicle_capacities_;
3720 std::vector<IntVar*> transits_;
3721 std::vector<IntVar*> fixed_transits_;
3724 std::vector<int> class_evaluators_;
3725 std::vector<int> vehicle_to_class_;
3726
3730 std::vector<int> cumul_dependent_class_evaluators_;
3731 std::vector<int> vehicle_to_cumul_dependent_class_;
3732#ifndef SWIG
3733 ReverseArcListGraph<int, int> path_precedence_graph_;
3734#endif
3735 // For every {first_node, second_node, offset} element in node_precedences_,
3736 // if both first_node and second_node are performed, then
3737 // cumuls_[second_node] must be greater than (or equal to)
3738 // cumuls_[first_node] + offset.
3739 std::vector<NodePrecedence> node_precedences_;
3740
3741 // The transits of a dimension may depend on its cumuls or the cumuls of
3742 // another dimension. There can be no cycles, except for self loops, a
3743 // typical example for this is a time dimension.
3744 const RoutingDimension* const base_dimension_;
3745 // Values in state_dependent_class_evaluators_ correspond to the evaluators
3746 // in RoutingModel::state_dependent_transit_evaluators_ for each vehicle
3747 // class.
3748 std::vector<int> state_dependent_class_evaluators_;
3749 std::vector<int> state_dependent_vehicle_to_class_;
3750
3751 // For each pickup/delivery pair_index for which limits have been set,
3752 // pickup_to_delivery_limits_per_pair_index_[pair_index] contains the
3753 // PickupToDeliveryLimitFunction for the pickup and deliveries in this pair.
3754 std::vector<PickupToDeliveryLimitFunction>
3755 pickup_to_delivery_limits_per_pair_index_;
3756
3757 // Used if some vehicle has breaks in this dimension, typically time.
3758 bool break_constraints_are_initialized_ = false;
3759 // clang-format off
3760 std::vector<std::vector<IntervalVar*> > vehicle_break_intervals_;
3761 std::vector<std::vector<std::pair<int64_t, int64_t> > >
3762 vehicle_break_distance_duration_;
3763 // clang-format on
3764 // For each vehicle, stores the part of travel that is made directly
3765 // after (before) the departure (arrival) node of the travel.
3766 // These parts of the travel are non-interruptible, in particular by a break.
3767 std::vector<int> vehicle_pre_travel_evaluators_;
3768 std::vector<int> vehicle_post_travel_evaluators_;
3769
3770 std::vector<IntVar*> slacks_;
3771 std::vector<IntVar*> dependent_transits_;
3772 std::vector<int64_t> vehicle_span_upper_bounds_;
3773 int64_t global_span_cost_coefficient_;
3774 std::vector<int64_t> vehicle_span_cost_coefficients_;
3775 std::vector<int64_t> vehicle_slack_cost_coefficients_;
3776 std::vector<SoftBound> cumul_var_soft_upper_bound_;
3777 std::vector<SoftBound> cumul_var_soft_lower_bound_;
3778 std::vector<PiecewiseLinearCost> cumul_var_piecewise_linear_cost_;
3779 RoutingModel* const model_;
3780 const std::string name_;
3781 int64_t global_optimizer_offset_;
3782 std::vector<int64_t> local_optimizer_offset_for_vehicle_;
3784 std::unique_ptr<SimpleBoundCosts> vehicle_soft_span_upper_bound_;
3785 std::unique_ptr<SimpleBoundCosts>
3786 vehicle_quadratic_cost_soft_span_upper_bound_;
3787 friend class RoutingModel;
3788 friend class RoutingModelInspector;
3789};
3790
3795bool SolveModelWithSat(RoutingModel* model,
3796 const RoutingSearchParameters& search_parameters,
3797 const Assignment* initial_solution,
3798 Assignment* solution);
3799
3800#if !defined(SWIG)
3801IntVarLocalSearchFilter* MakeVehicleBreaksFilter(
3802 const RoutingModel& routing_model, const RoutingDimension& dimension);
3803#endif
3804
3805} // namespace operations_research
3806#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
#define OR_DLL
Definition base_export.h:27
-------— Local Search Phase Parameters -------—
A reversible switch that can switch once from false to true.
Base class of all search limits.
A search monitor is a simple set of callbacks to monitor all search events.
For the time being, Solver is neither MT_SAFE nor MT_HOT.
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
time_limit
Definition solve.cc:22
bool operator==(const ProtoEnumIterator< E > &a, const ProtoEnumIterator< E > &b)
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, std::shared_ptr< Variable > i)
dual_gradient T(y - `dual_solution`) class DiagonalTrustRegionProblemFromQp
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.
int64_t CapAdd(int64_t x, int64_t y)
void CapAddTo(int64_t x, int64_t *y)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
std::function< const FloatSlopePiecewiseLinearFunction *(int64_t, int64_t)> RoutingCumulDependentTransitCallback2
std::function< int64_t(int64_t)> RoutingTransitCallback1
void FillTravelBoundsOfVehicle(int vehicle, absl::Span< const int64_t > path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
void AppendTasksFromPath(absl::Span< const int64_t > path, const TravelBounds &travel_bounds, const RoutingDimension &dimension, DisjunctivePropagator::Tasks *tasks)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
bool SolveModelWithSat(RoutingModel *model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
void FillPathEvaluation(absl::Span< const int64_t > path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
Definition routing.cc:6745
void AppendTasksFromIntervals(const std::vector< IntervalVar * > &intervals, DisjunctivePropagator::Tasks *tasks)
bool Next()
trees with all degrees equal to
std::string DebugString() const
--— Constraint --—
Definition model.cc:804
static const int64_t kint64max
Definition types.h:32
static const int64_t kint64min
Definition types.h:31