14#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
15#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_LP_SCHEDULING_H_
28#include "absl/container/flat_hash_map.h"
29#include "absl/memory/memory.h"
30#include "absl/time/time.h"
31#include "ortools/base/logging.h"
32#include "ortools/base/mathutil.h"
35#include "ortools/glop/lp_solver.h"
36#include "ortools/glop/parameters.pb.h"
37#include "ortools/lp_data/lp_data.h"
38#include "ortools/lp_data/lp_types.h"
39#include "ortools/sat/cp_model.pb.h"
40#include "ortools/sat/cp_model_solver.h"
41#include "ortools/sat/model.h"
42#include "ortools/sat/sat_parameters.pb.h"
43#include "ortools/util/saturated_arithmetic.h"
44#include "ortools/util/sorted_interval_list.h"
64 const std::function<int64_t(int64_t)>& next_accessor,
65 int64_t cumul_offset);
68 return propagated_bounds_[PositiveNode(index)];
72 const int64_t negated_upper_bound = propagated_bounds_[NegativeNode(index)];
73 return negated_upper_bound == std::numeric_limits<int64_t>::min()
74 ? std::numeric_limits<int64_t>::max()
75 : -negated_upper_bound;
88 static const int kNoParent;
89 static const int kParentToBePropagated;
93 int PositiveNode(
int index)
const {
return 2 * index; }
94 int NegativeNode(
int index)
const {
return 2 * index + 1; }
96 void AddNodeToQueue(
int node) {
97 if (!node_in_queue_[node]) {
98 bf_queue_.push_back(node);
99 node_in_queue_[node] =
true;
106 void AddArcs(
int first_index,
int second_index, int64_t offset);
108 bool InitializeArcsAndBounds(
109 const std::function<int64_t(int64_t)>& next_accessor,
110 int64_t cumul_offset);
112 bool UpdateCurrentLowerBoundOfNode(
int node, int64_t new_lb, int64_t offset);
114 bool DisassembleSubtree(
int source,
int target);
116 bool CleanupAndReturnFalse() {
118 for (
int node_to_cleanup : bf_queue_) {
119 node_in_queue_[node_to_cleanup] =
false;
125 const RoutingDimension& dimension_;
126 const int64_t num_nodes_;
131 std::vector<std::vector<ArcInfo>> outgoing_arcs_;
133 std::deque<int> bf_queue_;
134 std::vector<bool> node_in_queue_;
135 std::vector<int> tree_parent_node_of_;
139 std::vector<int64_t> propagated_bounds_;
142 std::vector<int> tmp_dfs_stack_;
145 std::vector<std::pair<int64_t, int64_t>>
146 visited_pickup_delivery_indices_for_pair_;
165 int64_t upper_bound) = 0;
167 const std::vector<int64_t>& starts,
168 const std::vector<int64_t>& ends) = 0;
188 CHECK_LE(lower_bound, upper_bound);
197 int64_t lower_bound, int64_t upper_bound,
198 const std::vector<std::pair<int, double>>& variable_coeffs) {
199 CHECK_LE(lower_bound, upper_bound);
201 for (
const auto& variable_coeff : variable_coeffs) {
210 int64_t lower_bound, int64_t upper_bound,
211 const std::vector<std::pair<int, double>>& weighted_variables) {
213 if (std::numeric_limits<int64_t>::min() < lower_bound) {
216 const int under_lower_bound_ct =
218 lower_bound - 1, weighted_variables);
221 if (upper_bound < std::numeric_limits<int64_t>::max()) {
225 upper_bound + 1, std::numeric_limits<int64_t>::max(),
231 const int within_bounds_ct =
234 return within_bounds;
241 : is_relaxation_(is_relaxation) {
242 lp_solver_.SetParameters(parameters);
243 linear_program_.SetMaximizationProblem(
false);
246 linear_program_.Clear();
247 linear_program_.SetMaximizationProblem(
false);
248 allowed_intervals_.clear();
251 return linear_program_.CreateNewVariable().value();
254 int64_t upper_bound)
override {
255 DCHECK_GE(lower_bound, 0);
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);
272 const std::vector<int64_t>& ends)
override {
277 allowed_intervals_[index] =
278 absl::make_unique<SortedDisjointIntervalList>(starts, ends);
281 return linear_program_.variable_lower_bounds()[glop::ColIndex(index)];
284 linear_program_.SetObjectiveCoefficient(glop::ColIndex(index), coefficient);
287 return linear_program_.objective_coefficients()[glop::ColIndex(index)];
290 for (glop::ColIndex i(0); i < linear_program_.num_variables(); ++i) {
291 linear_program_.SetObjectiveCoefficient(i, 0);
295 return linear_program_.num_variables().value();
298 const glop::RowIndex ct = linear_program_.CreateNewConstraint();
299 linear_program_.SetConstraintBounds(
301 (lower_bound == std::numeric_limits<int64_t>::min()) ? -glop::kInfinity
303 (upper_bound == std::numeric_limits<int64_t>::max()) ? glop::kInfinity
308 linear_program_.SetCoefficient(glop::RowIndex(ct), glop::ColIndex(index),
314 for (
int variable = 0; variable <
NumVariables(); variable++) {
316 if (coefficient != 0) {
325 lp_solver_.GetMutableParameters()->set_max_time_in_seconds(
326 absl::ToDoubleSeconds(duration_limit));
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 &&
337 status != glop::ProblemStatus::IMPRECISE) {
338 linear_program_.Clear();
341 if (is_relaxation_) {
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())
348 ? 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) {
360 return MathUtil::FastInt64Round(lp_solver_.GetObjectiveValue());
363 return lp_solver_.variable_values()[glop::ColIndex(index)];
366 return linear_program_.SolutionIsInteger(lp_solver_.variable_values(),
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>>
381 parameters_.set_num_search_workers(1);
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);
388 parameters_.set_search_branching(sat::SatParameters::LP_SEARCH);
394 objective_coefficients_.clear();
395 variable_offset_.clear();
396 constraint_offset_.clear();
397 first_constraint_to_offset_ = 0;
400 const int index = model_.variables_size();
401 if (index >= variable_offset_.size()) {
402 variable_offset_.resize(index + 1, 0);
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()));
410 int64_t upper_bound)
override {
411 DCHECK_GE(lower_bound, 0);
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);
428 const std::vector<int64_t>& ends)
override {
429 DCHECK_EQ(starts.size(), ends.size());
431 for (
int i = 0; i < starts.size(); ++i) {
437 model_.mutable_constraints(window_ct)->add_enforcement_literal(variable);
441 return CapAdd(model_.variables(index).domain(0), variable_offset_[index]);
446 if (index >= objective_coefficients_.size()) {
447 objective_coefficients_.resize(index + 1, 0);
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]);
457 return (index < objective_coefficients_.size())
458 ? objective_coefficients_[index]
464 const int ct_index = model_.constraints_size();
465 if (ct_index >= constraint_offset_.size()) {
466 constraint_offset_.resize(ct_index + 1, 0);
468 sat::LinearConstraintProto*
const ct =
469 model_.add_constraints()->mutable_linear();
470 ct->add_domain(lower_bound);
471 ct->add_domain(upper_bound);
477 sat::LinearConstraintProto*
const ct =
478 model_.mutable_constraints(ct_index)->mutable_linear();
480 ct->add_coeffs(coefficient);
481 constraint_offset_[ct_index] =
482 CapAdd(constraint_offset_[ct_index],
483 CapProd(variable_offset_[index], coefficient));
487 const double scaling_factor =
488 model_.has_objective() && model_.objective().scaling_factor() != 0
489 ? model_.objective().scaling_factor()
493 model_.objective().offset());
494 const sat::CpObjectiveProto& objective = model_.objective();
495 for (
int i = 0; i < objective.vars_size(); ++i) {
500 sat::LinearArgumentProto*
const ct =
501 model_.add_constraints()->mutable_lin_max();
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();
511 sat::IntegerArgumentProto*
const ct =
512 model_.add_constraints()->mutable_int_prod();
513 ct->set_target(product_var);
514 for (
const int var : vars) {
519 DCHECK_LT(ct, constraint_offset_.size());
520 model_.mutable_constraints(ct)->add_enforcement_literal(condition);
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;
527 sat::LinearConstraintProto*
const ct =
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]));
532 first_constraint_to_offset_ = constraint_offset_.size();
535 sat::CpObjectiveProto*
const objective = model_.mutable_objective();
537 for (int64_t coeff : objective->coeffs()) {
538 gcd = MathUtil::GCD64(gcd, std::abs(coeff));
541 for (
int i = 0; i < objective->coeffs_size(); ++i) {
542 objective->set_coeffs(i, objective->coeffs(i) / gcd);
544 objective->set_offset(objective->offset() / gcd);
545 objective->set_scaling_factor(gcd);
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_;
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())) {
561 for (
int i = 0; i < response_.solution_size(); ++i) {
563 hint_.add_values(response_.solution(i));
570 return MathUtil::FastInt64Round(response_.objective_value());
573 return response_.solution(index) + variable_offset_[index];
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_;
585 sat::PartialVariableAssignment hint_;
593 bool use_precedence_propagator);
600 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
602 std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
603 bool clear_lp =
true);
606 const std::function<int64_t(int64_t)>& next_accessor,
608 std::vector<int64_t>* break_values, int64_t* cost, int64_t* transit_cost,
609 bool clear_lp =
true);
612 const std::function<int64_t(int64_t)>& next_accessor,
614 std::vector<int64_t>* break_values);
617 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
619 std::vector<int64_t>* break_values);
634 bool ComputeRouteCumulBounds(
const std::vector<int64_t>& route,
635 const std::vector<int64_t>& fixed_transits,
636 int64_t cumul_offset);
642 bool SetRouteCumulConstraints(
643 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
644 int64_t cumul_offset,
bool optimize_costs,
646 int64_t* route_cost_offset);
654 void SetGlobalConstraints(
655 const std::function<int64_t(int64_t)>& next_accessor,
656 int64_t cumul_offset,
bool optimize_costs,
659 void SetValuesFromLP(
const std::vector<int>& lp_variables, int64_t offset,
661 std::vector<int64_t>* lp_values);
672 std::unique_ptr<CumulBoundsPropagator> propagator_;
673 std::vector<int64_t> current_route_min_cumuls_;
674 std::vector<int64_t> current_route_max_cumuls_;
677 std::vector<int> current_route_cumul_variables_;
678 std::vector<int> index_to_cumul_variable_;
683 std::vector<int> current_route_break_variables_;
687 std::vector<int> all_break_variables_;
691 std::vector<int> vehicle_to_all_break_variables_offset_;
694 int min_start_cumul_;
695 std::vector<std::pair<int64_t, int64_t>>
696 visited_pickup_delivery_indices_for_pair_;
715 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
716 int64_t* optimal_cost);
721 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
722 int64_t* optimal_cost_without_transits);
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);
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);
746 std::vector<std::unique_ptr<RoutingLinearSolverWrapper>> solver_;
761 const std::function<int64_t(int64_t)>& next_accessor,
762 int64_t* optimal_cost_without_transits);
769 const std::function<int64_t(int64_t)>& next_accessor,
770 std::vector<int64_t>* optimal_cumuls,
771 std::vector<int64_t>* optimal_breaks);
777 const std::function<int64_t(int64_t)>& next_accessor,
778 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks);
785 std::unique_ptr<RoutingLinearSolverWrapper> solver_;
const RoutingDimension & dimension() const
int64_t CumulMax(int index) const
bool PropagateCumulBounds(const std::function< int64_t(int64_t)> &next_accessor, int64_t cumul_offset)
int64_t CumulMin(int index) const
CumulBoundsPropagator(const RoutingDimension *dimension)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
const RoutingDimension * dimension() const
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)
const RoutingDimension * dimension() const
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)
const RoutingDimension * dimension() const
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)
void SetCoefficient(int ct_index, int index, double coefficient) override
int NumVariables() const override
bool IsCPSATSolver() override
int CreateNewPositiveVariable() override
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
double GetValue(int index) const override
int64_t GetObjectiveValue() const override
DimensionSchedulingStatus Solve(absl::Duration duration_limit) override
~RoutingCPSatWrapper() 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 AddObjectiveConstraint() override
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
bool SolutionIsInteger() const 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
void ClearObjective() override
Dimensions represent quantities accumulated at nodes along the routes.
int NumVariables() const override
bool IsCPSATSolver() override
RoutingGlopWrapper(bool is_relaxation, const glop::GlopParameters ¶meters)
int CreateNewPositiveVariable() override
bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound) override
double GetValue(int index) const override
int64_t GetObjectiveValue() 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 AddObjectiveConstraint() override
void SetVariableDisjointBounds(int index, const std::vector< int64_t > &starts, const std::vector< int64_t > &ends) override
bool SolutionIsInteger() const 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
void ClearObjective() override
virtual void SetCoefficient(int ct, int index, double coefficient)=0
virtual int NumVariables() const =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
virtual int CreateNewPositiveVariable()=0
int AddVariable(int64_t lower_bound, int64_t upper_bound)
virtual bool IsCPSATSolver()=0
virtual int64_t GetObjectiveValue() const =0
virtual void AddObjectiveConstraint()=0
virtual void ClearObjective()=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 bool SolutionIsInteger() const =0
virtual ~RoutingLinearSolverWrapper()
virtual int64_t GetVariableLowerBound(int index) const =0
virtual void AddMaximumConstraint(int max_var, std::vector< int > vars)=0
Collection of objects used to extend the Constraint Solver library.
RoutingSearchParameters_SchedulingSolver
DimensionSchedulingStatus