Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_filters.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
14#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
15#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
16
17#include <cstdint>
18#include <functional>
19#include <initializer_list>
20#include <memory>
21#include <utility>
22#include <vector>
23
24#include "absl/strings/string_view.h"
25#include "absl/types/span.h"
26#include "ortools/base/types.h"
34#include "ortools/util/bitset.h"
36
37namespace operations_research {
38
39// Given a DimensionValues whose path has changed nodes, fills the travels,
40// travel_sums, transits, cumuls, and span of the new path.
41// This only sets the initial values at each node, and does not propagate
42// the transit constraint cumul[i+1] = cumul[i] + transits[i].
43// Returns false if some cumul.min exceeds the capacity, or if the sum of
44// travels exceeds the span_upper_bound.
46 int path, int64_t capacity, int64_t span_upper_bound,
47 absl::Span<const DimensionValues::Interval> cumul_of_node,
48 absl::Span<const DimensionValues::Interval> slack_of_node,
49 absl::AnyInvocable<int64_t(int64_t, int64_t) const> evaluator,
50 DimensionValues& dimension_values);
51
53 int path, const DimensionValues& dimension_values,
54 absl::AnyInvocable<int64_t(int64_t, int64_t) const> pre_travel_evaluator,
55 absl::AnyInvocable<int64_t(int64_t, int64_t) const> post_travel_evaluator,
56 PrePostVisitValues& visit_values);
57
58// Propagates vehicle break constraints in dimension_values.
59// This returns false if breaks cannot fit the path.
60// Otherwise, this returns true, and modifies the start cumul, end cumul and the
61// span of the given path.
62// This applies light reasoning, and runs in O(#breaks * #interbreak rules).
64 int path, DimensionValues& dimension_values,
65 absl::Span<const std::pair<int64_t, int64_t>> interbreaks);
66
69 const RoutingModel& routing_model);
70
73 const RoutingModel& routing_model);
74
78 const RoutingModel& routing_model);
79
82 const RoutingModel& routing_model, bool filter_cost);
83
86 const RoutingModel& routing_model);
87
90 const RoutingModel& routing_model);
91
93IntVarLocalSearchFilter* MakePathCumulFilter(const RoutingDimension& dimension,
94 bool propagate_own_objective_value,
95 bool filter_objective_cost,
96 bool may_use_optimizers);
97
100 const RoutingDimension& dimension);
101
104 GlobalDimensionCumulOptimizer* lp_optimizer,
105 GlobalDimensionCumulOptimizer* mp_optimizer, bool filter_objective_cost);
106
111 LocalDimensionCumulOptimizer* mp_optimizer,
112 bool propagate_own_objective_value, bool filter_objective_cost);
113
115IntVarLocalSearchFilter* MakeCPFeasibilityFilter(RoutingModel* routing_model);
116
117#if !defined(SWIG)
118// A PathState represents a set of paths and changes made on it.
119//
120// More accurately, let us define P_{num_nodes, starts, ends}-graphs the set of
121// directed graphs with nodes [0, num_nodes) whose connected components are
122// paths from starts[i] to ends[i] (for the same i) and loops.
123// Let us fix num_nodes, starts and ends, so we call these P-graphs.
124//
125// A P-graph can be described by the sequence of nodes of each of its paths,
126// and its set of loops. To describe a change made on a given P-graph G0 that
127// yields another P-graph G1, we choose to describe G1 in terms of G0. When
128// the difference between G0 and G1 is small, as is almost always the case in a
129// local search setting, the description is compact, allowing for incremental
130// filters to be efficient.
131//
132// In order to describe G1 in terms of G0 succintly, we describe each path of
133// G1 as a sequence of chains of G0. A chain of G0 is either a nonempty sequence
134// of consecutive nodes of a path of G0, or a node that was a loop in G0.
135// For instance, a path that was not modified from G0 to G1 has one chain,
136// the sequence of all nodes in the path. Typically, local search operators
137// modify one or two paths, and the resulting paths can described as sequences
138// of two to four chains of G0. Paths that were modified are listed explicitly,
139// allowing to iterate only on changed paths.
140// The loops of G1 are described more implicitly: the loops of G1 not in G0
141// are listed explicitly, but those in both G1 and G0 are not listed.
142//
143// A PathState object can be in two states: committed or changed.
144// At construction, the object is committed, G0.
145// To enter a changed state G1, one can pass modifications with ChangePath() and
146// ChangeLoops(). For reasons of efficiency, a chain is described as a range of
147// node indices in the representation of the committed graph G0. To that effect,
148// the nodes of a path of G0 are guaranteed to have consecutive indices.
149//
150// Filters can then browse the change efficiently using ChangedPaths(),
151// Chains(), Nodes() and ChangedLoops().
152//
153// Then Commit() or Revert() can be called: Commit() sets the changed state G1
154// as the new committed state, Revert() erases all changes.
156 public:
157 // A Chain allows to iterate on all nodes of a chain, and access some data:
158 // first node, last node, number of nodes in the chain.
159 // Chain is a range, its iterator ChainNodeIterator, its value type int.
160 // Chains are returned by PathChainIterator's operator*().
161 class Chain;
162 // A ChainRange allows to iterate on all chains of a path.
163 // ChainRange is a range, its iterator Chain*, its value type Chain.
164 class ChainRange;
165 // A NodeRange allows to iterate on all nodes of a path.
166 // NodeRange is a range, its iterator PathNodeIterator, its value type int.
167 class NodeRange;
168
176 int CommittedIndex(int node) const { return committed_index_[node]; }
177 ChainBounds CommittedPathRange(int path) const { return chains_[path]; }
178
179 // Path constructor: path_start and path_end must be disjoint,
180 // their values in [0, num_nodes).
181 PathState(int num_nodes, std::vector<int> path_start,
182 std::vector<int> path_end);
183
184 // Instance-constant accessors.
185
186 // Returns the number of nodes in the underlying graph.
187 int NumNodes() const { return num_nodes_; }
188 // Returns the number of paths (empty paths included).
189 int NumPaths() const { return num_paths_; }
190 // Returns the start of a path.
191 int Start(int path) const { return path_start_end_[path].start; }
192 // Returns the end of a path.
193 int End(int path) const { return path_start_end_[path].end; }
194
195 // State-dependent accessors.
196
197 static constexpr int kUnassigned = -2;
198 static constexpr int kLoop = -1;
199 // Returns the committed path of a given node, kLoop if it is a loop,
200 // kUnassigned if it is not assigned,
201 int Path(int node) const { return committed_paths_[node]; }
202 // Returns the set of paths that actually changed,
203 // i.e. that have more than one chain.
204 const std::vector<int>& ChangedPaths() const { return changed_paths_; }
205 // Returns the set of loops that were added by the change.
206 const std::vector<int>& ChangedLoops() const { return changed_loops_; }
207 // Returns the current range of chains of path.
208 ChainRange Chains(int path) const;
209 // Returns the current range of nodes of path.
210 NodeRange Nodes(int path) const;
211
212 // State modifiers.
213
214 // Changes the path to the given sequence of chains of the committed state.
215 // Chains are described by semi-open intervals. No optimization is made in
216 // case two consecutive chains are actually already consecutive in the
217 // committed state: they are not merged into one chain, and Chains(path) will
218 // report the two chains.
219 void ChangePath(int path, absl::Span<const ChainBounds> chains);
220 // Same as above, but the initializer_list interface avoids the need to pass
221 // a vector.
222 void ChangePath(int path, const std::initializer_list<ChainBounds>& chains) {
223 changed_paths_.push_back(path);
224 const int path_begin_index = chains_.size();
225 chains_.insert(chains_.end(), chains.begin(), chains.end());
226 const int path_end_index = chains_.size();
227 paths_[path] = {path_begin_index, path_end_index};
228 // Always add sentinel, in case this is the last path change.
229 chains_.emplace_back(0, 0);
230 }
231
232 // Describes the nodes that are newly loops in this change.
233 void ChangeLoops(absl::Span<const int> new_loops);
234
235 // Set the current state G1 as committed. See class comment for details.
236 void Commit();
237 // Erase incremental changes. See class comment for details.
238 void Revert();
239 // Sets all paths to start->end, all other nodes to kUnassigned.
240 void Reset();
241
242 // LNS Operators may not fix variables,
243 // in which case we mark the candidate invalid.
244 void SetInvalid() { is_invalid_ = true; }
245 bool IsInvalid() const { return is_invalid_; }
246
247 private:
248 // Most structs below are named pairs of ints, for typing purposes.
249
250 // Start and end are stored together to optimize (likely) simultaneous access.
251 struct PathStartEnd {
252 PathStartEnd(int start, int end) : start(start), end(end) {}
253 int start;
254 int end;
255 };
256 // Paths are ranges of chains, which are ranges of committed nodes, see below.
257 struct PathBounds {
258 int begin_index;
259 int end_index;
260 };
261
262 // Copies nodes in chains of path at the end of nodes,
263 // and sets those nodes' path member to value path.
264 void CopyNewPathAtEndOfNodes(int path);
265 // Commits paths in O(#{changed paths' nodes}) time,
266 // increasing this object's space usage by O(|changed path nodes|).
267 void IncrementalCommit();
268 // Commits paths in O(num_nodes + num_paths) time,
269 // reducing this object's space usage to O(num_nodes + num_paths).
270 void FullCommit();
271
272 // Instance-constant data.
273 const int num_nodes_;
274 const int num_paths_;
275 std::vector<PathStartEnd> path_start_end_;
276
277 // Representation of the committed and changed paths.
278 // A path is a range of chains, which is a range of nodes.
279 // Ranges are represented internally by indices in vectors:
280 // ChainBounds are indices in committed_nodes_. PathBounds are indices in
281 // chains_. When committed (after construction, Revert() or Commit()):
282 // - path ranges are [path, path+1): they have one chain.
283 // - chain ranges don't overlap, chains_ has an empty sentinel at the end.
284 // The sentinel allows the Nodes() iterator to maintain its current pointer
285 // to committed nodes on NodeRange::operator++().
286 // - committed_nodes_ contains all nodes, both paths and loops.
287 // Actually, old duplicates will likely appear,
288 // the current version of a node is at the index given by
289 // committed_index_[node]. A Commit() can add nodes at the end of
290 // committed_nodes_ in a space/time tradeoff, but if committed_nodes_' size
291 // is above num_nodes_threshold_, Commit() must reclaim useless duplicates'
292 // space by rewriting the path/chain/nodes structure.
293 // When changed (after ChangePaths() and ChangeLoops()),
294 // the structure is updated accordingly:
295 // - path ranges that were changed have nonoverlapping values [begin, end)
296 // where begin is >= num_paths_ + 1, i.e. new chains are stored after
297 // the committed state.
298 // - additional chain ranges are stored after the committed chains and its
299 // sentinel to represent the new chains resulting from the changes.
300 // Those chains do not overlap with one another or with committed chains.
301 // - committed_nodes_ are not modified, and still represent the committed
302 // paths. committed_index_ is not modified either.
303 std::vector<int> committed_nodes_;
304 // Maps nodes to their path in the latest committed state.
305 std::vector<int> committed_paths_;
306 // Maps nodes to their index in the latest committed state.
307 std::vector<int> committed_index_;
308 const int num_nodes_threshold_;
309 std::vector<ChainBounds> chains_;
310 std::vector<PathBounds> paths_;
311
312 // Incremental information.
313 std::vector<int> changed_paths_;
314 std::vector<int> changed_loops_;
315
316 // See IsInvalid() and SetInvalid().
317 bool is_invalid_ = false;
318};
319
320// A Chain is a range of committed nodes.
322 public:
323 class Iterator {
324 public:
325 Iterator& operator++() {
326 ++current_node_;
327 return *this;
328 }
329 int operator*() const { return *current_node_; }
330 bool operator!=(Iterator other) const {
331 return current_node_ != other.current_node_;
332 }
333
334 private:
335 // Only a Chain can construct its iterator.
336 friend class PathState::Chain;
337 explicit Iterator(const int* node) : current_node_(node) {}
338 const int* current_node_;
339 };
340
341 // Chains hold CommittedNode* values, a Chain may be invalidated
342 // if the underlying vector is modified.
343 Chain(const int* begin_node, const int* end_node)
344 : begin_(begin_node), end_(end_node) {}
345
346 int NumNodes() const { return end_ - begin_; }
347 int First() const { return *begin_; }
348 int Last() const { return *(end_ - 1); }
349 Iterator begin() const { return Iterator(begin_); }
350 Iterator end() const { return Iterator(end_); }
351
352 Chain WithoutFirstNode() const { return Chain(begin_ + 1, end_); }
353
354 private:
355 const int* const begin_;
356 const int* const end_;
357};
358
359// A ChainRange is a range of Chains, committed or not.
361 public:
362 class Iterator {
363 public:
364 Iterator& operator++() {
365 ++current_chain_;
366 return *this;
367 }
368 Chain operator*() const {
369 return {first_node_ + current_chain_->begin_index,
370 first_node_ + current_chain_->end_index};
371 }
372 bool operator!=(Iterator other) const {
373 return current_chain_ != other.current_chain_;
374 }
375
376 private:
377 // Only a ChainRange can construct its Iterator.
378 friend class ChainRange;
379 Iterator(const ChainBounds* chain, const int* const first_node)
380 : current_chain_(chain), first_node_(first_node) {}
381 const ChainBounds* current_chain_;
382 const int* const first_node_;
383 };
384
385 // ChainRanges hold ChainBounds* and CommittedNode*,
386 // a ChainRange may be invalidated if on of the underlying vector is modified.
387 ChainRange(const ChainBounds* const begin_chain,
388 const ChainBounds* const end_chain, const int* const first_node)
389 : begin_(begin_chain), end_(end_chain), first_node_(first_node) {}
390
391 Iterator begin() const { return {begin_, first_node_}; }
392 Iterator end() const { return {end_, first_node_}; }
393
395 return (begin_ == end_) ? *this : ChainRange(begin_ + 1, end_, first_node_);
396 }
397
399 return (begin_ == end_) ? *this : ChainRange(begin_, end_ - 1, first_node_);
400 }
401
402 private:
403 const ChainBounds* const begin_;
404 const ChainBounds* const end_;
405 const int* const first_node_;
406};
407
408// A NodeRange allows to iterate on all nodes of a path,
409// by a two-level iteration on ChainBounds* and CommittedNode* of a PathState.
411 public:
412 class Iterator {
413 public:
414 Iterator& operator++() {
415 ++current_node_;
416 if (current_node_ == end_node_) {
417 ++current_chain_;
418 // Note: dereferencing bounds is valid because there is a sentinel
419 // value at the end of PathState::chains_ to that intent.
420 const ChainBounds bounds = *current_chain_;
421 current_node_ = first_node_ + bounds.begin_index;
422 end_node_ = first_node_ + bounds.end_index;
423 }
424 return *this;
425 }
426 int operator*() const { return *current_node_; }
427 bool operator!=(Iterator other) const {
428 return current_chain_ != other.current_chain_;
429 }
430
431 private:
432 // Only a NodeRange can construct its Iterator.
433 friend class NodeRange;
434 Iterator(const ChainBounds* current_chain, const int* const first_node)
435 : current_node_(first_node + current_chain->begin_index),
436 end_node_(first_node + current_chain->end_index),
437 current_chain_(current_chain),
438 first_node_(first_node) {}
439 const int* current_node_;
440 const int* end_node_;
441 const ChainBounds* current_chain_;
442 const int* const first_node_;
443 };
444
445 // NodeRanges hold ChainBounds* and int* (first committed node),
446 // a NodeRange may be invalidated if on of the underlying vector is modified.
447 NodeRange(const ChainBounds* begin_chain, const ChainBounds* end_chain,
448 const int* first_node)
449 : begin_chain_(begin_chain),
450 end_chain_(end_chain),
451 first_node_(first_node) {}
452 Iterator begin() const { return {begin_chain_, first_node_}; }
453 // Note: there is a sentinel value at the end of PathState::chains_,
454 // so dereferencing chain_range_.end()->begin_ is always valid.
455 Iterator end() const { return {end_chain_, first_node_}; }
456
457 private:
458 const ChainBounds* begin_chain_;
459 const ChainBounds* end_chain_;
460 const int* const first_node_;
461};
462
463// Make a filter that takes ownership of a PathState and synchronizes it with
464// solver events. The solver represents a graph with array of variables 'nexts'.
465// Solver events are embodied by Assignment* deltas, that are translated to node
466// changes during Relax(), committed during Synchronize(), and reverted on
467// Revert().
469 std::unique_ptr<PathState> path_state,
470 const std::vector<IntVar*>& nexts);
471
473LocalSearchFilter* MakeVehicleVarFilter(const RoutingModel& routing_model,
474 const PathState* path_state);
475
479 const RoutingModel& routing_model, const PathState* path_state,
480 const std::vector<PickupDeliveryPair>& pairs,
481 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies);
482
483// This checker enforces dimension requirements.
484// A dimension requires that there is some valuation of
485// cumul and demand such that for all paths:
486// - cumul[A] is in interval node_capacity[A]
487// - if arc A -> B is on a path of path_class p,
488// then cumul[A] + demand[p](A, B) = cumul[B].
489// - if A is on a path of class p, then
490// cumul[A] must be inside interval path_capacity[path].
492 public:
493 struct Interval {
494 int64_t min;
495 int64_t max;
496 };
497
499 int64_t min;
500 int64_t max;
503 };
504
505 // TODO(user): the addition of kMinRangeSizeForRIQ slowed down Check().
506 // See if using a template parameter makes it faster.
507 DimensionChecker(const PathState* path_state,
508 std::vector<Interval> path_capacity,
509 std::vector<int> path_class,
510 std::vector<std::function<Interval(int64_t, int64_t)>>
511 demand_per_path_class,
512 std::vector<Interval> node_capacity,
513 int min_range_size_for_riq = kOptimalMinRangeSizeForRIQ);
514
515 // Given the change made in PathState, checks that the dimension
516 // constraint is still feasible.
517 bool Check() const;
518
519 // Commits to the changes made in PathState,
520 // must be called before PathState::Commit().
521 void Commit();
522
523 static constexpr int kOptimalMinRangeSizeForRIQ = 4;
524
525 private:
526 inline void UpdateCumulUsingChainRIQ(int first_index, int last_index,
527 const ExtendedInterval& path_capacity,
528 ExtendedInterval& cumul) const;
529
530 // Commits to the current solution and rebuilds structures from scratch.
531 void FullCommit();
532 // Commits to the current solution and only build structures for paths that
533 // changed, using additional space to do so in a time-memory tradeoff.
534 void IncrementalCommit();
535 // Adds sums of given path to the bottom layer of the Range Intersection Query
536 // structure, updates index_ and previous_nontrivial_index_.
537 void AppendPathDemandsToSums(int path);
538 // Updates the Range Intersection Query structure from its bottom layer,
539 // with [begin_index, end_index) the range of the change,
540 // which must be at the end of the bottom layer.
541 // Supposes that requests overlapping the range will be inside the range,
542 // to avoid updating all layers.
543 void UpdateRIQStructure(int begin_index, int end_index);
544
545 const PathState* const path_state_;
546 const std::vector<ExtendedInterval> path_capacity_;
547 const std::vector<int> path_class_;
548 const std::vector<std::function<Interval(int64_t, int64_t)>>
549 demand_per_path_class_;
550 std::vector<ExtendedInterval> cached_demand_;
551 const std::vector<ExtendedInterval> node_capacity_;
552
553 // Precomputed data.
554 // Maps nodes to their pre-computed data, except for isolated nodes,
555 // which do not have precomputed data.
556 // Only valid for nodes that are in some path in the committed state.
557 std::vector<int> index_;
558 // Range intersection query in <O(n log n), O(1)>, with n = #nodes.
559 // Let node be in a path, i = index_[node], start the start of node's path.
560 // Let l such that index_[start] <= i - 2**l.
561 // - riq_[l][i].tsum_at_lst contains the sum of demands from start to node.
562 // - riq_[l][i].tsum_at_fst contains the sum of demands from start to the
563 // node at i - 2**l.
564 // - riq_[l][i].tightest_tsum contains the intersection of
565 // riq_[0][j].tsum_at_lst for all j in (i - 2**l, i].
566 // - riq_[0][i].cumuls_to_lst and riq_[0][i].cumuls_to_fst contain
567 // the node's capacity.
568 // - riq_[l][i].cumuls_to_lst is the intersection, for j in (i - 2**l, i], of
569 // riq_[0][j].cumuls_to_lst + sum_{k in [j, i)} demand(k, k+1)
570 // - riq_[l][i].cumuls_to_fst is the intersection, for j in (i - 2**l, i], of
571 // riq_[0][j].cumuls_to_fst - sum_{k in (i-2**l, j)} demand(k, k+1)
572 struct RIQNode {
573 ExtendedInterval cumuls_to_fst;
574 ExtendedInterval tightest_tsum;
575 ExtendedInterval cumuls_to_lst;
576 ExtendedInterval tsum_at_fst;
577 ExtendedInterval tsum_at_lst;
578 };
579 std::vector<std::vector<RIQNode>> riq_;
580 // The incremental branch of Commit() may waste space in the layers of the
581 // RIQ structure. This is the upper limit of a layer's size.
582 const int maximum_riq_layer_size_;
583 // Range queries are used on a chain only if the range is larger than this.
584 const int min_range_size_for_riq_;
585};
586
587// Make a filter that translates solver events to the input checker's interface.
588// Since DimensionChecker has a PathState, the filter returned by this
589// must be synchronized to the corresponding PathStateFilter:
590// - Relax() must be called after the PathStateFilter's.
591// - Accept() must be called after.
592// - Synchronize() must be called before.
593// - Revert() must be called before.
594LocalSearchFilter* MakeDimensionFilter(
595 Solver* solver, std::unique_ptr<DimensionChecker> checker,
596 absl::string_view dimension_name);
597#endif // !defined(SWIG)
598
600 public:
602 int64_t start_min;
603 int64_t start_max;
604 int64_t end_min;
605 int64_t end_max;
609 };
614
623
625 std::vector<PathData> path_data);
626
627 void Relax() const;
628
629 bool Check() const;
630
631 private:
632 PathState* path_state_;
633 std::vector<PathData> path_data_;
634};
635
637 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
638 absl::string_view dimension_name);
639
640// This class allows making fast range queries on sequences of elements.
641// * Main characteristics.
642// - queries on sequences of elements {height, weight},
643// parametrized by (begin, end, T), returning
644// sum_{i \in [begin, end), S[i].height >= T} S[i].weight
645// - O(log (#different heights)) time complexity thanks to an underlying
646// wavelet tree (https://en.wikipedia.org/wiki/Wavelet_Tree)
647// - holds several sequences at once, can be cleared while still keeping
648// allocated memory to avoid allocations.
649// More details on these points follow.
650//
651// * Query complexity.
652// The time complexity of a query in S is O(log H), where H is the number of
653// different heights appearing in S.
654// The particular implementation guarantees that queries that are trivial in
655// the .height dimension, that is if threshold_height is <= or >= all heights
656// in the range, are O(1).
657//
658// * Initialization complexity.
659// The time complexity of filling the underlying data structures,
660// which is done by running MakeTreeFromNewElements(),
661// is O(N log N) where N is the number of new elements.
662// The space complexity is a O(N log H).
663//
664// * Usage.
665// Given Histogram holding elements with fields {.height, .weight},
666// Histogram hist1 {{2, 3}, {1, 4}, {4, 1}, {2, 2}, {3, 1}, {0, 4}};
667// Histogram hist2 {{-2, -3}, {-1, -4}, {-4, -1}, {-2, -2}};
668// WeightedWaveletTree tree;
669//
670// for (const auto [height, weight] : hist1]) {
671// tree.PushBack(height, weight);
672// }
673// const int begin1 = tree.TreeSize();
674// tree.MakeTreeFromNewElements();
675// const int end1 = tree.TreeSize();
676// const int begin2 = tree.TreeSize(); // begin2 == end1.
677// for (const auto [height, weight] : hist2]) {
678// tree.PushBack(height, weight);
679// }
680// tree.MakeTreeFromNewElements();
681// const int end2 = tree.TreeSize();
682//
683// // Sum of weights on whole first sequence, == 3 + 4 + 1 + 2 + 1 + 4
684// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin1, /*end=*/end1);
685// // Sum of weights on whole second sequence, all heights are negative,
686// // so the result is 0.
687// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin2, /*end=*/end2);
688// // This is forbidden, because the range overlaps two sequences.
689// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/2, /*end=*/10);
690// // Returns 2 = 0 + 1 + 0 + 1.
691// tree.RangeSumWithThreshold(/*threshold=*/3, /*begin=*/1, /*end=*/5);
692// // Returns -6 = -4 + 0 + -2.
693// tree.RangeSumWithThreshold(/*threshold=*/-2, /*begin=*/1, /*end=*/4);
694// // Add another sequence.
695// Histogram hist3 {{1, 1}, {3, 4}};
696// const int begin3 = tree.TreeSize();
697// for (const auto [height, weight] : hist3) {
698// tree.PushBack(height, weight);
699// }
700// tree.MakeTreeFromNewElements();
701// const int end3 = tree.TreeSize();
702// // Returns 4 = 0 + 4.
703// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
704// // Clear the tree, this invalidates all range queries.
705// tree.Clear();
706// // Forbidden!
707// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
708//
709// * Implementation.
710// This data structure uses two main techniques of the wavelet tree:
711// - a binary search tree in the height dimension.
712// - nodes only hold information about elements in their height range,
713// keeping selected elements in the same order as the full sequence,
714// and can map the index of its elements to their left and right child.
715// The layout of the tree is packed by separating the tree navigation
716// information from the (prefix sum + mapping) information.
717// Here is how the tree for heights 6 4 1 3 6 1 7 4 2 is laid out in memory:
718// tree_layers_ // nodes_
719// 6 4 1 3 6 1 7 4 2 // 4
720// 1 3 1 2|6 4 6 7 4 // 2 6
721// 1 1|3 2|4 4|6 6 7 // _ 3 _ 7
722// _ _|2|3|_ _|6 6|7 // Dummy information is used to pad holes in nodes_.
723// In addition to the mapping information of each element, each node holds
724// the prefix sum of weights up to each element, to be able to compute the sum
725// of S[i].weight of elements in its height range, for any range, in O(1).
726// The data structure does not actually need height information inside the tree
727// nodes, and does not store them.
729 public:
731
732 // Clears all trees, which invalidates all further range queries on currently
733 // existing trees. This does *not* release memory held by this object.
734 void Clear();
735
736 // Returns the total number of elements in trees.
737 int TreeSize() const { return tree_location_.size(); }
738
739 // Adds an element at index this->Size().
740 void PushBack(int64_t height, int64_t weight) {
741 elements_.push_back({.height = height, .weight = weight});
742 }
743
744 // Generates the wavelet tree for all new elements, i.e. elements that were
745 // added with PushBack() since the latest of these events: construction of
746 // this object, a previous call to MakeTreeFromNewElements(), or a call to
747 // Clear().
748 // The range of new elements [begin, end), with begin the Size() at the
749 // latest event, and end the current Size().
751
752 // Returns sum_{begin_index <= i < end_index,
753 // S[i].height >= threshold_height} S[i].weight.
754 // The range [begin_index, end_index) can only cover elements that were new
755 // at the same call to MakeTreeFromNewElements().
756 // When calling this method, there must be no pending new elements,
757 // i.e. the last method called must not have been PushBack() or TreeSize().
758 int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index,
759 int end_index) const;
760
761 private:
762 // Internal copy of an element.
763 struct Element {
764 int64_t height;
765 int64_t weight;
766 };
767 // Elements are stored in a vector, they are only used during the
768 // initialization of the data structure.
769 std::vector<Element> elements_;
770
771 // Maps the index of an element to the location of its tree.
772 // Elements of the same sequence have the same TreeLocation value.
773 struct TreeLocation {
774 int node_begin; // index of the first node in the tree in nodes_.
775 int node_end; // index of the last node in the tree in nodes_, plus 1.
776 int sequence_first; // index of the first element in all layers.
777 };
778 std::vector<TreeLocation> tree_location_;
779
780 // A node of the tree is represented by the height of its pivot element and
781 // the index of its pivot in the layer below, or -1 if the node is a leaf.
782 struct Node {
783 int64_t pivot_height;
784 int pivot_index;
785 bool operator<(const Node& other) const {
786 return pivot_height < other.pivot_height;
787 }
788 bool operator==(const Node& other) const {
789 return pivot_height == other.pivot_height;
790 }
791 };
792 std::vector<Node> nodes_;
793
794 // Holds range sum query and mapping information of each element
795 // in each layer.
796 // - prefix_sum: sum of weights in this node up to this element, included.
797 // - left_index: number of elements in the same layer that are either:
798 // - in a node on the left of this node, or
799 // - in the same node, preceding this element, mapped to the left subtree.
800 // Coincides with this element's index in the left subtree if is_left = 1.
801 // - is_left: 1 if the element is in the left subtree, otherwise 0.
802 struct ElementInfo {
803 int64_t prefix_sum;
804 int left_index : 31;
805 unsigned int is_left : 1;
806 };
807 // Contains range sum query and mapping data of all elements in their
808 // respective tree, arranged by layer (depth) in the tree.
809 // Layer 0 has root data, layer 1 has information of the left child
810 // then the right child, layer 2 has left-left, left-right, right-left,
811 // then right-right, etc.
812 // Trees are stored consecutively, e.g. in each layer, the tree resulting
813 // from the second MakeTreeFromNewElements() has its root information
814 // after that of the tree resulting from the first MakeTreeFromNewElements().
815 // If a node does not exist, some padding is stored instead.
816 // Padding allows all layers to store the same number of element information,
817 // which is one ElementInfo per element of the original sequence.
818 // The values necessary to navigate the tree are stored in a separate
819 // structure, in tree_location_ and nodes_.
820 std::vector<std::vector<ElementInfo>> tree_layers_;
821
822 // Represents a range of elements inside a node of a wavelet tree.
823 // Also provides methods to compute the range sum query corresponding to
824 // the range, and to project the range to left and right children.
825 struct ElementRange {
826 int range_first_index;
827 int range_last_index; // Last element of the range, inclusive.
828 // True when the first element of this range is the first element of the
829 // node. This is tracked to avoid out-of-bounds indices when computing range
830 // sum queries from prefix sums.
831 bool range_first_is_node_first;
832
833 bool Empty() const { return range_first_index > range_last_index; }
834
835 int64_t Sum(const ElementInfo* elements) const {
836 return elements[range_last_index].prefix_sum -
837 (range_first_is_node_first
838 ? 0
839 : elements[range_first_index - 1].prefix_sum);
840 }
841
842 ElementRange RightSubRange(const ElementInfo* els, int pivot_index) const {
843 ElementRange right = {
844 .range_first_index =
845 pivot_index +
846 (range_first_index - els[range_first_index].left_index),
847 .range_last_index =
848 pivot_index +
849 (range_last_index - els[range_last_index].left_index) -
850 static_cast<int>(els[range_last_index].is_left),
851 .range_first_is_node_first = false};
852 right.range_first_is_node_first = right.range_first_index == pivot_index;
853 return right;
854 }
855
856 ElementRange LeftSubRange(const ElementInfo* els) const {
857 return {.range_first_index = els[range_first_index].left_index,
858 .range_last_index = els[range_last_index].left_index -
859 !els[range_last_index].is_left,
860 .range_first_is_node_first = range_first_is_node_first};
861 }
862 };
863};
864
865// TODO(user): improve this class by:
866// - using WeightedWaveletTree to get the amount of energy above the threshold.
867// - detect when costs above and below are the same, to avoid correcting for
868// energy above the threshold and get O(1) time per chain.
870 public:
881 const PathState* path_state, std::vector<int64_t> force_start_min,
882 std::vector<int64_t> force_end_min, std::vector<int> force_class,
883 std::vector<const std::function<int64_t(int64_t)>*> force_per_class,
884 std::vector<int> distance_class,
885 std::vector<const std::function<int64_t(int64_t, int64_t)>*>
886 distance_per_class,
887 std::vector<EnergyCost> path_energy_cost,
888 std::vector<bool> path_has_cost_when_empty);
889 bool Check();
890 void Commit();
891 int64_t CommittedCost() const { return committed_total_cost_; }
892 int64_t AcceptedCost() const { return accepted_total_cost_; }
893
894 private:
895 int64_t ComputePathCost(int64_t path) const;
896 void CacheAndPrecomputeRangeQueriesOfPath(int path);
897 void IncrementalCacheAndPrecompute();
898 void FullCacheAndPrecompute();
899
900 const PathState* const path_state_;
901 const std::vector<int64_t> force_start_min_;
902 const std::vector<int64_t> force_end_min_;
903 const std::vector<int> force_class_;
904 const std::vector<int> distance_class_;
905 const std::vector<const std::function<int64_t(int64_t)>*> force_per_class_;
906 const std::vector<const std::function<int64_t(int64_t, int64_t)>*>
907 distance_per_class_;
908 const std::vector<EnergyCost> path_energy_cost_;
909 const std::vector<bool> path_has_cost_when_empty_;
910
911 // Range queries.
912 const int maximum_range_query_size_;
913 // Allows to compute the minimum total_force over any chain,
914 // supposing the starting force is 0.
916 std::vector<int> force_rmq_index_of_node_;
917 // Allows to compute the sum of energies of transitions whose total_force is
918 // above a threshold over any chain. Supposes total_force at start is 0.
919 WeightedWaveletTree energy_query_;
920 // Allows to compute the sum of distances of transitions whose total_force is
921 // above a threshold over any chain. Supposes total_force at start is 0.
922 WeightedWaveletTree distance_query_;
923 // Maps nodes to their common index in both threshold queries.
924 std::vector<int> threshold_query_index_of_node_;
925
926 std::vector<int64_t> cached_force_;
927 std::vector<int64_t> cached_distance_;
928
929 // Incremental cost computation.
930 int64_t committed_total_cost_;
931 int64_t accepted_total_cost_;
932 std::vector<int64_t> committed_path_cost_;
933};
934
935LocalSearchFilter* MakePathEnergyCostFilter(
936 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
937 absl::string_view dimension_name);
938
942 const PathState* path_state,
943 const std::vector<RoutingDimension*>& dimensions,
944 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
945
947 const std::vector<RoutingDimension*>& dimensions,
948 const RoutingSearchParameters& parameters, bool filter_objective_cost,
949 bool use_chain_cumul_filter,
950 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
951
953
955 public:
956 BasePathFilter(const std::vector<IntVar*>& nexts, int next_domain_size,
957 const PathsMetadata& paths_metadata);
958 ~BasePathFilter() override = default;
959 bool Accept(const Assignment* delta, const Assignment* deltadelta,
960 int64_t objective_min, int64_t objective_max) override;
961 void OnSynchronize(const Assignment* delta) override;
962
963 protected:
964 static const int64_t kUnassigned;
965
966 int64_t GetNext(int64_t node) const {
967 return (new_nexts_[node] == kUnassigned)
968 ? (IsVarSynced(node) ? Value(node) : kUnassigned)
969 : new_nexts_[node];
970 }
971 bool HasAnySyncedPath() const {
972 for (int64_t start : paths_metadata_.Starts()) {
973 if (IsVarSynced(start)) return true;
974 }
975 return false;
976 }
977 int NumPaths() const { return paths_metadata_.NumPaths(); }
978 int64_t Start(int i) const { return paths_metadata_.Start(i); }
979 int64_t End(int i) const { return paths_metadata_.End(i); }
980 int GetPath(int64_t node) const { return paths_metadata_.GetPath(node); }
981 int Rank(int64_t node) const { return ranks_[node]; }
982 const std::vector<int64_t>& GetTouchedPathStarts() const {
983 return touched_paths_.PositionsSetAtLeastOnce();
984 }
985 bool PathStartTouched(int64_t start) const { return touched_paths_[start]; }
986 const std::vector<int64_t>& GetNewSynchronizedUnperformedNodes() const {
987 return new_synchronized_unperformed_nodes_.PositionsSetAtLeastOnce();
988 }
989
990 bool lns_detected() const { return lns_detected_; }
991
992 private:
993 virtual void OnBeforeSynchronizePaths(bool) {}
994 virtual void OnAfterSynchronizePaths() {}
995 virtual void OnSynchronizePathFromStart(int64_t) {}
996 virtual bool InitializeAcceptPath() { return true; }
997 virtual bool AcceptPath(int64_t path_start, int64_t chain_start,
998 int64_t chain_end) = 0;
999 virtual bool FinalizeAcceptPath(int64_t, int64_t) { return true; }
1001 void ComputePathStarts(std::vector<int64_t>* path_starts,
1002 std::vector<int>* index_to_path);
1003 bool HavePathsChanged();
1004 void SynchronizeFullAssignment();
1005 void UpdateAllRanks();
1006 void UpdatePathRanksFromStart(int start);
1007
1008 const PathsMetadata& paths_metadata_;
1009 std::vector<int64_t> node_path_starts_;
1010 SparseBitset<int64_t> new_synchronized_unperformed_nodes_;
1011 std::vector<int64_t> new_nexts_;
1012 std::vector<int> delta_touched_;
1013 SparseBitset<> touched_paths_;
1014 // clang-format off
1015 std::vector<std::pair<int64_t, int64_t> > touched_path_chain_start_ends_;
1016 // clang-format on
1017 std::vector<int> ranks_;
1018
1019 bool lns_detected_;
1020};
1021
1022} // namespace operations_research
1023
1024#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
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
const std::vector< int64_t > & GetNewSynchronizedUnperformedNodes() const
void OnSynchronize(const Assignment *delta) override
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)
LightVehicleBreaksChecker(PathState *path_state, std::vector< PathData > path_data)
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)
friend class ChainRange
Only a ChainRange can construct its Iterator.
A ChainRange is a range of Chains, committed or not.
ChainRange(const ChainBounds *const begin_chain, const ChainBounds *const end_chain, const int *const first_node)
A Chain is a range of committed nodes.
Chain(const int *begin_node, const int *end_node)
friend class NodeRange
Only a NodeRange can construct its Iterator.
NodeRange(const ChainBounds *begin_chain, const ChainBounds *end_chain, const int *first_node)
int NumNodes() const
Instance-constant accessors.
void ChangeLoops(absl::Span< const int > new_loops)
Describes the nodes that are newly loops in this change.
const std::vector< int > & ChangedLoops() const
Returns the set of loops that were added by the change.
void Reset()
Sets all paths to start->end, all other nodes to kUnassigned.
const std::vector< int > & ChangedPaths() const
void Commit()
Set the current state G1 as committed. See class comment for details.
ChainBounds CommittedPathRange(int path) const
ChainRange Chains(int path) const
Returns the current range of chains of path.
static constexpr int kUnassigned
State-dependent accessors.
int NumPaths() const
Returns the number of paths (empty paths included).
void Revert()
Erase incremental changes. See class comment for details.
NodeRange Nodes(int path) const
Returns the current range of nodes of path.
int End(int path) const
Returns the end of a path.
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
void ChangePath(int path, const std::initializer_list< ChainBounds > &chains)
int CommittedIndex(int node) const
void ChangePath(int path, absl::Span< const ChainBounds > chains)
State modifiers.
int Start(int path) const
Returns the start of a path.
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void PushBack(int64_t height, int64_t weight)
Adds an element at index this->Size().
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
int TreeSize() const
Returns the total number of elements in trees.
In SWIG mode, we don't want anything besides these top-level includes.
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.
LocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const PathState *path_state, const std::vector< PickupDeliveryPair > &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
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)
void FillPrePostVisitValues(int path, const DimensionValues &dimension_values, absl::AnyInvocable< int64_t(int64_t, int64_t) const > pre_travel_evaluator, absl::AnyInvocable< int64_t(int64_t, int64_t) const > post_travel_evaluator, PrePostVisitValues &visit_values)
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.
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 &parameters, 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
Definition base_types.h:55
IntVarLocalSearchFilter * MakeActiveNodeGroupFilter(const RoutingModel &routing_model)
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, absl::string_view dimension_name)
ChainBounds(int begin_index, int end_index)
static const int64_t kint64max
Definition types.h:32