C++ Reference

C++ Reference: Routing

routing_lp_scheduling.h
Go to the documentation of this file.
1 // Copyright 2010-2018 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_LP_SCHEDULING_H_
15 #define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
16 
17 #include "absl/container/flat_hash_map.h"
18 #include "absl/memory/memory.h"
20 #include "ortools/glop/lp_solver.h"
21 #include "ortools/lp_data/lp_types.h"
22 #include "ortools/sat/cp_model.h"
23 #include "ortools/util/saturated_arithmetic.h"
24 
25 namespace operations_research {
26 
27 // Classes to solve dimension cumul placement (aka scheduling) problems using
28 // linear programming.
29 
30 // Utility class used in the core optimizer to tighten the cumul bounds as much
31 // as possible based on the model precedences.
33  public:
35 
36  // Tightens the cumul bounds starting from the current cumul var min/max,
37  // and propagating the precedences resulting from the next_accessor, and the
38  // dimension's precedence rules.
39  // Returns false iff the precedences are infeasible with the given routes.
40  // Otherwise, the user can call CumulMin() and CumulMax() to retrieve the new
41  // bounds of an index.
42  bool PropagateCumulBounds(const std::function<int64(int64)>& next_accessor,
43  int64 cumul_offset);
44 
45  int64 CumulMin(int index) const {
46  return propagated_bounds_[PositiveNode(index)];
47  }
48 
49  int64 CumulMax(int index) const {
50  const int64 negated_upper_bound = propagated_bounds_[NegativeNode(index)];
51  return negated_upper_bound == kint64min ? kint64max : -negated_upper_bound;
52  }
53 
54  const RoutingDimension& dimension() const { return dimension_; }
55 
56  private:
57  // An arc "tail --offset--> head" represents the relation
58  // tail + offset <= head.
59  // As arcs are stored by tail, we don't store it in the struct.
60  struct ArcInfo {
61  int head;
62  int64 offset;
63  };
64  static const int kNoParent;
65  static const int kParentToBePropagated;
66 
67  // Return the node corresponding to the lower bound of the cumul of index and
68  // -index respectively.
69  int PositiveNode(int index) const { return 2 * index; }
70  int NegativeNode(int index) const { return 2 * index + 1; }
71 
72  void AddNodeToQueue(int node) {
73  if (!node_in_queue_[node]) {
74  bf_queue_.push_back(node);
75  node_in_queue_[node] = true;
76  }
77  }
78 
79  // Adds the relation first_index + offset <= second_index, by adding arcs
80  // first_index --offset--> second_index and
81  // -second_index --offset--> -first_index.
82  void AddArcs(int first_index, int second_index, int64 offset);
83 
84  bool InitializeArcsAndBounds(const std::function<int64(int64)>& next_accessor,
85  int64 cumul_offset);
86 
87  bool UpdateCurrentLowerBoundOfNode(int node, int64 new_lb, int64 offset);
88 
89  bool DisassembleSubtree(int source, int target);
90 
91  bool CleanupAndReturnFalse() {
92  // We clean-up node_in_queue_ for future calls, and return false.
93  for (int node_to_cleanup : bf_queue_) {
94  node_in_queue_[node_to_cleanup] = false;
95  }
96  bf_queue_.clear();
97  return false;
98  }
99 
100  const RoutingDimension& dimension_;
101  const int64 num_nodes_;
102 
103  // TODO(user): Investigate if all arcs for a given tail can be created
104  // at the same time, in which case outgoing_arcs_ could point to an absl::Span
105  // for each tail index.
106  std::vector<std::vector<ArcInfo>> outgoing_arcs_;
107 
108  std::deque<int> bf_queue_;
109  std::vector<bool> node_in_queue_;
110  std::vector<int> tree_parent_node_of_;
111  // After calling PropagateCumulBounds(), for each node index n,
112  // propagated_bounds_[2*n] and -propagated_bounds_[2*n+1] respectively contain
113  // the propagated lower and upper bounds of n's cumul variable.
114  std::vector<int64> propagated_bounds_;
115 
116  // Vector used in DisassembleSubtree() to avoid memory reallocation.
117  std::vector<int> tmp_dfs_stack_;
118 
119  // Used to store the pickup/delivery pairs encountered on the routes.
120  std::vector<std::pair<int64, int64>>
121  visited_pickup_delivery_indices_for_pair_;
122 };
123 
125  // An optimal solution was found respecting all constraints.
126  OPTIMAL,
127  // An optimal solution was found, however constraints which were relaxed were
128  // violated.
130  // A solution could not be found.
131  INFEASIBLE
132 };
133 
135  public:
137  virtual void Clear() = 0;
138  virtual int CreateNewPositiveVariable() = 0;
139  virtual bool SetVariableBounds(int index, int64 lower_bound,
140  int64 upper_bound) = 0;
141  virtual void SetVariableDisjointBounds(int index,
142  const std::vector<int64>& starts,
143  const std::vector<int64>& ends) = 0;
144  virtual int64 GetVariableLowerBound(int index) const = 0;
145  virtual void SetObjectiveCoefficient(int index, double coefficient) = 0;
146  virtual double GetObjectiveCoefficient(int index) const = 0;
147  virtual void ClearObjective() = 0;
148  virtual int NumVariables() const = 0;
149  virtual int CreateNewConstraint(int64 lower_bound, int64 upper_bound) = 0;
150  virtual void SetCoefficient(int ct, int index, double coefficient) = 0;
151  virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit) = 0;
152  virtual double GetObjectiveValue() const = 0;
153  virtual double GetValue(int index) const = 0;
154 };
155 
157  public:
158  explicit RoutingGlopWrapper(const glop::GlopParameters& parameters) {
159  lp_solver_.SetParameters(parameters);
160  linear_program_.SetMaximizationProblem(false);
161  }
162  void Clear() override {
163  linear_program_.Clear();
164  linear_program_.SetMaximizationProblem(false);
165  allowed_intervals_.clear();
166  }
167  int CreateNewPositiveVariable() override {
168  return linear_program_.CreateNewVariable().value();
169  }
170  bool SetVariableBounds(int index, int64 lower_bound,
171  int64 upper_bound) override {
172  DCHECK_GE(lower_bound, 0);
173  // When variable upper bounds are greater than this threshold, precision
174  // issues arise in GLOP. In this case we are just going to suppose that
175  // these high bound values are infinite and not set the upper bound.
176  const int64 kMaxValue = 1e10;
177  const double lp_min = lower_bound;
178  const double lp_max =
179  (upper_bound > kMaxValue) ? glop::kInfinity : upper_bound;
180  if (lp_min <= lp_max) {
181  linear_program_.SetVariableBounds(glop::ColIndex(index), lp_min, lp_max);
182  return true;
183  }
184  // The linear_program would not be feasible, and it cannot handle the
185  // lp_min > lp_max case, so we must detect infeasibility here.
186  return false;
187  }
188  void SetVariableDisjointBounds(int index, const std::vector<int64>& starts,
189  const std::vector<int64>& ends) override {
190  // TODO(user): Investigate if we can avoid rebuilding the interval list
191  // each time (we could keep a reference to the forbidden interval list in
192  // RoutingDimension but we would need to store cumul offsets and use them
193  // when checking intervals).
194  allowed_intervals_[index] =
195  absl::make_unique<SortedDisjointIntervalList>(starts, ends);
196  }
197  int64 GetVariableLowerBound(int index) const override {
198  return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
199  }
200  void SetObjectiveCoefficient(int index, double coefficient) override {
201  linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
202  }
203  double GetObjectiveCoefficient(int index) const override {
204  return linear_program_.objective_coefficients()[glop::ColIndex(index)];
205  }
206  void ClearObjective() override {
207  for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
208  linear_program_.SetObjectiveCoefficient(i, 0);
209  }
210  }
211  int NumVariables() const override {
212  return linear_program_.num_variables().value();
213  }
214  int CreateNewConstraint(int64 lower_bound, int64 upper_bound) override {
215  const glop::RowIndex ct = linear_program_.CreateNewConstraint();
216  linear_program_.SetConstraintBounds(
217  ct, (lower_bound == kint64min) ? -glop::kInfinity : lower_bound,
218  (upper_bound == kint64max) ? glop::kInfinity : upper_bound);
219  return ct.value();
220  }
221  void SetCoefficient(int ct, int index, double coefficient) override {
222  linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
223  coefficient);
224  }
225  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
226  lp_solver_.GetMutableParameters()->set_max_time_in_seconds(
227  absl::ToDoubleSeconds(duration_limit));
228 
229  // Because we construct the lp one constraint at a time and we never call
230  // SetCoefficient() on the same variable twice for a constraint, we know
231  // that the columns do not contain duplicates and are already ordered by
232  // constraint so we do not need to call linear_program->CleanUp() which can
233  // be costly. Note that the assumptions are DCHECKed() in the call below.
234  linear_program_.NotifyThatColumnsAreClean();
235  VLOG(2) << linear_program_.Dump();
236  const glop::ProblemStatus status = lp_solver_.Solve(linear_program_);
237  if (status != glop::ProblemStatus::OPTIMAL &&
238  status != glop::ProblemStatus::IMPRECISE) {
239  linear_program_.Clear();
241  }
242  for (const auto& allowed_interval : allowed_intervals_) {
243  const double value_double = GetValue(allowed_interval.first);
244  const int64 value = (value_double >= kint64max)
245  ? kint64max
246  : static_cast<int64>(std::round(value_double));
247  const SortedDisjointIntervalList* const interval_list =
248  allowed_interval.second.get();
249  const auto it = interval_list->FirstIntervalGreaterOrEqual(value);
250  if (it == interval_list->end() || value < it->start) {
252  }
253  }
255  }
256  double GetObjectiveValue() const override {
257  return lp_solver_.GetObjectiveValue();
258  }
259  double GetValue(int index) const override {
260  return lp_solver_.variable_values()[glop::ColIndex(index)];
261  }
262 
263  private:
264  glop::LinearProgram linear_program_;
265  glop::LPSolver lp_solver_;
266  absl::flat_hash_map<int, std::unique_ptr<SortedDisjointIntervalList>>
267  allowed_intervals_;
268 };
269 
271  public:
272  RoutingCPSatWrapper() : objective_offset_(0), first_constraint_to_offset_(0) {
273  parameters_.set_num_search_workers(1);
274  // Keeping presolve but with 0 iterations; as of 11/2019 it is
275  // significantly faster than both full presolve and no presolve.
276  parameters_.set_cp_model_presolve(true);
277  parameters_.set_max_presolve_iterations(0);
278  parameters_.set_catch_sigint_signal(false);
279  }
280  ~RoutingCPSatWrapper() override {}
281  void Clear() override {
282  model_.Clear();
283  response_.Clear();
284  objective_coefficients_.clear();
285  objective_offset_ = 0;
286  variable_offset_.clear();
287  constraint_offset_.clear();
288  first_constraint_to_offset_ = 0;
289  }
290  int CreateNewPositiveVariable() override {
291  const int index = model_.variables_size();
292  if (index >= variable_offset_.size()) {
293  variable_offset_.resize(index + 1, 0);
294  }
295  sat::IntegerVariableProto* const variable = model_.add_variables();
296  variable->add_domain(0);
297  variable->add_domain(static_cast<int64>(parameters_.mip_max_bound()));
298  return index;
299  }
300  bool SetVariableBounds(int index, int64 lower_bound,
301  int64 upper_bound) override {
302  DCHECK_GE(lower_bound, 0);
303  variable_offset_[index] = lower_bound;
304  sat::IntegerVariableProto* const variable = model_.mutable_variables(index);
305  variable->set_domain(0, 0);
306  const int64 offset_upper_bound = CapSub(upper_bound, lower_bound);
307  if (offset_upper_bound < 0) return false;
308  if (offset_upper_bound <= parameters_.mip_max_bound()) {
309  variable->set_domain(1, offset_upper_bound);
310  } else {
311  variable->set_domain(1, parameters_.mip_max_bound());
312  }
313  return true;
314  }
315  void SetVariableDisjointBounds(int index, const std::vector<int64>& starts,
316  const std::vector<int64>& ends) override {
317  DCHECK_EQ(starts.size(), ends.size());
318  const int ct = CreateNewConstraint(1, 1);
319  for (int i = 0; i < starts.size(); ++i) {
320  const int variable = CreateNewPositiveVariable();
321  SetVariableBounds(variable, 0, 1);
322  SetCoefficient(ct, variable, 1);
323  const int window_ct = CreateNewConstraint(starts[i], ends[i]);
324  SetCoefficient(window_ct, index, 1);
325  model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
326  }
327  }
328  int64 GetVariableLowerBound(int index) const override {
329  return CapAdd(model_.variables(index).domain(0), variable_offset_[index]);
330  }
331  void SetObjectiveCoefficient(int index, double coefficient) override {
332  // TODO(user): Check variable bounds are never set after setting the
333  // objective coefficient.
334  if (index >= objective_coefficients_.size()) {
335  objective_coefficients_.resize(index + 1, 0);
336  }
337  objective_coefficients_[index] = coefficient;
338  sat::CpObjectiveProto* const objective = model_.mutable_objective();
339  objective->add_vars(index);
340  objective->add_coeffs(coefficient);
341  objective_offset_ += coefficient * variable_offset_[index];
342  }
343  double GetObjectiveCoefficient(int index) const override {
344  return (index < objective_coefficients_.size())
345  ? objective_coefficients_[index]
346  : 0;
347  }
348  void ClearObjective() override {
349  model_.mutable_objective()->Clear();
350  objective_offset_ = 0;
351  }
352  int NumVariables() const override { return model_.variables_size(); }
353  int CreateNewConstraint(int64 lower_bound, int64 upper_bound) override {
354  const int ct_index = model_.constraints_size();
355  if (ct_index >= constraint_offset_.size()) {
356  constraint_offset_.resize(ct_index + 1, 0);
357  }
358  sat::LinearConstraintProto* const ct =
359  model_.add_constraints()->mutable_linear();
360  ct->add_domain(lower_bound);
361  ct->add_domain(upper_bound);
362  return ct_index;
363  }
364  void SetCoefficient(int ct_index, int index, double coefficient) override {
365  // TODO(user): Check variable bounds are never set after setting the
366  // variable coefficient.
367  sat::LinearConstraintProto* const ct =
368  model_.mutable_constraints(ct_index)->mutable_linear();
369  ct->add_vars(index);
370  ct->add_coeffs(coefficient);
371  constraint_offset_[ct_index] =
372  CapAdd(constraint_offset_[ct_index],
373  CapProd(variable_offset_[index], coefficient));
374  }
375  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
376  // Set constraint offsets
377  for (int ct_index = first_constraint_to_offset_;
378  ct_index < constraint_offset_.size(); ++ct_index) {
379  sat::LinearConstraintProto* const ct =
380  model_.mutable_constraints(ct_index)->mutable_linear();
381  ct->set_domain(0, CapSub(ct->domain(0), constraint_offset_[ct_index]));
382  ct->set_domain(1, CapSub(ct->domain(1), constraint_offset_[ct_index]));
383  }
384  first_constraint_to_offset_ = constraint_offset_.size();
385  parameters_.set_max_time_in_seconds(absl::ToDoubleSeconds(duration_limit));
386  VLOG(2) << model_.DebugString();
387  if (hint_.vars_size() == model_.variables_size()) {
388  *model_.mutable_solution_hint() = hint_;
389  }
390  sat::Model model;
391  model.Add(sat::NewSatParameters(parameters_));
392  response_ = sat::SolveCpModel(model_, &model);
393  VLOG(2) << response_.DebugString();
394  if (response_.status() == sat::CpSolverStatus::OPTIMAL ||
395  (response_.status() == sat::CpSolverStatus::FEASIBLE &&
396  !model_.has_objective())) {
397  hint_.Clear();
398  for (int i = 0; i < response_.solution_size(); ++i) {
399  hint_.add_vars(i);
400  hint_.add_values(response_.solution(i));
401  }
403  }
405  }
406  double GetObjectiveValue() const override {
407  return response_.objective_value() + objective_offset_;
408  }
409  double GetValue(int index) const override {
410  return response_.solution(index) + variable_offset_[index];
411  }
412 
413  private:
414  sat::CpModelProto model_;
415  sat::CpSolverResponse response_;
416  sat::SatParameters parameters_;
417  std::vector<double> objective_coefficients_;
418  double objective_offset_;
419  std::vector<int64> variable_offset_;
420  std::vector<int64> constraint_offset_;
421  int first_constraint_to_offset_;
422  sat::PartialVariableAssignment hint_;
423 };
424 
425 // Utility class used in Local/GlobalDimensionCumulOptimizer to set the linear
426 // solver constraints and solve the problem.
428  public:
430  bool use_precedence_propagator)
431  : dimension_(dimension),
432  visited_pickup_delivery_indices_for_pair_(
433  dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1}) {
434  if (use_precedence_propagator) {
435  propagator_ = absl::make_unique<CumulBoundsPropagator>(dimension);
436  }
437  }
438 
439  // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
440  // and "cost" parameters are null, we don't optimize the cost and stop at the
441  // first feasible solution in the linear solver (since in this case only
442  // feasibility is of interest).
444  int vehicle, const std::function<int64(int64)>& next_accessor,
445  RoutingLinearSolverWrapper* solver, std::vector<int64>* cumul_values,
446  int64* cost, int64* transit_cost, bool clear_lp = true);
447 
448  bool Optimize(const std::function<int64(int64)>& next_accessor,
450  std::vector<int64>* cumul_values, int64* cost,
451  int64* transit_cost, bool clear_lp = true);
452 
453  bool OptimizeAndPack(const std::function<int64(int64)>& next_accessor,
455  std::vector<int64>* cumul_values);
456 
458  int vehicle, const std::function<int64(int64)>& next_accessor,
459  RoutingLinearSolverWrapper* solver, std::vector<int64>* cumul_values);
460 
461  const RoutingDimension* dimension() const { return dimension_; }
462 
463  private:
464  // Initializes the containers and given solver. Must be called prior to
465  // setting any contraints and solving.
466  void InitOptimizer(RoutingLinearSolverWrapper* solver);
467 
468  // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
469  // in current_route_[min|max]_cumuls_ respectively.
470  // If the propagator_ is not null, uses the bounds tightened by the
471  // propagator.
472  // Otherwise, the bounds are computed by going over the nodes on the route
473  // using the CP bounds, and the fixed transits are used to tighten them.
474  bool ComputeRouteCumulBounds(const std::vector<int64>& route,
475  const std::vector<int64>& fixed_transits,
476  int64 cumul_offset);
477 
478  // Sets the constraints for all nodes on "vehicle"'s route according to
479  // "next_accessor". If optimize_costs is true, also sets the objective
480  // coefficients for the LP.
481  // Returns false if some infeasibility was detected, true otherwise.
482  bool SetRouteCumulConstraints(
483  int vehicle, const std::function<int64(int64)>& next_accessor,
484  int64 cumul_offset, bool optimize_costs,
485  RoutingLinearSolverWrapper* solver, int64* route_transit_cost,
486  int64* route_cost_offset);
487 
488  // Sets the global constraints on the dimension, and adds global objective
489  // cost coefficients if optimize_costs is true.
490  // NOTE: When called, the call to this function MUST come after
491  // SetRouteCumulConstraints() has been called on all routes, so that
492  // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
493  // initialized.
494  void SetGlobalConstraints(bool optimize_costs,
496 
497  void SetCumulValuesFromLP(const std::vector<int>& cumul_variables,
498  int64 offset, RoutingLinearSolverWrapper* solver,
499  std::vector<int64>* cumul_values);
500 
501  // This function packs the routes of the given vehicles while keeping the cost
502  // of the LP lower than its current (supposed optimal) objective value.
503  // It does so by setting the current objective variables' coefficient to 0 and
504  // setting the coefficient of the route ends to 1, to first minimize the route
505  // ends' cumuls, and then maximizes the starts' cumuls without increasing the
506  // ends.
507  DimensionSchedulingStatus PackRoutes(std::vector<int> vehicles,
509 
510  std::unique_ptr<CumulBoundsPropagator> propagator_;
511  std::vector<int64> current_route_min_cumuls_;
512  std::vector<int64> current_route_max_cumuls_;
513  const RoutingDimension* const dimension_;
514  std::vector<int> current_route_cumul_variables_;
515  std::vector<int> index_to_cumul_variable_;
516  int max_end_cumul_;
517  int min_start_cumul_;
518  std::vector<std::pair<int64, int64>>
519  visited_pickup_delivery_indices_for_pair_;
520 };
521 
522 // Class used to compute optimal values for dimension cumuls of routes,
523 // minimizing cumul soft lower and upper bound costs, and vehicle span costs of
524 // a route.
525 // In its methods, next_accessor is a callback returning the next node of a
526 // given node on a route.
528  public:
531  RoutingSearchParameters::SchedulingSolver solver_type);
532 
533  // If feasible, computes the optimal cost of the route performed by a vehicle,
534  // minimizing cumul soft lower and upper bound costs and vehicle span costs,
535  // and stores it in "optimal_cost" (if not null).
536  // Returns true iff the route respects all constraints.
538  int vehicle, const std::function<int64(int64)>& next_accessor,
539  int64* optimal_cost);
540 
541  // Same as ComputeRouteCumulCost, but the cost computed does not contain
542  // the part of the vehicle span cost due to fixed transits.
544  int vehicle, const std::function<int64(int64)>& next_accessor,
545  int64* optimal_cost_without_transits);
546 
547  // If feasible, computes the optimal cumul values of the route performed by a
548  // vehicle, minimizing cumul soft lower and upper bound costs and vehicle span
549  // costs, stores them in "optimal_cumuls" (if not null), and returns true.
550  // Returns false if the route is not feasible.
552  int vehicle, const std::function<int64(int64)>& next_accessor,
553  std::vector<int64>* optimal_cumuls);
554 
555  // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
556  // the route, such that the cost remains the same, the cumul of route end is
557  // minimized, and then the cumul of the start of the route is maximized.
559  int vehicle, const std::function<int64(int64)>& next_accessor,
560  std::vector<int64>* packed_cumuls);
561 
562  const RoutingDimension* dimension() const {
563  return optimizer_core_.dimension();
564  }
565 
566  private:
567  std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
568  DimensionCumulOptimizerCore optimizer_core_;
569 };
570 
572  public:
574  // If feasible, computes the optimal cost of the entire model with regards to
575  // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
576  // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
577  // (if not null).
578  // Returns true iff all the constraints can be respected.
580  const std::function<int64(int64)>& next_accessor,
581  int64* optimal_cost_without_transits);
582  // If feasible, computes the optimal cumul values, minimizing cumul soft
583  // lower/upper bound costs and vehicle/global span costs, stores them in
584  // "optimal_cumuls" (if not null), and returns true.
585  // Returns false if the routes are not feasible.
586  bool ComputeCumuls(const std::function<int64(int64)>& next_accessor,
587  std::vector<int64>* optimal_cumuls);
588 
589  // Returns true iff the routes resulting from the next_accessor are feasible
590  // wrt the constraints on the optimizer_core_.dimension()'s cumuls.
591  bool IsFeasible(const std::function<int64(int64)>& next_accessor);
592 
593  // Similar to ComputeCumuls, but also tries to pack the cumul values on all
594  // routes, such that the cost remains the same, the cumuls of route ends are
595  // minimized, and then the cumuls of the starts of the routes are maximized.
596  bool ComputePackedCumuls(const std::function<int64(int64)>& next_accessor,
597  std::vector<int64>* packed_cumuls);
598 
599  const RoutingDimension* dimension() const {
600  return optimizer_core_.dimension();
601  }
602 
603  private:
604  std::unique_ptr<RoutingLinearSolverWrapper> solver_;
605  DimensionCumulOptimizerCore optimizer_core_;
606 };
607 
608 } // namespace operations_research
609 
610 #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
virtual int CreateNewPositiveVariable()=0
double GetObjectiveValue() const override
void SetVariableDisjointBounds(int index, const std::vector< int64 > &starts, const std::vector< int64 > &ends) override
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
virtual double GetObjectiveValue() const =0
double GetObjectiveCoefficient(int index) const override
virtual double GetValue(int index) const =0
~RoutingCPSatWrapper() override
bool SetVariableBounds(int index, int64 lower_bound, int64 upper_bound) override
int64 GetVariableLowerBound(int index) const override
const RoutingDimension * dimension() const
@ RELAXED_OPTIMAL_ONLY
void Clear() override
const RoutingDimension & dimension() const
virtual int CreateNewConstraint(int64 lower_bound, int64 upper_bound)=0
void SetCoefficient(int ct_index, int index, double coefficient) override
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
const RoutingDimension * dimension() const
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
DimensionSchedulingStatus
bool ComputeCumulCostWithoutFixedTransits(const std::function< int64(int64)> &next_accessor, int64 *optimal_cost_without_transits)
bool OptimizeAndPack(const std::function< int64(int64)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64 > *cumul_values)
int NumVariables() const override
virtual int64 GetVariableLowerBound(int index) const =0
double GetValue(int index) const override
DimensionSchedulingStatus ComputePackedRouteCumuls(int vehicle, const std::function< int64(int64)> &next_accessor, std::vector< int64 > *packed_cumuls)
RoutingGlopWrapper(const glop::GlopParameters &parameters)
bool IsFeasible(const std::function< int64(int64)> &next_accessor)
int64 CumulMin(int index) const
virtual void SetObjectiveCoefficient(int index, double coefficient)=0
int CreateNewPositiveVariable() override
int CreateNewConstraint(int64 lower_bound, int64 upper_bound) override
void SetObjectiveCoefficient(int index, double coefficient) override
RoutingCPSatWrapper()
int CreateNewConstraint(int64 lower_bound, int64 upper_bound) override
bool SetVariableBounds(int index, int64 lower_bound, int64 upper_bound) override
@ INFEASIBLE
@ OPTIMAL
int NumVariables() const override
void SetVariableDisjointBounds(int index, const std::vector< int64 > &starts, const std::vector< int64 > &ends) override
double GetValue(int index) const override
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2160
bool PropagateCumulBounds(const std::function< int64(int64)> &next_accessor, int64 cumul_offset)
bool ComputeCumuls(const std::function< int64(int64)> &next_accessor, std::vector< int64 > *optimal_cumuls)
virtual void Clear()=0
void SetCoefficient(int ct, int index, double coefficient) override
double GetObjectiveValue() const override
CumulBoundsPropagator(const RoutingDimension *dimension)
double GetObjectiveCoefficient(int index) const override
int CreateNewPositiveVariable() override
void SetObjectiveCoefficient(int index, double coefficient) override
virtual void SetCoefficient(int ct, int index, double coefficient)=0
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(int vehicle, const std::function< int64(int64)> &next_accessor, int64 *optimal_cost_without_transits)
virtual void SetVariableDisjointBounds(int index, const std::vector< int64 > &starts, const std::vector< int64 > &ends)=0
void ClearObjective() override
DimensionSchedulingStatus ComputeRouteCumuls(int vehicle, const std::function< int64(int64)> &next_accessor, std::vector< int64 > *optimal_cumuls)
int64 GetVariableLowerBound(int index) const override
DimensionSchedulingStatus OptimizeSingleRoute(int vehicle, const std::function< int64(int64)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64 > *cumul_values, int64 *cost, int64 *transit_cost, bool clear_lp=true)
int64 CumulMax(int index) const
virtual bool SetVariableBounds(int index, int64 lower_bound, int64 upper_bound)=0
bool Optimize(const std::function< int64(int64)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64 > *cumul_values, int64 *cost, int64 *transit_cost, bool clear_lp=true)
DimensionSchedulingStatus ComputeRouteCumulCost(int vehicle, const std::function< int64(int64)> &next_accessor, int64 *optimal_cost)
virtual int NumVariables() const =0
DimensionSchedulingStatus OptimizeAndPackSingleRoute(int vehicle, const std::function< int64(int64)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64 > *cumul_values)
LocalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
virtual double GetObjectiveCoefficient(int index) const =0
virtual ~RoutingLinearSolverWrapper()
const RoutingDimension * dimension() const
void ClearObjective() override
bool ComputePackedCumuls(const std::function< int64(int64)> &next_accessor, std::vector< int64 > *packed_cumuls)
virtual void ClearObjective()=0
void Clear() override
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension)