C++ Reference

C++ Reference: Routing

routing_lp_scheduling.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <ostream>
25 #include <string>
26 #include <utility>
27 #include <vector>
28 
29 #include "absl/container/flat_hash_map.h"
30 #include "absl/time/time.h"
31 #include "ortools/base/dump_vars.h"
32 #include "ortools/base/logging.h"
33 #include "ortools/base/mathutil.h"
35 #include "ortools/constraint_solver/routing_parameters.pb.h"
36 #include "ortools/glop/lp_solver.h"
37 #include "ortools/glop/parameters.pb.h"
38 #include "ortools/lp_data/lp_data.h"
39 #include "ortools/lp_data/lp_types.h"
40 #include "ortools/sat/cp_model.pb.h"
41 #include "ortools/sat/cp_model_solver.h"
42 #include "ortools/sat/model.h"
43 #include "ortools/sat/sat_parameters.pb.h"
44 #include "ortools/util/sorted_interval_list.h"
45 
46 namespace operations_research {
47 
48 // Classes to solve dimension cumul placement (aka scheduling) problems using
49 // linear programming.
50 
51 // Utility class used in the core optimizer to tighten the cumul bounds as much
52 // as possible based on the model precedences.
54  public:
56 
57  // Tightens the cumul bounds starting from the current cumul var min/max,
58  // and propagating the precedences resulting from the next_accessor, and the
59  // dimension's precedence rules.
60  // Returns false iff the precedences are infeasible with the given routes.
61  // Otherwise, the user can call CumulMin() and CumulMax() to retrieve the new
62  // bounds of an index.
64  const std::function<int64_t(int64_t)>& next_accessor,
65  int64_t cumul_offset,
66  const std::vector<RoutingModel::RouteDimensionTravelInfo>*
67  dimension_travel_info_per_route = nullptr);
68 
69  int64_t CumulMin(int index) const {
70  return propagated_bounds_[PositiveNode(index)];
71  }
72 
73  int64_t CumulMax(int index) const {
74  const int64_t negated_upper_bound = propagated_bounds_[NegativeNode(index)];
75  return negated_upper_bound == std::numeric_limits<int64_t>::min()
76  ? std::numeric_limits<int64_t>::max()
77  : -negated_upper_bound;
78  }
79 
80  const RoutingDimension& dimension() const { return dimension_; }
81 
82  private:
83  // An arc "tail --offset--> head" represents the relation
84  // tail + offset <= head.
85  // As arcs are stored by tail, we don't store it in the struct.
86  struct ArcInfo {
87  int head;
88  int64_t offset;
89  };
90  static const int kNoParent;
91  static const int kParentToBePropagated;
92 
93  // Return the node corresponding to the lower bound of the cumul of index and
94  // -index respectively.
95  int PositiveNode(int index) const { return 2 * index; }
96  int NegativeNode(int index) const { return 2 * index + 1; }
97 
98  void AddNodeToQueue(int node) {
99  if (!node_in_queue_[node]) {
100  bf_queue_.push_back(node);
101  node_in_queue_[node] = true;
102  }
103  }
104 
105  // Adds the relation first_index + offset <= second_index, by adding arcs
106  // first_index --offset--> second_index and
107  // -second_index --offset--> -first_index.
108  void AddArcs(int first_index, int second_index, int64_t offset);
109 
110  bool InitializeArcsAndBounds(
111  const std::function<int64_t(int64_t)>& next_accessor,
112  int64_t cumul_offset,
113  const std::vector<RoutingModel::RouteDimensionTravelInfo>*
114  dimension_travel_info_per_route = nullptr);
115 
116  bool UpdateCurrentLowerBoundOfNode(int node, int64_t new_lb, int64_t offset);
117 
118  bool DisassembleSubtree(int source, int target);
119 
120  bool CleanupAndReturnFalse() {
121  // We clean-up node_in_queue_ for future calls, and return false.
122  for (int node_to_cleanup : bf_queue_) {
123  node_in_queue_[node_to_cleanup] = false;
124  }
125  bf_queue_.clear();
126  return false;
127  }
128 
129  const RoutingDimension& dimension_;
130  const int64_t num_nodes_;
131 
132  // TODO(user): Investigate if all arcs for a given tail can be created
133  // at the same time, in which case outgoing_arcs_ could point to an absl::Span
134  // for each tail index.
135  std::vector<std::vector<ArcInfo>> outgoing_arcs_;
136 
137  std::deque<int> bf_queue_;
138  std::vector<bool> node_in_queue_;
139  std::vector<int> tree_parent_node_of_;
140  // After calling PropagateCumulBounds(), for each node index n,
141  // propagated_bounds_[2*n] and -propagated_bounds_[2*n+1] respectively contain
142  // the propagated lower and upper bounds of n's cumul variable.
143  std::vector<int64_t> propagated_bounds_;
144 
145  // Vector used in DisassembleSubtree() to avoid memory reallocation.
146  std::vector<int> tmp_dfs_stack_;
147 
148  // Used to store the pickup/delivery pairs encountered on the routes.
149  std::vector<std::pair<int64_t, int64_t>>
150  visited_pickup_delivery_indices_for_pair_;
151 };
152 
154  // An optimal solution was found respecting all constraints.
155  OPTIMAL,
156  // An optimal solution was found, however constraints which were relaxed were
157  // violated.
159  // A solution could not be found.
160  INFEASIBLE
161 };
162 
164  public:
166  virtual void Clear() = 0;
167  virtual int CreateNewPositiveVariable() = 0;
168  virtual void SetVariableName(int index, absl::string_view name) = 0;
169  virtual bool SetVariableBounds(int index, int64_t lower_bound,
170  int64_t upper_bound) = 0;
171  virtual void SetVariableDisjointBounds(int index,
172  const std::vector<int64_t>& starts,
173  const std::vector<int64_t>& ends) = 0;
174  virtual int64_t GetVariableLowerBound(int index) const = 0;
175  virtual int64_t GetVariableUpperBound(int index) const = 0;
176  virtual void SetObjectiveCoefficient(int index, double coefficient) = 0;
177  virtual double GetObjectiveCoefficient(int index) const = 0;
178  virtual void ClearObjective() = 0;
179  virtual int NumVariables() const = 0;
180  virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) = 0;
181  virtual void SetCoefficient(int ct, int index, double coefficient) = 0;
182  virtual bool IsCPSATSolver() = 0;
183  virtual void AddObjectiveConstraint() = 0;
184  virtual void AddMaximumConstraint(int max_var, std::vector<int> vars) = 0;
185  virtual void AddProductConstraint(int product_var, std::vector<int> vars) = 0;
186  virtual void SetEnforcementLiteral(int ct, int condition) = 0;
187  virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit) = 0;
188  virtual int64_t GetObjectiveValue() const = 0;
189  virtual double GetValue(int index) const = 0;
190  virtual bool SolutionIsInteger() const = 0;
191 
192  // This function is meant to override the parameters of the solver.
193  virtual void SetParameters(const std::string& parameters) = 0;
194 
195  // Returns if the model is empty or not.
196  virtual bool ModelIsEmpty() const { return true; }
197 
198  // Prints an understandable view of the model.
199  virtual std::string PrintModel() const = 0;
200 
201  // Adds a variable with bounds [lower_bound, upper_bound].
202  int AddVariable(int64_t lower_bound, int64_t upper_bound) {
203  CHECK_LE(lower_bound, upper_bound);
204  const int variable = CreateNewPositiveVariable();
205  SetVariableBounds(variable, lower_bound, upper_bound);
206  return variable;
207  }
208  // Adds a linear constraint, enforcing
209  // lower_bound <= sum variable * coeff <= upper_bound,
210  // and returns the identifier of that constraint.
212  int64_t lower_bound, int64_t upper_bound,
213  const std::vector<std::pair<int, double>>& variable_coeffs) {
214  CHECK_LE(lower_bound, upper_bound);
215  const int ct = CreateNewConstraint(lower_bound, upper_bound);
216  for (const auto& variable_coeff : variable_coeffs) {
217  SetCoefficient(ct, variable_coeff.first, variable_coeff.second);
218  }
219  return ct;
220  }
221  // Adds a linear constraint and a 0/1 variable that is true iff
222  // lower_bound <= sum variable * coeff <= upper_bound,
223  // and returns the identifier of that variable.
225  int64_t lower_bound, int64_t upper_bound,
226  const std::vector<std::pair<int, double>>& weighted_variables) {
227  const int reification_ct = AddLinearConstraint(1, 1, {});
228  if (std::numeric_limits<int64_t>::min() < lower_bound) {
229  const int under_lower_bound = AddVariable(0, 1);
230 #ifndef NDEBUG
231  SetVariableName(under_lower_bound, "under_lower_bound");
232 #endif
233  SetCoefficient(reification_ct, under_lower_bound, 1);
234  const int under_lower_bound_ct =
235  AddLinearConstraint(std::numeric_limits<int64_t>::min(),
236  lower_bound - 1, weighted_variables);
237  SetEnforcementLiteral(under_lower_bound_ct, under_lower_bound);
238  }
239  if (upper_bound < std::numeric_limits<int64_t>::max()) {
240  const int above_upper_bound = AddVariable(0, 1);
241 #ifndef NDEBUG
242  SetVariableName(above_upper_bound, "above_upper_bound");
243 #endif
244  SetCoefficient(reification_ct, above_upper_bound, 1);
245  const int above_upper_bound_ct = AddLinearConstraint(
246  upper_bound + 1, std::numeric_limits<int64_t>::max(),
247  weighted_variables);
248  SetEnforcementLiteral(above_upper_bound_ct, above_upper_bound);
249  }
250  const int within_bounds = AddVariable(0, 1);
251 #ifndef NDEBUG
252  SetVariableName(within_bounds, "within_bounds");
253 #endif
254  SetCoefficient(reification_ct, within_bounds, 1);
255  const int within_bounds_ct =
256  AddLinearConstraint(lower_bound, upper_bound, weighted_variables);
257  SetEnforcementLiteral(within_bounds_ct, within_bounds);
258  return within_bounds;
259  }
260 };
261 
263  public:
264  RoutingGlopWrapper(bool is_relaxation, const glop::GlopParameters& parameters)
265  : is_relaxation_(is_relaxation) {
266  lp_solver_.SetParameters(parameters);
267  linear_program_.SetMaximizationProblem(false);
268  }
269  void Clear() override {
270  linear_program_.Clear();
271  linear_program_.SetMaximizationProblem(false);
272  allowed_intervals_.clear();
273  }
274  int CreateNewPositiveVariable() override {
275  return linear_program_.CreateNewVariable().value();
276  }
277  void SetVariableName(int index, absl::string_view name) override {
278  linear_program_.SetVariableName(glop::ColIndex(index), name);
279  }
280  bool SetVariableBounds(int index, int64_t lower_bound,
281  int64_t upper_bound) override {
282  DCHECK_GE(lower_bound, 0);
283  // When variable upper bounds are greater than this threshold, precision
284  // issues arise in GLOP. In this case we are just going to suppose that
285  // these high bound values are infinite and not set the upper bound.
286  const int64_t kMaxValue = 1e10;
287  const double lp_min = lower_bound;
288  const double lp_max =
289  (upper_bound > kMaxValue) ? glop::kInfinity : upper_bound;
290  if (lp_min <= lp_max) {
291  linear_program_.SetVariableBounds(glop::ColIndex(index), lp_min, lp_max);
292  return true;
293  }
294  // The linear_program would not be feasible, and it cannot handle the
295  // lp_min > lp_max case, so we must detect infeasibility here.
296  return false;
297  }
298  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
299  const std::vector<int64_t>& ends) override {
300  // TODO(user): Investigate if we can avoid rebuilding the interval list
301  // each time (we could keep a reference to the forbidden interval list in
302  // RoutingDimension but we would need to store cumul offsets and use them
303  // when checking intervals).
304  allowed_intervals_[index] =
305  std::make_unique<SortedDisjointIntervalList>(starts, ends);
306  }
307  int64_t GetVariableLowerBound(int index) const override {
308  return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
309  }
310  int64_t GetVariableUpperBound(int index) const override {
311  const double upper_bound =
312  linear_program_.variable_upper_bounds()[glop::ColIndex(index)];
313  DCHECK_GE(upper_bound, 0);
314  return upper_bound == glop::kInfinity ? std::numeric_limits<int64_t>::max()
315  : static_cast<int64_t>(upper_bound);
316  }
317  void SetObjectiveCoefficient(int index, double coefficient) override {
318  linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
319  }
320  double GetObjectiveCoefficient(int index) const override {
321  return linear_program_.objective_coefficients()[glop::ColIndex(index)];
322  }
323  void ClearObjective() override {
324  for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
325  linear_program_.SetObjectiveCoefficient(i, 0);
326  }
327  }
328  int NumVariables() const override {
329  return linear_program_.num_variables().value();
330  }
331  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
332  const glop::RowIndex ct = linear_program_.CreateNewConstraint();
333  linear_program_.SetConstraintBounds(
334  ct,
335  (lower_bound == std::numeric_limits<int64_t>::min()) ? -glop::kInfinity
336  : lower_bound,
337  (upper_bound == std::numeric_limits<int64_t>::max()) ? glop::kInfinity
338  : upper_bound);
339  return ct.value();
340  }
341  void SetCoefficient(int ct, int index, double coefficient) override {
342  // Necessary to keep the model clean
343  // (cf. glop::LinearProgram::NotifyThatColumnsAreClean).
344  if (coefficient == 0.0) return;
345  linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
346  coefficient);
347  }
348  bool IsCPSATSolver() override { return false; }
349  void AddObjectiveConstraint() override {
350  double max_coefficient = 0;
351  for (int variable = 0; variable < NumVariables(); variable++) {
352  const double coefficient = GetObjectiveCoefficient(variable);
353  max_coefficient = std::max(MathUtil::Abs(coefficient), max_coefficient);
354  }
355  DCHECK_GE(max_coefficient, 0);
356  if (max_coefficient == 0) {
357  // There are no terms in the objective.
358  return;
359  }
360  const glop::RowIndex ct = linear_program_.CreateNewConstraint();
361  double normalized_objective_value = 0;
362  for (int variable = 0; variable < NumVariables(); variable++) {
363  const double coefficient = GetObjectiveCoefficient(variable);
364  if (coefficient != 0) {
365  const double normalized_coeff = coefficient / max_coefficient;
366  SetCoefficient(ct.value(), variable, normalized_coeff);
367  normalized_objective_value += normalized_coeff * GetValue(variable);
368  }
369  }
370  normalized_objective_value = std::max(
371  normalized_objective_value, GetObjectiveValue() / max_coefficient);
372  linear_program_.SetConstraintBounds(ct, -glop::kInfinity,
373  normalized_objective_value);
374  }
375  void AddMaximumConstraint(int /*max_var*/,
376  std::vector<int> /*vars*/) override {}
377  void AddProductConstraint(int /*product_var*/,
378  std::vector<int> /*vars*/) override {}
379  void SetEnforcementLiteral(int /*ct*/, int /*condition*/) override{};
380  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
381  lp_solver_.GetMutableParameters()->set_max_time_in_seconds(
382  absl::ToDoubleSeconds(duration_limit));
383 
384  // Because we construct the lp one constraint at a time and we never call
385  // SetCoefficient() on the same variable twice for a constraint, we know
386  // that the columns do not contain duplicates and are already ordered by
387  // constraint so we do not need to call linear_program->CleanUp() which can
388  // be costly. Note that the assumptions are DCHECKed() in the call below.
389  linear_program_.NotifyThatColumnsAreClean();
390  VLOG(2) << linear_program_.Dump();
391  const glop::ProblemStatus status = lp_solver_.Solve(linear_program_);
392  if (status != glop::ProblemStatus::OPTIMAL &&
393  status != glop::ProblemStatus::IMPRECISE) {
395  }
396  if (is_relaxation_) {
398  }
399  for (const auto& allowed_interval : allowed_intervals_) {
400  const double value_double = GetValue(allowed_interval.first);
401  const int64_t value =
402  (value_double >= std::numeric_limits<int64_t>::max())
403  ? std::numeric_limits<int64_t>::max()
404  : MathUtil::FastInt64Round(value_double);
405  const SortedDisjointIntervalList* const interval_list =
406  allowed_interval.second.get();
407  const auto it = interval_list->FirstIntervalGreaterOrEqual(value);
408  if (it == interval_list->end() || value < it->start) {
410  }
411  }
413  }
414  int64_t GetObjectiveValue() const override {
415  return MathUtil::FastInt64Round(lp_solver_.GetObjectiveValue());
416  }
417  double GetValue(int index) const override {
418  return lp_solver_.variable_values()[glop::ColIndex(index)];
419  }
420  bool SolutionIsInteger() const override {
421  return linear_program_.SolutionIsInteger(lp_solver_.variable_values(),
422  /*absolute_tolerance*/ 1e-3);
423  }
424 
425  void SetParameters(const std::string& parameters) override {
426  glop::GlopParameters params;
427  const bool status = params.ParseFromString(parameters);
428  DCHECK(status);
429  lp_solver_.SetParameters(params);
430  }
431 
432  // Prints an understandable view of the model
433  // TODO(user): Improve output readability.
434  std::string PrintModel() const override { return linear_program_.Dump(); }
435 
436  private:
437  const bool is_relaxation_;
438  glop::LinearProgram linear_program_;
439  glop::LPSolver lp_solver_;
440  absl::flat_hash_map<int, std::unique_ptr<SortedDisjointIntervalList>>
441  allowed_intervals_;
442 };
443 
445  public:
447  parameters_.set_num_search_workers(1);
448  // Keeping presolve but with 0 iterations; as of 11/2019 it is
449  // significantly faster than both full presolve and no presolve.
450  parameters_.set_cp_model_presolve(true);
451  parameters_.set_max_presolve_iterations(0);
452  parameters_.set_catch_sigint_signal(false);
453  parameters_.set_mip_max_bound(1e8);
454  parameters_.set_search_branching(sat::SatParameters::LP_SEARCH);
455  parameters_.set_linearization_level(2);
456  parameters_.set_cut_level(0);
457  parameters_.set_use_absl_random(false);
458  }
459  ~RoutingCPSatWrapper() override {}
460  void Clear() override {
461  model_.Clear();
462  response_.Clear();
463  objective_coefficients_.clear();
464  }
465  int CreateNewPositiveVariable() override {
466  const int index = model_.variables_size();
467  sat::IntegerVariableProto* const variable = model_.add_variables();
468  variable->add_domain(0);
469  variable->add_domain(static_cast<int64_t>(parameters_.mip_max_bound()));
470  return index;
471  }
472  void SetVariableName(int index, absl::string_view name) override {
473  model_.mutable_variables(index)->set_name(name.data());
474  }
475  bool SetVariableBounds(int index, int64_t lower_bound,
476  int64_t upper_bound) override {
477  DCHECK_GE(lower_bound, 0);
478  const int64_t capped_upper_bound =
479  std::min<int64_t>(upper_bound, parameters_.mip_max_bound());
480  if (lower_bound > capped_upper_bound) return false;
481  sat::IntegerVariableProto* const variable = model_.mutable_variables(index);
482  variable->set_domain(0, lower_bound);
483  variable->set_domain(1, capped_upper_bound);
484  return true;
485  }
486  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
487  const std::vector<int64_t>& ends) override {
488  DCHECK_EQ(starts.size(), ends.size());
489  const int ct = CreateNewConstraint(1, 1);
490  for (int i = 0; i < starts.size(); ++i) {
491  const int variable = CreateNewPositiveVariable();
492 #ifndef NDEBUG
493  SetVariableName(variable,
494  absl::StrFormat("disjoint(%ld, %ld)", index, i));
495 #endif
496  SetVariableBounds(variable, 0, 1);
497  SetCoefficient(ct, variable, 1);
498  const int window_ct = CreateNewConstraint(starts[i], ends[i]);
499  SetCoefficient(window_ct, index, 1);
500  model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
501  }
502  }
503  int64_t GetVariableLowerBound(int index) const override {
504  return model_.variables(index).domain(0);
505  }
506  int64_t GetVariableUpperBound(int index) const override {
507  const auto& domain = model_.variables(index).domain();
508  return domain[domain.size() - 1];
509  }
510  void SetObjectiveCoefficient(int index, double coefficient) override {
511  if (index >= objective_coefficients_.size()) {
512  objective_coefficients_.resize(index + 1, 0);
513  }
514  objective_coefficients_[index] = coefficient;
515  sat::FloatObjectiveProto* const objective =
516  model_.mutable_floating_point_objective();
517  objective->add_vars(index);
518  objective->add_coeffs(coefficient);
519  }
520  double GetObjectiveCoefficient(int index) const override {
521  return (index < objective_coefficients_.size())
522  ? objective_coefficients_[index]
523  : 0;
524  }
525  void ClearObjective() override {
526  model_.mutable_floating_point_objective()->Clear();
527  }
528  int NumVariables() const override { return model_.variables_size(); }
529  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
530  sat::LinearConstraintProto* const ct =
531  model_.add_constraints()->mutable_linear();
532  ct->add_domain(lower_bound);
533  ct->add_domain(upper_bound);
534  return model_.constraints_size() - 1;
535  }
536  void SetCoefficient(int ct_index, int index, double coefficient) override {
537  sat::LinearConstraintProto* const ct =
538  model_.mutable_constraints(ct_index)->mutable_linear();
539  ct->add_vars(index);
540  const int64_t integer_coefficient = coefficient;
541  ct->add_coeffs(integer_coefficient);
542  }
543  bool IsCPSATSolver() override { return true; }
544  void AddObjectiveConstraint() override {
545  const sat::CpObjectiveProto& objective = response_.integer_objective();
546  int64_t activity = 0;
547  for (int i = 0; i < objective.vars_size(); ++i) {
548  activity += response_.solution(objective.vars(i)) * objective.coeffs(i);
549  }
550  const int ct =
551  CreateNewConstraint(std::numeric_limits<int64_t>::min(), activity);
552  for (int i = 0; i < objective.vars_size(); ++i) {
553  SetCoefficient(ct, objective.vars(i), objective.coeffs(i));
554  }
555  model_.clear_objective();
556  }
557  void AddMaximumConstraint(int max_var, std::vector<int> vars) override {
558  sat::LinearArgumentProto* const ct =
559  model_.add_constraints()->mutable_lin_max();
560  ct->mutable_target()->add_vars(max_var);
561  ct->mutable_target()->add_coeffs(1);
562  for (const int var : vars) {
563  sat::LinearExpressionProto* const expr = ct->add_exprs();
564  expr->add_vars(var);
565  expr->add_coeffs(1);
566  }
567  }
568  void AddProductConstraint(int product_var, std::vector<int> vars) override {
569  sat::LinearArgumentProto* const ct =
570  model_.add_constraints()->mutable_int_prod();
571  ct->mutable_target()->add_vars(product_var);
572  ct->mutable_target()->add_coeffs(1);
573  for (const int var : vars) {
574  sat::LinearExpressionProto* expr = ct->add_exprs();
575  expr->add_vars(var);
576  expr->add_coeffs(1);
577  }
578  }
579  void SetEnforcementLiteral(int ct, int condition) override {
580  DCHECK_LT(ct, model_.constraints_size());
581  model_.mutable_constraints(ct)->add_enforcement_literal(condition);
582  }
583  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
584  parameters_.set_max_time_in_seconds(absl::ToDoubleSeconds(duration_limit));
585  VLOG(2) << model_.DebugString();
586  if (hint_.vars_size() == model_.variables_size()) {
587  *model_.mutable_solution_hint() = hint_;
588  }
589  sat::Model model;
590  model.Add(sat::NewSatParameters(parameters_));
591  response_ = sat::SolveCpModel(model_, &model);
592  VLOG(2) << response_.DebugString();
593  if (response_.status() == sat::CpSolverStatus::OPTIMAL ||
594  (response_.status() == sat::CpSolverStatus::FEASIBLE &&
595  !model_.has_floating_point_objective())) {
596  hint_.Clear();
597  for (int i = 0; i < response_.solution_size(); ++i) {
598  hint_.add_vars(i);
599  hint_.add_values(response_.solution(i));
600  }
602  }
604  }
605  int64_t GetObjectiveValue() const override {
606  return MathUtil::FastInt64Round(response_.objective_value());
607  }
608  double GetValue(int index) const override {
609  return response_.solution(index);
610  }
611  bool SolutionIsInteger() const override { return true; }
612 
613  // NOTE: This function is not implemented for the CP-SAT solver.
614  void SetParameters(const std::string& /*parameters*/) override {
615  DCHECK(false);
616  }
617 
618  bool ModelIsEmpty() const override { return model_.ByteSizeLong() == 0; }
619 
620  // Prints an understandable view of the model
621  std::string PrintModel() const override;
622 
623  private:
624  sat::CpModelProto model_;
625  sat::CpSolverResponse response_;
626  sat::SatParameters parameters_;
627  std::vector<double> objective_coefficients_;
628  sat::PartialVariableAssignment hint_;
629 };
630 
631 // Utility class used in Local/GlobalDimensionCumulOptimizer to set the linear
632 // solver constraints and solve the problem.
635 
636  public:
638  bool use_precedence_propagator);
639 
640  // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
641  // and "cost" parameters are null, we don't optimize the cost and stop at the
642  // first feasible solution in the linear solver (since in this case only
643  // feasibility is of interest).
645  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
646  const RouteDimensionTravelInfo& dimension_travel_info,
647  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
648  std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
649  bool clear_lp = true);
650 
651  // Given some cumuls and breaks, computes the solution cost by solving the
652  // same model as in OptimizeSingleRoute() with the addition of constraints for
653  // cumuls and breaks.
655  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
656  const RouteDimensionTravelInfo& dimension_travel_info,
658  const std::vector<int64_t>& solution_cumul_values,
659  const std::vector<int64_t>& solution_break_values, int64_t* cost,
660  int64_t* transit_cost, int64_t* cost_offset = nullptr,
661  bool reuse_previous_model_if_possible = true, bool clear_lp = false,
662  bool clear_solution_constraints = true,
663  absl::Duration* const solve_duration = nullptr);
664 
665  std::vector<DimensionSchedulingStatus> OptimizeSingleRouteWithResources(
666  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
667  const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
668  const RouteDimensionTravelInfo& dimension_travel_info,
669  const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
670  const std::vector<int>& resource_indices, bool optimize_vehicle_costs,
672  std::vector<int64_t>* costs_without_transits,
673  std::vector<std::vector<int64_t>>* cumul_values,
674  std::vector<std::vector<int64_t>>* break_values, bool clear_lp = true);
675 
677  const std::function<int64_t(int64_t)>& next_accessor,
678  const std::vector<RouteDimensionTravelInfo>&
679  dimension_travel_info_per_route,
680  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
681  std::vector<int64_t>* break_values,
682  std::vector<std::vector<int>>* resource_indices_per_group, int64_t* cost,
683  int64_t* transit_cost, bool clear_lp = true);
684 
686  const std::function<int64_t(int64_t)>& next_accessor,
687  const std::vector<RouteDimensionTravelInfo>&
688  dimension_travel_info_per_route,
689  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
690  std::vector<int64_t>* break_values,
691  std::vector<std::vector<int>>* resource_indices_per_group);
692 
694  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
695  const RouteDimensionTravelInfo& dimension_travel_info,
697  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
698  std::vector<int64_t>* break_values);
699 
700  const RoutingDimension* dimension() const { return dimension_; }
701 
702  private:
703  // Initializes the containers and given solver. Must be called prior to
704  // setting any constraints and solving.
705  void InitOptimizer(RoutingLinearSolverWrapper* solver);
706 
707  // Initializes the model for a single route and a given dimension and sets its
708  // constraints.
709  bool InitSingleRoute(int vehicle,
710  const std::function<int64_t(int64_t)>& next_accessor,
711  const RouteDimensionTravelInfo& dimension_travel_info,
713  std::vector<int64_t>* cumul_values, int64_t* cost,
714  int64_t* transit_cost, int64_t* cumul_offset,
715  int64_t* const cost_offset);
716 
717  // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
718  // in current_route_[min|max]_cumuls_ respectively.
719  bool ExtractRouteCumulBounds(const std::vector<int64_t>& route,
720  int64_t cumul_offset);
721 
722  // Tighten the minimum/maximum of cumuls for nodes on "route"
723  // If the propagator_ is not null, uses the bounds tightened by the
724  // propagator. Otherwise, the minimum transits are used to tighten them.
725  bool TightenRouteCumulBounds(const std::vector<int64_t>& route,
726  const std::vector<int64_t>& min_transits,
727  int64_t cumul_offset);
728 
729  // Sets the constraints for all nodes on "vehicle"'s route according to
730  // "next_accessor". If optimize_costs is true, also sets the objective
731  // coefficients for the LP.
732  // Returns false if some infeasibility was detected, true otherwise.
733  bool SetRouteCumulConstraints(
734  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
735  const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
736  const RouteDimensionTravelInfo& dimension_travel_info,
737  int64_t cumul_offset, bool optimize_costs,
738  RoutingLinearSolverWrapper* solver, int64_t* route_transit_cost,
739  int64_t* route_cost_offset);
740 
741  // Sets the constraints for all variables related to travel. Handles
742  // static or time-dependent travel values.
743  // Returns false if some infeasibility was detected, true otherwise.
744  bool SetRouteTravelConstraints(
745  const RouteDimensionTravelInfo& dimension_travel_info,
746  const std::vector<int>& lp_slacks,
747  const std::vector<int64_t>& fixed_transit,
749 
750  // Sets the global constraints on the dimension, and adds global objective
751  // cost coefficients if optimize_costs is true.
752  // NOTE: When called, the call to this function MUST come after
753  // SetRouteCumulConstraints() has been called on all routes, so that
754  // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
755  // initialized.
756  // Returns false if some infeasibility was detected, true otherwise.
757  bool SetGlobalConstraints(
758  const std::function<int64_t(int64_t)>& next_accessor,
759  int64_t cumul_offset, bool optimize_costs,
761 
762  void SetValuesFromLP(const std::vector<int>& lp_variables, int64_t offset,
764  std::vector<int64_t>* lp_values) const;
765 
766  void SetResourceIndices(
768  std::vector<std::vector<int>>* resource_indices_per_group) const;
769 
770  // This function packs the routes of the given vehicles while keeping the cost
771  // of the LP lower than its current (supposed optimal) objective value.
772  // It does so by setting the current objective variables' coefficient to 0 and
773  // setting the coefficient of the route ends to 1, to first minimize the route
774  // ends' cumuls, and then maximizes the starts' cumuls without increasing the
775  // ends.
776  DimensionSchedulingStatus PackRoutes(
777  std::vector<int> vehicles, RoutingLinearSolverWrapper* solver,
778  const glop::GlopParameters& packing_parameters);
779 
780  std::unique_ptr<CumulBoundsPropagator> propagator_;
781  std::vector<int64_t> current_route_min_cumuls_;
782  std::vector<int64_t> current_route_max_cumuls_;
783  const RoutingDimension* const dimension_;
784  // Scheduler variables for current route cumuls and for all nodes cumuls.
785  std::vector<int> current_route_cumul_variables_;
786  std::vector<int> index_to_cumul_variable_;
787  // Scheduler variables for current route breaks and all vehicle breaks.
788  // There are two variables for each break: start and end.
789  // current_route_break_variables_ has variables corresponding to
790  // break[0] start, break[0] end, break[1] start, break[1] end, etc.
791  std::vector<int> current_route_break_variables_;
792  // Vector all_break_variables contains the break variables of all vehicles,
793  // in the same format as current_route_break_variables.
794  // It is the concatenation of break variables of vehicles in [0, #vehicles).
795  std::vector<int> all_break_variables_;
796  // Allows to retrieve break variables of a given vehicle: those go from
797  // all_break_variables_[vehicle_to_all_break_variables_offset_[vehicle]] to
798  // all_break_variables[vehicle_to_all_break_variables_offset_[vehicle+1]-1].
799  std::vector<int> vehicle_to_all_break_variables_offset_;
800  // The following vector contains indices of resource-to-vehicle assignment
801  // variables. For every resource group, stores indices of
802  // num_resources*num_vehicles boolean variables indicating whether resource #r
803  // is assigned to vehicle #v.
804  std::vector<std::vector<int>>
805  resource_group_to_resource_to_vehicle_assignment_variables_;
806 
807  int max_end_cumul_;
808  int min_start_cumul_;
809  std::vector<std::pair<int64_t, int64_t>>
810  visited_pickup_delivery_indices_for_pair_;
811 };
812 
813 // Class used to compute optimal values for dimension cumuls of routes,
814 // minimizing cumul soft lower and upper bound costs, and vehicle span costs of
815 // a route.
816 // In its methods, next_accessor is a callback returning the next node of a
817 // given node on a route.
819  public:
822  RoutingSearchParameters::SchedulingSolver solver_type);
823 
824  // If feasible, computes the optimal cost of the route performed by a vehicle,
825  // minimizing cumul soft lower and upper bound costs and vehicle span costs,
826  // and stores it in "optimal_cost" (if not null).
827  // Returns true iff the route respects all constraints.
829  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
830  int64_t* optimal_cost);
831 
832  // Same as ComputeRouteCumulCost, but the cost computed does not contain
833  // the part of the vehicle span cost due to fixed transits.
835  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
836  int64_t* optimal_cost_without_transits);
837 
838  std::vector<DimensionSchedulingStatus>
840  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
841  const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
842  const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
843  const std::vector<int>& resource_indices, bool optimize_vehicle_costs,
844  std::vector<int64_t>* optimal_costs_without_transits,
845  std::vector<std::vector<int64_t>>* optimal_cumuls,
846  std::vector<std::vector<int64_t>>* optimal_breaks);
847 
848  // If feasible, computes the optimal values for cumul and break variables
849  // of the route performed by a vehicle, minimizing cumul soft lower, upper
850  // bound costs and vehicle span costs, stores them in "optimal_cumuls"
851  // (if not null), and optimal_breaks, and returns true.
852  // Returns false if the route is not feasible.
854  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
855  const RoutingModel::RouteDimensionTravelInfo& dimension_travel_info,
856  std::vector<int64_t>* optimal_cumuls,
857  std::vector<int64_t>* optimal_breaks);
858 
859  // Simple combination of ComputeRouteCumulCost() and ComputeRouteCumuls()
861  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
862  const RoutingModel::RouteDimensionTravelInfo& dimension_travel_info,
863  std::vector<int64_t>* optimal_cumuls,
864  std::vector<int64_t>* optimal_breaks, int64_t* optimal_cost);
865 
866  // If feasible, computes the cost of a given route performed by a vehicle
867  // defined by its cumuls and breaks.
869  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
870  const RoutingModel::RouteDimensionTravelInfo& dimension_travel_info,
871  const std::vector<int64_t>& solution_cumul_values,
872  const std::vector<int64_t>& solution_break_values, int64_t* solution_cost,
873  int64_t* cost_offset = nullptr,
874  bool reuse_previous_model_if_possible = false, bool clear_lp = true,
875  absl::Duration* solve_duration = nullptr);
876 
877  // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
878  // the route, such that the cost remains the same, the cumul of route end is
879  // minimized, and then the cumul of the start of the route is maximized.
880  // If 'resource' is non-null, the packed route must also respect its start/end
881  // time window.
883  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
884  const RoutingModel::RouteDimensionTravelInfo& dimension_travel_info,
886  std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
887 
888  const RoutingDimension* dimension() const {
889  return optimizer_core_.dimension();
890  }
891 
892  private:
893  std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
894  DimensionCumulOptimizerCore optimizer_core_;
895 };
896 
898  public:
901  RoutingSearchParameters::SchedulingSolver solver_type);
902  // If feasible, computes the optimal cost of the entire model with regards to
903  // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
904  // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
905  // (if not null).
906  // Returns true iff all the constraints can be respected.
908  const std::function<int64_t(int64_t)>& next_accessor,
909  int64_t* optimal_cost_without_transits);
910  // If feasible, computes the optimal values for cumul, break and resource
911  // variables, minimizing cumul soft lower/upper bound costs and vehicle/global
912  // span costs, stores them in "optimal_cumuls" (if not null), "optimal_breaks"
913  // and "optimal_resource_indices_per_group", and returns true.
914  // Returns false if the routes are not feasible.
916  const std::function<int64_t(int64_t)>& next_accessor,
917  const std::vector<RoutingModel::RouteDimensionTravelInfo>&
918  dimension_travel_info_per_route,
919  std::vector<int64_t>* optimal_cumuls,
920  std::vector<int64_t>* optimal_breaks,
921  std::vector<std::vector<int>>* optimal_resource_indices_per_group);
922 
923  // Similar to ComputeCumuls, but also tries to pack the cumul values on all
924  // routes, such that the cost remains the same, the cumuls of route ends are
925  // minimized, and then the cumuls of the starts of the routes are maximized.
927  const std::function<int64_t(int64_t)>& next_accessor,
928  const std::vector<RoutingModel::RouteDimensionTravelInfo>&
929  dimension_travel_info_per_route,
930  std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks,
931  std::vector<std::vector<int>>* resource_indices_per_group);
932 
933  const RoutingDimension* dimension() const {
934  return optimizer_core_.dimension();
935  }
936 
937  private:
938  std::unique_ptr<RoutingLinearSolverWrapper> solver_;
939  DimensionCumulOptimizerCore optimizer_core_;
940 };
941 
942 // Finds the approximate (*) min-cost (i.e. best) assignment of all vehicles
943 // v ∈ 'vehicles' to resources, i.e. indices in [0..num_resources), where the
944 // costs of assigning a vehicle v to a resource r is given by
945 // 'vehicle_to_resource_cost(v)[r]', unless 'vehicle_to_resource_cost(v)' is
946 // empty in which case vehicle v does not need a resource.
947 //
948 // Returns the cost of that optimal assignment, or -1 if it's infeasible.
949 // Moreover, if 'resource_indices' != nullptr, it assumes that its size is the
950 // global number of vehicles, and assigns its element #v with the resource r
951 // assigned to v, or -1 if none.
952 //
953 // (*) COST SCALING: When the costs are so large that they could possibly yield
954 // int64_t overflow, this method returns a *lower* bound of the actual optimal
955 // cost, and the assignment output in 'resource_indices' may be suboptimal if
956 // that lower bound isn't tight (but it should be very close).
957 //
958 // COMPLEXITY: in practice, should be roughly
959 // O(num_resources * vehicles.size() + resource_indices->size()).
961  std::vector<int> vehicles, int num_resources,
962  std::function<const std::vector<int64_t>*(int)>
963  vehicle_to_resource_assignment_costs,
964  std::vector<int>* resource_indices);
965 
966 // Computes the vehicle-to-resource assignment costs for the given vehicle to
967 // all resources in the group, and sets these costs in 'assignment_costs' (if
968 // non-null). The latter is cleared and kept empty if the vehicle 'v' should not
969 // have a resource assigned to it.
970 // optimize_vehicle_costs indicates if the costs should be optimized or if
971 // we merely care about feasibility (cost of 0) and infeasibility (cost of -1)
972 // of the assignments.
973 // The cumul and break values corresponding to the assignment of each resource
974 // are also set in cumul_values and break_values, if non-null.
976  int v, const RoutingModel::ResourceGroup& resource_group,
977  const std::function<int64_t(int64_t)>& next_accessor,
978  const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
979  bool optimize_vehicle_costs, LocalDimensionCumulOptimizer* lp_optimizer,
980  LocalDimensionCumulOptimizer* mp_optimizer,
981  std::vector<int64_t>* assignment_costs,
982  std::vector<std::vector<int64_t>>* cumul_values,
983  std::vector<std::vector<int64_t>>* break_values);
984 
985 // Simple struct returned by ComputePiecewiseLinearFormulationValue() to
986 // indicate if the value could be computed and if not, on what side the value
987 // was from the definition interval.
989  UNSPECIFIED = 0,
993 };
994 
995 // Computes pwl(x) for pwl a PieceWiseLinearFormulation.
996 // Returns a PieceWiseEvaluationStatus to indicate if the value could be
997 // computed (filled in value) and if not, why.
1000  PiecewiseLinearFormulation& pwl,
1001  int64_t x, int64_t* value, double delta = 0);
1002 
1003 // Like ComputePiecewiseLinearFormulationValue(), computes pwl(x) for pwl a
1004 // PiecewiseLinearFormulation. For convex PiecewiseLinearFormulations, if x is
1005 // outside the bounds of the function, instead of returning an error like in
1006 // PiecewiseLinearFormulation, the function will still be defined by its outer
1007 // segments.
1010  PiecewiseLinearFormulation& pwl,
1011  int64_t x, double delta = 0);
1012 
1013 // Structure to store the slope and y_intercept of a segment.
1015  double slope;
1016  double y_intercept;
1017 
1018  friend ::std::ostream& operator<<(::std::ostream& os,
1019  const SlopeAndYIntercept& it) {
1020  return os << "{" << it.slope << ", " << it.y_intercept << "}";
1021  }
1022 };
1023 
1024 // Converts a vector of SlopeAndYIntercept to a vector of convexity regions.
1025 // Convexity regions are defined such that, all segment in a convexity region
1026 // form a convex function. The boolean in the vector is set to true if the
1027 // segment associated to it starts a new convexity region. Therefore, a convex
1028 // function would yield {true, false, false, ...} and a concave function would
1029 // yield {true, true, true, ...}.
1031  const std::vector<SlopeAndYIntercept>& slope_and_y_intercept);
1032 
1033 // Given a PiecewiseLinearFormulation, returns a vector of slope and y-intercept
1034 // corresponding to each segment. Only the segments in [index_start, index_end[
1035 // will be considered.
1036 std::vector<SlopeAndYIntercept> PiecewiseLinearFormulationToSlopeAndYIntercept(
1038  PiecewiseLinearFormulation& pwl_function,
1039  int index_start = 0, int index_end = -1);
1040 
1041 } // namespace operations_research
1042 
1043 #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
const RoutingDimension & dimension() const
bool PropagateCumulBounds(const std::function< int64_t(int64_t)> &next_accessor, int64_t cumul_offset, const std::vector< RoutingModel::RouteDimensionTravelInfo > *dimension_travel_info_per_route=nullptr)
CumulBoundsPropagator(const RoutingDimension *dimension)
DimensionSchedulingStatus OptimizeSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, 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)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
DimensionSchedulingStatus ComputeSingleRouteSolutionCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, RoutingLinearSolverWrapper *solver, const std::vector< int64_t > &solution_cumul_values, const std::vector< int64_t > &solution_break_values, int64_t *cost, int64_t *transit_cost, int64_t *cost_offset=nullptr, bool reuse_previous_model_if_possible=true, bool clear_lp=false, bool clear_solution_constraints=true, absl::Duration *const solve_duration=nullptr)
DimensionSchedulingStatus Optimize(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RouteDimensionTravelInfo > &dimension_travel_info_per_route, 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)
std::vector< DimensionSchedulingStatus > OptimizeSingleRouteWithResources(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, const RouteDimensionTravelInfo &dimension_travel_info, 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)
DimensionSchedulingStatus OptimizeAndPackSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, const RoutingModel::ResourceGroup::Resource *resource, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
DimensionSchedulingStatus OptimizeAndPack(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RouteDimensionTravelInfo > &dimension_travel_info_per_route, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, std::vector< std::vector< int >> *resource_indices_per_group)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
DimensionSchedulingStatus ComputePackedCumuls(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::RouteDimensionTravelInfo > &dimension_travel_info_per_route, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks, std::vector< std::vector< int >> *resource_indices_per_group)
DimensionSchedulingStatus ComputeCumulCostWithoutFixedTransits(const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
DimensionSchedulingStatus ComputeCumuls(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::RouteDimensionTravelInfo > &dimension_travel_info_per_route, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, std::vector< std::vector< int >> *optimal_resource_indices_per_group)
DimensionSchedulingStatus ComputeRouteCumulCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost)
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
LocalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
DimensionSchedulingStatus ComputeRouteSolutionCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, const std::vector< int64_t > &solution_cumul_values, const std::vector< int64_t > &solution_break_values, int64_t *solution_cost, int64_t *cost_offset=nullptr, bool reuse_previous_model_if_possible=false, bool clear_lp=true, absl::Duration *solve_duration=nullptr)
DimensionSchedulingStatus ComputePackedRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, const RoutingModel::ResourceGroup::Resource *resource, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks)
std::vector< DimensionSchedulingStatus > ComputeRouteCumulCostsForResourcesWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_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)
DimensionSchedulingStatus ComputeRouteCumulsAndCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, int64_t *optimal_cost)
DimensionSchedulingStatus ComputeRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
void SetCoefficient(int ct_index, int index, double coefficient) override
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
double GetValue(int index) const override
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
void AddMaximumConstraint(int max_var, std::vector< int > vars) override
void AddProductConstraint(int product_var, std::vector< int > vars) override
std::string PrintModel() const override
void SetEnforcementLiteral(int ct, int condition) override
double GetObjectiveCoefficient(int index) const override
int64_t GetVariableUpperBound(int index) const override
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override
void SetVariableName(int index, absl::string_view name) override
void SetParameters(const std::string &) override
void SetObjectiveCoefficient(int index, double coefficient) override
int64_t GetVariableLowerBound(int index) const override
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2750
void AddProductConstraint(int, std::vector< int >) override
RoutingGlopWrapper(bool is_relaxation, const glop::GlopParameters &parameters)
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
double GetValue(int index) const override
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
void SetCoefficient(int ct, int index, double coefficient) override
double GetObjectiveCoefficient(int index) const override
int64_t GetVariableUpperBound(int index) const override
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override
void SetVariableName(int index, absl::string_view name) override
void SetParameters(const std::string &parameters) override
void AddMaximumConstraint(int, std::vector< int >) override
void SetObjectiveCoefficient(int index, double coefficient) override
int64_t GetVariableLowerBound(int index) const override
virtual void SetCoefficient(int ct, int index, double coefficient)=0
virtual void SetParameters(const std::string &parameters)=0
virtual double GetObjectiveCoefficient(int index) const =0
virtual void AddProductConstraint(int product_var, std::vector< int > vars)=0
int AddLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double >> &variable_coeffs)
int AddVariable(int64_t lower_bound, int64_t upper_bound)
virtual int64_t GetObjectiveValue() const =0
int AddReifiedLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double >> &weighted_variables)
virtual void SetVariableName(int index, absl::string_view name)=0
virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound)=0
virtual double GetValue(int index) const =0
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
virtual void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends)=0
virtual bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound)=0
virtual void SetObjectiveCoefficient(int index, double coefficient)=0
virtual void SetEnforcementLiteral(int ct, int condition)=0
virtual std::string PrintModel() const =0
virtual int64_t GetVariableUpperBound(int index) const =0
virtual int64_t GetVariableLowerBound(int index) const =0
virtual void AddMaximumConstraint(int max_var, std::vector< int > vars)=0
A Resource sets attributes (costs/constraints) for a set of dimensions.
Definition: routing.h:458
A ResourceGroup defines a set of available Resources with attributes on one or multiple dimensions.
Definition: routing.h:437
Collection of objects used to extend the Constraint Solver library.
int64_t ComputeConvexPiecewiseLinearFormulationValue(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl, int64_t x, double delta=0)
std::vector< SlopeAndYIntercept > PiecewiseLinearFormulationToSlopeAndYIntercept(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl_function, int index_start=0, int index_end=-1)
PiecewiseEvaluationStatus ComputePiecewiseLinearFormulationValue(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl, int64_t x, int64_t *value, double delta=0)
std::vector< bool > SlopeAndYInterceptToConvexityRegions(const std::vector< SlopeAndYIntercept > &slope_and_y_intercept)
bool ComputeVehicleToResourcesAssignmentCosts(int v, const RoutingModel::ResourceGroup &resource_group, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, bool optimize_vehicle_costs, LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, std::vector< int64_t > *assignment_costs, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values)
int64_t ComputeBestVehicleToResourceAssignment(std::vector< int > vehicles, int num_resources, std::function< const std::vector< int64_t > *(int)> vehicle_to_resource_assignment_costs, std::vector< int > *resource_indices)
Contains the information for a single transition on the route.
Definition: routing.h:1347
Contains the information needed by the solver to optimize a dimension's cumuls with travel-start depe...
Definition: routing.h:1345
friend ::std::ostream & operator<<(::std::ostream &os, const SlopeAndYIntercept &it)