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"
59constexpr int64_t
kint64min = std::numeric_limits<int64_t>::min();
60constexpr int64_t
kint64max = std::numeric_limits<int64_t>::max();
67 parameters.set_use_preprocessing(
false);
78 int64_t node_index, int64_t cumul_offset,
79 int64_t* lower_bound, int64_t* upper_bound) {
80 DCHECK(lower_bound !=
nullptr);
81 DCHECK(upper_bound !=
nullptr);
83 const IntVar& cumul_var = *dimension.CumulVar(node_index);
84 *upper_bound = cumul_var.
Max();
85 if (*upper_bound < cumul_offset) {
89 const int64_t first_after_offset =
90 std::max(dimension.GetFirstPossibleGreaterOrEqualValueForNode(
91 node_index, cumul_offset),
94 *lower_bound =
CapSub(first_after_offset, cumul_offset);
95 DCHECK_GE(*lower_bound, 0);
100 *upper_bound =
CapSub(*upper_bound, cumul_offset);
101 DCHECK_GE(*upper_bound, *lower_bound);
105int64_t GetFirstPossibleValueForCumulWithOffset(
107 int64_t lower_bound_without_offset, int64_t cumul_offset) {
109 dimension.GetFirstPossibleGreaterOrEqualValueForNode(
110 node_index,
CapAdd(lower_bound_without_offset, cumul_offset)),
114int64_t GetLastPossibleValueForCumulWithOffset(
116 int64_t upper_bound_without_offset, int64_t cumul_offset) {
118 dimension.GetLastPossibleLessOrEqualValueForNode(
119 node_index,
CapAdd(upper_bound_without_offset, cumul_offset)),
129void StoreVisitedPickupDeliveryPairsOnRoute(
131 const std::function<int64_t(int64_t)>& next_accessor,
132 std::vector<int>* visited_pairs,
133 std::vector<std::pair<int64_t, int64_t>>*
134 visited_pickup_delivery_indices_for_pair) {
136 DCHECK_EQ(visited_pickup_delivery_indices_for_pair->size(),
137 dimension.model()->GetPickupAndDeliveryPairs().size());
138 DCHECK(std::all_of(visited_pickup_delivery_indices_for_pair->begin(),
139 visited_pickup_delivery_indices_for_pair->end(),
140 [](std::pair<int64_t, int64_t> p) {
141 return p.first == -1 && p.second == -1;
143 visited_pairs->clear();
144 if (!dimension.HasPickupToDeliveryLimits()) {
149 int64_t node_index = model.
Start(vehicle);
150 while (!model.IsEnd(node_index)) {
151 if (model.IsPickup(node_index)) {
153 const std::optional<PDPosition> pickup_position =
154 model.GetPickupPosition(node_index);
155 DCHECK(pickup_position.has_value());
156 const int pair_index = pickup_position->pd_pair_index;
157 (*visited_pickup_delivery_indices_for_pair)[pair_index].first =
159 visited_pairs->push_back(pair_index);
160 }
else if (model.IsDelivery(node_index)) {
163 const std::optional<PDPosition> delivery_position =
164 model.GetDeliveryPosition(node_index);
165 DCHECK(delivery_position.has_value());
166 const int pair_index = delivery_position->pd_pair_index;
167 std::pair<int64_t, int64_t>& pickup_delivery_index =
168 (*visited_pickup_delivery_indices_for_pair)[pair_index];
169 if (pickup_delivery_index.first < 0) {
172 node_index = next_accessor(node_index);
175 pickup_delivery_index.second = node_index;
177 node_index = next_accessor(node_index);
192 const int vehicles =
dimension->model()->vehicles();
193 solver_.resize(vehicles);
194 switch (solver_type) {
197 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
200 solver_[vehicle] = std::make_unique<RoutingGlopWrapper>(
201 false, parameters, search_stats);
206 for (
int vehicle = 0; vehicle < vehicles; ++vehicle) {
207 solver_[vehicle] = std::make_unique<RoutingCPSatWrapper>(search_stats);
212 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
217 int vehicle,
double solve_duration_ratio,
218 const std::function<int64_t(int64_t)>& next_accessor,
219 int64_t* optimal_cost) {
220 DCHECK_GT(solve_duration_ratio, 0);
221 DCHECK_LE(solve_duration_ratio, 1);
222 int64_t transit_cost = 0;
224 optimizer_core_.OptimizeSingleRouteWithResource(
225 vehicle, solve_duration_ratio, next_accessor,
228 optimal_cost !=
nullptr,
229 solver_[vehicle].get(),
nullptr,
230 nullptr, optimal_cost, &transit_cost);
232 optimal_cost !=
nullptr) {
233 DCHECK_GE(*optimal_cost, 0);
234 *optimal_cost =
CapAdd(*optimal_cost, transit_cost);
241 int vehicle,
double solve_duration_ratio,
242 const std::function<int64_t(int64_t)>& next_accessor,
244 int64_t* optimal_cost_without_transits) {
245 DCHECK_GT(solve_duration_ratio, 0);
246 DCHECK_LE(solve_duration_ratio, 1);
247 return optimizer_core_.OptimizeSingleRouteWithResource(
248 vehicle, solve_duration_ratio, next_accessor,
250 optimal_cost_without_transits !=
nullptr,
251 solver_[vehicle].get(),
nullptr,
252 nullptr, optimal_cost_without_transits,
nullptr);
257 int vehicle,
double solve_duration_ratio,
258 const std::function<int64_t(int64_t)>& next_accessor,
259 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
260 absl::Span<const RoutingModel::ResourceGroup::Resource> resources,
261 absl::Span<const int> resource_indices,
bool optimize_vehicle_costs,
262 std::vector<int64_t>* optimal_costs_without_transits,
263 std::vector<std::vector<int64_t>>* optimal_cumuls,
264 std::vector<std::vector<int64_t>>* optimal_breaks) {
265 DCHECK_GT(solve_duration_ratio, 0);
266 DCHECK_LE(solve_duration_ratio, 1);
267 return optimizer_core_.OptimizeSingleRouteWithResources(
268 vehicle, solve_duration_ratio, next_accessor, transit_accessor,
nullptr,
269 resources, resource_indices, optimize_vehicle_costs,
270 solver_[vehicle].get(), optimal_cumuls, optimal_breaks,
271 optimal_costs_without_transits,
nullptr);
275 int vehicle,
double solve_duration_ratio,
276 const std::function<int64_t(int64_t)>& next_accessor,
279 std::vector<int64_t>* optimal_cumuls,
280 std::vector<int64_t>* optimal_breaks) {
281 DCHECK_GT(solve_duration_ratio, 0);
282 DCHECK_LE(solve_duration_ratio, 1);
283 return optimizer_core_.OptimizeSingleRouteWithResource(
284 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
285 resource,
true, solver_[vehicle].get(),
286 optimal_cumuls, optimal_breaks,
nullptr,
292 int vehicle,
double solve_duration_ratio,
293 const std::function<int64_t(int64_t)>& next_accessor,
295 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
296 int64_t* optimal_cost_without_transits) {
297 DCHECK_GT(solve_duration_ratio, 0);
298 DCHECK_LE(solve_duration_ratio, 1);
299 return optimizer_core_.OptimizeSingleRouteWithResource(
300 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
301 nullptr,
true, solver_[vehicle].get(),
302 optimal_cumuls, optimal_breaks, optimal_cost_without_transits,
nullptr);
307 int vehicle,
double solve_duration_ratio,
308 const std::function<int64_t(int64_t)>& next_accessor,
310 absl::Span<const int64_t> solution_cumul_values,
311 absl::Span<const int64_t> solution_break_values, int64_t* solution_cost,
312 int64_t* cost_offset,
bool reuse_previous_model_if_possible,
bool clear_lp,
313 absl::Duration* solve_duration) {
314 DCHECK_GT(solve_duration_ratio, 0);
315 DCHECK_LE(solve_duration_ratio, 1);
317 return optimizer_core_.ComputeSingleRouteSolutionCostWithoutFixedTransits(
318 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
319 solver, solution_cumul_values, solution_break_values, solution_cost,
320 cost_offset, reuse_previous_model_if_possible, clear_lp,
321 true, solve_duration);
326 int vehicle,
double solve_duration_ratio,
327 const std::function<int64_t(int64_t)>& next_accessor,
330 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks) {
331 DCHECK_GT(solve_duration_ratio, 0);
332 DCHECK_LE(solve_duration_ratio, 1);
333 return optimizer_core_.OptimizeAndPackSingleRoute(
334 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
335 resource, solver_[vehicle].get(), packed_cumuls, packed_breaks);
340 int vehicle,
double solve_duration_ratio,
341 const std::function<int64_t(int64_t)>& next_accessor,
342 absl::Span<const int64_t> transit_targets,
344 std::vector<int64_t>* optimal_transits,
345 std::vector<int64_t>* optimal_cumuls,
346 std::vector<int64_t>* optimal_breaks) {
347 DCHECK_GT(solve_duration_ratio, 0);
348 DCHECK_LE(solve_duration_ratio, 1);
349 return optimizer_core_.OptimizeSingleRouteWithTransitTargets(
350 vehicle, solve_duration_ratio, next_accessor, transit_targets,
351 transit_target_cost, solver_[vehicle].get(), optimal_transits,
352 optimal_cumuls, optimal_breaks);
355const int CumulBoundsPropagator::kNoParent = -2;
356const int CumulBoundsPropagator::kParentToBePropagated = -1;
360 outgoing_arcs_.resize(num_nodes_);
361 node_in_queue_.resize(num_nodes_,
false);
362 tree_parent_node_of_.resize(num_nodes_, kNoParent);
363 propagated_bounds_.resize(num_nodes_);
364 visited_pickup_delivery_indices_for_pair_.resize(
365 dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1});
368void CumulBoundsPropagator::AddArcs(
int first_index,
int second_index,
371 outgoing_arcs_[PositiveNode(first_index)].push_back(
372 {PositiveNode(second_index), offset});
373 AddNodeToQueue(PositiveNode(first_index));
375 outgoing_arcs_[NegativeNode(second_index)].push_back(
376 {NegativeNode(first_index), offset});
377 AddNodeToQueue(NegativeNode(second_index));
380bool CumulBoundsPropagator::InitializeArcsAndBounds(
381 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
382 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
383 dimension_travel_info_per_route) {
384 propagated_bounds_.assign(num_nodes_,
kint64min);
386 for (std::vector<ArcInfo>& arcs : outgoing_arcs_) {
390 RoutingModel*
const model = dimension_.model();
391 std::vector<int64_t>& lower_bounds = propagated_bounds_;
393 for (
int vehicle = 0; vehicle < model->vehicles(); vehicle++) {
394 const std::function<int64_t(int64_t, int64_t)>& transit_accessor =
395 dimension_.transit_evaluator(vehicle);
397 int node = model->Start(vehicle);
398 int index_on_route = 0;
400 int64_t cumul_lb, cumul_ub;
401 if (!GetCumulBoundsWithOffset(dimension_, node, cumul_offset, &cumul_lb,
405 lower_bounds[PositiveNode(node)] = cumul_lb;
407 lower_bounds[NegativeNode(node)] = -cumul_ub;
410 if (model->IsEnd(node)) {
414 const int next = next_accessor(node);
415 int64_t transit = transit_accessor(node, next);
416 if (dimension_travel_info_per_route !=
nullptr &&
417 !dimension_travel_info_per_route->empty()) {
418 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
419 transition_info = (*dimension_travel_info_per_route)[vehicle]
420 .transition_info[index_on_route];
421 transit = transition_info.compressed_travel_value_lower_bound +
422 transition_info.pre_travel_transit_value +
423 transition_info.post_travel_transit_value;
426 const IntVar& slack_var = *dimension_.SlackVar(node);
429 AddArcs(node, next,
CapAdd(transit, slack_var.Min()));
432 AddArcs(next, node,
CapSub(-slack_var.Max(), transit));
439 const int64_t span_ub = dimension_.GetSpanUpperBoundForVehicle(vehicle);
441 AddArcs(model->End(vehicle), model->Start(vehicle), -span_ub);
445 std::vector<int> visited_pairs;
446 StoreVisitedPickupDeliveryPairsOnRoute(
447 dimension_, vehicle, next_accessor, &visited_pairs,
448 &visited_pickup_delivery_indices_for_pair_);
449 for (
int pair_index : visited_pairs) {
450 const int64_t pickup_index =
451 visited_pickup_delivery_indices_for_pair_[pair_index].first;
452 const int64_t delivery_index =
453 visited_pickup_delivery_indices_for_pair_[pair_index].second;
454 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
456 DCHECK_GE(pickup_index, 0);
457 if (delivery_index < 0) {
462 const int64_t limit = dimension_.GetPickupToDeliveryLimitForPair(
463 pair_index, model->GetPickupPosition(pickup_index)->alternative_index,
464 model->GetDeliveryPosition(delivery_index)->alternative_index);
467 AddArcs(delivery_index, pickup_index, -limit);
472 for (
const auto [first_node, second_node, offset, performed_constraint] :
473 dimension_.GetNodePrecedences()) {
474 if (next_accessor(first_node) == -1 || next_accessor(second_node) == -1) {
477 const bool first_node_unperformed =
478 lower_bounds[PositiveNode(first_node)] ==
kint64min;
479 const bool second_node_unperformed =
480 lower_bounds[PositiveNode(second_node)] ==
kint64min;
482 second_node_unperformed,
483 performed_constraint)) {
491 AddArcs(first_node, second_node, offset);
497bool CumulBoundsPropagator::UpdateCurrentLowerBoundOfNode(
int node,
500 const int cumul_var_index = node / 2;
502 if (node == PositiveNode(cumul_var_index)) {
504 propagated_bounds_[node] = GetFirstPossibleValueForCumulWithOffset(
505 dimension_, cumul_var_index, new_lb, offset);
508 const int64_t new_ub =
CapSub(0, new_lb);
509 propagated_bounds_[node] =
510 CapSub(0, GetLastPossibleValueForCumulWithOffset(
511 dimension_, cumul_var_index, new_ub, offset));
515 const int64_t cumul_lower_bound =
516 propagated_bounds_[PositiveNode(cumul_var_index)];
518 const int64_t negated_cumul_upper_bound =
519 propagated_bounds_[NegativeNode(cumul_var_index)];
521 return CapAdd(negated_cumul_upper_bound, cumul_lower_bound) <= 0;
524bool CumulBoundsPropagator::DisassembleSubtree(
int source,
int target) {
525 tmp_dfs_stack_.clear();
526 tmp_dfs_stack_.push_back(source);
527 while (!tmp_dfs_stack_.empty()) {
528 const int tail = tmp_dfs_stack_.back();
529 tmp_dfs_stack_.pop_back();
530 for (
const ArcInfo& arc : outgoing_arcs_[tail]) {
531 const int child_node = arc.head;
532 if (tree_parent_node_of_[child_node] != tail)
continue;
533 if (child_node == target)
return false;
534 tree_parent_node_of_[child_node] = kParentToBePropagated;
535 tmp_dfs_stack_.push_back(child_node);
542 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
543 const std::vector<RoutingModel::RouteDimensionTravelInfo>*
544 dimension_travel_info_per_route) {
545 tree_parent_node_of_.assign(num_nodes_, kNoParent);
546 DCHECK(std::none_of(node_in_queue_.begin(), node_in_queue_.end(),
547 [](
bool b) { return b; }));
548 DCHECK(bf_queue_.empty());
550 if (!InitializeArcsAndBounds(next_accessor, cumul_offset,
551 dimension_travel_info_per_route)) {
552 return CleanupAndReturnFalse();
555 std::vector<int64_t>& current_lb = propagated_bounds_;
558 while (!bf_queue_.empty()) {
559 const int node = bf_queue_.front();
560 bf_queue_.pop_front();
561 node_in_queue_[node] =
false;
563 if (tree_parent_node_of_[node] == kParentToBePropagated) {
569 const int64_t lower_bound = current_lb[node];
570 for (
const ArcInfo& arc : outgoing_arcs_[node]) {
573 const int64_t induced_lb = (lower_bound ==
kint64min)
575 :
CapAdd(lower_bound, arc.offset);
577 const int head_node = arc.head;
578 if (induced_lb <= current_lb[head_node]) {
583 if (!UpdateCurrentLowerBoundOfNode(head_node, induced_lb, cumul_offset) ||
584 !DisassembleSubtree(head_node, node)) {
587 return CleanupAndReturnFalse();
590 tree_parent_node_of_[head_node] = node;
591 AddNodeToQueue(head_node);
600 visited_pickup_delivery_indices_for_pair_(
601 dimension->model()->GetPickupAndDeliveryPairs().size(), {-1, -1}) {
602 if (use_precedence_propagator) {
603 propagator_ = std::make_unique<CumulBoundsPropagator>(dimension);
605 const RoutingModel& model = *dimension_->model();
606 if (dimension_->HasBreakConstraints()) {
610 const int num_vehicles = model.vehicles();
611 vehicle_to_all_break_variables_offset_.reserve(num_vehicles);
612 int num_break_vars = 0;
613 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
614 vehicle_to_all_break_variables_offset_.push_back(num_break_vars);
615 const auto& intervals = dimension_->GetBreakIntervalsOfVehicle(vehicle);
616 num_break_vars += 2 * intervals.size();
618 all_break_variables_.resize(num_break_vars, -1);
620 if (!model.GetDimensionResourceGroupIndices(dimension_).empty()) {
621 resource_class_to_vehicle_assignment_variables_per_group_.resize(
622 model.GetResourceGroups().size());
623 resource_class_ignored_resources_per_group_.resize(
624 model.GetResourceGroups().size());
630 int vehicle,
double solve_duration_ratio,
631 const std::function<int64_t(int64_t)>& next_accessor,
632 const RouteDimensionTravelInfo* dimension_travel_info,
634 absl::Span<const int64_t> solution_cumul_values,
635 absl::Span<const int64_t> solution_break_values,
636 int64_t* cost_without_transits, int64_t* cost_offset,
637 bool reuse_previous_model_if_possible,
bool clear_lp,
638 bool clear_solution_constraints, absl::Duration*
const solve_duration) {
639 absl::Duration solve_duration_value;
640 int64_t cost_offset_value;
641 if (!reuse_previous_model_if_possible || solver->
ModelIsEmpty()) {
642 InitOptimizer(solver);
645 DCHECK_EQ(propagator_.get(),
nullptr);
648 const bool optimize_vehicle_costs =
649 !model->
IsEnd(next_accessor(model->
Start(vehicle))) ||
651 if (!SetRouteCumulConstraints(
652 vehicle, next_accessor, dimension_->transit_evaluator(vehicle), {},
653 dimension_travel_info,
654 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle),
655 optimize_vehicle_costs, solver,
nullptr, &cost_offset_value)) {
661 solve_duration_value = model->
RemainingTime() * solve_duration_ratio;
662 if (solve_duration !=
nullptr) *solve_duration = solve_duration_value;
663 if (cost_offset !=
nullptr) *cost_offset = cost_offset_value;
665 CHECK(cost_offset !=
nullptr)
666 <<
"Cannot reuse model without the cost_offset";
667 cost_offset_value = *cost_offset;
668 CHECK(solve_duration !=
nullptr)
669 <<
"Cannot reuse model without the solve_duration";
670 solve_duration_value = *solve_duration * solve_duration_ratio;
674 DCHECK_EQ(solution_cumul_values.size(),
675 current_route_cumul_variables_.size());
676 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
677 if (solution_cumul_values[i] < current_route_min_cumuls_[i] ||
678 solution_cumul_values[i] > current_route_max_cumuls_[i]) {
682 solution_cumul_values[i],
683 solution_cumul_values[i]);
687 DCHECK_EQ(solution_break_values.size(),
688 current_route_break_variables_.size());
689 std::vector<int64_t> current_route_min_breaks(
690 current_route_break_variables_.size());
691 std::vector<int64_t> current_route_max_breaks(
692 current_route_break_variables_.size());
693 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
694 current_route_min_breaks[i] =
696 current_route_max_breaks[i] =
699 solution_break_values[i],
700 solution_break_values[i]);
709 if (cost_without_transits !=
nullptr) {
710 *cost_without_transits =
716 }
else if (clear_solution_constraints) {
717 for (
int i = 0; i < current_route_cumul_variables_.size(); ++i) {
719 current_route_min_cumuls_[i],
720 current_route_max_cumuls_[i]);
722 for (
int i = 0; i < current_route_break_variables_.size(); ++i) {
724 current_route_min_breaks[i],
725 current_route_max_breaks[i]);
733void ClearIfNonNull(std::vector<T>* v) {
742 int vehicle,
double solve_duration_ratio,
743 const std::function<int64_t(int64_t)>& next_accessor,
744 const RouteDimensionTravelInfo* dimension_travel_info,
747 std::vector<int64_t>* cumul_values, std::vector<int64_t>* break_values,
748 int64_t* cost_without_transits, int64_t* transit_cost,
bool clear_lp) {
749 if (cost_without_transits !=
nullptr) *cost_without_transits = -1;
750 ClearIfNonNull(cumul_values);
751 ClearIfNonNull(break_values);
753 const std::vector<Resource> resources =
754 resource ==
nullptr ? std::vector<Resource>()
755 : std::vector<Resource>({*resource});
756 const std::vector<int> resource_indices =
757 resource ==
nullptr ? std::vector<int>() : std::vector<int>({0});
758 std::vector<int64_t> costs_without_transits;
759 std::vector<std::vector<int64_t>> cumul_values_vec;
760 std::vector<std::vector<int64_t>> break_values_vec;
761 const std::vector<DimensionSchedulingStatus> statuses =
763 vehicle, solve_duration_ratio, next_accessor,
764 dimension_->transit_evaluator(vehicle), dimension_travel_info,
765 resources, resource_indices, optimize_vehicle_costs, solver,
766 cumul_values !=
nullptr ? &cumul_values_vec :
nullptr,
767 break_values !=
nullptr ? &break_values_vec :
nullptr,
768 cost_without_transits !=
nullptr ? &costs_without_transits :
nullptr,
769 transit_cost, clear_lp);
771 if (
dimension()->model()->CheckLimit()) {
774 DCHECK_EQ(statuses.size(), 1);
779 if (cost_without_transits !=
nullptr) {
780 *cost_without_transits = costs_without_transits[0];
782 if (cumul_values !=
nullptr) *cumul_values = std::move(cumul_values_vec[0]);
783 if (break_values !=
nullptr) *break_values = std::move(break_values_vec[0]);
792bool GetDomainOffsetBounds(
const Domain& domain, int64_t offset,
794 const int64_t lower_bound =
795 std::max<int64_t>(
CapSub(domain.
Min(), offset), 0);
796 const int64_t upper_bound =
798 if (lower_bound > upper_bound)
return false;
804bool GetIntervalIntersectionWithOffsetDomain(
const ClosedInterval& interval,
807 ClosedInterval* intersection) {
809 *intersection = interval;
813 if (!GetDomainOffsetBounds(domain, offset, &offset_domain)) {
816 const int64_t intersection_lb = std::max(interval.start, offset_domain.start);
817 const int64_t intersection_ub = std::min(interval.end, offset_domain.end);
818 if (intersection_lb > intersection_ub)
return false;
827 solver.GetVariableUpperBound(index));
830bool TightenStartEndVariableBoundsWithResource(
835 const ResourceGroup::Attributes& attributes =
836 resource.GetDimensionAttributes(dimension.index());
839 return GetIntervalIntersectionWithOffsetDomain(start_bounds,
840 attributes.start_domain(),
841 offset, &new_start_bounds) &&
842 solver->SetVariableBounds(start_index, new_start_bounds.start,
843 new_start_bounds.end) &&
844 GetIntervalIntersectionWithOffsetDomain(
845 end_bounds, attributes.end_domain(), offset, &new_end_bounds) &&
846 solver->SetVariableBounds(end_index, new_end_bounds.start,
850bool TightenStartEndVariableBoundsWithAssignedResources(
854 for (
int rg_index : model->GetDimensionResourceGroupIndices(&dimension)) {
855 const IntVar* res_var = model->ResourceVars(rg_index)[v];
856 DCHECK(res_var->Bound());
857 const int res_index = res_var->Value();
860 DCHECK(!model->GetResourceGroup(rg_index)->VehicleRequiresAResource(v));
863 const ResourceGroup::Resource& resource =
864 model->GetResourceGroup(rg_index)->GetResource(res_index);
865 if (!TightenStartEndVariableBoundsWithResource(
866 dimension, resource, GetVariableBounds(start_index, *solver),
867 start_index, GetVariableBounds(end_index, *solver), end_index,
877std::vector<DimensionSchedulingStatus>
879 int vehicle,
double solve_duration_ratio,
880 const std::function<int64_t(int64_t)>& next_accessor,
881 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
882 const RouteDimensionTravelInfo* dimension_travel_info,
883 absl::Span<const RoutingModel::ResourceGroup::Resource> resources,
884 absl::Span<const int> resource_indices,
bool optimize_vehicle_costs,
886 std::vector<std::vector<int64_t>>* cumul_values,
887 std::vector<std::vector<int64_t>>* break_values,
888 std::vector<int64_t>* costs_without_transits, int64_t* transit_cost,
890 ClearIfNonNull(costs_without_transits);
891 const bool optimize_with_resources = !resource_indices.empty();
892 if (!optimize_with_resources && !resources.empty())
return {};
894 InitOptimizer(solver);
897 DCHECK_EQ(propagator_.get(),
nullptr);
900 if (model->
IsEnd(next_accessor(model->
Start(vehicle))) &&
903 DCHECK(!optimize_with_resources);
904 optimize_vehicle_costs =
false;
907 const int64_t cumul_offset =
908 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle);
909 int64_t cost_offset = 0;
910 if (!SetRouteCumulConstraints(vehicle, next_accessor, transit_accessor, {},
911 dimension_travel_info, cumul_offset,
912 optimize_vehicle_costs, solver, transit_cost,
914 if (costs_without_transits !=
nullptr) {
915 costs_without_transits->assign(1, -1);
919 DCHECK_GE(current_route_cumul_variables_.size(), 2);
925 const int num_solves = resource_indices.empty() ? 1 : resource_indices.size();
926 if (costs_without_transits !=
nullptr) {
927 costs_without_transits->assign(num_solves, -1);
929 if (cumul_values !=
nullptr) cumul_values->assign(num_solves, {});
930 if (break_values !=
nullptr) break_values->assign(num_solves, {});
932 const int start_cumul = current_route_cumul_variables_[0];
933 const ClosedInterval start_bounds = GetVariableBounds(start_cumul, *solver);
934 const int end_cumul = current_route_cumul_variables_.back();
935 const ClosedInterval end_bounds = GetVariableBounds(end_cumul, *solver);
936 std::vector<DimensionSchedulingStatus> statuses;
937 statuses.reserve(num_solves);
938 for (
int i = 0; i < num_solves; i++) {
941 ClearIfNonNull(costs_without_transits);
942 ClearIfNonNull(cumul_values);
943 ClearIfNonNull(break_values);
947 if (optimize_with_resources &&
948 !TightenStartEndVariableBoundsWithResource(
949 *dimension_, resources[resource_indices[i]], start_bounds,
950 start_cumul, end_bounds, end_cumul, cumul_offset, solver)) {
961 if (costs_without_transits !=
nullptr) {
962 costs_without_transits->at(i) =
963 optimize_vehicle_costs
968 if (cumul_values !=
nullptr) {
969 SetValuesFromLP(current_route_cumul_variables_, cumul_offset,
kint64min,
970 solver, &cumul_values->at(i));
972 if (break_values !=
nullptr) {
973 SetValuesFromLP(current_route_break_variables_, cumul_offset,
kint64min,
974 solver, &break_values->at(i));
985 const std::function<int64_t(int64_t)>& next_accessor,
986 const std::vector<RouteDimensionTravelInfo>&
987 dimension_travel_info_per_route,
989 std::vector<int64_t>* break_values,
990 std::vector<std::vector<int>>* resource_indices_per_group,
991 int64_t* cost_without_transits, int64_t* transit_cost,
bool clear_lp,
992 bool optimize_resource_assignment) {
993 InitOptimizer(solver);
994 if (!optimize_resource_assignment) {
995 DCHECK_EQ(resource_indices_per_group,
nullptr);
1000 const bool optimize_costs =
1001 (cumul_values !=
nullptr) || (cost_without_transits !=
nullptr);
1002 bool has_vehicles_being_optimized =
false;
1004 const int64_t cumul_offset = dimension_->GetGlobalOptimizerOffset();
1006 if (propagator_ !=
nullptr &&
1007 !propagator_->PropagateCumulBounds(next_accessor, cumul_offset,
1008 &dimension_travel_info_per_route)) {
1012 int64_t total_transit_cost = 0;
1013 int64_t total_cost_offset = 0;
1015 for (
int vehicle = 0; vehicle < model->
vehicles(); vehicle++) {
1016 int64_t route_transit_cost = 0;
1017 int64_t route_cost_offset = 0;
1018 const bool vehicle_is_used =
1019 !model->
IsEnd(next_accessor(model->
Start(vehicle))) ||
1021 const bool optimize_vehicle_costs = optimize_costs && vehicle_is_used;
1022 const RouteDimensionTravelInfo*
const dimension_travel_info =
1023 dimension_travel_info_per_route.empty()
1025 : &dimension_travel_info_per_route[vehicle];
1026 if (!SetRouteCumulConstraints(
1027 vehicle, next_accessor, dimension_->transit_evaluator(vehicle), {},
1028 dimension_travel_info, cumul_offset, optimize_vehicle_costs, solver,
1029 &route_transit_cost, &route_cost_offset)) {
1032 DCHECK_GE(current_route_cumul_variables_.size(), 2);
1033 if (vehicle_is_used && !optimize_resource_assignment) {
1035 if (!TightenStartEndVariableBoundsWithAssignedResources(
1036 *dimension_, vehicle, current_route_cumul_variables_[0],
1037 current_route_cumul_variables_.back(), cumul_offset, solver)) {
1041 total_transit_cost =
CapAdd(total_transit_cost, route_transit_cost);
1042 total_cost_offset =
CapAdd(total_cost_offset, route_cost_offset);
1043 has_vehicles_being_optimized |= optimize_vehicle_costs;
1045 if (transit_cost !=
nullptr) {
1046 *transit_cost = total_transit_cost;
1049 if (!SetGlobalConstraints(next_accessor, cumul_offset,
1050 has_vehicles_being_optimized,
1051 optimize_resource_assignment, solver)) {
1064 SetValuesFromLP(index_to_cumul_variable_, cumul_offset,
kint64min, solver,
1066 SetValuesFromLP(all_break_variables_, cumul_offset,
kint64min, solver,
1068 SetResourceIndices(solver, resource_indices_per_group);
1070 if (cost_without_transits !=
nullptr) {
1071 *cost_without_transits =
1082 const std::function<int64_t(int64_t)>& next_accessor,
1083 const std::vector<RouteDimensionTravelInfo>&
1084 dimension_travel_info_per_route,
1086 std::vector<int64_t>* break_values) {
1093 packing_parameters = original_params;
1096 solver->
SetParameters(packing_parameters.SerializeAsString());
1099 Optimize(next_accessor, dimension_travel_info_per_route, solver,
1105 std::vector<int> vehicles(
dimension()->model()->vehicles());
1106 std::iota(vehicles.begin(), vehicles.end(), 0);
1109 status = PackRoutes(std::move(vehicles), 1.0,
1110 solver, packing_parameters);
1120 const int64_t global_offset = dimension_->GetGlobalOptimizerOffset();
1121 SetValuesFromLP(index_to_cumul_variable_, global_offset,
kint64min, solver,
1123 SetValuesFromLP(all_break_variables_, global_offset,
kint64min, solver,
1131 int vehicle,
double solve_duration_ratio,
1132 const std::function<int64_t(int64_t)>& next_accessor,
1133 const RouteDimensionTravelInfo* dimension_travel_info,
1136 std::vector<int64_t>* break_values) {
1140 packing_parameters = original_params;
1143 solver->
SetParameters(packing_parameters.SerializeAsString());
1150 vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
1151 resource,
true, solver,
1158 PackRoutes({vehicle}, solve_duration_ratio, solver, packing_parameters);
1167 const int64_t local_offset =
1168 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle);
1169 SetValuesFromLP(current_route_cumul_variables_, local_offset,
kint64min,
1170 solver, cumul_values);
1171 SetValuesFromLP(current_route_break_variables_, local_offset,
kint64min,
1172 solver, break_values);
1178 std::vector<int> vehicles,
double solve_duration_ratio,
1197 for (
int vehicle : vehicles) {
1199 index_to_cumul_variable_[model->
End(vehicle)], 1);
1202 glop::GlopParameters current_params;
1203 const auto retry_solving = [¤t_params, model, solver,
1204 solve_duration_ratio]() {
1208 current_params.set_use_dual_simplex(!current_params.use_dual_simplex());
1209 solver->SetParameters(current_params.SerializeAsString());
1210 return solver->Solve(model->RemainingTime() * solve_duration_ratio);
1212 if (solver->Solve(model->RemainingTime() * solve_duration_ratio) ==
1214 if (solver->IsCPSATSolver()) {
1218 current_params = packing_parameters;
1226 solver->ClearObjective();
1227 for (
int vehicle : vehicles) {
1228 const int end_cumul_var = index_to_cumul_variable_[model->End(vehicle)];
1230 solver->SetVariableBounds(end_cumul_var,
1231 solver->GetVariableLowerBound(end_cumul_var),
1232 solver->GetVariableValue(end_cumul_var));
1235 solver->SetObjectiveCoefficient(
1236 index_to_cumul_variable_[model->Start(vehicle)], -1);
1240 solver->Solve(model->RemainingTime() * solve_duration_ratio);
1241 if (!solver->IsCPSATSolver() &&
1243 status = retry_solving();
1250 int vehicle,
double solve_duration_ratio,
1251 const std::function<int64_t(int64_t)>& next_accessor,
1252 absl::Span<const int64_t> transit_targets,
1254 std::vector<int64_t>* optimal_transits,
1255 std::vector<int64_t>* optimal_cumuls,
1256 std::vector<int64_t>* optimal_breaks) {
1257 ClearIfNonNull(optimal_transits);
1258 ClearIfNonNull(optimal_cumuls);
1259 ClearIfNonNull(optimal_breaks);
1260 InitOptimizer(solver);
1261 const int64_t cumul_offset =
1262 dimension_->GetLocalOptimizerOffsetForVehicle(vehicle);
1263 const auto& transit_evaluator = dimension_->transit_evaluator(vehicle);
1265 if (!SetRouteCumulConstraints(
1266 vehicle, next_accessor, transit_evaluator, transit_targets, {},
1267 cumul_offset,
false, solver,
nullptr,
nullptr)) {
1270 DCHECK_GE(current_route_cumul_variables_.size(), 2);
1271 DCHECK_EQ(transit_targets.size(), current_route_cumul_variables_.size() - 1);
1273 const auto [threshold_ratio, cost_coefficient_below_threshold,
1274 cost_coefficient_above_threshold] = transit_target_cost;
1275 DCHECK_GT(threshold_ratio, 0);
1276 DCHECK_LT(threshold_ratio, 1);
1277 DCHECK_GT(cost_coefficient_above_threshold, 0);
1278 DCHECK_GT(cost_coefficient_below_threshold, cost_coefficient_above_threshold);
1282 const std::vector<int>& variable_transits =
1283 current_route_variable_transit_variables_;
1284 DCHECK_EQ(variable_transits.size(), transit_targets.size());
1285 for (
int pos = 0; pos < variable_transits.size(); ++pos) {
1286 int variable_transit = variable_transits[pos];
1287 if (variable_transit < 0) {
1288 DCHECK_EQ(transit_targets[pos],
1289 transit_evaluator(current_route_nodes_[pos],
1290 current_route_nodes_[pos + 1]));
1327 const int64_t variable_transit_ub =
1330 const int64_t transit_target = transit_targets[pos];
1331 const int64_t fixed_transit =
CapSub(transit_target, variable_transit_ub);
1332 DCHECK_EQ(fixed_transit, transit_evaluator(current_route_nodes_[pos],
1333 current_route_nodes_[pos + 1]));
1334 DCHECK_GT(transit_target, fixed_transit);
1335 DCHECK_GE(fixed_transit, 0);
1336 const int64_t threshold = std::max<int64_t>(
1337 CapSub(threshold_ratio * transit_target, fixed_transit), 0L);
1339 DCHECK_GT(variable_transit_ub, threshold);
1340 const int violation_above_threshold =
1342 const int violation_below_threshold = solver->
AddVariable(0, threshold);
1344 {{variable_transit, 1},
1345 {violation_above_threshold, 1},
1346 {violation_below_threshold, 1}});
1348 cost_coefficient_above_threshold);
1350 cost_coefficient_below_threshold);
1372 for (
int pos = 0; pos < variable_transits.size(); ++pos) {
1373 const int variable_transit = variable_transits[pos];
1374 if (variable_transit < 0) {
1377 const int64_t variable_transit_value =
1379 DCHECK_GE(variable_transit_value, 0);
1381 variable_transit_value);
1384 SetRouteCumulCosts(vehicle, cumul_offset, 0, solver,
1391 SetValuesFromLP(current_route_cumul_variables_, cumul_offset,
kint64min,
1392 solver, optimal_cumuls);
1393 SetValuesFromLP(current_route_break_variables_, cumul_offset,
kint64min,
1394 solver, optimal_breaks);
1395 SetValuesFromLP(current_route_variable_transit_variables_, 0, 0, solver,
1397 if (optimal_transits !=
nullptr) {
1398 DCHECK_EQ(optimal_transits->size(), current_route_nodes_.size() - 1);
1400 for (
int pos = 0; pos < optimal_transits->size(); ++pos) {
1401 const int64_t fixed_transit = transit_evaluator(
1402 current_route_nodes_[pos], current_route_nodes_[pos + 1]);
1403 DCHECK_GE(transit_targets[pos], fixed_transit);
1404 CapAddTo(fixed_transit, &(*optimal_transits)[pos]);
1411#define SET_DEBUG_VARIABLE_NAME(solver, var, name) \
1414 solver->SetVariableName(var, name); \
1418void DimensionCumulOptimizerCore::InitOptimizer(
1419 RoutingLinearSolverWrapper* solver) {
1421 index_to_cumul_variable_.assign(dimension_->
cumuls().size(), -1);
1422 max_end_cumul_ = solver->CreateNewPositiveVariable();
1424 min_start_cumul_ = solver->CreateNewPositiveVariable();
1428bool DimensionCumulOptimizerCore::ExtractRouteCumulBounds(
1429 absl::Span<const int64_t> route, int64_t cumul_offset) {
1430 const int route_size = route.size();
1431 current_route_min_cumuls_.resize(route_size);
1432 current_route_max_cumuls_.resize(route_size);
1435 for (
int pos = 0; pos < route_size; ++pos) {
1436 if (!GetCumulBoundsWithOffset(*dimension_, route[pos], cumul_offset,
1437 ¤t_route_min_cumuls_[pos],
1438 ¤t_route_max_cumuls_[pos])) {
1445bool DimensionCumulOptimizerCore::TightenRouteCumulBounds(
1446 absl::Span<const int64_t> route, absl::Span<const int64_t> min_transits,
1447 int64_t cumul_offset) {
1448 const int route_size = route.size();
1449 if (propagator_ !=
nullptr) {
1450 for (
int pos = 0; pos < route_size; pos++) {
1451 const int64_t node = route[pos];
1452 current_route_min_cumuls_[pos] = propagator_->CumulMin(node);
1453 DCHECK_GE(current_route_min_cumuls_[pos], 0);
1454 current_route_max_cumuls_[pos] = propagator_->CumulMax(node);
1455 DCHECK_GE(current_route_max_cumuls_[pos], current_route_min_cumuls_[pos]);
1462 for (
int pos = 1; pos < route_size; ++pos) {
1463 const int64_t slack_min = dimension_->SlackVar(route[pos - 1])->Min();
1464 current_route_min_cumuls_[pos] = std::max(
1465 current_route_min_cumuls_[pos],
1467 CapAdd(current_route_min_cumuls_[pos - 1], min_transits[pos - 1]),
1469 current_route_min_cumuls_[pos] = GetFirstPossibleValueForCumulWithOffset(
1470 *dimension_, route[pos], current_route_min_cumuls_[pos], cumul_offset);
1471 if (current_route_min_cumuls_[pos] > current_route_max_cumuls_[pos]) {
1476 for (
int pos = route_size - 2; pos >= 0; --pos) {
1479 if (current_route_max_cumuls_[pos + 1] <
kint64max) {
1480 const int64_t slack_min = dimension_->SlackVar(route[pos])->Min();
1481 current_route_max_cumuls_[pos] = std::min(
1482 current_route_max_cumuls_[pos],
1483 CapSub(
CapSub(current_route_max_cumuls_[pos + 1], min_transits[pos]),
1485 current_route_max_cumuls_[pos] = GetLastPossibleValueForCumulWithOffset(
1486 *dimension_, route[pos], current_route_max_cumuls_[pos],
1488 if (current_route_max_cumuls_[pos] < current_route_min_cumuls_[pos]) {
1499 const auto& x_anchors = pwl_function.
x_anchors();
1500 const auto& y_anchors = pwl_function.
y_anchors();
1501 if (index_end < 0) index_end = x_anchors.size() - 1;
1502 const int num_segments = index_end - index_start;
1503 DCHECK_GE(num_segments, 1);
1504 std::vector<SlopeAndYIntercept> slope_and_y_intercept(num_segments);
1505 for (
int seg = index_start; seg < index_end; ++seg) {
1506 auto& [slope, y_intercept] = slope_and_y_intercept[seg - index_start];
1507 slope = (y_anchors[seg + 1] - y_anchors[seg]) /
1508 static_cast<double>(x_anchors[seg + 1] - x_anchors[seg]);
1509 y_intercept = y_anchors[seg] - slope * x_anchors[seg];
1511 return slope_and_y_intercept;
1515 absl::Span<const SlopeAndYIntercept> slope_and_y_intercept) {
1516 CHECK(!slope_and_y_intercept.empty());
1517 std::vector<bool> convex(slope_and_y_intercept.size(),
false);
1518 double previous_slope = std::numeric_limits<double>::max();
1519 for (
int i = 0; i < slope_and_y_intercept.size(); ++i) {
1520 const auto& pair = slope_and_y_intercept[i];
1521 if (pair.slope < previous_slope) {
1524 previous_slope = pair.slope;
1532double FindBestScaling(absl::Span<const double> coefficients,
1533 absl::Span<const double> lower_bounds,
1534 absl::Span<const double> upper_bounds,
1535 int64_t max_absolute_activity,
1536 double wanted_absolute_activity_precision) {
1537 double unused_relative_coeff_error = 0;
1538 double unused_scaled_sum_error = 0;
1540 coefficients, lower_bounds, upper_bounds, max_absolute_activity,
1541 wanted_absolute_activity_precision, &unused_relative_coeff_error,
1542 &unused_scaled_sum_error);
1546bool DimensionCumulOptimizerCore::SetRouteTravelConstraints(
1547 const RouteDimensionTravelInfo* dimension_travel_info,
1548 absl::Span<const int> lp_slacks, absl::Span<const int64_t> fixed_transits,
1549 absl::Span<const int64_t> transit_targets,
1551 const std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1552 const int path_size = lp_cumuls.size();
1553 std::vector<int>& variable_transits =
1554 current_route_variable_transit_variables_;
1556 if (dimension_travel_info ==
nullptr ||
1557 dimension_travel_info->transition_info.empty()) {
1558 variable_transits.assign(path_size - 1, -1);
1564 for (
int pos = 0; pos < path_size - 1; ++pos) {
1565 const int64_t fixed_transit = fixed_transits[pos];
1566 const int ct = solver->CreateNewConstraint(fixed_transit, fixed_transit);
1567 solver->SetCoefficient(ct, lp_cumuls[pos + 1], 1);
1568 solver->SetCoefficient(ct, lp_cumuls[pos], -1);
1569 solver->SetCoefficient(ct, lp_slacks[pos], -1);
1570 if (!transit_targets.empty()) {
1571 const int64_t max_variable_transit =
1572 CapSub(transit_targets[pos], fixed_transit);
1573 if (max_variable_transit > 0) {
1574 variable_transits[pos] = solver->AddVariable(0, max_variable_transit);
1575 solver->SetCoefficient(ct, variable_transits[pos], -1);
1584 for (
int pos = 0; pos < path_size - 1; ++pos) {
1588 const int compression_cost = solver->CreateNewPositiveVariable();
1590 absl::StrFormat(
"compression_cost(%ld)", pos));
1597 const int relative_compression_cost = solver->CreateNewPositiveVariable();
1599 solver, relative_compression_cost,
1600 absl::StrFormat(
"relative_compression_cost(%ld)", pos));
1602 const RoutingModel::RouteDimensionTravelInfo::TransitionInfo&
1603 transition_info = dimension_travel_info->transition_info[pos];
1604 const FloatSlopePiecewiseLinearFunction& travel_function =
1605 transition_info.travel_start_dependent_travel;
1606 const auto& travel_x_anchors = travel_function.x_anchors();
1610 const int64_t pre_travel_transit = transition_info.pre_travel_transit_value;
1611 const int64_t post_travel_transit =
1612 transition_info.post_travel_transit_value;
1613 const int64_t compressed_travel_value_lower_bound =
1614 transition_info.compressed_travel_value_lower_bound;
1615 const int64_t travel_value_upper_bound =
1616 transition_info.travel_value_upper_bound;
1622 const int travel_value = solver->AddVariable(
1623 compressed_travel_value_lower_bound, travel_value_upper_bound);
1625 absl::StrFormat(
"travel_value(%ld)", pos));
1626 const int travel_start = solver->AddVariable(
1627 current_route_min_cumuls_[pos] + pre_travel_transit,
1628 current_route_max_cumuls_[pos + 1] - post_travel_transit -
1629 compressed_travel_value_lower_bound);
1631 absl::StrFormat(
"travel_start(%ld)", pos));
1634 solver->AddLinearConstraint(pre_travel_transit, pre_travel_transit,
1635 {{travel_start, 1}, {lp_cumuls[pos], -1}});
1640 const int num_pwl_anchors = travel_x_anchors.size();
1641 int index_anchor_start = 0;
1642 while (index_anchor_start < num_pwl_anchors - 1 &&
1643 travel_x_anchors[index_anchor_start + 1] <=
1644 current_route_min_cumuls_[pos] + pre_travel_transit) {
1645 ++index_anchor_start;
1647 int index_anchor_end = num_pwl_anchors - 1;
1648 while (index_anchor_end > 0 &&
1649 travel_x_anchors[index_anchor_end - 1] >=
1650 current_route_max_cumuls_[pos] + pre_travel_transit) {
1654 if (index_anchor_start >= index_anchor_end)
return false;
1659 const std::vector<SlopeAndYIntercept> slope_and_y_intercept =
1661 travel_function, index_anchor_start, index_anchor_end);
1664 const std::vector<bool> convexities =
1667 int nb_bin_variables = 0;
1668 for (
const bool convexity : convexities) {
1671 if (nb_bin_variables >= 2)
break;
1674 const bool need_bins = (nb_bin_variables > 1);
1676 const int travel_start_in_one_segment_ct =
1677 need_bins ? solver->CreateNewConstraint(1, 1)
1680 int belongs_to_this_segment_var;
1681 for (
int seg = 0; seg < convexities.size(); ++seg) {
1682 if (need_bins && convexities[seg]) {
1683 belongs_to_this_segment_var = solver->AddVariable(0, 1);
1685 solver, belongs_to_this_segment_var,
1686 absl::StrFormat(
"travel_start(%ld)belongs_to_seg(%ld)", pos, seg));
1687 solver->SetCoefficient(travel_start_in_one_segment_ct,
1688 belongs_to_this_segment_var, 1);
1693 const int64_t lower_bound_interval =
1694 seg > 0 ? travel_x_anchors[index_anchor_start + seg]
1695 : current_route_min_cumuls_[pos] + pre_travel_transit;
1696 int64_t end_of_seg = seg + 1;
1697 const int num_convexities = convexities.size();
1698 while (end_of_seg < num_convexities && !convexities[end_of_seg]) {
1701 const int64_t higher_bound_interval =
1702 end_of_seg < num_pwl_anchors - 1
1703 ? travel_x_anchors[index_anchor_start + end_of_seg]
1704 : current_route_max_cumuls_[pos] + pre_travel_transit;
1705 const int travel_start_in_segment_ct = solver->AddLinearConstraint(
1706 lower_bound_interval, higher_bound_interval, {{travel_start, 1}});
1707 solver->SetEnforcementLiteral(travel_start_in_segment_ct,
1708 belongs_to_this_segment_var);
1713 const auto [slope, y_intercept] = slope_and_y_intercept[seg];
1715 DCHECK_GE(slope, -1.0) <<
"Travel value PWL should have a slope >= -1";
1725 const double upper_bound = current_route_max_cumuls_[pos];
1726 const double factor = FindBestScaling(
1727 {1.0, -slope, y_intercept - 0.5},
1728 {
static_cast<double>(compressed_travel_value_lower_bound), 0, 1},
1730 {
static_cast<double>(travel_value_upper_bound), upper_bound, 1},
1736 if (factor <= 0)
return false;
1738 const int linearization_ct = solver->AddLinearConstraint(
1743 solver->SetEnforcementLiteral(linearization_ct,
1744 belongs_to_this_segment_var);
1770 const int compressed_travel_value = solver->AddVariable(
1771 compressed_travel_value_lower_bound, travel_value_upper_bound);
1773 solver, compressed_travel_value,
1774 absl::StrFormat(
"compressed_travel_value(%ld)", pos));
1775 solver->AddLinearConstraint(post_travel_transit + pre_travel_transit,
1776 post_travel_transit + pre_travel_transit,
1777 {{compressed_travel_value, -1},
1778 {lp_cumuls[pos + 1], 1},
1779 {lp_cumuls[pos], -1},
1780 {lp_slacks[pos], -1}});
1787 const int travel_compression_absolute = solver->AddVariable(
1788 0, travel_value_upper_bound - compressed_travel_value_lower_bound);
1790 solver, travel_compression_absolute,
1791 absl::StrFormat(
"travel_compression_absolute(%ld)", pos));
1793 solver->AddLinearConstraint(0, 0,
1794 {{travel_compression_absolute, 1},
1796 {compressed_travel_value, 1}});
1802 solver->SetObjectiveCoefficient(
1803 travel_value, dimension_travel_info->travel_cost_coefficient);
1807 const FloatSlopePiecewiseLinearFunction& cost_function =
1808 transition_info.travel_compression_cost;
1809 const auto& cost_x_anchors = cost_function.x_anchors();
1811 const std::vector<SlopeAndYIntercept> cost_slope_and_y_intercept =
1813 const double cost_max = cost_function.ComputeConvexValue(
1814 travel_value_upper_bound - compressed_travel_value_lower_bound);
1815 double previous_slope = 0;
1816 for (
int seg = 0; seg < cost_x_anchors.size() - 1; ++seg) {
1817 const auto [slope, y_intercept] = cost_slope_and_y_intercept[seg];
1819 DCHECK_GE(slope, previous_slope)
1820 <<
"Compression error is not convex. Segment " << (1 + seg)
1821 <<
" out of " << (cost_x_anchors.size() - 1);
1822 previous_slope = slope;
1823 const double factor = FindBestScaling(
1824 {1.0, -slope, y_intercept},
1825 {0,
static_cast<double>(compressed_travel_value_lower_bound), 1},
1827 {cost_max,
static_cast<double>(travel_value_upper_bound), 1},
1828 (
static_cast<int64_t
>(1) << 62),
1833 if (factor <= 0)
return false;
1835 solver->AddLinearConstraint(
1837 {{compression_cost, std::round(factor)},
1838 {travel_compression_absolute,
1857 solver->AddLinearConstraint(
1858 0,
kint64max, {{relative_compression_cost, 1}, {compression_cost, -1}});
1860 solver->SetObjectiveCoefficient(relative_compression_cost, 1.0);
1866bool RouteIsValid(
const RoutingModel& model,
int vehicle,
1867 const std::function<int64_t(int64_t)>& next_accessor) {
1868 absl::flat_hash_set<int64_t> visited;
1869 int node = model.Start(vehicle);
1870 visited.insert(node);
1871 while (!model.IsEnd(node)) {
1872 node = next_accessor(node);
1873 if (visited.contains(node))
return false;
1874 visited.insert(node);
1876 return visited.size() >= 2;
1880bool DimensionCumulOptimizerCore::SetRouteCumulConstraints(
1881 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
1882 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
1883 absl::Span<const int64_t> transit_targets,
1884 const RouteDimensionTravelInfo* dimension_travel_info, int64_t cumul_offset,
1886 int64_t* route_transit_cost, int64_t* route_cost_offset) {
1887 RoutingModel*
const model = dimension_->model();
1889 std::vector<int64_t>& path = current_route_nodes_;
1892 DCHECK(RouteIsValid(*model, vehicle, next_accessor));
1893 int node = model->Start(vehicle);
1894 path.push_back(node);
1895 while (!model->IsEnd(node)) {
1896 node = next_accessor(node);
1897 path.push_back(node);
1899 DCHECK_GE(path.size(), 2);
1901 const int path_size = path.size();
1903 std::vector<int64_t> fixed_transit(path_size - 1);
1904 int64_t total_fixed_transit = 0;
1906 for (
int pos = 1; pos < path_size; ++pos) {
1907 int64_t& transit = fixed_transit[pos - 1];
1908 transit = transit_accessor(path[pos - 1], path[pos]);
1909 if (!transit_targets.empty()) {
1914 DCHECK_GE(transit_targets[pos - 1], transit);
1916 total_fixed_transit =
CapAdd(total_fixed_transit, transit);
1919 if (!ExtractRouteCumulBounds(path, cumul_offset)) {
1922 if (dimension_travel_info ==
nullptr ||
1923 dimension_travel_info->transition_info.empty()) {
1924 if (!TightenRouteCumulBounds(path, fixed_transit, cumul_offset)) {
1929 std::vector<int64_t> min_transit(path_size - 1);
1930 for (
int pos = 0; pos < path_size - 1; ++pos) {
1932 dimension_travel_info->transition_info[pos];
1934 transition.compressed_travel_value_lower_bound +
1935 transition.post_travel_transit_value;
1937 if (!TightenRouteCumulBounds(path, min_transit, cumul_offset)) {
1944 std::vector<int>& lp_cumuls = current_route_cumul_variables_;
1945 lp_cumuls.assign(path_size, -1);
1946 for (
int pos = 0; pos < path_size; ++pos) {
1947 const int lp_cumul = solver->CreateNewPositiveVariable();
1949 absl::StrFormat(
"lp_cumul(%ld)", pos));
1950 index_to_cumul_variable_[path[pos]] = lp_cumul;
1951 lp_cumuls[pos] = lp_cumul;
1952 if (!solver->SetVariableBounds(lp_cumul, current_route_min_cumuls_[pos],
1953 current_route_max_cumuls_[pos])) {
1956 const SortedDisjointIntervalList& forbidden =
1957 dimension_->forbidden_intervals()[path[pos]];
1958 if (forbidden.NumIntervals() > 0) {
1959 std::vector<int64_t> starts;
1960 std::vector<int64_t> ends;
1961 for (
const ClosedInterval interval :
1962 dimension_->GetAllowedIntervalsInRange(
1963 path[pos],
CapAdd(current_route_min_cumuls_[pos], cumul_offset),
1964 CapAdd(current_route_max_cumuls_[pos], cumul_offset))) {
1965 starts.push_back(
CapSub(interval.start, cumul_offset));
1966 ends.push_back(
CapSub(interval.end, cumul_offset));
1968 solver->SetVariableDisjointBounds(lp_cumul, starts, ends);
1971 solver->AddRoute(path, lp_cumuls);
1973 std::vector<int> lp_slacks(path_size - 1, -1);
1974 for (
int pos = 0; pos < path_size - 1; ++pos) {
1975 const IntVar* cp_slack = dimension_->SlackVar(path[pos]);
1976 lp_slacks[pos] = solver->CreateNewPositiveVariable();
1978 absl::StrFormat(
"lp_slacks(%ld)", pos));
1979 if (!solver->SetVariableBounds(lp_slacks[pos], cp_slack->Min(),
1985 if (!SetRouteTravelConstraints(dimension_travel_info, lp_slacks,
1986 fixed_transit, transit_targets, solver)) {
1990 if (route_cost_offset !=
nullptr) *route_cost_offset = 0;
1992 std::vector<int> visited_pairs;
1993 StoreVisitedPickupDeliveryPairsOnRoute(
1994 *dimension_, vehicle, next_accessor, &visited_pairs,
1995 &visited_pickup_delivery_indices_for_pair_);
1996 for (
int pair_index : visited_pairs) {
1997 const int64_t pickup_index =
1998 visited_pickup_delivery_indices_for_pair_[pair_index].first;
1999 const int64_t delivery_index =
2000 visited_pickup_delivery_indices_for_pair_[pair_index].second;
2001 visited_pickup_delivery_indices_for_pair_[pair_index] = {-1, -1};
2003 DCHECK_GE(pickup_index, 0);
2004 if (delivery_index < 0) {
2009 const int64_t limit = dimension_->GetPickupToDeliveryLimitForPair(
2010 pair_index, model->GetPickupPosition(pickup_index)->alternative_index,
2011 model->GetDeliveryPosition(delivery_index)->alternative_index);
2014 const int ct = solver->CreateNewConstraint(
kint64min, limit);
2015 solver->SetCoefficient(ct, index_to_cumul_variable_[delivery_index], 1);
2016 solver->SetCoefficient(ct, index_to_cumul_variable_[pickup_index], -1);
2021 const int64_t span_bound = dimension_->GetSpanUpperBoundForVehicle(vehicle);
2024 const int ct = solver->CreateNewConstraint(
kint64min, span_bound);
2025 solver->SetCoefficient(ct, lp_cumuls.back(), 1);
2026 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
2029 if (optimize_costs) {
2030 SetRouteCumulCosts(vehicle, cumul_offset, total_fixed_transit, solver,
2031 route_transit_cost, route_cost_offset);
2034 current_route_break_variables_.clear();
2035 if (!dimension_->HasBreakConstraints())
return true;
2042 const std::vector<IntervalVar*>& breaks =
2043 dimension_->GetBreakIntervalsOfVehicle(vehicle);
2044 const int num_breaks = breaks.size();
2048 if (num_breaks == 0) {
2050 for (
const auto& distance_duration :
2051 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
2052 maximum_route_span =
2053 std::min(maximum_route_span, distance_duration.first);
2056 const int ct = solver->CreateNewConstraint(
kint64min, maximum_route_span);
2057 solver->SetCoefficient(ct, lp_cumuls.back(), 1);
2058 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
2065 std::vector<int64_t> pre_travel(path_size - 1, 0);
2066 std::vector<int64_t> post_travel(path_size - 1, 0);
2068 const int pre_travel_index =
2069 dimension_->GetPreTravelEvaluatorOfVehicle(vehicle);
2070 if (pre_travel_index != -1) {
2074 const int post_travel_index =
2075 dimension_->GetPostTravelEvaluatorOfVehicle(vehicle);
2076 if (post_travel_index != -1) {
2090 std::vector<LpBreak> lp_breaks;
2091 if (solver->IsCPSATSolver()) {
2092 lp_breaks.resize(num_breaks, {.start = -1, .end = -1, .duration = -1});
2095 std::vector<int> slack_exact_lower_bound_ct(path_size - 1, -1);
2096 std::vector<int> slack_linear_lower_bound_ct(path_size - 1, -1);
2098 const int64_t vehicle_start_min = current_route_min_cumuls_.front();
2099 const int64_t vehicle_start_max = current_route_max_cumuls_.front();
2100 const int64_t vehicle_end_min = current_route_min_cumuls_.back();
2101 const int64_t vehicle_end_max = current_route_max_cumuls_.back();
2102 const int all_break_variables_offset =
2103 vehicle_to_all_break_variables_offset_[vehicle];
2104 for (
int br = 0; br < num_breaks; ++br) {
2105 const IntervalVar& break_var = *breaks[br];
2106 if (!break_var.MustBePerformed())
continue;
2107 const int64_t break_start_min =
CapSub(break_var.StartMin(), cumul_offset);
2108 const int64_t break_start_max =
CapSub(break_var.StartMax(), cumul_offset);
2109 const int64_t break_end_min =
CapSub(break_var.EndMin(), cumul_offset);
2110 const int64_t break_end_max =
CapSub(break_var.EndMax(), cumul_offset);
2111 const int64_t break_duration_min = break_var.DurationMin();
2112 const int64_t break_duration_max = break_var.DurationMax();
2115 if (solver->IsCPSATSolver()) {
2116 if (break_end_max <= vehicle_start_min ||
2117 vehicle_end_max <= break_start_min) {
2118 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
2119 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
2120 current_route_break_variables_.push_back(-1);
2121 current_route_break_variables_.push_back(-1);
2125 .start = solver->AddVariable(break_start_min, break_start_max),
2126 .end = solver->AddVariable(break_end_min, break_end_max),
2128 solver->AddVariable(break_duration_min, break_duration_max)};
2129 const auto [break_start, break_end, break_duration] = lp_breaks[br];
2131 absl::StrFormat(
"lp_break_start(%ld)", br));
2133 absl::StrFormat(
"lp_break_end(%ld)", br));
2135 absl::StrFormat(
"lp_break_duration(%ld)", br));
2137 solver->AddLinearConstraint(
2138 0, 0, {{break_end, 1}, {break_start, -1}, {break_duration, -1}});
2140 all_break_variables_[all_break_variables_offset + 2 * br] = break_start;
2141 all_break_variables_[all_break_variables_offset + 2 * br + 1] = break_end;
2142 current_route_break_variables_.push_back(break_start);
2143 current_route_break_variables_.push_back(break_end);
2145 if (break_end_min <= vehicle_start_max ||
2146 vehicle_end_min <= break_start_max) {
2147 all_break_variables_[all_break_variables_offset + 2 * br] = -1;
2148 all_break_variables_[all_break_variables_offset + 2 * br + 1] = -1;
2149 current_route_break_variables_.push_back(-1);
2150 current_route_break_variables_.push_back(-1);
2158 const int break_in_one_slack_ct = solver->CreateNewConstraint(1, 1);
2160 if (solver->IsCPSATSolver()) {
2162 if (break_end_min <= vehicle_start_max) {
2163 const int ct = solver->AddLinearConstraint(
2164 0,
kint64max, {{lp_cumuls.front(), 1}, {lp_breaks[br].end, -1}});
2165 const int break_is_before_route = solver->AddVariable(0, 1);
2167 solver, break_is_before_route,
2168 absl::StrFormat(
"break_is_before_route(%ld)", br));
2169 solver->SetEnforcementLiteral(ct, break_is_before_route);
2170 solver->SetCoefficient(break_in_one_slack_ct, break_is_before_route, 1);
2173 if (vehicle_end_min <= break_start_max) {
2174 const int ct = solver->AddLinearConstraint(
2175 0,
kint64max, {{lp_breaks[br].start, 1}, {lp_cumuls.back(), -1}});
2176 const int break_is_after_route = solver->AddVariable(0, 1);
2178 solver, break_is_after_route,
2179 absl::StrFormat(
"break_is_after_route(%ld)", br));
2180 solver->SetEnforcementLiteral(ct, break_is_after_route);
2181 solver->SetCoefficient(break_in_one_slack_ct, break_is_after_route, 1);
2186 for (
int pos = 0; pos < path_size - 1; ++pos) {
2189 const int64_t slack_start_min =
2190 CapAdd(current_route_min_cumuls_[pos], pre_travel[pos]);
2191 if (slack_start_min > break_start_max)
break;
2192 const int64_t slack_end_max =
2193 CapSub(current_route_max_cumuls_[pos + 1], post_travel[pos]);
2194 if (break_end_min > slack_end_max)
continue;
2195 const int64_t slack_duration_max =
2196 std::min(
CapSub(
CapSub(current_route_max_cumuls_[pos + 1],
2197 current_route_min_cumuls_[pos]),
2198 fixed_transit[pos]),
2199 dimension_->SlackVar(path[pos])->Max());
2200 if (slack_duration_max < break_duration_min)
continue;
2207 const int break_in_slack = solver->AddVariable(0, 1);
2209 solver, break_in_slack,
2210 absl::StrFormat(
"break_in_slack(%ld, %ld)", br, pos));
2211 if (slack_linear_lower_bound_ct[pos] == -1) {
2212 slack_linear_lower_bound_ct[pos] =
2213 solver->AddLinearConstraint(
kint64min, 0, {{lp_slacks[pos], -1}});
2218 if (break_in_one_slack_ct < slack_linear_lower_bound_ct[pos]) {
2219 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2220 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2221 break_duration_min);
2223 solver->SetCoefficient(slack_linear_lower_bound_ct[pos], break_in_slack,
2224 break_duration_min);
2225 solver->SetCoefficient(break_in_one_slack_ct, break_in_slack, 1);
2228 if (solver->IsCPSATSolver()) {
2233 const int break_duration_in_slack =
2234 solver->AddVariable(0, slack_duration_max);
2236 solver, break_duration_in_slack,
2237 absl::StrFormat(
"break_duration_in_slack(%ld, %ld)", br, pos));
2238 solver->AddProductConstraint(break_duration_in_slack,
2239 {break_in_slack, lp_breaks[br].duration});
2240 if (slack_exact_lower_bound_ct[pos] == -1) {
2241 slack_exact_lower_bound_ct[pos] =
2242 solver->AddLinearConstraint(
kint64min, 0, {{lp_slacks[pos], -1}});
2244 solver->SetCoefficient(slack_exact_lower_bound_ct[pos],
2245 break_duration_in_slack, 1);
2248 const int break_start_after_current_ct = solver->AddLinearConstraint(
2250 {{lp_breaks[br].start, 1}, {lp_cumuls[pos], -1}});
2251 solver->SetEnforcementLiteral(break_start_after_current_ct,
2254 const int break_ends_before_next_ct = solver->AddLinearConstraint(
2256 {{lp_cumuls[pos + 1], 1}, {lp_breaks[br].end, -1}});
2257 solver->SetEnforcementLiteral(break_ends_before_next_ct,
2263 for (
const auto& distance_duration :
2264 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
2265 const int64_t limit = distance_duration.first;
2266 const int64_t min_break_duration = distance_duration.second;
2267 int64_t min_num_breaks = 0;
2270 std::max<int64_t>(0,
CapSub(total_fixed_transit, 1) / limit);
2272 if (
CapSub(current_route_min_cumuls_.back(),
2273 current_route_max_cumuls_.front()) > limit) {
2274 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
2276 if (num_breaks < min_num_breaks)
return false;
2277 if (min_num_breaks == 0)
continue;
2299 std::vector<int64_t> sum_transits(path_size);
2301 sum_transits[0] = 0;
2302 for (
int pos = 1; pos < path_size; ++pos) {
2303 sum_transits[pos] = sum_transits[pos - 1] + fixed_transit[pos - 1];
2310 std::vector<int> slack_sum_vars(path_size, -1);
2315 auto trigger = [&](
int k,
int pl,
int pr) ->
bool {
2316 const int64_t r_pre_travel = pr < path_size - 1 ? pre_travel[pr] : 0;
2317 const int64_t l_post_travel = pl >= 1 ? post_travel[pl - 1] : 0;
2318 const int64_t extra_travel =
CapAdd(r_pre_travel, l_post_travel);
2320 const int64_t span_lb =
CapAdd(
CapSub(current_route_min_cumuls_[pr],
2321 current_route_max_cumuls_[pl]),
2323 if (span_lb > limit)
return true;
2325 return CapAdd(
CapSub(sum_transits[pr], sum_transits[pl]), extra_travel) >
2328 int min_sum_var_index = path_size;
2329 int max_sum_var_index = -1;
2330 for (
int k = 1; k <= min_num_breaks; ++k) {
2332 for (
int pl = 0; pl < path_size - 1; ++pl) {
2333 pr = std::max(pr, pl + 1);
2335 while (pr < path_size && !trigger(k, pl, pr)) ++pr;
2336 if (pr == path_size)
break;
2338 while (pl < pr && trigger(k, pl + 1, pr)) ++pl;
2341 if (pl == pr)
return false;
2346 if (k < min_num_breaks && trigger(k + 1, pl, pr))
continue;
2350 if (solver->IsCPSATSolver()) {
2353 solver->AddLinearConstraint(
2355 CapProd(k, min_break_duration)),
2356 kint64max, {{lp_cumuls[pr], 1}, {lp_cumuls[pl], -1}});
2358 if (slack_sum_vars[pl] == -1) {
2359 slack_sum_vars[pl] = solver->CreateNewPositiveVariable();
2361 absl::StrFormat(
"slack_sum_vars(%ld)", pl));
2362 min_sum_var_index = std::min(min_sum_var_index, pl);
2364 if (slack_sum_vars[pr] == -1) {
2365 slack_sum_vars[pr] = solver->CreateNewPositiveVariable();
2367 absl::StrFormat(
"slack_sum_vars(%ld)", pr));
2368 max_sum_var_index = std::max(max_sum_var_index, pr);
2371 solver->AddLinearConstraint(
2373 {{slack_sum_vars[pr], 1}, {slack_sum_vars[pl], -1}});
2377 if (min_sum_var_index < max_sum_var_index) {
2378 slack_sum_vars[min_sum_var_index] = solver->AddVariable(0, 0);
2379 int prev_index = min_sum_var_index;
2380 for (
int pos = min_sum_var_index + 1; pos <= max_sum_var_index; ++pos) {
2381 if (slack_sum_vars[pos] == -1)
continue;
2384 const int ct = solver->AddLinearConstraint(
2385 0, 0, {{slack_sum_vars[pos], 1}, {slack_sum_vars[prev_index], -1}});
2386 for (
int p = prev_index; p < pos; ++p) {
2387 solver->SetCoefficient(ct, lp_slacks[p], -1);
2393 if (!solver->IsCPSATSolver())
return true;
2394 if (!dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
2397 for (
const IntervalVar* interval :
2398 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
2399 if (!interval->MustBePerformed())
return true;
2402 for (
int br = 1; br < num_breaks; ++br) {
2403 if (lp_breaks[br].start == -1 || lp_breaks[br - 1].start == -1)
continue;
2404 solver->AddLinearConstraint(
2406 {{lp_breaks[br - 1].end, -1}, {lp_breaks[br].start, 1}});
2409 for (
const auto& distance_duration :
2410 dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
2411 const int64_t limit = distance_duration.first;
2412 const int64_t min_break_duration = distance_duration.second;
2440 int previous_cover = solver->AddVariable(
CapAdd(vehicle_start_min, limit),
2441 CapAdd(vehicle_start_max, limit));
2443 solver->AddLinearConstraint(limit, limit,
2444 {{previous_cover, 1}, {lp_cumuls.front(), -1}});
2445 for (
int br = 0; br < num_breaks; ++br) {
2446 const LpBreak& lp_break = lp_breaks[br];
2447 if (lp_break.start == -1)
continue;
2448 const int64_t break_end_min =
CapSub(breaks[br]->EndMin(), cumul_offset);
2449 const int64_t break_end_max =
CapSub(breaks[br]->EndMax(), cumul_offset);
2452 const int break_is_eligible = solver->AddVariable(0, 1);
2454 absl::StrFormat(
"break_is_eligible(%ld)", br));
2455 const int break_is_not_eligible = solver->AddVariable(0, 1);
2457 solver, break_is_not_eligible,
2458 absl::StrFormat(
"break_is_not_eligible(%ld)", br));
2460 solver->AddLinearConstraint(
2461 1, 1, {{break_is_eligible, 1}, {break_is_not_eligible, 1}});
2462 const int positive_ct = solver->AddLinearConstraint(
2464 {{lp_break.end, 1}, {lp_break.start, -1}});
2465 solver->SetEnforcementLiteral(positive_ct, break_is_eligible);
2466 const int negative_ct = solver->AddLinearConstraint(
2468 {{lp_break.end, 1}, {lp_break.start, -1}});
2469 solver->SetEnforcementLiteral(negative_ct, break_is_not_eligible);
2476 const int break_cover = solver->AddVariable(
2477 CapAdd(std::min(vehicle_start_min, break_end_min), limit),
2478 CapAdd(std::max(vehicle_start_min, break_end_max), limit));
2480 absl::StrFormat(
"break_cover(%ld)", br));
2481 const int limit_cover_ct = solver->AddLinearConstraint(
2482 limit, limit, {{break_cover, 1}, {lp_break.end, -1}});
2483 solver->SetEnforcementLiteral(limit_cover_ct, break_is_eligible);
2484 const int empty_cover_ct = solver->AddLinearConstraint(
2485 CapAdd(vehicle_start_min, limit),
CapAdd(vehicle_start_min, limit),
2486 {{break_cover, 1}});
2487 solver->SetEnforcementLiteral(empty_cover_ct, break_is_not_eligible);
2492 solver->AddMaximumConstraint(cover, {previous_cover, break_cover});
2495 const int route_end_is_not_covered = solver->AddReifiedLinearConstraint(
2496 1,
kint64max, {{lp_cumuls.back(), 1}, {previous_cover, -1}});
2497 const int break_start_cover_ct = solver->AddLinearConstraint(
2498 0,
kint64max, {{previous_cover, 1}, {lp_break.start, -1}});
2499 solver->SetEnforcementLiteral(break_start_cover_ct,
2500 route_end_is_not_covered);
2502 previous_cover = cover;
2504 solver->AddLinearConstraint(0,
kint64max,
2505 {{previous_cover, 1}, {lp_cumuls.back(), -1}});
2511void DimensionCumulOptimizerCore::SetRouteCumulCosts(
2512 int vehicle, int64_t cumul_offset, int64_t total_fixed_transit,
2514 int64_t* route_cost_offset) {
2515 const std::vector<int>& lp_cumuls = current_route_cumul_variables_;
2516 const std::vector<int64_t>& path = current_route_nodes_;
2517 const int path_size = path.size();
2519 for (
int pos = 0; pos < path_size; ++pos) {
2520 const int64_t node = path[pos];
2521 if (!dimension_->HasCumulVarSoftUpperBound(node))
continue;
2522 const int64_t coef = dimension_->GetCumulVarSoftUpperBoundCoefficient(node);
2523 if (coef == 0)
continue;
2524 int64_t
bound = dimension_->GetCumulVarSoftUpperBound(node);
2525 if (bound < cumul_offset && route_cost_offset !=
nullptr) {
2527 *route_cost_offset =
CapAdd(*route_cost_offset,
2530 bound = std::max<int64_t>(0,
CapSub(bound, cumul_offset));
2531 if (current_route_max_cumuls_[pos] <= bound) {
2535 const int soft_ub_diff = solver->CreateNewPositiveVariable();
2537 absl::StrFormat(
"soft_ub_diff(%ld)", pos));
2538 solver->SetObjectiveCoefficient(soft_ub_diff, coef);
2540 const int ct = solver->CreateNewConstraint(
kint64min, bound);
2541 solver->SetCoefficient(ct, lp_cumuls[pos], 1);
2542 solver->SetCoefficient(ct, soft_ub_diff, -1);
2545 for (
int pos = 0; pos < path_size; ++pos) {
2546 const int64_t node = path[pos];
2547 if (!dimension_->HasCumulVarSoftLowerBound(node))
continue;
2548 const int64_t coef = dimension_->GetCumulVarSoftLowerBoundCoefficient(node);
2549 if (coef == 0)
continue;
2550 const int64_t
bound = std::max<int64_t>(
2551 0,
CapSub(dimension_->GetCumulVarSoftLowerBound(node), cumul_offset));
2552 if (current_route_min_cumuls_[pos] >= bound) {
2556 const int soft_lb_diff = solver->CreateNewPositiveVariable();
2558 absl::StrFormat(
"soft_lb_diff(%ld)", pos));
2559 solver->SetObjectiveCoefficient(soft_lb_diff, coef);
2561 const int ct = solver->CreateNewConstraint(bound,
kint64max);
2562 solver->SetCoefficient(ct, lp_cumuls[pos], 1);
2563 solver->SetCoefficient(ct, soft_lb_diff, 1);
2569 const int64_t span_cost_coef =
2570 dimension_->GetSpanCostCoefficientForVehicle(vehicle);
2571 const int64_t slack_cost_coef =
CapAdd(
2572 span_cost_coef, dimension_->GetSlackCostCoefficientForVehicle(vehicle));
2573 if (slack_cost_coef > 0) {
2576 const int span_without_fixed_transit_var =
2577 solver->CreateNewPositiveVariable();
2579 "span_without_fixed_transit_var");
2580 solver->AddLinearConstraint(total_fixed_transit, total_fixed_transit,
2581 {{lp_cumuls.back(), 1},
2582 {lp_cumuls.front(), -1},
2583 {span_without_fixed_transit_var, -1}});
2584 solver->SetObjectiveCoefficient(span_without_fixed_transit_var,
2588 if (dimension_->HasSoftSpanUpperBounds()) {
2589 const BoundCost bound_cost =
2590 dimension_->GetSoftSpanUpperBoundForVehicle(vehicle);
2591 if (bound_cost.bound <
kint64max && bound_cost.cost > 0) {
2592 const int span_violation = solver->CreateNewPositiveVariable();
2595 const int violation =
2596 solver->CreateNewConstraint(
kint64min, bound_cost.bound);
2597 solver->SetCoefficient(violation, lp_cumuls.back(), 1.0);
2598 solver->SetCoefficient(violation, lp_cumuls.front(), -1.0);
2599 solver->SetCoefficient(violation, span_violation, -1.0);
2601 solver->SetObjectiveCoefficient(span_violation, bound_cost.cost);
2604 if (solver->IsCPSATSolver() &&
2605 dimension_->HasQuadraticCostSoftSpanUpperBounds()) {
2607 const BoundCost bound_cost =
2608 dimension_->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2609 if (bound_cost.bound <
kint64max && bound_cost.cost > 0) {
2610 const int span_violation = solver->CreateNewPositiveVariable();
2612 solver, span_violation,
2613 absl::StrFormat(
"quadratic_span_violation(%ld)", vehicle));
2615 const int violation =
2616 solver->CreateNewConstraint(
kint64min, bound_cost.bound);
2617 solver->SetCoefficient(violation, lp_cumuls.back(), 1.0);
2618 solver->SetCoefficient(violation, lp_cumuls.front(), -1.0);
2619 solver->SetCoefficient(violation, span_violation, -1.0);
2621 const int squared_span_violation = solver->CreateNewPositiveVariable();
2623 solver, squared_span_violation,
2624 absl::StrFormat(
"squared_span_violation(%ld)", vehicle));
2625 solver->AddProductConstraint(squared_span_violation,
2626 {span_violation, span_violation});
2628 solver->SetObjectiveCoefficient(squared_span_violation, bound_cost.cost);
2632 if (dimension_->global_span_cost_coefficient() > 0) {
2634 int ct = solver->CreateNewConstraint(
kint64min, 0);
2635 solver->SetCoefficient(ct, min_start_cumul_, 1);
2636 solver->SetCoefficient(ct, lp_cumuls.front(), -1);
2638 ct = solver->CreateNewConstraint(0,
kint64max);
2639 solver->SetCoefficient(ct, max_end_cumul_, 1);
2640 solver->SetCoefficient(ct, lp_cumuls.back(), -1);
2643 if (route_transit_cost !=
nullptr) {
2644 if (span_cost_coef > 0) {
2645 *route_transit_cost =
CapProd(total_fixed_transit, span_cost_coef);
2647 *route_transit_cost = 0;
2653bool AllValuesContainedExcept(
const IntVar& var, absl::Span<const int> values,
2654 const absl::flat_hash_set<int>& ignored_values) {
2655 for (
int val : values) {
2656 if (!ignored_values.contains(val) && !var.Contains(val))
return false;
2662bool DimensionCumulOptimizerCore::SetGlobalConstraints(
2663 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
2664 bool optimize_costs,
bool optimize_resource_assignment,
2668 const int64_t global_span_coeff = dimension_->global_span_cost_coefficient();
2669 if (optimize_costs && global_span_coeff > 0) {
2671 const int global_span_var = solver->CreateNewPositiveVariable();
2673 solver->AddLinearConstraint(
2675 {{global_span_var, 1}, {max_end_cumul_, -1}, {min_start_cumul_, 1}});
2680 solver->SetObjectiveCoefficient(global_span_var, global_span_coeff);
2684 for (
const auto [first_node, second_node, offset, performed_constraint] :
2685 dimension_->GetNodePrecedences()) {
2686 if (next_accessor(first_node) == -1 || next_accessor(second_node) == -1) {
2689 const int first_cumul_var = index_to_cumul_variable_[first_node];
2690 const int second_cumul_var = index_to_cumul_variable_[second_node];
2692 first_cumul_var < 0, second_cumul_var < 0, performed_constraint)) {
2700 DCHECK_NE(first_cumul_var, second_cumul_var)
2701 <<
"Dimension " << dimension_->name()
2702 <<
" has a self-precedence on node " << first_node <<
".";
2705 const int ct = solver->CreateNewConstraint(offset,
kint64max);
2706 solver->SetCoefficient(ct, second_cumul_var, 1);
2707 solver->SetCoefficient(ct, first_cumul_var, -1);
2710 if (optimize_resource_assignment &&
2711 !SetGlobalConstraintsForResourceAssignment(next_accessor, cumul_offset,
2718bool DimensionCumulOptimizerCore::SetGlobalConstraintsForResourceAssignment(
2719 const std::function<int64_t(int64_t)>& next_accessor, int64_t cumul_offset,
2721 if (!solver->IsCPSATSolver()) {
2728 const RoutingModel& model = *dimension_->model();
2729 const int num_vehicles = model.vehicles();
2730 const auto& resource_groups = model.GetResourceGroups();
2731 for (
int rg_index : model.GetDimensionResourceGroupIndices(dimension_)) {
2743 const ResourceGroup& resource_group = *resource_groups[rg_index];
2744 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2747 int num_required_resources = 0;
2750 std::vector<int> vehicle_constraints(model.vehicles(), kNoConstraint);
2751 const int num_resource_classes = resource_group.GetResourceClassesCount();
2752 std::vector<absl::flat_hash_set<int>>& ignored_resources_per_class =
2753 resource_class_ignored_resources_per_group_[rg_index];
2754 ignored_resources_per_class.assign(num_resource_classes, {});
2755 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2756 const IntVar& resource_var = *model.ResourceVar(v, rg_index);
2757 if (model.IsEnd(next_accessor(model.Start(v))) &&
2758 !model.IsVehicleUsedWhenEmpty(v)) {
2759 if (resource_var.Bound() && resource_var.Value() >= 0) {
2767 if (resource_var.Bound()) {
2768 const int resource_index = resource_var.Value();
2769 if (resource_index < 0) {
2773 ignored_resources_per_class
2774 [resource_group.GetResourceClassIndex(resource_index).value()]
2775 .insert(resource_index);
2777 const int start_index = index_to_cumul_variable_[model.Start(v)];
2778 const int end_index = index_to_cumul_variable_[model.End(v)];
2779 if (!TightenStartEndVariableBoundsWithResource(
2780 *dimension_, resource_group.GetResource(resource_index),
2781 GetVariableBounds(start_index, *solver), start_index,
2782 GetVariableBounds(end_index, *solver), end_index, cumul_offset,
2788 num_required_resources++;
2789 vehicle_constraints[v] = solver->CreateNewConstraint(1, 1);
2794 std::vector<int> resource_class_cstrs(num_resource_classes, kNoConstraint);
2795 int num_available_resources = 0;
2796 for (RCIndex rc_index(0); rc_index < num_resource_classes; rc_index++) {
2797 const ResourceGroup::Attributes& attributes =
2798 resource_group.GetDimensionAttributesForClass(dimension_->index(),
2800 if (attributes.start_domain().Max() < cumul_offset ||
2801 attributes.end_domain().Max() < cumul_offset) {
2807 const int rc = rc_index.value();
2808 const int num_available_class_resources =
2809 resource_group.GetResourceIndicesInClass(rc_index).size() -
2810 ignored_resources_per_class[rc].size();
2811 DCHECK_GE(num_available_class_resources, 0);
2812 if (num_available_class_resources > 0) {
2813 num_available_resources += num_available_class_resources;
2814 resource_class_cstrs[rc] =
2815 solver->CreateNewConstraint(0, num_available_class_resources);
2819 if (num_required_resources > num_available_resources) {
2825 std::vector<int>& resource_class_to_vehicle_assignment_vars =
2826 resource_class_to_vehicle_assignment_variables_per_group_[rg_index];
2827 resource_class_to_vehicle_assignment_vars.assign(
2828 num_resource_classes * num_vehicles, -1);
2834 DCHECK_EQ(resource_group.Index(), rg_index);
2835 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2836 if (vehicle_constraints[v] == kNoConstraint)
continue;
2837 IntVar*
const resource_var = model.ResourceVar(v, rg_index);
2838 std::unique_ptr<IntVarIterator> it(
2839 resource_var->MakeDomainIterator(
false));
2840 std::vector<bool> resource_class_considered(num_resource_classes,
false);
2841 for (
int r : InitAndGetValues(it.get())) {
2842 if (r < 0)
continue;
2843 const RCIndex rc_index = resource_group.GetResourceClassIndex(r);
2844 const int rc = rc_index.value();
2845 if (resource_class_considered[rc]) {
2848 resource_class_considered[rc] =
true;
2849 if (resource_class_cstrs[rc] == kNoConstraint)
continue;
2855 DCHECK(AllValuesContainedExcept(
2856 *resource_var, resource_group.GetResourceIndicesInClass(rc_index),
2857 ignored_resources_per_class[rc]))
2859 resource_group.GetResourceIndicesInClass(rc_index),
2860 resource_var->Min(), resource_var->Max());
2862 const int assign_rc_to_v = solver->AddVariable(0, 1);
2864 solver, assign_rc_to_v,
2865 absl::StrFormat(
"assign_rc_to_v(%ld, %ld)", rc, v));
2866 resource_class_to_vehicle_assignment_vars[rc * num_vehicles + v] =
2871 DCHECK_LT(vehicle_constraints[v], resource_class_cstrs[rc]);
2872 solver->SetCoefficient(vehicle_constraints[v], assign_rc_to_v, 1);
2873 solver->SetCoefficient(resource_class_cstrs[rc], assign_rc_to_v, 1);
2875 const auto& add_domain_constraint =
2876 [&solver, cumul_offset, assign_rc_to_v](
const Domain& domain,
2877 int cumul_variable) {
2881 ClosedInterval cumul_bounds;
2882 if (!GetDomainOffsetBounds(domain, cumul_offset, &cumul_bounds)) {
2884 solver->SetVariableBounds(assign_rc_to_v, 0, 0);
2887 const int cumul_constraint = solver->AddLinearConstraint(
2888 cumul_bounds.start, cumul_bounds.end, {{cumul_variable, 1}});
2889 solver->SetEnforcementLiteral(cumul_constraint, assign_rc_to_v);
2891 const ResourceGroup::Attributes& attributes =
2892 resource_group.GetDimensionAttributesForClass(dimension_->index(),
2894 add_domain_constraint(attributes.start_domain(),
2895 index_to_cumul_variable_[model.Start(v)]);
2896 add_domain_constraint(attributes.end_domain(),
2897 index_to_cumul_variable_[model.End(v)]);
2904#undef SET_DEBUG_VARIABLE_NAME
2906void DimensionCumulOptimizerCore::SetValuesFromLP(
2907 absl::Span<const int> lp_variables, int64_t offset, int64_t default_value,
2908 RoutingLinearSolverWrapper* solver, std::vector<int64_t>* lp_values)
const {
2909 if (lp_values ==
nullptr)
return;
2910 lp_values->assign(lp_variables.size(), default_value);
2911 for (
int i = 0;
i < lp_variables.size();
i++) {
2912 const int lp_var = lp_variables[
i];
2913 if (lp_var < 0)
continue;
2914 (*lp_values)[
i] =
CapAdd(solver->GetVariableValue(lp_var), offset);
2918void DimensionCumulOptimizerCore::SetResourceIndices(
2919 RoutingLinearSolverWrapper* solver,
2920 std::vector<std::vector<int>>* resource_indices_per_group)
const {
2921 if (resource_indices_per_group ==
nullptr ||
2922 resource_class_to_vehicle_assignment_variables_per_group_.empty()) {
2925 using RCIndex = RoutingModel::ResourceClassIndex;
2926 const RoutingModel& model = *dimension_->model();
2927 const int num_vehicles = model.vehicles();
2928 DCHECK(!model.GetDimensionResourceGroupIndices(dimension_).empty());
2929 const auto& resource_groups = model.GetResourceGroups();
2930 resource_indices_per_group->resize(resource_groups.size());
2931 for (
int rg_index : model.GetDimensionResourceGroupIndices(dimension_)) {
2932 const ResourceGroup& resource_group = *resource_groups[rg_index];
2933 DCHECK(!resource_group.GetVehiclesRequiringAResource().empty());
2936 resource_indices_per_class =
2937 resource_group.GetResourceIndicesPerClass();
2938 const int num_resource_classes = resource_group.GetResourceClassesCount();
2939 std::vector<int> current_resource_pos_for_class(num_resource_classes, 0);
2940 std::vector<int>& resource_indices =
2941 resource_indices_per_group->at(rg_index);
2942 resource_indices.assign(num_vehicles, -1);
2944 const std::vector<int>& resource_class_to_vehicle_assignment_vars =
2945 resource_class_to_vehicle_assignment_variables_per_group_[rg_index];
2946 DCHECK_EQ(resource_class_to_vehicle_assignment_vars.size(),
2947 num_resource_classes * num_vehicles);
2948 const std::vector<absl::flat_hash_set<int>>& ignored_resources_per_class =
2949 resource_class_ignored_resources_per_group_[rg_index];
2950 DCHECK_EQ(ignored_resources_per_class.size(), num_resource_classes);
2951 for (
int v : resource_group.GetVehiclesRequiringAResource()) {
2952 const IntVar& resource_var = *model.ResourceVar(v, rg_index);
2953 if (resource_var.Bound()) {
2954 resource_indices[v] = resource_var.Value();
2957 for (
int rc = 0; rc < num_resource_classes; rc++) {
2958 const int assignment_var =
2959 resource_class_to_vehicle_assignment_vars[rc * num_vehicles + v];
2960 if (assignment_var >= 0 &&
2961 solver->GetVariableValue(assignment_var) == 1) {
2963 const std::vector<int>& class_resource_indices =
2964 resource_indices_per_class[RCIndex(rc)];
2965 int& pos = current_resource_pos_for_class[rc];
2966 while (ignored_resources_per_class[rc].contains(
2967 class_resource_indices[pos])) {
2969 DCHECK_LT(pos, class_resource_indices.size());
2971 resource_indices[v] = class_resource_indices[pos++];
2987 !
dimension->GetNodePrecedences().empty()) {
2988 switch (solver_type) {
2990 solver_ = std::make_unique<RoutingGlopWrapper>(
2992 ->GetDimensionResourceGroupIndices(
dimension)
2994 GetGlopParametersForGlobalLP(), search_stats);
2998 solver_ = std::make_unique<RoutingCPSatWrapper>(search_stats);
3002 LOG(DFATAL) <<
"Unrecognized solver type: " << solver_type;
3008 const std::function<int64_t(int64_t)>& next_accessor,
3009 int64_t* optimal_cost_without_transits) {
3010 return optimizer_core_.Optimize(next_accessor, {}, solver_.get(),
nullptr,
3012 optimal_cost_without_transits,
nullptr);
3016 const std::function<int64_t(int64_t)>& next_accessor,
3017 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
3018 dimension_travel_info_per_route,
3019 std::vector<int64_t>* optimal_cumuls, std::vector<int64_t>* optimal_breaks,
3020 std::vector<std::vector<int>>* optimal_resource_indices) {
3021 return optimizer_core_.Optimize(next_accessor,
3022 dimension_travel_info_per_route,
3023 solver_.get(), optimal_cumuls, optimal_breaks,
3024 optimal_resource_indices,
nullptr,
nullptr);
3028 const std::function<int64_t(int64_t)>& next_accessor,
3029 const std::vector<RoutingModel::RouteDimensionTravelInfo>&
3030 dimension_travel_info_per_route,
3031 std::vector<int64_t>* packed_cumuls, std::vector<int64_t>* packed_breaks) {
3032 return optimizer_core_.OptimizeAndPack(
3033 next_accessor, dimension_travel_info_per_route, solver_.get(),
3034 packed_cumuls, packed_breaks);
3039template <
typename T>
3040void MoveValuesToIndicesFrom(std::vector<T>* out_values,
3041 absl::Span<const int> out_indices_to_evaluate,
3042 const std::function<
int(
int)>& index_evaluator,
3043 std::vector<T>& values_to_copy) {
3044 if (out_values ==
nullptr) {
3045 DCHECK(values_to_copy.empty());
3048 DCHECK_EQ(values_to_copy.size(), out_indices_to_evaluate.size());
3049 for (
int i = 0; i < out_indices_to_evaluate.size(); i++) {
3050 const int output_index = index_evaluator(out_indices_to_evaluate[i]);
3051 DCHECK_LT(output_index, out_values->size());
3052 out_values->at(output_index) = std::move(values_to_copy[i]);
3059 int v,
double solve_duration_ratio,
3062 absl::flat_hash_set<int>>&
3063 ignored_resources_per_class,
3064 const std::function<int64_t(int64_t)>& next_accessor,
3065 const std::function<int64_t(int64_t, int64_t)>& transit_accessor,
3068 std::vector<int64_t>* assignment_costs,
3069 std::vector<std::vector<int64_t>>* cumul_values,
3070 std::vector<std::vector<int64_t>>* break_values) {
3071 DCHECK_GT(solve_duration_ratio, 0);
3072 DCHECK_LE(solve_duration_ratio, 1);
3073 DCHECK(lp_optimizer !=
nullptr);
3074 DCHECK(mp_optimizer !=
nullptr);
3075 DCHECK_NE(assignment_costs,
nullptr);
3076 assignment_costs->clear();
3077 ClearIfNonNull(cumul_values);
3078 ClearIfNonNull(break_values);
3081 DCHECK_EQ(dimension, mp_optimizer->
dimension());
3085 next_accessor(model->
Start(v)) == model->
End(v))) {
3095 std::vector<int> considered_resource_indices;
3096 considered_resource_indices.reserve(
3097 std::min<int>(resource_var->
Size(), num_resource_classes));
3098 std::vector<bool> resource_class_considered(num_resource_classes,
false);
3106 const int rc_index = resource_class.value();
3107 const absl::flat_hash_set<int>& ignored_resources =
3108 ignored_resources_per_class[resource_class];
3109 if (resource_class_considered[rc_index] ||
3110 ignored_resources.contains(res)) {
3113 resource_class_considered[rc_index] =
true;
3118 DCHECK(AllValuesContainedExcept(
3120 ignored_resources));
3121 considered_resource_indices.push_back(res);
3123 const bool use_mp_optimizer =
3127 use_mp_optimizer ? mp_optimizer : lp_optimizer;
3129 const std::vector<ResourceGroup::Resource>& resources =
3131 std::vector<int64_t> considered_assignment_costs;
3132 std::vector<std::vector<int64_t>> considered_cumul_values;
3133 std::vector<std::vector<int64_t>> considered_break_values;
3134 std::vector<DimensionSchedulingStatus> statuses =
3136 v, solve_duration_ratio, next_accessor, transit_accessor, resources,
3137 considered_resource_indices, optimize_vehicle_costs,
3138 &considered_assignment_costs,
3139 cumul_values ==
nullptr ?
nullptr : &considered_cumul_values,
3140 break_values ==
nullptr ?
nullptr : &considered_break_values);
3142 if (statuses.empty() ||
3143 (statuses.size() == 1 &&
3149 assignment_costs->resize(num_resource_classes, -1);
3150 if (cumul_values !=
nullptr) {
3151 cumul_values->resize(num_resource_classes, {});
3153 if (break_values !=
nullptr) {
3154 break_values->resize(num_resource_classes, {});
3157 const auto resource_to_class_index = [&resource_group](
int resource_index) {
3160 MoveValuesToIndicesFrom(assignment_costs, considered_resource_indices,
3161 resource_to_class_index, considered_assignment_costs);
3162 MoveValuesToIndicesFrom(cumul_values, considered_resource_indices,
3163 resource_to_class_index, considered_cumul_values);
3164 MoveValuesToIndicesFrom(break_values, considered_resource_indices,
3165 resource_to_class_index, considered_break_values);
3167 if (use_mp_optimizer) {
3171 return absl::c_any_of(*assignment_costs,
3172 [](int64_t cost) {
return cost >= 0; });
3175 std::vector<int> mp_resource_indices;
3176 DCHECK_EQ(statuses.size(), considered_resource_indices.size());
3177 for (
int i = 0; i < considered_resource_indices.size(); i++) {
3179 mp_resource_indices.push_back(considered_resource_indices[i]);
3183 std::vector<int64_t> mp_assignment_costs;
3184 std::vector<std::vector<int64_t>> mp_cumul_values;
3185 std::vector<std::vector<int64_t>> mp_break_values;
3187 v, solve_duration_ratio, next_accessor, transit_accessor, resources,
3188 mp_resource_indices, optimize_vehicle_costs, &mp_assignment_costs,
3189 cumul_values ==
nullptr ?
nullptr : &mp_cumul_values,
3190 break_values ==
nullptr ?
nullptr : &mp_break_values);
3191 if (!mp_resource_indices.empty() && mp_assignment_costs.empty()) {
3196 MoveValuesToIndicesFrom(assignment_costs, mp_resource_indices,
3197 resource_to_class_index, mp_assignment_costs);
3198 MoveValuesToIndicesFrom(cumul_values, mp_resource_indices,
3199 resource_to_class_index, mp_cumul_values);
3200 MoveValuesToIndicesFrom(break_values, mp_resource_indices,
3201 resource_to_class_index, mp_break_values);
3203 return absl::c_any_of(*assignment_costs,
3204 [](int64_t cost) {
return cost >= 0; });
3208 absl::Span<const int> vehicles,
3211 resource_indices_per_class,
3213 absl::flat_hash_set<int>>&
3214 ignored_resources_per_class,
3215 std::function<
const std::vector<int64_t>*(
int)>
3216 vehicle_to_resource_class_assignment_costs,
3217 std::vector<int>* resource_indices) {
3218 const int total_num_resources = absl::c_accumulate(
3219 resource_indices_per_class, 0,
3220 [](
int acc, absl::Span<const int> res) {
return acc + res.size(); });
3221 DCHECK_GE(total_num_resources, 1);
3222 const int num_ignored_resources =
3223 absl::c_accumulate(ignored_resources_per_class, 0,
3224 [](
int acc,
const absl::flat_hash_set<int>& res) {
3225 return acc + res.size();
3227 const int num_resources = total_num_resources - num_ignored_resources;
3228 const int num_vehicles = vehicles.size();
3229 int num_total_vehicles = -1;
3230 if (resource_indices !=
nullptr) {
3231 num_total_vehicles = resource_indices->size();
3234 resource_indices->clear();
3235 DCHECK_GE(num_total_vehicles, num_vehicles);
3236 for (
int v : vehicles) {
3238 DCHECK_LT(v, num_total_vehicles);
3246 const int num_resource_classes = resource_indices_per_class.size();
3247 std::vector<const std::vector<int64_t>*> vi_to_rc_cost(num_vehicles);
3248 int num_vehicles_to_assign = 0;
3249 for (
int i = 0; i < num_vehicles; ++i) {
3250 vi_to_rc_cost[i] = vehicle_to_resource_class_assignment_costs(vehicles[i]);
3251 if (!vi_to_rc_cost[i]->empty()) {
3252 DCHECK_EQ(vi_to_rc_cost[i]->size(), num_resource_classes);
3253 ++num_vehicles_to_assign;
3256 if (num_vehicles_to_assign > num_resources) {
3257 VLOG(3) <<
"Less resources (" << num_resources <<
") than the vehicles"
3258 <<
" requiring one (" << num_vehicles_to_assign <<
")";
3266 for (
int i = 0; i < num_vehicles; ++i) {
3267 if (!vi_to_rc_cost[i]->empty() &&
3268 *absl::c_max_element(*vi_to_rc_cost[i]) < 0) {
3269 VLOG(3) <<
"Vehicle #" << vehicles[i] <<
" has no feasible resource";
3277 int64_t max_arc_cost = 0;
3278 for (
const std::vector<int64_t>* costs : vi_to_rc_cost) {
3279 if (costs->empty())
continue;
3280 max_arc_cost = std::max(max_arc_cost, *absl::c_max_element(*costs));
3285 const int real_num_nodes = 4 + num_vehicles + num_resource_classes;
3286 const int64_t max_acceptable_arc_cost =
kint64max / (4 * real_num_nodes) - 1;
3289 int cost_right_shift = 0;
3290 while ((max_arc_cost >> cost_right_shift) > max_acceptable_arc_cost) {
3300 2 + num_vehicles + num_resource_classes,
3301 num_vehicles + num_vehicles * num_resource_classes +
3302 num_resource_classes);
3304 const int source_index = num_vehicles + num_resource_classes;
3305 const int sink_index = source_index + 1;
3306 const auto flow_rc_index = [num_vehicles](
int rc) {
3307 return num_vehicles + rc;
3312 if (resource_indices !=
nullptr) {
3313 vehicle_to_rc_arc_index =
3316 for (
int vi = 0; vi < num_vehicles; ++vi) {
3317 const std::vector<int64_t>& assignment_costs = *vi_to_rc_cost[vi];
3318 if (assignment_costs.empty())
continue;
3324 for (
int rc = 0; rc < num_resource_classes; rc++) {
3325 const int64_t assignment_cost = assignment_costs[rc];
3326 if (assignment_cost < 0)
continue;
3328 vi, flow_rc_index(rc), 1, assignment_cost >> cost_right_shift);
3329 if (resource_indices !=
nullptr) {
3330 vehicle_to_rc_arc_index[vi][rc] = arc;
3338 for (
int rc = 0; rc < num_resource_classes; rc++) {
3339 const RCIndex rci(rc);
3340 const int num_available_res = resource_indices_per_class[rci].size() -
3341 ignored_resources_per_class[rci].size();
3342 DCHECK_GE(num_available_res, 0);
3344 num_available_res, 0);
3353 VLOG(3) <<
"Non-OPTIMAL flow result";
3357 if (resource_indices !=
nullptr) {
3359 resource_indices->assign(num_total_vehicles, -1);
3360 std::vector<int> current_resource_pos_for_class(num_resource_classes, 0);
3361 for (
int vi = 0; vi < num_vehicles; ++vi) {
3362 if (vi_to_rc_cost[vi]->empty()) {
3366 for (
int rc = 0; rc < num_resource_classes; rc++) {
3367 const ArcIndex arc = vehicle_to_rc_arc_index[vi][rc];
3368 if (arc >= 0 && flow.
Flow(arc) > 0) {
3369 const RCIndex rci(rc);
3370 const std::vector<int>& class_resource_indices =
3371 resource_indices_per_class[rci];
3372 const absl::flat_hash_set<int>& ignored_resources =
3373 ignored_resources_per_class[rci];
3374 int& pos = current_resource_pos_for_class[rc];
3375 DCHECK_LT(pos, class_resource_indices.size());
3376 while (ignored_resources.contains(class_resource_indices[pos])) {
3378 DCHECK_LT(pos, class_resource_indices.size());
3380 resource_indices->at(vehicles[vi]) = class_resource_indices[pos++];
3388 DCHECK_LE(cost,
kint64max >> cost_right_shift);
3389 return cost << cost_right_shift;
3393 if (number ==
kint64min)
return "-infty";
3394 if (number ==
kint64max)
return "+infty";
3395 return std::to_string(number);
3399 const ::google::protobuf::RepeatedField<int64_t>* domain) {
3400 if (domain->size() > 2 && domain->size() % 2 == 0) {
3401 std::string s =
"∈ ";
3402 for (
int i = 0; i < domain->size(); i += 2) {
3403 s += absl::StrFormat(
"[%s, %s]",
Int64ToStr(domain->Get(i)),
3405 if (i < domain->size() - 2) s +=
" ∪ ";
3408 }
else if (domain->size() == 2) {
3409 if (domain->Get(0) == domain->Get(1)) {
3410 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
3411 }
else if (domain->Get(0) == 0 && domain->Get(1) == 1) {
3415 }
else if (domain->Get(0) ==
kint64min) {
3416 return absl::StrFormat(
"≤ %s",
Int64ToStr(domain->Get(1)));
3417 }
else if (domain->Get(1) ==
kint64max) {
3418 return absl::StrFormat(
"≥ %s",
Int64ToStr(domain->Get(0)));
3420 return absl::StrFormat(
"∈ [%ls, %s]",
Int64ToStr(domain->Get(0)),
3422 }
else if (domain->size() == 1) {
3423 return absl::StrFormat(
"= %s",
Int64ToStr(domain->Get(0)));
3425 return absl::StrFormat(
"∈ Unknown domain (size=%ld)", domain->size());
3430 std::pair<sat::IntegerVariableProto, int>& variable_pair,
3434 const int index = variable_pair.second;
3448 bool show_enforcement =
true) {
3451 const auto& linear = constraint.
linear();
3452 for (
int j = 0; j < linear.vars().size(); ++j) {
3453 const std::string sign = linear.coeffs(j) > 0 ?
"+" :
"-";
3454 const std::string mult =
3455 std::abs(linear.coeffs(j)) != 1
3456 ? std::to_string(std::abs(linear.coeffs(j))) +
" * "
3458 if (j > 0 || sign !=
"+") s += sign +
" ";
3464 if (show_enforcement) {
3466 s += (j == 0) ?
"\t if " :
" and ";
3477 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>&
3479 absl::flat_hash_map<std::string, std::vector<int>>& variable_instances,
3480 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>&
3483 std::string prefix =
"") {
3484 if (variable.empty()) {
3486 const auto& childs = variable_childs[
""];
3487 for (
const std::string& child : childs) {
3490 response_, child, prefix) +
3496 const auto& instances = variable_instances[variable];
3497 std::string variable_display(variable);
3498 std::size_t bracket_pos = variable.find_last_of(
')');
3499 if (bracket_pos != std::string::npos) {
3500 variable_display = variable.substr(bracket_pos + 1);
3502 std::string s = variable_display +
" | ";
3503 prefix += std::string(variable_display.length(),
' ') +
" | ";
3504 for (
int i = 0; i < instances.size(); ++i) {
3505 const std::string instance_name =
3506 absl::StrFormat(
"%s(%ld)", variable, instances[i]);
3507 if (i > 0) s += prefix;
3508 s += absl::StrFormat(
"%ld: %s", instances[i],
3512 const auto& childs = variable_childs[instance_name];
3513 for (
const std::string& child : childs) {
3514 s +=
"\n" + prefix +
"| ";
3516 response_, child, prefix +
"| ");
3518 if (childs.empty()) s +=
"\n";
3525 std::vector<std::vector<std::string>> constraints_apart;
3526 constraints_apart.push_back(
3527 {
"compression_cost",
"travel_compression_absolute"});
3532 absl::flat_hash_map<std::string, std::vector<int>> variable_instances;
3536 absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>
3539 absl::flat_hash_map<std::string, std::pair<sat::IntegerVariableProto, int>>
3541 variable_children[
""] = {};
3543 const int num_constraints = model_.constraints_size();
3544 const int num_variables = model_.variables_size();
3545 int num_binary_variables = 0;
3546 for (
int i = 0; i < num_variables; ++i) {
3547 const auto& variable = model_.variables(i);
3548 const auto& name = variable.name();
3549 const int pos_bracket = name.find_last_of(
'(');
3550 if (pos_bracket != std::string::npos) {
3551 const std::string lemma = name.substr(0, pos_bracket);
3552 const int pos_closing_bracket = name.find_last_of(
')');
3553 CHECK_NE(pos_closing_bracket, std::string::npos);
3555 std::stoi(name.substr(pos_bracket + 1, pos_closing_bracket));
3556 std::vector<int>* instances =
gtl::FindOrNull(variable_instances, lemma);
3557 if (instances !=
nullptr) {
3558 instances->push_back(index);
3560 variable_instances[lemma] = {index};
3562 variable_children[name] = {};
3564 std::string parent =
"";
3565 const int pos_parent_closing_bracket = lemma.find_last_of(
')');
3566 if (pos_parent_closing_bracket != std::string::npos) {
3567 parent = lemma.substr(0, pos_parent_closing_bracket + 1);
3569 variable_children[parent].emplace(lemma);
3570 variables[name] = std::make_pair(variable, i);
3571 if (variable.domain(0) == 0 && variable.domain(1) == 1) {
3572 ++num_binary_variables;
3583 absl::flat_hash_map<std::string, std::vector<sat::ConstraintProto>>
3585 absl::flat_hash_map<std::vector<std::string>,
3586 std::vector<sat::ConstraintProto>>
3588 for (
int i = 0; i < num_constraints; ++i) {
3589 const auto& constraint = model_.constraints(i);
3590 std::string enforcement =
"";
3591 if (constraint.enforcement_literal_size() == 1) {
3592 enforcement = model_.variables(constraint.enforcement_literal(0)).name();
3593 }
else if (constraint.enforcement_literal_size() > 1) {
3594 enforcement =
"multiple";
3596 if (constraint.has_linear()) {
3597 const auto& linear = constraint.linear();
3598 std::vector<std::string> key;
3599 for (
int j = 0; j < linear.vars().size(); ++j) {
3600 std::string var_name = model_.variables(linear.vars(j)).name();
3601 std::string lemma = var_name.substr(0, var_name.find_last_of(
'('));
3602 key.push_back(lemma);
3605 if (constraint_group !=
nullptr) {
3606 constraint_group->push_back(constraint);
3608 constraint_groups[key] = {constraint};
3612 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
3613 if (constraints_enforced !=
nullptr) {
3614 constraints[enforcement].push_back(constraint);
3616 constraints[enforcement] = {constraint};
3620 const std::string prefix_constraint =
" • ";
3621 std::string s =
"Using RoutingCPSatWrapper.\n";
3624 for (
int i = 0; i < objective_coefficients_.size(); ++i) {
3625 double coeff = objective_coefficients_[i];
3627 s += absl::StrFormat(
" | %f * %s\n", coeff, model_.variables(i).name());
3631 s += absl::StrFormat(
"\nVariables %d (%d Binary - %d Non Binary)\n",
3632 num_variables, num_binary_variables,
3633 num_variables - num_binary_variables);
3635 response_,
"",
" | ");
3636 s += absl::StrFormat(
"\n\nConstraints (%d)\n", num_constraints);
3639 s +=
"\n- Not enforced\n";
3640 bool at_least_one_not_enforced =
false;
3641 for (
const auto& pair : constraint_groups) {
3642 if (!std::count(constraints_apart.begin(), constraints_apart.end(),
3644 for (
const auto& constraint : pair.second) {
3647 at_least_one_not_enforced =
true;
3651 if (!at_least_one_not_enforced) {
3652 s += prefix_constraint +
"None\n";
3656 s +=
"\n- Single enforcement\n";
3657 bool at_least_one_single_enforced =
false;
3658 for (
const auto& pair : variable_instances) {
3659 const std::string lemma = pair.first;
3660 bool found_one_constraint =
false;
3661 std::string prefix =
"";
3662 for (
int instance : pair.second) {
3663 const std::string enforcement =
3664 absl::StrFormat(
"%s(%d)", lemma, instance);
3665 auto* constraints_enforced =
gtl::FindOrNull(constraints, enforcement);
3666 std::string prefix_instance =
"";
3667 if (constraints_enforced !=
nullptr) {
3668 at_least_one_single_enforced =
true;
3669 if (!found_one_constraint) {
3670 found_one_constraint =
true;
3671 s += prefix_constraint +
"if " + lemma +
" | ";
3673 std::string(prefix_constraint.size() + 1 + lemma.size(),
' ') +
3678 s += absl::StrFormat(
"%d: | ", instance);
3679 prefix_instance = prefix +
" | ";
3681 for (
const auto& constraint : *constraints_enforced) {
3683 s += prefix_instance;
3691 if (!at_least_one_single_enforced) {
3692 s += prefix_constraint +
"None\n";
3696 s +=
"\n- Multiple enforcement\n";
3697 auto* constraints_multiple_enforced =
3699 if (constraints_multiple_enforced !=
nullptr) {
3700 for (
const auto& constraint : *constraints_multiple_enforced) {
3705 s += prefix_constraint +
"None\n";
3709 s +=
"\n- Set apart\n";
3710 bool at_least_one_apart =
false;
3711 for (
const auto& pair : constraint_groups) {
3712 if (std::count(constraints_apart.begin(), constraints_apart.end(),
3714 for (
const auto& constraint : pair.second) {
3717 at_least_one_apart =
true;
3721 if (!at_least_one_apart) {
3722 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)
DimensionSchedulingStatus OptimizeSingleRouteWithTransitTargets(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, absl::Span< const int64_t > transit_targets, TransitTargetCost transit_target_cost, RoutingLinearSolverWrapper *solver, std::vector< int64_t > *optimal_transits, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
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()
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, RoutingSearchStats *search_stats)
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, RoutingSearchStats *search_stats)
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)
Merge with the packing method DimensionSchedulingStatus ComputeRouteCumulsWithTransitTargets(int vehicle, double solve_duration_ratio, const std::function< int64_t(int64_t)> &next_accessor, absl::Span< const int64_t > transit_targets, DimensionCumulOptimizerCore::TransitTargetCost transit_target_cost, std::vector< int64_t > *optimal_transits, std::vector< int64_t > *optimal_cumuls, std::vector< int64_t > *optimal_breaks)
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 IntOut Round(FloatIn x)
std::string PrintModel() const override
int64_t GetObjectiveValue() const override
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
const std::vector< operations_research::IntVar * > & cumuls() const
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
RoutingModel * model() const
Returns the model on which the dimension was created.
static PrecedenceStatus GetPrecedenceStatus(bool first_unperformed, bool second_unperformed, NodePrecedence::PerformedConstraint performed_constraint)
virtual int64_t GetObjectiveValue() const =0
virtual DimensionSchedulingStatus Solve(absl::Duration duration_limit)=0
virtual int64_t GetVariableUpperBound(int index) const =0
int AddLinearConstraint(int64_t lower_bound, int64_t upper_bound, absl::Span< const std::pair< int, double > > variable_coeffs)
virtual bool SolutionIsInteger() const =0
virtual int64_t GetVariableValue(int index) 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
virtual void AddObjectiveConstraint()=0
virtual bool ModelIsEmpty() const
virtual int64_t GetVariableLowerBound(int index) const =0
virtual bool IsCPSATSolver()=0
int AddVariable(int64_t lower_bound, int64_t upper_bound)
A Resource sets attributes (costs/constraints) for a set of dimensions.
bool VehicleRequiresAResource(int vehicle) const
const std::vector< int > & GetResourceIndicesInClass(ResourceClassIndex resource_class) const
int GetResourceClassesCount() const
const std::vector< Resource > & GetResources() const
ResourceClassIndex GetResourceClassIndex(int resource_index) const
int64_t Start(int vehicle) const
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
bool IsVehicleUsedWhenEmpty(int vehicle) const
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int vehicles() const
Returns the number of vehicle routes in the model.
RoutingResourceClassIndex ResourceClassIndex
bool CheckLimit(absl::Duration offset=absl::ZeroDuration())
absl::Duration RemainingTime() const
Returns the time left in the search limit.
operations_research::IntVar * ResourceVar(int vehicle, int resource_group) const
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
int enforcement_literal_size() const
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)
int64_t CapAdd(int64_t x, int64_t y)
std::string ConstraintToString(const sat::ConstraintProto &constraint, const sat::CpModelProto &model_, bool show_enforcement=true)
void CapAddTo(int64_t x, int64_t *y)
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
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_)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
#define SET_DEBUG_VARIABLE_NAME(solver, var, name)
The position of a node in the set of pickup and delivery pairs.
Contains the information for a single transition on the route.
int64_t pre_travel_transit_value
static const int64_t kint64max
static const int64_t kint64min