29 #include "absl/algorithm/container.h"
30 #include "absl/container/flat_hash_map.h"
31 #include "absl/container/flat_hash_set.h"
32 #include "absl/log/check.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/time/time.h"
41 #include "ortools/constraint_solver/routing_parameters.pb.h"
42 #include "ortools/glop/parameters.pb.h"
44 #include "ortools/sat/cp_model.pb.h"
56 glop::GlopParameters GetGlopParametersForLocalLP() {
63 glop::GlopParameters GetGlopParametersForGlobalLP() {
69 bool GetCumulBoundsWithOffset(
const RoutingDimension& dimension,
70 int64_t node_index, int64_t cumul_offset,
75 const IntVar& cumul_var = *dimension.CumulVar(node_index);
81 const int64_t first_after_offset =
82 std::max(dimension.GetFirstPossibleGreaterOrEqualValueForNode(
83 node_index, cumul_offset),
97 int64_t GetFirstPossibleValueForCumulWithOffset(
98 const RoutingDimension& dimension, int64_t node_index,
99 int64_t lower_bound_without_offset, int64_t cumul_offset) {
101 dimension.GetFirstPossibleGreaterOrEqualValueForNode(
102 node_index,
CapAdd(lower_bound_without_offset, cumul_offset)),
106 int64_t GetLastPossibleValueForCumulWithOffset(
107 const RoutingDimension& dimension, int64_t node_index,
108 int64_t upper_bound_without_offset, int64_t cumul_offset) {
110 dimension.GetLastPossibleLessOrEqualValueForNode(
111 node_index,
CapAdd(upper_bound_without_offset, cumul_offset)),
120 void StoreVisitedPickupDeliveryPairsOnRoute(
121 const RoutingDimension& dimension,
int vehicle,
122 const std::function<int64_t(int64_t)>& next_accessor,
123 std::vector<int>* visited_pairs,
124 std::vector<std::pair<int64_t, int64_t>>*
125 visited_pickup_delivery_indices_for_pair) {
127 DCHECK_EQ(visited_pickup_delivery_indices_for_pair->size(),
128 dimension.model()->GetPickupAndDeliveryPairs().size());
129 DCHECK(std::all_of(visited_pickup_delivery_indices_for_pair->begin(),
130 visited_pickup_delivery_indices_for_pair->end(),
131 [](std::pair<int64_t, int64_t> p) {
132 return p.first == -1 && p.second == -1;
134 visited_pairs->clear();
135 if (!dimension.HasPickupToDeliveryLimits()) {
138 const RoutingModel&
model = *dimension.model();
140 int64_t node_index =
model.Start(vehicle);
141 while (!
model.IsEnd(node_index)) {
142 const std::vector<std::pair<int, int>>& pickup_index_pairs =
143 model.GetPickupIndexPairs(node_index);
144 const std::vector<std::pair<int, int>>& delivery_index_pairs =
145 model.GetDeliveryIndexPairs(node_index);
146 if (!pickup_index_pairs.empty()) {
149 DCHECK(delivery_index_pairs.empty());
150 DCHECK_EQ(pickup_index_pairs.size(), 1);
151 (*visited_pickup_delivery_indices_for_pair)[pickup_index_pairs[0].first]
153 visited_pairs->push_back(pickup_index_pairs[0].first);
154 }
else if (!delivery_index_pairs.empty()) {
158 DCHECK_EQ(delivery_index_pairs.size(), 1);
159 const int pair_index = delivery_index_pairs[0].first;
160 std::pair<int64_t, int64_t>& pickup_delivery_index =
161 (*visited_pickup_delivery_indices_for_pair)[pair_index];
162 if (pickup_delivery_index.first < 0) {
165 node_index = next_accessor(node_index);
168 pickup_delivery_index.second = node_index;
170 node_index = next_accessor(node_index);
180 RoutingSearchParameters::SchedulingSolver solver_type)
181 : optimizer_core_(dimension, false) {
185 solver_.resize(vehicles);
186 switch (solver_type) {
187 case RoutingSearchParameters::SCHEDULING_GLOP: {
188 const glop::GlopParameters
parameters = GetGlopParametersForLocalLP();
189 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
193 std::make_unique<RoutingGlopWrapper>(
false,
parameters);
197 case RoutingSearchParameters::SCHEDULING_CP_SAT: {
198 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
199 solver_[vehicle] = std::make_unique<RoutingCPSatWrapper>();
204 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
209 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
210 int64_t* optimal_cost) {
212 solver_[vehicle].get(),
nullptr,
213 nullptr, optimal_cost,
nullptr);
218 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
219 int64_t* optimal_cost_without_transits) {
221 int64_t transit_cost = 0;
223 vehicle, next_accessor, {}, solver_[vehicle].get(),
nullptr,
nullptr,
224 &
cost, &transit_cost);
226 optimal_cost_without_transits !=
nullptr) {
227 *optimal_cost_without_transits =
CapSub(
cost, transit_cost);
234 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
235 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
236 const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
237 const std::vector<int>& resource_indices,
bool optimize_vehicle_costs,
238 std::vector<int64_t>* optimal_costs_without_transits,
239 std::vector<std::vector<int64_t>>* optimal_cumuls,
240 std::vector<std::vector<int64_t>>* optimal_breaks) {
242 vehicle, next_accessor, transit_accessor, {}, resources, resource_indices,
243 optimize_vehicle_costs, solver_[vehicle].get(),
244 optimal_costs_without_transits, optimal_cumuls, optimal_breaks);
248 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
250 std::vector<int64_t>* optimal_cumuls,
251 std::vector<int64_t>* optimal_breaks) {
253 vehicle, next_accessor, dimension_travel_info, solver_[vehicle].get(),
254 optimal_cumuls, optimal_breaks,
nullptr,
nullptr);
259 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
261 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
262 int64_t* optimal_cost) {
264 vehicle, next_accessor, dimension_travel_info, solver_[vehicle].get(),
265 optimal_cumuls, optimal_breaks, optimal_cost,
nullptr);
270 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
272 const std::vector<int64_t>& solution_cumul_values,
273 const std::vector<int64_t>& solution_break_values, int64_t* solution_cost,
274 int64_t* cost_offset,
bool reuse_previous_model_if_possible,
bool clear_lp,
275 absl::Duration* solve_duration) {
278 vehicle, next_accessor, dimension_travel_info, solver,
279 solution_cumul_values, solution_break_values, solution_cost,
nullptr,
280 cost_offset, reuse_previous_model_if_possible, clear_lp,
281 true, solve_duration);
286 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
289 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks) {
291 vehicle, next_accessor, dimension_travel_info, resource,
292 solver_[vehicle].get(), packed_cumuls, packed_breaks);
295 const int CumulBoundsPropagator::kNoParent = -2;
296 const int CumulBoundsPropagator::kParentToBePropagated = -1;
299 : dimension_(*dimension), num_nodes_(2 * dimension->cumuls().size()) {
300 outgoing_arcs_.resize(num_nodes_);
301 node_in_queue_.resize(num_nodes_,
false);
302 tree_parent_node_of_.resize(num_nodes_, kNoParent);
303 propagated_bounds_.resize(num_nodes_);
304 visited_pickup_delivery_indices_for_pair_.resize(
308 void CumulBoundsPropagator::AddArcs(
int first_index,
int second_index,
311 outgoing_arcs_[PositiveNode(first_index)].push_back(
312 {PositiveNode(second_index), offset});
313 AddNodeToQueue(PositiveNode(first_index));
315 outgoing_arcs_[NegativeNode(second_index)].push_back(
316 {NegativeNode(first_index), offset});
317 AddNodeToQueue(NegativeNode(second_index));
320 bool CumulBoundsPropagator::InitializeArcsAndBounds(
321 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
322 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
323 dimension_travel_info_per_route) {
326 for (std::vector<ArcInfo>& arcs : outgoing_arcs_) {
330 RoutingModel*
const model = dimension_.
model();
331 std::vector<int64_t>&
lower_bounds = propagated_bounds_;
333 for (
int vehicle = 0; vehicle <
model->vehicles(); vehicle++) {
334 const std::function<int64_t(int64_t, int64_t)>& transit_accessor =
337 int node =
model->Start(vehicle);
338 int index_on_route = 0;
340 int64_t cumul_lb, cumul_ub;
341 if (!GetCumulBoundsWithOffset(dimension_, node, cumul_offset, &cumul_lb,
350 if (
model->IsEnd(node)) {
354 const int next = next_accessor(node);
355 int64_t transit = transit_accessor(node,
next);
356 if (dimension_travel_info_per_route !=
nullptr &&
357 !dimension_travel_info_per_route->empty()) {
358 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
359 transition_info = (*dimension_travel_info_per_route)[vehicle]
360 .transition_info[index_on_route];
361 transit = transition_info.compressed_travel_value_lower_bound +
362 transition_info.pre_travel_transit_value +
363 transition_info.post_travel_transit_value;
366 const IntVar& slack_var = *dimension_.
SlackVar(node);
369 AddArcs(node,
next,
CapAdd(transit, slack_var.Min()));
372 AddArcs(
next, node,
CapSub(-slack_var.Max(), transit));
381 AddArcs(
model->End(vehicle),
model->Start(vehicle), -span_ub);
385 std::vector<int> visited_pairs;
386 StoreVisitedPickupDeliveryPairsOnRoute(
387 dimension_, vehicle, next_accessor, &visited_pairs,
388 &visited_pickup_delivery_indices_for_pair_);
389 for (
int pair_index : visited_pairs) {
390 const int64_t pickup_index =
391 visited_pickup_delivery_indices_for_pair_[pair_index].first;
392 const int64_t delivery_index =
393 visited_pickup_delivery_indices_for_pair_[pair_index].second;
394 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
396 DCHECK_GE(pickup_index, 0);
397 if (delivery_index < 0) {
403 pair_index,
model->GetPickupIndexPairs(pickup_index)[0].second,
404 model->GetDeliveryIndexPairs(delivery_index)[0].second);
407 AddArcs(delivery_index, pickup_index, -limit);
412 for (
const RoutingDimension::NodePrecedence& precedence :
414 const int first_index = precedence.first_node;
415 const int second_index = precedence.second_node;
423 AddArcs(first_index, second_index, precedence.offset);
429 bool CumulBoundsPropagator::UpdateCurrentLowerBoundOfNode(
int node,
432 const int cumul_var_index = node / 2;
434 if (node == PositiveNode(cumul_var_index)) {
436 propagated_bounds_[node] = GetFirstPossibleValueForCumulWithOffset(
437 dimension_, cumul_var_index, new_lb, offset);
440 const int64_t new_ub =
CapSub(0, new_lb);
441 propagated_bounds_[node] =
442 CapSub(0, GetLastPossibleValueForCumulWithOffset(
443 dimension_, cumul_var_index, new_ub, offset));
447 const int64_t cumul_lower_bound =
448 propagated_bounds_[PositiveNode(cumul_var_index)];
450 const int64_t negated_cumul_upper_bound =
451 propagated_bounds_[NegativeNode(cumul_var_index)];
453 return CapAdd(negated_cumul_upper_bound, cumul_lower_bound) <= 0;
456 bool CumulBoundsPropagator::DisassembleSubtree(
int source,
int target) {
457 tmp_dfs_stack_.clear();
458 tmp_dfs_stack_.push_back(source);
459 while (!tmp_dfs_stack_.empty()) {
460 const int tail = tmp_dfs_stack_.back();
461 tmp_dfs_stack_.pop_back();
462 for (
const ArcInfo&
arc : outgoing_arcs_[
tail]) {
463 const int child_node =
arc.head;
464 if (tree_parent_node_of_[child_node] !=
tail)
continue;
465 if (child_node == target)
return false;
466 tree_parent_node_of_[child_node] = kParentToBePropagated;
467 tmp_dfs_stack_.push_back(child_node);
474 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
475 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
476 dimension_travel_info_per_route) {
477 tree_parent_node_of_.assign(num_nodes_, kNoParent);
478 DCHECK(std::none_of(node_in_queue_.begin(), node_in_queue_.end(),
479 [](
bool b) { return b; }));
480 DCHECK(bf_queue_.empty());
482 if (!InitializeArcsAndBounds(next_accessor, cumul_offset,
483 dimension_travel_info_per_route)) {
484 return CleanupAndReturnFalse();
487 std::vector<int64_t>& current_lb = propagated_bounds_;
490 while (!bf_queue_.empty()) {
491 const int node = bf_queue_.front();
492 bf_queue_.pop_front();
493 node_in_queue_[node] =
false;
495 if (tree_parent_node_of_[node] == kParentToBePropagated) {
502 for (
const ArcInfo&
arc : outgoing_arcs_[node]) {
505 const int64_t induced_lb =
510 const int head_node =
arc.head;
511 if (induced_lb <= current_lb[head_node]) {
516 if (!UpdateCurrentLowerBoundOfNode(head_node, induced_lb, cumul_offset) ||
517 !DisassembleSubtree(head_node, node)) {
520 return CleanupAndReturnFalse();
523 tree_parent_node_of_[head_node] = node;
524 AddNodeToQueue(head_node);
532 : dimension_(dimension),
533 visited_pickup_delivery_indices_for_pair_(
534 dimension->
model()->GetPickupAndDeliveryPairs().size(), {-1, -1}) {
535 if (use_precedence_propagator) {
536 propagator_ = std::make_unique<CumulBoundsPropagator>(dimension);
538 const RoutingModel&
model = *dimension_->
model();
543 const int num_vehicles =
model.vehicles();
544 vehicle_to_all_break_variables_offset_.reserve(num_vehicles);
545 int num_break_vars = 0;
546 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
547 vehicle_to_all_break_variables_offset_.push_back(num_break_vars);
549 num_break_vars += 2 * intervals.size();
551 all_break_variables_.resize(num_break_vars, -1);
553 if (!
model.GetDimensionResourceGroupIndices(dimension_).empty()) {
554 resource_group_to_resource_to_vehicle_assignment_variables_.resize(
555 model.GetResourceGroups().size());
559 bool DimensionCumulOptimizerCore::InitSingleRoute(
560 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
561 const RouteDimensionTravelInfo& dimension_travel_info,
562 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
563 int64_t*
cost, int64_t* transit_cost, int64_t* cumul_offset,
564 int64_t*
const cost_offset) {
565 InitOptimizer(solver);
568 DCHECK_EQ(propagator_.get(),
nullptr);
571 const bool optimize_vehicle_costs =
572 (cumul_values !=
nullptr ||
cost !=
nullptr) &&
573 (!
model->IsEnd(next_accessor(
model->Start(vehicle))) ||
574 model->IsVehicleUsedWhenEmpty(vehicle));
576 if (!SetRouteCumulConstraints(
578 dimension_travel_info, *cumul_offset, optimize_vehicle_costs, solver,
579 transit_cost, cost_offset)) {
582 if (
model->CheckLimit()) {
590 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
593 const std::vector<int64_t>& solution_cumul_values,
594 const std::vector<int64_t>& solution_break_values, int64_t*
cost,
595 int64_t* transit_cost, int64_t* cost_offset,
596 bool reuse_previous_model_if_possible,
bool clear_lp,
597 bool clear_solution_constraints, absl::Duration*
const solve_duration) {
598 absl::Duration solve_duration_value;
599 int64_t cost_offset_value;
600 if (!reuse_previous_model_if_possible || solver->
ModelIsEmpty()) {
601 int64_t cumul_offset;
602 std::vector<int64_t> cumul_values;
603 if (!InitSingleRoute(vehicle, next_accessor, dimension_travel_info, solver,
604 &cumul_values,
cost, transit_cost, &cumul_offset,
605 &cost_offset_value)) {
609 if (solve_duration !=
nullptr) *solve_duration = solve_duration_value;
610 if (cost_offset !=
nullptr) *cost_offset = cost_offset_value;
612 CHECK(cost_offset !=
nullptr)
613 <<
"Cannot reuse model without the cost_offset";
614 cost_offset_value = *cost_offset;
615 CHECK(solve_duration !=
nullptr)
616 <<
"Cannot reuse model without the solve_duration";
617 solve_duration_value = *solve_duration;
621 DCHECK_EQ(solution_cumul_values.size(),
622 current_route_cumul_variables_.size());
623 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
624 if (solution_cumul_values[i] < current_route_min_cumuls_[i] ||
625 solution_cumul_values[i] > current_route_max_cumuls_[i]) {
629 solution_cumul_values[i],
630 solution_cumul_values[i]);
634 DCHECK_EQ(solution_break_values.size(),
635 current_route_break_variables_.size());
636 std::vector<int64_t> current_route_min_breaks(
637 current_route_break_variables_.size());
638 std::vector<int64_t> current_route_max_breaks(
639 current_route_break_variables_.size());
640 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
641 current_route_min_breaks[i] =
643 current_route_max_breaks[i] =
646 solution_break_values[i],
647 solution_break_values[i]);
656 if (
cost !=
nullptr) {
662 }
else if (clear_solution_constraints) {
663 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
665 current_route_min_cumuls_[i],
666 current_route_max_cumuls_[i]);
668 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
670 current_route_min_breaks[i],
671 current_route_max_breaks[i]);
678 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
681 std::vector<int64_t>* break_values, int64_t*
cost, int64_t* transit_cost,
683 int64_t cumul_offset, cost_offset;
684 if (!InitSingleRoute(vehicle, next_accessor, dimension_travel_info, solver,
685 cumul_values,
cost, transit_cost, &cumul_offset,
696 SetValuesFromLP(current_route_cumul_variables_, cumul_offset, solver,
698 SetValuesFromLP(current_route_break_variables_, cumul_offset, solver,
700 if (
cost !=
nullptr) {
714 bool GetDomainOffsetBounds(
const Domain& domain, int64_t offset,
717 std::max<int64_t>(
CapSub(domain.
Min(), offset), 0);
721 :
CapSub(domain.Max(), offset);
728 bool GetIntervalIntersectionWithOffsetDomain(
const ClosedInterval&
interval,
729 const Domain& domain,
731 ClosedInterval* intersection) {
732 ClosedInterval domain_bounds;
733 if (!GetDomainOffsetBounds(domain, offset, &domain_bounds)) {
736 const int64_t intersection_lb =
std::max(
interval.start, domain_bounds.start);
738 if (intersection_lb > intersection_ub)
return false;
740 *intersection = ClosedInterval(intersection_lb, intersection_ub);
744 ClosedInterval GetVariableBounds(
int index,
745 const RoutingLinearSolverWrapper& solver) {
746 return ClosedInterval(solver.GetVariableLowerBound(
index),
747 solver.GetVariableUpperBound(
index));
750 bool TightenStartEndVariableBoundsWithResource(
751 const RoutingDimension& dimension,
const ResourceGroup::Resource& resource,
752 const ClosedInterval& start_bounds,
int start_index,
753 const ClosedInterval& end_bounds,
int end_index, int64_t offset,
754 RoutingLinearSolverWrapper* solver) {
755 const ResourceGroup::Attributes& attributes =
756 resource.GetDimensionAttributes(&dimension);
757 ClosedInterval new_start_bounds;
758 ClosedInterval new_end_bounds;
759 return GetIntervalIntersectionWithOffsetDomain(start_bounds,
760 attributes.start_domain(),
761 offset, &new_start_bounds) &&
762 solver->SetVariableBounds(start_index, new_start_bounds.start,
763 new_start_bounds.end) &&
764 GetIntervalIntersectionWithOffsetDomain(
765 end_bounds, attributes.end_domain(), offset, &new_end_bounds) &&
766 solver->SetVariableBounds(end_index, new_end_bounds.start,
772 std::vector<DimensionSchedulingStatus>
774 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
775 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
777 const std::vector<RoutingModel::ResourceGroup::Resource>& resources,
778 const std::vector<int>& resource_indices,
bool optimize_vehicle_costs,
780 std::vector<int64_t>* costs_without_transits,
781 std::vector<std::vector<int64_t>>* cumul_values,
782 std::vector<std::vector<int64_t>>* break_values,
bool clear_lp) {
783 if (resource_indices.empty())
return {};
785 InitOptimizer(solver);
788 DCHECK_EQ(propagator_.get(),
nullptr);
789 DCHECK_NE(costs_without_transits,
nullptr);
790 costs_without_transits->clear();
793 if (
model->IsEnd(next_accessor(
model->Start(vehicle))) &&
794 !
model->IsVehicleUsedWhenEmpty(vehicle)) {
799 const int64_t cumul_offset =
801 int64_t cost_offset = 0;
802 int64_t transit_cost = 0;
803 if (!SetRouteCumulConstraints(vehicle, next_accessor, transit_accessor,
804 dimension_travel_info, cumul_offset,
805 optimize_vehicle_costs, solver, &transit_cost,
810 costs_without_transits->assign(resource_indices.size(), -1);
811 if (cumul_values !=
nullptr) {
812 cumul_values->assign(resource_indices.size(), {});
814 if (break_values !=
nullptr) {
815 break_values->assign(resource_indices.size(), {});
818 DCHECK_GE(current_route_cumul_variables_.size(), 2);
820 const int start_cumul = current_route_cumul_variables_[0];
821 const ClosedInterval start_bounds = GetVariableBounds(start_cumul, *solver);
822 const int end_cumul = current_route_cumul_variables_.back();
823 const ClosedInterval end_bounds = GetVariableBounds(end_cumul, *solver);
824 std::vector<DimensionSchedulingStatus> statuses;
825 for (
int i = 0; i < resource_indices.size(); i++) {
826 if (
model->CheckLimit()) {
828 costs_without_transits->clear();
829 if (cumul_values !=
nullptr) {
830 cumul_values->clear();
832 if (break_values !=
nullptr) {
833 break_values->clear();
837 if (!TightenStartEndVariableBoundsWithResource(
838 *dimension_, resources[resource_indices[i]], start_bounds,
839 start_cumul, end_bounds, end_cumul, cumul_offset, solver)) {
845 statuses.push_back(solver->
Solve(
model->RemainingTime()));
849 costs_without_transits->at(i) =
850 optimize_vehicle_costs
855 if (cumul_values !=
nullptr) {
856 SetValuesFromLP(current_route_cumul_variables_, cumul_offset, solver,
857 &cumul_values->at(i));
859 if (break_values !=
nullptr) {
860 SetValuesFromLP(current_route_break_variables_, cumul_offset, solver,
861 &break_values->at(i));
872 const std::function<int64_t(int64_t)>& next_accessor,
873 const std::vector<RouteDimensionTravelInfo>&
874 dimension_travel_info_per_route,
876 std::vector<int64_t>* break_values,
877 std::vector<std::vector<int>>* resource_indices_per_group, int64_t*
cost,
878 int64_t* transit_cost,
bool clear_lp) {
879 InitOptimizer(solver);
883 const bool optimize_costs = (cumul_values !=
nullptr) || (
cost !=
nullptr);
884 bool has_vehicles_being_optimized =
false;
888 if (propagator_ !=
nullptr &&
889 !propagator_->PropagateCumulBounds(next_accessor, cumul_offset,
890 &dimension_travel_info_per_route)) {
894 int64_t total_transit_cost = 0;
895 int64_t total_cost_offset = 0;
897 for (
int vehicle = 0; vehicle <
model->vehicles(); vehicle++) {
898 int64_t route_transit_cost = 0;
899 int64_t route_cost_offset = 0;
900 const bool vehicle_is_used =
901 !
model->IsEnd(next_accessor(
model->Start(vehicle))) ||
902 model->IsVehicleUsedWhenEmpty(vehicle);
903 const bool optimize_vehicle_costs = optimize_costs && vehicle_is_used;
905 dimension_travel_info_per_route.empty()
907 : dimension_travel_info_per_route[vehicle];
908 if (!SetRouteCumulConstraints(
910 dimension_travel_info, cumul_offset, optimize_vehicle_costs, solver,
911 &route_transit_cost, &route_cost_offset)) {
914 total_transit_cost =
CapAdd(total_transit_cost, route_transit_cost);
915 total_cost_offset =
CapAdd(total_cost_offset, route_cost_offset);
916 has_vehicles_being_optimized |= optimize_vehicle_costs;
918 if (transit_cost !=
nullptr) {
919 *transit_cost = total_transit_cost;
922 if (!SetGlobalConstraints(next_accessor, cumul_offset,
923 has_vehicles_being_optimized, solver)) {
936 SetValuesFromLP(index_to_cumul_variable_, cumul_offset, solver, cumul_values);
937 SetValuesFromLP(all_break_variables_, cumul_offset, solver, break_values);
938 SetResourceIndices(solver, resource_indices_per_group);
940 if (
cost !=
nullptr) {
951 const std::function<int64_t(int64_t)>& next_accessor,
952 const std::vector<RouteDimensionTravelInfo>&
953 dimension_travel_info_per_route,
955 std::vector<int64_t>* break_values,
956 std::vector<std::vector<int>>* resource_indices_per_group) {
960 const glop::GlopParameters original_params = GetGlopParametersForGlobalLP();
961 glop::GlopParameters packing_parameters;
963 packing_parameters = original_params;
964 packing_parameters.set_use_dual_simplex(
false);
965 packing_parameters.set_use_preprocessing(
true);
966 solver->
SetParameters(packing_parameters.SerializeAsString());
969 if (
Optimize(next_accessor, dimension_travel_info_per_route, solver,
978 std::iota(vehicles.begin(), vehicles.end(), 0);
981 status = PackRoutes(vehicles, solver, packing_parameters);
992 SetValuesFromLP(index_to_cumul_variable_, global_offset, solver,
994 SetValuesFromLP(all_break_variables_, global_offset, solver, break_values);
995 SetResourceIndices(solver, resource_indices_per_group);
1002 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
1006 std::vector<int64_t>* break_values) {
1007 const glop::GlopParameters original_params = GetGlopParametersForLocalLP();
1008 glop::GlopParameters packing_parameters;
1010 packing_parameters = original_params;
1011 packing_parameters.set_use_dual_simplex(
false);
1012 packing_parameters.set_use_preprocessing(
true);
1013 solver->
SetParameters(packing_parameters.SerializeAsString());
1016 if (resource ==
nullptr) {
1028 std::vector<int64_t> costs_without_transits;
1029 const std::vector<DimensionSchedulingStatus> statuses =
1032 dimension_travel_info, {*resource}, {0},
1033 true, solver, &costs_without_transits,
1039 DCHECK_EQ(statuses.size(), 1);
1045 status = PackRoutes({vehicle}, solver, packing_parameters);
1054 const int64_t local_offset =
1056 SetValuesFromLP(current_route_cumul_variables_, local_offset, solver,
1058 SetValuesFromLP(current_route_break_variables_, local_offset, solver,
1066 const glop::GlopParameters& packing_parameters) {
1083 for (
int vehicle : vehicles) {
1085 index_to_cumul_variable_[
model->End(vehicle)], 1);
1088 glop::GlopParameters current_params;
1089 const auto retry_solving = [¤t_params,
model, solver]() {
1093 current_params.set_use_dual_simplex(!current_params.use_dual_simplex());
1095 return solver->
Solve(
model->RemainingTime());
1103 current_params = packing_parameters;
1112 for (
int vehicle : vehicles) {
1113 const int end_cumul_var = index_to_cumul_variable_[
model->End(vehicle)];
1121 index_to_cumul_variable_[
model->Start(vehicle)], -1);
1127 status = retry_solving();
1132 #define SET_DEBUG_VARIABLE_NAME(solver, var, name) \
1135 solver->SetVariableName(var, name); \
1139 void DimensionCumulOptimizerCore::InitOptimizer(
1140 RoutingLinearSolverWrapper* solver) {
1142 index_to_cumul_variable_.assign(dimension_->
cumuls().size(), -1);
1143 max_end_cumul_ = solver->CreateNewPositiveVariable();
1145 min_start_cumul_ = solver->CreateNewPositiveVariable();
1149 bool DimensionCumulOptimizerCore::ExtractRouteCumulBounds(
1150 const std::vector<int64_t>& route, int64_t cumul_offset) {
1151 const int route_size = route.size();
1152 current_route_min_cumuls_.resize(route_size);
1153 current_route_max_cumuls_.resize(route_size);
1156 for (
int pos = 0; pos < route_size; ++pos) {
1157 if (!GetCumulBoundsWithOffset(*dimension_, route[pos], cumul_offset,
1158 ¤t_route_min_cumuls_[pos],
1159 ¤t_route_max_cumuls_[pos])) {
1166 bool DimensionCumulOptimizerCore::TightenRouteCumulBounds(
1167 const std::vector<int64_t>& route,
const std::vector<int64_t>& min_transits,
1168 int64_t cumul_offset) {
1169 const int route_size = route.size();
1170 if (propagator_ !=
nullptr) {
1171 for (
int pos = 0; pos < route_size; pos++) {
1172 const int64_t node = route[pos];
1173 current_route_min_cumuls_[pos] = propagator_->CumulMin(node);
1174 DCHECK_GE(current_route_min_cumuls_[pos], 0);
1175 current_route_max_cumuls_[pos] = propagator_->CumulMax(node);
1176 DCHECK_GE(current_route_max_cumuls_[pos], current_route_min_cumuls_[pos]);
1183 for (
int pos = 1; pos < route_size; ++pos) {
1184 const int64_t slack_min = dimension_->
SlackVar(route[pos - 1])->
Min();
1185 current_route_min_cumuls_[pos] =
std::max(
1186 current_route_min_cumuls_[pos],
1188 CapAdd(current_route_min_cumuls_[pos - 1], min_transits[pos - 1]),
1190 current_route_min_cumuls_[pos] = GetFirstPossibleValueForCumulWithOffset(
1191 *dimension_, route[pos], current_route_min_cumuls_[pos], cumul_offset);
1192 if (current_route_min_cumuls_[pos] > current_route_max_cumuls_[pos]) {
1197 for (
int pos = route_size - 2; pos >= 0; --pos) {
1200 if (current_route_max_cumuls_[pos + 1] <
1202 const int64_t slack_min = dimension_->
SlackVar(route[pos])->
Min();
1203 current_route_max_cumuls_[pos] =
std::min(
1204 current_route_max_cumuls_[pos],
1205 CapSub(
CapSub(current_route_max_cumuls_[pos + 1], min_transits[pos]),
1207 current_route_max_cumuls_[pos] = GetLastPossibleValueForCumulWithOffset(
1208 *dimension_, route[pos], current_route_max_cumuls_[pos],
1210 if (current_route_max_cumuls_[pos] < current_route_min_cumuls_[pos]) {
1219 const std::vector<SlopeAndYIntercept>& slope_and_y_intercept) {
1220 CHECK(!slope_and_y_intercept.empty());
1221 std::vector<bool> convex(slope_and_y_intercept.size(),
false);
1223 for (
int i = 0; i < slope_and_y_intercept.size(); ++i) {
1224 const auto& pair = slope_and_y_intercept[i];
1225 if (pair.slope < previous_slope) {
1228 previous_slope = pair.slope;
1235 PiecewiseLinearFormulation& pwl_function,
1236 int index_start,
int index_end) {
1237 if (index_end < 0) index_end = pwl_function.x_anchors.size() - 1;
1238 const int num_segments = index_end - index_start;
1239 DCHECK_GE(num_segments, 1);
1240 std::vector<SlopeAndYIntercept> slope_and_y_intercept(num_segments);
1241 for (
int seg = index_start; seg < index_end; ++seg) {
1242 auto& [slope, y_intercept] = slope_and_y_intercept[seg - index_start];
1243 slope = (pwl_function.y_anchors[seg + 1] - pwl_function.y_anchors[seg]) /
1244 static_cast<double>(pwl_function.x_anchors[seg + 1] -
1245 pwl_function.x_anchors[seg]);
1247 pwl_function.y_anchors[seg] - slope * pwl_function.x_anchors[seg];
1249 return slope_and_y_intercept;
1256 double FindBestScaling(
const std::vector<double>&
coefficients,
1259 int64_t max_absolute_activity,
1260 double wanted_absolute_activity_precision) {
1261 double unused_relative_coeff_error = 0;
1262 double unused_scaled_sum_error = 0;
1265 wanted_absolute_activity_precision, &unused_relative_coeff_error,
1266 &unused_scaled_sum_error);
1271 int64_t PieceWiseLinearFormulationValueKnownSegment(
1272 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::
1273 PiecewiseLinearFormulation& pwl,
1274 int64_t x,
int upper_segment_index,
double delta = 0) {
1275 DCHECK_GE(upper_segment_index, 1);
1276 DCHECK_LE(upper_segment_index, pwl.x_anchors.size() - 1);
1277 const double alpha =
1278 static_cast<double>(pwl.y_anchors[upper_segment_index] -
1279 pwl.y_anchors[upper_segment_index - 1]) /
1280 (pwl.x_anchors[upper_segment_index] -
1281 pwl.x_anchors[upper_segment_index - 1]);
1282 const double beta = pwl.y_anchors[upper_segment_index] -
1283 pwl.x_anchors[upper_segment_index] * alpha;
1284 return std::ceil(alpha * x + beta +
delta);
1291 PiecewiseLinearFormulation& pwl,
1294 const auto upper_segment =
1296 const int upper_segment_index =
1300 if (upper_segment_index == 0) {
1302 }
else if (upper_segment == pwl.x_anchors.end()) {
1303 if (x == pwl.x_anchors.back()) {
1304 *
value = std::ceil(pwl.y_anchors.back() +
delta);
1310 *
value = PieceWiseLinearFormulationValueKnownSegment(
1311 pwl, x, upper_segment_index,
delta);
1317 PiecewiseLinearFormulation& pwl,
1318 int64_t x,
double delta) {
1325 LOG(FATAL) <<
"Unspecified PiecewiseEvaluationStatus.";
1333 return PieceWiseLinearFormulationValueKnownSegment(pwl, x, 1,
delta);
1337 return PieceWiseLinearFormulationValueKnownSegment(
1338 pwl, x, pwl.x_anchors.size() - 1,
delta);
1342 bool DimensionCumulOptimizerCore::SetRouteTravelConstraints(
1343 const RouteDimensionTravelInfo& dimension_travel_info,
1344 const std::vector<int>& lp_slacks,
1345 const std::vector<int64_t>& fixed_transit,
1346 RoutingLinearSolverWrapper* solver) {
1347 const std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1348 const int path_size = lp_cumuls.size();
1350 if (dimension_travel_info.transition_info.empty()) {
1355 for (
int pos = 0; pos < path_size - 1; ++pos) {
1357 solver->CreateNewConstraint(fixed_transit[pos], fixed_transit[pos]);
1358 solver->SetCoefficient(
ct, lp_cumuls[pos + 1], 1);
1359 solver->SetCoefficient(
ct, lp_cumuls[pos], -1);
1360 solver->SetCoefficient(
ct, lp_slacks[pos], -1);
1367 for (
int pos = 0; pos < path_size - 1; ++pos) {
1371 const int compression_cost = solver->CreateNewPositiveVariable();
1373 absl::StrFormat(
"compression_cost(%ld)", pos));
1380 const int relative_compression_cost = solver->CreateNewPositiveVariable();
1382 solver, relative_compression_cost,
1383 absl::StrFormat(
"relative_compression_cost(%ld)", pos));
1385 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
1386 transition_info = dimension_travel_info.transition_info[pos];
1387 const RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation&
1388 travel_function = transition_info.travel_start_dependent_travel;
1389 const int num_pwl_anchors = travel_function.x_anchors.size();
1390 DCHECK_GE(num_pwl_anchors, 2)
1391 <<
"Travel value PWL must have at least 2 points";
1392 DCHECK_EQ(num_pwl_anchors, travel_function.y_anchors.size())
1393 <<
"Travel value PWL must have as many x anchors than y.";
1397 const int64_t pre_travel_transit = transition_info.pre_travel_transit_value;
1398 const int64_t post_travel_transit =
1399 transition_info.post_travel_transit_value;
1400 const int64_t compressed_travel_value_lower_bound =
1401 transition_info.compressed_travel_value_lower_bound;
1402 const int64_t travel_value_upper_bound =
1403 dimension_travel_info.transition_info[pos].travel_value_upper_bound;
1409 const int travel_value = solver->AddVariable(
1410 compressed_travel_value_lower_bound, travel_value_upper_bound);
1412 absl::StrFormat(
"travel_value(%ld)", pos));
1413 const int travel_start = solver->AddVariable(
1414 current_route_min_cumuls_[pos] + pre_travel_transit,
1415 current_route_max_cumuls_[pos + 1] - post_travel_transit -
1416 compressed_travel_value_lower_bound);
1418 absl::StrFormat(
"travel_start(%ld)", pos));
1421 solver->AddLinearConstraint(pre_travel_transit, pre_travel_transit,
1422 {{travel_start, 1}, {lp_cumuls[pos], -1}});
1427 int index_anchor_start = 0;
1428 while (index_anchor_start < num_pwl_anchors - 1 &&
1429 travel_function.x_anchors[index_anchor_start + 1] <=
1430 current_route_min_cumuls_[pos] + pre_travel_transit) {
1431 ++index_anchor_start;
1433 int index_anchor_end = num_pwl_anchors - 1;
1434 while (index_anchor_end > 0 &&
1435 travel_function.x_anchors[index_anchor_end - 1] >=
1436 current_route_max_cumuls_[pos] + pre_travel_transit) {
1440 if (index_anchor_start >= index_anchor_end)
return false;
1444 const std::vector<SlopeAndYIntercept> slope_and_y_intercept =
1446 travel_function, index_anchor_start, index_anchor_end);
1449 const std::vector<bool> convexities =
1452 int nb_bin_variables = 0;
1453 for (
const bool convexity : convexities) {
1456 if (nb_bin_variables >= 2)
break;
1459 const bool need_bins = (nb_bin_variables > 1);
1461 const int travel_start_in_one_segment_ct =
1462 need_bins ? solver->CreateNewConstraint(1, 1)
1465 int belongs_to_this_segment_var;
1466 for (
int seg = 0; seg < convexities.size(); ++seg) {
1467 if (need_bins && convexities[seg]) {
1468 belongs_to_this_segment_var = solver->AddVariable(0, 1);
1470 solver, belongs_to_this_segment_var,
1471 absl::StrFormat(
"travel_start(%ld)belongs_to_seg(%ld)", pos, seg));
1472 solver->SetCoefficient(travel_start_in_one_segment_ct,
1473 belongs_to_this_segment_var, 1);
1478 const int64_t lower_bound_interval =
1479 seg > 0 ? travel_function.x_anchors[index_anchor_start + seg]
1480 : current_route_min_cumuls_[pos] + pre_travel_transit;
1481 int64_t end_of_seg = seg + 1;
1482 while (end_of_seg < num_pwl_anchors - 1 && !convexities[end_of_seg]) {
1485 const int64_t higher_bound_interval =
1486 end_of_seg < num_pwl_anchors - 1
1487 ? travel_function.x_anchors[index_anchor_start + end_of_seg]
1488 : current_route_max_cumuls_[pos] + pre_travel_transit;
1489 const int travel_start_in_segment_ct = solver->AddLinearConstraint(
1490 lower_bound_interval, higher_bound_interval, {{travel_start, 1}});
1491 solver->SetEnforcementLiteral(travel_start_in_segment_ct,
1492 belongs_to_this_segment_var);
1497 const auto [slope, y_intercept] = slope_and_y_intercept[seg];
1499 DCHECK_GE(slope, -1.0) <<
"Travel value PWL should have a slope >= -1";
1509 const double upper_bound = current_route_max_cumuls_[pos];
1510 const double factor = FindBestScaling(
1511 {1.0, -slope, y_intercept - 0.5},
1512 {
static_cast<double>(compressed_travel_value_lower_bound), 0, 1},
1514 {
static_cast<double>(travel_value_upper_bound),
upper_bound, 1},
1520 if (factor <= 0)
return false;
1522 const int linearization_ct = solver->AddLinearConstraint(
1528 solver->SetEnforcementLiteral(linearization_ct,
1529 belongs_to_this_segment_var);
1555 const int compressed_travel_value = solver->AddVariable(
1556 compressed_travel_value_lower_bound, travel_value_upper_bound);
1558 solver, compressed_travel_value,
1559 absl::StrFormat(
"compressed_travel_value(%ld)", pos));
1560 solver->AddLinearConstraint(post_travel_transit + pre_travel_transit,
1561 post_travel_transit + pre_travel_transit,
1562 {{compressed_travel_value, -1},
1563 {lp_cumuls[pos + 1], 1},
1564 {lp_cumuls[pos], -1},
1565 {lp_slacks[pos], -1}});
1572 const int travel_compression_absolute = solver->AddVariable(
1573 0, travel_value_upper_bound - compressed_travel_value_lower_bound);
1575 solver, travel_compression_absolute,
1576 absl::StrFormat(
"travel_compression_absolute(%ld)", pos));
1578 solver->AddLinearConstraint(0, 0,
1579 {{travel_compression_absolute, 1},
1581 {compressed_travel_value, 1}});
1587 solver->SetObjectiveCoefficient(
1588 travel_value, dimension_travel_info.travel_cost_coefficient);
1592 const RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation&
1594 dimension_travel_info.transition_info[pos].travel_compression_cost;
1595 const std::vector<SlopeAndYIntercept> cost_slope_and_y_intercept =
1599 travel_value_upper_bound - compressed_travel_value_lower_bound);
1600 double previous_slope = 0;
1601 for (
int seg = 0; seg < cost_function.x_anchors.size() - 1; ++seg) {
1602 const auto [slope, y_intercept] = cost_slope_and_y_intercept[seg];
1604 DCHECK_GE(slope, previous_slope)
1605 <<
"Compression error is not convex. Segment " << (1 + seg)
1606 <<
" out of " << (cost_function.x_anchors.size() - 1);
1607 previous_slope = slope;
1608 const double factor = FindBestScaling(
1609 {1.0, -slope, y_intercept},
1610 {0,
static_cast<double>(compressed_travel_value_lower_bound), 1},
1612 {cost_max,
static_cast<double>(travel_value_upper_bound), 1},
1613 (
static_cast<int64_t
>(1) << 62),
1618 if (factor <= 0)
return false;
1620 solver->AddLinearConstraint(
1623 {{compression_cost, std::round(factor)},
1624 {travel_compression_absolute,
1644 solver->AddLinearConstraint(
1646 {{relative_compression_cost, 1}, {compression_cost, -1}});
1648 solver->SetObjectiveCoefficient(relative_compression_cost, 1.0);
1653 bool DimensionCumulOptimizerCore::SetRouteCumulConstraints(
1654 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
1655 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
1656 const RouteDimensionTravelInfo& dimension_travel_info, int64_t cumul_offset,
1657 bool optimize_costs, RoutingLinearSolverWrapper* solver,
1658 int64_t* route_transit_cost, int64_t* route_cost_offset) {
1659 RoutingModel*
const model = dimension_->
model();
1661 std::vector<int64_t> path;
1663 int node =
model->Start(vehicle);
1664 path.push_back(node);
1665 while (!
model->IsEnd(node)) {
1666 node = next_accessor(node);
1667 path.push_back(node);
1669 DCHECK_GE(path.size(), 2);
1671 const int path_size = path.size();
1673 std::vector<int64_t> fixed_transit(path_size - 1);
1675 for (
int pos = 1; pos < path_size; ++pos) {
1676 fixed_transit[pos - 1] = transit_accessor(path[pos - 1], path[pos]);
1679 if (!ExtractRouteCumulBounds(path, cumul_offset)) {
1682 if (dimension_travel_info.transition_info.empty()) {
1683 if (!TightenRouteCumulBounds(path, fixed_transit, cumul_offset)) {
1688 std::vector<int64_t> min_transit(path_size - 1);
1689 for (
int pos = 0; pos < path_size - 1; ++pos) {
1690 const RouteDimensionTravelInfo::TransitionInfo& transition =
1691 dimension_travel_info.transition_info[pos];
1692 min_transit[pos] = transition.pre_travel_transit_value +
1693 transition.compressed_travel_value_lower_bound +
1694 transition.post_travel_transit_value;
1696 if (!TightenRouteCumulBounds(path, min_transit, cumul_offset)) {
1703 std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1704 lp_cumuls.assign(path_size, -1);
1705 for (
int pos = 0; pos < path_size; ++pos) {
1706 const int lp_cumul = solver->CreateNewPositiveVariable();
1708 absl::StrFormat(
"lp_cumul(%ld)", pos));
1709 index_to_cumul_variable_[path[pos]] = lp_cumul;
1710 lp_cumuls[pos] = lp_cumul;
1711 if (!solver->SetVariableBounds(lp_cumul, current_route_min_cumuls_[pos],
1712 current_route_max_cumuls_[pos])) {
1715 const SortedDisjointIntervalList& forbidden =
1717 if (forbidden.NumIntervals() > 0) {
1718 std::vector<int64_t> starts;
1719 std::vector<int64_t> ends;
1720 for (
const ClosedInterval
interval :
1722 path[pos],
CapAdd(current_route_min_cumuls_[pos], cumul_offset),
1723 CapAdd(current_route_max_cumuls_[pos], cumul_offset))) {
1727 solver->SetVariableDisjointBounds(lp_cumul, starts, ends);
1731 std::vector<int> lp_slacks(path_size - 1, -1);
1732 for (
int pos = 0; pos < path_size - 1; ++pos) {
1733 const IntVar* cp_slack = dimension_->
SlackVar(path[pos]);
1734 lp_slacks[pos] = solver->CreateNewPositiveVariable();
1736 absl::StrFormat(
"lp_slacks(%ld)", pos));
1737 if (!solver->SetVariableBounds(lp_slacks[pos], cp_slack->Min(),
1743 if (!SetRouteTravelConstraints(dimension_travel_info, lp_slacks,
1744 fixed_transit, solver)) {
1748 if (route_cost_offset !=
nullptr) *route_cost_offset = 0;
1749 if (optimize_costs) {
1751 for (
int pos = 0; pos < path_size; ++pos) {
1753 const int64_t
coef =
1755 if (
coef == 0)
continue;
1757 if (
bound < cumul_offset && route_cost_offset !=
nullptr) {
1759 *route_cost_offset =
CapAdd(*route_cost_offset,
1763 if (current_route_max_cumuls_[pos] <=
bound) {
1767 const int soft_ub_diff = solver->CreateNewPositiveVariable();
1769 absl::StrFormat(
"soft_ub_diff(%ld)", pos));
1770 solver->SetObjectiveCoefficient(soft_ub_diff,
coef);
1772 const int ct = solver->CreateNewConstraint(
1774 solver->SetCoefficient(
ct, lp_cumuls[pos], 1);
1775 solver->SetCoefficient(
ct, soft_ub_diff, -1);
1778 for (
int pos = 0; pos < path_size; ++pos) {
1780 const int64_t
coef =
1782 if (
coef == 0)
continue;
1783 const int64_t
bound = std::max<int64_t>(
1786 if (current_route_min_cumuls_[pos] >=
bound) {
1790 const int soft_lb_diff = solver->CreateNewPositiveVariable();
1792 absl::StrFormat(
"soft_lb_diff(%ld)", pos));
1793 solver->SetObjectiveCoefficient(soft_lb_diff,
coef);
1795 const int ct = solver->CreateNewConstraint(
1797 solver->SetCoefficient(
ct, lp_cumuls[pos], 1);
1798 solver->SetCoefficient(
ct, soft_lb_diff, 1);
1802 std::vector<int> visited_pairs;
1803 StoreVisitedPickupDeliveryPairsOnRoute(
1804 *dimension_, vehicle, next_accessor, &visited_pairs,
1805 &visited_pickup_delivery_indices_for_pair_);
1806 for (
int pair_index : visited_pairs) {
1807 const int64_t pickup_index =
1808 visited_pickup_delivery_indices_for_pair_[pair_index].first;
1809 const int64_t delivery_index =
1810 visited_pickup_delivery_indices_for_pair_[pair_index].second;
1811 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
1813 DCHECK_GE(pickup_index, 0);
1814 if (delivery_index < 0) {
1820 pair_index,
model->GetPickupIndexPairs(pickup_index)[0].second,
1821 model->GetDeliveryIndexPairs(delivery_index)[0].second);
1824 const int ct = solver->CreateNewConstraint(
1826 solver->SetCoefficient(
ct, index_to_cumul_variable_[delivery_index], 1);
1827 solver->SetCoefficient(
ct, index_to_cumul_variable_[pickup_index], -1);
1835 const int ct = solver->CreateNewConstraint(
1837 solver->SetCoefficient(
ct, lp_cumuls.back(), 1);
1838 solver->SetCoefficient(
ct, lp_cumuls.front(), -1);
1841 const int64_t span_cost_coef =
1843 if (optimize_costs && span_cost_coef > 0) {
1844 solver->SetObjectiveCoefficient(lp_cumuls.back(), span_cost_coef);
1845 solver->SetObjectiveCoefficient(lp_cumuls.front(), -span_cost_coef);
1849 const BoundCost bound_cost =
1852 bound_cost.cost > 0) {
1853 const int span_violation = solver->CreateNewPositiveVariable();
1856 const int violation = solver->CreateNewConstraint(
1858 solver->SetCoefficient(violation, lp_cumuls.back(), 1.0);
1859 solver->SetCoefficient(violation, lp_cumuls.front(), -1.0);
1860 solver->SetCoefficient(violation, span_violation, -1.0);
1862 solver->SetObjectiveCoefficient(span_violation, bound_cost.cost);
1870 solver->SetCoefficient(
ct, min_start_cumul_, 1);
1871 solver->SetCoefficient(
ct, lp_cumuls.front(), -1);
1874 solver->SetCoefficient(
ct, max_end_cumul_, 1);
1875 solver->SetCoefficient(
ct, lp_cumuls.back(), -1);
1878 if (route_transit_cost !=
nullptr) {
1879 if (optimize_costs && span_cost_coef > 0) {
1880 const int64_t total_fixed_transit = std::accumulate(
1881 fixed_transit.begin(), fixed_transit.end(), 0,
CapAdd);
1882 *route_transit_cost =
CapProd(total_fixed_transit, span_cost_coef);
1884 *route_transit_cost = 0;
1892 current_route_break_variables_.clear();
1894 const std::vector<IntervalVar*>& breaks =
1896 const int num_breaks = breaks.size();
1900 if (num_breaks == 0) {
1902 for (
const auto& distance_duration :
1904 maximum_route_span =
1905 std::min(maximum_route_span, distance_duration.first);
1908 const int ct = solver->CreateNewConstraint(
1910 solver->SetCoefficient(
ct, lp_cumuls.back(), 1);
1911 solver->SetCoefficient(
ct, lp_cumuls.front(), -1);
1918 std::vector<int64_t> pre_travel(path_size - 1, 0);
1919 std::vector<int64_t> post_travel(path_size - 1, 0);
1921 const int pre_travel_index =
1923 if (pre_travel_index != -1) {
1927 const int post_travel_index =
1929 if (post_travel_index != -1) {
1938 std::vector<int> lp_break_start;
1939 std::vector<int> lp_break_duration;
1940 std::vector<int> lp_break_end;
1941 if (solver->IsCPSATSolver()) {
1942 lp_break_start.resize(num_breaks, -1);
1943 lp_break_duration.resize(num_breaks, -1);
1944 lp_break_end.resize(num_breaks, -1);
1947 std::vector<int> slack_exact_lower_bound_ct(path_size - 1, -1);
1948 std::vector<int> slack_linear_lower_bound_ct(path_size - 1, -1);
1950 const int64_t vehicle_start_min = current_route_min_cumuls_.front();
1951 const int64_t vehicle_start_max = current_route_max_cumuls_.front();
1952 const int64_t vehicle_end_min = current_route_min_cumuls_.back();
1953 const int64_t vehicle_end_max = current_route_max_cumuls_.back();
1954 const int all_break_variables_offset =
1955 vehicle_to_all_break_variables_offset_[vehicle];
1956 for (
int br = 0; br < num_breaks; ++br) {
1957 const IntervalVar& break_var = *breaks[br];
1958 if (!break_var.MustBePerformed())
continue;
1959 const int64_t break_start_min =
CapSub(break_var.StartMin(), cumul_offset);
1960 const int64_t break_start_max =
CapSub(break_var.StartMax(), cumul_offset);
1961 const int64_t break_end_min =
CapSub(break_var.EndMin(), cumul_offset);
1962 const int64_t break_end_max =
CapSub(break_var.EndMax(), cumul_offset);
1963 const int64_t break_duration_min = break_var.DurationMin();
1964 const int64_t break_duration_max = break_var.DurationMax();
1967 if (solver->IsCPSATSolver()) {
1968 if (break_end_max <= vehicle_start_min ||
1969 vehicle_end_max <= break_start_min) {
1970 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
1971 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
1972 current_route_break_variables_.push_back(-1);
1973 current_route_break_variables_.push_back(-1);
1976 lp_break_start[br] =
1977 solver->AddVariable(break_start_min, break_start_max);
1979 absl::StrFormat(
"lp_break_start(%ld)", br));
1980 lp_break_end[br] = solver->AddVariable(break_end_min, break_end_max);
1982 absl::StrFormat(
"lp_break_end(%ld)", br));
1983 lp_break_duration[br] =
1984 solver->AddVariable(break_duration_min, break_duration_max);
1986 absl::StrFormat(
"lp_break_duration(%ld)", br));
1988 solver->AddLinearConstraint(0, 0,
1989 {{lp_break_end[br], 1},
1990 {lp_break_start[br], -1},
1991 {lp_break_duration[br], -1}});
1993 all_break_variables_[all_break_variables_offset + 2 * br] =
1995 all_break_variables_[all_break_variables_offset + 2 * br + 1] =
1997 current_route_break_variables_.push_back(lp_break_start[br]);
1998 current_route_break_variables_.push_back(lp_break_end[br]);
2000 if (break_end_min <= vehicle_start_max ||
2001 vehicle_end_min <= break_start_max) {
2002 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
2003 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
2004 current_route_break_variables_.push_back(-1);
2005 current_route_break_variables_.push_back(-1);
2013 const int break_in_one_slack_ct = solver->CreateNewConstraint(1, 1);
2015 if (solver->IsCPSATSolver()) {
2017 if (break_end_min <= vehicle_start_max) {
2018 const int ct = solver->AddLinearConstraint(
2020 {{lp_cumuls.front(), 1}, {lp_break_end[br], -1}});
2021 const int break_is_before_route = solver->AddVariable(0, 1);
2023 solver, break_is_before_route,
2024 absl::StrFormat(
"break_is_before_route(%ld)", br));
2025 solver->SetEnforcementLiteral(
ct, break_is_before_route);
2026 solver->SetCoefficient(break_in_one_slack_ct, break_is_before_route, 1);
2029 if (vehicle_end_min <= break_start_max) {
2030 const int ct = solver->AddLinearConstraint(
2032 {{lp_break_start[br], 1}, {lp_cumuls.back(), -1}});
2033 const int break_is_after_route = solver->AddVariable(0, 1);
2035 solver, break_is_after_route,
2036 absl::StrFormat(
"break_is_after_route(%ld)", br));
2037 solver->SetEnforcementLiteral(
ct, break_is_after_route);
2038 solver->SetCoefficient(break_in_one_slack_ct, break_is_after_route, 1);
2043 for (
int pos = 0; pos < path_size - 1; ++pos) {
2046 const int64_t slack_start_min =
2047 CapAdd(current_route_min_cumuls_[pos], pre_travel[pos]);
2048 if (slack_start_min > break_start_max)
break;
2049 const int64_t slack_end_max =
2050 CapSub(current_route_max_cumuls_[pos + 1], post_travel[pos]);
2051 if (break_end_min > slack_end_max)
continue;
2052 const int64_t slack_duration_max =
2054 current_route_min_cumuls_[pos]),
2055 fixed_transit[pos]),
2057 if (slack_duration_max < break_duration_min)
continue;
2064 const int break_in_slack = solver->AddVariable(0, 1);
2066 solver, break_in_slack,
2067 absl::StrFormat(
"break_in_slack(%ld, %ld)", br, pos));
2068 if (slack_linear_lower_bound_ct[pos] == -1) {
2069 slack_linear_lower_bound_ct[pos] = solver->AddLinearConstraint(
2075 if (break_in_one_slack_ct < slack_linear_lower_bound_ct[pos]) {
2076 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2077 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2078 break_duration_min);
2080 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2081 break_duration_min);
2082 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2085 if (solver->IsCPSATSolver()) {
2090 const int break_duration_in_slack =
2091 solver->AddVariable(0, slack_duration_max);
2093 solver, break_duration_in_slack,
2094 absl::StrFormat(
"break_duration_in_slack(%ld, %ld)", br, pos));
2095 solver->AddProductConstraint(break_duration_in_slack,
2096 {break_in_slack, lp_break_duration[br]});
2097 if (slack_exact_lower_bound_ct[pos] == -1) {
2098 slack_exact_lower_bound_ct[pos] = solver->AddLinearConstraint(
2101 solver->SetCoefficient(slack_exact_lower_bound_ct[pos],
2102 break_duration_in_slack, 1);
2105 const int break_start_after_current_ct = solver->AddLinearConstraint(
2107 {{lp_break_start[br], 1}, {lp_cumuls[pos], -1}});
2108 solver->SetEnforcementLiteral(break_start_after_current_ct,
2111 const int break_ends_before_next_ct = solver->AddLinearConstraint(
2113 {{lp_cumuls[pos + 1], 1}, {lp_break_end[br], -1}});
2114 solver->SetEnforcementLiteral(break_ends_before_next_ct,
2120 if (!solver->IsCPSATSolver())
return true;
2126 if (!
interval->MustBePerformed())
return true;
2129 for (
int br = 1; br < num_breaks; ++br) {
2130 if (lp_break_start[br] == -1 || lp_break_start[br - 1] == -1)
continue;
2131 solver->AddLinearConstraint(
2133 {{lp_break_end[br - 1], -1}, {lp_break_start[br], 1}});
2136 for (
const auto& distance_duration :
2138 const int64_t limit = distance_duration.first;
2139 const int64_t min_break_duration = distance_duration.second;
2167 int previous_cover = solver->AddVariable(
CapAdd(vehicle_start_min, limit),
2168 CapAdd(vehicle_start_max, limit));
2170 solver->AddLinearConstraint(limit, limit,
2171 {{previous_cover, 1}, {lp_cumuls.front(), -1}});
2172 for (
int br = 0; br < num_breaks; ++br) {
2173 if (lp_break_start[br] == -1)
continue;
2174 const int64_t break_end_min =
CapSub(breaks[br]->EndMin(), cumul_offset);
2175 const int64_t break_end_max =
CapSub(breaks[br]->EndMax(), cumul_offset);
2178 const int break_is_eligible = solver->AddVariable(0, 1);
2180 absl::StrFormat(
"break_is_eligible(%ld)", br));
2181 const int break_is_not_eligible = solver->AddVariable(0, 1);
2183 solver, break_is_not_eligible,
2184 absl::StrFormat(
"break_is_not_eligible(%ld)", br));
2186 solver->AddLinearConstraint(
2187 1, 1, {{break_is_eligible, 1}, {break_is_not_eligible, 1}});
2188 const int positive_ct = solver->AddLinearConstraint(
2190 {{lp_break_end[br], 1}, {lp_break_start[br], -1}});
2191 solver->SetEnforcementLiteral(positive_ct, break_is_eligible);
2192 const int negative_ct = solver->AddLinearConstraint(
2194 {{lp_break_end[br], 1}, {lp_break_start[br], -1}});
2195 solver->SetEnforcementLiteral(negative_ct, break_is_not_eligible);
2202 const int break_cover = solver->AddVariable(
2206 absl::StrFormat(
"break_cover(%ld)", br));
2207 const int limit_cover_ct = solver->AddLinearConstraint(
2208 limit, limit, {{break_cover, 1}, {lp_break_end[br], -1}});
2209 solver->SetEnforcementLiteral(limit_cover_ct, break_is_eligible);
2210 const int empty_cover_ct = solver->AddLinearConstraint(
2211 CapAdd(vehicle_start_min, limit),
CapAdd(vehicle_start_min, limit),
2212 {{break_cover, 1}});
2213 solver->SetEnforcementLiteral(empty_cover_ct, break_is_not_eligible);
2216 solver->AddVariable(
CapAdd(vehicle_start_min, limit),
2219 solver->AddMaximumConstraint(cover, {previous_cover, break_cover});
2222 const int route_end_is_not_covered = solver->AddReifiedLinearConstraint(
2224 {{lp_cumuls.back(), 1}, {previous_cover, -1}});
2225 const int break_start_cover_ct = solver->AddLinearConstraint(
2227 {{previous_cover, 1}, {lp_break_start[br], -1}});
2228 solver->SetEnforcementLiteral(break_start_cover_ct,
2229 route_end_is_not_covered);
2231 previous_cover = cover;
2234 {{previous_cover, 1}, {lp_cumuls.back(), -1}});
2240 bool DimensionCumulOptimizerCore::SetGlobalConstraints(
2241 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
2242 bool optimize_costs, RoutingLinearSolverWrapper* solver) {
2246 if (optimize_costs && global_span_coeff > 0) {
2247 solver->SetObjectiveCoefficient(max_end_cumul_, global_span_coeff);
2248 solver->SetObjectiveCoefficient(min_start_cumul_, -global_span_coeff);
2252 for (
const RoutingDimension::NodePrecedence& precedence :
2254 const int first_cumul_var = index_to_cumul_variable_[precedence.first_node];
2255 const int second_cumul_var =
2256 index_to_cumul_variable_[precedence.second_node];
2257 if (first_cumul_var < 0 || second_cumul_var < 0) {
2262 DCHECK_NE(first_cumul_var, second_cumul_var)
2263 <<
"Dimension " << dimension_->
name()
2264 <<
" has a self-precedence on node " << precedence.first_node <<
".";
2267 const int ct = solver->CreateNewConstraint(
2269 solver->SetCoefficient(
ct, second_cumul_var, 1);
2270 solver->SetCoefficient(
ct, first_cumul_var, -1);
2273 if (!solver->IsCPSATSolver()) {
2279 const RoutingModel&
model = *dimension_->
model();
2280 const int num_vehicles =
model.vehicles();
2281 const auto& resource_groups =
model.GetResourceGroups();
2282 for (
int rg_index :
model.GetDimensionResourceGroupIndices(dimension_)) {
2292 const ResourceGroup& resource_group = *resource_groups[rg_index];
2293 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2295 const std::vector<ResourceGroup::Resource>& resources =
2296 resource_group.GetResources();
2297 int num_required_resources = 0;
2298 static const int kNoConstraint = -1;
2301 std::vector<int> vehicle_constraints(
model.vehicles(), kNoConstraint);
2302 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2303 if (
model.IsEnd(next_accessor(
model.Start(v))) &&
2304 !
model.IsVehicleUsedWhenEmpty(v)) {
2308 num_required_resources++;
2309 vehicle_constraints[v] = solver->CreateNewConstraint(1, 1);
2313 const int num_resources = resources.size();
2314 std::vector<int> resource_constraints(num_resources, kNoConstraint);
2315 int num_available_resources = 0;
2316 for (
int r = 0; r < num_resources; r++) {
2317 const ResourceGroup::Attributes& attributes =
2318 resources[r].GetDimensionAttributes(dimension_);
2319 if (attributes.start_domain().Max() < cumul_offset ||
2320 attributes.end_domain().Max() < cumul_offset) {
2326 num_available_resources++;
2327 resource_constraints[r] = solver->CreateNewConstraint(0, 1);
2330 if (num_required_resources > num_available_resources) {
2335 std::vector<int>& resource_to_vehicle_assignment_variables =
2336 resource_group_to_resource_to_vehicle_assignment_variables_[rg_index];
2337 resource_to_vehicle_assignment_variables.assign(
2338 num_resources * num_vehicles, -1);
2344 for (
int r = 0; r < num_resources; r++) {
2345 if (resource_constraints[r] == kNoConstraint)
continue;
2346 const ResourceGroup::Attributes& attributes =
2347 resources[r].GetDimensionAttributes(dimension_);
2348 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2349 if (vehicle_constraints[v] == kNoConstraint)
continue;
2351 const int assign_r_to_v = solver->AddVariable(0, 1);
2353 solver, assign_r_to_v,
2354 absl::StrFormat(
"assign_r_to_v(%ld, %ld)", r, v));
2355 resource_to_vehicle_assignment_variables[r * num_vehicles + v] =
2360 if (vehicle_constraints[v] < resource_constraints[r]) {
2361 solver->SetCoefficient(vehicle_constraints[v], assign_r_to_v, 1);
2362 solver->SetCoefficient(resource_constraints[r], assign_r_to_v, 1);
2364 solver->SetCoefficient(resource_constraints[r], assign_r_to_v, 1);
2365 solver->SetCoefficient(vehicle_constraints[v], assign_r_to_v, 1);
2368 const auto& add_domain_constraint =
2369 [&solver, cumul_offset, assign_r_to_v](
const Domain& domain,
2370 int cumul_variable) {
2374 ClosedInterval cumul_bounds;
2375 if (!GetDomainOffsetBounds(domain, cumul_offset, &cumul_bounds)) {
2377 solver->SetVariableBounds(assign_r_to_v, 0, 0);
2380 const int cumul_constraint = solver->AddLinearConstraint(
2381 cumul_bounds.start, cumul_bounds.end, {{cumul_variable, 1}});
2382 solver->SetEnforcementLiteral(cumul_constraint, assign_r_to_v);
2384 add_domain_constraint(attributes.start_domain(),
2385 index_to_cumul_variable_[
model.Start(v)]);
2386 add_domain_constraint(attributes.end_domain(),
2387 index_to_cumul_variable_[
model.End(v)]);
2394 #undef SET_DEBUG_VARIABLE_NAME
2396 void DimensionCumulOptimizerCore::SetValuesFromLP(
2397 const std::vector<int>& lp_variables, int64_t offset,
2398 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* lp_values)
const {
2399 if (lp_values ==
nullptr)
return;
2401 for (
int i = 0; i < lp_variables.size(); i++) {
2402 const int lp_var = lp_variables[i];
2403 if (lp_var < 0)
continue;
2404 const double lp_value_double = solver->GetValue(lp_var);
2405 const int64_t lp_value_int64 =
2408 : MathUtil::FastInt64Round(lp_value_double);
2409 (*lp_values)[i] =
CapAdd(lp_value_int64, offset);
2413 void DimensionCumulOptimizerCore::SetResourceIndices(
2414 RoutingLinearSolverWrapper* solver,
2415 std::vector<std::vector<int>>* resource_indices_per_group)
const {
2416 if (resource_indices_per_group ==
nullptr ||
2417 resource_group_to_resource_to_vehicle_assignment_variables_.empty()) {
2420 const RoutingModel&
model = *dimension_->model();
2421 const int num_vehicles =
model.vehicles();
2422 DCHECK(!
model.GetDimensionResourceGroupIndices(dimension_).empty());
2423 const auto& resource_groups =
model.GetResourceGroups();
2424 resource_indices_per_group->resize(resource_groups.size());
2425 for (
int rg_index :
model.GetDimensionResourceGroupIndices(dimension_)) {
2426 const ResourceGroup& resource_group = *resource_groups[rg_index];
2427 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2429 const int num_resources = resource_group.Size();
2430 std::vector<int>& resource_indices =
2431 resource_indices_per_group->at(rg_index);
2432 resource_indices.assign(num_vehicles, -1);
2434 const std::vector<int>& resource_to_vehicle_assignment_variables =
2435 resource_group_to_resource_to_vehicle_assignment_variables_[rg_index];
2436 DCHECK_EQ(resource_to_vehicle_assignment_variables.size(),
2437 num_resources * num_vehicles);
2438 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2439 for (
int r = 0; r < num_resources; r++) {
2440 const int assignment_var =
2441 resource_to_vehicle_assignment_variables[r * num_vehicles + v];
2442 if (assignment_var >= 0 && solver->GetValue(assignment_var) == 1) {
2444 resource_indices[v] = r;
2454 GlobalDimensionCumulOptimizer::GlobalDimensionCumulOptimizer(
2456 RoutingSearchParameters::SchedulingSolver solver_type)
2457 : optimizer_core_(dimension,
2459 !dimension->GetNodePrecedences().empty()) {
2460 switch (solver_type) {
2461 case RoutingSearchParameters::SCHEDULING_GLOP: {
2462 solver_ = std::make_unique<RoutingGlopWrapper>(
2466 GetGlopParametersForGlobalLP());
2469 case RoutingSearchParameters::SCHEDULING_CP_SAT: {
2470 solver_ = std::make_unique<RoutingCPSatWrapper>();
2474 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
2480 const std::function<int64_t(int64_t)>& next_accessor,
2481 int64_t* optimal_cost_without_transits) {
2483 int64_t transit_cost = 0;
2485 optimizer_core_.
Optimize(next_accessor, {}, solver_.get(),
nullptr,
2486 nullptr,
nullptr, &
cost, &transit_cost);
2488 optimal_cost_without_transits !=
nullptr) {
2489 *optimal_cost_without_transits =
CapSub(
cost, transit_cost);
2495 const std::function<int64_t(int64_t)>& next_accessor,
2496 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
2497 dimension_travel_info_per_route,
2498 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
2499 std::vector<std::vector<int>>* optimal_resource_indices) {
2500 return optimizer_core_.
Optimize(next_accessor,
2501 dimension_travel_info_per_route,
2502 solver_.get(), optimal_cumuls, optimal_breaks,
2503 optimal_resource_indices,
nullptr,
nullptr);
2507 const std::function<int64_t(int64_t)>& next_accessor,
2508 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
2509 dimension_travel_info_per_route,
2510 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks,
2511 std::vector<std::vector<int>>* resource_indices) {
2513 next_accessor, dimension_travel_info_per_route, solver_.get(),
2514 packed_cumuls, packed_breaks, resource_indices);
2519 const std::function<int64_t(int64_t)>& next_accessor,
2520 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
2523 std::vector<int64_t>* assignment_costs,
2524 std::vector<std::vector<int64_t>>* cumul_values,
2525 std::vector<std::vector<int64_t>>* break_values) {
2526 DCHECK(lp_optimizer !=
nullptr);
2527 DCHECK(mp_optimizer !=
nullptr);
2529 DCHECK_EQ(dimension, mp_optimizer->
dimension());
2531 DCHECK_NE(assignment_costs,
nullptr);
2533 (!
model->IsVehicleUsedWhenEmpty(v) &&
2534 next_accessor(
model->Start(v)) ==
model->End(v))) {
2535 assignment_costs->clear();
2538 if (
model->CheckLimit()) {
2543 const std::vector<ResourceGroup::Resource>& resources =
2545 const int num_resources = resources.size();
2546 std::vector<int> all_resource_indices(num_resources);
2547 std::iota(all_resource_indices.begin(), all_resource_indices.end(), 0);
2548 const bool use_mp_optimizer =
2552 use_mp_optimizer ? mp_optimizer : lp_optimizer;
2553 std::vector<DimensionSchedulingStatus> statuses =
2555 v, next_accessor, transit_accessor, resources, all_resource_indices,
2556 optimize_vehicle_costs, assignment_costs, cumul_values, break_values);
2558 if (assignment_costs->empty()) {
2562 DCHECK_EQ(assignment_costs->size(), num_resources);
2563 DCHECK_EQ(statuses.size(), num_resources);
2564 DCHECK(cumul_values ==
nullptr || cumul_values->size() == num_resources);
2565 DCHECK(break_values ==
nullptr || break_values->size() == num_resources);
2567 if (use_mp_optimizer) {
2571 return absl::c_any_of(*assignment_costs,
2572 [](int64_t
cost) {
return cost >= 0; });
2575 std::vector<int> mp_optimizer_resource_indices;
2576 for (
int r = 0; r < num_resources; r++) {
2578 mp_optimizer_resource_indices.push_back(r);
2582 std::vector<int64_t> mp_assignment_costs;
2583 std::vector<std::vector<int64_t>> mp_cumul_values;
2584 std::vector<std::vector<int64_t>> mp_break_values;
2586 v, next_accessor, transit_accessor, resources,
2587 mp_optimizer_resource_indices, optimize_vehicle_costs,
2588 &mp_assignment_costs,
2589 cumul_values ==
nullptr ?
nullptr : &mp_cumul_values,
2590 break_values ==
nullptr ?
nullptr : &mp_break_values);
2591 if (!mp_optimizer_resource_indices.empty() && mp_assignment_costs.empty()) {
2595 DCHECK_EQ(mp_assignment_costs.size(), mp_optimizer_resource_indices.size());
2596 DCHECK(cumul_values ==
nullptr ||
2597 mp_cumul_values.size() == mp_optimizer_resource_indices.size());
2598 DCHECK(break_values ==
nullptr ||
2599 mp_break_values.size() == mp_optimizer_resource_indices.size());
2600 for (
int i = 0; i < mp_optimizer_resource_indices.size(); i++) {
2601 assignment_costs->at(mp_optimizer_resource_indices[i]) =
2602 mp_assignment_costs[i];
2603 if (cumul_values !=
nullptr) {
2604 cumul_values->at(mp_optimizer_resource_indices[i])
2605 .swap(mp_cumul_values[i]);
2607 if (break_values !=
nullptr) {
2608 break_values->at(mp_optimizer_resource_indices[i])
2609 .swap(mp_break_values[i]);
2612 return absl::c_any_of(*assignment_costs,
2613 [](int64_t
cost) {
return cost >= 0; });
2617 std::vector<int> vehicles,
int num_resources,
2618 std::function<
const std::vector<int64_t>*(
int)>
2619 vehicle_to_resource_assignment_costs,
2620 std::vector<int>* resource_indices) {
2621 DCHECK_GE(num_resources, 1);
2622 const int num_vehicles = vehicles.size();
2623 int num_total_vehicles = -1;
2624 if (resource_indices !=
nullptr) {
2625 num_total_vehicles = resource_indices->size();
2628 resource_indices->clear();
2629 DCHECK_GE(num_total_vehicles, num_vehicles);
2630 for (
int v : vehicles) {
2632 DCHECK_LT(v, num_total_vehicles);
2640 std::vector<const std::vector<int64_t>*> vi_to_resource_cost(num_vehicles);
2641 int num_vehicles_to_assign = 0;
2642 for (
int i = 0; i < num_vehicles; ++i) {
2643 vi_to_resource_cost[i] = vehicle_to_resource_assignment_costs(vehicles[i]);
2644 if (!vi_to_resource_cost[i]->empty()) {
2645 DCHECK_EQ(vi_to_resource_cost[i]->size(), num_resources);
2646 ++num_vehicles_to_assign;
2649 if (num_vehicles_to_assign > num_resources) {
2650 VLOG(3) <<
"Less resources (" << num_resources <<
") than the vehicles"
2651 <<
" requiring one (" << num_vehicles_to_assign <<
")";
2658 for (
int i = 0; i < num_vehicles; ++i) {
2659 if (!vi_to_resource_cost[i]->empty() &&
2660 *absl::c_max_element(*vi_to_resource_cost[i]) < 0) {
2661 VLOG(3) <<
"Vehicle #" << vehicles[i] <<
" has no feasible resource";
2669 int64_t max_arc_cost = 0;
2670 for (
const std::vector<int64_t>* costs : vi_to_resource_cost) {
2671 if (costs->empty())
continue;
2672 max_arc_cost =
std::max(max_arc_cost, *absl::c_max_element(*costs));
2678 const int real_num_nodes = 4 + num_vehicles + num_resources;
2679 const int64_t max_acceptable_arc_cost =
kint64max / (3 * real_num_nodes) - 1;
2682 int cost_right_shift = 0;
2683 while ((max_arc_cost >> cost_right_shift) > max_acceptable_arc_cost) {
2693 2 + num_vehicles + num_resources,
2694 num_vehicles + num_vehicles * num_resources +
2696 const int source_index = num_vehicles + num_resources;
2697 const int sink_index = source_index + 1;
2698 const auto resource_index = [num_vehicles](
int r) {
2699 return num_vehicles + r;
2704 if (resource_indices !=
nullptr) {
2705 vehicle_to_resource_arc_index =
2708 for (
int vi = 0; vi < num_vehicles; ++vi) {
2709 const std::vector<int64_t>& assignment_costs = *vi_to_resource_cost[vi];
2710 if (assignment_costs.empty())
continue;
2716 for (
int r = 0; r < num_resources; r++) {
2717 const int64_t assignment_cost = assignment_costs[r];
2718 if (assignment_cost < 0)
continue;
2720 vi, resource_index(r), 1, assignment_cost >> cost_right_shift);
2721 if (resource_indices !=
nullptr) {
2722 vehicle_to_resource_arc_index[vi][r] =
arc;
2728 for (
int r = 0; r < num_resources; r++) {
2738 VLOG(3) <<
"Non-OPTIMAL flow result";
2742 if (resource_indices !=
nullptr) {
2744 resource_indices->assign(num_total_vehicles, -1);
2745 for (
int vi = 0; vi < num_vehicles; ++vi) {
2746 for (
int r = 0; r < num_resources; r++) {
2747 const ArcIndex arc = vehicle_to_resource_arc_index[vi][r];
2749 resource_indices->at(vehicles[vi]) = r;
2758 return cost << cost_right_shift;
2762 if (number ==
kint64min)
return "-infty";
2763 if (number ==
kint64max)
return "+infty";
2764 return std::to_string(number);
2768 const ::google::protobuf::RepeatedField<int64_t>* domain) {
2769 if (domain->size() > 2 && domain->size() % 2 == 0) {
2770 std::string s =
"∈ ";
2771 for (
int i = 0; i < domain->size(); i += 2) {
2772 s += absl::StrFormat(
"[%s, %s]",
Int64ToStr(domain->Get(i)),
2774 if (i < domain->size() - 2) s +=
" ∪ ";
2777 }
else if (domain->size() == 2) {
2778 if (domain->Get(0) == domain->Get(1)) {
2779 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
2780 }
else if (domain->Get(0) == 0 && domain->Get(1) == 1) {
2786 return absl::StrFormat(
"≤ %s",
Int64ToStr(domain->Get(1)));
2788 return absl::StrFormat(
"≥ %s",
Int64ToStr(domain->Get(0)));
2790 return absl::StrFormat(
"∈ [%ls, %s]",
Int64ToStr(domain->Get(0)),
2792 }
else if (domain->size() == 1) {
2793 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
2795 return absl::StrFormat(
"∈ Unknown domain (size=%ld)", domain->size());
2800 std::pair<sat::IntegerVariableProto, int>& variable_pair,
2801 const sat::CpSolverResponse& response_) {
2803 sat::IntegerVariableProto& variable = variable_pair.first;
2804 const int index = variable_pair.second;
2805 if (response_.IsInitialized() && variable.IsInitialized() &&
2808 const double lp_value_double = response_.solution(
index);
2809 const int64_t lp_value_int64 =
2822 const sat::CpModelProto& model_,
2823 bool show_enforcement =
true) {
2825 if (constraint.has_linear()) {
2826 const auto& linear = constraint.linear();
2827 for (
int j = 0; j < linear.vars().size(); ++j) {
2828 const std::string sign = linear.coeffs(j) > 0 ?
"+" :
"-";
2829 const std::string mult =
2830 std::abs(linear.coeffs(j)) != 1
2831 ? std::to_string(std::abs(linear.coeffs(j))) +
" * "
2833 if (j > 0 || sign !=
"+") s += sign +
" ";
2834 s += mult + model_.variables(linear.vars(j)).name() +
" ";
2839 if (show_enforcement) {
2840 for (
int j = 0; j < constraint.enforcement_literal_size(); ++j) {
2841 s += (j == 0) ?
"\t if " :
" and ";
2842 s += model_.variables(constraint.enforcement_literal(j)).
name();
2846 s += constraint.ShortDebugString();
2852 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>&
2854 absl::flat_hash_map<std::string, std::vector<int>>& variable_instances,
2855 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>&
2857 const sat::CpSolverResponse& response_,
const std::string& variable,
2858 std::string prefix =
"") {
2859 if (variable.empty()) {
2861 const auto& childs = variable_childs[
""];
2862 for (
const std::string& child : childs) {
2865 response_, child, prefix) +
2871 const auto& instances = variable_instances[variable];
2872 std::string variable_display = variable;
2873 std::size_t bracket_pos = variable.find_last_of(
')');
2874 if (bracket_pos != std::string::npos) {
2875 variable_display = variable.substr(bracket_pos + 1);
2877 std::string s = variable_display +
" | ";
2878 prefix += std::string(variable_display.length(),
' ') +
" | ";
2879 for (
int i = 0; i < instances.size(); ++i) {
2880 const std::string instance_name =
2881 absl::StrFormat(
"%s(%ld)", variable, instances[i]);
2882 if (i > 0) s += prefix;
2883 s += absl::StrFormat(
"%ld: %s", instances[i],
2887 const auto& childs = variable_childs[instance_name];
2888 for (
const std::string& child : childs) {
2889 s +=
"\n" + prefix +
"| ";
2891 response_, child, prefix +
"| ");
2893 if (childs.empty()) s +=
"\n";
2900 std::vector<std::vector<std::string>> constraints_apart;
2901 constraints_apart.push_back(
2902 {
"compression_cost",
"travel_compression_absolute"});
2907 absl::flat_hash_map<std::string, std::vector<int>> variable_instances;
2911 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>
2914 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>
2916 variable_children[
""] = {};
2918 const int num_constraints = model_.constraints_size();
2919 const int num_variables = model_.variables_size();
2920 int num_binary_variables = 0;
2921 for (
int i = 0; i < num_variables; ++i) {
2922 const auto& variable = model_.variables(i);
2923 const auto&
name = variable.name();
2924 const int pos_bracket =
name.find_last_of(
'(');
2925 if (pos_bracket != std::string::npos) {
2926 const std::string lemma =
name.substr(0, pos_bracket);
2927 const int pos_closing_bracket =
name.find_last_of(
')');
2928 CHECK_NE(pos_closing_bracket, std::string::npos);
2930 std::stoi(
name.substr(pos_bracket + 1, pos_closing_bracket));
2931 std::vector<int>* instances =
gtl::FindOrNull(variable_instances, lemma);
2932 if (instances !=
nullptr) {
2933 instances->push_back(
index);
2935 variable_instances[lemma] = {
index};
2937 variable_children[
name] = {};
2939 std::string parent =
"";
2940 const int pos_parent_closing_bracket = lemma.find_last_of(
')');
2941 if (pos_parent_closing_bracket != std::string::npos) {
2942 parent = lemma.substr(0, pos_parent_closing_bracket + 1);
2944 variable_children[parent].emplace(lemma);
2945 variables[
name] = std::make_pair(variable, i);
2946 if (variable.domain(0) == 0 & variable.domain(1) == 1) {
2947 ++num_binary_variables;
2958 absl::flat_hash_map<std::string, std::vector<sat::ConstraintProto>>
2960 absl::flat_hash_map<std::vector<std::string>,
2961 std::vector<sat::ConstraintProto>>
2963 for (
int i = 0; i < num_constraints; ++i) {
2964 const auto& constraint = model_.constraints(i);
2965 std::string enforcement =
"";
2966 if (constraint.enforcement_literal_size() == 1) {
2967 enforcement = model_.variables(constraint.enforcement_literal(0)).name();
2968 }
else if (constraint.enforcement_literal_size() > 1) {
2969 enforcement =
"multiple";
2971 if (constraint.has_linear()) {
2972 const auto& linear = constraint.linear();
2973 std::vector<std::string> key;
2974 for (
int j = 0; j < linear.vars().size(); ++j) {
2975 std::string var_name = model_.variables(linear.vars(j)).name();
2976 std::string lemma = var_name.substr(0, var_name.find_last_of(
'('));
2977 key.push_back(lemma);
2980 if (constraint_group !=
nullptr) {
2981 constraint_group->push_back(constraint);
2983 constraint_groups[key] = {constraint};
2987 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
2988 if (constraints_enforced !=
nullptr) {
2989 constraints[enforcement].push_back(constraint);
2991 constraints[enforcement] = {constraint};
2995 const std::string prefix_constraint =
" • ";
2996 std::string s =
"Using RoutingCPSatWrapper.\n";
2999 for (
int i = 0; i < objective_coefficients_.size(); ++i) {
3000 double coeff = objective_coefficients_[i];
3002 s += absl::StrFormat(
" | %f * %s\n", coeff, model_.variables(i).name());
3006 s += absl::StrFormat(
"\nVariables %d (%d Binary - %d Non Binary)\n",
3007 num_variables, num_binary_variables,
3008 num_variables - num_binary_variables);
3010 response_,
"",
" | ");
3011 s += absl::StrFormat(
"\n\nConstraints (%d)\n", num_constraints);
3014 s +=
"\n- Not enforced\n";
3015 bool at_least_one_not_enforced =
false;
3016 for (
const auto& pair : constraint_groups) {
3017 if (!std::count(constraints_apart.begin(), constraints_apart.end(),
3019 for (
const auto& constraint : pair.second) {
3022 at_least_one_not_enforced =
true;
3026 if (!at_least_one_not_enforced) {
3027 s += prefix_constraint +
"None\n";
3031 s +=
"\n- Single enforcement\n";
3032 bool at_least_one_single_enforced =
false;
3033 for (
const auto& pair : variable_instances) {
3034 const std::string lemma = pair.first;
3035 bool found_one_constraint =
false;
3036 std::string prefix =
"";
3037 for (
int instance : pair.second) {
3038 const std::string enforcement =
3039 absl::StrFormat(
"%s(%d)", lemma, instance);
3040 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
3041 std::string prefix_instance =
"";
3042 if (constraints_enforced !=
nullptr) {
3043 at_least_one_single_enforced =
true;
3044 if (!found_one_constraint) {
3045 found_one_constraint =
true;
3046 s += prefix_constraint +
"if " + lemma +
" | ";
3048 std::string(prefix_constraint.size() + 1 + lemma.size(),
' ') +
3053 s += absl::StrFormat(
"%d: | ", instance);
3054 prefix_instance = prefix +
" | ";
3056 for (
const auto& constraint : *constraints_enforced) {
3058 s += prefix_instance;
3066 if (!at_least_one_single_enforced) {
3067 s += prefix_constraint +
"None\n";
3071 s +=
"\n- Multiple enforcement\n";
3072 auto* constraints_multiple_enforced =
3074 if (constraints_multiple_enforced !=
nullptr) {
3075 for (
const auto& constraint : *constraints_multiple_enforced) {
3080 s += prefix_constraint +
"None\n";
3084 s +=
"\n- Set apart\n";
3085 bool at_least_one_apart =
false;
3086 for (
const auto& pair : constraint_groups) {
3087 if (std::count(constraints_apart.begin(), constraints_apart.end(),
3089 for (
const auto& constraint : pair.second) {
3092 at_least_one_apart =
true;
3096 if (!at_least_one_apart) {
3097 s += prefix_constraint +
"None\n";
const RoutingDimension & dimension() const
bool PropagateCumulBounds(const std::function< int64_t(int64_t)> &next_accessor, int64_t cumul_offset, const std::vector< RoutingModel::RouteDimensionTravelInfo > *dimension_travel_info_per_route=nullptr)
CumulBoundsPropagator(const RoutingDimension *dimension)
DimensionSchedulingStatus OptimizeSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, int64_t *cost, int64_t *transit_cost, bool clear_lp=true)
std::vector< DimensionSchedulingStatus > OptimizeSingleRouteWithResources(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, const RouteDimensionTravelInfo &dimension_travel_info, const std::vector< RoutingModel::ResourceGroup::Resource > &resources, const std::vector< int > &resource_indices, bool optimize_vehicle_costs, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *costs_without_transits, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values, bool clear_lp=true)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
const RoutingDimension * dimension() const
DimensionSchedulingStatus ComputeSingleRouteSolutionCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, RoutingLinearSolverWrapper *solver, const std::vector< int64_t > &solution_cumul_values, const std::vector< int64_t > &solution_break_values, int64_t *cost, int64_t *transit_cost, int64_t *cost_offset=nullptr, bool reuse_previous_model_if_possible=true, bool clear_lp=false, bool clear_solution_constraints=true, absl::Duration *const solve_duration=nullptr)
DimensionSchedulingStatus Optimize(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RouteDimensionTravelInfo > &dimension_travel_info_per_route, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, std::vector< std::vector< int >> *resource_indices_per_group, int64_t *cost, int64_t *transit_cost, bool clear_lp=true)
DimensionSchedulingStatus OptimizeAndPackSingleRoute(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo &dimension_travel_info, const RoutingModel::ResourceGroup::Resource *resource, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
DimensionSchedulingStatus OptimizeAndPack(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RouteDimensionTravelInfo > &dimension_travel_info_per_route, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, std::vector< std::vector< int >> *resource_indices_per_group)
We call domain any subset of Int64 = [kint64min, kint64max].
static Domain AllValues()
Returns the full domain Int64.
int64_t Min() const
Returns the min value of the domain.
int64_t Max() const
Returns the max value of the domain.
DimensionSchedulingStatus ComputePackedCumuls(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::RouteDimensionTravelInfo > &dimension_travel_info_per_route, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks, std::vector< std::vector< int >> *resource_indices_per_group)
DimensionSchedulingStatus ComputeCumulCostWithoutFixedTransits(const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
DimensionSchedulingStatus ComputeCumuls(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::RouteDimensionTravelInfo > &dimension_travel_info_per_route, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, std::vector< std::vector< int >> *optimal_resource_indices_per_group)
const RoutingDimension * dimension() const
virtual int64_t Min() const =0
virtual int64_t Max() const =0
std::vector< DimensionSchedulingStatus > ComputeRouteCumulCostsForResourcesWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, const std::vector< RoutingModel::ResourceGroup::Resource > &resources, const std::vector< int > &resource_indices, bool optimize_vehicle_costs, std::vector< int64_t > *optimal_costs_without_transits, std::vector< std::vector< int64_t >> *optimal_cumuls, std::vector< std::vector< int64_t >> *optimal_breaks)
DimensionSchedulingStatus ComputeRouteCumulCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost)
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
LocalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
const RoutingDimension * dimension() const
DimensionSchedulingStatus ComputeRouteSolutionCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, const std::vector< int64_t > &solution_cumul_values, const std::vector< int64_t > &solution_break_values, int64_t *solution_cost, int64_t *cost_offset=nullptr, bool reuse_previous_model_if_possible=false, bool clear_lp=true, absl::Duration *solve_duration=nullptr)
DimensionSchedulingStatus ComputePackedRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, const RoutingModel::ResourceGroup::Resource *resource, std::vector< int64_t > *packed_cumuls, std::vector< int64_t > *packed_breaks)
DimensionSchedulingStatus ComputeRouteCumulsAndCost(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, int64_t *optimal_cost)
DimensionSchedulingStatus ComputeRouteCumuls(int vehicle, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo &dimension_travel_info, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
static int64_t FastInt64Round(double x)
int64_t GetObjectiveValue() const override
std::string PrintModel() const override
Dimensions represent quantities accumulated at nodes along the routes.
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
int64_t GetCumulVarSoftUpperBoundCoefficient(int64_t index) const
Returns the cost coefficient of the soft upper bound of a cumul variable for a given variable index.
RoutingModel * model() const
Returns the model on which the dimension was created.
int64_t GetGlobalOptimizerOffset() const
int64_t GetPickupToDeliveryLimitForPair(int pair_index, int pickup, int delivery) const
bool HasCumulVarSoftLowerBound(int64_t index) const
Returns true if a soft lower bound has been set for a given variable index.
BoundCost GetSoftSpanUpperBoundForVehicle(int vehicle) const
int64_t GetSpanCostCoefficientForVehicle(int vehicle) const
int64_t global_span_cost_coefficient() const
int64_t GetSpanUpperBoundForVehicle(int vehicle) const
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
SortedDisjointIntervalList GetAllowedIntervalsInRange(int64_t index, int64_t min_value, int64_t max_value) const
Returns allowed intervals for a given node in a given interval.
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
bool HasCumulVarSoftUpperBound(int64_t index) const
Returns true if a soft upper bound has been set for a given variable index.
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
IntVar * SlackVar(int64_t index) const
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
int64_t GetLocalOptimizerOffsetForVehicle(int vehicle) const
const std::string & name() const
Returns the name of the dimension.
const std::vector< NodePrecedence > & GetNodePrecedences() const
int64_t GetCumulVarSoftUpperBound(int64_t index) const
Returns the soft upper bound of a cumul variable for a given variable index.
const std::vector< std::pair< int64_t, int64_t > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
bool HasSoftSpanUpperBounds() const
int64_t GetCumulVarSoftLowerBoundCoefficient(int64_t index) const
Returns the cost coefficient of the soft lower bound of a cumul variable for a given variable index.
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
int64_t GetCumulVarSoftLowerBound(int64_t index) const
Returns the soft lower bound of a cumul variable for a given variable index.
const std::vector< SortedDisjointIntervalList > & forbidden_intervals() const
Returns forbidden intervals for each node.
virtual void SetParameters(const std::string ¶meters)=0
virtual bool ModelIsEmpty() const
virtual bool IsCPSATSolver()=0
virtual int64_t GetObjectiveValue() const =0
virtual void AddObjectiveConstraint()=0
virtual void ClearObjective()=0
virtual double GetValue(int index) const =0
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
virtual bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound)=0
virtual void SetObjectiveCoefficient(int index, double coefficient)=0
virtual int64_t GetVariableUpperBound(int index) const =0
virtual bool SolutionIsInteger() const =0
virtual int64_t GetVariableLowerBound(int index) const =0
A Resource sets attributes (costs/constraints) for a set of dimensions.
A ResourceGroup defines a set of available Resources with attributes on one or multiple dimensions.
bool VehicleRequiresAResource(int vehicle) const
const std::vector< Resource > & GetResources() const
const std::vector< int > & GetDimensionResourceGroupIndices(const RoutingDimension *dimension) const
Returns the indices of resource groups for this dimension.
bool CheckLimit(absl::Duration offset=absl::ZeroDuration())
Returns true if the search limit has been crossed with the given time offset.
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
int vehicles() const
Returns the number of vehicle routes in the model.
absl::Duration RemainingTime() const
Returns the time left in the search limit.
ArcIndex AddArcWithCapacityAndUnitCost(NodeIndex tail, NodeIndex head, FlowQuantity capacity, CostValue unit_cost)
FlowQuantity Flow(ArcIndex arc) const
void SetNodeSupply(NodeIndex node, FlowQuantity supply)
CostValue OptimalCost() const
absl::Span< const double > coefficients
static const int64_t kint64max
static const int64_t kint64min
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
double FindBestScalingAndComputeErrors(const std::vector< double > &coefficients, const std::vector< double > &lower_bounds, const std::vector< double > &upper_bounds, int64_t max_absolute_activity, double wanted_absolute_activity_precision, double *relative_coeff_error, double *scaled_sum_error)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
std::vector< SlopeAndYIntercept > PiecewiseLinearFormulationToSlopeAndYIntercept(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl_function, int index_start, int index_end)
std::string ConstraintToString(const sat::ConstraintProto &constraint, const sat::CpModelProto &model_, bool show_enforcement=true)
std::string DomainToString(const ::google::protobuf::RepeatedField< int64_t > *domain)
int64_t ComputeBestVehicleToResourceAssignment(std::vector< int > vehicles, int num_resources, std::function< const std::vector< int64_t > *(int)> vehicle_to_resource_assignment_costs, std::vector< int > *resource_indices)
int64_t CapSub(int64_t x, int64_t y)
int64_t ComputeConvexPiecewiseLinearFormulationValue(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl, int64_t x, double delta)
PiecewiseEvaluationStatus
@ SMALLER_THAN_LOWER_BOUND
@ LARGER_THAN_UPPER_BOUND
DimensionSchedulingStatus
int64_t CapProd(int64_t x, int64_t y)
void FillPathEvaluation(const std::vector< int64_t > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
std::vector< bool > SlopeAndYInterceptToConvexityRegions(const std::vector< SlopeAndYIntercept > &slope_and_y_intercept)
std::string Int64ToStr(int64_t number)
std::string VariablesToString(absl::flat_hash_map< std::string, std::pair< sat::IntegerVariableProto, int >> &variables, absl::flat_hash_map< std::string, std::vector< int >> &variable_instances, absl::flat_hash_map< std::string, absl::flat_hash_set< std::string >> &variable_childs, const sat::CpSolverResponse &response_, const std::string &variable, std::string prefix="")
bool ComputeVehicleToResourcesAssignmentCosts(int v, const RoutingModel::ResourceGroup &resource_group, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, bool optimize_vehicle_costs, LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, std::vector< int64_t > *assignment_costs, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values)
PiecewiseEvaluationStatus ComputePiecewiseLinearFormulationValue(const RoutingModel::RouteDimensionTravelInfo::TransitionInfo::PiecewiseLinearFormulation &pwl, int64_t x, int64_t *value, double delta)
std::string VariableToString(std::pair< sat::IntegerVariableProto, int > &variable_pair, const sat::CpSolverResponse &response_)
#define SET_DEBUG_VARIABLE_NAME(solver, var, name)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
Represents a closed interval [start, end].
Contains the information for a single transition on the route.
Contains the information needed by the solver to optimize a dimension's cumuls with travel-start depe...
#define VLOG(verboselevel)