Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_search.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_SEARCH_H_
15#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_SEARCH_H_
16
17#include <sys/types.h>
18
19#include <algorithm>
20#include <cstdint>
21#include <deque>
22#include <functional>
23#include <initializer_list>
24#include <limits>
25#include <map>
26#include <memory>
27#include <optional>
28#include <queue>
29#include <set>
30#include <string>
31#include <tuple>
32#include <type_traits>
33#include <utility>
34#include <vector>
35
36#include "absl/container/flat_hash_map.h"
37#include "absl/container/flat_hash_set.h"
38#include "absl/log/check.h"
39#include "absl/types/span.h"
44#include "ortools/constraint_solver/routing_enums.pb.h"
45#include "ortools/constraint_solver/routing_parameters.pb.h"
48
49namespace operations_research {
50
51class IntVarFilteredHeuristic;
52#ifndef SWIG
57 public:
59 const RoutingModel::VehicleTypeContainer& vehicle_type_container)
60 : vehicle_type_container_(&vehicle_type_container) {}
61
62 int NumTypes() const { return vehicle_type_container_->NumTypes(); }
63
64 int Type(int vehicle) const { return vehicle_type_container_->Type(vehicle); }
65
68 void Reset(const std::function<bool(int)>& store_vehicle);
69
72 void Update(const std::function<bool(int)>& remove_vehicle);
73
74 int GetLowestFixedCostVehicleOfType(int type) const {
75 DCHECK_LT(type, NumTypes());
76 const std::set<VehicleClassEntry>& vehicle_classes =
77 sorted_vehicle_classes_per_type_[type];
78 if (vehicle_classes.empty()) {
79 return -1;
80 }
81 const int vehicle_class = (vehicle_classes.begin())->vehicle_class;
82 DCHECK(!vehicles_per_vehicle_class_[vehicle_class].empty());
83 return vehicles_per_vehicle_class_[vehicle_class][0];
84 }
85
87 int64_t fixed_cost) {
88 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
89 if (vehicles.empty()) {
92 std::set<VehicleClassEntry>& vehicle_classes =
93 sorted_vehicle_classes_per_type_[Type(vehicle)];
94 const auto& insertion =
95 vehicle_classes.insert({vehicle_class, fixed_cost});
96 DCHECK(insertion.second);
97 }
98 vehicles.push_back(vehicle);
99 }
100
104 int type, const std::function<bool(int)>& vehicle_is_compatible) const;
113 std::pair<int, int> GetCompatibleVehicleOfType(
114 int type, const std::function<bool(int)>& vehicle_is_compatible,
115 const std::function<bool(int)>& stop_and_return_vehicle);
116
117 private:
118 using VehicleClassEntry =
119 RoutingModel::VehicleTypeContainer::VehicleClassEntry;
120 const RoutingModel::VehicleTypeContainer* const vehicle_type_container_;
121 // clang-format off
122 std::vector<std::set<VehicleClassEntry> > sorted_vehicle_classes_per_type_;
123 std::vector<std::vector<int> > vehicles_per_vehicle_class_;
124 // clang-format on
125};
126
129operations_research::FirstSolutionStrategy::Value
130AutomaticFirstSolutionStrategy(bool has_pickup_deliveries,
131 bool has_node_precedences,
132 bool has_single_vehicle_node);
133
136std::vector<int64_t> ComputeVehicleEndChainStarts(const RoutingModel& model);
137
150
152// TODO(user): Eventually move this to the core CP solver library
155 public:
157 std::unique_ptr<IntVarFilteredHeuristic> heuristic);
158
160
161 Decision* Next(Solver* solver) override;
162
163 std::string DebugString() const override;
164
166 int64_t number_of_decisions() const;
167 int64_t number_of_rejects() const;
168
169 private:
170 const std::unique_ptr<IntVarFilteredHeuristic> heuristic_;
171};
172
175 public:
176 IntVarFilteredHeuristic(Solver* solver, const std::vector<IntVar*>& vars,
177 const std::vector<IntVar*>& secondary_vars,
178 LocalSearchFilterManager* filter_manager);
179
181
185
188 int64_t number_of_decisions() const { return number_of_decisions_; }
189 int64_t number_of_rejects() const { return number_of_rejects_; }
190
191 virtual std::string DebugString() const { return "IntVarFilteredHeuristic"; }
192
193 protected:
195 void ResetSolution();
197 virtual void Initialize() {}
199 virtual bool InitializeSolution() { return true; }
201 virtual bool BuildSolutionInternal() = 0;
209 std::optional<int64_t> Evaluate(bool commit);
211 virtual bool StopSearch() { return false; }
214 void SetValue(int64_t index, int64_t value) {
215 DCHECK_LT(index, is_in_delta_.size());
216 if (!is_in_delta_[index]) {
217 delta_->FastAdd(vars_[index])->SetValue(value);
218 delta_indices_.push_back(index);
219 is_in_delta_[index] = true;
220 } else {
221 delta_->SetValue(vars_[index], value);
222 }
223 }
225 const std::vector<int>& delta_indices() const { return delta_indices_; }
228 int64_t Value(int64_t index) const {
230 }
232 bool Contains(int64_t index) const {
233 return assignment_->IntVarContainer().Element(index).Var() != nullptr;
234 }
236 IntVar* Var(int64_t index) const { return vars_[index]; }
238 int64_t SecondaryVarIndex(int64_t index) const {
239 DCHECK(HasSecondaryVars());
240 return index + base_vars_size_;
241 }
243 bool HasSecondaryVars() const { return base_vars_size_ != vars_.size(); }
245 bool IsSecondaryVar(int64_t index) const { return index >= base_vars_size_; }
247 void SynchronizeFilters();
248
250
251 private:
254 bool FilterAccept();
255
256 Solver* solver_;
257 std::vector<IntVar*> vars_;
258 const int base_vars_size_;
259 Assignment* const delta_;
260 std::vector<int> delta_indices_;
261 std::vector<bool> is_in_delta_;
262 Assignment* const empty_;
263 LocalSearchFilterManager* filter_manager_;
264 int64_t objective_upper_bound_;
266 int64_t number_of_decisions_;
267 int64_t number_of_rejects_;
268};
269
272 public:
273 RoutingFilteredHeuristic(RoutingModel* model,
274 std::function<bool()> stop_search,
275 LocalSearchFilterManager* filter_manager);
279 const std::function<int64_t(int64_t)>& next_accessor);
280 RoutingModel* model() const { return model_; }
282 int GetStartChainEnd(int vehicle) const { return start_chain_ends_[vehicle]; }
284 int GetEndChainStart(int vehicle) const { return end_chain_starts_[vehicle]; }
287 void MakeDisjunctionNodesUnperformed(int64_t node);
295
296 protected:
297 bool StopSearch() override { return stop_search_(); }
298 virtual void SetVehicleIndex(int64_t /*node*/, int /*vehicle*/) {}
299 virtual void ResetVehicleIndices() {}
300 bool VehicleIsEmpty(int vehicle) const {
301 return Value(model()->Start(vehicle)) == model()->End(vehicle);
302 }
303 void SetNext(int64_t node, int64_t next, int vehicle) {
304 SetValue(node, next);
305 if (HasSecondaryVars()) SetValue(SecondaryVarIndex(node), vehicle);
306 }
307
308 private:
310 bool InitializeSolution() override;
311
312 RoutingModel* const model_;
313 std::function<bool()> stop_search_;
314 std::vector<int64_t> start_chain_ends_;
315 std::vector<int64_t> end_chain_starts_;
316};
317
319 public:
322 RoutingModel* model, std::function<bool()> stop_search,
323 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
324 std::function<int64_t(int64_t)> penalty_evaluator,
325 LocalSearchFilterManager* filter_manager);
327
328 protected:
332 int64_t value;
333
334 bool operator<(const NodeInsertion& other) const {
335 return std::tie(value, insert_after, vehicle) <
336 std::tie(other.value, other.insert_after, other.vehicle);
337 }
338 };
340 int64_t distance;
342
343 bool operator<(const StartEndValue& other) const {
344 return std::tie(distance, vehicle) <
345 std::tie(other.distance, other.vehicle);
346 }
347 };
348 struct Seed {
350 int64_t neg_penalty;
354 bool is_node_index = true;
355 int index;
356
357 bool operator>(const Seed& other) const {
360 std::tie(other.num_allowed_vehicles, other.neg_penalty,
361 other.start_end_value, other.is_node_index, other.index);
362 }
363 };
364 // clang-format off
365 struct SeedQueue {
368
371 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed> >
376 };
377
383 std::vector<std::vector<StartEndValue> >
384 ComputeStartEndDistanceForVehicles(const std::vector<int>& vehicles);
385
390 std::vector<std::vector<StartEndValue> >* start_end_distances_per_node,
391 SeedQueue* sq);
392 // clang-format on
393
396 void AddSeedNodeToQueue(int node,
397 std::vector<StartEndValue>* start_end_distances,
398 SeedQueue* sq);
399
405 void InsertBetween(int64_t node, int64_t predecessor, int64_t successor,
406 int vehicle = -1);
412 int64_t node_to_insert, int64_t start, int64_t next_after_start,
413 int vehicle, bool ignore_cost,
414 std::vector<NodeInsertion>* node_insertions);
419 // TODO(user): Replace 'insert_before' and 'insert_after' by 'predecessor'
420 // and 'successor' in the code.
421 int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert,
422 int64_t insert_after,
423 int64_t insert_before,
424 int vehicle) const;
427 int64_t GetUnperformedValue(int64_t node_to_insert) const;
428
429 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator_;
430 std::function<int64_t(int64_t)> penalty_evaluator_;
431};
432
442 public:
466
469 RoutingModel* model, std::function<bool()> stop_search,
470 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
471 std::function<int64_t(int64_t)> penalty_evaluator,
472 LocalSearchFilterManager* filter_manager,
475 bool BuildSolutionInternal() override;
476 std::string DebugString() const override {
477 return "GlobalCheapestInsertionFilteredHeuristic";
478 }
479
480 private:
482 class NodeEntryQueue;
483
485 class PairEntry {
486 public:
487 PairEntry(int pickup_to_insert, int pickup_insert_after,
488 int delivery_to_insert, int delivery_insert_after, int vehicle,
489 int64_t bucket)
490 : value_(std::numeric_limits<int64_t>::max()),
491 heap_index_(-1),
492 pickup_to_insert_(pickup_to_insert),
493 pickup_insert_after_(pickup_insert_after),
494 delivery_to_insert_(delivery_to_insert),
495 delivery_insert_after_(delivery_insert_after),
496 vehicle_(vehicle),
497 bucket_(bucket) {}
498 // Note: for compatibility reasons, comparator follows tie-breaking rules
499 // used in the first version of GlobalCheapestInsertion.
500 bool operator<(const PairEntry& other) const {
501 // We give higher priority to insertions from lower buckets.
502 if (bucket_ != other.bucket_) {
503 return bucket_ > other.bucket_;
504 }
505 // We then compare by value, then we favor insertions (vehicle != -1).
506 // The rest of the tie-breaking is done with std::tie.
507 if (value_ != other.value_) {
508 return value_ > other.value_;
509 }
510 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
511 return vehicle_ == -1;
512 }
513 return std::tie(pickup_insert_after_, pickup_to_insert_,
514 delivery_insert_after_, delivery_to_insert_, vehicle_) >
515 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
516 other.delivery_insert_after_, other.delivery_to_insert_,
517 other.vehicle_);
518 }
519 void SetHeapIndex(int h) { heap_index_ = h; }
520 int GetHeapIndex() const { return heap_index_; }
521 void set_value(int64_t value) { value_ = value; }
522 int pickup_to_insert() const { return pickup_to_insert_; }
523 int pickup_insert_after() const { return pickup_insert_after_; }
524 void set_pickup_insert_after(int pickup_insert_after) {
525 pickup_insert_after_ = pickup_insert_after;
526 }
527 int delivery_to_insert() const { return delivery_to_insert_; }
528 int delivery_insert_after() const { return delivery_insert_after_; }
529 int vehicle() const { return vehicle_; }
530 void set_vehicle(int vehicle) { vehicle_ = vehicle; }
531
532 private:
533 int64_t value_;
534 int heap_index_;
535 int pickup_to_insert_;
536 int pickup_insert_after_;
537 int delivery_to_insert_;
538 int delivery_insert_after_;
539 int vehicle_;
540 int64_t bucket_;
541 };
542
543 typedef absl::flat_hash_set<PairEntry*> PairEntries;
544
546 template <typename T>
547 class EntryAllocator {
548 public:
549 EntryAllocator() {}
550 void Clear() {
551 entries_.clear();
552 free_entries_.clear();
553 }
554 template <typename... Args>
555 T* NewEntry(const Args&... args) {
556 if (!free_entries_.empty()) {
557 auto* entry = free_entries_.back();
558 free_entries_.pop_back();
559 *entry = T(args...);
560 return entry;
561 } else {
562 entries_.emplace_back(args...);
563 return &entries_.back();
564 }
565 }
566 void FreeEntry(T* entry) { free_entries_.push_back(entry); }
567
568 private:
570 std::deque<T> entries_;
571 std::vector<T*> free_entries_;
572 };
573
580 bool InsertPairsAndNodesByRequirementTopologicalOrder();
581
588 bool InsertPairs(
589 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket);
590
594 bool UseEmptyVehicleTypeCuratorForVehicle(int vehicle,
595 bool all_vehicles = true) {
596 return vehicle >= 0 && VehicleIsEmpty(vehicle) && all_vehicles;
597 }
598
605 bool InsertPairEntryUsingEmptyVehicleTypeCurator(
606 const absl::flat_hash_set<int>& pair_indices, PairEntry* pair_entry,
608 std::vector<PairEntries>* pickup_to_entries,
609 std::vector<PairEntries>* delivery_to_entries);
610
618 bool InsertNodesOnRoutes(
619 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
620 const absl::flat_hash_set<int>& vehicles);
621
629 bool InsertNodeEntryUsingEmptyVehicleTypeCurator(
630 const std::vector<bool>& nodes, bool all_vehicles, NodeEntryQueue* queue);
631
637 bool SequentialInsertNodes(
638 const std::map<int64_t, std::vector<int>>& nodes_by_bucket);
639
643 void DetectUsedVehicles(std::vector<bool>* is_vehicle_used,
644 std::vector<int>* unused_vehicles,
645 absl::flat_hash_set<int>* used_vehicles);
646
649 bool IsCheapestClassRepresentative(int vehicle) const;
650
654 void InsertFarthestNodesAsSeeds();
655
664 int InsertSeedNode(
665 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
666 SeedQueue* sq, std::vector<bool>* is_vehicle_used);
667 // clang-format on
668
671 bool InitializePairPositions(
672 const absl::flat_hash_set<int>& pair_indices,
674 std::vector<PairEntries>* pickup_to_entries,
675 std::vector<PairEntries>* delivery_to_entries);
681 void InitializeInsertionEntriesPerformingPair(
682 int64_t pickup, int64_t delivery,
684 std::vector<PairEntries>* pickup_to_entries,
685 std::vector<PairEntries>* delivery_to_entries);
689 bool UpdateAfterPairInsertion(
690 const absl::flat_hash_set<int>& pair_indices, int vehicle, int64_t pickup,
691 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
693 std::vector<PairEntries>* pickup_to_entries,
694 std::vector<PairEntries>* delivery_to_entries);
698 bool UpdateExistingPairEntriesOnChain(
699 int64_t insert_after_start, int64_t insert_after_end,
701 std::vector<PairEntries>* pickup_to_entries,
702 std::vector<PairEntries>* delivery_to_entries);
708 bool AddPairEntriesAfter(const absl::flat_hash_set<int>& pair_indices,
709 int vehicle, int64_t insert_after,
710 int64_t skip_entries_inserting_delivery_after,
712 std::vector<PairEntries>* pickup_to_entries,
713 std::vector<PairEntries>* delivery_to_entries) {
714 return AddPairEntriesWithDeliveryAfter(pair_indices, vehicle, insert_after,
715 priority_queue, pickup_to_entries,
716 delivery_to_entries) &&
717 AddPairEntriesWithPickupAfter(pair_indices, vehicle, insert_after,
718 skip_entries_inserting_delivery_after,
719 priority_queue, pickup_to_entries,
720 delivery_to_entries);
721 }
728 bool AddPairEntriesWithPickupAfter(
729 const absl::flat_hash_set<int>& pair_indices, int vehicle,
730 int64_t insert_after, int64_t skip_entries_inserting_delivery_after,
732 std::vector<PairEntries>* pickup_to_entries,
733 std::vector<PairEntries>* delivery_to_entries);
737 bool AddPairEntriesWithDeliveryAfter(
738 const absl::flat_hash_set<int>& pair_indices, int vehicle,
739 int64_t insert_after, AdjustablePriorityQueue<PairEntry>* priority_queue,
740 std::vector<PairEntries>* pickup_to_entries,
741 std::vector<PairEntries>* delivery_to_entries);
744 void DeletePairEntry(PairEntry* entry,
746 std::vector<PairEntries>* pickup_to_entries,
747 std::vector<PairEntries>* delivery_to_entries);
752 void AddPairEntry(int64_t pickup, int64_t pickup_insert_after,
753 int64_t delivery, int64_t delivery_insert_after,
754 int vehicle,
756 std::vector<PairEntries>* pickup_entries,
757 std::vector<PairEntries>* delivery_entries) const;
760 void UpdatePairEntry(
761 PairEntry* pair_entry,
762 AdjustablePriorityQueue<PairEntry>* priority_queue) const;
766 int64_t GetInsertionValueForPairAtPositions(int64_t pickup,
767 int64_t pickup_insert_after,
768 int64_t delivery,
769 int64_t delivery_insert_after,
770 int vehicle) const;
771
774 bool InitializePositions(const std::vector<bool>& nodes,
775 const absl::flat_hash_set<int>& vehicles,
776 NodeEntryQueue* queue);
782 void InitializeInsertionEntriesPerformingNode(
783 int64_t node, const absl::flat_hash_set<int>& vehicles,
784 NodeEntryQueue* queue);
787 bool UpdateAfterNodeInsertion(const std::vector<bool>& nodes, int vehicle,
788 int64_t node, int64_t insert_after,
789 bool all_vehicles, NodeEntryQueue* queue);
793 bool UpdateExistingNodeEntriesOnChain(const std::vector<bool>& nodes,
794 int vehicle, int64_t insert_after_start,
795 int64_t insert_after_end,
796 bool all_vehicles,
797 NodeEntryQueue* queue);
800 bool AddNodeEntriesAfter(const std::vector<bool>& nodes, int vehicle,
801 int64_t insert_after, bool all_vehicles,
802 NodeEntryQueue* queue);
803
807 void AddNodeEntry(int64_t node, int64_t insert_after, int vehicle,
808 bool all_vehicles, NodeEntryQueue* queue) const;
809
810 void ResetVehicleIndices() override {
811 node_index_to_vehicle_.assign(node_index_to_vehicle_.size(), -1);
812 }
813
814 void SetVehicleIndex(int64_t node, int vehicle) override {
815 DCHECK_LT(node, node_index_to_vehicle_.size());
816 node_index_to_vehicle_[node] = vehicle;
817 }
818
821 bool CheckVehicleIndices() const;
822
824 int64_t GetBucketOfNode(int node) const {
825 return model()->VehicleVar(node)->Size();
826 }
827
829 int64_t GetBucketOfPair(const PickupDeliveryPair& pair) const {
830 int64_t max_pickup_bucket = 0;
831 for (int64_t pickup : pair.pickup_alternatives) {
832 max_pickup_bucket = std::max(max_pickup_bucket, GetBucketOfNode(pickup));
833 }
834 int64_t max_delivery_bucket = 0;
835 for (int64_t delivery : pair.delivery_alternatives) {
836 max_delivery_bucket =
837 std::max(max_delivery_bucket, GetBucketOfNode(delivery));
838 }
839 return std::min(max_pickup_bucket, max_delivery_bucket);
840 }
841
844 template <typename T>
845 bool StopSearchAndCleanup(AdjustablePriorityQueue<T>* priority_queue) {
846 if (!StopSearch()) return false;
847 if constexpr (std::is_same_v<T, PairEntry>) {
848 pair_entry_allocator_.Clear();
849 }
850 priority_queue->Clear();
851 return true;
852 }
853
854 GlobalCheapestInsertionParameters gci_params_;
856 std::vector<int> node_index_to_vehicle_;
857
858 const RoutingModel::NodeNeighborsByCostClass*
859 node_index_to_neighbors_by_cost_class_;
860
861 std::unique_ptr<VehicleTypeCurator> empty_vehicle_type_curator_;
862
863 mutable EntryAllocator<PairEntry> pair_entry_allocator_;
864};
865
866// Holds sequences of insertions.
867// A sequence of insertions must be in the same path, each insertion must
868// take place either after the previously inserted node or further down the
869// path, never before.
871 private:
872 // InsertionSequenceContainer holds all insertion sequences in the same vector
873 // contiguously, each insertion sequence is defined by a pair of bounds.
874 // Using Insertion* directly to delimit bounds would cause out-of-memory reads
875 // when the underlying vector<Insertion> is extended and reallocated,
876 // so this stores integer bounds in InsertionBounds to delimit sequences,
877 // and InsertionSequenceIterator translates those bounds to
878 // Insertion*-based ranges (InsertionSequence) on-the-fly when iterating over
879 // all sequences.
880 struct InsertionBounds {
881 size_t begin;
882 size_t end;
883 int vehicle;
884 int64_t cost;
885 bool operator<(const InsertionBounds& other) const {
886 return std::tie(cost, vehicle, begin) <
887 std::tie(other.cost, other.vehicle, other.begin);
888 }
889 size_t Size() const { return end - begin; }
890 };
891
892 public:
893 struct Insertion {
894 int pred;
895 int node;
896 bool operator==(const Insertion& other) const {
897 return pred == other.pred && node == other.node;
898 }
899 };
900
901 // Represents an insertion sequence as passed to AddInsertionSequence.
902 // This only allows to modify the cost, as a means to reorder sequences.
904 public:
905 InsertionSequence(Insertion* data, InsertionBounds* bounds)
906 : data_(data), bounds_(bounds) {}
907
908 bool operator!=(const InsertionSequence& other) const {
909 DCHECK_NE(data_, other.data_);
910 return bounds_ != other.bounds_;
911 }
912
913 const Insertion* begin() const { return data_ + bounds_->begin; }
914 const Insertion* end() const { return data_ + bounds_->end; }
915 size_t Size() const { return bounds_->Size(); }
916 int Vehicle() const { return bounds_->vehicle; }
917 int64_t Cost() const { return bounds_->cost; }
918 int64_t& Cost() { return bounds_->cost; }
919
920 private:
921 const Insertion* const data_;
922 InsertionBounds* const bounds_;
923 };
925 public:
926 InsertionSequenceIterator(Insertion* data, InsertionBounds* bounds)
927 : data_(data), bounds_(bounds) {}
928 bool operator!=(const InsertionSequenceIterator& other) const {
929 DCHECK_EQ(data_, other.data_);
930 return bounds_ != other.bounds_;
931 }
933 ++bounds_;
934 return *this;
935 }
936 InsertionSequence operator*() const { return {data_, bounds_}; }
937
938 private:
939 Insertion* data_;
940 InsertionBounds* bounds_;
941 };
942
943 // InsertionSequenceContainer is a range over insertion sequences.
945 return {insertions_.data(), insertion_bounds_.data()};
946 }
948 return {insertions_.data(),
949 insertion_bounds_.data() + insertion_bounds_.size()};
950 }
951 // Returns the number of sequences of this container.
952 size_t Size() const { return insertion_bounds_.size(); }
953
954 // Adds an insertion sequence to the container.
955 // Passing an initializer_list allows deeper optimizations by the compiler
956 // for cases where the sequence has a compile-time fixed size.
958 int vehicle, std::initializer_list<Insertion> insertion_sequence) {
959 insertion_bounds_.push_back(
960 {.begin = insertions_.size(),
961 .end = insertions_.size() + insertion_sequence.size(),
962 .vehicle = vehicle,
963 .cost = 0});
964 insertions_.insert(insertions_.end(), insertion_sequence.begin(),
965 insertion_sequence.end());
966 }
967
968 // Adds an insertion sequence to the container.
969 void AddInsertionSequence(int vehicle,
970 absl::Span<const Insertion> insertion_sequence) {
971 insertion_bounds_.push_back(
972 {.begin = insertions_.size(),
973 .end = insertions_.size() + insertion_sequence.size(),
974 .vehicle = vehicle,
975 .cost = 0});
976 insertions_.insert(insertions_.end(), insertion_sequence.begin(),
977 insertion_sequence.end());
978 }
979
980 // Similar to std::remove_if(), removes all sequences that match a predicate.
981 // This keeps original order, and removes selected sequences.
982 void RemoveIf(const std::function<bool(const InsertionSequence&)>& p) {
983 size_t from = 0;
984 size_t to = 0;
985 for (const InsertionSequence& sequence : *this) {
986 // TODO(user): Benchmark this against std::swap().
987 if (!p(sequence)) insertion_bounds_[to++] = insertion_bounds_[from];
988 ++from;
989 }
990 insertion_bounds_.resize(to);
991 }
992
993 // Sorts sequences according to (cost, vehicle).
994 // TODO(user): benchmark this against other ways to get insertion
995 // sequence in order, for instance sorting by index, separating {cost, index},
996 // making a heap.
997 void Sort() { std::sort(insertion_bounds_.begin(), insertion_bounds_.end()); }
998
999 // Removes all sequences.
1000 void Clear() {
1001 insertions_.clear();
1002 insertion_bounds_.clear();
1003 }
1004
1005 private:
1006 std::vector<Insertion> insertions_;
1007 std::vector<InsertionBounds> insertion_bounds_;
1008};
1009
1010// Generates insertion positions respecting structural constraints.
1012 public:
1014
1033 int pickup, int delivery, int vehicle, const std::vector<int>& path,
1034 const std::vector<bool>& path_node_is_pickup,
1035 const std::vector<bool>& path_node_is_delivery,
1036 InsertionSequenceContainer& insertions);
1037
1038 private:
1039 // Information[i] describes the insertion between path[i] and path[i+1].
1040 std::vector<int> next_decrease_; // next position after a delivery.
1041 std::vector<int> next_increase_; // next position after a pickup.
1042 std::vector<int> prev_decrease_; // previous position after delivery.
1043 std::vector<int> prev_increase_; // previous position after pickup.
1044};
1045
1049 int64_t value;
1051
1052 bool operator<(const PickupDeliveryInsertion& other) const {
1054 vehicle) < std::tie(other.value, other.insert_pickup_after,
1056 other.vehicle);
1057 }
1058};
1059
1067 public:
1070 RoutingModel* model, std::function<bool()> stop_search,
1071 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
1072 RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy,
1073 LocalSearchFilterManager* filter_manager,
1074 BinCapacities* bin_capacities = nullptr,
1075 std::function<bool(const std::vector<RoutingModel::VariableValuePair>&,
1076 std::vector<RoutingModel::VariableValuePair>*)>
1077 optimize_on_insertion = nullptr);
1079 bool BuildSolutionInternal() override;
1080 std::string DebugString() const override {
1081 return "LocalCheapestInsertionFilteredHeuristic";
1082 }
1083
1084 protected:
1085 void Initialize() override;
1086
1087 private:
1091 void ComputeInsertionOrder();
1095 std::vector<NodeInsertion> ComputeEvaluatorSortedPositions(int64_t node);
1100 std::vector<NodeInsertion> ComputeEvaluatorSortedPositionsOnRouteAfter(
1101 int64_t node, int64_t start, int64_t next_after_start, int vehicle);
1102
1106 std::vector<PickupDeliveryInsertion> ComputeEvaluatorSortedPairPositions(
1107 int pickup, int delivery);
1108
1109 // Tries to insert any alternative of the given pair,
1110 // ordered by cost of pickup insertion, then by cost of delivery insertion.
1111 void InsertBestPickupThenDelivery(const PickupDeliveryPair& pair);
1112 // Tries to insert any alternative of the given pair,
1113 // ordered by the sum of pickup and delivery insertion.
1114 void InsertBestPair(const PickupDeliveryPair& pair);
1115 // Tries to insert any alternative of the given pair,
1116 // at a position that preserves the multitour property,
1117 // ordered by the sum of pickup and delivery insertion.
1118 void InsertBestPairMultitour(const PickupDeliveryPair& pair);
1119 // Tries to insert a pair at the given location. Returns true iff inserted.
1120 bool InsertPair(int64_t pickup, int64_t insert_pickup_after, int64_t delivery,
1121 int64_t insert_delivery_after, int vehicle);
1122
1123 // Runs an internal optimization. Returns true if the solution was changed.
1124 bool OptimizeOnInsertion(std::vector<int> delta_indices);
1125
1126 // Returns true if bin capacities should be updated.
1127 // TODO(user): Allow updating bin capacities when we do internal
1128 // optimizations.
1129 bool MustUpdateBinCapacities() const {
1130 return bin_capacities_ != nullptr && optimize_on_insertion_ == nullptr;
1131 }
1132
1133 std::vector<Seed> insertion_order_;
1134 const RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy_;
1135 InsertionSequenceContainer insertion_container_;
1136 InsertionSequenceGenerator insertion_generator_;
1137
1138 BinCapacities* const bin_capacities_;
1139
1140 std::function<bool(const std::vector<RoutingModel::VariableValuePair>&,
1141 std::vector<RoutingModel::VariableValuePair>*)>
1142 optimize_on_insertion_;
1143 bool synchronize_insertion_optimizer_ = true;
1144};
1145
1149 public:
1151 std::function<bool()> stop_search,
1152 LocalSearchFilterManager* filter_manager);
1154 bool BuildSolutionInternal() override;
1155
1156 private:
1157 class PartialRoutesAndLargeVehicleIndicesFirst {
1158 public:
1159 explicit PartialRoutesAndLargeVehicleIndicesFirst(
1160 const CheapestAdditionFilteredHeuristic& builder)
1161 : builder_(builder) {}
1162 bool operator()(int vehicle1, int vehicle2) const;
1163
1164 private:
1165 const CheapestAdditionFilteredHeuristic& builder_;
1166 };
1168 template <typename Iterator>
1169 std::vector<int64_t> GetPossibleNextsFromIterator(int64_t node,
1170 Iterator start,
1171 Iterator end) const {
1172 const int size = model()->Size();
1173 std::vector<int64_t> nexts;
1174 for (Iterator it = start; it != end; ++it) {
1175 const int64_t next = *it;
1176 if (next != node && (next >= size || !Contains(next))) {
1177 nexts.push_back(next);
1178 }
1179 }
1180 return nexts;
1181 }
1183 virtual void SortSuccessors(int64_t node,
1184 std::vector<int64_t>* successors) = 0;
1185 virtual int64_t FindTopSuccessor(int64_t node,
1186 const std::vector<int64_t>& successors) = 0;
1187};
1188
1193 public:
1196 RoutingModel* model, std::function<bool()> stop_search,
1197 std::function<int64_t(int64_t, int64_t)> evaluator,
1198 LocalSearchFilterManager* filter_manager);
1200 std::string DebugString() const override {
1201 return "EvaluatorCheapestAdditionFilteredHeuristic";
1202 }
1203
1204 private:
1206 void SortSuccessors(int64_t node, std::vector<int64_t>* successors) override;
1207 int64_t FindTopSuccessor(int64_t node,
1208 const std::vector<int64_t>& successors) override;
1209
1210 std::function<int64_t(int64_t, int64_t)> evaluator_;
1211};
1212
1217 public:
1220 RoutingModel* model, std::function<bool()> stop_search,
1222 LocalSearchFilterManager* filter_manager);
1224 std::string DebugString() const override {
1225 return "ComparatorCheapestAdditionFilteredHeuristic";
1226 }
1227
1228 private:
1230 void SortSuccessors(int64_t node, std::vector<int64_t>* successors) override;
1231 int64_t FindTopSuccessor(int64_t node,
1232 const std::vector<int64_t>& successors) override;
1233
1235};
1236
1246 public:
1250 double neighbors_ratio = 1.0;
1256 bool add_reverse_arcs = false;
1259 double arc_coefficient = 1.0;
1260 };
1261
1262 SavingsFilteredHeuristic(RoutingModel* model,
1263 std::function<bool()> stop_search,
1265 LocalSearchFilterManager* filter_manager);
1266 ~SavingsFilteredHeuristic() override;
1267 bool BuildSolutionInternal() override;
1268
1269 protected:
1270 struct Saving {
1271 int64_t saving;
1272 unsigned int vehicle_type : 20;
1273 unsigned int before_node : 22;
1274 unsigned int after_node : 22;
1275 bool operator<(const Saving& other) const {
1276 return std::tie(saving, vehicle_type, before_node, after_node) <
1277 std::tie(other.saving, other.vehicle_type, other.before_node,
1278 other.after_node);
1279 }
1280 };
1281
1282 template <typename S>
1284
1285 virtual double ExtraSavingsMemoryMultiplicativeFactor() const = 0;
1286
1287 virtual void BuildRoutesFromSavings() = 0;
1288
1298 int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node,
1299 int64_t after_node);
1300
1301 // clang-format off
1302 std::unique_ptr<SavingsContainer<Saving> > savings_container_;
1303 // clang-format on
1304 std::unique_ptr<VehicleTypeCurator> vehicle_type_curator_;
1305
1306 private:
1311 // clang-format off
1312 void AddSymmetricArcsToAdjacencyLists(
1313 std::vector<std::vector<int64_t> >* adjacency_lists);
1314 // clang-format on
1315
1324 bool ComputeSavings();
1326 Saving BuildSaving(int64_t saving, int vehicle_type, int before_node,
1327 int after_node) const {
1328 return {saving, static_cast<unsigned int>(vehicle_type),
1329 static_cast<unsigned int>(before_node),
1330 static_cast<unsigned int>(after_node)};
1331 }
1332
1336 int64_t MaxNumNeighborsPerNode(int num_vehicle_types) const;
1337
1338 const SavingsParameters savings_params_;
1339
1341};
1342
1344 public:
1346 std::function<bool()> stop_search,
1348 LocalSearchFilterManager* filter_manager)
1349 : SavingsFilteredHeuristic(model, std::move(stop_search), parameters,
1350 filter_manager) {}
1352 std::string DebugString() const override {
1353 return "SequentialSavingsFilteredHeuristic";
1354 }
1355
1356 private:
1361 void BuildRoutesFromSavings() override;
1362 double ExtraSavingsMemoryMultiplicativeFactor() const override { return 1.0; }
1363};
1364
1366 public:
1368 std::function<bool()> stop_search,
1370 LocalSearchFilterManager* filter_manager)
1371 : SavingsFilteredHeuristic(model, std::move(stop_search), parameters,
1372 filter_manager) {}
1374 std::string DebugString() const override {
1375 return "ParallelSavingsFilteredHeuristic";
1376 }
1377
1378 private:
1389 void BuildRoutesFromSavings() override;
1390
1391 double ExtraSavingsMemoryMultiplicativeFactor() const override { return 2.0; }
1392
1397 void MergeRoutes(int first_vehicle, int second_vehicle, int64_t before_node,
1398 int64_t after_node);
1399
1401 std::vector<int64_t> first_node_on_route_;
1402 std::vector<int64_t> last_node_on_route_;
1406 std::vector<int> vehicle_of_first_or_last_node_;
1407};
1408
1412
1414 public:
1416 std::function<bool()> stop_search,
1417 LocalSearchFilterManager* filter_manager,
1418 bool use_minimum_matching);
1420 bool BuildSolutionInternal() override;
1421 std::string DebugString() const override {
1422 return "ChristofidesFilteredHeuristic";
1423 }
1424
1425 private:
1426 const bool use_minimum_matching_;
1427};
1428
1432 public:
1433 explicit SweepArranger(
1434 const std::vector<std::pair<int64_t, int64_t>>& points);
1435
1436 // This type is neither copyable nor movable.
1439
1440 virtual ~SweepArranger() {}
1441 void ArrangeIndices(std::vector<int64_t>* indices);
1442 void SetSectors(int sectors) { sectors_ = sectors; }
1443
1444 private:
1445 std::vector<int> coordinates_;
1446 int sectors_;
1447};
1448#endif // SWIG
1449
1450// Returns a DecisionBuilder building a first solution based on the Sweep
1451// heuristic. Mostly suitable when cost is proportional to distance.
1452DecisionBuilder* MakeSweepDecisionBuilder(RoutingModel* model,
1453 bool check_assignment);
1454
1455// Returns a DecisionBuilder making all nodes unperformed.
1456DecisionBuilder* MakeAllUnperformed(RoutingModel* model);
1457
1458} // namespace operations_research
1459
1460#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_SEARCH_H_
IntegerValue size
int64_t max
const E & Element(const V *const var) const
void SetValue(const IntVar *var, int64_t value)
IntVarElement * FastAdd(IntVar *var)
Adds without checking if variable has been previously added.
const IntContainer & IntVarContainer() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
CheapestAdditionFilteredHeuristic.
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
void InitializeSeedQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, SeedQueue *sq)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void AppendInsertionPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int vehicle, bool ignore_cost, std::vector< NodeInsertion > *node_insertions)
int64_t GetUnperformedValue(int64_t node_to_insert) const
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor, int vehicle=-1)
void AddSeedNodeToQueue(int node, std::vector< StartEndValue > *start_end_distances, SeedQueue *sq)
clang-format on
ChristofidesFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
ChristofidesFilteredHeuristic.
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
bool operator!=(const InsertionSequenceIterator &other) const
InsertionSequence(Insertion *data, InsertionBounds *bounds)
void RemoveIf(const std::function< bool(const InsertionSequence &)> &p)
void AddInsertionSequence(int vehicle, std::initializer_list< Insertion > insertion_sequence)
InsertionSequenceIterator begin()
InsertionSequenceContainer is a range over insertion sequences.
size_t Size() const
Returns the number of sequences of this container.
void AddInsertionSequence(int vehicle, absl::Span< const Insertion > insertion_sequence)
Adds an insertion sequence to the container.
Generates insertion positions respecting structural constraints.
void AppendPickupDeliveryMultitourInsertions(int pickup, int delivery, int vehicle, const std::vector< int > &path, const std::vector< bool > &path_node_is_pickup, const std::vector< bool > &path_node_is_delivery, InsertionSequenceContainer &insertions)
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
std::string DebugString() const override
-------— Decision Builder -------—
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
— First solution decision builder —
Generic filter-based heuristic applied to IntVars.
bool HasSecondaryVars() const
Returns true if there are secondary variables.
void ResetSolution()
Resets the data members for a new solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, LocalSearchFilterManager *filter_manager)
— First solution heuristics —
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
virtual bool StopSearch()
Returns true if the search must be stopped.
virtual void Initialize()
Initialize the heuristic; called before starting to build a new solution.
int64_t SecondaryVarIndex(int64_t index) const
Returns the index of a secondary var.
void SetValue(int64_t index, int64_t value)
virtual bool InitializeSolution()
Virtual method to initialize the solution.
const std::vector< int > & delta_indices() const
Returns the indices of the nodes currently in the insertion delta.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
std::optional< int64_t > Evaluate(bool commit)
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
bool IsSecondaryVar(int64_t index) const
Returns true if 'index' is a secondary variable index.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, LocalSearchFilterManager *filter_manager, BinCapacities *bin_capacities=nullptr, std::function< bool(const std::vector< RoutingModel::VariableValuePair > &, std::vector< RoutingModel::VariableValuePair > *)> optimize_on_insertion=nullptr)
Takes ownership of evaluator.
void Initialize() override
Initialize the heuristic; called before starting to build a new solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ParallelSavingsFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
Filter-based heuristic dedicated to routing.
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
void SetNext(int64_t node, int64_t next, int vehicle)
bool MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed, always returns true.
RoutingFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
RoutingFilteredHeuristic.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
bool StopSearch() override
Returns true if the search must be stopped.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
SavingsFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
SavingsFilteredHeuristic.
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
std::unique_ptr< SavingsContainer< Saving > > savings_container_
clang-format off
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
clang-format on
SequentialSavingsFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
For the time being, Solver is neither MT_SAFE nor MT_HOT.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
SweepArranger(const SweepArranger &)=delete
This type is neither copyable nor movable.
SweepArranger(const std::vector< std::pair< int64_t, int64_t > > &points)
void ArrangeIndices(std::vector< int64_t > *indices)
SweepArranger & operator=(const SweepArranger &)=delete
void Update(const std::function< bool(int)> &remove_vehicle)
bool HasCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible) const
VehicleTypeCurator(const RoutingModel::VehicleTypeContainer &vehicle_type_container)
void ReinjectVehicleOfClass(int vehicle, int vehicle_class, int64_t fixed_cost)
int GetLowestFixedCostVehicleOfType(int type) const
std::pair< int, int > GetCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible, const std::function< bool(int)> &stop_and_return_vehicle)
void Reset(const std::function< bool(int)> &store_vehicle)
— VehicleTypeCurator —
Block * next
SatParameters parameters
int64_t value
GRBmodel * model
int index
In SWIG mode, we don't want anything besides these top-level includes.
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
Returns a DecisionBuilder making all nodes unperformed.
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
std::vector< int64_t > ComputeVehicleEndChainStarts(const RoutingModel &model)
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
STL namespace.
bool Next()
trees with all degrees equal to
int64_t fixed_cost
Contrarily to CostClass, here we need strict equivalence.
Definition routing.cc:1338
int vehicle_class
int nodes
std::optional< int64_t > end
int64_t start
std::priority_queue< Seed, std::vector< Seed >, std::greater< Seed > > priority_queue
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
bool operator<(const PickupDeliveryInsertion &other) const