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/strings/string_view.h"
35#include "absl/time/time.h"
36#include "absl/types/span.h"
65 parameters.set_use_preprocessing(
false);
75bool GetCumulBoundsWithOffset(
const RoutingDimension& dimension,
76 int64_t node_index, int64_t cumul_offset,
77 int64_t* lower_bound, int64_t* upper_bound) {
78 DCHECK(lower_bound !=
nullptr);
79 DCHECK(upper_bound !=
nullptr);
81 const IntVar& cumul_var = *dimension.CumulVar(node_index);
82 *upper_bound = cumul_var.
Max();
83 if (*upper_bound < cumul_offset) {
87 const int64_t first_after_offset =
88 std::max(dimension.GetFirstPossibleGreaterOrEqualValueForNode(
89 node_index, cumul_offset),
91 DCHECK_LT(first_after_offset, std::numeric_limits<int64_t>::max());
92 *lower_bound =
CapSub(first_after_offset, cumul_offset);
93 DCHECK_GE(*lower_bound, 0);
95 if (*upper_bound == std::numeric_limits<int64_t>::max()) {
98 *upper_bound =
CapSub(*upper_bound, cumul_offset);
99 DCHECK_GE(*upper_bound, *lower_bound);
103int64_t GetFirstPossibleValueForCumulWithOffset(
104 const RoutingDimension& dimension, int64_t node_index,
105 int64_t lower_bound_without_offset, int64_t cumul_offset) {
107 dimension.GetFirstPossibleGreaterOrEqualValueForNode(
108 node_index,
CapAdd(lower_bound_without_offset, cumul_offset)),
112int64_t GetLastPossibleValueForCumulWithOffset(
113 const RoutingDimension& dimension, int64_t node_index,
114 int64_t upper_bound_without_offset, int64_t cumul_offset) {
116 dimension.GetLastPossibleLessOrEqualValueForNode(
117 node_index,
CapAdd(upper_bound_without_offset, cumul_offset)),
121using PDPosition = RoutingModel::PickupDeliveryPosition;
127void StoreVisitedPickupDeliveryPairsOnRoute(
128 const RoutingDimension& dimension,
int vehicle,
129 const std::function<int64_t(int64_t)>& next_accessor,
130 std::vector<int>* visited_pairs,
131 std::vector<std::pair<int64_t, int64_t>>*
132 visited_pickup_delivery_indices_for_pair) {
134 DCHECK_EQ(visited_pickup_delivery_indices_for_pair->size(),
135 dimension.model()->GetPickupAndDeliveryPairs().size());
136 DCHECK(std::all_of(visited_pickup_delivery_indices_for_pair->begin(),
137 visited_pickup_delivery_indices_for_pair->end(),
138 [](std::pair<int64_t, int64_t> p) {
139 return p.first == -1 && p.second == -1;
141 visited_pairs->clear();
142 if (!dimension.HasPickupToDeliveryLimits()) {
145 const RoutingModel& model = *dimension.model();
147 int64_t node_index = model.Start(vehicle);
148 while (!model.IsEnd(node_index)) {
149 if (model.IsPickup(node_index)) {
151 const std::optional<PDPosition> pickup_position =
152 model.GetPickupPosition(node_index);
153 DCHECK(pickup_position.has_value());
154 const int pair_index = pickup_position->pd_pair_index;
155 (*visited_pickup_delivery_indices_for_pair)[pair_index].first =
157 visited_pairs->push_back(pair_index);
158 }
else if (model.IsDelivery(node_index)) {
161 const std::optional<PDPosition> delivery_position =
162 model.GetDeliveryPosition(node_index);
163 DCHECK(delivery_position.has_value());
164 const int pair_index = delivery_position->pd_pair_index;
165 std::pair<int64_t, int64_t>& pickup_delivery_index =
166 (*visited_pickup_delivery_indices_for_pair)[pair_index];
167 if (pickup_delivery_index.first < 0) {
170 node_index = next_accessor(node_index);
173 pickup_delivery_index.second = node_index;
175 node_index = next_accessor(node_index);
189 const int vehicles =
dimension->model()->vehicles();
190 solver_.resize(vehicles);
191 switch (solver_type) {
194 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
198 std::make_unique<RoutingGlopWrapper>(
false, parameters);
203 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
204 solver_[vehicle] = std::make_unique<RoutingCPSatWrapper>();
209 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
214 int vehicle,
double solve_duration_ratio,
215 const std::function<int64_t(int64_t)>& next_accessor,
216 int64_t* optimal_cost) {
217 DCHECK_GT(solve_duration_ratio, 0);
218 DCHECK_LE(solve_duration_ratio, 1);
219 int64_t transit_cost = 0;
221 optimizer_core_.OptimizeSingleRouteWithResource(
222 vehicle, solve_duration_ratio, next_accessor,
225 optimal_cost !=
nullptr,
226 solver_[vehicle].get(),
nullptr,
227 nullptr, optimal_cost, &transit_cost);
229 optimal_cost !=
nullptr) {
230 DCHECK_GE(*optimal_cost, 0);
231 *optimal_cost =
CapAdd(*optimal_cost, transit_cost);
238 int vehicle,
double solve_duration_ratio,
239 const std::function<int64_t(int64_t)>& next_accessor,
240 const RoutingModel::ResourceGroup::Resource* resource,
241 int64_t* optimal_cost_without_transits) {
242 DCHECK_GT(solve_duration_ratio, 0);
243 DCHECK_LE(solve_duration_ratio, 1);
244 return optimizer_core_.OptimizeSingleRouteWithResource(
245 vehicle, solve_duration_ratio, next_accessor,
247 optimal_cost_without_transits !=
nullptr,
248 solver_[vehicle].get(),
nullptr,
249 nullptr, optimal_cost_without_transits,
nullptr);
254 int vehicle,
double solve_duration_ratio,
255 const std::function<int64_t(int64_t)>& next_accessor,
256 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
257 absl::Span<const RoutingModel::ResourceGroup::Resource> resources,
258 absl::Span<const int> resource_indices,
bool optimize_vehicle_costs,
259 std::vector<int64_t>* optimal_costs_without_transits,
260 std::vector<std::vector<int64_t>>* optimal_cumuls,
261 std::vector<std::vector<int64_t>>* optimal_breaks) {
262 DCHECK_GT(solve_duration_ratio, 0);
263 DCHECK_LE(solve_duration_ratio, 1);
264 return optimizer_core_.OptimizeSingleRouteWithResources(
265 vehicle, solve_duration_ratio, next_accessor, transit_accessor,
nullptr,
266 resources, resource_indices, optimize_vehicle_costs,
267 solver_[vehicle].get(), optimal_cumuls, optimal_breaks,
268 optimal_costs_without_transits,
nullptr);
272 int vehicle,
double solve_duration_ratio,
273 const std::function<int64_t(int64_t)>& next_accessor,
274 const RoutingModel::RouteDimensionTravelInfo* dimension_travel_info,
275 const RoutingModel::ResourceGroup::Resource* resource,
276 std::vector<int64_t>* optimal_cumuls,
277 std::vector<int64_t>* optimal_breaks) {
278 DCHECK_GT(solve_duration_ratio, 0);
279 DCHECK_LE(solve_duration_ratio, 1);
280 return optimizer_core_.OptimizeSingleRouteWithResource(
281 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
282 resource,
true, solver_[vehicle].get(),
283 optimal_cumuls, optimal_breaks,
nullptr,
289 int vehicle,
double solve_duration_ratio,
290 const std::function<int64_t(int64_t)>& next_accessor,
291 const RoutingModel::RouteDimensionTravelInfo* dimension_travel_info,
292 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
293 int64_t* optimal_cost_without_transits) {
294 DCHECK_GT(solve_duration_ratio, 0);
295 DCHECK_LE(solve_duration_ratio, 1);
296 return optimizer_core_.OptimizeSingleRouteWithResource(
297 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
298 nullptr,
true, solver_[vehicle].get(),
299 optimal_cumuls, optimal_breaks, optimal_cost_without_transits,
nullptr);
304 int vehicle,
double solve_duration_ratio,
305 const std::function<int64_t(int64_t)>& next_accessor,
306 const RoutingModel::RouteDimensionTravelInfo* dimension_travel_info,
307 absl::Span<const int64_t> solution_cumul_values,
308 absl::Span<const int64_t> solution_break_values, int64_t* solution_cost,
309 int64_t* cost_offset,
bool reuse_previous_model_if_possible,
bool clear_lp,
310 absl::Duration* solve_duration) {
311 DCHECK_GT(solve_duration_ratio, 0);
312 DCHECK_LE(solve_duration_ratio, 1);
314 return optimizer_core_.ComputeSingleRouteSolutionCostWithoutFixedTransits(
315 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
316 solver, solution_cumul_values, solution_break_values, solution_cost,
317 cost_offset, reuse_previous_model_if_possible, clear_lp,
318 true, solve_duration);
323 int vehicle,
double solve_duration_ratio,
324 const std::function<int64_t(int64_t)>& next_accessor,
325 const RoutingModel::RouteDimensionTravelInfo* dimension_travel_info,
326 const RoutingModel::ResourceGroup::Resource* resource,
327 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks) {
328 DCHECK_GT(solve_duration_ratio, 0);
329 DCHECK_LE(solve_duration_ratio, 1);
330 return optimizer_core_.OptimizeAndPackSingleRoute(
331 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
332 resource, solver_[vehicle].get(), packed_cumuls, packed_breaks);
335const int CumulBoundsPropagator::kNoParent = -2;
336const int CumulBoundsPropagator::kParentToBePropagated = -1;
340 outgoing_arcs_.resize(num_nodes_);
341 node_in_queue_.resize(num_nodes_,
false);
342 tree_parent_node_of_.resize(num_nodes_, kNoParent);
343 propagated_bounds_.resize(num_nodes_);
344 visited_pickup_delivery_indices_for_pair_.resize(
345 dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1});
348void CumulBoundsPropagator::AddArcs(
int first_index,
int second_index,
351 outgoing_arcs_[PositiveNode(first_index)].push_back(
352 {PositiveNode(second_index), offset});
353 AddNodeToQueue(PositiveNode(first_index));
355 outgoing_arcs_[NegativeNode(second_index)].push_back(
356 {NegativeNode(first_index), offset});
357 AddNodeToQueue(NegativeNode(second_index));
360bool CumulBoundsPropagator::InitializeArcsAndBounds(
361 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
362 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
363 dimension_travel_info_per_route) {
364 propagated_bounds_.assign(num_nodes_, std::numeric_limits<int64_t>::min());
366 for (std::vector<ArcInfo>& arcs : outgoing_arcs_) {
370 RoutingModel*
const model = dimension_.model();
371 std::vector<int64_t>& lower_bounds = propagated_bounds_;
373 for (
int vehicle = 0; vehicle < model->vehicles(); vehicle++) {
374 const std::function<int64_t(int64_t, int64_t)>& transit_accessor =
375 dimension_.transit_evaluator(vehicle);
377 int node = model->Start(vehicle);
378 int index_on_route = 0;
380 int64_t cumul_lb, cumul_ub;
381 if (!GetCumulBoundsWithOffset(dimension_, node, cumul_offset, &cumul_lb,
385 lower_bounds[PositiveNode(node)] = cumul_lb;
386 if (cumul_ub < std::numeric_limits<int64_t>::max()) {
387 lower_bounds[NegativeNode(node)] = -cumul_ub;
390 if (model->IsEnd(node)) {
394 const int next = next_accessor(node);
395 int64_t transit = transit_accessor(node, next);
396 if (dimension_travel_info_per_route !=
nullptr &&
397 !dimension_travel_info_per_route->empty()) {
398 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
399 transition_info = (*dimension_travel_info_per_route)[vehicle]
400 .transition_info[index_on_route];
401 transit = transition_info.compressed_travel_value_lower_bound +
402 transition_info.pre_travel_transit_value +
403 transition_info.post_travel_transit_value;
406 const IntVar& slack_var = *dimension_.SlackVar(node);
409 AddArcs(node, next,
CapAdd(transit, slack_var.Min()));
410 if (slack_var.Max() < std::numeric_limits<int64_t>::max()) {
412 AddArcs(next, node,
CapSub(-slack_var.Max(), transit));
419 const int64_t span_ub = dimension_.GetSpanUpperBoundForVehicle(vehicle);
420 if (span_ub < std::numeric_limits<int64_t>::max()) {
421 AddArcs(model->End(vehicle), model->Start(vehicle), -span_ub);
425 std::vector<int> visited_pairs;
426 StoreVisitedPickupDeliveryPairsOnRoute(
427 dimension_, vehicle, next_accessor, &visited_pairs,
428 &visited_pickup_delivery_indices_for_pair_);
429 for (
int pair_index : visited_pairs) {
430 const int64_t pickup_index =
431 visited_pickup_delivery_indices_for_pair_[pair_index].first;
432 const int64_t delivery_index =
433 visited_pickup_delivery_indices_for_pair_[pair_index].second;
434 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
436 DCHECK_GE(pickup_index, 0);
437 if (delivery_index < 0) {
442 const int64_t limit = dimension_.GetPickupToDeliveryLimitForPair(
443 pair_index, model->GetPickupPosition(pickup_index)->alternative_index,
444 model->GetDeliveryPosition(delivery_index)->alternative_index);
445 if (limit < std::numeric_limits<int64_t>::max()) {
447 AddArcs(delivery_index, pickup_index, -limit);
452 for (
const auto [first_node, second_node, offset, performed_constraint] :
453 dimension_.GetNodePrecedences()) {
454 if (next_accessor(first_node) == -1 || next_accessor(second_node) == -1) {
457 const bool first_node_unperformed =
458 lower_bounds[PositiveNode(first_node)] ==
459 std::numeric_limits<int64_t>::min();
460 const bool second_node_unperformed =
461 lower_bounds[PositiveNode(second_node)] ==
462 std::numeric_limits<int64_t>::min();
463 switch (RoutingDimension::GetPrecedenceStatus(first_node_unperformed,
464 second_node_unperformed,
465 performed_constraint)) {
466 case RoutingDimension::PrecedenceStatus::kActive:
468 case RoutingDimension::PrecedenceStatus::kInactive:
470 case RoutingDimension::PrecedenceStatus::kInvalid:
473 AddArcs(first_node, second_node, offset);
479bool CumulBoundsPropagator::UpdateCurrentLowerBoundOfNode(
int node,
482 const int cumul_var_index = node / 2;
484 if (node == PositiveNode(cumul_var_index)) {
486 propagated_bounds_[node] = GetFirstPossibleValueForCumulWithOffset(
487 dimension_, cumul_var_index, new_lb, offset);
490 const int64_t new_ub =
CapSub(0, new_lb);
491 propagated_bounds_[node] =
492 CapSub(0, GetLastPossibleValueForCumulWithOffset(
493 dimension_, cumul_var_index, new_ub, offset));
497 const int64_t cumul_lower_bound =
498 propagated_bounds_[PositiveNode(cumul_var_index)];
500 const int64_t negated_cumul_upper_bound =
501 propagated_bounds_[NegativeNode(cumul_var_index)];
503 return CapAdd(negated_cumul_upper_bound, cumul_lower_bound) <= 0;
506bool CumulBoundsPropagator::DisassembleSubtree(
int source,
int target) {
507 tmp_dfs_stack_.clear();
508 tmp_dfs_stack_.push_back(source);
509 while (!tmp_dfs_stack_.empty()) {
510 const int tail = tmp_dfs_stack_.back();
511 tmp_dfs_stack_.pop_back();
512 for (
const ArcInfo& arc : outgoing_arcs_[tail]) {
513 const int child_node = arc.head;
514 if (tree_parent_node_of_[child_node] != tail)
continue;
515 if (child_node == target)
return false;
516 tree_parent_node_of_[child_node] = kParentToBePropagated;
517 tmp_dfs_stack_.push_back(child_node);
524 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
525 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
526 dimension_travel_info_per_route) {
527 tree_parent_node_of_.assign(num_nodes_, kNoParent);
528 DCHECK(std::none_of(node_in_queue_.begin(), node_in_queue_.end(),
529 [](
bool b) { return b; }));
530 DCHECK(bf_queue_.empty());
532 if (!InitializeArcsAndBounds(next_accessor, cumul_offset,
533 dimension_travel_info_per_route)) {
534 return CleanupAndReturnFalse();
537 std::vector<int64_t>& current_lb = propagated_bounds_;
540 while (!bf_queue_.empty()) {
541 const int node = bf_queue_.front();
542 bf_queue_.pop_front();
543 node_in_queue_[node] =
false;
545 if (tree_parent_node_of_[node] == kParentToBePropagated) {
551 const int64_t lower_bound = current_lb[node];
552 for (
const ArcInfo& arc : outgoing_arcs_[node]) {
555 const int64_t induced_lb =
556 (lower_bound == std::numeric_limits<int64_t>::min())
557 ? std::numeric_limits<int64_t>::min()
558 :
CapAdd(lower_bound, arc.offset);
560 const int head_node = arc.head;
561 if (induced_lb <= current_lb[head_node]) {
566 if (!UpdateCurrentLowerBoundOfNode(head_node, induced_lb, cumul_offset) ||
567 !DisassembleSubtree(head_node, node)) {
570 return CleanupAndReturnFalse();
573 tree_parent_node_of_[head_node] = node;
574 AddNodeToQueue(head_node);
581 const RoutingDimension*
dimension,
bool use_precedence_propagator)
583 visited_pickup_delivery_indices_for_pair_(
584 dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1}) {
585 if (use_precedence_propagator) {
586 propagator_ = std::make_unique<CumulBoundsPropagator>(dimension);
588 const RoutingModel& model = *dimension_->model();
589 if (dimension_->HasBreakConstraints()) {
593 const int num_vehicles = model.vehicles();
594 vehicle_to_all_break_variables_offset_.reserve(num_vehicles);
595 int num_break_vars = 0;
596 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
597 vehicle_to_all_break_variables_offset_.push_back(num_break_vars);
598 const auto& intervals = dimension_->GetBreakIntervalsOfVehicle(vehicle);
599 num_break_vars += 2 * intervals.size();
601 all_break_variables_.resize(num_break_vars, -1);
603 if (!model.GetDimensionResourceGroupIndices(dimension_).empty()) {
604 resource_class_to_vehicle_assignment_variables_per_group_.resize(
605 model.GetResourceGroups().size());
606 resource_class_ignored_resources_per_group_.resize(
607 model.GetResourceGroups().size());
613 int vehicle,
double solve_duration_ratio,
614 const std::function<int64_t(int64_t)>& next_accessor,
615 const RouteDimensionTravelInfo* dimension_travel_info,
617 absl::Span<const int64_t> solution_cumul_values,
618 absl::Span<const int64_t> solution_break_values,
619 int64_t* cost_without_transits, int64_t* cost_offset,
620 bool reuse_previous_model_if_possible,
bool clear_lp,
621 bool clear_solution_constraints, absl::Duration*
const solve_duration) {
622 absl::Duration solve_duration_value;
623 int64_t cost_offset_value;
624 if (!reuse_previous_model_if_possible || solver->
ModelIsEmpty()) {
625 InitOptimizer(solver);
628 DCHECK_EQ(propagator_.get(),
nullptr);
630 RoutingModel*
const model = dimension_->model();
631 const bool optimize_vehicle_costs =
632 !model->IsEnd(next_accessor(model->Start(vehicle))) ||
633 model->IsVehicleUsedWhenEmpty(vehicle);
634 if (!SetRouteCumulConstraints(
635 vehicle, next_accessor, dimension_->transit_evaluator(vehicle),
636 dimension_travel_info,
637 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle),
638 optimize_vehicle_costs, solver,
nullptr, &cost_offset_value)) {
641 if (model->CheckLimit()) {
644 solve_duration_value = model->RemainingTime() * solve_duration_ratio;
645 if (solve_duration !=
nullptr) *solve_duration = solve_duration_value;
646 if (cost_offset !=
nullptr) *cost_offset = cost_offset_value;
648 CHECK(cost_offset !=
nullptr)
649 <<
"Cannot reuse model without the cost_offset";
650 cost_offset_value = *cost_offset;
651 CHECK(solve_duration !=
nullptr)
652 <<
"Cannot reuse model without the solve_duration";
653 solve_duration_value = *solve_duration * solve_duration_ratio;
657 DCHECK_EQ(solution_cumul_values.size(),
658 current_route_cumul_variables_.size());
659 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
660 if (solution_cumul_values[i] < current_route_min_cumuls_[i] ||
661 solution_cumul_values[i] > current_route_max_cumuls_[i]) {
665 solution_cumul_values[i],
666 solution_cumul_values[i]);
670 DCHECK_EQ(solution_break_values.size(),
671 current_route_break_variables_.size());
672 std::vector<int64_t> current_route_min_breaks(
673 current_route_break_variables_.size());
674 std::vector<int64_t> current_route_max_breaks(
675 current_route_break_variables_.size());
676 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
677 current_route_min_breaks[i] =
679 current_route_max_breaks[i] =
682 solution_break_values[i],
683 solution_break_values[i]);
692 if (cost_without_transits !=
nullptr) {
693 *cost_without_transits =
699 }
else if (clear_solution_constraints) {
700 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
702 current_route_min_cumuls_[i],
703 current_route_max_cumuls_[i]);
705 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
707 current_route_min_breaks[i],
708 current_route_max_breaks[i]);
716void ClearIfNonNull(std::vector<T>* v) {
725 int vehicle,
double solve_duration_ratio,
726 const std::function<int64_t(int64_t)>& next_accessor,
727 const RouteDimensionTravelInfo* dimension_travel_info,
728 const RoutingModel::ResourceGroup::Resource* resource,
730 std::vector<int64_t>* cumul_values, std::vector<int64_t>* break_values,
731 int64_t* cost_without_transits, int64_t* transit_cost,
bool clear_lp) {
732 if (cost_without_transits !=
nullptr) *cost_without_transits = -1;
733 ClearIfNonNull(cumul_values);
734 ClearIfNonNull(break_values);
736 const std::vector<Resource> resources =
737 resource ==
nullptr ? std::vector<Resource>()
738 : std::vector<Resource>({*resource});
739 const std::vector<int> resource_indices =
740 resource ==
nullptr ? std::vector<int>() : std::vector<int>({0});
741 std::vector<int64_t> costs_without_transits;
742 std::vector<std::vector<int64_t>> cumul_values_vec;
743 std::vector<std::vector<int64_t>> break_values_vec;
744 const std::vector<DimensionSchedulingStatus> statuses =
746 vehicle, solve_duration_ratio, next_accessor,
747 dimension_->transit_evaluator(vehicle), dimension_travel_info,
748 resources, resource_indices, optimize_vehicle_costs, solver,
749 cumul_values !=
nullptr ? &cumul_values_vec :
nullptr,
750 break_values !=
nullptr ? &break_values_vec :
nullptr,
751 cost_without_transits !=
nullptr ? &costs_without_transits :
nullptr,
752 transit_cost, clear_lp);
754 if (
dimension()->model()->CheckLimit()) {
757 DCHECK_EQ(statuses.size(), 1);
762 if (cost_without_transits !=
nullptr) {
763 *cost_without_transits = costs_without_transits[0];
765 if (cumul_values !=
nullptr) *cumul_values = std::move(cumul_values_vec[0]);
766 if (break_values !=
nullptr) *break_values = std::move(break_values_vec[0]);
773using ResourceGroup = RoutingModel::ResourceGroup;
775bool GetDomainOffsetBounds(
const Domain& domain, int64_t offset,
777 const int64_t lower_bound =
778 std::max<int64_t>(
CapSub(domain.
Min(), offset), 0);
779 const int64_t upper_bound =
780 domain.
Max() == std::numeric_limits<int64_t>::max()
781 ? std::numeric_limits<int64_t>::max()
782 :
CapSub(domain.Max(), offset);
783 if (lower_bound > upper_bound)
return false;
789bool GetIntervalIntersectionWithOffsetDomain(
const ClosedInterval& interval,
792 ClosedInterval* intersection) {
794 *intersection = interval;
798 if (!GetDomainOffsetBounds(domain, offset, &offset_domain)) {
801 const int64_t intersection_lb = std::max(interval.start, offset_domain.start);
802 const int64_t intersection_ub = std::min(interval.end, offset_domain.end);
803 if (intersection_lb > intersection_ub)
return false;
812 solver.GetVariableUpperBound(index));
815bool TightenStartEndVariableBoundsWithResource(
816 const RoutingDimension& dimension,
const ResourceGroup::Resource& resource,
820 const ResourceGroup::Attributes& attributes =
821 resource.GetDimensionAttributes(&dimension);
824 return GetIntervalIntersectionWithOffsetDomain(start_bounds,
825 attributes.start_domain(),
826 offset, &new_start_bounds) &&
827 solver->SetVariableBounds(start_index, new_start_bounds.start,
828 new_start_bounds.end) &&
829 GetIntervalIntersectionWithOffsetDomain(
830 end_bounds, attributes.end_domain(), offset, &new_end_bounds) &&
831 solver->SetVariableBounds(end_index, new_end_bounds.start,
835bool TightenStartEndVariableBoundsWithAssignedResources(
836 const RoutingDimension& dimension,
int v,
int start_index,
int end_index,
838 const RoutingModel* model = dimension.model();
839 for (
int rg_index : model->GetDimensionResourceGroupIndices(&dimension)) {
840 const IntVar* res_var = model->ResourceVars(rg_index)[v];
841 DCHECK(res_var->Bound());
842 const int res_index = res_var->Value();
845 DCHECK(!model->GetResourceGroup(rg_index)->VehicleRequiresAResource(v));
848 const ResourceGroup::Resource& resource =
849 model->GetResourceGroup(rg_index)->GetResource(res_index);
850 if (!TightenStartEndVariableBoundsWithResource(
851 dimension, resource, GetVariableBounds(start_index, *solver),
852 start_index, GetVariableBounds(end_index, *solver), end_index,
862std::vector<DimensionSchedulingStatus>
864 int vehicle,
double solve_duration_ratio,
865 const std::function<int64_t(int64_t)>& next_accessor,
866 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
867 const RouteDimensionTravelInfo* dimension_travel_info,
868 absl::Span<const RoutingModel::ResourceGroup::Resource> resources,
869 absl::Span<const int> resource_indices,
bool optimize_vehicle_costs,
871 std::vector<std::vector<int64_t>>* cumul_values,
872 std::vector<std::vector<int64_t>>* break_values,
873 std::vector<int64_t>* costs_without_transits, int64_t* transit_cost,
875 ClearIfNonNull(costs_without_transits);
876 const bool optimize_with_resources = !resource_indices.empty();
877 if (!optimize_with_resources && !resources.empty())
return {};
879 InitOptimizer(solver);
882 DCHECK_EQ(propagator_.get(),
nullptr);
884 RoutingModel*
const model =
dimension()->model();
885 if (model->IsEnd(next_accessor(model->Start(vehicle))) &&
886 !model->IsVehicleUsedWhenEmpty(vehicle)) {
888 DCHECK(!optimize_with_resources);
889 optimize_vehicle_costs =
false;
892 const int64_t cumul_offset =
893 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle);
894 int64_t cost_offset = 0;
895 if (!SetRouteCumulConstraints(vehicle, next_accessor, transit_accessor,
896 dimension_travel_info, cumul_offset,
897 optimize_vehicle_costs, solver, transit_cost,
899 if (costs_without_transits !=
nullptr) {
900 costs_without_transits->assign(1, -1);
904 DCHECK_GE(current_route_cumul_variables_.size(), 2);
910 const int num_solves = resource_indices.empty() ? 1 : resource_indices.size();
911 if (costs_without_transits !=
nullptr) {
912 costs_without_transits->assign(num_solves, -1);
914 if (cumul_values !=
nullptr) cumul_values->assign(num_solves, {});
915 if (break_values !=
nullptr) break_values->assign(num_solves, {});
917 const int start_cumul = current_route_cumul_variables_[0];
918 const ClosedInterval start_bounds = GetVariableBounds(start_cumul, *solver);
919 const int end_cumul = current_route_cumul_variables_.back();
920 const ClosedInterval end_bounds = GetVariableBounds(end_cumul, *solver);
921 std::vector<DimensionSchedulingStatus> statuses;
922 statuses.reserve(num_solves);
923 for (
int i = 0; i < num_solves; i++) {
924 if (model->CheckLimit()) {
926 ClearIfNonNull(costs_without_transits);
927 ClearIfNonNull(cumul_values);
928 ClearIfNonNull(break_values);
932 if (optimize_with_resources &&
933 !TightenStartEndVariableBoundsWithResource(
934 *dimension_, resources[resource_indices[i]], start_bounds,
935 start_cumul, end_bounds, end_cumul, cumul_offset, solver)) {
942 solver->
Solve(model->RemainingTime() * solve_duration_ratio));
946 if (costs_without_transits !=
nullptr) {
947 costs_without_transits->at(i) =
948 optimize_vehicle_costs
953 if (cumul_values !=
nullptr) {
954 SetValuesFromLP(current_route_cumul_variables_, cumul_offset, solver,
955 &cumul_values->at(i));
957 if (break_values !=
nullptr) {
958 SetValuesFromLP(current_route_break_variables_, cumul_offset, solver,
959 &break_values->at(i));
970 const std::function<int64_t(int64_t)>& next_accessor,
971 const std::vector<RouteDimensionTravelInfo>&
972 dimension_travel_info_per_route,
974 std::vector<int64_t>* break_values,
975 std::vector<std::vector<int>>* resource_indices_per_group,
976 int64_t* cost_without_transits, int64_t* transit_cost,
bool clear_lp,
977 bool optimize_resource_assignment) {
978 InitOptimizer(solver);
979 if (!optimize_resource_assignment) {
980 DCHECK_EQ(resource_indices_per_group,
nullptr);
985 const bool optimize_costs =
986 (cumul_values !=
nullptr) || (cost_without_transits !=
nullptr);
987 bool has_vehicles_being_optimized =
false;
989 const int64_t cumul_offset = dimension_->GetGlobalOptimizerOffset();
991 if (propagator_ !=
nullptr &&
992 !propagator_->PropagateCumulBounds(next_accessor, cumul_offset,
993 &dimension_travel_info_per_route)) {
997 int64_t total_transit_cost = 0;
998 int64_t total_cost_offset = 0;
999 const RoutingModel* model = dimension_->model();
1000 for (
int vehicle = 0; vehicle < model->vehicles(); vehicle++) {
1001 int64_t route_transit_cost = 0;
1002 int64_t route_cost_offset = 0;
1003 const bool vehicle_is_used =
1004 !model->IsEnd(next_accessor(model->Start(vehicle))) ||
1005 model->IsVehicleUsedWhenEmpty(vehicle);
1006 const bool optimize_vehicle_costs = optimize_costs && vehicle_is_used;
1007 const RouteDimensionTravelInfo*
const dimension_travel_info =
1008 dimension_travel_info_per_route.empty()
1010 : &dimension_travel_info_per_route[vehicle];
1011 if (!SetRouteCumulConstraints(
1012 vehicle, next_accessor, dimension_->transit_evaluator(vehicle),
1013 dimension_travel_info, cumul_offset, optimize_vehicle_costs, solver,
1014 &route_transit_cost, &route_cost_offset)) {
1017 DCHECK_GE(current_route_cumul_variables_.size(), 2);
1018 if (vehicle_is_used && !optimize_resource_assignment) {
1020 if (!TightenStartEndVariableBoundsWithAssignedResources(
1021 *dimension_, vehicle, current_route_cumul_variables_[0],
1022 current_route_cumul_variables_.back(), cumul_offset, solver)) {
1026 total_transit_cost =
CapAdd(total_transit_cost, route_transit_cost);
1027 total_cost_offset =
CapAdd(total_cost_offset, route_cost_offset);
1028 has_vehicles_being_optimized |= optimize_vehicle_costs;
1030 if (transit_cost !=
nullptr) {
1031 *transit_cost = total_transit_cost;
1034 if (!SetGlobalConstraints(next_accessor, cumul_offset,
1035 has_vehicles_being_optimized,
1036 optimize_resource_assignment, solver)) {
1041 solver->
Solve(model->RemainingTime());
1049 SetValuesFromLP(index_to_cumul_variable_, cumul_offset, solver, cumul_values);
1050 SetValuesFromLP(all_break_variables_, cumul_offset, solver, break_values);
1051 SetResourceIndices(solver, resource_indices_per_group);
1053 if (cost_without_transits !=
nullptr) {
1054 *cost_without_transits =
1065 const std::function<int64_t(int64_t)>& next_accessor,
1066 const std::vector<RouteDimensionTravelInfo>&
1067 dimension_travel_info_per_route,
1069 std::vector<int64_t>* break_values) {
1076 packing_parameters = original_params;
1079 solver->
SetParameters(packing_parameters.SerializeAsString());
1082 Optimize(next_accessor, dimension_travel_info_per_route, solver,
1088 std::vector<int> vehicles(
dimension()->model()->vehicles());
1089 std::iota(vehicles.begin(), vehicles.end(), 0);
1092 status = PackRoutes(std::move(vehicles), 1.0,
1093 solver, packing_parameters);
1103 const int64_t global_offset = dimension_->GetGlobalOptimizerOffset();
1104 SetValuesFromLP(index_to_cumul_variable_, global_offset, solver,
1106 SetValuesFromLP(all_break_variables_, global_offset, solver, break_values);
1113 int vehicle,
double solve_duration_ratio,
1114 const std::function<int64_t(int64_t)>& next_accessor,
1115 const RouteDimensionTravelInfo* dimension_travel_info,
1116 const RoutingModel::ResourceGroup::Resource* resource,
1118 std::vector<int64_t>* break_values) {
1122 packing_parameters = original_params;
1125 solver->
SetParameters(packing_parameters.SerializeAsString());
1132 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
1133 resource,
true, solver,
1140 PackRoutes({vehicle}, solve_duration_ratio, solver, packing_parameters);
1149 const int64_t local_offset =
1150 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle);
1151 SetValuesFromLP(current_route_cumul_variables_, local_offset, solver,
1153 SetValuesFromLP(current_route_break_variables_, local_offset, solver,
1160 std::vector<int> vehicles,
double solve_duration_ratio,
1163 const RoutingModel* model = dimension_->model();
1179 for (
int vehicle : vehicles) {
1181 index_to_cumul_variable_[model->End(vehicle)], 1);
1184 glop::GlopParameters current_params;
1185 const auto retry_solving = [¤t_params, model, solver,
1186 solve_duration_ratio]() {
1190 current_params.set_use_dual_simplex(!current_params.use_dual_simplex());
1191 solver->SetParameters(current_params.SerializeAsString());
1192 return solver->Solve(model->RemainingTime() * solve_duration_ratio);
1194 if (solver->Solve(model->RemainingTime() * solve_duration_ratio) ==
1196 if (solver->IsCPSATSolver()) {
1200 current_params = packing_parameters;
1208 solver->ClearObjective();
1209 for (
int vehicle : vehicles) {
1210 const int end_cumul_var = index_to_cumul_variable_[model->End(vehicle)];
1212 solver->SetVariableBounds(
1213 end_cumul_var, solver->GetVariableLowerBound(end_cumul_var),
1217 solver->SetObjectiveCoefficient(
1218 index_to_cumul_variable_[model->Start(vehicle)], -1);
1222 solver->Solve(model->RemainingTime() * solve_duration_ratio);
1223 if (!solver->IsCPSATSolver() &&
1225 status = retry_solving();
1230#define SET_DEBUG_VARIABLE_NAME(solver, var, name) \
1233 solver->SetVariableName(var, name); \
1237void DimensionCumulOptimizerCore::InitOptimizer(
1238 RoutingLinearSolverWrapper* solver) {
1240 index_to_cumul_variable_.assign(dimension_->cumuls().size(), -1);
1241 max_end_cumul_ = solver->CreateNewPositiveVariable();
1243 min_start_cumul_ = solver->CreateNewPositiveVariable();
1247bool DimensionCumulOptimizerCore::ExtractRouteCumulBounds(
1248 absl::Span<const int64_t> route, int64_t cumul_offset) {
1249 const int route_size = route.size();
1250 current_route_min_cumuls_.resize(route_size);
1251 current_route_max_cumuls_.resize(route_size);
1254 for (
int pos = 0; pos < route_size; ++pos) {
1255 if (!GetCumulBoundsWithOffset(*dimension_, route[pos], cumul_offset,
1256 ¤t_route_min_cumuls_[pos],
1257 ¤t_route_max_cumuls_[pos])) {
1264bool DimensionCumulOptimizerCore::TightenRouteCumulBounds(
1265 absl::Span<const int64_t> route, absl::Span<const int64_t> min_transits,
1266 int64_t cumul_offset) {
1267 const int route_size = route.size();
1268 if (propagator_ !=
nullptr) {
1269 for (
int pos = 0; pos < route_size; pos++) {
1270 const int64_t node = route[pos];
1271 current_route_min_cumuls_[pos] = propagator_->CumulMin(node);
1272 DCHECK_GE(current_route_min_cumuls_[pos], 0);
1273 current_route_max_cumuls_[pos] = propagator_->CumulMax(node);
1274 DCHECK_GE(current_route_max_cumuls_[pos], current_route_min_cumuls_[pos]);
1281 for (
int pos = 1; pos < route_size; ++pos) {
1282 const int64_t slack_min = dimension_->SlackVar(route[pos - 1])->Min();
1283 current_route_min_cumuls_[pos] = std::max(
1284 current_route_min_cumuls_[pos],
1286 CapAdd(current_route_min_cumuls_[pos - 1], min_transits[pos - 1]),
1288 current_route_min_cumuls_[pos] = GetFirstPossibleValueForCumulWithOffset(
1289 *dimension_, route[pos], current_route_min_cumuls_[pos], cumul_offset);
1290 if (current_route_min_cumuls_[pos] > current_route_max_cumuls_[pos]) {
1295 for (
int pos = route_size - 2; pos >= 0; --pos) {
1298 if (current_route_max_cumuls_[pos + 1] <
1299 std::numeric_limits<int64_t>::max()) {
1300 const int64_t slack_min = dimension_->SlackVar(route[pos])->Min();
1301 current_route_max_cumuls_[pos] = std::min(
1302 current_route_max_cumuls_[pos],
1303 CapSub(
CapSub(current_route_max_cumuls_[pos + 1], min_transits[pos]),
1305 current_route_max_cumuls_[pos] = GetLastPossibleValueForCumulWithOffset(
1306 *dimension_, route[pos], current_route_max_cumuls_[pos],
1308 if (current_route_max_cumuls_[pos] < current_route_min_cumuls_[pos]) {
1319 const auto& x_anchors = pwl_function.
x_anchors();
1320 const auto& y_anchors = pwl_function.
y_anchors();
1321 if (index_end < 0) index_end = x_anchors.size() - 1;
1322 const int num_segments = index_end - index_start;
1323 DCHECK_GE(num_segments, 1);
1324 std::vector<SlopeAndYIntercept> slope_and_y_intercept(num_segments);
1325 for (
int seg = index_start; seg < index_end; ++seg) {
1326 auto& [slope, y_intercept] = slope_and_y_intercept[seg - index_start];
1327 slope = (y_anchors[seg + 1] - y_anchors[seg]) /
1328 static_cast<double>(x_anchors[seg + 1] - x_anchors[seg]);
1329 y_intercept = y_anchors[seg] - slope * x_anchors[seg];
1331 return slope_and_y_intercept;
1335 absl::Span<const SlopeAndYIntercept> slope_and_y_intercept) {
1336 CHECK(!slope_and_y_intercept.empty());
1337 std::vector<bool> convex(slope_and_y_intercept.size(),
false);
1338 double previous_slope = std::numeric_limits<double>::max();
1339 for (
int i = 0; i < slope_and_y_intercept.size(); ++i) {
1340 const auto& pair = slope_and_y_intercept[i];
1341 if (pair.slope < previous_slope) {
1344 previous_slope = pair.slope;
1352double FindBestScaling(absl::Span<const double> coefficients,
1353 absl::Span<const double> lower_bounds,
1354 absl::Span<const double> upper_bounds,
1355 int64_t max_absolute_activity,
1356 double wanted_absolute_activity_precision) {
1357 double unused_relative_coeff_error = 0;
1358 double unused_scaled_sum_error = 0;
1360 coefficients, lower_bounds, upper_bounds, max_absolute_activity,
1361 wanted_absolute_activity_precision, &unused_relative_coeff_error,
1362 &unused_scaled_sum_error);
1366bool DimensionCumulOptimizerCore::SetRouteTravelConstraints(
1367 const RouteDimensionTravelInfo* dimension_travel_info,
1368 absl::Span<const int> lp_slacks, absl::Span<const int64_t> fixed_transit,
1370 const std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1371 const int path_size = lp_cumuls.size();
1373 if (dimension_travel_info ==
nullptr ||
1374 dimension_travel_info->transition_info.empty()) {
1379 for (
int pos = 0; pos < path_size - 1; ++pos) {
1381 solver->CreateNewConstraint(fixed_transit[pos], fixed_transit[pos]);
1382 solver->SetCoefficient(ct, lp_cumuls[pos + 1], 1);
1383 solver->SetCoefficient(ct, lp_cumuls[pos], -1);
1384 solver->SetCoefficient(ct, lp_slacks[pos], -1);
1391 for (
int pos = 0; pos < path_size - 1; ++pos) {
1395 const int compression_cost = solver->CreateNewPositiveVariable();
1397 absl::StrFormat(
"compression_cost(%ld)", pos));
1404 const int relative_compression_cost = solver->CreateNewPositiveVariable();
1406 solver, relative_compression_cost,
1407 absl::StrFormat(
"relative_compression_cost(%ld)", pos));
1409 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
1410 transition_info = dimension_travel_info->transition_info[pos];
1411 const FloatSlopePiecewiseLinearFunction& travel_function =
1412 transition_info.travel_start_dependent_travel;
1413 const auto& travel_x_anchors = travel_function.x_anchors();
1417 const int64_t pre_travel_transit = transition_info.pre_travel_transit_value;
1418 const int64_t post_travel_transit =
1419 transition_info.post_travel_transit_value;
1420 const int64_t compressed_travel_value_lower_bound =
1421 transition_info.compressed_travel_value_lower_bound;
1422 const int64_t travel_value_upper_bound =
1423 transition_info.travel_value_upper_bound;
1429 const int travel_value = solver->AddVariable(
1430 compressed_travel_value_lower_bound, travel_value_upper_bound);
1432 absl::StrFormat(
"travel_value(%ld)", pos));
1433 const int travel_start = solver->AddVariable(
1434 current_route_min_cumuls_[pos] + pre_travel_transit,
1435 current_route_max_cumuls_[pos + 1] - post_travel_transit -
1436 compressed_travel_value_lower_bound);
1438 absl::StrFormat(
"travel_start(%ld)", pos));
1441 solver->AddLinearConstraint(pre_travel_transit, pre_travel_transit,
1442 {{travel_start, 1}, {lp_cumuls[pos], -1}});
1447 const int num_pwl_anchors = travel_x_anchors.size();
1448 int index_anchor_start = 0;
1449 while (index_anchor_start < num_pwl_anchors - 1 &&
1450 travel_x_anchors[index_anchor_start + 1] <=
1451 current_route_min_cumuls_[pos] + pre_travel_transit) {
1452 ++index_anchor_start;
1454 int index_anchor_end = num_pwl_anchors - 1;
1455 while (index_anchor_end > 0 &&
1456 travel_x_anchors[index_anchor_end - 1] >=
1457 current_route_max_cumuls_[pos] + pre_travel_transit) {
1461 if (index_anchor_start >= index_anchor_end)
return false;
1466 const std::vector<SlopeAndYIntercept> slope_and_y_intercept =
1468 travel_function, index_anchor_start, index_anchor_end);
1471 const std::vector<bool> convexities =
1474 int nb_bin_variables = 0;
1475 for (
const bool convexity : convexities) {
1478 if (nb_bin_variables >= 2)
break;
1481 const bool need_bins = (nb_bin_variables > 1);
1483 const int travel_start_in_one_segment_ct =
1484 need_bins ? solver->CreateNewConstraint(1, 1)
1487 int belongs_to_this_segment_var;
1488 for (
int seg = 0; seg < convexities.size(); ++seg) {
1489 if (need_bins && convexities[seg]) {
1490 belongs_to_this_segment_var = solver->AddVariable(0, 1);
1492 solver, belongs_to_this_segment_var,
1493 absl::StrFormat(
"travel_start(%ld)belongs_to_seg(%ld)", pos, seg));
1494 solver->SetCoefficient(travel_start_in_one_segment_ct,
1495 belongs_to_this_segment_var, 1);
1500 const int64_t lower_bound_interval =
1501 seg > 0 ? travel_x_anchors[index_anchor_start + seg]
1502 : current_route_min_cumuls_[pos] + pre_travel_transit;
1503 int64_t end_of_seg = seg + 1;
1504 const int num_convexities = convexities.size();
1505 while (end_of_seg < num_convexities && !convexities[end_of_seg]) {
1508 const int64_t higher_bound_interval =
1509 end_of_seg < num_pwl_anchors - 1
1510 ? travel_x_anchors[index_anchor_start + end_of_seg]
1511 : current_route_max_cumuls_[pos] + pre_travel_transit;
1512 const int travel_start_in_segment_ct = solver->AddLinearConstraint(
1513 lower_bound_interval, higher_bound_interval, {{travel_start, 1}});
1514 solver->SetEnforcementLiteral(travel_start_in_segment_ct,
1515 belongs_to_this_segment_var);
1520 const auto [slope, y_intercept] = slope_and_y_intercept[seg];
1522 DCHECK_GE(slope, -1.0) <<
"Travel value PWL should have a slope >= -1";
1532 const double upper_bound = current_route_max_cumuls_[pos];
1533 const double factor = FindBestScaling(
1534 {1.0, -slope, y_intercept - 0.5},
1535 {
static_cast<double>(compressed_travel_value_lower_bound), 0, 1},
1537 {
static_cast<double>(travel_value_upper_bound), upper_bound, 1},
1543 if (factor <= 0)
return false;
1545 const int linearization_ct = solver->AddLinearConstraint(
1547 std::numeric_limits<int64_t>::max(),
1551 solver->SetEnforcementLiteral(linearization_ct,
1552 belongs_to_this_segment_var);
1578 const int compressed_travel_value = solver->AddVariable(
1579 compressed_travel_value_lower_bound, travel_value_upper_bound);
1581 solver, compressed_travel_value,
1582 absl::StrFormat(
"compressed_travel_value(%ld)", pos));
1583 solver->AddLinearConstraint(post_travel_transit + pre_travel_transit,
1584 post_travel_transit + pre_travel_transit,
1585 {{compressed_travel_value, -1},
1586 {lp_cumuls[pos + 1], 1},
1587 {lp_cumuls[pos], -1},
1588 {lp_slacks[pos], -1}});
1595 const int travel_compression_absolute = solver->AddVariable(
1596 0, travel_value_upper_bound - compressed_travel_value_lower_bound);
1598 solver, travel_compression_absolute,
1599 absl::StrFormat(
"travel_compression_absolute(%ld)", pos));
1601 solver->AddLinearConstraint(0, 0,
1602 {{travel_compression_absolute, 1},
1604 {compressed_travel_value, 1}});
1610 solver->SetObjectiveCoefficient(
1611 travel_value, dimension_travel_info->travel_cost_coefficient);
1615 const FloatSlopePiecewiseLinearFunction& cost_function =
1616 transition_info.travel_compression_cost;
1617 const auto& cost_x_anchors = cost_function.x_anchors();
1619 const std::vector<SlopeAndYIntercept> cost_slope_and_y_intercept =
1621 const double cost_max = cost_function.ComputeConvexValue(
1622 travel_value_upper_bound - compressed_travel_value_lower_bound);
1623 double previous_slope = 0;
1624 for (
int seg = 0; seg < cost_x_anchors.size() - 1; ++seg) {
1625 const auto [slope, y_intercept] = cost_slope_and_y_intercept[seg];
1627 DCHECK_GE(slope, previous_slope)
1628 <<
"Compression error is not convex. Segment " << (1 + seg)
1629 <<
" out of " << (cost_x_anchors.size() - 1);
1630 previous_slope = slope;
1631 const double factor = FindBestScaling(
1632 {1.0, -slope, y_intercept},
1633 {0,
static_cast<double>(compressed_travel_value_lower_bound), 1},
1635 {cost_max,
static_cast<double>(travel_value_upper_bound), 1},
1636 (
static_cast<int64_t
>(1) << 62),
1641 if (factor <= 0)
return false;
1643 solver->AddLinearConstraint(
1645 std::numeric_limits<int64_t>::max(),
1646 {{compression_cost, std::round(factor)},
1647 {travel_compression_absolute,
1667 solver->AddLinearConstraint(
1668 0, std::numeric_limits<int64_t>::max(),
1669 {{relative_compression_cost, 1}, {compression_cost, -1}});
1671 solver->SetObjectiveCoefficient(relative_compression_cost, 1.0);
1677bool RouteIsValid(
const RoutingModel& model,
int vehicle,
1678 const std::function<int64_t(int64_t)>& next_accessor) {
1679 absl::flat_hash_set<int64_t> visited;
1680 int node = model.Start(vehicle);
1681 visited.insert(node);
1682 while (!model.IsEnd(node)) {
1683 node = next_accessor(node);
1684 if (visited.contains(node))
return false;
1685 visited.insert(node);
1687 return visited.size() >= 2;
1691bool DimensionCumulOptimizerCore::SetRouteCumulConstraints(
1692 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
1693 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
1694 const RouteDimensionTravelInfo* dimension_travel_info, int64_t cumul_offset,
1696 int64_t* route_transit_cost, int64_t* route_cost_offset) {
1697 RoutingModel*
const model = dimension_->model();
1699 std::vector<int64_t> path;
1701 DCHECK(RouteIsValid(*model, vehicle, next_accessor));
1702 int node = model->Start(vehicle);
1703 path.push_back(node);
1704 while (!model->IsEnd(node)) {
1705 node = next_accessor(node);
1706 path.push_back(node);
1708 DCHECK_GE(path.size(), 2);
1710 const int path_size = path.size();
1712 std::vector<int64_t> fixed_transit(path_size - 1);
1713 int64_t total_fixed_transit = 0;
1715 for (
int pos = 1; pos < path_size; ++pos) {
1716 int64_t& transit = fixed_transit[pos - 1];
1717 transit = transit_accessor(path[pos - 1], path[pos]);
1718 total_fixed_transit =
CapAdd(total_fixed_transit, transit);
1721 if (!ExtractRouteCumulBounds(path, cumul_offset)) {
1724 if (dimension_travel_info ==
nullptr ||
1725 dimension_travel_info->transition_info.empty()) {
1726 if (!TightenRouteCumulBounds(path, fixed_transit, cumul_offset)) {
1731 std::vector<int64_t> min_transit(path_size - 1);
1732 for (
int pos = 0; pos < path_size - 1; ++pos) {
1733 const RouteDimensionTravelInfo::TransitionInfo& transition =
1734 dimension_travel_info->transition_info[pos];
1735 min_transit[pos] = transition.pre_travel_transit_value +
1736 transition.compressed_travel_value_lower_bound +
1737 transition.post_travel_transit_value;
1739 if (!TightenRouteCumulBounds(path, min_transit, cumul_offset)) {
1746 std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1747 lp_cumuls.assign(path_size, -1);
1748 for (
int pos = 0; pos < path_size; ++pos) {
1749 const int lp_cumul = solver->CreateNewPositiveVariable();
1751 absl::StrFormat(
"lp_cumul(%ld)", pos));
1752 index_to_cumul_variable_[path[pos]] = lp_cumul;
1753 lp_cumuls[pos] = lp_cumul;
1754 if (!solver->SetVariableBounds(lp_cumul, current_route_min_cumuls_[pos],
1755 current_route_max_cumuls_[pos])) {
1758 const SortedDisjointIntervalList& forbidden =
1759 dimension_->forbidden_intervals()[path[pos]];
1760 if (forbidden.NumIntervals() > 0) {
1761 std::vector<int64_t> starts;
1762 std::vector<int64_t> ends;
1763 for (
const ClosedInterval interval :
1764 dimension_->GetAllowedIntervalsInRange(
1765 path[pos],
CapAdd(current_route_min_cumuls_[pos], cumul_offset),
1766 CapAdd(current_route_max_cumuls_[pos], cumul_offset))) {
1767 starts.push_back(
CapSub(interval.start, cumul_offset));
1768 ends.push_back(
CapSub(interval.end, cumul_offset));
1770 solver->SetVariableDisjointBounds(lp_cumul, starts, ends);
1773 solver->AddRoute(path, lp_cumuls);
1775 std::vector<int> lp_slacks(path_size - 1, -1);
1776 for (
int pos = 0; pos < path_size - 1; ++pos) {
1777 const IntVar* cp_slack = dimension_->SlackVar(path[pos]);
1778 lp_slacks[pos] = solver->CreateNewPositiveVariable();
1780 absl::StrFormat(
"lp_slacks(%ld)", pos));
1781 if (!solver->SetVariableBounds(lp_slacks[pos], cp_slack->Min(),
1787 if (!SetRouteTravelConstraints(dimension_travel_info, lp_slacks,
1788 fixed_transit, solver)) {
1792 if (route_cost_offset !=
nullptr) *route_cost_offset = 0;
1793 if (optimize_costs) {
1795 for (
int pos = 0; pos < path_size; ++pos) {
1796 if (!dimension_->HasCumulVarSoftUpperBound(path[pos]))
continue;
1797 const int64_t coef =
1798 dimension_->GetCumulVarSoftUpperBoundCoefficient(path[pos]);
1799 if (coef == 0)
continue;
1800 int64_t
bound = dimension_->GetCumulVarSoftUpperBound(path[pos]);
1801 if (bound < cumul_offset && route_cost_offset !=
nullptr) {
1803 *route_cost_offset =
CapAdd(*route_cost_offset,
1806 bound = std::max<int64_t>(0,
CapSub(bound, cumul_offset));
1807 if (current_route_max_cumuls_[pos] <= bound) {
1811 const int soft_ub_diff = solver->CreateNewPositiveVariable();
1813 absl::StrFormat(
"soft_ub_diff(%ld)", pos));
1814 solver->SetObjectiveCoefficient(soft_ub_diff, coef);
1816 const int ct = solver->CreateNewConstraint(
1817 std::numeric_limits<int64_t>::min(), bound);
1818 solver->SetCoefficient(ct, lp_cumuls[pos], 1);
1819 solver->SetCoefficient(ct, soft_ub_diff, -1);
1822 for (
int pos = 0; pos < path_size; ++pos) {
1823 if (!dimension_->HasCumulVarSoftLowerBound(path[pos]))
continue;
1824 const int64_t coef =
1825 dimension_->GetCumulVarSoftLowerBoundCoefficient(path[pos]);
1826 if (coef == 0)
continue;
1827 const int64_t
bound = std::max<int64_t>(
1828 0,
CapSub(dimension_->GetCumulVarSoftLowerBound(path[pos]),
1830 if (current_route_min_cumuls_[pos] >= bound) {
1834 const int soft_lb_diff = solver->CreateNewPositiveVariable();
1836 absl::StrFormat(
"soft_lb_diff(%ld)", pos));
1837 solver->SetObjectiveCoefficient(soft_lb_diff, coef);
1839 const int ct = solver->CreateNewConstraint(
1840 bound, std::numeric_limits<int64_t>::max());
1841 solver->SetCoefficient(ct, lp_cumuls[pos], 1);
1842 solver->SetCoefficient(ct, soft_lb_diff, 1);
1846 std::vector<int> visited_pairs;
1847 StoreVisitedPickupDeliveryPairsOnRoute(
1848 *dimension_, vehicle, next_accessor, &visited_pairs,
1849 &visited_pickup_delivery_indices_for_pair_);
1850 for (
int pair_index : visited_pairs) {
1851 const int64_t pickup_index =
1852 visited_pickup_delivery_indices_for_pair_[pair_index].first;
1853 const int64_t delivery_index =
1854 visited_pickup_delivery_indices_for_pair_[pair_index].second;
1855 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
1857 DCHECK_GE(pickup_index, 0);
1858 if (delivery_index < 0) {
1863 const int64_t limit = dimension_->GetPickupToDeliveryLimitForPair(
1864 pair_index, model->GetPickupPosition(pickup_index)->alternative_index,
1865 model->GetDeliveryPosition(delivery_index)->alternative_index);
1866 if (limit < std::numeric_limits<int64_t>::max()) {
1868 const int ct = solver->CreateNewConstraint(
1869 std::numeric_limits<int64_t>::min(), limit);
1870 solver->SetCoefficient(ct, index_to_cumul_variable_[delivery_index], 1);
1871 solver->SetCoefficient(ct, index_to_cumul_variable_[pickup_index], -1);
1876 const int64_t span_bound = dimension_->GetSpanUpperBoundForVehicle(vehicle);
1877 if (span_bound < std::numeric_limits<int64_t>::max()) {
1879 const int ct = solver->CreateNewConstraint(
1880 std::numeric_limits<int64_t>::min(), span_bound);
1881 solver->SetCoefficient(ct, lp_cumuls.back(), 1);
1882 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
1887 const int64_t span_cost_coef =
1888 dimension_->GetSpanCostCoefficientForVehicle(vehicle);
1889 const int64_t slack_cost_coef =
CapAdd(
1890 span_cost_coef, dimension_->GetSlackCostCoefficientForVehicle(vehicle));
1891 if (optimize_costs && slack_cost_coef > 0) {
1894 const int span_without_fixed_transit_var =
1895 solver->CreateNewPositiveVariable();
1897 "span_without_fixed_transit_var");
1898 solver->AddLinearConstraint(total_fixed_transit, total_fixed_transit,
1899 {{lp_cumuls.back(), 1},
1900 {lp_cumuls.front(), -1},
1901 {span_without_fixed_transit_var, -1}});
1902 solver->SetObjectiveCoefficient(span_without_fixed_transit_var,
1906 if (optimize_costs && dimension_->HasSoftSpanUpperBounds()) {
1907 const BoundCost bound_cost =
1908 dimension_->GetSoftSpanUpperBoundForVehicle(vehicle);
1909 if (bound_cost.bound < std::numeric_limits<int64_t>::max() &&
1910 bound_cost.cost > 0) {
1911 const int span_violation = solver->CreateNewPositiveVariable();
1914 const int violation = solver->CreateNewConstraint(
1915 std::numeric_limits<int64_t>::min(), bound_cost.bound);
1916 solver->SetCoefficient(violation, lp_cumuls.back(), 1.0);
1917 solver->SetCoefficient(violation, lp_cumuls.front(), -1.0);
1918 solver->SetCoefficient(violation, span_violation, -1.0);
1920 solver->SetObjectiveCoefficient(span_violation, bound_cost.cost);
1923 if (optimize_costs && solver->IsCPSATSolver() &&
1924 dimension_->HasQuadraticCostSoftSpanUpperBounds()) {
1926 const BoundCost bound_cost =
1927 dimension_->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1928 if (bound_cost.bound < std::numeric_limits<int64_t>::max() &&
1929 bound_cost.cost > 0) {
1930 const int span_violation = solver->CreateNewPositiveVariable();
1932 solver, span_violation,
1933 absl::StrFormat(
"quadratic_span_violation(%ld)", vehicle));
1935 const int violation = solver->CreateNewConstraint(
1936 std::numeric_limits<int64_t>::min(), bound_cost.bound);
1937 solver->SetCoefficient(violation, lp_cumuls.back(), 1.0);
1938 solver->SetCoefficient(violation, lp_cumuls.front(), -1.0);
1939 solver->SetCoefficient(violation, span_violation, -1.0);
1941 const int squared_span_violation = solver->CreateNewPositiveVariable();
1943 solver, squared_span_violation,
1944 absl::StrFormat(
"squared_span_violation(%ld)", vehicle));
1945 solver->AddProductConstraint(squared_span_violation,
1946 {span_violation, span_violation});
1948 solver->SetObjectiveCoefficient(squared_span_violation, bound_cost.cost);
1952 if (optimize_costs && dimension_->global_span_cost_coefficient() > 0) {
1955 solver->CreateNewConstraint(std::numeric_limits<int64_t>::min(), 0);
1956 solver->SetCoefficient(ct, min_start_cumul_, 1);
1957 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
1959 ct = solver->CreateNewConstraint(0, std::numeric_limits<int64_t>::max());
1960 solver->SetCoefficient(ct, max_end_cumul_, 1);
1961 solver->SetCoefficient(ct, lp_cumuls.back(), -1);
1964 if (route_transit_cost !=
nullptr) {
1965 if (optimize_costs && span_cost_coef > 0) {
1966 *route_transit_cost =
CapProd(total_fixed_transit, span_cost_coef);
1968 *route_transit_cost = 0;
1976 current_route_break_variables_.clear();
1977 if (!dimension_->HasBreakConstraints())
return true;
1978 const std::vector<IntervalVar*>& breaks =
1979 dimension_->GetBreakIntervalsOfVehicle(vehicle);
1980 const int num_breaks = breaks.size();
1984 if (num_breaks == 0) {
1985 int64_t maximum_route_span = std::numeric_limits<int64_t>::max();
1986 for (
const auto& distance_duration :
1987 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
1988 maximum_route_span =
1989 std::min(maximum_route_span, distance_duration.first);
1991 if (maximum_route_span < std::numeric_limits<int64_t>::max()) {
1992 const int ct = solver->CreateNewConstraint(
1993 std::numeric_limits<int64_t>::min(), maximum_route_span);
1994 solver->SetCoefficient(ct, lp_cumuls.back(), 1);
1995 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
2002 std::vector<int64_t> pre_travel(path_size - 1, 0);
2003 std::vector<int64_t> post_travel(path_size - 1, 0);
2005 const int pre_travel_index =
2006 dimension_->GetPreTravelEvaluatorOfVehicle(vehicle);
2007 if (pre_travel_index != -1) {
2011 const int post_travel_index =
2012 dimension_->GetPostTravelEvaluatorOfVehicle(vehicle);
2013 if (post_travel_index != -1) {
2027 std::vector<LpBreak> lp_breaks;
2028 if (solver->IsCPSATSolver()) {
2029 lp_breaks.resize(num_breaks, {.start = -1, .end = -1, .duration = -1});
2032 std::vector<int> slack_exact_lower_bound_ct(path_size - 1, -1);
2033 std::vector<int> slack_linear_lower_bound_ct(path_size - 1, -1);
2035 const int64_t vehicle_start_min = current_route_min_cumuls_.front();
2036 const int64_t vehicle_start_max = current_route_max_cumuls_.front();
2037 const int64_t vehicle_end_min = current_route_min_cumuls_.back();
2038 const int64_t vehicle_end_max = current_route_max_cumuls_.back();
2039 const int all_break_variables_offset =
2040 vehicle_to_all_break_variables_offset_[vehicle];
2041 for (
int br = 0; br < num_breaks; ++br) {
2042 const IntervalVar& break_var = *breaks[br];
2043 if (!break_var.MustBePerformed())
continue;
2044 const int64_t break_start_min =
CapSub(break_var.StartMin(), cumul_offset);
2045 const int64_t break_start_max =
CapSub(break_var.StartMax(), cumul_offset);
2046 const int64_t break_end_min =
CapSub(break_var.EndMin(), cumul_offset);
2047 const int64_t break_end_max =
CapSub(break_var.EndMax(), cumul_offset);
2048 const int64_t break_duration_min = break_var.DurationMin();
2049 const int64_t break_duration_max = break_var.DurationMax();
2052 if (solver->IsCPSATSolver()) {
2053 if (break_end_max <= vehicle_start_min ||
2054 vehicle_end_max <= break_start_min) {
2055 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
2056 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
2057 current_route_break_variables_.push_back(-1);
2058 current_route_break_variables_.push_back(-1);
2062 .start = solver->AddVariable(break_start_min, break_start_max),
2063 .end = solver->AddVariable(break_end_min, break_end_max),
2065 solver->AddVariable(break_duration_min, break_duration_max)};
2066 const auto [break_start, break_end, break_duration] = lp_breaks[br];
2068 absl::StrFormat(
"lp_break_start(%ld)", br));
2070 absl::StrFormat(
"lp_break_end(%ld)", br));
2072 absl::StrFormat(
"lp_break_duration(%ld)", br));
2074 solver->AddLinearConstraint(
2075 0, 0, {{break_end, 1}, {break_start, -1}, {break_duration, -1}});
2077 all_break_variables_[all_break_variables_offset + 2 * br] = break_start;
2078 all_break_variables_[all_break_variables_offset + 2 * br + 1] = break_end;
2079 current_route_break_variables_.push_back(break_start);
2080 current_route_break_variables_.push_back(break_end);
2082 if (break_end_min <= vehicle_start_max ||
2083 vehicle_end_min <= break_start_max) {
2084 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
2085 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
2086 current_route_break_variables_.push_back(-1);
2087 current_route_break_variables_.push_back(-1);
2095 const int break_in_one_slack_ct = solver->CreateNewConstraint(1, 1);
2097 if (solver->IsCPSATSolver()) {
2099 if (break_end_min <= vehicle_start_max) {
2100 const int ct = solver->AddLinearConstraint(
2101 0, std::numeric_limits<int64_t>::max(),
2102 {{lp_cumuls.front(), 1}, {lp_breaks[br].end, -1}});
2103 const int break_is_before_route = solver->AddVariable(0, 1);
2105 solver, break_is_before_route,
2106 absl::StrFormat(
"break_is_before_route(%ld)", br));
2107 solver->SetEnforcementLiteral(ct, break_is_before_route);
2108 solver->SetCoefficient(break_in_one_slack_ct, break_is_before_route, 1);
2111 if (vehicle_end_min <= break_start_max) {
2112 const int ct = solver->AddLinearConstraint(
2113 0, std::numeric_limits<int64_t>::max(),
2114 {{lp_breaks[br].start, 1}, {lp_cumuls.back(), -1}});
2115 const int break_is_after_route = solver->AddVariable(0, 1);
2117 solver, break_is_after_route,
2118 absl::StrFormat(
"break_is_after_route(%ld)", br));
2119 solver->SetEnforcementLiteral(ct, break_is_after_route);
2120 solver->SetCoefficient(break_in_one_slack_ct, break_is_after_route, 1);
2125 for (
int pos = 0; pos < path_size - 1; ++pos) {
2128 const int64_t slack_start_min =
2129 CapAdd(current_route_min_cumuls_[pos], pre_travel[pos]);
2130 if (slack_start_min > break_start_max)
break;
2131 const int64_t slack_end_max =
2132 CapSub(current_route_max_cumuls_[pos + 1], post_travel[pos]);
2133 if (break_end_min > slack_end_max)
continue;
2134 const int64_t slack_duration_max =
2135 std::min(
CapSub(
CapSub(current_route_max_cumuls_[pos + 1],
2136 current_route_min_cumuls_[pos]),
2137 fixed_transit[pos]),
2138 dimension_->SlackVar(path[pos])->Max());
2139 if (slack_duration_max < break_duration_min)
continue;
2146 const int break_in_slack = solver->AddVariable(0, 1);
2148 solver, break_in_slack,
2149 absl::StrFormat(
"break_in_slack(%ld, %ld)", br, pos));
2150 if (slack_linear_lower_bound_ct[pos] == -1) {
2151 slack_linear_lower_bound_ct[pos] = solver->AddLinearConstraint(
2152 std::numeric_limits<int64_t>::min(), 0, {{lp_slacks[pos], -1}});
2157 if (break_in_one_slack_ct < slack_linear_lower_bound_ct[pos]) {
2158 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2159 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2160 break_duration_min);
2162 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2163 break_duration_min);
2164 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2167 if (solver->IsCPSATSolver()) {
2172 const int break_duration_in_slack =
2173 solver->AddVariable(0, slack_duration_max);
2175 solver, break_duration_in_slack,
2176 absl::StrFormat(
"break_duration_in_slack(%ld, %ld)", br, pos));
2177 solver->AddProductConstraint(break_duration_in_slack,
2178 {break_in_slack, lp_breaks[br].duration});
2179 if (slack_exact_lower_bound_ct[pos] == -1) {
2180 slack_exact_lower_bound_ct[pos] = solver->AddLinearConstraint(
2181 std::numeric_limits<int64_t>::min(), 0, {{lp_slacks[pos], -1}});
2183 solver->SetCoefficient(slack_exact_lower_bound_ct[pos],
2184 break_duration_in_slack, 1);
2187 const int break_start_after_current_ct = solver->AddLinearConstraint(
2188 pre_travel[pos], std::numeric_limits<int64_t>::max(),
2189 {{lp_breaks[br].start, 1}, {lp_cumuls[pos], -1}});
2190 solver->SetEnforcementLiteral(break_start_after_current_ct,
2193 const int break_ends_before_next_ct = solver->AddLinearConstraint(
2194 post_travel[pos], std::numeric_limits<int64_t>::max(),
2195 {{lp_cumuls[pos + 1], 1}, {lp_breaks[br].end, -1}});
2196 solver->SetEnforcementLiteral(break_ends_before_next_ct,
2202 for (
const auto& distance_duration :
2203 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
2204 const int64_t limit = distance_duration.first;
2205 const int64_t min_break_duration = distance_duration.second;
2206 int64_t min_num_breaks = 0;
2209 std::max<int64_t>(0,
CapSub(total_fixed_transit, 1) / limit);
2211 if (
CapSub(current_route_min_cumuls_.back(),
2212 current_route_max_cumuls_.front()) > limit) {
2213 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
2215 if (num_breaks < min_num_breaks)
return false;
2216 if (min_num_breaks == 0)
continue;
2238 std::vector<int64_t> sum_transits(path_size);
2240 sum_transits[0] = 0;
2241 for (
int pos = 1; pos < path_size; ++pos) {
2242 sum_transits[pos] = sum_transits[pos - 1] + fixed_transit[pos - 1];
2249 std::vector<int> slack_sum_vars(path_size, -1);
2254 auto trigger = [&](
int k,
int pl,
int pr) ->
bool {
2255 const int64_t r_pre_travel = pr < path_size - 1 ? pre_travel[pr] : 0;
2256 const int64_t l_post_travel = pl >= 1 ? post_travel[pl - 1] : 0;
2257 const int64_t extra_travel =
CapAdd(r_pre_travel, l_post_travel);
2259 const int64_t span_lb =
CapAdd(
CapSub(current_route_min_cumuls_[pr],
2260 current_route_max_cumuls_[pl]),
2262 if (span_lb > limit)
return true;
2264 return CapAdd(
CapSub(sum_transits[pr], sum_transits[pl]), extra_travel) >
2267 int min_sum_var_index = path_size;
2268 int max_sum_var_index = -1;
2269 for (
int k = 1; k <= min_num_breaks; ++k) {
2271 for (
int pl = 0; pl < path_size - 1; ++pl) {
2272 pr = std::max(pr, pl + 1);
2274 while (pr < path_size && !trigger(k, pl, pr)) ++pr;
2275 if (pr == path_size)
break;
2277 while (pl < pr && trigger(k, pl + 1, pr)) ++pl;
2280 if (pl == pr)
return false;
2285 if (k < min_num_breaks && trigger(k + 1, pl, pr))
continue;
2289 if (solver->IsCPSATSolver()) {
2292 solver->AddLinearConstraint(
2294 CapProd(k, min_break_duration)),
2295 kint64max, {{lp_cumuls[pr], 1}, {lp_cumuls[pl], -1}});
2297 if (slack_sum_vars[pl] == -1) {
2298 slack_sum_vars[pl] = solver->CreateNewPositiveVariable();
2300 absl::StrFormat(
"slack_sum_vars(%ld)", pl));
2301 min_sum_var_index = std::min(min_sum_var_index, pl);
2303 if (slack_sum_vars[pr] == -1) {
2304 slack_sum_vars[pr] = solver->CreateNewPositiveVariable();
2306 absl::StrFormat(
"slack_sum_vars(%ld)", pr));
2307 max_sum_var_index = std::max(max_sum_var_index, pr);
2310 solver->AddLinearConstraint(
2312 {{slack_sum_vars[pr], 1}, {slack_sum_vars[pl], -1}});
2316 if (min_sum_var_index < max_sum_var_index) {
2317 slack_sum_vars[min_sum_var_index] = solver->AddVariable(0, 0);
2318 int prev_index = min_sum_var_index;
2319 for (
int pos = min_sum_var_index + 1; pos <= max_sum_var_index; ++pos) {
2320 if (slack_sum_vars[pos] == -1)
continue;
2323 const int ct = solver->AddLinearConstraint(
2324 0, 0, {{slack_sum_vars[pos], 1}, {slack_sum_vars[prev_index], -1}});
2325 for (
int p = prev_index; p < pos; ++p) {
2326 solver->SetCoefficient(ct, lp_slacks[p], -1);
2332 if (!solver->IsCPSATSolver())
return true;
2333 if (!dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
2336 for (
const IntervalVar* interval :
2337 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
2338 if (!interval->MustBePerformed())
return true;
2341 for (
int br = 1; br < num_breaks; ++br) {
2342 if (lp_breaks[br].start == -1 || lp_breaks[br - 1].start == -1)
continue;
2343 solver->AddLinearConstraint(
2344 0, std::numeric_limits<int64_t>::max(),
2345 {{lp_breaks[br - 1].end, -1}, {lp_breaks[br].start, 1}});
2348 for (
const auto& distance_duration :
2349 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
2350 const int64_t limit = distance_duration.first;
2351 const int64_t min_break_duration = distance_duration.second;
2379 int previous_cover = solver->AddVariable(
CapAdd(vehicle_start_min, limit),
2380 CapAdd(vehicle_start_max, limit));
2382 solver->AddLinearConstraint(limit, limit,
2383 {{previous_cover, 1}, {lp_cumuls.front(), -1}});
2384 for (
int br = 0; br < num_breaks; ++br) {
2385 const LpBreak& lp_break = lp_breaks[br];
2386 if (lp_break.start == -1)
continue;
2387 const int64_t break_end_min =
CapSub(breaks[br]->EndMin(), cumul_offset);
2388 const int64_t break_end_max =
CapSub(breaks[br]->EndMax(), cumul_offset);
2391 const int break_is_eligible = solver->AddVariable(0, 1);
2393 absl::StrFormat(
"break_is_eligible(%ld)", br));
2394 const int break_is_not_eligible = solver->AddVariable(0, 1);
2396 solver, break_is_not_eligible,
2397 absl::StrFormat(
"break_is_not_eligible(%ld)", br));
2399 solver->AddLinearConstraint(
2400 1, 1, {{break_is_eligible, 1}, {break_is_not_eligible, 1}});
2401 const int positive_ct = solver->AddLinearConstraint(
2402 min_break_duration, std::numeric_limits<int64_t>::max(),
2403 {{lp_break.end, 1}, {lp_break.start, -1}});
2404 solver->SetEnforcementLiteral(positive_ct, break_is_eligible);
2405 const int negative_ct = solver->AddLinearConstraint(
2406 std::numeric_limits<int64_t>::min(), min_break_duration - 1,
2407 {{lp_break.end, 1}, {lp_break.start, -1}});
2408 solver->SetEnforcementLiteral(negative_ct, break_is_not_eligible);
2415 const int break_cover = solver->AddVariable(
2416 CapAdd(std::min(vehicle_start_min, break_end_min), limit),
2417 CapAdd(std::max(vehicle_start_min, break_end_max), limit));
2419 absl::StrFormat(
"break_cover(%ld)", br));
2420 const int limit_cover_ct = solver->AddLinearConstraint(
2421 limit, limit, {{break_cover, 1}, {lp_break.end, -1}});
2422 solver->SetEnforcementLiteral(limit_cover_ct, break_is_eligible);
2423 const int empty_cover_ct = solver->AddLinearConstraint(
2424 CapAdd(vehicle_start_min, limit),
CapAdd(vehicle_start_min, limit),
2425 {{break_cover, 1}});
2426 solver->SetEnforcementLiteral(empty_cover_ct, break_is_not_eligible);
2429 solver->AddVariable(
CapAdd(vehicle_start_min, limit),
2430 std::numeric_limits<int64_t>::max());
2432 solver->AddMaximumConstraint(cover, {previous_cover, break_cover});
2435 const int route_end_is_not_covered = solver->AddReifiedLinearConstraint(
2436 1, std::numeric_limits<int64_t>::max(),
2437 {{lp_cumuls.back(), 1}, {previous_cover, -1}});
2438 const int break_start_cover_ct = solver->AddLinearConstraint(
2439 0, std::numeric_limits<int64_t>::max(),
2440 {{previous_cover, 1}, {lp_break.start, -1}});
2441 solver->SetEnforcementLiteral(break_start_cover_ct,
2442 route_end_is_not_covered);
2444 previous_cover = cover;
2446 solver->AddLinearConstraint(0, std::numeric_limits<int64_t>::max(),
2447 {{previous_cover, 1}, {lp_cumuls.back(), -1}});
2454bool AllValuesContainedExcept(
const IntVar& var, absl::Span<const int> values,
2455 const absl::flat_hash_set<int>& ignored_values) {
2456 for (
int val : values) {
2457 if (!ignored_values.contains(val) && !var.Contains(val))
return false;
2463bool DimensionCumulOptimizerCore::SetGlobalConstraints(
2464 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
2465 bool optimize_costs,
bool optimize_resource_assignment,
2469 const int64_t global_span_coeff = dimension_->global_span_cost_coefficient();
2470 if (optimize_costs && global_span_coeff > 0) {
2472 const int global_span_var = solver->CreateNewPositiveVariable();
2474 solver->AddLinearConstraint(
2476 {{global_span_var, 1}, {max_end_cumul_, -1}, {min_start_cumul_, 1}});
2481 solver->SetObjectiveCoefficient(global_span_var, global_span_coeff);
2485 for (
const auto [first_node, second_node, offset, performed_constraint] :
2486 dimension_->GetNodePrecedences()) {
2487 if (next_accessor(first_node) == -1 || next_accessor(second_node) == -1) {
2490 const int first_cumul_var = index_to_cumul_variable_[first_node];
2491 const int second_cumul_var = index_to_cumul_variable_[second_node];
2492 switch (RoutingDimension::GetPrecedenceStatus(
2493 first_cumul_var < 0, second_cumul_var < 0, performed_constraint)) {
2494 case RoutingDimension::PrecedenceStatus::kActive:
2496 case RoutingDimension::PrecedenceStatus::kInactive:
2498 case RoutingDimension::PrecedenceStatus::kInvalid:
2501 DCHECK_NE(first_cumul_var, second_cumul_var)
2502 <<
"Dimension " << dimension_->name()
2503 <<
" has a self-precedence on node " << first_node <<
".";
2506 const int ct = solver->CreateNewConstraint(
2507 offset, std::numeric_limits<int64_t>::max());
2508 solver->SetCoefficient(ct, second_cumul_var, 1);
2509 solver->SetCoefficient(ct, first_cumul_var, -1);
2512 if (optimize_resource_assignment &&
2513 !SetGlobalConstraintsForResourceAssignment(next_accessor, cumul_offset,
2520bool DimensionCumulOptimizerCore::SetGlobalConstraintsForResourceAssignment(
2521 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
2523 if (!solver->IsCPSATSolver()) {
2529 using RCIndex = RoutingModel::ResourceClassIndex;
2530 const RoutingModel& model = *dimension_->model();
2531 const int num_vehicles = model.vehicles();
2532 const auto& resource_groups = model.GetResourceGroups();
2533 for (
int rg_index : model.GetDimensionResourceGroupIndices(dimension_)) {
2545 const ResourceGroup& resource_group = *resource_groups[rg_index];
2546 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2549 int num_required_resources = 0;
2552 std::vector<int> vehicle_constraints(model.vehicles(), kNoConstraint);
2553 const int num_resource_classes = resource_group.GetResourceClassesCount();
2554 std::vector<absl::flat_hash_set<int>>& ignored_resources_per_class =
2555 resource_class_ignored_resources_per_group_[rg_index];
2556 ignored_resources_per_class.assign(num_resource_classes, {});
2557 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2558 const IntVar& resource_var = *model.ResourceVar(v, rg_index);
2559 if (model.IsEnd(next_accessor(model.Start(v))) &&
2560 !model.IsVehicleUsedWhenEmpty(v)) {
2561 if (resource_var.Bound() && resource_var.Value() >= 0) {
2569 if (resource_var.Bound()) {
2570 const int resource_index = resource_var.Value();
2571 if (resource_index < 0) {
2575 ignored_resources_per_class
2576 [resource_group.GetResourceClassIndex(resource_index).value()]
2577 .insert(resource_index);
2579 const int start_index = index_to_cumul_variable_[model.Start(v)];
2580 const int end_index = index_to_cumul_variable_[model.End(v)];
2581 if (!TightenStartEndVariableBoundsWithResource(
2582 *dimension_, resource_group.GetResource(resource_index),
2583 GetVariableBounds(start_index, *solver), start_index,
2584 GetVariableBounds(end_index, *solver), end_index, cumul_offset,
2590 num_required_resources++;
2591 vehicle_constraints[v] = solver->CreateNewConstraint(1, 1);
2596 std::vector<int> resource_class_cstrs(num_resource_classes, kNoConstraint);
2597 int num_available_resources = 0;
2598 for (RCIndex rc_index(0); rc_index < num_resource_classes; rc_index++) {
2599 const ResourceGroup::Attributes& attributes =
2600 resource_group.GetDimensionAttributesForClass(dimension_, rc_index);
2601 if (attributes.start_domain().Max() < cumul_offset ||
2602 attributes.end_domain().Max() < cumul_offset) {
2608 const int rc = rc_index.value();
2609 const int num_available_class_resources =
2610 resource_group.GetResourceIndicesInClass(rc_index).size() -
2611 ignored_resources_per_class[rc].size();
2612 DCHECK_GE(num_available_class_resources, 0);
2613 if (num_available_class_resources > 0) {
2614 num_available_resources += num_available_class_resources;
2615 resource_class_cstrs[rc] =
2616 solver->CreateNewConstraint(0, num_available_class_resources);
2620 if (num_required_resources > num_available_resources) {
2626 std::vector<int>& resource_class_to_vehicle_assignment_vars =
2627 resource_class_to_vehicle_assignment_variables_per_group_[rg_index];
2628 resource_class_to_vehicle_assignment_vars.assign(
2629 num_resource_classes * num_vehicles, -1);
2635 DCHECK_EQ(resource_group.Index(), rg_index);
2636 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2637 if (vehicle_constraints[v] == kNoConstraint)
continue;
2638 IntVar*
const resource_var = model.ResourceVar(v, rg_index);
2639 std::unique_ptr<IntVarIterator> it(
2640 resource_var->MakeDomainIterator(
false));
2641 std::vector<bool> resource_class_considered(num_resource_classes,
false);
2642 for (
int r : InitAndGetValues(it.get())) {
2643 if (r < 0)
continue;
2644 const RCIndex rc_index = resource_group.GetResourceClassIndex(r);
2645 const int rc = rc_index.value();
2646 if (resource_class_considered[rc]) {
2649 resource_class_considered[rc] =
true;
2650 if (resource_class_cstrs[rc] == kNoConstraint)
continue;
2656 DCHECK(AllValuesContainedExcept(
2657 *resource_var, resource_group.GetResourceIndicesInClass(rc_index),
2658 ignored_resources_per_class[rc]))
2660 resource_group.GetResourceIndicesInClass(rc_index),
2661 resource_var->Min(), resource_var->Max());
2663 const int assign_rc_to_v = solver->AddVariable(0, 1);
2665 solver, assign_rc_to_v,
2666 absl::StrFormat(
"assign_rc_to_v(%ld, %ld)", rc, v));
2667 resource_class_to_vehicle_assignment_vars[rc * num_vehicles + v] =
2672 DCHECK_LT(vehicle_constraints[v], resource_class_cstrs[rc]);
2673 solver->SetCoefficient(vehicle_constraints[v], assign_rc_to_v, 1);
2674 solver->SetCoefficient(resource_class_cstrs[rc], assign_rc_to_v, 1);
2676 const auto& add_domain_constraint =
2677 [&solver, cumul_offset, assign_rc_to_v](
const Domain& domain,
2678 int cumul_variable) {
2682 ClosedInterval cumul_bounds;
2683 if (!GetDomainOffsetBounds(domain, cumul_offset, &cumul_bounds)) {
2685 solver->SetVariableBounds(assign_rc_to_v, 0, 0);
2688 const int cumul_constraint = solver->AddLinearConstraint(
2689 cumul_bounds.start, cumul_bounds.end, {{cumul_variable, 1}});
2690 solver->SetEnforcementLiteral(cumul_constraint, assign_rc_to_v);
2692 const ResourceGroup::Attributes& attributes =
2693 resource_group.GetDimensionAttributesForClass(dimension_, rc_index);
2694 add_domain_constraint(attributes.start_domain(),
2695 index_to_cumul_variable_[model.Start(v)]);
2696 add_domain_constraint(attributes.end_domain(),
2697 index_to_cumul_variable_[model.End(v)]);
2704#undef SET_DEBUG_VARIABLE_NAME
2706void DimensionCumulOptimizerCore::SetValuesFromLP(
2707 absl::Span<const int> lp_variables, int64_t offset,
2708 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* lp_values)
const {
2709 if (lp_values ==
nullptr)
return;
2710 lp_values->assign(lp_variables.size(), std::numeric_limits<int64_t>::min());
2711 for (
int i = 0;
i < lp_variables.size();
i++) {
2712 const int lp_var = lp_variables[
i];
2713 if (lp_var < 0)
continue;
2714 const double lp_value_double = solver->GetValue(lp_var);
2715 const int64_t lp_value_int64 =
2716 (lp_value_double >= std::numeric_limits<int64_t>::max())
2717 ? std::numeric_limits<int64_t>::max()
2718 : MathUtil::FastInt64Round(lp_value_double);
2719 (*lp_values)[
i] =
CapAdd(lp_value_int64, offset);
2723void DimensionCumulOptimizerCore::SetResourceIndices(
2724 RoutingLinearSolverWrapper* solver,
2725 std::vector<std::vector<int>>* resource_indices_per_group)
const {
2726 if (resource_indices_per_group ==
nullptr ||
2727 resource_class_to_vehicle_assignment_variables_per_group_.empty()) {
2730 using RCIndex = RoutingModel::ResourceClassIndex;
2731 const RoutingModel& model = *dimension_->model();
2732 const int num_vehicles = model.vehicles();
2733 DCHECK(!model.GetDimensionResourceGroupIndices(dimension_).empty());
2734 const auto& resource_groups = model.GetResourceGroups();
2735 resource_indices_per_group->resize(resource_groups.size());
2736 for (
int rg_index : model.GetDimensionResourceGroupIndices(dimension_)) {
2737 const ResourceGroup& resource_group = *resource_groups[rg_index];
2738 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2741 resource_indices_per_class =
2742 resource_group.GetResourceIndicesPerClass();
2743 const int num_resource_classes = resource_group.GetResourceClassesCount();
2744 std::vector<int> current_resource_pos_for_class(num_resource_classes, 0);
2745 std::vector<int>& resource_indices =
2746 resource_indices_per_group->at(rg_index);
2747 resource_indices.assign(num_vehicles, -1);
2749 const std::vector<int>& resource_class_to_vehicle_assignment_vars =
2750 resource_class_to_vehicle_assignment_variables_per_group_[rg_index];
2751 DCHECK_EQ(resource_class_to_vehicle_assignment_vars.size(),
2752 num_resource_classes * num_vehicles);
2753 const std::vector<absl::flat_hash_set<int>>& ignored_resources_per_class =
2754 resource_class_ignored_resources_per_group_[rg_index];
2755 DCHECK_EQ(ignored_resources_per_class.size(), num_resource_classes);
2756 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2757 const IntVar& resource_var = *model.ResourceVar(v, rg_index);
2758 if (resource_var.Bound()) {
2759 resource_indices[v] = resource_var.Value();
2762 for (
int rc = 0; rc < num_resource_classes; rc++) {
2763 const int assignment_var =
2764 resource_class_to_vehicle_assignment_vars[rc * num_vehicles + v];
2765 if (assignment_var >= 0 && solver->GetValue(assignment_var) == 1) {
2767 const std::vector<int>& class_resource_indices =
2768 resource_indices_per_class[RCIndex(rc)];
2769 int& pos = current_resource_pos_for_class[rc];
2770 while (ignored_resources_per_class[rc].contains(
2771 class_resource_indices[pos])) {
2773 DCHECK_LT(pos, class_resource_indices.size());
2775 resource_indices[v] = class_resource_indices[pos++];
2790 !
dimension->GetNodePrecedences().empty()) {
2791 switch (solver_type) {
2793 solver_ = std::make_unique<RoutingGlopWrapper>(
2795 ->GetDimensionResourceGroupIndices(
dimension)
2797 GetGlopParametersForGlobalLP());
2801 solver_ = std::make_unique<RoutingCPSatWrapper>();
2805 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
2811 const std::function<int64_t(int64_t)>& next_accessor,
2812 int64_t* optimal_cost_without_transits) {
2813 return optimizer_core_.Optimize(next_accessor, {}, solver_.get(),
nullptr,
2815 optimal_cost_without_transits,
nullptr);
2819 const std::function<int64_t(int64_t)>& next_accessor,
2820 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
2821 dimension_travel_info_per_route,
2822 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
2823 std::vector<std::vector<int>>* optimal_resource_indices) {
2824 return optimizer_core_.Optimize(next_accessor,
2825 dimension_travel_info_per_route,
2826 solver_.get(), optimal_cumuls, optimal_breaks,
2827 optimal_resource_indices,
nullptr,
nullptr);
2831 const std::function<int64_t(int64_t)>& next_accessor,
2832 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
2833 dimension_travel_info_per_route,
2834 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks) {
2835 return optimizer_core_.OptimizeAndPack(
2836 next_accessor, dimension_travel_info_per_route, solver_.get(),
2837 packed_cumuls, packed_breaks);
2842template <
typename T>
2843void MoveValuesToIndicesFrom(std::vector<T>* out_values,
2844 absl::Span<const int> out_indices_to_evaluate,
2845 const std::function<
int(
int)>& index_evaluator,
2846 std::vector<T>& values_to_copy) {
2847 if (out_values ==
nullptr) {
2848 DCHECK(values_to_copy.empty());
2851 DCHECK_EQ(values_to_copy.size(), out_indices_to_evaluate.size());
2852 for (
int i = 0; i < out_indices_to_evaluate.size(); i++) {
2853 const int output_index = index_evaluator(out_indices_to_evaluate[i]);
2854 DCHECK_LT(output_index, out_values->size());
2855 out_values->at(output_index) = std::move(values_to_copy[i]);
2862 int v,
double solve_duration_ratio,
2863 const RoutingModel::ResourceGroup& resource_group,
2865 absl::flat_hash_set<int>>&
2866 ignored_resources_per_class,
2867 const std::function<int64_t(int64_t)>& next_accessor,
2868 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
2871 std::vector<int64_t>* assignment_costs,
2872 std::vector<std::vector<int64_t>>* cumul_values,
2873 std::vector<std::vector<int64_t>>* break_values) {
2874 DCHECK_GT(solve_duration_ratio, 0);
2875 DCHECK_LE(solve_duration_ratio, 1);
2876 DCHECK(lp_optimizer !=
nullptr);
2877 DCHECK(mp_optimizer !=
nullptr);
2878 DCHECK_NE(assignment_costs,
nullptr);
2879 assignment_costs->clear();
2880 ClearIfNonNull(cumul_values);
2881 ClearIfNonNull(break_values);
2883 const RoutingDimension* dimension = lp_optimizer->
dimension();
2884 DCHECK_EQ(dimension, mp_optimizer->
dimension());
2885 RoutingModel*
const model = dimension->model();
2886 if (!resource_group.VehicleRequiresAResource(v) ||
2887 (!model->IsVehicleUsedWhenEmpty(v) &&
2888 next_accessor(model->Start(v)) == model->End(v))) {
2891 if (model->CheckLimit()) {
2896 IntVar*
const resource_var = model->ResourceVar(v, resource_group.Index());
2897 const int num_resource_classes = resource_group.GetResourceClassesCount();
2898 std::vector<int> considered_resource_indices;
2899 considered_resource_indices.reserve(
2900 std::min<int>(resource_var->
Size(), num_resource_classes));
2901 std::vector<bool> resource_class_considered(num_resource_classes,
false);
2907 const RoutingModel::ResourceClassIndex resource_class =
2908 resource_group.GetResourceClassIndex(res);
2909 const int rc_index = resource_class.value();
2910 const absl::flat_hash_set<int>& ignored_resources =
2911 ignored_resources_per_class[resource_class];
2912 if (resource_class_considered[rc_index] ||
2913 ignored_resources.contains(res)) {
2916 resource_class_considered[rc_index] =
true;
2921 DCHECK(AllValuesContainedExcept(
2922 *resource_var, resource_group.GetResourceIndicesInClass(resource_class),
2923 ignored_resources));
2924 considered_resource_indices.push_back(res);
2926 const bool use_mp_optimizer =
2927 dimension->HasBreakConstraints() &&
2928 !dimension->GetBreakIntervalsOfVehicle(v).empty();
2930 use_mp_optimizer ? mp_optimizer : lp_optimizer;
2932 const std::vector<ResourceGroup::Resource>& resources =
2933 resource_group.GetResources();
2934 std::vector<int64_t> considered_assignment_costs;
2935 std::vector<std::vector<int64_t>> considered_cumul_values;
2936 std::vector<std::vector<int64_t>> considered_break_values;
2937 std::vector<DimensionSchedulingStatus> statuses =
2939 v, solve_duration_ratio, next_accessor, transit_accessor, resources,
2940 considered_resource_indices, optimize_vehicle_costs,
2941 &considered_assignment_costs,
2942 cumul_values ==
nullptr ?
nullptr : &considered_cumul_values,
2943 break_values ==
nullptr ?
nullptr : &considered_break_values);
2945 if (statuses.empty() ||
2946 (statuses.size() == 1 &&
2952 assignment_costs->resize(num_resource_classes, -1);
2953 if (cumul_values !=
nullptr) {
2954 cumul_values->resize(num_resource_classes, {});
2956 if (break_values !=
nullptr) {
2957 break_values->resize(num_resource_classes, {});
2960 const auto resource_to_class_index = [&resource_group](
int resource_index) {
2961 return resource_group.GetResourceClassIndex(resource_index).value();
2963 MoveValuesToIndicesFrom(assignment_costs, considered_resource_indices,
2964 resource_to_class_index, considered_assignment_costs);
2965 MoveValuesToIndicesFrom(cumul_values, considered_resource_indices,
2966 resource_to_class_index, considered_cumul_values);
2967 MoveValuesToIndicesFrom(break_values, considered_resource_indices,
2968 resource_to_class_index, considered_break_values);
2970 if (use_mp_optimizer) {
2974 return absl::c_any_of(*assignment_costs,
2975 [](int64_t cost) {
return cost >= 0; });
2978 std::vector<int> mp_resource_indices;
2979 DCHECK_EQ(statuses.size(), considered_resource_indices.size());
2980 for (
int i = 0; i < considered_resource_indices.size(); i++) {
2982 mp_resource_indices.push_back(considered_resource_indices[i]);
2986 std::vector<int64_t> mp_assignment_costs;
2987 std::vector<std::vector<int64_t>> mp_cumul_values;
2988 std::vector<std::vector<int64_t>> mp_break_values;
2990 v, solve_duration_ratio, next_accessor, transit_accessor, resources,
2991 mp_resource_indices, optimize_vehicle_costs, &mp_assignment_costs,
2992 cumul_values ==
nullptr ?
nullptr : &mp_cumul_values,
2993 break_values ==
nullptr ?
nullptr : &mp_break_values);
2994 if (!mp_resource_indices.empty() && mp_assignment_costs.empty()) {
2999 MoveValuesToIndicesFrom(assignment_costs, mp_resource_indices,
3000 resource_to_class_index, mp_assignment_costs);
3001 MoveValuesToIndicesFrom(cumul_values, mp_resource_indices,
3002 resource_to_class_index, mp_cumul_values);
3003 MoveValuesToIndicesFrom(break_values, mp_resource_indices,
3004 resource_to_class_index, mp_break_values);
3006 return absl::c_any_of(*assignment_costs,
3007 [](int64_t cost) {
return cost >= 0; });
3011 absl::Span<const int> vehicles,
3014 resource_indices_per_class,
3016 absl::flat_hash_set<int>>&
3017 ignored_resources_per_class,
3018 std::function<
const std::vector<int64_t>*(
int)>
3019 vehicle_to_resource_class_assignment_costs,
3020 std::vector<int>* resource_indices) {
3021 const int total_num_resources = absl::c_accumulate(
3022 resource_indices_per_class, 0,
3023 [](
int acc, absl::Span<const int> res) {
return acc + res.size(); });
3024 DCHECK_GE(total_num_resources, 1);
3025 const int num_ignored_resources =
3026 absl::c_accumulate(ignored_resources_per_class, 0,
3027 [](
int acc,
const absl::flat_hash_set<int>& res) {
3028 return acc + res.size();
3030 const int num_resources = total_num_resources - num_ignored_resources;
3031 const int num_vehicles = vehicles.size();
3032 int num_total_vehicles = -1;
3033 if (resource_indices !=
nullptr) {
3034 num_total_vehicles = resource_indices->size();
3037 resource_indices->clear();
3038 DCHECK_GE(num_total_vehicles, num_vehicles);
3039 for (
int v : vehicles) {
3041 DCHECK_LT(v, num_total_vehicles);
3049 const int num_resource_classes = resource_indices_per_class.size();
3050 std::vector<const std::vector<int64_t>*> vi_to_rc_cost(num_vehicles);
3051 int num_vehicles_to_assign = 0;
3052 for (
int i = 0; i < num_vehicles; ++i) {
3053 vi_to_rc_cost[i] = vehicle_to_resource_class_assignment_costs(vehicles[i]);
3054 if (!vi_to_rc_cost[i]->empty()) {
3055 DCHECK_EQ(vi_to_rc_cost[i]->size(), num_resource_classes);
3056 ++num_vehicles_to_assign;
3059 if (num_vehicles_to_assign > num_resources) {
3060 VLOG(3) <<
"Less resources (" << num_resources <<
") than the vehicles"
3061 <<
" requiring one (" << num_vehicles_to_assign <<
")";
3069 for (
int i = 0; i < num_vehicles; ++i) {
3070 if (!vi_to_rc_cost[i]->empty() &&
3071 *absl::c_max_element(*vi_to_rc_cost[i]) < 0) {
3072 VLOG(3) <<
"Vehicle #" << vehicles[i] <<
" has no feasible resource";
3080 int64_t max_arc_cost = 0;
3081 for (
const std::vector<int64_t>* costs : vi_to_rc_cost) {
3082 if (costs->empty())
continue;
3083 max_arc_cost = std::max(max_arc_cost, *absl::c_max_element(*costs));
3088 const int real_num_nodes = 4 + num_vehicles + num_resource_classes;
3089 const int64_t max_acceptable_arc_cost =
kint64max / (4 * real_num_nodes) - 1;
3092 int cost_right_shift = 0;
3093 while ((max_arc_cost >> cost_right_shift) > max_acceptable_arc_cost) {
3103 2 + num_vehicles + num_resource_classes,
3104 num_vehicles + num_vehicles * num_resource_classes +
3105 num_resource_classes);
3107 const int source_index = num_vehicles + num_resource_classes;
3108 const int sink_index = source_index + 1;
3109 const auto flow_rc_index = [num_vehicles](
int rc) {
3110 return num_vehicles + rc;
3115 if (resource_indices !=
nullptr) {
3116 vehicle_to_rc_arc_index =
3119 for (
int vi = 0; vi < num_vehicles; ++vi) {
3120 const std::vector<int64_t>& assignment_costs = *vi_to_rc_cost[vi];
3121 if (assignment_costs.empty())
continue;
3127 for (
int rc = 0; rc < num_resource_classes; rc++) {
3128 const int64_t assignment_cost = assignment_costs[rc];
3129 if (assignment_cost < 0)
continue;
3131 vi, flow_rc_index(rc), 1, assignment_cost >> cost_right_shift);
3132 if (resource_indices !=
nullptr) {
3133 vehicle_to_rc_arc_index[vi][rc] = arc;
3140 using RCIndex = RoutingModel::ResourceClassIndex;
3141 for (
int rc = 0; rc < num_resource_classes; rc++) {
3142 const RCIndex rci(rc);
3143 const int num_available_res = resource_indices_per_class[rci].size() -
3144 ignored_resources_per_class[rci].size();
3145 DCHECK_GE(num_available_res, 0);
3147 num_available_res, 0);
3156 VLOG(3) <<
"Non-OPTIMAL flow result";
3160 if (resource_indices !=
nullptr) {
3162 resource_indices->assign(num_total_vehicles, -1);
3163 std::vector<int> current_resource_pos_for_class(num_resource_classes, 0);
3164 for (
int vi = 0; vi < num_vehicles; ++vi) {
3165 if (vi_to_rc_cost[vi]->empty()) {
3169 for (
int rc = 0; rc < num_resource_classes; rc++) {
3170 const ArcIndex arc = vehicle_to_rc_arc_index[vi][rc];
3171 if (arc >= 0 && flow.
Flow(arc) > 0) {
3172 const RCIndex rci(rc);
3173 const std::vector<int>& class_resource_indices =
3174 resource_indices_per_class[rci];
3175 const absl::flat_hash_set<int>& ignored_resources =
3176 ignored_resources_per_class[rci];
3177 int& pos = current_resource_pos_for_class[rc];
3178 DCHECK_LT(pos, class_resource_indices.size());
3179 while (ignored_resources.contains(class_resource_indices[pos])) {
3181 DCHECK_LT(pos, class_resource_indices.size());
3183 resource_indices->at(vehicles[vi]) = class_resource_indices[pos++];
3191 DCHECK_LE(cost,
kint64max >> cost_right_shift);
3192 return cost << cost_right_shift;
3196 if (number ==
kint64min)
return "-infty";
3197 if (number ==
kint64max)
return "+infty";
3198 return std::to_string(number);
3202 const ::google::protobuf::RepeatedField<int64_t>* domain) {
3203 if (domain->size() > 2 && domain->size() % 2 == 0) {
3204 std::string s =
"∈ ";
3205 for (
int i = 0; i < domain->size(); i += 2) {
3206 s += absl::StrFormat(
"[%s, %s]",
Int64ToStr(domain->Get(i)),
3208 if (i < domain->size() - 2) s +=
" ∪ ";
3211 }
else if (domain->size() == 2) {
3212 if (domain->Get(0) == domain->Get(1)) {
3213 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
3214 }
else if (domain->Get(0) == 0 && domain->Get(1) == 1) {
3216 }
else if (domain->Get(0) == std::numeric_limits<int64_t>::min() &&
3217 domain->Get(1) == std::numeric_limits<int64_t>::max()) {
3219 }
else if (domain->Get(0) == std::numeric_limits<int64_t>::min()) {
3220 return absl::StrFormat(
"≤ %s",
Int64ToStr(domain->Get(1)));
3221 }
else if (domain->Get(1) == std::numeric_limits<int64_t>::max()) {
3222 return absl::StrFormat(
"≥ %s",
Int64ToStr(domain->Get(0)));
3224 return absl::StrFormat(
"∈ [%ls, %s]",
Int64ToStr(domain->Get(0)),
3226 }
else if (domain->size() == 1) {
3227 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
3229 return absl::StrFormat(
"∈ Unknown domain (size=%ld)", domain->size());
3234 std::pair<sat::IntegerVariableProto, int>& variable_pair,
3238 const int index = variable_pair.second;
3242 const double lp_value_double = response_.
solution(index);
3243 const int64_t lp_value_int64 =
3244 (lp_value_double >= std::numeric_limits<int64_t>::max())
3245 ? std::numeric_limits<int64_t>::max()
3257 bool show_enforcement =
true) {
3260 const auto& linear = constraint.
linear();
3261 for (
int j = 0; j < linear.vars().size(); ++j) {
3262 const std::string sign = linear.coeffs(j) > 0 ?
"+" :
"-";
3263 const std::string mult =
3264 std::abs(linear.coeffs(j)) != 1
3265 ? std::to_string(std::abs(linear.coeffs(j))) +
" * "
3267 if (j > 0 || sign !=
"+") s += sign +
" ";
3273 if (show_enforcement) {
3275 s += (j == 0) ?
"\t if " :
" and ";
3286 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>&
3288 absl::flat_hash_map<std::string, std::vector<int>>& variable_instances,
3289 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>&
3292 std::string prefix =
"") {
3293 if (variable.empty()) {
3295 const auto& childs = variable_childs[
""];
3296 for (
const std::string& child : childs) {
3299 response_, child, prefix) +
3305 const auto& instances = variable_instances[variable];
3306 std::string variable_display(variable);
3307 std::size_t bracket_pos = variable.find_last_of(
')');
3308 if (bracket_pos != std::string::npos) {
3309 variable_display = variable.substr(bracket_pos + 1);
3311 std::string s = variable_display +
" | ";
3312 prefix += std::string(variable_display.length(),
' ') +
" | ";
3313 for (
int i = 0; i < instances.size(); ++i) {
3314 const std::string instance_name =
3315 absl::StrFormat(
"%s(%ld)", variable, instances[i]);
3316 if (i > 0) s += prefix;
3317 s += absl::StrFormat(
"%ld: %s", instances[i],
3321 const auto& childs = variable_childs[instance_name];
3322 for (
const std::string& child : childs) {
3323 s +=
"\n" + prefix +
"| ";
3325 response_, child, prefix +
"| ");
3327 if (childs.empty()) s +=
"\n";
3334 std::vector<std::vector<std::string>> constraints_apart;
3335 constraints_apart.push_back(
3336 {
"compression_cost",
"travel_compression_absolute"});
3341 absl::flat_hash_map<std::string, std::vector<int>> variable_instances;
3345 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>
3348 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>
3350 variable_children[
""] = {};
3352 const int num_constraints = model_.constraints_size();
3353 const int num_variables = model_.variables_size();
3354 int num_binary_variables = 0;
3355 for (
int i = 0; i < num_variables; ++i) {
3356 const auto& variable = model_.variables(i);
3357 const auto& name = variable.name();
3358 const int pos_bracket = name.find_last_of(
'(');
3359 if (pos_bracket != std::string::npos) {
3360 const std::string lemma = name.substr(0, pos_bracket);
3361 const int pos_closing_bracket = name.find_last_of(
')');
3362 CHECK_NE(pos_closing_bracket, std::string::npos);
3364 std::stoi(name.substr(pos_bracket + 1, pos_closing_bracket));
3365 std::vector<int>* instances =
gtl::FindOrNull(variable_instances, lemma);
3366 if (instances !=
nullptr) {
3367 instances->push_back(index);
3369 variable_instances[lemma] = {index};
3371 variable_children[name] = {};
3373 std::string parent =
"";
3374 const int pos_parent_closing_bracket = lemma.find_last_of(
')');
3375 if (pos_parent_closing_bracket != std::string::npos) {
3376 parent = lemma.substr(0, pos_parent_closing_bracket + 1);
3378 variable_children[parent].emplace(lemma);
3379 variables[name] = std::make_pair(variable, i);
3380 if (variable.domain(0) == 0 & variable.domain(1) == 1) {
3381 ++num_binary_variables;
3392 absl::flat_hash_map<std::string, std::vector<sat::ConstraintProto>>
3394 absl::flat_hash_map<std::vector<std::string>,
3395 std::vector<sat::ConstraintProto>>
3397 for (
int i = 0; i < num_constraints; ++i) {
3398 const auto& constraint = model_.constraints(i);
3399 std::string enforcement =
"";
3400 if (constraint.enforcement_literal_size() == 1) {
3401 enforcement = model_.variables(constraint.enforcement_literal(0)).name();
3402 }
else if (constraint.enforcement_literal_size() > 1) {
3403 enforcement =
"multiple";
3405 if (constraint.has_linear()) {
3406 const auto& linear = constraint.linear();
3407 std::vector<std::string> key;
3408 for (
int j = 0; j < linear.vars().size(); ++j) {
3409 std::string var_name = model_.variables(linear.vars(j)).name();
3410 std::string lemma = var_name.substr(0, var_name.find_last_of(
'('));
3411 key.push_back(lemma);
3414 if (constraint_group !=
nullptr) {
3415 constraint_group->push_back(constraint);
3417 constraint_groups[key] = {constraint};
3421 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
3422 if (constraints_enforced !=
nullptr) {
3423 constraints[enforcement].push_back(constraint);
3425 constraints[enforcement] = {constraint};
3429 const std::string prefix_constraint =
" • ";
3430 std::string s =
"Using RoutingCPSatWrapper.\n";
3433 for (
int i = 0; i < objective_coefficients_.size(); ++i) {
3434 double coeff = objective_coefficients_[i];
3436 s += absl::StrFormat(
" | %f * %s\n", coeff, model_.variables(i).name());
3440 s += absl::StrFormat(
"\nVariables %d (%d Binary - %d Non Binary)\n",
3441 num_variables, num_binary_variables,
3442 num_variables - num_binary_variables);
3444 response_,
"",
" | ");
3445 s += absl::StrFormat(
"\n\nConstraints (%d)\n", num_constraints);
3448 s +=
"\n- Not enforced\n";
3449 bool at_least_one_not_enforced =
false;
3450 for (
const auto& pair : constraint_groups) {
3451 if (!std::count(constraints_apart.begin(), constraints_apart.end(),
3453 for (
const auto& constraint : pair.second) {
3456 at_least_one_not_enforced =
true;
3460 if (!at_least_one_not_enforced) {
3461 s += prefix_constraint +
"None\n";
3465 s +=
"\n- Single enforcement\n";
3466 bool at_least_one_single_enforced =
false;
3467 for (
const auto& pair : variable_instances) {
3468 const std::string lemma = pair.first;
3469 bool found_one_constraint =
false;
3470 std::string prefix =
"";
3471 for (
int instance : pair.second) {
3472 const std::string enforcement =
3473 absl::StrFormat(
"%s(%d)", lemma, instance);
3474 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
3475 std::string prefix_instance =
"";
3476 if (constraints_enforced !=
nullptr) {
3477 at_least_one_single_enforced =
true;
3478 if (!found_one_constraint) {
3479 found_one_constraint =
true;
3480 s += prefix_constraint +
"if " + lemma +
" | ";
3482 std::string(prefix_constraint.size() + 1 + lemma.size(),
' ') +
3487 s += absl::StrFormat(
"%d: | ", instance);
3488 prefix_instance = prefix +
" | ";
3490 for (
const auto& constraint : *constraints_enforced) {
3492 s += prefix_instance;
3500 if (!at_least_one_single_enforced) {
3501 s += prefix_constraint +
"None\n";
3505 s +=
"\n- Multiple enforcement\n";
3506 auto* constraints_multiple_enforced =
3508 if (constraints_multiple_enforced !=
nullptr) {
3509 for (
const auto& constraint : *constraints_multiple_enforced) {
3514 s += prefix_constraint +
"None\n";
3518 s +=
"\n- Set apart\n";
3519 bool at_least_one_apart =
false;
3520 for (
const auto& pair : constraint_groups) {
3521 if (std::count(constraints_apart.begin(), constraints_apart.end(),
3523 for (
const auto& constraint : pair.second) {
3526 at_least_one_apart =
true;
3530 if (!at_least_one_apart) {
3531 s += prefix_constraint +
"None\n";
CumulBoundsPropagator(const RoutingDimension *dimension)
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)
const RoutingDimension & dimension() const
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_without_transits, int64_t *transit_cost, bool clear_lp=true, bool optimize_resource_assignment=true)
DimensionSchedulingStatus ComputeSingleRouteSolutionCostWithoutFixedTransits(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo *dimension_travel_info, RoutingLinearSolverWrapper *solver, absl::Span< const int64_t > solution_cumul_values, absl::Span< const int64_t > solution_break_values, int64_t *cost_without_transits, int64_t *cost_offset=nullptr, bool reuse_previous_model_if_possible=true, bool clear_lp=false, bool clear_solution_constraints=true, absl::Duration *solve_duration=nullptr)
const RoutingDimension * dimension() const
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)
DimensionCumulOptimizerCore(const RoutingDimension *dimension, bool use_precedence_propagator)
DimensionSchedulingStatus OptimizeSingleRouteWithResource(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo *dimension_travel_info, const Resource *resource, bool optimize_vehicle_costs, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values, int64_t *cost_without_transit, int64_t *transit_cost, bool clear_lp=true)
DimensionSchedulingStatus OptimizeAndPackSingleRoute(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RouteDimensionTravelInfo *dimension_travel_info, const Resource *resource, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *cumul_values, std::vector< int64_t > *break_values)
std::vector< DimensionSchedulingStatus > OptimizeSingleRouteWithResources(int vehicle, double solve_duration_ratio, 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, absl::Span< const Resource > resources, absl::Span< const int > resource_indices, bool optimize_vehicle_costs, RoutingLinearSolverWrapper *solver, std::vector< std::vector< int64_t > > *cumul_values, std::vector< std::vector< int64_t > > *break_values, std::vector< int64_t > *costs_without_transits, int64_t *transit_cost, bool clear_lp=true)
static Domain AllValues()
NOTE(user): T=bool is not yet supported (the [] operator doesn't work).
const absl::InlinedVector< int64_t, 8 > & x_anchors() const
const absl::InlinedVector< int64_t, 8 > & y_anchors() const
DimensionSchedulingStatus ComputeCumulCostWithoutFixedTransits(const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost_without_transits)
GlobalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
GlobalDimensionCumulOptimizer.
const RoutingDimension * dimension() const
DimensionSchedulingStatus ComputeCumuls(const std::function< int64_t(int64_t)> &next_accessor, const std::vector< RoutingModel::RouteDimensionTravelInfo > &dimension_travel_info_per_route, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks, std::vector< std::vector< int > > *optimal_resource_indices_per_group)
DimensionSchedulingStatus 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)
virtual int64_t Max() const =0
virtual IntVarIterator * MakeDomainIterator(bool reversible) const =0
virtual uint64_t Size() const =0
This method returns the number of values in the domain of the variable.
const RoutingDimension * dimension() const
DimensionSchedulingStatus ComputeRouteSolutionCostWithoutFixedTransits(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo *dimension_travel_info, absl::Span< const int64_t > solution_cumul_values, absl::Span< const 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, double solve_duration_ratio, 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)
LocalDimensionCumulOptimizer(const RoutingDimension *dimension, RoutingSearchParameters::SchedulingSolver solver_type)
LocalDimensionCumulOptimizer.
DimensionSchedulingStatus ComputeRouteCumuls(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::RouteDimensionTravelInfo *dimension_travel_info, const RoutingModel::ResourceGroup::Resource *resource, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const RoutingModel::ResourceGroup::Resource *resource, int64_t *optimal_cost_without_transits)
DimensionSchedulingStatus ComputeRouteCumulsAndCostWithoutFixedTransits(int vehicle, double solve_duration_ratio, 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_without_transits)
std::vector< DimensionSchedulingStatus > ComputeRouteCumulCostsForResourcesWithoutFixedTransits(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, absl::Span< const RoutingModel::ResourceGroup::Resource > resources, absl::Span< const 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, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, int64_t *optimal_cost)
static int64_t FastInt64Round(double x)
std::string PrintModel() const override
Prints an understandable view of the model.
int64_t GetObjectiveValue() const override
virtual int64_t GetObjectiveValue() const =0
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
virtual int64_t GetVariableUpperBound(int index) const =0
virtual bool SolutionIsInteger() const =0
virtual void SetObjectiveCoefficient(int index, double coefficient)=0
static const int kNoConstraint
virtual void ClearObjective()=0
virtual bool SetVariableBounds(int index, int64_t lower_bound, int64_t upper_bound)=0
virtual void SetParameters(const std::string ¶meters)=0
This function is meant to override the parameters of the solver.
virtual void AddObjectiveConstraint()=0
virtual bool ModelIsEmpty() const
Returns if the model is empty or not.
virtual int64_t GetVariableLowerBound(int index) const =0
virtual bool IsCPSATSolver()=0
static constexpr SchedulingSolver SCHEDULING_GLOP
RoutingSearchParameters_SchedulingSolver SchedulingSolver
static constexpr SchedulingSolver SCHEDULING_CP_SAT
CostValue OptimalCost() const
void SetNodeSupply(NodeIndex node, FlowQuantity supply)
FlowQuantity Flow(ArcIndex arc) const
ArcIndex AddArcWithCapacityAndUnitCost(NodeIndex tail, NodeIndex head, FlowQuantity capacity, CostValue unit_cost)
void set_use_dual_simplex(bool value)
void set_use_preprocessing(bool value)
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::LinearConstraintProto & linear() const
bool has_linear() const
.operations_research.sat.LinearConstraintProto linear = 12;
int enforcement_literal_size() const
repeated int32 enforcement_literal = 2;
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
::operations_research::sat::CpSolverStatus status() const
bool IsInitialized() const
::int64_t solution(int index) const
::google::protobuf::RepeatedField<::int64_t > *PROTOBUF_NONNULL mutable_domain()
const ::std::string & name() const
bool IsInitialized() const
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
double FindBestScalingAndComputeErrors(absl::Span< const double > coefficients, absl::Span< const double > lower_bounds, absl::Span< const double > upper_bounds, int64_t max_absolute_activity, double wanted_absolute_activity_precision, double *relative_coeff_error, double *scaled_sum_error)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
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(absl::Span< const int > vehicles, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, std::vector< int > > &resource_indices_per_class, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, absl::flat_hash_set< int > > &ignored_resources_per_class, std::function< const std::vector< int64_t > *(int)> vehicle_to_resource_class_assignment_costs, std::vector< int > *resource_indices)
DimensionSchedulingStatus
@ INFEASIBLE
A solution could not be found.
int64_t CapSub(int64_t x, int64_t y)
std::vector< bool > SlopeAndYInterceptToConvexityRegions(absl::Span< const SlopeAndYIntercept > slope_and_y_intercept)
ClosedInterval::Iterator end(ClosedInterval interval)
std::string ProtobufShortDebugString(const P &message)
bool ComputeVehicleToResourceClassAssignmentCosts(int v, double solve_duration_ratio, const RoutingModel::ResourceGroup &resource_group, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, absl::flat_hash_set< int > > &ignored_resources_per_class, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, bool optimize_vehicle_costs, LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, std::vector< int64_t > *assignment_costs, std::vector< std::vector< int64_t > > *cumul_values, std::vector< std::vector< int64_t > > *break_values)
int64_t CapProd(int64_t x, int64_t y)
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_, absl::string_view variable, std::string prefix="")
std::string Int64ToStr(int64_t number)
std::vector< SlopeAndYIntercept > PiecewiseLinearFunctionToSlopeAndYIntercept(const FloatSlopePiecewiseLinearFunction &pwl_function, int index_start, int index_end)
void FillPathEvaluation(absl::Span< const int64_t > path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
std::string VariableToString(std::pair< sat::IntegerVariableProto, int > &variable_pair, const sat::CpSolverResponse &response_)
#define SET_DEBUG_VARIABLE_NAME(solver, var, name)
static const int64_t kint64max
static const int64_t kint64min