Google OR-Tools v9.15
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 ORTOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
15#define ORTOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
16
17#include <algorithm>
18#include <array>
19#include <cstdint>
20#include <functional>
21#include <initializer_list>
22#include <memory>
23#include <optional>
24#include <utility>
25#include <vector>
26
27#include "absl/functional/any_invocable.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
30#include "ortools/base/types.h"
38#include "ortools/util/bitset.h"
40
41namespace operations_research {
42
43// Given a DimensionValues whose path has changed nodes, fills the travels,
44// travel_sums, transits, cumuls, and span of the new path.
45// This only sets the initial values at each node, and does not propagate
46// the transit constraint cumul[i+1] = cumul[i] + transits[i].
47// Returns false if some cumul.min exceeds the capacity, or if the sum of
48// travels exceeds the span_upper_bound.
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,
54 DimensionValues& dimension_values);
55
57 int path, const DimensionValues& dimension_values,
58 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t) const>>
59 pre_travel_evaluator,
60 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t) const>>
61 post_travel_evaluator,
62 PrePostVisitValues& visit_values);
63
64// Propagates vehicle break constraints in dimension_values.
65// This returns false if breaks cannot fit the path.
66// Otherwise, this returns true, and modifies the start cumul, end cumul and the
67// span of the given path.
68// This applies light reasoning, and runs in O(#breaks * #interbreak rules).
70 int path, DimensionValues& dimension_values,
71 absl::Span<const std::pair<int64_t, int64_t>> interbreaks);
72
75 const RoutingModel& routing_model);
76
79 const RoutingModel& routing_model);
80
84 const RoutingModel& routing_model);
85
89 const RoutingModel& routing_model);
90
93 const RoutingModel& routing_model, bool filter_cost);
94
97 const RoutingModel& routing_model);
98
101 const RoutingModel& routing_model);
102
105 const RoutingModel& routing_model);
106
109 bool propagate_own_objective_value,
110 bool filter_objective_cost,
111 bool may_use_optimizers);
112
115 const RoutingDimension& dimension);
116
119 GlobalDimensionCumulOptimizer* lp_optimizer,
120 GlobalDimensionCumulOptimizer* mp_optimizer, bool filter_objective_cost);
121
126 LocalDimensionCumulOptimizer* mp_optimizer,
127 bool propagate_own_objective_value, bool filter_objective_cost);
128
131
132#if !defined(SWIG)
133// A PathState represents a set of paths and changes made on it.
134//
135// More accurately, let us define P_{num_nodes, starts, ends}-graphs the set of
136// directed graphs with nodes [0, num_nodes) whose connected components are
137// paths from starts[i] to ends[i] (for the same i) and loops.
138// Let us fix num_nodes, starts and ends, so we call these P-graphs.
139//
140// A P-graph can be described by the sequence of nodes of each of its paths,
141// and its set of loops. To describe a change made on a given P-graph G0 that
142// yields another P-graph G1, we choose to describe G1 in terms of G0. When
143// the difference between G0 and G1 is small, as is almost always the case in a
144// local search setting, the description is compact, allowing for incremental
145// filters to be efficient.
146//
147// In order to describe G1 in terms of G0 succintly, we describe each path of
148// G1 as a sequence of chains of G0. A chain of G0 is either a nonempty sequence
149// of consecutive nodes of a path of G0, or a node that was a loop in G0.
150// For instance, a path that was not modified from G0 to G1 has one chain,
151// the sequence of all nodes in the path. Typically, local search operators
152// modify one or two paths, and the resulting paths can described as sequences
153// of two to four chains of G0. Paths that were modified are listed explicitly,
154// allowing to iterate only on changed paths.
155// The loops of G1 are described more implicitly: the loops of G1 not in G0
156// are listed explicitly, but those in both G1 and G0 are not listed.
157//
158// A PathState object can be in two states: committed or changed.
159// At construction, the object is committed, G0.
160// To enter a changed state G1, one can pass modifications with ChangePath() and
161// ChangeLoops(). For reasons of efficiency, a chain is described as a range of
162// node indices in the representation of the committed graph G0. To that effect,
163// the nodes of a path of G0 are guaranteed to have consecutive indices.
164//
165// Filters can then browse the change efficiently using ChangedPaths(),
166// Chains(), Nodes() and ChangedLoops().
167//
168// Then Commit() or Revert() can be called: Commit() sets the changed state G1
169// as the new committed state, Revert() erases all changes.
171 public:
172 // A Chain allows to iterate on all nodes of a chain, and access some data:
173 // first node, last node, number of nodes in the chain.
174 // Chain is a range, its iterator ChainNodeIterator, its value type int.
175 // Chains are returned by PathChainIterator's operator*().
176 class Chain;
177 // A ChainRange allows to iterate on all chains of a path.
178 // ChainRange is a range, its iterator Chain*, its value type Chain.
179 class ChainRange;
180 // A NodeRange allows to iterate on all nodes of a path.
181 // NodeRange is a range, its iterator PathNodeIterator, its value type int.
182 class NodeRange;
183
191 int CommittedIndex(int node) const { return committed_index_[node]; }
192 ChainBounds CommittedPathRange(int path) const { return chains_[path]; }
193
194 // Path constructor: path_start and path_end must be disjoint,
195 // their values in [0, num_nodes).
196 PathState(int num_nodes, std::vector<int> path_start,
197 std::vector<int> path_end);
198
199 // Instance-constant accessors.
200
201 // Returns the number of nodes in the underlying graph.
202 int NumNodes() const { return num_nodes_; }
203 // Returns the number of paths (empty paths included).
204 int NumPaths() const { return num_paths_; }
205 // Returns the start of a path.
206 int Start(int path) const { return path_start_end_[path].start; }
207 // Returns the end of a path.
208 int End(int path) const { return path_start_end_[path].end; }
209
210 // State-dependent accessors.
211
212 static constexpr int kUnassigned = -2;
213 static constexpr int kLoop = -1;
214 // Returns the committed path of a given node, kLoop if it is a loop,
215 // kUnassigned if it is not assigned,
216 int Path(int node) const { return committed_paths_[node]; }
217 // Returns the set of paths that actually changed,
218 // i.e. that have more than one chain.
219 const std::vector<int>& ChangedPaths() const { return changed_paths_; }
220 // Returns the set of loops that were added by the change.
221 const std::vector<int>& ChangedLoops() const { return changed_loops_; }
222 // Returns the current range of chains of path.
223 ChainRange Chains(int path) const;
224 // Returns the current range of nodes of path.
225 NodeRange Nodes(int path) const;
226
227 // State modifiers.
228
229 // Changes the path to the given sequence of chains of the committed state.
230 // Chains are described by semi-open intervals. No optimization is made in
231 // case two consecutive chains are actually already consecutive in the
232 // committed state: they are not merged into one chain, and Chains(path) will
233 // report the two chains.
234 void ChangePath(int path, absl::Span<const ChainBounds> chains);
235 // Same as above, but the initializer_list interface avoids the need to pass
236 // a vector.
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};
243 // Always add sentinel, in case this is the last path change.
244 chains_.emplace_back(0, 0);
245 }
246
247 // Describes the nodes that are newly loops in this change.
248 void ChangeLoops(absl::Span<const int> new_loops);
249
250 // Set the current state G1 as committed. See class comment for details.
251 void Commit();
252 // Erase incremental changes. See class comment for details.
253 void Revert();
254 // Sets all paths to start->end, all other nodes to kUnassigned.
255 void Reset();
256
257 // LNS Operators may not fix variables,
258 // in which case we mark the candidate invalid.
259 void SetInvalid() { is_invalid_ = true; }
260 bool IsInvalid() const { return is_invalid_; }
261
262 private:
263 // Most structs below are named pairs of ints, for typing purposes.
264
265 // Start and end are stored together to optimize (likely) simultaneous access.
266 struct PathStartEnd {
267 PathStartEnd(int start, int end) : start(start), end(end) {}
268 int start;
269 int end;
270 };
271 // Paths are ranges of chains, which are ranges of committed nodes, see below.
272 struct PathBounds {
273 int begin_index;
274 int end_index;
275 };
276
277 // Copies nodes in chains of path at the end of nodes,
278 // and sets those nodes' path member to value path.
279 void CopyNewPathAtEndOfNodes(int path);
280 // Commits paths in O(#{changed paths' nodes}) time,
281 // increasing this object's space usage by O(|changed path nodes|).
282 void IncrementalCommit();
283 // Commits paths in O(num_nodes + num_paths) time,
284 // reducing this object's space usage to O(num_nodes + num_paths).
285 void FullCommit();
286
287 // Instance-constant data.
288 const int num_nodes_;
289 const int num_paths_;
290 std::vector<PathStartEnd> path_start_end_;
291
292 // Representation of the committed and changed paths.
293 // A path is a range of chains, which is a range of nodes.
294 // Ranges are represented internally by indices in vectors:
295 // ChainBounds are indices in committed_nodes_. PathBounds are indices in
296 // chains_. When committed (after construction, Revert() or Commit()):
297 // - path ranges are [path, path+1): they have one chain.
298 // - chain ranges don't overlap, chains_ has an empty sentinel at the end.
299 // The sentinel allows the Nodes() iterator to maintain its current pointer
300 // to committed nodes on NodeRange::operator++().
301 // - committed_nodes_ contains all nodes, both paths and loops.
302 // Actually, old duplicates will likely appear,
303 // the current version of a node is at the index given by
304 // committed_index_[node]. A Commit() can add nodes at the end of
305 // committed_nodes_ in a space/time tradeoff, but if committed_nodes_' size
306 // is above num_nodes_threshold_, Commit() must reclaim useless duplicates'
307 // space by rewriting the path/chain/nodes structure.
308 // When changed (after ChangePaths() and ChangeLoops()),
309 // the structure is updated accordingly:
310 // - path ranges that were changed have nonoverlapping values [begin, end)
311 // where begin is >= num_paths_ + 1, i.e. new chains are stored after
312 // the committed state.
313 // - additional chain ranges are stored after the committed chains and its
314 // sentinel to represent the new chains resulting from the changes.
315 // Those chains do not overlap with one another or with committed chains.
316 // - committed_nodes_ are not modified, and still represent the committed
317 // paths. committed_index_ is not modified either.
318 std::vector<int> committed_nodes_;
319 // Maps nodes to their path in the latest committed state.
320 std::vector<int> committed_paths_;
321 // Maps nodes to their index in the latest committed state.
322 std::vector<int> committed_index_;
323 const int num_nodes_threshold_;
324 std::vector<ChainBounds> chains_;
325 std::vector<PathBounds> paths_;
326
327 // Incremental information.
328 std::vector<int> changed_paths_;
329 std::vector<int> changed_loops_;
330
331 // See IsInvalid() and SetInvalid().
332 bool is_invalid_ = false;
333};
334
335// A Chain is a range of committed nodes.
337 public:
338 class Iterator {
339 public:
340 Iterator& operator++() {
341 ++current_node_;
342 return *this;
343 }
344 int operator*() const { return *current_node_; }
345 bool operator!=(Iterator other) const {
346 return current_node_ != other.current_node_;
347 }
348
349 private:
350 // Only a Chain can construct its iterator.
351 friend class PathState::Chain;
352 explicit Iterator(const int* node) : current_node_(node) {}
353 const int* current_node_;
354 };
355
356 // Chains hold CommittedNode* values, a Chain may be invalidated
357 // if the underlying vector is modified.
358 Chain(const int* begin_node, const int* end_node)
359 : begin_(begin_node), end_(end_node) {}
360
361 int NumNodes() const { return end_ - begin_; }
362 int First() const { return *begin_; }
363 int Last() const { return *(end_ - 1); }
364 Iterator begin() const { return Iterator(begin_); }
365 Iterator end() const { return Iterator(end_); }
366
367 Chain WithoutFirstNode() const { return Chain(begin_ + 1, end_); }
368
369 private:
370 const int* const begin_;
371 const int* const end_;
372};
373
374// A ChainRange is a range of Chains, committed or not.
376 public:
377 class Iterator {
378 public:
379 Iterator& operator++() {
380 ++current_chain_;
381 return *this;
382 }
383 Chain operator*() const {
384 return {first_node_ + current_chain_->begin_index,
385 first_node_ + current_chain_->end_index};
386 }
387 bool operator!=(Iterator other) const {
388 return current_chain_ != other.current_chain_;
389 }
390
391 private:
392 // Only a ChainRange can construct its Iterator.
393 friend class ChainRange;
394 Iterator(const ChainBounds* chain, const int* const first_node)
395 : current_chain_(chain), first_node_(first_node) {}
396 const ChainBounds* current_chain_;
397 const int* const first_node_;
398 };
399
400 // ChainRanges hold ChainBounds* and CommittedNode*,
401 // a ChainRange may be invalidated if on of the underlying vector is modified.
402 ChainRange(const ChainBounds* const begin_chain,
403 const ChainBounds* const end_chain, const int* const first_node)
404 : begin_(begin_chain), end_(end_chain), first_node_(first_node) {}
405
406 Iterator begin() const { return {begin_, first_node_}; }
407 Iterator end() const { return {end_, first_node_}; }
408
410 return (begin_ == end_) ? *this : ChainRange(begin_ + 1, end_, first_node_);
411 }
412
414 return (begin_ == end_) ? *this : ChainRange(begin_, end_ - 1, first_node_);
415 }
416
417 private:
418 const ChainBounds* const begin_;
419 const ChainBounds* const end_;
420 const int* const first_node_;
421};
422
423// A NodeRange allows to iterate on all nodes of a path,
424// by a two-level iteration on ChainBounds* and CommittedNode* of a PathState.
426 public:
427 class Iterator {
428 public:
429 Iterator& operator++() {
430 ++current_node_;
431 if (current_node_ == end_node_) {
432 ++current_chain_;
433 // Note: dereferencing bounds is valid because there is a sentinel
434 // value at the end of PathState::chains_ to that intent.
435 const ChainBounds bounds = *current_chain_;
436 current_node_ = first_node_ + bounds.begin_index;
437 end_node_ = first_node_ + bounds.end_index;
438 }
439 return *this;
440 }
441 int operator*() const { return *current_node_; }
442 bool operator!=(Iterator other) const {
443 return current_chain_ != other.current_chain_;
444 }
445
446 private:
447 // Only a NodeRange can construct its Iterator.
448 friend class NodeRange;
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_;
456 const ChainBounds* current_chain_;
457 const int* const first_node_;
458 };
459
460 // NodeRanges hold ChainBounds* and int* (first committed node),
461 // a NodeRange may be invalidated if on of the underlying vector is modified.
462 NodeRange(const ChainBounds* begin_chain, const ChainBounds* end_chain,
463 const int* first_node)
464 : begin_chain_(begin_chain),
465 end_chain_(end_chain),
466 first_node_(first_node) {}
467 Iterator begin() const { return {begin_chain_, first_node_}; }
468 // Note: there is a sentinel value at the end of PathState::chains_,
469 // so dereferencing chain_range_.end()->begin_ is always valid.
470 Iterator end() const { return {end_chain_, first_node_}; }
471
472 private:
473 const ChainBounds* begin_chain_;
474 const ChainBounds* end_chain_;
475 const int* const first_node_;
476};
477
478// Make a filter that takes ownership of a PathState and synchronizes it with
479// solver events. The solver represents a graph with array of variables 'nexts'.
480// Solver events are embodied by Assignment* deltas, that are translated to node
481// changes during Relax(), committed during Synchronize(), and reverted on
482// Revert().
484 std::unique_ptr<PathState> path_state,
485 const std::vector<IntVar*>& nexts);
486
489 const PathState* path_state);
490
494 const RoutingModel& routing_model, const PathState* path_state,
495 absl::Span<const PickupDeliveryPair> pairs,
496 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies);
497
498// This checker enforces dimension requirements.
499// A dimension requires that there is some valuation of
500// cumul and demand such that for all paths:
501// - cumul[A] is in interval node_capacity[A]
502// - if arc A -> B is on a path of path_class p,
503// then cumul[A] + demand[p](A, B) = cumul[B].
504// - if A is on a path of class p, then
505// cumul[A] must be inside interval path_capacity[path].
507 public:
508 struct Interval {
509 int64_t min;
510 int64_t max;
511 };
512
514 int64_t min;
515 int64_t max;
518 };
519
520 // TODO(user): the addition of kMinRangeSizeForRIQ slowed down Check().
521 // See if using a template parameter makes it faster.
522 DimensionChecker(const PathState* path_state,
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,
528 int min_range_size_for_riq = kOptimalMinRangeSizeForRIQ);
529
530 // Given the change made in PathState, checks that the dimension
531 // constraint is still feasible.
532 bool Check() const;
533
534 // Commits to the changes made in PathState,
535 // must be called before PathState::Commit().
536 void Commit();
537
538 static constexpr int kOptimalMinRangeSizeForRIQ = 4;
539
540 private:
541 inline void UpdateCumulUsingChainRIQ(int first_index, int last_index,
542 const ExtendedInterval& path_capacity,
543 ExtendedInterval& cumul) const;
544
545 // Commits to the current solution and rebuilds structures from scratch.
546 void FullCommit();
547 // Commits to the current solution and only build structures for paths that
548 // changed, using additional space to do so in a time-memory tradeoff.
549 void IncrementalCommit();
550 // Adds sums of given path to the bottom layer of the Range Intersection Query
551 // structure, updates index_ and previous_nontrivial_index_.
552 void AppendPathDemandsToSums(int path);
553 // Updates the Range Intersection Query structure from its bottom layer,
554 // with [begin_index, end_index) the range of the change,
555 // which must be at the end of the bottom layer.
556 // Supposes that requests overlapping the range will be inside the range,
557 // to avoid updating all layers.
558 void UpdateRIQStructure(int begin_index, int end_index);
559
560 const PathState* const path_state_;
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_;
567
568 // Precomputed data.
569 // Maps nodes to their pre-computed data, except for isolated nodes,
570 // which do not have precomputed data.
571 // Only valid for nodes that are in some path in the committed state.
572 std::vector<int> index_;
573 // Range intersection query in <O(n log n), O(1)>, with n = #nodes.
574 // Let node be in a path, i = index_[node], start the start of node's path.
575 // Let l such that index_[start] <= i - 2**l.
576 // - riq_[l][i].tsum_at_lst contains the sum of demands from start to node.
577 // - riq_[l][i].tsum_at_fst contains the sum of demands from start to the
578 // node at i - 2**l.
579 // - riq_[l][i].tightest_tsum contains the intersection of
580 // riq_[0][j].tsum_at_lst for all j in (i - 2**l, i].
581 // - riq_[0][i].cumuls_to_lst and riq_[0][i].cumuls_to_fst contain
582 // the node's capacity.
583 // - riq_[l][i].cumuls_to_lst is the intersection, for j in (i - 2**l, i], of
584 // riq_[0][j].cumuls_to_lst + sum_{k in [j, i)} demand(k, k+1)
585 // - riq_[l][i].cumuls_to_fst is the intersection, for j in (i - 2**l, i], of
586 // riq_[0][j].cumuls_to_fst - sum_{k in (i-2**l, j)} demand(k, k+1)
587 struct RIQNode {
588 ExtendedInterval cumuls_to_fst;
589 ExtendedInterval tightest_tsum;
590 ExtendedInterval cumuls_to_lst;
591 ExtendedInterval tsum_at_fst;
592 ExtendedInterval tsum_at_lst;
593 };
594 std::vector<std::vector<RIQNode>> riq_;
595 // The incremental branch of Commit() may waste space in the layers of the
596 // RIQ structure. This is the upper limit of a layer's size.
597 const int maximum_riq_layer_size_;
598 // Range queries are used on a chain only if the range is larger than this.
599 const int min_range_size_for_riq_;
600};
601
602// Make a filter that translates solver events to the input checker's interface.
603// Since DimensionChecker has a PathState, the filter returned by this
604// must be synchronized to the corresponding PathStateFilter:
605// - Relax() must be called after the PathStateFilter's.
606// - Accept() must be called after.
607// - Synchronize() must be called before.
608// - Revert() must be called before.
609LocalSearchFilter* MakeDimensionFilter(
610 Solver* solver, std::unique_ptr<DimensionChecker> checker,
611 absl::string_view dimension_name);
612#endif // !defined(SWIG)
613
615 public:
617 int64_t start_min;
618 int64_t start_max;
619 int64_t end_min;
620 int64_t end_max;
624 };
629
638
640 std::vector<PathData> path_data);
641
642 void Relax() const;
643
644 bool Check() const;
645
646 private:
647 PathState* path_state_;
648 std::vector<PathData> path_data_;
649};
650
652 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
653 absl::string_view dimension_name);
654
655// This class allows making fast range queries on sequences of elements.
656// * Main characteristics.
657// - queries on sequences of elements {height, weight},
658// parametrized by (begin, end, T), returning
659// sum_{i \in [begin, end), S[i].height >= T} S[i].weight
660// - O(log (#different heights)) time complexity thanks to an underlying
661// wavelet tree (https://en.wikipedia.org/wiki/Wavelet_Tree)
662// - holds several sequences at once, can be cleared while still keeping
663// allocated memory to avoid allocations.
664// More details on these points follow.
665//
666// * Query complexity.
667// The time complexity of a query in S is O(log H), where H is the number of
668// different heights appearing in S.
669// The particular implementation guarantees that queries that are trivial in
670// the .height dimension, that is if threshold_height is <= or >= all heights
671// in the range, are O(1).
672//
673// * Initialization complexity.
674// The time complexity of filling the underlying data structures,
675// which is done by running MakeTreeFromNewElements(),
676// is O(N log N) where N is the number of new elements.
677// The space complexity is a O(N log H).
678//
679// * Usage.
680// Given Histogram holding elements with fields {.height, .weight},
681// Histogram hist1 {{2, 3}, {1, 4}, {4, 1}, {2, 2}, {3, 1}, {0, 4}};
682// Histogram hist2 {{-2, -3}, {-1, -4}, {-4, -1}, {-2, -2}};
683// WeightedWaveletTree tree;
684//
685// for (const auto [height, weight] : hist1]) {
686// tree.PushBack(height, weight);
687// }
688// const int begin1 = tree.TreeSize();
689// tree.MakeTreeFromNewElements();
690// const int end1 = tree.TreeSize();
691// const int begin2 = tree.TreeSize(); // begin2 == end1.
692// for (const auto [height, weight] : hist2]) {
693// tree.PushBack(height, weight);
694// }
695// tree.MakeTreeFromNewElements();
696// const int end2 = tree.TreeSize();
697//
698// // Sum of weights on whole first sequence, == 3 + 4 + 1 + 2 + 1 + 4
699// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin1, /*end=*/end1);
700// // Sum of weights on whole second sequence, all heights are negative,
701// // so the result is 0.
702// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin2, /*end=*/end2);
703// // This is forbidden, because the range overlaps two sequences.
704// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/2, /*end=*/10);
705// // Returns 2 = 0 + 1 + 0 + 1.
706// tree.RangeSumWithThreshold(/*threshold=*/3, /*begin=*/1, /*end=*/5);
707// // Returns -6 = -4 + 0 + -2.
708// tree.RangeSumWithThreshold(/*threshold=*/-2, /*begin=*/1, /*end=*/4);
709// // Add another sequence.
710// Histogram hist3 {{1, 1}, {3, 4}};
711// const int begin3 = tree.TreeSize();
712// for (const auto [height, weight] : hist3) {
713// tree.PushBack(height, weight);
714// }
715// tree.MakeTreeFromNewElements();
716// const int end3 = tree.TreeSize();
717// // Returns 4 = 0 + 4.
718// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
719// // Clear the tree, this invalidates all range queries.
720// tree.Clear();
721// // Forbidden!
722// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
723//
724// * Implementation.
725// This data structure uses two main techniques of the wavelet tree:
726// - a binary search tree in the height dimension.
727// - nodes only hold information about elements in their height range,
728// keeping selected elements in the same order as the full sequence,
729// and can map the index of its elements to their left and right child.
730// The layout of the tree is packed by separating the tree navigation
731// information from the (prefix sum + mapping) information.
732// Here is how the tree for heights 6 4 1 3 6 1 7 4 2 is laid out in memory:
733// tree_layers_ // nodes_
734// 6 4 1 3 6 1 7 4 2 // 4
735// 1 3 1 2|6 4 6 7 4 // 2 6
736// 1 1|3 2|4 4|6 6 7 // _ 3 _ 7
737// _ _|2|3|_ _|6 6|7 // Dummy information is used to pad holes in nodes_.
738// In addition to the mapping information of each element, each node holds
739// the prefix sum of weights up to each element, to be able to compute the sum
740// of S[i].weight of elements in its height range, for any range, in O(1).
741// The data structure does not actually need height information inside the tree
742// nodes, and does not store them.
744 public:
746
747 // Clears all trees, which invalidates all further range queries on currently
748 // existing trees. This does *not* release memory held by this object.
749 void Clear();
750
751 // Returns the total number of elements in trees.
752 int TreeSize() const { return tree_location_.size(); }
753
754 // Adds an element at index this->Size().
755 void PushBack(int64_t height, int64_t weight) {
756 elements_.push_back({.height = height, .weight = weight});
757 }
758
759 // Generates the wavelet tree for all new elements, i.e. elements that were
760 // added with PushBack() since the latest of these events: construction of
761 // this object, a previous call to MakeTreeFromNewElements(), or a call to
762 // Clear().
763 // The range of new elements [begin, end), with begin the Size() at the
764 // latest event, and end the current Size().
766
767 // Returns sum_{begin_index <= i < end_index,
768 // S[i].height >= threshold_height} S[i].weight.
769 // The range [begin_index, end_index) can only cover elements that were new
770 // at the same call to MakeTreeFromNewElements().
771 // When calling this method, there must be no pending new elements,
772 // i.e. the last method called must not have been PushBack() or TreeSize().
773 int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index,
774 int end_index) const;
775
776 private:
777 // Internal copy of an element.
778 struct Element {
779 int64_t height;
780 int64_t weight;
781 };
782 // Elements are stored in a vector, they are only used during the
783 // initialization of the data structure.
784 std::vector<Element> elements_;
785
786 // Maps the index of an element to the location of its tree.
787 // Elements of the same sequence have the same TreeLocation value.
788 struct TreeLocation {
789 int node_begin; // index of the first node in the tree in nodes_.
790 int node_end; // index of the last node in the tree in nodes_, plus 1.
791 int sequence_first; // index of the first element in all layers.
792 };
793 std::vector<TreeLocation> tree_location_;
794
795 // A node of the tree is represented by the height of its pivot element and
796 // the index of its pivot in the layer below, or -1 if the node is a leaf.
797 struct Node {
798 int64_t pivot_height;
799 int pivot_index;
800 bool operator<(const Node& other) const {
801 return pivot_height < other.pivot_height;
802 }
803 bool operator==(const Node& other) const {
804 return pivot_height == other.pivot_height;
805 }
806 };
807 std::vector<Node> nodes_;
808
809 // Holds range sum query and mapping information of each element
810 // in each layer.
811 // - prefix_sum: sum of weights in this node up to this element, included.
812 // - left_index: number of elements in the same layer that are either:
813 // - in a node on the left of this node, or
814 // - in the same node, preceding this element, mapped to the left subtree.
815 // Coincides with this element's index in the left subtree if is_left = 1.
816 // - is_left: 1 if the element is in the left subtree, otherwise 0.
817 struct ElementInfo {
818 int64_t prefix_sum;
819 int left_index : 31;
820 unsigned int is_left : 1;
821 };
822 // Contains range sum query and mapping data of all elements in their
823 // respective tree, arranged by layer (depth) in the tree.
824 // Layer 0 has root data, layer 1 has information of the left child
825 // then the right child, layer 2 has left-left, left-right, right-left,
826 // then right-right, etc.
827 // Trees are stored consecutively, e.g. in each layer, the tree resulting
828 // from the second MakeTreeFromNewElements() has its root information
829 // after that of the tree resulting from the first MakeTreeFromNewElements().
830 // If a node does not exist, some padding is stored instead.
831 // Padding allows all layers to store the same number of element information,
832 // which is one ElementInfo per element of the original sequence.
833 // The values necessary to navigate the tree are stored in a separate
834 // structure, in tree_location_ and nodes_.
835 std::vector<std::vector<ElementInfo>> tree_layers_;
836
837 // Represents a range of elements inside a node of a wavelet tree.
838 // Also provides methods to compute the range sum query corresponding to
839 // the range, and to project the range to left and right children.
840 struct ElementRange {
841 int range_first_index;
842 int range_last_index; // Last element of the range, inclusive.
843 // True when the first element of this range is the first element of the
844 // node. This is tracked to avoid out-of-bounds indices when computing range
845 // sum queries from prefix sums.
846 bool range_first_is_node_first;
847
848 bool Empty() const { return range_first_index > range_last_index; }
849
850 int64_t Sum(const ElementInfo* elements) const {
851 return elements[range_last_index].prefix_sum -
852 (range_first_is_node_first
853 ? 0
854 : elements[range_first_index - 1].prefix_sum);
855 }
856
857 ElementRange RightSubRange(const ElementInfo* els, int pivot_index) const {
858 ElementRange right = {
859 .range_first_index =
860 pivot_index +
861 (range_first_index - els[range_first_index].left_index),
862 .range_last_index =
863 pivot_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;
868 return right;
869 }
870
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};
876 }
877 };
878};
879
880// TODO(user): improve this class by:
881// - using WeightedWaveletTree to get the amount of energy above the threshold.
882// - detect when costs above and below are the same, to avoid correcting for
883// energy above the threshold and get O(1) time per chain.
885 public:
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)>*>
901 distance_per_class,
902 std::vector<EnergyCost> path_energy_cost,
903 std::vector<bool> path_has_cost_when_empty);
904 bool Check();
905 void Commit();
906 int64_t CommittedCost() const { return committed_total_cost_; }
907 int64_t AcceptedCost() const { return accepted_total_cost_; }
908
909 private:
910 int64_t ComputePathCost(int64_t path) const;
911 void CacheAndPrecomputeRangeQueriesOfPath(int path);
912 void IncrementalCacheAndPrecompute();
913 void FullCacheAndPrecompute();
914
915 const PathState* const path_state_;
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)>*>
922 distance_per_class_;
923 const std::vector<EnergyCost> path_energy_cost_;
924 const std::vector<bool> path_has_cost_when_empty_;
925
926 // Range queries.
927 const int maximum_range_query_size_;
928 // Allows to compute the minimum total_force over any chain,
929 // supposing the starting force is 0.
931 std::vector<int> force_rmq_index_of_node_;
932 // Allows to compute the sum of energies of transitions whose total_force is
933 // above a threshold over any chain. Supposes total_force at start is 0.
934 WeightedWaveletTree energy_query_;
935 // Allows to compute the sum of distances of transitions whose total_force is
936 // above a threshold over any chain. Supposes total_force at start is 0.
937 WeightedWaveletTree distance_query_;
938 // Maps nodes to their common index in both threshold queries.
939 std::vector<int> threshold_query_index_of_node_;
940
941 std::vector<int64_t> cached_force_;
942 std::vector<int64_t> cached_distance_;
943
944 // Incremental cost computation.
945 int64_t committed_total_cost_;
946 int64_t accepted_total_cost_;
947 std::vector<int64_t> committed_path_cost_;
948};
949
950LocalSearchFilter* MakePathEnergyCostFilter(
951 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
952 absl::string_view dimension_name);
953
957 const PathState* path_state,
958 const std::vector<RoutingDimension*>& dimensions,
959 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
960
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);
966
968
970 public:
971 BasePathFilter(const std::vector<IntVar*>& nexts, int next_domain_size,
972 const PathsMetadata& paths_metadata);
973 ~BasePathFilter() override = default;
974 bool Accept(const Assignment* delta, const Assignment* deltadelta,
975 int64_t objective_min, int64_t objective_max) override;
976 void OnSynchronize(const Assignment* delta) override;
977
978 protected:
979 static const int64_t kUnassigned;
980
981 int64_t GetNext(int64_t node) const {
982 return (new_nexts_[node] == kUnassigned)
983 ? (IsVarSynced(node) ? Value(node) : kUnassigned)
984 : new_nexts_[node];
985 }
986 bool HasAnySyncedPath() const {
987 for (int64_t start : paths_metadata_.Starts()) {
988 if (IsVarSynced(start)) return true;
989 }
990 return false;
991 }
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]; }
997 const std::vector<int64_t>& GetTouchedPathStarts() const {
998 return touched_paths_.PositionsSetAtLeastOnce();
999 }
1000 bool PathStartTouched(int64_t start) const { return touched_paths_[start]; }
1001 const std::vector<int64_t>& GetNewSynchronizedUnperformedNodes() const {
1002 return new_synchronized_unperformed_nodes_.PositionsSetAtLeastOnce();
1003 }
1004
1005 bool lns_detected() const { return lns_detected_; }
1006
1007 private:
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);
1022
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_;
1029 // clang-format off
1030 std::vector<std::pair<int64_t, int64_t> > touched_path_chain_start_ends_;
1031 // clang-format on
1032 std::vector<int> ranks_;
1033
1034 bool lns_detected_;
1035};
1036
1037// For a fixed matrix of coefficients rows, allows to computes
1038// max_r(sum_c(rows[r][c] * values[c])) efficiently for any vector of values.
1039// A straightforward computation would best leverage SIMD instructions when
1040// there are many columns. This class computes kBlockSize scalar products in
1041// parallel, which optimizes the many rows and few columns cases.
1042// The constructor reorganizes the input rows into a blocked layout, so that
1043// subsequent calls to Evaluate() can benefit from more efficient memory access.
1044//
1045// For instance, suppose the kBlockSize is 4 and rows is a 7 x 5 matrix:
1046// 11 12 13 14 15
1047// 21 22 23 24 25
1048// 31 32 33 34 35
1049// 41 42 43 44 45
1050// 51 52 53 54 55
1051// 61 62 63 64 65
1052// 71 72 73 74 75
1053//
1054// This class will separate the matrix into 4 x 1 submatrices:
1055// 11 | 12 | 13 | 14 | 15
1056// 21 | 22 | 23 | 24 | 25
1057// 31 | 32 | 33 | 34 | 35
1058// 41 | 42 | 43 | 44 | 45
1059// ---+----+----+----+----
1060// 51 | 52 | 53 | 54 | 55
1061// 61 | 62 | 63 | 64 | 65
1062// 71 | 72 | 73 | 74 | 75
1063// XX | XX | XX | XX | XX
1064// NOTE: we need to expand the matrix until the number of rows is a multiple of
1065// kBlockSize. We do that by adding copies of an existing row, which does not
1066// change the semantics "maximum over linear expressions".
1067//
1068// Those blocks are aggregated into a single vector of blocks:
1069// {{11, 21, 31, 41}, {12, 22, 32, 42}, {13, 23, 33, 43}, {14, 24, 34, 44}.
1070// {15, 25, 35, 45}, {51, 61, 71, XX}, {52, 62, 72, XX}, {53, 63, 73, XX},
1071// {54, 64, 74, XX}, {55, 65, 75, XX}}.
1072//
1073// The general formula to map rows to blocks: rows[r][v] is mapped to
1074// blocks_[r / kBlockSize * num_variables_ + v].coefficient[r % kBlockSize].
1075// blocks_[(br, v)].coefficient[c] = row[br * kBlockSize + c][v].
1076//
1077// When evaluating a vector of values, instead of computing:
1078// max_{r in [0, num_rows)}
1079// sum_{c in [0, num_variables_)} rows[r][c] * values[c],
1080// we compute:
1081// max_{r' in [0, ceil(num_rows / kBlockSize))}
1082// BlockMaximum(sum_{i in [0, num_variables)}
1083// blocks[r' * num_variables + i] * values[i]),
1084// with BlockMaximum(block) = max_{j in [0, kBlockSize)} block[j].
1086 public:
1087 // Makes an object that can evaluate the expression
1088 // max_r(sum_c(rows[r][c] * values[c])) for any vector of values.
1090 const std::vector<std::vector<double>>& rows);
1091 // Returns max_r(sum_c(rows[r][c] * values[c])).
1092 double Evaluate(absl::Span<const double> values) const;
1093
1094 private:
1095 // This number was found by running the associated microbenchmarks.
1096 // It is larger than one cacheline or SIMD register, surprisingly.
1097 static constexpr int kBlockSize = 16;
1098 struct Block {
1099 std::array<double, kBlockSize> coefficients;
1100 // Returns *this += other * value.
1101 Block& BlockMultiplyAdd(const Block& other, double value) {
1102 // The loop bounds are known in advance, we rely on the compiler to unroll
1103 // and SIMD optimize it.
1104 for (int i = 0; i < kBlockSize; ++i) {
1105 coefficients[i] += other.coefficients[i] * value;
1106 }
1107 return *this;
1108 }
1109 Block& MaximumWith(const Block& other) {
1110 for (int i = 0; i < kBlockSize; ++i) {
1111 coefficients[i] = std::max(coefficients[i], other.coefficients[i]);
1112 }
1113 return *this;
1114 }
1115 double Maximum() const {
1116 return *std::max_element(coefficients.begin(), coefficients.end());
1117 }
1118 };
1119 std::vector<Block> blocks_;
1120 const int64_t num_variables_;
1121 const int64_t num_rows_;
1122};
1123
1124} // namespace operations_research
1125
1126#endif // ORTOOLS_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)
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)
ChainRange(const ChainBounds *const begin_chain, const ChainBounds *const end_chain, const int *const first_node)
Chain(const int *begin_node, const int *end_node)
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)
void ChangePath(int path, const std::initializer_list< ChainBounds > &chains)
int CommittedIndex(int node) const
void ChangePath(int path, absl::Span< const ChainBounds > chains)
void PushBack(int64_t height, int64_t weight)
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
OR-Tools root namespace.
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 &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)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
ChainBounds(int begin_index, int end_index)
static const int64_t kint64max
Definition types.h:32