14#ifndef ORTOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
15#define ORTOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
21#include <initializer_list>
27#include "absl/functional/any_invocable.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
50 int path, int64_t capacity, int64_t span_upper_bound,
51 absl::Span<const DimensionValues::Interval> cumul_of_node,
52 absl::Span<const DimensionValues::Interval> slack_of_node,
53 absl::AnyInvocable<int64_t(int64_t, int64_t)
const> evaluator,
58 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t)
const>>
60 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t)
const>>
61 post_travel_evaluator,
71 absl::Span<
const std::pair<int64_t, int64_t>> interbreaks);
109 bool propagate_own_objective_value,
110 bool filter_objective_cost,
111 bool may_use_optimizers);
127 bool propagate_own_objective_value,
bool filter_objective_cost);
196 PathState(
int num_nodes, std::vector<int> path_start,
197 std::vector<int> path_end);
206 int Start(
int path)
const {
return path_start_end_[path].start; }
208 int End(
int path)
const {
return path_start_end_[path].end; }
216 int Path(
int node)
const {
return committed_paths_[node]; }
219 const std::vector<int>&
ChangedPaths()
const {
return changed_paths_; }
221 const std::vector<int>&
ChangedLoops()
const {
return changed_loops_; }
223 ChainRange
Chains(
int path)
const;
225 NodeRange
Nodes(
int path)
const;
234 void ChangePath(
int path, absl::Span<const ChainBounds> chains);
237 void ChangePath(
int path,
const std::initializer_list<ChainBounds>& chains) {
238 changed_paths_.push_back(path);
239 const int path_begin_index = chains_.size();
240 chains_.insert(chains_.end(), chains.begin(), chains.end());
241 const int path_end_index = chains_.size();
242 paths_[path] = {path_begin_index, path_end_index};
244 chains_.emplace_back(0, 0);
266 struct PathStartEnd {
267 PathStartEnd(
int start,
int end) : start(start),
end(
end) {}
279 void CopyNewPathAtEndOfNodes(
int path);
282 void IncrementalCommit();
288 const int num_nodes_;
289 const int num_paths_;
290 std::vector<PathStartEnd> path_start_end_;
318 std::vector<int> committed_nodes_;
320 std::vector<int> committed_paths_;
322 std::vector<int> committed_index_;
323 const int num_nodes_threshold_;
324 std::vector<ChainBounds> chains_;
325 std::vector<PathBounds> paths_;
328 std::vector<int> changed_paths_;
329 std::vector<int> changed_loops_;
332 bool is_invalid_ =
false;
346 return current_node_ != other.current_node_;
352 explicit Iterator(
const int* node) : current_node_(node) {}
353 const int* current_node_;
358 Chain(
const int* begin_node,
const int* end_node)
359 : begin_(begin_node), end_(end_node) {}
362 int First()
const {
return *begin_; }
363 int Last()
const {
return *(end_ - 1); }
370 const int*
const begin_;
371 const int*
const end_;
384 return {first_node_ + current_chain_->begin_index,
385 first_node_ + current_chain_->end_index};
388 return current_chain_ != other.current_chain_;
394 Iterator(
const ChainBounds* chain,
const int*
const first_node)
395 : current_chain_(chain), first_node_(first_node) {}
397 const int*
const first_node_;
403 const ChainBounds*
const end_chain,
const int*
const first_node)
404 : begin_(begin_chain), end_(end_chain), first_node_(first_node) {}
410 return (begin_ == end_) ? *this :
ChainRange(begin_ + 1, end_, first_node_);
414 return (begin_ == end_) ? *this :
ChainRange(begin_, end_ - 1, first_node_);
420 const int*
const first_node_;
431 if (current_node_ == end_node_) {
437 end_node_ = first_node_ + bounds.
end_index;
443 return current_chain_ != other.current_chain_;
449 Iterator(
const ChainBounds* current_chain,
const int*
const first_node)
450 : current_node_(first_node + current_chain->begin_index),
451 end_node_(first_node + current_chain->end_index),
452 current_chain_(current_chain),
453 first_node_(first_node) {}
454 const int* current_node_;
455 const int* end_node_;
457 const int*
const first_node_;
463 const int* first_node)
464 : begin_chain_(begin_chain),
465 end_chain_(end_chain),
466 first_node_(first_node) {}
475 const int*
const first_node_;
484 std::unique_ptr<PathState> path_state,
485 const std::vector<IntVar*>& nexts);
495 absl::Span<const PickupDeliveryPair> pairs,
496 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies);
523 std::vector<Interval> path_capacity,
524 std::vector<int> path_class,
525 std::vector<std::function<
Interval(int64_t, int64_t)>>
526 demand_per_path_class,
527 std::vector<Interval> node_capacity,
541 inline void UpdateCumulUsingChainRIQ(
int first_index,
int last_index,
549 void IncrementalCommit();
552 void AppendPathDemandsToSums(
int path);
558 void UpdateRIQStructure(
int begin_index,
int end_index);
561 const std::vector<ExtendedInterval> path_capacity_;
562 const std::vector<int> path_class_;
563 const std::vector<std::function<
Interval(int64_t, int64_t)>>
564 demand_per_path_class_;
565 std::vector<ExtendedInterval> cached_demand_;
566 const std::vector<ExtendedInterval> node_capacity_;
572 std::vector<int> index_;
594 std::vector<std::vector<RIQNode>> riq_;
597 const int maximum_riq_layer_size_;
599 const int min_range_size_for_riq_;
610 Solver* solver, std::unique_ptr<DimensionChecker> checker,
611 absl::string_view dimension_name);
640 std::vector<PathData> path_data);
648 std::vector<PathData> path_data_;
652 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
653 absl::string_view dimension_name);
752 int TreeSize()
const {
return tree_location_.size(); }
756 elements_.push_back({.height = height, .weight = weight});
774 int end_index)
const;
784 std::vector<Element> elements_;
788 struct TreeLocation {
793 std::vector<TreeLocation> tree_location_;
798 int64_t pivot_height;
800 bool operator<(
const Node& other)
const {
801 return pivot_height < other.pivot_height;
803 bool operator==(
const Node& other)
const {
804 return pivot_height == other.pivot_height;
807 std::vector<Node> nodes_;
820 unsigned int is_left : 1;
835 std::vector<std::vector<ElementInfo>> tree_layers_;
840 struct ElementRange {
841 int range_first_index;
842 int range_last_index;
846 bool range_first_is_node_first;
848 bool Empty()
const {
return range_first_index > range_last_index; }
850 int64_t Sum(
const ElementInfo* elements)
const {
851 return elements[range_last_index].prefix_sum -
852 (range_first_is_node_first
854 : elements[range_first_index - 1].prefix_sum);
857 ElementRange RightSubRange(
const ElementInfo* els,
int pivot_index)
const {
861 (range_first_index - els[range_first_index].left_index),
864 (range_last_index - els[range_last_index].left_index) -
865 static_cast<int>(els[range_last_index].is_left),
866 .range_first_is_node_first =
false};
867 right.range_first_is_node_first = right.range_first_index == pivot_index;
871 ElementRange LeftSubRange(
const ElementInfo* els)
const {
872 return {.range_first_index = els[range_first_index].left_index,
873 .range_last_index = els[range_last_index].left_index -
874 !els[range_last_index].is_left,
875 .range_first_is_node_first = range_first_is_node_first};
896 const PathState* path_state, std::vector<int64_t> force_start_min,
897 std::vector<int64_t> force_end_min, std::vector<int> force_class,
898 std::vector<
const std::function<int64_t(int64_t)>*> force_per_class,
899 std::vector<int> distance_class,
900 std::vector<
const std::function<int64_t(int64_t, int64_t)>*>
902 std::vector<EnergyCost> path_energy_cost,
903 std::vector<bool> path_has_cost_when_empty);
910 int64_t ComputePathCost(int64_t path)
const;
911 void CacheAndPrecomputeRangeQueriesOfPath(
int path);
912 void IncrementalCacheAndPrecompute();
913 void FullCacheAndPrecompute();
916 const std::vector<int64_t> force_start_min_;
917 const std::vector<int64_t> force_end_min_;
918 const std::vector<int> force_class_;
919 const std::vector<int> distance_class_;
920 const std::vector<
const std::function<int64_t(int64_t)>*> force_per_class_;
921 const std::vector<
const std::function<int64_t(int64_t, int64_t)>*>
923 const std::vector<EnergyCost> path_energy_cost_;
924 const std::vector<bool> path_has_cost_when_empty_;
927 const int maximum_range_query_size_;
931 std::vector<int> force_rmq_index_of_node_;
939 std::vector<int> threshold_query_index_of_node_;
941 std::vector<int64_t> cached_force_;
942 std::vector<int64_t> cached_distance_;
945 int64_t committed_total_cost_;
946 int64_t accepted_total_cost_;
947 std::vector<int64_t> committed_path_cost_;
951 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
952 absl::string_view dimension_name);
957 const PathState* path_state,
958 const std::vector<RoutingDimension*>& dimensions,
959 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
962 const std::vector<RoutingDimension*>& dimensions,
963 const RoutingSearchParameters& parameters,
bool filter_objective_cost,
964 bool use_chain_cumul_filter,
965 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
971 BasePathFilter(
const std::vector<IntVar*>& nexts,
int next_domain_size,
975 int64_t objective_min, int64_t objective_max)
override;
987 for (int64_t start : paths_metadata_.Starts()) {
992 int NumPaths()
const {
return paths_metadata_.NumPaths(); }
993 int64_t
Start(
int i)
const {
return paths_metadata_.Start(i); }
994 int64_t
End(
int i)
const {
return paths_metadata_.End(i); }
995 int GetPath(int64_t node)
const {
return paths_metadata_.GetPath(node); }
996 int Rank(int64_t node)
const {
return ranks_[node]; }
998 return touched_paths_.PositionsSetAtLeastOnce();
1002 return new_synchronized_unperformed_nodes_.PositionsSetAtLeastOnce();
1008 virtual void OnBeforeSynchronizePaths(
bool) {}
1009 virtual void OnAfterSynchronizePaths() {}
1010 virtual void OnSynchronizePathFromStart(int64_t) {}
1011 virtual bool InitializeAcceptPath() {
return true; }
1012 virtual bool AcceptPath(int64_t path_start, int64_t chain_start,
1013 int64_t chain_end) = 0;
1014 virtual bool FinalizeAcceptPath(int64_t, int64_t) {
return true; }
1016 void ComputePathStarts(std::vector<int64_t>* path_starts,
1017 std::vector<int>* index_to_path);
1018 bool HavePathsChanged();
1019 void SynchronizeFullAssignment();
1020 void UpdateAllRanks();
1021 void UpdatePathRanksFromStart(
int start);
1023 const PathsMetadata& paths_metadata_;
1024 std::vector<int64_t> node_path_starts_;
1025 SparseBitset<int64_t> new_synchronized_unperformed_nodes_;
1026 std::vector<int64_t> new_nexts_;
1027 std::vector<int> delta_touched_;
1028 SparseBitset<> touched_paths_;
1030 std::vector<std::pair<int64_t, int64_t> > touched_path_chain_start_ends_;
1032 std::vector<int> ranks_;
1090 const std::vector<std::vector<double>>& rows);
1092 double Evaluate(absl::Span<const double> values)
const;
1097 static constexpr int kBlockSize = 16;
1099 std::array<double, kBlockSize> coefficients;
1101 Block& BlockMultiplyAdd(
const Block& other,
double value) {
1104 for (
int i = 0; i < kBlockSize; ++i) {
1105 coefficients[i] += other.coefficients[i] * value;
1109 Block& MaximumWith(
const Block& other) {
1110 for (
int i = 0; i < kBlockSize; ++i) {
1111 coefficients[i] = std::max(coefficients[i], other.coefficients[i]);
1115 double Maximum()
const {
1116 return *std::max_element(coefficients.begin(), coefficients.end());
1119 std::vector<Block> blocks_;
1120 const int64_t num_variables_;
1121 const int64_t num_rows_;
~BasePathFilter() override=default
static const int64_t kUnassigned
bool lns_detected() const
bool PathStartTouched(int64_t start) const
BasePathFilter(const std::vector< IntVar * > &nexts, int next_domain_size, const PathsMetadata &paths_metadata)
int64_t GetNext(int64_t node) const
const std::vector< int64_t > & GetTouchedPathStarts() const
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max) override
bool HasAnySyncedPath() const
int GetPath(int64_t node) const
int Rank(int64_t node) const
const std::vector< int64_t > & GetNewSynchronizedUnperformedNodes() const
void OnSynchronize(const Assignment *delta) override
int64_t Start(int i) const
DimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::function< Interval(int64_t, int64_t)> > demand_per_path_class, std::vector< Interval > node_capacity, int min_range_size_for_riq=kOptimalMinRangeSizeForRIQ)
static constexpr int kOptimalMinRangeSizeForRIQ
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
int64_t Value(int index) const
bool IsVarSynced(int index) const
LightVehicleBreaksChecker(PathState *path_state, std::vector< PathData > path_data)
double Evaluate(absl::Span< const double > values) const
MaxLinearExpressionEvaluator(const std::vector< std::vector< double > > &rows)
PathEnergyCostChecker(const PathState *path_state, std::vector< int64_t > force_start_min, std::vector< int64_t > force_end_min, std::vector< int > force_class, std::vector< const std::function< int64_t(int64_t)> * > force_per_class, std::vector< int > distance_class, std::vector< const std::function< int64_t(int64_t, int64_t)> * > distance_per_class, std::vector< EnergyCost > path_energy_cost, std::vector< bool > path_has_cost_when_empty)
int64_t AcceptedCost() const
int64_t CommittedCost() const
bool operator!=(Iterator other) const
ChainRange DropLastChain() const
ChainRange DropFirstChain() const
ChainRange(const ChainBounds *const begin_chain, const ChainBounds *const end_chain, const int *const first_node)
bool operator!=(Iterator other) const
Chain(const int *begin_node, const int *end_node)
Chain WithoutFirstNode() const
bool operator!=(Iterator other) const
NodeRange(const ChainBounds *begin_chain, const ChainBounds *end_chain, const int *first_node)
void ChangeLoops(absl::Span< const int > new_loops)
const std::vector< int > & ChangedLoops() const
const std::vector< int > & ChangedPaths() const
ChainBounds CommittedPathRange(int path) const
ChainRange Chains(int path) const
static constexpr int kUnassigned
NodeRange Nodes(int path) const
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
static constexpr int kLoop
void ChangePath(int path, const std::initializer_list< ChainBounds > &chains)
int CommittedIndex(int node) const
void ChangePath(int path, absl::Span< const ChainBounds > chains)
int Start(int path) const
void MakeTreeFromNewElements()
WeightedWaveletTree()=default
void PushBack(int64_t height, int64_t weight)
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
LocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const PathState *path_state, absl::Span< const PickupDeliveryPair > pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
bool FillDimensionValuesFromRoutingDimension(int path, int64_t capacity, int64_t span_upper_bound, absl::Span< const DimensionValues::Interval > cumul_of_node, absl::Span< const DimensionValues::Interval > slack_of_node, absl::AnyInvocable< int64_t(int64_t, int64_t) const > evaluator, DimensionValues &dimension_values)
IntVarLocalSearchFilter * MakeRouteConstraintFilter(const RoutingModel &routing_model)
Returns a filter tracking route constraints.
IntVarLocalSearchFilter * MakeSameVehicleCostFilter(const RoutingModel &routing_model)
Returns a filter computing same vehicle costs.
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model, bool filter_cost)
Returns a filter ensuring that node disjunction constraints are enforced.
LocalSearchFilter * MakePathEnergyCostFilter(Solver *solver, std::unique_ptr< PathEnergyCostChecker > checker, absl::string_view dimension_name)
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Returns a filter handling dimension cumul bounds.
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
ClosedInterval::Iterator end(ClosedInterval interval)
LocalSearchFilter * MakeLightVehicleBreaksFilter(Solver *solver, std::unique_ptr< LightVehicleBreaksChecker > checker, absl::string_view dimension_name)
IntVarLocalSearchFilter * MakePathCumulFilter(const RoutingDimension &dimension, bool propagate_own_objective_value, bool filter_objective_cost, bool may_use_optimizers)
Returns a filter handling dimension costs and constraints.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
void FillPrePostVisitValues(int path, const DimensionValues &dimension_values, std::optional< absl::AnyInvocable< int64_t(int64_t, int64_t) const > > pre_travel_evaluator, std::optional< absl::AnyInvocable< int64_t(int64_t, int64_t) const > > post_travel_evaluator, PrePostVisitValues &visit_values)
IntVarLocalSearchFilter * MakeOrderedActivityGroupFilter(const RoutingModel &routing_model)
LocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model, const PathState *path_state)
Returns a filter checking that vehicle variable domains are respected.
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
LocalSearchFilter * MakeResourceAssignmentFilter(LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool propagate_own_objective_value, bool filter_objective_cost)
bool PropagateLightweightVehicleBreaks(int path, DimensionValues &dimension_values, absl::Span< const std::pair< int64_t, int64_t > > interbreaks)
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *lp_optimizer, GlobalDimensionCumulOptimizer *mp_optimizer, bool filter_objective_cost)
Returns a filter checking global linear constraints and costs.
util_intops::StrongIntRange< ElementIndex > ElementRange
IntVarLocalSearchFilter * MakeActiveNodeGroupFilter(const RoutingModel &routing_model)
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, absl::string_view dimension_name)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
int64_t num_positive_infinity
int64_t num_negative_infinity
int64_t max_interbreak_duration
int64_t min_break_duration
std::vector< VehicleBreak > vehicle_breaks
LocalSearchState::Variable start_cumul
LocalSearchState::Variable end_cumul
std::vector< InterbreakLimit > interbreak_limits
LocalSearchState::Variable total_transit
LocalSearchState::Variable span
int64_t cost_per_unit_above_threshold
int64_t cost_per_unit_below_threshold
ChainBounds(int begin_index, int end_index)
static const int64_t kint64max