C++ Reference

C++ Reference: Routing

routing_lp_scheduling.h
Go to the documentation of this file.
1 // Copyright 2010-2021 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 <algorithm>
18 #include <cstdint>
19 #include <deque>
20 #include <functional>
21 #include <limits>
22 #include <map>
23 #include <memory>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "absl/container/flat_hash_map.h"
29 #include "absl/memory/memory.h"
30 #include "absl/time/time.h"
31 #include "ortools/base/logging.h"
32 #include "ortools/base/mathutil.h"
35 #include "ortools/glop/lp_solver.h"
36 #include "ortools/glop/parameters.pb.h"
37 #include "ortools/lp_data/lp_data.h"
38 #include "ortools/lp_data/lp_types.h"
39 #include "ortools/sat/cp_model.pb.h"
40 #include "ortools/sat/cp_model_solver.h"
41 #include "ortools/sat/lp_utils.h"
42 #include "ortools/sat/model.h"
43 #include "ortools/sat/sat_parameters.pb.h"
44 #include "ortools/util/saturated_arithmetic.h"
45 #include "ortools/util/sorted_interval_list.h"
46 
47 namespace operations_research {
48 
49 // Classes to solve dimension cumul placement (aka scheduling) problems using
50 // linear programming.
51 
52 // Utility class used in the core optimizer to tighten the cumul bounds as much
53 // as possible based on the model precedences.
55  public:
57 
58  // Tightens the cumul bounds starting from the current cumul var min/max,
59  // and propagating the precedences resulting from the next_accessor, and the
60  // dimension's precedence rules.
61  // Returns false iff the precedences are infeasible with the given routes.
62  // Otherwise, the user can call CumulMin() and CumulMax() to retrieve the new
63  // bounds of an index.
65  const std::function<int64_t(int64_t)>& next_accessor,
66  int64_t cumul_offset);
67 
68  int64_t CumulMin(int index) const {
69  return propagated_bounds_[PositiveNode(index)];
70  }
71 
72  int64_t CumulMax(int index) const {
73  const int64_t negated_upper_bound = propagated_bounds_[NegativeNode(index)];
74  return negated_upper_bound == std::numeric_limits<int64_t>::min()
75  ? std::numeric_limits<int64_t>::max()
76  : -negated_upper_bound;
77  }
78 
79  const RoutingDimension& dimension() const { return dimension_; }
80 
81  private:
82  // An arc "tail --offset--> head" represents the relation
83  // tail + offset <= head.
84  // As arcs are stored by tail, we don't store it in the struct.
85  struct ArcInfo {
86  int head;
87  int64_t offset;
88  };
89  static const int kNoParent;
90  static const int kParentToBePropagated;
91 
92  // Return the node corresponding to the lower bound of the cumul of index and
93  // -index respectively.
94  int PositiveNode(int index) const { return 2 * index; }
95  int NegativeNode(int index) const { return 2 * index + 1; }
96 
97  void AddNodeToQueue(int node) {
98  if (!node_in_queue_[node]) {
99  bf_queue_.push_back(node);
100  node_in_queue_[node] = true;
101  }
102  }
103 
104  // Adds the relation first_index + offset <= second_index, by adding arcs
105  // first_index --offset--> second_index and
106  // -second_index --offset--> -first_index.
107  void AddArcs(int first_index, int second_index, int64_t offset);
108 
109  bool InitializeArcsAndBounds(
110  const std::function<int64_t(int64_t)>& next_accessor,
111  int64_t cumul_offset);
112 
113  bool UpdateCurrentLowerBoundOfNode(int node, int64_t new_lb, int64_t offset);
114 
115  bool DisassembleSubtree(int source, int target);
116 
117  bool CleanupAndReturnFalse() {
118  // We clean-up node_in_queue_ for future calls, and return false.
119  for (int node_to_cleanup : bf_queue_) {
120  node_in_queue_[node_to_cleanup] = false;
121  }
122  bf_queue_.clear();
123  return false;
124  }
125 
126  const RoutingDimension& dimension_;
127  const int64_t num_nodes_;
128 
129  // TODO(user): Investigate if all arcs for a given tail can be created
130  // at the same time, in which case outgoing_arcs_ could point to an absl::Span
131  // for each tail index.
132  std::vector<std::vector<ArcInfo>> outgoing_arcs_;
133 
134  std::deque<int> bf_queue_;
135  std::vector<bool> node_in_queue_;
136  std::vector<int> tree_parent_node_of_;
137  // After calling PropagateCumulBounds(), for each node index n,
138  // propagated_bounds_[2*n] and -propagated_bounds_[2*n+1] respectively contain
139  // the propagated lower and upper bounds of n's cumul variable.
140  std::vector<int64_t> propagated_bounds_;
141 
142  // Vector used in DisassembleSubtree() to avoid memory reallocation.
143  std::vector<int> tmp_dfs_stack_;
144 
145  // Used to store the pickup/delivery pairs encountered on the routes.
146  std::vector<std::pair<int64_t, int64_t>>
147  visited_pickup_delivery_indices_for_pair_;
148 };
149 
151  // An optimal solution was found respecting all constraints.
152  OPTIMAL,
153  // An optimal solution was found, however constraints which were relaxed were
154  // violated.
156  // A solution could not be found.
157  INFEASIBLE
158 };
159 
161  public:
163  virtual void Clear() = 0;
164  virtual int CreateNewPositiveVariable() = 0;
165  virtual bool SetVariableBounds(int index, int64_t lower_bound,
166  int64_t upper_bound) = 0;
167  virtual void SetVariableDisjointBounds(int index,
168  const std::vector<int64_t>& starts,
169  const std::vector<int64_t>& ends) = 0;
170  virtual int64_t GetVariableLowerBound(int index) const = 0;
171  virtual int64_t GetVariableUpperBound(int index) const = 0;
172  virtual void SetObjectiveCoefficient(int index, double coefficient) = 0;
173  virtual double GetObjectiveCoefficient(int index) const = 0;
174  virtual void ClearObjective() = 0;
175  virtual int NumVariables() const = 0;
176  virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) = 0;
177  virtual void SetCoefficient(int ct, int index, double coefficient) = 0;
178  virtual bool IsCPSATSolver() = 0;
179  virtual void AddObjectiveConstraint() = 0;
180  virtual void AddMaximumConstraint(int max_var, std::vector<int> vars) = 0;
181  virtual void AddProductConstraint(int product_var, std::vector<int> vars) = 0;
182  virtual void SetEnforcementLiteral(int ct, int condition) = 0;
183  virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit) = 0;
184  virtual int64_t GetObjectiveValue() const = 0;
185  virtual double GetValue(int index) const = 0;
186  virtual bool SolutionIsInteger() const = 0;
187 
188  // Adds a variable with bounds [lower_bound, upper_bound].
189  int AddVariable(int64_t lower_bound, int64_t upper_bound) {
190  CHECK_LE(lower_bound, upper_bound);
191  const int variable = CreateNewPositiveVariable();
192  SetVariableBounds(variable, lower_bound, upper_bound);
193  return variable;
194  }
195  // Adds a linear constraint, enforcing
196  // lower_bound <= sum variable * coeff <= upper_bound,
197  // and returns the identifier of that constraint.
199  int64_t lower_bound, int64_t upper_bound,
200  const std::vector<std::pair<int, double>>& variable_coeffs) {
201  CHECK_LE(lower_bound, upper_bound);
202  const int ct = CreateNewConstraint(lower_bound, upper_bound);
203  for (const auto& variable_coeff : variable_coeffs) {
204  SetCoefficient(ct, variable_coeff.first, variable_coeff.second);
205  }
206  return ct;
207  }
208  // Adds a linear constraint and a 0/1 variable that is true iff
209  // lower_bound <= sum variable * coeff <= upper_bound,
210  // and returns the identifier of that variable.
212  int64_t lower_bound, int64_t upper_bound,
213  const std::vector<std::pair<int, double>>& weighted_variables) {
214  const int reification_ct = AddLinearConstraint(1, 1, {});
215  if (std::numeric_limits<int64_t>::min() < lower_bound) {
216  const int under_lower_bound = AddVariable(0, 1);
217  SetCoefficient(reification_ct, under_lower_bound, 1);
218  const int under_lower_bound_ct =
219  AddLinearConstraint(std::numeric_limits<int64_t>::min(),
220  lower_bound - 1, weighted_variables);
221  SetEnforcementLiteral(under_lower_bound_ct, under_lower_bound);
222  }
223  if (upper_bound < std::numeric_limits<int64_t>::max()) {
224  const int above_upper_bound = AddVariable(0, 1);
225  SetCoefficient(reification_ct, above_upper_bound, 1);
226  const int above_upper_bound_ct = AddLinearConstraint(
227  upper_bound + 1, std::numeric_limits<int64_t>::max(),
228  weighted_variables);
229  SetEnforcementLiteral(above_upper_bound_ct, above_upper_bound);
230  }
231  const int within_bounds = AddVariable(0, 1);
232  SetCoefficient(reification_ct, within_bounds, 1);
233  const int within_bounds_ct =
234  AddLinearConstraint(lower_bound, upper_bound, weighted_variables);
235  SetEnforcementLiteral(within_bounds_ct, within_bounds);
236  return within_bounds;
237  }
238 };
239 
241  public:
242  RoutingGlopWrapper(bool is_relaxation, const glop::GlopParameters& parameters)
243  : is_relaxation_(is_relaxation) {
244  lp_solver_.SetParameters(parameters);
245  linear_program_.SetMaximizationProblem(false);
246  }
247  void Clear() override {
248  linear_program_.Clear();
249  linear_program_.SetMaximizationProblem(false);
250  allowed_intervals_.clear();
251  }
252  int CreateNewPositiveVariable() override {
253  return linear_program_.CreateNewVariable().value();
254  }
255  bool SetVariableBounds(int index, int64_t lower_bound,
256  int64_t upper_bound) override {
257  DCHECK_GE(lower_bound, 0);
258  // When variable upper bounds are greater than this threshold, precision
259  // issues arise in GLOP. In this case we are just going to suppose that
260  // these high bound values are infinite and not set the upper bound.
261  const int64_t kMaxValue = 1e10;
262  const double lp_min = lower_bound;
263  const double lp_max =
264  (upper_bound > kMaxValue) ? glop::kInfinity : upper_bound;
265  if (lp_min <= lp_max) {
266  linear_program_.SetVariableBounds(glop::ColIndex(index), lp_min, lp_max);
267  return true;
268  }
269  // The linear_program would not be feasible, and it cannot handle the
270  // lp_min > lp_max case, so we must detect infeasibility here.
271  return false;
272  }
273  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
274  const std::vector<int64_t>& ends) override {
275  // TODO(user): Investigate if we can avoid rebuilding the interval list
276  // each time (we could keep a reference to the forbidden interval list in
277  // RoutingDimension but we would need to store cumul offsets and use them
278  // when checking intervals).
279  allowed_intervals_[index] =
280  absl::make_unique<SortedDisjointIntervalList>(starts, ends);
281  }
282  int64_t GetVariableLowerBound(int index) const override {
283  return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
284  }
285  int64_t GetVariableUpperBound(int index) const override {
286  const double upper_bound =
287  linear_program_.variable_upper_bounds()[glop::ColIndex(index)];
288  DCHECK_GE(upper_bound, 0);
289  return upper_bound == glop::kInfinity ? std::numeric_limits<int64_t>::max()
290  : static_cast<int64_t>(upper_bound);
291  }
292  void SetObjectiveCoefficient(int index, double coefficient) override {
293  linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
294  }
295  double GetObjectiveCoefficient(int index) const override {
296  return linear_program_.objective_coefficients()[glop::ColIndex(index)];
297  }
298  void ClearObjective() override {
299  for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
300  linear_program_.SetObjectiveCoefficient(i, 0);
301  }
302  }
303  int NumVariables() const override {
304  return linear_program_.num_variables().value();
305  }
306  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
307  const glop::RowIndex ct = linear_program_.CreateNewConstraint();
308  linear_program_.SetConstraintBounds(
309  ct,
310  (lower_bound == std::numeric_limits<int64_t>::min()) ? -glop::kInfinity
311  : lower_bound,
312  (upper_bound == std::numeric_limits<int64_t>::max()) ? glop::kInfinity
313  : upper_bound);
314  return ct.value();
315  }
316  void SetCoefficient(int ct, int index, double coefficient) override {
317  linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
318  coefficient);
319  }
320  bool IsCPSATSolver() override { return false; }
321  void AddObjectiveConstraint() override {
322  const int ct = CreateNewConstraint(0, GetObjectiveValue());
323  for (int variable = 0; variable < NumVariables(); variable++) {
324  const double coefficient = GetObjectiveCoefficient(variable);
325  if (coefficient != 0) {
326  SetCoefficient(ct, variable, coefficient);
327  }
328  }
329  }
330  void AddMaximumConstraint(int max_var, std::vector<int> vars) override {}
331  void AddProductConstraint(int product_var, std::vector<int> vars) override {}
332  void SetEnforcementLiteral(int ct, int condition) override{};
333  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
334  lp_solver_.GetMutableParameters()->set_max_time_in_seconds(
335  absl::ToDoubleSeconds(duration_limit));
336 
337  // Because we construct the lp one constraint at a time and we never call
338  // SetCoefficient() on the same variable twice for a constraint, we know
339  // that the columns do not contain duplicates and are already ordered by
340  // constraint so we do not need to call linear_program->CleanUp() which can
341  // be costly. Note that the assumptions are DCHECKed() in the call below.
342  linear_program_.NotifyThatColumnsAreClean();
343  VLOG(2) << linear_program_.Dump();
344  const glop::ProblemStatus status = lp_solver_.Solve(linear_program_);
345  if (status != glop::ProblemStatus::OPTIMAL &&
346  status != glop::ProblemStatus::IMPRECISE) {
348  }
349  if (is_relaxation_) {
351  }
352  for (const auto& allowed_interval : allowed_intervals_) {
353  const double value_double = GetValue(allowed_interval.first);
354  const int64_t value =
355  (value_double >= std::numeric_limits<int64_t>::max())
356  ? std::numeric_limits<int64_t>::max()
357  : MathUtil::FastInt64Round(value_double);
358  const SortedDisjointIntervalList* const interval_list =
359  allowed_interval.second.get();
360  const auto it = interval_list->FirstIntervalGreaterOrEqual(value);
361  if (it == interval_list->end() || value < it->start) {
363  }
364  }
366  }
367  int64_t GetObjectiveValue() const override {
368  return MathUtil::FastInt64Round(lp_solver_.GetObjectiveValue());
369  }
370  double GetValue(int index) const override {
371  return lp_solver_.variable_values()[glop::ColIndex(index)];
372  }
373  bool SolutionIsInteger() const override {
374  return linear_program_.SolutionIsInteger(lp_solver_.variable_values(),
375  /*absolute_tolerance*/ 1e-3);
376  }
377 
378  private:
379  const bool is_relaxation_;
380  glop::LinearProgram linear_program_;
381  glop::LPSolver lp_solver_;
382  absl::flat_hash_map<int, std::unique_ptr<SortedDisjointIntervalList>>
383  allowed_intervals_;
384 };
385 
387  public:
389  parameters_.set_num_search_workers(1);
390  // Keeping presolve but with 0 iterations; as of 11/2019 it is
391  // significantly faster than both full presolve and no presolve.
392  parameters_.set_cp_model_presolve(true);
393  parameters_.set_max_presolve_iterations(0);
394  parameters_.set_catch_sigint_signal(false);
395  parameters_.set_mip_max_bound(1e8);
396  parameters_.set_search_branching(sat::SatParameters::LP_SEARCH);
397  }
398  ~RoutingCPSatWrapper() override {}
399  void Clear() override {
400  model_.Clear();
401  response_.Clear();
402  objective_coefficients_.clear();
403  }
404  int CreateNewPositiveVariable() override {
405  const int index = model_.variables_size();
406  sat::IntegerVariableProto* const variable = model_.add_variables();
407  variable->add_domain(0);
408  variable->add_domain(static_cast<int64_t>(parameters_.mip_max_bound()));
409  return index;
410  }
411  bool SetVariableBounds(int index, int64_t lower_bound,
412  int64_t upper_bound) override {
413  DCHECK_GE(lower_bound, 0);
414  const int64_t capped_upper_bound =
415  std::min<int64_t>(upper_bound, parameters_.mip_max_bound());
416  if (lower_bound > capped_upper_bound) return false;
417  sat::IntegerVariableProto* const variable = model_.mutable_variables(index);
418  variable->set_domain(0, lower_bound);
419  variable->set_domain(1, capped_upper_bound);
420  return true;
421  }
422  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
423  const std::vector<int64_t>& ends) override {
424  DCHECK_EQ(starts.size(), ends.size());
425  const int ct = CreateNewConstraint(1, 1);
426  for (int i = 0; i < starts.size(); ++i) {
427  const int variable = CreateNewPositiveVariable();
428  SetVariableBounds(variable, 0, 1);
429  SetCoefficient(ct, variable, 1);
430  const int window_ct = CreateNewConstraint(starts[i], ends[i]);
431  SetCoefficient(window_ct, index, 1);
432  model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
433  }
434  }
435  int64_t GetVariableLowerBound(int index) const override {
436  return model_.variables(index).domain(0);
437  }
438  int64_t GetVariableUpperBound(int index) const override {
439  const auto& domain = model_.variables(index).domain();
440  return domain[domain.size() - 1];
441  }
442  void SetObjectiveCoefficient(int index, double coefficient) override {
443  if (index >= objective_coefficients_.size()) {
444  objective_coefficients_.resize(index + 1, 0);
445  }
446  objective_coefficients_[index] = coefficient;
447  sat::FloatObjectiveProto* const objective =
448  model_.mutable_floating_point_objective();
449  objective->add_vars(index);
450  objective->add_coeffs(coefficient);
451  }
452  double GetObjectiveCoefficient(int index) const override {
453  return (index < objective_coefficients_.size())
454  ? objective_coefficients_[index]
455  : 0;
456  }
457  void ClearObjective() override {
458  model_.mutable_floating_point_objective()->Clear();
459  }
460  int NumVariables() const override { return model_.variables_size(); }
461  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
462  sat::LinearConstraintProto* const ct =
463  model_.add_constraints()->mutable_linear();
464  ct->add_domain(lower_bound);
465  ct->add_domain(upper_bound);
466  return model_.constraints_size() - 1;
467  }
468  void SetCoefficient(int ct_index, int index, double coefficient) override {
469  sat::LinearConstraintProto* const ct =
470  model_.mutable_constraints(ct_index)->mutable_linear();
471  ct->add_vars(index);
472  const int64_t integer_coefficient = coefficient;
473  ct->add_coeffs(integer_coefficient);
474  }
475  bool IsCPSATSolver() override { return true; }
476  void AddObjectiveConstraint() override {
477  // Scale the objective to get its integer representation.
478  const sat::FloatObjectiveProto& fp_objective =
479  model_.floating_point_objective();
480  std::vector<std::pair<int, double>> terms;
481  for (int i = 0; i < fp_objective.vars_size(); ++i) {
482  terms.push_back({fp_objective.vars(i), fp_objective.coeffs(i)});
483  }
484  SolverLogger logger;
485  const bool status =
486  sat::ScaleAndSetObjective(parameters_, terms, fp_objective.offset(),
487  fp_objective.maximize(), &model_, &logger);
488  DCHECK(status);
489  const sat::CpObjectiveProto& objective = model_.objective();
490  int64_t activity = 0;
491  for (int i = 0; i < objective.vars_size(); ++i) {
492  activity += response_.solution(objective.vars(i)) * objective.coeffs(i);
493  }
494  const int ct = CreateNewConstraint(0, activity);
495  for (int i = 0; i < objective.vars_size(); ++i) {
496  SetCoefficient(ct, objective.vars(i), objective.coeffs(i));
497  }
498  model_.clear_objective();
499  }
500  void AddMaximumConstraint(int max_var, std::vector<int> vars) override {
501  sat::LinearArgumentProto* const ct =
502  model_.add_constraints()->mutable_lin_max();
503  ct->mutable_target()->add_vars(max_var);
504  ct->mutable_target()->add_coeffs(1);
505  for (const int var : vars) {
506  sat::LinearExpressionProto* const expr = ct->add_exprs();
507  expr->add_vars(var);
508  expr->add_coeffs(1);
509  }
510  }
511  void AddProductConstraint(int product_var, std::vector<int> vars) override {
512  sat::LinearArgumentProto* const ct =
513  model_.add_constraints()->mutable_int_prod();
514  ct->mutable_target()->add_vars(product_var);
515  ct->mutable_target()->add_coeffs(1);
516  for (const int var : vars) {
517  sat::LinearExpressionProto* expr = ct->add_exprs();
518  expr->add_vars(var);
519  expr->add_coeffs(1);
520  }
521  }
522  void SetEnforcementLiteral(int ct, int condition) override {
523  DCHECK_LT(ct, model_.constraints_size());
524  model_.mutable_constraints(ct)->add_enforcement_literal(condition);
525  }
526  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
527  parameters_.set_max_time_in_seconds(absl::ToDoubleSeconds(duration_limit));
528  VLOG(2) << model_.DebugString();
529  if (hint_.vars_size() == model_.variables_size()) {
530  *model_.mutable_solution_hint() = hint_;
531  }
532  sat::Model model;
533  model.Add(sat::NewSatParameters(parameters_));
534  response_ = sat::SolveCpModel(model_, &model);
535  VLOG(2) << response_.DebugString();
536  if (response_.status() == sat::CpSolverStatus::OPTIMAL ||
537  (response_.status() == sat::CpSolverStatus::FEASIBLE &&
538  !model_.has_floating_point_objective())) {
539  hint_.Clear();
540  for (int i = 0; i < response_.solution_size(); ++i) {
541  hint_.add_vars(i);
542  hint_.add_values(response_.solution(i));
543  }
545  }
547  }
548  int64_t GetObjectiveValue() const override {
549  return MathUtil::FastInt64Round(response_.objective_value());
550  }
551  double GetValue(int index) const override {
552  return response_.solution(index);
553  }
554  bool SolutionIsInteger() const override { return true; }
555 
556  private:
557  sat::CpModelProto model_;
558  sat::CpSolverResponse response_;
559  sat::SatParameters parameters_;
560  std::vector<double> objective_coefficients_;
561  sat::PartialVariableAssignment hint_;
562 };
563 
564 // Utility class used in Local/GlobalDimensionCumulOptimizer to set the linear
565 // solver constraints and solve the problem.
567  public:
569  bool use_precedence_propagator);
570 
571  // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
572  // and "cost" parameters are null, we don't optimize the cost and stop at the
573  // first feasible solution in the linear solver (since in this case only
574  // feasibility is of interest).
576  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
577  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
578  std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
579  bool clear_lp = true);
580 
581  std::vector<DimensionSchedulingStatus> OptimizeSingleRouteWithResources(
582  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
583  const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
584  const std::vector<int>& resource_indices, bool optimize_vehicle_costs,
586  std::vector<int64_t>* costs_without_transits,
587  std::vector<std::vector<int64_t>>* cumul_values,
588  std::vector<std::vector<int64_t>>* break_values, bool clear_lp = true);
589 
591  const std::function<int64_t(int64_t)>& next_accessor,
592  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
593  std::vector<int64_t>* break_values,
594  std::vector<std::vector<int>>* resource_indices_per_group, int64_t* cost,
595  int64_t* transit_cost, bool clear_lp = true);
596 
598  const std::function<int64_t(int64_t)>& next_accessor,
599  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
600  std::vector<int64_t>* break_values,
601  std::vector<std::vector<int>>* resource_indices_per_group);
602 
604  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
606  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
607  std::vector<int64_t>* break_values);
608 
609  const RoutingDimension* dimension() const { return dimension_; }
610 
611  private:
612  // Initializes the containers and given solver. Must be called prior to
613  // setting any contraints and solving.
614  void InitOptimizer(RoutingLinearSolverWrapper* solver);
615 
616  // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
617  // in current_route_[min|max]_cumuls_ respectively.
618  // If the propagator_ is not null, uses the bounds tightened by the
619  // propagator.
620  // Otherwise, the bounds are computed by going over the nodes on the route
621  // using the CP bounds, and the fixed transits are used to tighten them.
622  bool ComputeRouteCumulBounds(const std::vector<int64_t>& route,
623  const std::vector<int64_t>& fixed_transits,
624  int64_t cumul_offset);
625 
626  // Sets the constraints for all nodes on "vehicle"'s route according to
627  // "next_accessor". If optimize_costs is true, also sets the objective
628  // coefficients for the LP.
629  // Returns false if some infeasibility was detected, true otherwise.
630  bool SetRouteCumulConstraints(
631  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
632  int64_t cumul_offset, bool optimize_costs,
633  RoutingLinearSolverWrapper* solver, int64_t* route_transit_cost,
634  int64_t* route_cost_offset);
635 
636  // Sets the global constraints on the dimension, and adds global objective
637  // cost coefficients if optimize_costs is true.
638  // NOTE: When called, the call to this function MUST come after
639  // SetRouteCumulConstraints() has been called on all routes, so that
640  // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
641  // initialized.
642  // Returns false if some infeasibility was detected, true otherwise.
643  bool SetGlobalConstraints(
644  const std::function<int64_t(int64_t)>& next_accessor,
645  int64_t cumul_offset, bool optimize_costs,
647 
648  void SetValuesFromLP(const std::vector<int>& lp_variables, int64_t offset,
650  std::vector<int64_t>* lp_values) const;
651 
652  void SetResourceIndices(
654  std::vector<std::vector<int>>* resource_indices_per_group) const;
655 
656  // This function packs the routes of the given vehicles while keeping the cost
657  // of the LP lower than its current (supposed optimal) objective value.
658  // It does so by setting the current objective variables' coefficient to 0 and
659  // setting the coefficient of the route ends to 1, to first minimize the route
660  // ends' cumuls, and then maximizes the starts' cumuls without increasing the
661  // ends.
662  DimensionSchedulingStatus PackRoutes(std::vector<int> vehicles,
664 
665  std::unique_ptr<CumulBoundsPropagator> propagator_;
666  std::vector<int64_t> current_route_min_cumuls_;
667  std::vector<int64_t> current_route_max_cumuls_;
668  const RoutingDimension* const dimension_;
669  // Scheduler variables for current route cumuls and for all nodes cumuls.
670  std::vector<int> current_route_cumul_variables_;
671  std::vector<int> index_to_cumul_variable_;
672  // Scheduler variables for current route breaks and all vehicle breaks.
673  // There are two variables for each break: start and end.
674  // current_route_break_variables_ has variables corresponding to
675  // break[0] start, break[0] end, break[1] start, break[1] end, etc.
676  std::vector<int> current_route_break_variables_;
677  // Vector all_break_variables contains the break variables of all vehicles,
678  // in the same format as current_route_break_variables.
679  // It is the concatenation of break variables of vehicles in [0, #vehicles).
680  std::vector<int> all_break_variables_;
681  // Allows to retrieve break variables of a given vehicle: those go from
682  // all_break_variables_[vehicle_to_all_break_variables_offset_[vehicle]] to
683  // all_break_variables[vehicle_to_all_break_variables_offset_[vehicle+1]-1].
684  std::vector<int> vehicle_to_all_break_variables_offset_;
685  // The following vector contains indices of resource-to-vehicle assignment
686  // variables. For every resource group, stores indices of
687  // num_resources*num_vehicles boolean variables indicating whether resource #r
688  // is assigned to vehicle #v.
689  std::vector<std::vector<int>>
690  resource_group_to_resource_to_vehicle_assignment_variables_;
691 
692  int max_end_cumul_;
693  int min_start_cumul_;
694  std::vector<std::pair<int64_t, int64_t>>
695  visited_pickup_delivery_indices_for_pair_;
696 };
697 
698 // Class used to compute optimal values for dimension cumuls of routes,
699 // minimizing cumul soft lower and upper bound costs, and vehicle span costs of
700 // a route.
701 // In its methods, next_accessor is a callback returning the next node of a
702 // given node on a route.
704  public:
708 
709  // If feasible, computes the optimal cost of the route performed by a vehicle,
710  // minimizing cumul soft lower and upper bound costs and vehicle span costs,
711  // and stores it in "optimal_cost" (if not null).
712  // Returns true iff the route respects all constraints.
714  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
715  int64_t* optimal_cost);
716 
717  // Same as ComputeRouteCumulCost, but the cost computed does not contain
718  // the part of the vehicle span cost due to fixed transits.
720  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
721  int64_t* optimal_cost_without_transits);
722 
723  std::vector<DimensionSchedulingStatus>
725  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
726  const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
727  const std::vector<int>& resource_indices, bool optimize_vehicle_costs,
728  std::vector<int64_t>* optimal_costs_without_transits,
729  std::vector<std::vector<int64_t>>* optimal_cumuls,
730  std::vector<std::vector<int64_t>>* optimal_breaks);
731 
732  // If feasible, computes the optimal values for cumul and break variables
733  // of the route performed by a vehicle, minimizing cumul soft lower, upper
734  // bound costs and vehicle span costs, stores them in "optimal_cumuls"
735  // (if not null), and optimal_breaks, and returns true.
736  // Returns false if the route is not feasible.
738  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
739  std::vector<int64_t>* optimal_cumuls,
740  std::vector<int64_t>* optimal_breaks);
741 
742  // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
743  // the route, such that the cost remains the same, the cumul of route end is
744  // minimized, and then the cumul of the start of the route is maximized.
745  // If 'resource' is non-null, the packed route must also respect its start/end
746  // time window.
748  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
750  std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
751 
752  const RoutingDimension* dimension() const {
753  return optimizer_core_.dimension();
754  }
755 
756  private:
757  std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
758  DimensionCumulOptimizerCore optimizer_core_;
759 };
760 
762  public:
766  // If feasible, computes the optimal cost of the entire model with regards to
767  // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
768  // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
769  // (if not null).
770  // Returns true iff all the constraints can be respected.
772  const std::function<int64_t(int64_t)>& next_accessor,
773  int64_t* optimal_cost_without_transits);
774  // If feasible, computes the optimal values for cumul, break and resource
775  // variables, minimizing cumul soft lower/upper bound costs and vehicle/global
776  // span costs, stores them in "optimal_cumuls" (if not null), "optimal_breaks"
777  // and "optimal_resource_indices_per_group", and returns true.
778  // Returns false if the routes are not feasible.
780  const std::function<int64_t(int64_t)>& next_accessor,
781  std::vector<int64_t>* optimal_cumuls,
782  std::vector<int64_t>* optimal_breaks,
783  std::vector<std::vector<int>>* optimal_resource_indices_per_group);
784 
785  // Similar to ComputeCumuls, but also tries to pack the cumul values on all
786  // routes, such that the cost remains the same, the cumuls of route ends are
787  // minimized, and then the cumuls of the starts of the routes are maximized.
789  const std::function<int64_t(int64_t)>& next_accessor,
790  std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks,
791  std::vector<std::vector<int>>* resource_indices_per_group);
792 
793  const RoutingDimension* dimension() const {
794  return optimizer_core_.dimension();
795  }
796 
797  private:
798  std::unique_ptr<RoutingLinearSolverWrapper> solver_;
799  DimensionCumulOptimizerCore optimizer_core_;
800 };
801 
803  public:
805  LocalDimensionCumulOptimizer* optimizer,
806  LocalDimensionCumulOptimizer* mp_optimizer);
807 
808  // Returns the cost resulting from the min-cost assignment of resources to
809  // (used) vehicles, or -1 if the assignment is impossible.
810  // For each vehicle v and resource r, the cost of assigning r to v is equal to
811  // - primary_vehicle_to_resource_assignment_costs[v][r] if
812  // primary_vehicle_to_resource_assignment_costs[v] is not empty,
813  // - secondary_vehicle_to_resource_assignment_costs[v][r] otherwise
814  // (secondary_vehicle_to_resource_assignment_costs[v] can never be empty).
815  // If non-null, 'resource_indices' contains the index of the resource assigned
816  // to each vehicle.
818  const std::vector<std::vector<int64_t>>&
819  primary_vehicle_to_resource_assignment_costs,
820  const std::vector<std::vector<int64_t>>&
821  secondary_vehicle_to_resource_assignment_costs,
822  const std::function<bool(int)>& use_primary_for_vehicle,
823  std::vector<int>* resource_indices) const;
824 
825  // Computes the vehicle-to-resource assignment costs for the given vehicle to
826  // all resources in the group, and sets these costs in assignment_costs (if
827  // non-null).
828  // optimize_vehicle_costs indicates if the costs should be optimized or if
829  // we merely care about feasibility (cost of 0) and infeasibility (cost of -1)
830  // of the assignments.
831  // The cumul and break values corresponding to the assignment of each resource
832  // are also set in cumul_values and break_values, if non-null.
834  int v, const std::function<int64_t(int64_t)>& next_accessor,
835  bool optimize_vehicle_costs, std::vector<int64_t>* assignment_costs,
836  std::vector<std::vector<int64_t>>* cumul_values,
837  std::vector<std::vector<int64_t>>* break_values);
838 
839  const RoutingDimension* const dimension() const {
840  return optimizer_.dimension();
841  }
842 
843  private:
844  LocalDimensionCumulOptimizer& optimizer_;
845  LocalDimensionCumulOptimizer& mp_optimizer_;
846  const RoutingModel& model_;
847  const RoutingModel::ResourceGroup& resource_group_;
848 };
849 
850 } // namespace operations_research
851 
852 #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
int64_t GetVariableUpperBound(int index) const override
int AddLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double >> &variable_coeffs)
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
DimensionSchedulingStatus OptimizeAndPackSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::ResourceGroup::Resource *resource, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
virtual double GetObjectiveCoefficient(int index) const =0
void SetCoefficient(int ct, int index, double coefficient) override
DimensionSchedulingStatus ComputeCumuls(const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, std::vector< std::vector< int >> *optimal_resource_indices_per_group)
virtual double GetValue(int index) const =0
virtual bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound)=0
void SetCoefficient(int ct_index, int index, double coefficient) override
int AddReifiedLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double >> &weighted_variables)
virtual void SetObjectiveCoefficient(int index, double coefficient)=0
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
virtual int64_t GetVariableUpperBound(int index) const =0
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2562
std::vector< DimensionSchedulingStatus > OptimizeSingleRouteWithResources(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::ResourceGroup::Resource > &resources, const std::vector< int > &resource_indices, bool optimize_vehicle_costs, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *costs_without_transits, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values, bool clear_lp=true)
double GetValue(int index) const override
virtual void AddProductConstraint(int product_var, std::vector< int > vars)=0
virtual void SetCoefficient(int ct, int index, double coefficient)=0
int64_t GetVariableLowerBound(int index) const override
const RoutingDimension & dimension() const
int64_t ComputeBestAssignmentCost(const std::vector< std::vector< int64_t >> &primary_vehicle_to_resource_assignment_costs, const std::vector< std::vector< int64_t >> &secondary_vehicle_to_resource_assignment_costs, const std::function< bool(int)> &use_primary_for_vehicle, std::vector< int > *resource_indices) const
ResourceAssignmentOptimizer(const RoutingModel::ResourceGroup *resource_group, LocalDimensionCumulOptimizer *optimizer, LocalDimensionCumulOptimizer *mp_optimizer)
virtual void AddMaximumConstraint(int max_var, std::vector< int > vars)=0
DimensionSchedulingStatus ComputeCumulCostWithoutFixedTransits(const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
virtual int64_t GetObjectiveValue() const =0
const RoutingDimension *const dimension() const
DimensionSchedulingStatus ComputePackedRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::ResourceGroup::Resource *resource, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks)
A Resource sets attributes (costs/constraints) for a set of dimensions.
Definition: routing.h:416
DimensionSchedulingStatus ComputePackedCumuls(const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks, std::vector< std::vector< int >> *resource_indices_per_group)
DimensionSchedulingStatus OptimizeAndPack(const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, std::vector< std::vector< int >> *resource_indices_per_group)
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
void AddMaximumConstraint(int max_var, std::vector< int > vars) override
A ResourceGroup defines a set of available Resources with attributes on one or multiple dimensions.
Definition: routing.h:395
DimensionSchedulingStatus OptimizeSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, int64_t *cost, int64_t *transit_cost, bool clear_lp=true)
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
void SetObjectiveCoefficient(int index, double coefficient) override
void AddProductConstraint(int product_var, std::vector< int > vars) override
int64_t GetVariableUpperBound(int index) const override
int64_t GetVariableLowerBound(int index) const override
DimensionSchedulingStatus ComputeRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound)=0
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
DimensionSchedulingStatus Optimize(const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, std::vector< std::vector< int >> *resource_indices_per_group, int64_t *cost, int64_t *transit_cost, bool clear_lp=true)
RoutingGlopWrapper(bool is_relaxation, const glop::GlopParameters &parameters)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
virtual void SetEnforcementLiteral(int ct, int condition)=0
void SetEnforcementLiteral(int ct, int condition) override
LocalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
Collection of objects used to extend the Constraint Solver library.
bool PropagateCumulBounds(const std::function< int64_t(int64_t)> &next_accessor, int64_t cumul_offset)
void SetObjectiveCoefficient(int index, double coefficient) override
int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override
int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override
bool ComputeAssignmentCostsForVehicle(int v, const std::function< int64_t(int64_t)> &next_accessor, bool optimize_vehicle_costs, std::vector< int64_t > *assignment_costs, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values)
double GetValue(int index) const override
double GetObjectiveCoefficient(int index) const override
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
std::vector< DimensionSchedulingStatus > ComputeRouteCumulCostsForResourcesWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::ResourceGroup::Resource > &resources, const std::vector< int > &resource_indices, bool optimize_vehicle_costs, std::vector< int64_t > *optimal_costs_without_transits, std::vector< std::vector< int64_t >> *optimal_cumuls, std::vector< std::vector< int64_t >> *optimal_breaks)
void AddProductConstraint(int product_var, std::vector< int > vars) override
double GetObjectiveCoefficient(int index) const override
int AddVariable(int64_t lower_bound, int64_t upper_bound)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
virtual int64_t GetVariableLowerBound(int index) const =0
virtual void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends)=0
void AddMaximumConstraint(int max_var, std::vector< int > vars) override
void SetEnforcementLiteral(int ct, int condition) override
CumulBoundsPropagator(const RoutingDimension *dimension)
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
DimensionSchedulingStatus ComputeRouteCumulCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost)