OR-Tools  9.1
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"
41#include "ortools/sat/model.h"
45
46namespace 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.
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 AddObjectiveConstraint() = 0;
178 virtual void AddMaximumConstraint(int max_var, std::vector<int> vars) = 0;
179 virtual void AddProductConstraint(int product_var, std::vector<int> vars) = 0;
180 virtual void SetEnforcementLiteral(int ct, int condition) = 0;
181 virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit) = 0;
182 virtual int64_t GetObjectiveValue() const = 0;
183 virtual double GetValue(int index) const = 0;
184 virtual bool SolutionIsInteger() const = 0;
185
186 // Adds a variable with bounds [lower_bound, upper_bound].
187 int AddVariable(int64_t lower_bound, int64_t upper_bound) {
189 const int variable = CreateNewPositiveVariable();
191 return variable;
192 }
193 // Adds a linear constraint, enforcing
194 // lower_bound <= sum variable * coeff <= upper_bound,
195 // and returns the identifier of that constraint.
197 int64_t lower_bound, int64_t upper_bound,
198 const std::vector<std::pair<int, double>>& variable_coeffs) {
201 for (const auto& variable_coeff : variable_coeffs) {
202 SetCoefficient(ct, variable_coeff.first, variable_coeff.second);
203 }
204 return ct;
205 }
206 // Adds a linear constraint and a 0/1 variable that is true iff
207 // lower_bound <= sum variable * coeff <= upper_bound,
208 // and returns the identifier of that variable.
210 int64_t lower_bound, int64_t upper_bound,
211 const std::vector<std::pair<int, double>>& weighted_variables) {
212 const int reification_ct = AddLinearConstraint(1, 1, {});
214 const int under_lower_bound = AddVariable(0, 1);
215 SetCoefficient(reification_ct, under_lower_bound, 1);
216 const int under_lower_bound_ct =
218 lower_bound - 1, weighted_variables);
219 SetEnforcementLiteral(under_lower_bound_ct, under_lower_bound);
220 }
222 const int above_upper_bound = AddVariable(0, 1);
223 SetCoefficient(reification_ct, above_upper_bound, 1);
224 const int above_upper_bound_ct = AddLinearConstraint(
226 weighted_variables);
227 SetEnforcementLiteral(above_upper_bound_ct, above_upper_bound);
228 }
229 const int within_bounds = AddVariable(0, 1);
230 SetCoefficient(reification_ct, within_bounds, 1);
231 const int within_bounds_ct =
232 AddLinearConstraint(lower_bound, upper_bound, weighted_variables);
233 SetEnforcementLiteral(within_bounds_ct, within_bounds);
234 return within_bounds;
235 }
236};
237
239 public:
241 : is_relaxation_(is_relaxation) {
242 lp_solver_.SetParameters(parameters);
243 linear_program_.SetMaximizationProblem(false);
244 }
245 void Clear() override {
246 linear_program_.Clear();
247 linear_program_.SetMaximizationProblem(false);
248 allowed_intervals_.clear();
249 }
251 return linear_program_.CreateNewVariable().value();
252 }
254 int64_t upper_bound) override {
256 // When variable upper bounds are greater than this threshold, precision
257 // issues arise in GLOP. In this case we are just going to suppose that
258 // these high bound values are infinite and not set the upper bound.
259 const int64_t kMaxValue = 1e10;
260 const double lp_min = lower_bound;
261 const double lp_max =
262 (upper_bound > kMaxValue) ? glop::kInfinity : upper_bound;
263 if (lp_min <= lp_max) {
264 linear_program_.SetVariableBounds(glop::ColIndex(index), lp_min, lp_max);
265 return true;
266 }
267 // The linear_program would not be feasible, and it cannot handle the
268 // lp_min > lp_max case, so we must detect infeasibility here.
269 return false;
270 }
271 void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
272 const std::vector<int64_t>& ends) override {
273 // TODO(user): Investigate if we can avoid rebuilding the interval list
274 // each time (we could keep a reference to the forbidden interval list in
275 // RoutingDimension but we would need to store cumul offsets and use them
276 // when checking intervals).
277 allowed_intervals_[index] =
278 absl::make_unique<SortedDisjointIntervalList>(starts, ends);
279 }
280 int64_t GetVariableLowerBound(int index) const override {
281 return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
282 }
283 void SetObjectiveCoefficient(int index, double coefficient) override {
284 linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
285 }
286 double GetObjectiveCoefficient(int index) const override {
287 return linear_program_.objective_coefficients()[glop::ColIndex(index)];
288 }
289 void ClearObjective() override {
290 for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
291 linear_program_.SetObjectiveCoefficient(i, 0);
292 }
293 }
294 int NumVariables() const override {
295 return linear_program_.num_variables().value();
296 }
297 int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
298 const glop::RowIndex ct = linear_program_.CreateNewConstraint();
299 linear_program_.SetConstraintBounds(
300 ct,
302 : lower_bound,
304 : upper_bound);
305 return ct.value();
306 }
307 void SetCoefficient(int ct, int index, double coefficient) override {
308 linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
310 }
311 bool IsCPSATSolver() override { return false; }
312 void AddObjectiveConstraint() override {
313 const int ct = CreateNewConstraint(0, GetObjectiveValue());
314 for (int variable = 0; variable < NumVariables(); variable++) {
315 const double coefficient = GetObjectiveCoefficient(variable);
316 if (coefficient != 0) {
317 SetCoefficient(ct, variable, coefficient);
318 }
319 }
320 }
321 void AddMaximumConstraint(int max_var, std::vector<int> vars) override {}
322 void AddProductConstraint(int product_var, std::vector<int> vars) override {}
323 void SetEnforcementLiteral(int ct, int condition) override{};
324 DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
326 absl::ToDoubleSeconds(duration_limit));
327
328 // Because we construct the lp one constraint at a time and we never call
329 // SetCoefficient() on the same variable twice for a constraint, we know
330 // that the columns do not contain duplicates and are already ordered by
331 // constraint so we do not need to call linear_program->CleanUp() which can
332 // be costly. Note that the assumptions are DCHECKed() in the call below.
333 linear_program_.NotifyThatColumnsAreClean();
334 VLOG(2) << linear_program_.Dump();
335 const glop::ProblemStatus status = lp_solver_.Solve(linear_program_);
336 if (status != glop::ProblemStatus::OPTIMAL &&
338 linear_program_.Clear();
340 }
341 if (is_relaxation_) {
343 }
344 for (const auto& allowed_interval : allowed_intervals_) {
345 const double value_double = GetValue(allowed_interval.first);
346 const int64_t value =
347 (value_double >= std::numeric_limits<int64_t>::max())
349 : MathUtil::FastInt64Round(value_double);
350 const SortedDisjointIntervalList* const interval_list =
351 allowed_interval.second.get();
352 const auto it = interval_list->FirstIntervalGreaterOrEqual(value);
353 if (it == interval_list->end() || value < it->start) {
355 }
356 }
358 }
359 int64_t GetObjectiveValue() const override {
360 return MathUtil::FastInt64Round(lp_solver_.GetObjectiveValue());
361 }
362 double GetValue(int index) const override {
363 return lp_solver_.variable_values()[glop::ColIndex(index)];
364 }
365 bool SolutionIsInteger() const override {
366 return linear_program_.SolutionIsInteger(lp_solver_.variable_values(),
367 /*absolute_tolerance*/ 1e-3);
368 }
369
370 private:
371 const bool is_relaxation_;
372 glop::LinearProgram linear_program_;
373 glop::LPSolver lp_solver_;
374 absl::flat_hash_map<int, std::unique_ptr<SortedDisjointIntervalList>>
375 allowed_intervals_;
376};
377
379 public:
380 RoutingCPSatWrapper() : first_constraint_to_offset_(0) {
381 parameters_.set_num_search_workers(1);
382 // Keeping presolve but with 0 iterations; as of 11/2019 it is
383 // significantly faster than both full presolve and no presolve.
384 parameters_.set_cp_model_presolve(true);
385 parameters_.set_max_presolve_iterations(0);
386 parameters_.set_catch_sigint_signal(false);
387 parameters_.set_mip_max_bound(1e8);
389 }
391 void Clear() override {
392 model_.Clear();
393 response_.Clear();
394 objective_coefficients_.clear();
395 variable_offset_.clear();
396 constraint_offset_.clear();
397 first_constraint_to_offset_ = 0;
398 }
400 const int index = model_.variables_size();
401 if (index >= variable_offset_.size()) {
402 variable_offset_.resize(index + 1, 0);
403 }
404 sat::IntegerVariableProto* const variable = model_.add_variables();
405 variable->add_domain(0);
406 variable->add_domain(static_cast<int64_t>(parameters_.mip_max_bound()));
407 return index;
408 }
410 int64_t upper_bound) override {
412 // TODO(user): Find whether there is a way to make the offsetting
413 // system work with other CP-SAT constraints than linear constraints.
414 // variable_offset_[index] = lower_bound;
415 variable_offset_[index] = 0;
416 const int64_t offset_upper_bound =
417 std::min<int64_t>(CapSub(upper_bound, variable_offset_[index]),
418 parameters_.mip_max_bound());
419 const int64_t offset_lower_bound =
420 CapSub(lower_bound, variable_offset_[index]);
421 if (offset_lower_bound > offset_upper_bound) return false;
422 sat::IntegerVariableProto* const variable = model_.mutable_variables(index);
423 variable->set_domain(0, offset_lower_bound);
424 variable->set_domain(1, offset_upper_bound);
425 return true;
426 }
427 void SetVariableDisjointBounds(int index, const std::vector<int64_t>& starts,
428 const std::vector<int64_t>& ends) override {
429 DCHECK_EQ(starts.size(), ends.size());
430 const int ct = CreateNewConstraint(1, 1);
431 for (int i = 0; i < starts.size(); ++i) {
432 const int variable = CreateNewPositiveVariable();
433 SetVariableBounds(variable, 0, 1);
434 SetCoefficient(ct, variable, 1);
435 const int window_ct = CreateNewConstraint(starts[i], ends[i]);
436 SetCoefficient(window_ct, index, 1);
437 model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
438 }
439 }
440 int64_t GetVariableLowerBound(int index) const override {
441 return CapAdd(model_.variables(index).domain(0), variable_offset_[index]);
442 }
443 void SetObjectiveCoefficient(int index, double coefficient) override {
444 // TODO(user): Check variable bounds are never set after setting the
445 // objective coefficient.
446 if (index >= objective_coefficients_.size()) {
447 objective_coefficients_.resize(index + 1, 0);
448 }
449 objective_coefficients_[index] = coefficient;
450 sat::CpObjectiveProto* const objective = model_.mutable_objective();
451 objective->add_vars(index);
452 objective->add_coeffs(coefficient);
453 objective->set_offset(objective->offset() +
454 coefficient * variable_offset_[index]);
455 }
456 double GetObjectiveCoefficient(int index) const override {
457 return (index < objective_coefficients_.size())
458 ? objective_coefficients_[index]
459 : 0;
460 }
461 void ClearObjective() override { model_.mutable_objective()->Clear(); }
462 int NumVariables() const override { return model_.variables_size(); }
463 int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound) override {
464 const int ct_index = model_.constraints_size();
465 if (ct_index >= constraint_offset_.size()) {
466 constraint_offset_.resize(ct_index + 1, 0);
467 }
470 ct->add_domain(lower_bound);
471 ct->add_domain(upper_bound);
472 return ct_index;
473 }
474 void SetCoefficient(int ct_index, int index, double coefficient) override {
475 // TODO(user): Check variable bounds are never set after setting the
476 // variable coefficient.
478 model_.mutable_constraints(ct_index)->mutable_linear();
479 ct->add_vars(index);
480 ct->add_coeffs(coefficient);
481 constraint_offset_[ct_index] =
482 CapAdd(constraint_offset_[ct_index],
483 CapProd(variable_offset_[index], coefficient));
484 }
485 bool IsCPSATSolver() override { return true; }
486 void AddObjectiveConstraint() override {
487 const double scaling_factor =
488 model_.has_objective() && model_.objective().scaling_factor() != 0
489 ? model_.objective().scaling_factor()
490 : 1;
491 const int ct =
492 CreateNewConstraint(0, response_.objective_value() / scaling_factor -
493 model_.objective().offset());
494 const sat::CpObjectiveProto& objective = model_.objective();
495 for (int i = 0; i < objective.vars_size(); ++i) {
496 SetCoefficient(ct, objective.vars(i), objective.coeffs(i));
497 }
498 }
499 void AddMaximumConstraint(int max_var, std::vector<int> vars) override {
502 ct->mutable_target()->add_vars(max_var);
503 ct->mutable_target()->add_coeffs(1);
504 for (const int var : vars) {
505 sat::LinearExpressionProto* const expr = ct->add_exprs();
506 expr->add_vars(var);
507 expr->add_coeffs(1);
508 }
509 }
510 void AddProductConstraint(int product_var, std::vector<int> vars) override {
513 ct->set_target(product_var);
514 for (const int var : vars) {
515 ct->add_vars(var);
516 }
517 }
518 void SetEnforcementLiteral(int ct, int condition) override {
519 DCHECK_LT(ct, constraint_offset_.size());
521 }
522 DimensionSchedulingStatus Solve(absl::Duration duration_limit) override {
523 // Set constraint offsets
524 for (int ct_index = first_constraint_to_offset_;
525 ct_index < constraint_offset_.size(); ++ct_index) {
526 if (!model_.mutable_constraints(ct_index)->has_linear()) continue;
528 model_.mutable_constraints(ct_index)->mutable_linear();
529 ct->set_domain(0, CapSub(ct->domain(0), constraint_offset_[ct_index]));
530 ct->set_domain(1, CapSub(ct->domain(1), constraint_offset_[ct_index]));
531 }
532 first_constraint_to_offset_ = constraint_offset_.size();
533
534 // Scale the objective
535 sat::CpObjectiveProto* const objective = model_.mutable_objective();
536 int64_t gcd(0);
537 for (int64_t coeff : objective->coeffs()) {
538 gcd = MathUtil::GCD64(gcd, std::abs(coeff));
539 }
540 if (gcd > 1) {
541 for (int i = 0; i < objective->coeffs_size(); ++i) {
542 objective->set_coeffs(i, objective->coeffs(i) / gcd);
543 }
544 objective->set_offset(objective->offset() / gcd);
545 objective->set_scaling_factor(gcd);
546 }
547
548 parameters_.set_max_time_in_seconds(absl::ToDoubleSeconds(duration_limit));
549 VLOG(2) << model_.DebugString();
550 if (hint_.vars_size() == model_.variables_size()) {
551 *model_.mutable_solution_hint() = hint_;
552 }
554 model.Add(sat::NewSatParameters(parameters_));
555 response_ = sat::SolveCpModel(model_, &model);
556 VLOG(2) << response_.DebugString();
557 if (response_.status() == sat::CpSolverStatus::OPTIMAL ||
558 (response_.status() == sat::CpSolverStatus::FEASIBLE &&
559 !model_.has_objective())) {
560 hint_.Clear();
561 for (int i = 0; i < response_.solution_size(); ++i) {
562 hint_.add_vars(i);
563 hint_.add_values(response_.solution(i));
564 }
566 }
568 }
569 int64_t GetObjectiveValue() const override {
570 return MathUtil::FastInt64Round(response_.objective_value());
571 }
572 double GetValue(int index) const override {
573 return response_.solution(index) + variable_offset_[index];
574 }
575 bool SolutionIsInteger() const override { return true; }
576
577 private:
578 sat::CpModelProto model_;
579 sat::CpSolverResponse response_;
580 sat::SatParameters parameters_;
581 std::vector<double> objective_coefficients_;
582 std::vector<int64_t> variable_offset_;
583 std::vector<int64_t> constraint_offset_;
584 int first_constraint_to_offset_;
586};
587
588// Utility class used in Local/GlobalDimensionCumulOptimizer to set the linear
589// solver constraints and solve the problem.
591 public:
593 bool use_precedence_propagator);
594
595 // In the OptimizeSingleRoute() and Optimize() methods, if both "cumul_values"
596 // and "cost" parameters are null, we don't optimize the cost and stop at the
597 // first feasible solution in the linear solver (since in this case only
598 // feasibility is of interest).
600 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
601 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
602 std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
603 bool clear_lp = true);
604
606 const std::function<int64_t(int64_t)>& next_accessor,
607 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
608 std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
609 bool clear_lp = true);
610
612 const std::function<int64_t(int64_t)>& next_accessor,
613 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
614 std::vector<int64_t>* break_values);
615
617 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
618 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
619 std::vector<int64_t>* break_values);
620
621 const RoutingDimension* dimension() const { return dimension_; }
622
623 private:
624 // Initializes the containers and given solver. Must be called prior to
625 // setting any contraints and solving.
626 void InitOptimizer(RoutingLinearSolverWrapper* solver);
627
628 // Computes the minimum/maximum of cumuls for nodes on "route", and sets them
629 // in current_route_[min|max]_cumuls_ respectively.
630 // If the propagator_ is not null, uses the bounds tightened by the
631 // propagator.
632 // Otherwise, the bounds are computed by going over the nodes on the route
633 // using the CP bounds, and the fixed transits are used to tighten them.
634 bool ComputeRouteCumulBounds(const std::vector<int64_t>& route,
635 const std::vector<int64_t>& fixed_transits,
636 int64_t cumul_offset);
637
638 // Sets the constraints for all nodes on "vehicle"'s route according to
639 // "next_accessor". If optimize_costs is true, also sets the objective
640 // coefficients for the LP.
641 // Returns false if some infeasibility was detected, true otherwise.
642 bool SetRouteCumulConstraints(
643 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
644 int64_t cumul_offset, bool optimize_costs,
645 RoutingLinearSolverWrapper* solver, int64_t* route_transit_cost,
646 int64_t* route_cost_offset);
647
648 // Sets the global constraints on the dimension, and adds global objective
649 // cost coefficients if optimize_costs is true.
650 // NOTE: When called, the call to this function MUST come after
651 // SetRouteCumulConstraints() has been called on all routes, so that
652 // index_to_cumul_variable_ and min_start/max_end_cumul_ are correctly
653 // initialized.
654 void SetGlobalConstraints(
655 const std::function<int64_t(int64_t)>& next_accessor,
656 int64_t cumul_offset, bool optimize_costs,
658
659 void SetValuesFromLP(const std::vector<int>& lp_variables, int64_t offset,
661 std::vector<int64_t>* lp_values);
662
663 // This function packs the routes of the given vehicles while keeping the cost
664 // of the LP lower than its current (supposed optimal) objective value.
665 // It does so by setting the current objective variables' coefficient to 0 and
666 // setting the coefficient of the route ends to 1, to first minimize the route
667 // ends' cumuls, and then maximizes the starts' cumuls without increasing the
668 // ends.
669 DimensionSchedulingStatus PackRoutes(std::vector<int> vehicles,
671
672 std::unique_ptr<CumulBoundsPropagator> propagator_;
673 std::vector<int64_t> current_route_min_cumuls_;
674 std::vector<int64_t> current_route_max_cumuls_;
675 const RoutingDimension* const dimension_;
676 // Scheduler variables for current route cumuls and for all nodes cumuls.
677 std::vector<int> current_route_cumul_variables_;
678 std::vector<int> index_to_cumul_variable_;
679 // Scheduler variables for current route breaks and all vehicle breaks.
680 // There are two variables for each break: start and end.
681 // current_route_break_variables_ has variables corresponding to
682 // break[0] start, break[0] end, break[1] start, break[1] end, etc.
683 std::vector<int> current_route_break_variables_;
684 // Vector all_break_variables contains the break variables of all vehicles,
685 // in the same format as current_route_break_variables.
686 // It is the concatenation of break variables of vehicles in [0, #vehicles).
687 std::vector<int> all_break_variables_;
688 // Allows to retrieve break variables of a given vehicle: those go from
689 // all_break_variables_[vehicle_to_all_break_variables_offset_[vehicle]] to
690 // all_break_variables[vehicle_to_all_break_variables_offset_[vehicle+1]-1].
691 std::vector<int> vehicle_to_all_break_variables_offset_;
692
693 int max_end_cumul_;
694 int min_start_cumul_;
695 std::vector<std::pair<int64_t, int64_t>>
696 visited_pickup_delivery_indices_for_pair_;
697};
698
699// Class used to compute optimal values for dimension cumuls of routes,
700// minimizing cumul soft lower and upper bound costs, and vehicle span costs of
701// a route.
702// In its methods, next_accessor is a callback returning the next node of a
703// given node on a route.
705 public:
709
710 // If feasible, computes the optimal cost of the route performed by a vehicle,
711 // minimizing cumul soft lower and upper bound costs and vehicle span costs,
712 // and stores it in "optimal_cost" (if not null).
713 // Returns true iff the route respects all constraints.
715 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
716 int64_t* optimal_cost);
717
718 // Same as ComputeRouteCumulCost, but the cost computed does not contain
719 // the part of the vehicle span cost due to fixed transits.
721 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
722 int64_t* optimal_cost_without_transits);
723
724 // If feasible, computes the optimal values for cumul and break variables
725 // of the route performed by a vehicle, minimizing cumul soft lower, upper
726 // bound costs and vehicle span costs, stores them in "optimal_cumuls"
727 // (if not null), and optimal_breaks, and returns true.
728 // Returns false if the route is not feasible.
730 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
731 std::vector<int64_t>* optimal_cumuls,
732 std::vector<int64_t>* optimal_breaks);
733
734 // Similar to ComputeRouteCumuls, but also tries to pack the cumul values on
735 // the route, such that the cost remains the same, the cumul of route end is
736 // minimized, and then the cumul of the start of the route is maximized.
738 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
739 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
740
742 return optimizer_core_.dimension();
743 }
744
745 private:
746 std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
747 DimensionCumulOptimizerCore optimizer_core_;
748};
749
751 public:
755 // If feasible, computes the optimal cost of the entire model with regards to
756 // the optimizer_core_'s dimension costs, minimizing cumul soft lower/upper
757 // bound costs and vehicle/global span costs, and stores it in "optimal_cost"
758 // (if not null).
759 // Returns true iff all the constraints can be respected.
761 const std::function<int64_t(int64_t)>& next_accessor,
762 int64_t* optimal_cost_without_transits);
763 // If feasible, computes the optimal values for cumul and break variables,
764 // minimizing cumul soft lower/upper bound costs and vehicle/global span
765 // costs, stores them in "optimal_cumuls" (if not null) and optimal breaks,
766 // and returns true.
767 // Returns false if the routes are not feasible.
769 const std::function<int64_t(int64_t)>& next_accessor,
770 std::vector<int64_t>* optimal_cumuls,
771 std::vector<int64_t>* optimal_breaks);
772
773 // Similar to ComputeCumuls, but also tries to pack the cumul values on all
774 // routes, such that the cost remains the same, the cumuls of route ends are
775 // minimized, and then the cumuls of the starts of the routes are maximized.
777 const std::function<int64_t(int64_t)>& next_accessor,
778 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
779
781 return optimizer_core_.dimension();
782 }
783
784 private:
785 std::unique_ptr<RoutingLinearSolverWrapper> solver_;
786 DimensionCumulOptimizerCore optimizer_core_;
787};
788
789} // namespace operations_research
790
791#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:890
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define VLOG(verboselevel)
Definition: base/logging.h:979
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)
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)
DimensionSchedulingStatus OptimizeAndPack(const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
DimensionSchedulingStatus Optimize(const std::function< int64_t(int64_t)> &next_accessor, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, 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)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
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, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
DimensionSchedulingStatus 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
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
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:2494
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
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
virtual void SetCoefficient(int ct, int index, double coefficient)=0
int AddReifiedLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double > > &weighted_variables)
virtual double GetObjectiveCoefficient(int index) const =0
virtual void AddProductConstraint(int product_var, std::vector< int > vars)=0
int AddVariable(int64_t lower_bound, int64_t upper_bound)
virtual int64_t GetObjectiveValue() const =0
virtual int CreateNewConstraint(int64_t lower_bound, int64_t upper_bound)=0
int AddLinearConstraint(int64_t lower_bound, int64_t upper_bound, const std::vector< std::pair< int, double > > &variable_coeffs)
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
Fractional GetObjectiveValue() const
Definition: lp_solver.cc:487
const DenseRow & variable_values() const
Definition: lp_solver.h:100
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
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
const DenseRow & objective_coefficients() const
Definition: lp_data.h:223
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:326
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:229
void SetMaximizationProblem(bool maximize)
Definition: lp_data.cc:343
::operations_research::sat::LinearArgumentProto * mutable_lin_max()
::operations_research::sat::IntegerArgumentProto * mutable_int_prod()
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:9391
::operations_research::sat::LinearConstraintProto * mutable_linear()
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
::operations_research::sat::PartialVariableAssignment * mutable_solution_hint()
::operations_research::sat::ConstraintProto * mutable_constraints(int index)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
::operations_research::sat::ConstraintProto * add_constraints()
::operations_research::sat::IntegerVariableProto * add_variables()
::operations_research::sat::CpObjectiveProto * mutable_objective()
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
void add_coeffs(::PROTOBUF_NAMESPACE_ID::int64 value)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
void set_coeffs(int index, ::PROTOBUF_NAMESPACE_ID::int64 value)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
::operations_research::sat::CpSolverStatus status() const
::PROTOBUF_NAMESPACE_ID::int64 solution(int index) const
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:6904
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
Definition: cp_model.pb.h:6893
void set_domain(int index, ::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:6897
void add_coeffs(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:7124
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:7077
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
void add_values(::PROTOBUF_NAMESPACE_ID::int64 value)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_search_branching(::operations_research::sat::SatParameters_SearchBranching value)
void set_max_presolve_iterations(::PROTOBUF_NAMESPACE_ID::int32 value)
static constexpr SearchBranching LP_SEARCH
void set_num_search_workers(::PROTOBUF_NAMESPACE_ID::int32 value)
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