OR-Tools  9.0
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"
41 #include "ortools/sat/model.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 
67  int64_t CumulMin(int index) const {
68  return propagated_bounds_[PositiveNode(index)];
69  }
70 
71  int64_t CumulMax(int index) const {
72  const int64_t negated_upper_bound = propagated_bounds_[NegativeNode(index)];
73  return negated_upper_bound == std::numeric_limits<int64_t>::min()
75  : -negated_upper_bound;
76  }
77 
78  const RoutingDimension& dimension() const { return dimension_; }
79 
80  private:
81  // An arc "tail --offset--> head" represents the relation
82  // tail + offset <= head.
83  // As arcs are stored by tail, we don't store it in the struct.
84  struct ArcInfo {
85  int head;
86  int64_t offset;
87  };
88  static const int kNoParent;
89  static const int kParentToBePropagated;
90 
91  // Return the node corresponding to the lower bound of the cumul of index and
92  // -index respectively.
93  int PositiveNode(int index) const { return 2 * index; }
94  int NegativeNode(int index) const { return 2 * index + 1; }
95 
96  void AddNodeToQueue(int node) {
97  if (!node_in_queue_[node]) {
98  bf_queue_.push_back(node);
99  node_in_queue_[node] = true;
100  }
101  }
102 
103  // Adds the relation first_index + offset <= second_index, by adding arcs
104  // first_index --offset--> second_index and
105  // -second_index --offset--> -first_index.
106  void AddArcs(int first_index, int second_index, int64_t offset);
107 
108  bool InitializeArcsAndBounds(
109  const std::function<int64_t(int64_t)>& next_accessor,
110  int64_t cumul_offset);
111 
112  bool UpdateCurrentLowerBoundOfNode(int node, int64_t new_lb, int64_t offset);
113 
114  bool DisassembleSubtree(int source, int target);
115 
116  bool CleanupAndReturnFalse() {
117  // We clean-up node_in_queue_ for future calls, and return false.
118  for (int node_to_cleanup : bf_queue_) {
119  node_in_queue_[node_to_cleanup] = false;
120  }
121  bf_queue_.clear();
122  return false;
123  }
124 
125  const RoutingDimension& dimension_;
126  const int64_t num_nodes_;
127 
128  // TODO(user): Investigate if all arcs for a given tail can be created
129  // at the same time, in which case outgoing_arcs_ could point to an absl::Span
130  // for each tail index.
131  std::vector<std::vector<ArcInfo>> outgoing_arcs_;
132 
133  std::deque<int> bf_queue_;
134  std::vector<bool> node_in_queue_;
135  std::vector<int> tree_parent_node_of_;
136  // After calling PropagateCumulBounds(), for each node index n,
137  // propagated_bounds_[2*n] and -propagated_bounds_[2*n+1] respectively contain
138  // the propagated lower and upper bounds of n's cumul variable.
139  std::vector<int64_t> propagated_bounds_;
140 
141  // Vector used in DisassembleSubtree() to avoid memory reallocation.
142  std::vector<int> tmp_dfs_stack_;
143 
144  // Used to store the pickup/delivery pairs encountered on the routes.
145  std::vector<std::pair<int64_t, int64_t>>
146  visited_pickup_delivery_indices_for_pair_;
147 };
148 
150  // An optimal solution was found respecting all constraints.
151  OPTIMAL,
152  // An optimal solution was found, however constraints which were relaxed were
153  // violated.
155  // A solution could not be found.
156  INFEASIBLE
157 };
158 
160  public:
162  virtual void Clear() = 0;
163  virtual int CreateNewPositiveVariable() = 0;
164  virtual bool SetVariableBounds(int index, int64_t lower_bound,
165  int64_t upper_bound) = 0;
167  const std::vector<int64_t>& starts,
168  const std::vector<int64_t>& ends) = 0;
169  virtual int64_t GetVariableLowerBound(int index) const = 0;
170  virtual void SetObjectiveCoefficient(int index, double coefficient) = 0;
171  virtual double GetObjectiveCoefficient(int index) const = 0;
172  virtual void ClearObjective() = 0;
173  virtual int NumVariables() const = 0;
174  virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) = 0;
175  virtual void SetCoefficient(int ct, int index, double coefficient) = 0;
176  virtual bool IsCPSATSolver() = 0;
177  virtual void AddMaximumConstraint(int max_var, std::vector<int> vars) = 0;
178  virtual void AddProductConstraint(int product_var, std::vector<int> vars) = 0;
179  virtual void SetEnforcementLiteral(int ct, int condition) = 0;
180  virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit) = 0;
181  virtual int64_t GetObjectiveValue() const = 0;
182  virtual double GetValue(int index) const = 0;
183  virtual bool SolutionIsInteger() const = 0;
184 
185  // Adds a variable with bounds [lower_bound, upper_bound].
186  int AddVariable(int64_t lower_bound, int64_t upper_bound) {
188  const int variable = CreateNewPositiveVariable();
190  return variable;
191  }
192  // Adds a linear constraint, enforcing
193  // lower_bound <= sum variable * coeff <= upper_bound,
194  // and returns the identifier of that constraint.
196  int64_t lower_bound, int64_t upper_bound,
197  const std::vector<std::pair<int, double>>& variable_coeffs) {
200  for (const auto& variable_coeff : variable_coeffs) {
201  SetCoefficient(ct, variable_coeff.first, variable_coeff.second);
202  }
203  return ct;
204  }
205  // Adds a linear constraint and a 0/1 variable that is true iff
206  // lower_bound <= sum variable * coeff <= upper_bound,
207  // and returns the identifier of that variable.
209  int64_t lower_bound, int64_t upper_bound,
210  const std::vector<std::pair<int, double>>& weighted_variables) {
211  const int reification_ct = AddLinearConstraint(1, 1, {});
213  const int under_lower_bound = AddVariable(0, 1);
214  SetCoefficient(reification_ct, under_lower_bound, 1);
215  const int under_lower_bound_ct =
217  lower_bound - 1, weighted_variables);
218  SetEnforcementLiteral(under_lower_bound_ct, under_lower_bound);
219  }
221  const int above_upper_bound = AddVariable(0, 1);
222  SetCoefficient(reification_ct, above_upper_bound, 1);
223  const int above_upper_bound_ct = AddLinearConstraint(
225  weighted_variables);
226  SetEnforcementLiteral(above_upper_bound_ct, above_upper_bound);
227  }
228  const int within_bounds = AddVariable(0, 1);
229  SetCoefficient(reification_ct, within_bounds, 1);
230  const int within_bounds_ct =
231  AddLinearConstraint(lower_bound, upper_bound, weighted_variables);
232  SetEnforcementLiteral(within_bounds_ct, within_bounds);
233  return within_bounds;
234  }
235 };
236 
238  public:
239  explicit RoutingGlopWrapper(const glop::GlopParameters& parameters) {
240  lp_solver_.SetParameters(parameters);
241  linear_program_.SetMaximizationProblem(false);
242  }
243  void Clear() override {
244  linear_program_.Clear();
245  linear_program_.SetMaximizationProblem(false);
246  allowed_intervals_.clear();
247  }
248  int CreateNewPositiveVariable() override {
249  return linear_program_.CreateNewVariable().value();
250  }
252  int64_t upper_bound) override {
254  // When variable upper bounds are greater than this threshold, precision
255  // issues arise in GLOP. In this case we are just going to suppose that
256  // these high bound values are infinite and not set the upper bound.
257  const int64_t kMaxValue = 1e10;
258  const double lp_min = lower_bound;
259  const double lp_max =
260  (upper_bound > kMaxValue) ? glop::kInfinity : upper_bound;
261  if (lp_min <= lp_max) {
262  linear_program_.SetVariableBounds(glop::ColIndex(index), lp_min, lp_max);
263  return true;
264  }
265  // The linear_program would not be feasible, and it cannot handle the
266  // lp_min > lp_max case, so we must detect infeasibility here.
267  return false;
268  }
269  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
270  const std::vector<int64_t>& ends) override {
271  // TODO(user): Investigate if we can avoid rebuilding the interval list
272  // each time (we could keep a reference to the forbidden interval list in
273  // RoutingDimension but we would need to store cumul offsets and use them
274  // when checking intervals).
275  allowed_intervals_[index] =
276  absl::make_unique<SortedDisjointIntervalList>(starts, ends);
277  }
278  int64_t GetVariableLowerBound(int index) const override {
279  return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
280  }
281  void SetObjectiveCoefficient(int index, double coefficient) override {
282  linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
283  }
284  double GetObjectiveCoefficient(int index) const override {
285  return linear_program_.objective_coefficients()[glop::ColIndex(index)];
286  }
287  void ClearObjective() override {
288  for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
289  linear_program_.SetObjectiveCoefficient(i, 0);
290  }
291  }
292  int NumVariables() const override {
293  return linear_program_.num_variables().value();
294  }
295  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
296  const glop::RowIndex ct = linear_program_.CreateNewConstraint();
297  linear_program_.SetConstraintBounds(
298  ct,
300  : lower_bound,
302  : upper_bound);
303  return ct.value();
304  }
305  void SetCoefficient(int ct, int index, double coefficient) override {
306  linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
307  coefficient);
308  }
309  bool IsCPSATSolver() override { return false; }
310  void AddMaximumConstraint(int max_var, std::vector<int> vars) override{};
311  void AddProductConstraint(int product_var, std::vector<int> vars) override{};
312  void SetEnforcementLiteral(int ct, int condition) override{};
313  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
314  lp_solver_.GetMutableParameters()->set_max_time_in_seconds(
315  absl::ToDoubleSeconds(duration_limit));
316 
317  // Because we construct the lp one constraint at a time and we never call
318  // SetCoefficient() on the same variable twice for a constraint, we know
319  // that the columns do not contain duplicates and are already ordered by
320  // constraint so we do not need to call linear_program->CleanUp() which can
321  // be costly. Note that the assumptions are DCHECKed() in the call below.
322  linear_program_.NotifyThatColumnsAreClean();
323  VLOG(2) << linear_program_.Dump();
324  const glop::ProblemStatus status = lp_solver_.Solve(linear_program_);
325  if (status != glop::ProblemStatus::OPTIMAL &&
326  status != glop::ProblemStatus::IMPRECISE) {
327  linear_program_.Clear();
329  }
330  for (const auto& allowed_interval : allowed_intervals_) {
331  const double value_double = GetValue(allowed_interval.first);
332  const int64_t value =
333  (value_double >= std::numeric_limits<int64_t>::max())
335  : MathUtil::FastInt64Round(value_double);
336  const SortedDisjointIntervalList* const interval_list =
337  allowed_interval.second.get();
338  const auto it = interval_list->FirstIntervalGreaterOrEqual(value);
339  if (it == interval_list->end() || value < it->start) {
341  }
342  }
344  }
345  int64_t GetObjectiveValue() const override {
346  return MathUtil::FastInt64Round(lp_solver_.GetObjectiveValue());
347  }
348  double GetValue(int index) const override {
349  return lp_solver_.variable_values()[glop::ColIndex(index)];
350  }
351  bool SolutionIsInteger() const override {
352  return linear_program_.SolutionIsInteger(lp_solver_.variable_values(),
353  /*absolute_tolerance*/ 1e-3);
354  }
355 
356  private:
357  glop::LinearProgram linear_program_;
358  glop::LPSolver lp_solver_;
359  absl::flat_hash_map<int, std::unique_ptr<SortedDisjointIntervalList>>
360  allowed_intervals_;
361 };
362 
364  public:
365  RoutingCPSatWrapper() : objective_offset_(0), first_constraint_to_offset_(0) {
366  parameters_.set_num_search_workers(1);
367  // Keeping presolve but with 0 iterations; as of 11/2019 it is
368  // significantly faster than both full presolve and no presolve.
369  parameters_.set_cp_model_presolve(true);
370  parameters_.set_max_presolve_iterations(0);
371  parameters_.set_catch_sigint_signal(false);
372  parameters_.set_mip_max_bound(1e8);
373  parameters_.set_search_branching(sat::SatParameters::LP_SEARCH);
374  }
375  ~RoutingCPSatWrapper() override {}
376  void Clear() override {
377  model_.Clear();
378  response_.Clear();
379  objective_coefficients_.clear();
380  objective_offset_ = 0;
381  variable_offset_.clear();
382  constraint_offset_.clear();
383  first_constraint_to_offset_ = 0;
384  }
385  int CreateNewPositiveVariable() override {
386  const int index = model_.variables_size();
387  if (index >= variable_offset_.size()) {
388  variable_offset_.resize(index + 1, 0);
389  }
390  sat::IntegerVariableProto* const variable = model_.add_variables();
391  variable->add_domain(0);
392  variable->add_domain(static_cast<int64_t>(parameters_.mip_max_bound()));
393  return index;
394  }
396  int64_t upper_bound) override {
398  // TODO(user): Find whether there is a way to make the offsetting
399  // system work with other CP-SAT constraints than linear constraints.
400  // variable_offset_[index] = lower_bound;
401  variable_offset_[index] = 0;
402  const int64_t offset_upper_bound =
403  std::min<int64_t>(CapSub(upper_bound, variable_offset_[index]),
404  parameters_.mip_max_bound());
405  const int64_t offset_lower_bound =
406  CapSub(lower_bound, variable_offset_[index]);
407  if (offset_lower_bound > offset_upper_bound) return false;
408  sat::IntegerVariableProto* const variable = model_.mutable_variables(index);
409  variable->set_domain(0, offset_lower_bound);
410  variable->set_domain(1, offset_upper_bound);
411  return true;
412  }
413  void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
414  const std::vector<int64_t>& ends) override {
415  DCHECK_EQ(starts.size(), ends.size());
416  const int ct = CreateNewConstraint(1, 1);
417  for (int i = 0; i < starts.size(); ++i) {
418  const int variable = CreateNewPositiveVariable();
419  SetVariableBounds(variable, 0, 1);
420  SetCoefficient(ct, variable, 1);
421  const int window_ct = CreateNewConstraint(starts[i], ends[i]);
422  SetCoefficient(window_ct, index, 1);
423  model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
424  }
425  }
426  int64_t GetVariableLowerBound(int index) const override {
427  return CapAdd(model_.variables(index).domain(0), variable_offset_[index]);
428  }
429  void SetObjectiveCoefficient(int index, double coefficient) override {
430  // TODO(user): Check variable bounds are never set after setting the
431  // objective coefficient.
432  if (index >= objective_coefficients_.size()) {
433  objective_coefficients_.resize(index + 1, 0);
434  }
435  objective_coefficients_[index] = coefficient;
436  sat::CpObjectiveProto* const objective = model_.mutable_objective();
437  objective->add_vars(index);
438  objective->add_coeffs(coefficient);
439  objective_offset_ += coefficient * variable_offset_[index];
440  }
441  double GetObjectiveCoefficient(int index) const override {
442  return (index < objective_coefficients_.size())
443  ? objective_coefficients_[index]
444  : 0;
445  }
446  void ClearObjective() override {
447  model_.mutable_objective()->Clear();
448  objective_offset_ = 0;
449  }
450  int NumVariables() const override { return model_.variables_size(); }
451  int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
452  const int ct_index = model_.constraints_size();
453  if (ct_index >= constraint_offset_.size()) {
454  constraint_offset_.resize(ct_index + 1, 0);
455  }
456  sat::LinearConstraintProto* const ct =
457  model_.add_constraints()->mutable_linear();
458  ct->add_domain(lower_bound);
459  ct->add_domain(upper_bound);
460  return ct_index;
461  }
462  void SetCoefficient(int ct_index, int index, double coefficient) override {
463  // TODO(user): Check variable bounds are never set after setting the
464  // variable coefficient.
465  sat::LinearConstraintProto* const ct =
466  model_.mutable_constraints(ct_index)->mutable_linear();
467  ct->add_vars(index);
468  ct->add_coeffs(coefficient);
469  constraint_offset_[ct_index] =
470  CapAdd(constraint_offset_[ct_index],
471  CapProd(variable_offset_[index], coefficient));
472  }
473  bool IsCPSATSolver() override { return true; }
474  void AddMaximumConstraint(int max_var, std::vector<int> vars) override {
475  sat::LinearArgumentProto* const ct =
476  model_.add_constraints()->mutable_lin_max();
477  ct->mutable_target()->add_vars(max_var);
478  ct->mutable_target()->add_coeffs(1);
479  for (const int var : vars) {
480  sat::LinearExpressionProto* const expr = ct->add_exprs();
481  expr->add_vars(var);
482  expr->add_coeffs(1);
483  }
484  }
485  void AddProductConstraint(int product_var, std::vector<int> vars) override {
486  sat::IntegerArgumentProto* const ct =
487  model_.add_constraints()->mutable_int_prod();
488  ct->set_target(product_var);
489  for (const int var : vars) {
490  ct->add_vars(var);
491  }
492  }
493  void SetEnforcementLiteral(int ct, int condition) override {
494  DCHECK_LT(ct, constraint_offset_.size());
495  model_.mutable_constraints(ct)->add_enforcement_literal(condition);
496  }
497  DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
498  // Set constraint offsets
499  for (int ct_index = first_constraint_to_offset_;
500  ct_index < constraint_offset_.size(); ++ct_index) {
501  if (!model_.mutable_constraints(ct_index)->has_linear()) continue;
502  sat::LinearConstraintProto* const ct =
503  model_.mutable_constraints(ct_index)->mutable_linear();
504  ct->set_domain(0, CapSub(ct->domain(0), constraint_offset_[ct_index]));
505  ct->set_domain(1, CapSub(ct->domain(1), constraint_offset_[ct_index]));
506  }
507  first_constraint_to_offset_ = constraint_offset_.size();
508  parameters_.set_max_time_in_seconds(absl::ToDoubleSeconds(duration_limit));
509  VLOG(2) << model_.DebugString();
510  if (hint_.vars_size() == model_.variables_size()) {
511  *model_.mutable_solution_hint() = hint_;
512  }
514  model.Add(sat::NewSatParameters(parameters_));
515  response_ = sat::SolveCpModel(model_, &model);
516  VLOG(2) << response_.DebugString();
517  if (response_.status() == sat::CpSolverStatus::OPTIMAL ||
518  (response_.status() == sat::CpSolverStatus::FEASIBLE &&
519  !model_.has_objective())) {
520  hint_.Clear();
521  for (int i = 0; i < response_.solution_size(); ++i) {
522  hint_.add_vars(i);
523  hint_.add_values(response_.solution(i));
524  }
526  }
528  }
529  int64_t GetObjectiveValue() const override {
530  return MathUtil::FastInt64Round(response_.objective_value() +
531  objective_offset_);
532  }
533  double GetValue(int index) const override {
534  return response_.solution(index) + variable_offset_[index];
535  }
536  bool SolutionIsInteger() const override { return true; }
537 
538  private:
539  sat::CpModelProto model_;
540  sat::CpSolverResponse response_;
541  sat::SatParameters parameters_;
542  std::vector<double> objective_coefficients_;
543  double objective_offset_;
544  std::vector<int64_t> variable_offset_;
545  std::vector<int64_t> constraint_offset_;
546  int first_constraint_to_offset_;
547  sat::PartialVariableAssignment hint_;
548 };
549 
550 // Utility class used in Local/GlobalDimensionCumulOptimizer to set the linear
551 // solver constraints and solve the problem.
553  public:
555  bool use_precedence_propagator);
556 
557  // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
558  // and "cost" parameters are null, we don't optimize the cost and stop at the
559  // first feasible solution in the linear solver (since in this case only
560  // feasibility is of interest).
562  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
563  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
564  std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
565  bool clear_lp = true);
566 
567  bool Optimize(const std::function<int64_t(int64_t)>& next_accessor,
569  std::vector<int64_t>* cumul_values,
570  std::vector<int64_t>* break_values, int64_t* cost,
571  int64_t* transit_cost, bool clear_lp = true);
572 
573  bool OptimizeAndPack(const std::function<int64_t(int64_t)>& next_accessor,
575  std::vector<int64_t>* cumul_values,
576  std::vector<int64_t>* break_values);
577 
579  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
580  RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
581  std::vector<int64_t>* break_values);
582 
583  const RoutingDimension* dimension() const { return dimension_; }
584 
585  private:
586  // Initializes the containers and given solver. Must be called prior to
587  // setting any contraints and solving.
588  void InitOptimizer(RoutingLinearSolverWrapper* solver);
589 
590  // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
591  // in current_route_[min|max]_cumuls_ respectively.
592  // If the propagator_ is not null, uses the bounds tightened by the
593  // propagator.
594  // Otherwise, the bounds are computed by going over the nodes on the route
595  // using the CP bounds, and the fixed transits are used to tighten them.
596  bool ComputeRouteCumulBounds(const std::vector<int64_t>& route,
597  const std::vector<int64_t>& fixed_transits,
598  int64_t cumul_offset);
599 
600  // Sets the constraints for all nodes on "vehicle"'s route according to
601  // "next_accessor". If optimize_costs is true, also sets the objective
602  // coefficients for the LP.
603  // Returns false if some infeasibility was detected, true otherwise.
604  bool SetRouteCumulConstraints(
605  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
606  int64_t cumul_offset, bool optimize_costs,
607  RoutingLinearSolverWrapper* solver, int64_t* route_transit_cost,
608  int64_t* route_cost_offset);
609 
610  // Sets the global constraints on the dimension, and adds global objective
611  // cost coefficients if optimize_costs is true.
612  // NOTE: When called, the call to this function MUST come after
613  // SetRouteCumulConstraints() has been called on all routes, so that
614  // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
615  // initialized.
616  void SetGlobalConstraints(bool optimize_costs,
618 
619  void SetValuesFromLP(const std::vector<int>& lp_variables, int64_t offset,
621  std::vector<int64_t>* lp_values);
622 
623  // This function packs the routes of the given vehicles while keeping the cost
624  // of the LP lower than its current (supposed optimal) objective value.
625  // It does so by setting the current objective variables' coefficient to 0 and
626  // setting the coefficient of the route ends to 1, to first minimize the route
627  // ends' cumuls, and then maximizes the starts' cumuls without increasing the
628  // ends.
629  DimensionSchedulingStatus PackRoutes(std::vector<int> vehicles,
631 
632  std::unique_ptr<CumulBoundsPropagator> propagator_;
633  std::vector<int64_t> current_route_min_cumuls_;
634  std::vector<int64_t> current_route_max_cumuls_;
635  const RoutingDimension* const dimension_;
636  // Scheduler variables for current route cumuls and for all nodes cumuls.
637  std::vector<int> current_route_cumul_variables_;
638  std::vector<int> index_to_cumul_variable_;
639  // Scheduler variables for current route breaks and all vehicle breaks.
640  // There are two variables for each break: start and end.
641  // current_route_break_variables_ has variables corresponding to
642  // break[0] start, break[0] end, break[1] start, break[1] end, etc.
643  std::vector<int> current_route_break_variables_;
644  // Vector all_break_variables contains the break variables of all vehicles,
645  // in the same format as current_route_break_variables.
646  // It is the concatenation of break variables of vehicles in [0, #vehicles).
647  std::vector<int> all_break_variables_;
648  // Allows to retrieve break variables of a given vehicle: those go from
649  // all_break_variables_[vehicle_to_all_break_variables_offset_[vehicle]] to
650  // all_break_variables[vehicle_to_all_break_variables_offset_[vehicle+1]-1].
651  std::vector<int> vehicle_to_all_break_variables_offset_;
652 
653  int max_end_cumul_;
654  int min_start_cumul_;
655  std::vector<std::pair<int64_t, int64_t>>
656  visited_pickup_delivery_indices_for_pair_;
657 };
658 
659 // Class used to compute optimal values for dimension cumuls of routes,
660 // minimizing cumul soft lower and upper bound costs, and vehicle span costs of
661 // a route.
662 // In its methods, next_accessor is a callback returning the next node of a
663 // given node on a route.
665  public:
668  RoutingSearchParameters::SchedulingSolver solver_type);
669 
670  // If feasible, computes the optimal cost of the route performed by a vehicle,
671  // minimizing cumul soft lower and upper bound costs and vehicle span costs,
672  // and stores it in "optimal_cost" (if not null).
673  // Returns true iff the route respects all constraints.
675  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
676  int64_t* optimal_cost);
677 
678  // Same as ComputeRouteCumulCost, but the cost computed does not contain
679  // the part of the vehicle span cost due to fixed transits.
681  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
682  int64_t* optimal_cost_without_transits);
683 
684  // If feasible, computes the optimal values for cumul and break variables
685  // of the route performed by a vehicle, minimizing cumul soft lower, upper
686  // bound costs and vehicle span costs, stores them in "optimal_cumuls"
687  // (if not null), and optimal_breaks, and returns true.
688  // Returns false if the route is not feasible.
690  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
691  std::vector<int64_t>* optimal_cumuls,
692  std::vector<int64_t>* optimal_breaks);
693 
694  // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
695  // the route, such that the cost remains the same, the cumul of route end is
696  // minimized, and then the cumul of the start of the route is maximized.
698  int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
699  std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
700 
701  const RoutingDimension* dimension() const {
702  return optimizer_core_.dimension();
703  }
704 
705  private:
706  std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
707  DimensionCumulOptimizerCore optimizer_core_;
708 };
709 
711  public:
713  // If feasible, computes the optimal cost of the entire model with regards to
714  // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
715  // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
716  // (if not null).
717  // Returns true iff all the constraints can be respected.
719  const std::function<int64_t(int64_t)>& next_accessor,
720  int64_t* optimal_cost_without_transits);
721  // If feasible, computes the optimal values for cumul and break variables,
722  // minimizing cumul soft lower/upper bound costs and vehicle/global span
723  // costs, stores them in "optimal_cumuls" (if not null) and optimal breaks,
724  // and returns true.
725  // Returns false if the routes are not feasible.
726  bool ComputeCumuls(const std::function<int64_t(int64_t)>& next_accessor,
727  std::vector<int64_t>* optimal_cumuls,
728  std::vector<int64_t>* optimal_breaks);
729 
730  // Returns true iff the routes resulting from the next_accessor are feasible
731  // wrt the constraints on the optimizer_core_.dimension()'s cumuls.
732  bool IsFeasible(const std::function<int64_t(int64_t)>& next_accessor);
733 
734  // Similar to ComputeCumuls, but also tries to pack the cumul values on all
735  // routes, such that the cost remains the same, the cumuls of route ends are
736  // minimized, and then the cumuls of the starts of the routes are maximized.
737  bool ComputePackedCumuls(const std::function<int64_t(int64_t)>& next_accessor,
738  std::vector<int64_t>* packed_cumuls,
739  std::vector<int64_t>* packed_breaks);
740 
741  const RoutingDimension* dimension() const {
742  return optimizer_core_.dimension();
743  }
744 
745  private:
746  std::unique_ptr<RoutingLinearSolverWrapper> solver_;
747  DimensionCumulOptimizerCore optimizer_core_;
748 };
749 
750 } // namespace operations_research
751 
752 #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
#define VLOG(verboselevel)
Definition: base/logging.h:986
const RoutingDimension & dimension() const
bool PropagateCumulBounds(const std::function< int64_t(int64_t)> &next_accessor, int64_t cumul_offset)
CumulBoundsPropagator(const RoutingDimension *dimension)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
bool OptimizeAndPack(const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
DimensionSchedulingStatus OptimizeAndPackSingleRoute(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)
bool Optimize(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)
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)
bool ComputeCumulCostWithoutFixedTransits(const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension)
bool IsFeasible(const std::function< int64_t(int64_t)> &next_accessor)
bool ComputeCumuls(const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
bool ComputePackedCumuls(const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks)
DimensionSchedulingStatus ComputeRouteCumulCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost)
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)
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 ComputePackedRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks)
static int64_t FastInt64Round(double x)
Definition: mathutil.h:138
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
void SetEnforcementLiteral(int ct, int condition) override
double GetObjectiveCoefficient(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 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:2374
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
void AddMaximumConstraint(int max_var, std::vector< int > vars) override
void AddProductConstraint(int product_var, std::vector< int > vars) override
void SetEnforcementLiteral(int ct, int condition) override
double GetObjectiveCoefficient(int index) const override
RoutingGlopWrapper(const glop::GlopParameters &parameters)
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 SetObjectiveCoefficient(int index, double coefficient) override
int64_t GetVariableLowerBound(int index) const override
virtual void SetCoefficient(int ct, int index, double coefficient)=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 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 int64_t GetVariableLowerBound(int index) const =0
virtual void AddMaximumConstraint(int max_var, std::vector< int > vars)=0
This class represents a sorted list of disjoint, closed intervals.
Iterator FirstIntervalGreaterOrEqual(int64_t value) const
Returns an iterator to either:
GlopParameters * GetMutableParameters()
Definition: lp_solver.cc:130
const DenseRow & variable_values() const
Definition: lp_solver.h:100
Fractional GetObjectiveValue() const
Definition: lp_solver.cc:487
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:132
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:116
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:249
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:317
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:229
const DenseRow & objective_coefficients() const
Definition: lp_data.h:223
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:309
bool SolutionIsInteger(const DenseRow &solution, Fractional absolute_tolerance) const
Definition: lp_data.cc:517
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:326
void SetMaximizationProblem(bool maximize)
Definition: lp_data.cc:343
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
SatParameters parameters
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
GRBmodel * model
const double kInfinity
Definition: lp_types.h:84
std::function< SatParameters(Model *)> NewSatParameters(const std::string &params)
Creates parameters for the solver, which you can add to the model with.
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
int index
Definition: pack.cc:509
int64_t coefficient
int64_t cost
int64_t head