bump routing code

This commit is contained in:
Laurent Perron
2024-12-28 11:21:20 +01:00
parent 7d64449edb
commit d3d258bd04
7 changed files with 283 additions and 188 deletions

View File

@@ -183,9 +183,11 @@ class ResourceAssignmentConstraint : public Constraint {
const util_intops::StrongVector<RCIndex, absl::flat_hash_set<int>>
ignored_resources_per_class(resource_group_.GetResourceClassesCount());
std::vector<std::vector<int64_t>> assignment_costs(model_.vehicles());
// TODO(user): Adjust the 'solve_duration_ratio' parameter.
for (int v : resource_group_.GetVehiclesRequiringAResource()) {
if (!ComputeVehicleToResourceClassAssignmentCosts(
v, resource_group_, ignored_resources_per_class, next, transit,
v, /*solve_duration_ratio=*/1.0, resource_group_,
ignored_resources_per_class, next, transit,
/*optimize_vehicle_costs*/ false,
model_.GetMutableLocalCumulLPOptimizer(dimension),
model_.GetMutableLocalCumulMPOptimizer(dimension),

View File

@@ -251,16 +251,24 @@ class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
// First look at vehicles that do not need resource assignment (fewer/faster
// computations).
// NOTE(user): If it ever becomes an issue, we can consider leaving more
// 'shares' for the resource assignment calls since they're more expensive.
int solve_duration_shares = vehicles_without_resource_assignment.size() +
vehicles_with_resource_assignment.size();
for (int vehicle : vehicles_without_resource_assignment) {
solver->TopPeriodicCheck();
std::vector<int64_t> cumul_values;
std::vector<int64_t> break_start_end_values;
// TODO(user): Distinguish between FEASIBLE and OPTIMAL statuses to
// keep track of the FEASIBLE-only cases.
if (!ComputeCumulAndBreakValuesForVehicle(vehicle, next, &cumul_values,
&break_start_end_values)) {
// keep track of the FEASIBLE-only cases, and resolve the feasible-only
// cases with the remaining time (if any) after all routes have been
// scheduled with the initial 'solve_duration_ratio'.
if (!ComputeCumulAndBreakValuesForVehicle(
vehicle, /*solve_duration_ratio=*/1.0 / solve_duration_shares,
next, &cumul_values, &break_start_end_values)) {
return false;
}
solve_duration_shares--;
AppendRouteCumulAndBreakVarAndValues(dimension_, vehicle, cumul_values,
break_start_end_values,
&cp_variables_, &cp_values_);
@@ -353,7 +361,8 @@ class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
}
bool ComputeCumulAndBreakValuesForVehicle(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
int vehicle, double solve_duration_ratio,
const std::function<int64_t(int64_t)>& next_accessor,
std::vector<int64_t>* cumul_values,
std::vector<int64_t>* break_start_end_values) {
cumul_values->clear();
@@ -379,29 +388,29 @@ class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
use_mp_optimizer ? mp_optimizer_ : lp_optimizer_;
DCHECK_NE(optimizer, nullptr);
DimensionSchedulingStatus status =
optimize_and_pack_
? optimizer->ComputePackedRouteCumuls(
vehicle, next_accessor, dimension_travel_info, resource,
cumul_values, break_start_end_values)
: optimizer->ComputeRouteCumuls(
vehicle, next_accessor, dimension_travel_info, resource,
cumul_values, break_start_end_values);
optimize_and_pack_ ? optimizer->ComputePackedRouteCumuls(
vehicle, solve_duration_ratio, next_accessor,
dimension_travel_info, resource, cumul_values,
break_start_end_values)
: optimizer->ComputeRouteCumuls(
vehicle, solve_duration_ratio, next_accessor,
dimension_travel_info, resource, cumul_values,
break_start_end_values);
// If relaxation is not feasible, try the MP optimizer.
if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
DCHECK(!use_mp_optimizer);
DCHECK_NE(mp_optimizer_, nullptr);
status = optimize_and_pack_
? mp_optimizer_->ComputePackedRouteCumuls(
vehicle, next_accessor, dimension_travel_info,
resource, cumul_values, break_start_end_values)
vehicle, solve_duration_ratio, next_accessor,
dimension_travel_info, resource, cumul_values,
break_start_end_values)
: mp_optimizer_->ComputeRouteCumuls(
vehicle, next_accessor, dimension_travel_info,
resource, cumul_values, break_start_end_values);
vehicle, solve_duration_ratio, next_accessor,
dimension_travel_info, resource, cumul_values,
break_start_end_values);
}
if (status == DimensionSchedulingStatus::INFEASIBLE) {
return false;
}
return true;
return status != DimensionSchedulingStatus::INFEASIBLE;
}
bool ComputeVehicleResourceClassValuesAndIndices(
@@ -414,17 +423,20 @@ class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
if (vehicles_to_assign.empty()) return true;
DCHECK_NE(resource_group_, nullptr);
int solve_duration_shares = vehicles_to_assign.size();
for (int v : vehicles_to_assign) {
DCHECK(resource_group_->VehicleRequiresAResource(v));
auto& [assignment_costs, cumul_values, break_values] =
vehicle_resource_class_values_[v];
if (!ComputeVehicleToResourceClassAssignmentCosts(
v, *resource_group_, used_resources_per_class, next_accessor,
v, /*solve_duration_ratio=*/1.0 / solve_duration_shares,
*resource_group_, used_resources_per_class, next_accessor,
dimension_.transit_evaluator(v),
/*optimize_vehicle_costs*/ true, lp_optimizer_, mp_optimizer_,
&assignment_costs, &cumul_values, &break_values)) {
return false;
}
solve_duration_shares--;
}
return ComputeBestVehicleToResourceAssignment(

View File

@@ -1114,7 +1114,7 @@ bool ChainCumulFilter::AcceptPath(int64_t path_start, int64_t chain_start,
bool PropagateLightweightVehicleBreaks(
int path, DimensionValues& dimension_values,
const std::vector<std::pair<int64_t, int64_t>>& interbreaks) {
absl::Span<const std::pair<int64_t, int64_t>> interbreaks) {
using Interval = DimensionValues::Interval;
using VehicleBreak = DimensionValues::VehicleBreak;
const int64_t total_travel = dimension_values.TravelSums(path).back();
@@ -2001,6 +2001,9 @@ bool PathCumulFilter::FinalizeAcceptPath(int64_t /*objective_min*/,
if (may_use_optimizers_ && lp_optimizer_ != nullptr &&
accepted_objective_value_ <= objective_max) {
std::vector<int> paths_requiring_mp_optimizer;
// TODO(user): Further optimize the LPs when we find feasible-only
// solutions with the original time shares, if there's time left in the end.
int solve_duration_shares = dimension_values_.ChangedPaths().size();
for (const int vehicle : dimension_values_.ChangedPaths()) {
if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
continue;
@@ -2008,13 +2011,17 @@ bool PathCumulFilter::FinalizeAcceptPath(int64_t /*objective_min*/,
int64_t path_cost_with_lp = 0;
const DimensionSchedulingStatus status =
lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
vehicle, path_accessor_, /*resource=*/nullptr,
vehicle, /*solve_duration_ratio=*/1.0 / solve_duration_shares,
path_accessor_, /*resource=*/nullptr,
filter_objective_cost_ ? &path_cost_with_lp : nullptr);
solve_duration_shares--;
if (status == DimensionSchedulingStatus::INFEASIBLE) {
return false;
}
// Replace previous path cost with the LP optimizer cost.
if (filter_objective_cost_ &&
(status == DimensionSchedulingStatus::OPTIMAL ||
status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) &&
path_cost_with_lp > cost_of_path_.Get(vehicle)) {
CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
CapAddTo(path_cost_with_lp, &accepted_objective_value_);
@@ -2033,15 +2040,20 @@ bool PathCumulFilter::FinalizeAcceptPath(int64_t /*objective_min*/,
DCHECK_LE(accepted_objective_value_, objective_max);
solve_duration_shares = paths_requiring_mp_optimizer.size();
for (const int vehicle : paths_requiring_mp_optimizer) {
int64_t path_cost_with_mp = 0;
if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
vehicle, path_accessor_, /*resource=*/nullptr,
filter_objective_cost_ ? &path_cost_with_mp : nullptr) ==
DimensionSchedulingStatus::INFEASIBLE) {
const DimensionSchedulingStatus status =
mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
vehicle, /*solve_duration_ratio=*/1.0 / solve_duration_shares,
path_accessor_, /*resource=*/nullptr,
filter_objective_cost_ ? &path_cost_with_mp : nullptr);
solve_duration_shares--;
if (status == DimensionSchedulingStatus::INFEASIBLE) {
return false;
}
if (filter_objective_cost_ &&
status == DimensionSchedulingStatus::OPTIMAL &&
path_cost_with_mp > cost_of_path_.Get(vehicle)) {
CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
CapAddTo(path_cost_with_mp, &accepted_objective_value_);
@@ -2994,9 +3006,10 @@ bool ResourceGroupAssignmentFilter::FinalizeAcceptPath(
continue;
}
if (!ComputeVehicleToResourceClassAssignmentCosts(
vehicle, resource_group_, ignored_resources_per_class_,
next_accessor, dimension_.transit_evaluator(vehicle),
filter_objective_cost_, lp_optimizer_, mp_optimizer_,
vehicle, /*solve_duration_ratio=*/1.0, resource_group_,
ignored_resources_per_class_, next_accessor,
dimension_.transit_evaluator(vehicle), filter_objective_cost_,
lp_optimizer_, mp_optimizer_,
&delta_vehicle_to_resource_class_assignment_costs_[vehicle],
nullptr, nullptr)) {
return false;
@@ -3068,8 +3081,10 @@ void ResourceGroupAssignmentFilter::OnSynchronizePathFromStart(int64_t start) {
// vehicle requiring resource assignment to keep track of whether or not a
// given vehicle-to-resource-class assignment is possible by storing 0 or -1
// in vehicle_to_resource_class_assignment_costs_.
// TODO(user): Adjust the 'solve_duration_ratio' below.
if (!ComputeVehicleToResourceClassAssignmentCosts(
v, resource_group_, ignored_resources_per_class_, next_accessor,
v, /*solve_duration_ratio=*/1.0, resource_group_,
ignored_resources_per_class_, next_accessor,
dimension_.transit_evaluator(v), filter_objective_cost_,
lp_optimizer_, mp_optimizer_,
&vehicle_to_resource_class_assignment_costs_[v], nullptr, nullptr)) {
@@ -3152,14 +3167,14 @@ ResourceGroupAssignmentFilter::ComputeRouteCumulCostWithoutResourceAssignment(
int64_t route_cost = 0;
const DimensionSchedulingStatus status =
lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
vehicle, next_accessor, resource,
vehicle, /*solve_duration_ratio=*/1.0, next_accessor, resource,
filter_objective_cost_ ? &route_cost : nullptr);
switch (status) {
case DimensionSchedulingStatus::INFEASIBLE:
return -1;
case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
vehicle, next_accessor, resource,
vehicle, /*solve_duration_ratio=*/1.0, next_accessor, resource,
filter_objective_cost_ ? &route_cost : nullptr) ==
DimensionSchedulingStatus::INFEASIBLE) {
return -1;

View File

@@ -524,7 +524,7 @@ class DimensionValues {
// This applies light reasoning, and runs in O(#breaks * #interbreak rules).
bool PropagateLightweightVehicleBreaks(
int path, DimensionValues& dimension_values,
const std::vector<std::pair<int64_t, int64_t>>& interbreaks);
absl::Span<const std::pair<int64_t, int64_t>> interbreaks);
/// Returns a filter tracking route constraints.
IntVarLocalSearchFilter* MakeRouteConstraintFilter(

View File

@@ -287,7 +287,7 @@ bool RoutingModel::SolveMatchingModel(
// TODO(user): if the result is RELAXED_OPTIMAL_ONLY, do a
// second pass with an MP solver.
if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
vehicle,
vehicle, /*solve_duration_ratio=*/1.0,
[&nexts](int64_t node) {
return nexts.find(node)->second;
},
@@ -312,7 +312,7 @@ bool RoutingModel::SolveMatchingModel(
// TODO(user): if the result is RELAXED_OPTIMAL_ONLY, do a
// second pass with an MP solver.
if (optimizer.ComputeRouteCumulCostWithoutFixedTransits(
vehicle,
vehicle, /*solve_duration_ratio=*/1.0,
[&nexts](int64_t node) {
return nexts.find(node)->second;
},

View File

@@ -212,12 +212,15 @@ LocalDimensionCumulOptimizer::LocalDimensionCumulOptimizer(
}
DimensionSchedulingStatus LocalDimensionCumulOptimizer::ComputeRouteCumulCost(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
int vehicle, double solve_duration_ratio,
const std::function<int64_t(int64_t)>& next_accessor,
int64_t* optimal_cost) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
int64_t transit_cost = 0;
const DimensionSchedulingStatus status =
optimizer_core_.OptimizeSingleRouteWithResource(
vehicle, next_accessor,
vehicle, solve_duration_ratio, next_accessor,
/*dimension_travel_info=*/nullptr,
/*resource=*/nullptr,
/*optimize_vehicle_costs=*/optimal_cost != nullptr,
@@ -233,11 +236,15 @@ DimensionSchedulingStatus LocalDimensionCumulOptimizer::ComputeRouteCumulCost(
DimensionSchedulingStatus
LocalDimensionCumulOptimizer::ComputeRouteCumulCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
return optimizer_core_.OptimizeSingleRouteWithResource(
vehicle, next_accessor, /*dimension_travel_info=*/nullptr, resource,
vehicle, solve_duration_ratio, next_accessor,
/*dimension_travel_info=*/nullptr, resource,
/*optimize_vehicle_costs=*/optimal_cost_without_transits != nullptr,
solver_[vehicle].get(), /*cumul_values=*/nullptr,
/*break_values=*/nullptr, optimal_cost_without_transits, nullptr);
@@ -245,69 +252,85 @@ LocalDimensionCumulOptimizer::ComputeRouteCumulCostWithoutFixedTransits(
std::vector<DimensionSchedulingStatus> LocalDimensionCumulOptimizer::
ComputeRouteCumulCostsForResourcesWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
return optimizer_core_.OptimizeSingleRouteWithResources(
vehicle, next_accessor, transit_accessor, nullptr, resources,
resource_indices, optimize_vehicle_costs, solver_[vehicle].get(),
optimal_cumuls, optimal_breaks, optimal_costs_without_transits, nullptr);
vehicle, solve_duration_ratio, next_accessor, transit_accessor, nullptr,
resources, resource_indices, optimize_vehicle_costs,
solver_[vehicle].get(), optimal_cumuls, optimal_breaks,
optimal_costs_without_transits, nullptr);
}
DimensionSchedulingStatus LocalDimensionCumulOptimizer::ComputeRouteCumuls(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
return optimizer_core_.OptimizeSingleRouteWithResource(
vehicle, next_accessor, dimension_travel_info, resource,
/*optimize_vehicle_costs=*/true, solver_[vehicle].get(), optimal_cumuls,
optimal_breaks, /*cost_without_transit=*/nullptr,
vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
resource, /*optimize_vehicle_costs=*/true, solver_[vehicle].get(),
optimal_cumuls, optimal_breaks, /*cost_without_transit=*/nullptr,
/*transit_cost=*/nullptr);
}
DimensionSchedulingStatus
LocalDimensionCumulOptimizer::ComputeRouteCumulsAndCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
return optimizer_core_.OptimizeSingleRouteWithResource(
vehicle, next_accessor, dimension_travel_info, nullptr,
/*optimize_vehicle_costs=*/true, solver_[vehicle].get(), optimal_cumuls,
optimal_breaks, optimal_cost_without_transits, nullptr);
vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
nullptr, /*optimize_vehicle_costs=*/true, solver_[vehicle].get(),
optimal_cumuls, optimal_breaks, optimal_cost_without_transits, nullptr);
}
DimensionSchedulingStatus
LocalDimensionCumulOptimizer::ComputeRouteSolutionCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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, bool reuse_previous_model_if_possible, bool clear_lp,
absl::Duration* solve_duration) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
RoutingLinearSolverWrapper* solver = solver_[vehicle].get();
return optimizer_core_.ComputeSingleRouteSolutionCostWithoutFixedTransits(
vehicle, next_accessor, dimension_travel_info, solver,
solution_cumul_values, solution_break_values, solution_cost, cost_offset,
reuse_previous_model_if_possible, clear_lp,
vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
solver, solution_cumul_values, solution_break_values, solution_cost,
cost_offset, reuse_previous_model_if_possible, clear_lp,
/*clear_solution_constraints=*/true, solve_duration);
}
DimensionSchedulingStatus
LocalDimensionCumulOptimizer::ComputePackedRouteCumuls(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
return optimizer_core_.OptimizeAndPackSingleRoute(
vehicle, next_accessor, dimension_travel_info, resource,
solver_[vehicle].get(), packed_cumuls, packed_breaks);
vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
resource, solver_[vehicle].get(), packed_cumuls, packed_breaks);
}
const int CumulBoundsPropagator::kNoParent = -2;
@@ -578,7 +601,8 @@ DimensionCumulOptimizerCore::DimensionCumulOptimizerCore(
DimensionSchedulingStatus
DimensionCumulOptimizerCore::ComputeSingleRouteSolutionCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -608,7 +632,7 @@ DimensionCumulOptimizerCore::ComputeSingleRouteSolutionCostWithoutFixedTransits(
if (model->CheckLimit()) {
return DimensionSchedulingStatus::INFEASIBLE;
}
solve_duration_value = model->RemainingTime();
solve_duration_value = model->RemainingTime() * solve_duration_ratio;
if (solve_duration != nullptr) *solve_duration = solve_duration_value;
if (cost_offset != nullptr) *cost_offset = cost_offset_value;
} else {
@@ -617,7 +641,7 @@ DimensionCumulOptimizerCore::ComputeSingleRouteSolutionCostWithoutFixedTransits(
cost_offset_value = *cost_offset;
CHECK(solve_duration != nullptr)
<< "Cannot reuse model without the solve_duration";
solve_duration_value = *solve_duration;
solve_duration_value = *solve_duration * solve_duration_ratio;
}
// Constrains the cumuls.
@@ -689,7 +713,8 @@ void ClearIfNonNull(std::vector<T>* v) {
DimensionSchedulingStatus
DimensionCumulOptimizerCore::OptimizeSingleRouteWithResource(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
int vehicle, double solve_duration_ratio,
const std::function<int64_t(int64_t)>& next_accessor,
const RouteDimensionTravelInfo* dimension_travel_info,
const RoutingModel::ResourceGroup::Resource* resource,
bool optimize_vehicle_costs, RoutingLinearSolverWrapper* solver,
@@ -709,9 +734,9 @@ DimensionCumulOptimizerCore::OptimizeSingleRouteWithResource(
std::vector<std::vector<int64_t>> break_values_vec;
const std::vector<DimensionSchedulingStatus> statuses =
DimensionCumulOptimizerCore::OptimizeSingleRouteWithResources(
vehicle, next_accessor, dimension_->transit_evaluator(vehicle),
dimension_travel_info, resources, resource_indices,
optimize_vehicle_costs, solver,
vehicle, solve_duration_ratio, next_accessor,
dimension_->transit_evaluator(vehicle), dimension_travel_info,
resources, resource_indices, optimize_vehicle_costs, solver,
cumul_values != nullptr ? &cumul_values_vec : nullptr,
break_values != nullptr ? &break_values_vec : nullptr,
cost_without_transits != nullptr ? &costs_without_transits : nullptr,
@@ -827,7 +852,8 @@ bool TightenStartEndVariableBoundsWithAssignedResources(
std::vector<DimensionSchedulingStatus>
DimensionCumulOptimizerCore::OptimizeSingleRouteWithResources(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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 RoutingModel::ResourceGroup::Resource> resources,
@@ -870,9 +896,9 @@ DimensionCumulOptimizerCore::OptimizeSingleRouteWithResources(
// NOTE: When there are no resources to optimize for, we still solve the
// optimization problem for the route (without any added resource constraint).
const int num_solves =
std::max(static_cast<decltype(resource_indices.size())>(1UL),
resource_indices.size());
// TODO(user): Consider taking 'num_solves' into account to re-adjust the
// solve_duration_ratio to make sure all sub-problems are given enough time.
const int num_solves = resource_indices.empty() ? 1 : resource_indices.size();
if (costs_without_transits != nullptr) {
costs_without_transits->assign(num_solves, -1);
}
@@ -903,7 +929,8 @@ DimensionCumulOptimizerCore::OptimizeSingleRouteWithResources(
continue;
}
statuses.push_back(solver->Solve(model->RemainingTime()));
statuses.push_back(
solver->Solve(model->RemainingTime() * solve_duration_ratio));
if (statuses.back() == DimensionSchedulingStatus::INFEASIBLE) {
continue;
}
@@ -1053,7 +1080,8 @@ DimensionSchedulingStatus DimensionCumulOptimizerCore::OptimizeAndPack(
std::iota(vehicles.begin(), vehicles.end(), 0);
// Subtle: Even if the status was RELAXED_OPTIMAL_ONLY we try to pack just
// in case packing manages to make the solution completely feasible.
status = PackRoutes(std::move(vehicles), solver, packing_parameters);
status = PackRoutes(std::move(vehicles), /*solve_duration_ratio=*/1.0,
solver, packing_parameters);
}
if (!solver->IsCPSATSolver()) {
solver->SetParameters(original_params.SerializeAsString());
@@ -1073,7 +1101,8 @@ DimensionSchedulingStatus DimensionCumulOptimizerCore::OptimizeAndPack(
DimensionSchedulingStatus
DimensionCumulOptimizerCore::OptimizeAndPackSingleRoute(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
int vehicle, double solve_duration_ratio,
const std::function<int64_t(int64_t)>& next_accessor,
const RouteDimensionTravelInfo* dimension_travel_info,
const RoutingModel::ResourceGroup::Resource* resource,
RoutingLinearSolverWrapper* solver, std::vector<int64_t>* cumul_values,
@@ -1086,15 +1115,20 @@ DimensionCumulOptimizerCore::OptimizeAndPackSingleRoute(
packing_parameters.set_use_preprocessing(true);
solver->SetParameters(packing_parameters.SerializeAsString());
}
// TODO(user): Since there are 3 separate solves for packing, divide the
// input solve_duration_ratio by 3 before passing to the below functions? Or
// maybe divide by 2 and let PackRoutes() divide by 2 itself (since the 2
// solves in PackRoutes() should start with a very good first solution hint).
DimensionSchedulingStatus status = OptimizeSingleRouteWithResource(
vehicle, next_accessor, dimension_travel_info, resource,
/*optimize_vehicle_costs=*/true, solver,
vehicle, solve_duration_ratio, next_accessor, dimension_travel_info,
resource, /*optimize_vehicle_costs=*/true, solver,
/*cumul_values=*/nullptr, /*break_values=*/nullptr,
/*cost_without_transit=*/nullptr, /*transit_cost=*/nullptr,
/*clear_lp=*/false);
if (status != DimensionSchedulingStatus::INFEASIBLE) {
status = PackRoutes({vehicle}, solver, packing_parameters);
status =
PackRoutes({vehicle}, solve_duration_ratio, solver, packing_parameters);
}
if (!solver->IsCPSATSolver()) {
solver->SetParameters(original_params.SerializeAsString());
@@ -1114,7 +1148,8 @@ DimensionCumulOptimizerCore::OptimizeAndPackSingleRoute(
}
DimensionSchedulingStatus DimensionCumulOptimizerCore::PackRoutes(
std::vector<int> vehicles, RoutingLinearSolverWrapper* solver,
std::vector<int> vehicles, double solve_duration_ratio,
RoutingLinearSolverWrapper* solver,
const glop::GlopParameters& packing_parameters) {
const RoutingModel* model = dimension_->model();
@@ -1138,15 +1173,16 @@ DimensionSchedulingStatus DimensionCumulOptimizerCore::PackRoutes(
}
glop::GlopParameters current_params;
const auto retry_solving = [&current_params, model, solver]() {
const auto retry_solving = [&current_params, model, solver,
solve_duration_ratio]() {
// NOTE: To bypass some cases of false negatives due to imprecisions, we try
// running Glop with a different use_dual_simplex parameter when running
// into an infeasible status.
current_params.set_use_dual_simplex(!current_params.use_dual_simplex());
solver->SetParameters(current_params.SerializeAsString());
return solver->Solve(model->RemainingTime());
return solver->Solve(model->RemainingTime() * solve_duration_ratio);
};
if (solver->Solve(model->RemainingTime()) ==
if (solver->Solve(model->RemainingTime() * solve_duration_ratio) ==
DimensionSchedulingStatus::INFEASIBLE) {
if (solver->IsCPSATSolver()) {
return DimensionSchedulingStatus::INFEASIBLE;
@@ -1173,7 +1209,8 @@ DimensionSchedulingStatus DimensionCumulOptimizerCore::PackRoutes(
index_to_cumul_variable_[model->Start(vehicle)], -1);
}
DimensionSchedulingStatus status = solver->Solve(model->RemainingTime());
DimensionSchedulingStatus status =
solver->Solve(model->RemainingTime() * solve_duration_ratio);
if (!solver->IsCPSATSolver() &&
status == DimensionSchedulingStatus::INFEASIBLE) {
status = retry_solving();
@@ -2147,77 +2184,96 @@ bool DimensionCumulOptimizerCore::SetRouteCumulConstraints(
}
}
// TODO(user): find why adding these constraints make CPSAT slower.
if (!solver->IsCPSATSolver()) {
for (const auto& [limit, min_break_duration] :
dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
int64_t min_num_breaks = 0;
if (limit > 0) {
min_num_breaks =
std::max<int64_t>(0, CapSub(total_fixed_transit, 1) / limit);
}
if (CapSub(current_route_min_cumuls_.back(),
current_route_max_cumuls_.front()) > limit) {
min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
}
if (num_breaks < min_num_breaks) return false;
if (min_num_breaks == 0) continue;
for (const auto& [limit, min_break_duration] :
dimension_->GetBreakDistanceDurationOfVehicle(vehicle)) {
int64_t min_num_breaks = 0;
if (limit > 0) {
min_num_breaks =
std::max<int64_t>(0, CapSub(total_fixed_transit, 1) / limit);
}
if (CapSub(current_route_min_cumuls_.back(),
current_route_max_cumuls_.front()) > limit) {
min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
}
if (num_breaks < min_num_breaks) return false;
if (min_num_breaks == 0) continue;
// Adds an LP relaxation of interbreak constraints.
// For all 0 <= pl < pr < path_size, for k > 0,
// if sum_{p in [pl, pr)} fixed_transit[p] > k * limit,
// then sum_{p in [pl, pr)} slack[p] >= k * min_break_duration.
//
// Moreover, if end_min[pr] - start_max[pl] > limit,
// the sum_{p in [pl, pr)} slack[p] >= min_break_duration.
//
// We want to apply the constraints above, without the ones that are
// dominated:
// - do not add the same constraint for k' < k, keep the largest k.
// - do not add the constraint for both (pl', pr') and (pl, pr)
// if [pl', pr') is a subset of [pl, pr), keep the smallest interval.
// TODO(user): reduce the number of constraints further;
// for instance if the constraint holds for (k, pl, pr) and (k', pl', pr')
// with pr <= pr', then no need to add the constraint for (k+k', pl, pr').
//
// We need fast access to sum_{p in [pl, pr)} fixed_transit[p].
// This will be sum_transits[pr] - sum_transits[pl]. Note that
// sum_transits[0] = 0, sum_transits[path_size-1] = total_fixed_transit.
std::vector<int64_t> sum_transits(path_size);
{
sum_transits[0] = 0;
for (int pos = 1; pos < path_size; ++pos) {
sum_transits[pos] = sum_transits[pos - 1] + fixed_transit[pos - 1];
}
// Adds an LP relaxation of interbreak constraints.
// For all 0 <= pl < pr < path_size, for k > 0,
// if sum_{p in [pl, pr)} fixed_transit[p] > k * limit,
// then sum_{p in [pl, pr)} slack[p] >= k * min_break_duration.
//
// Moreover, if end_min[pr] - start_max[pl] > limit,
// the sum_{p in [pl, pr)} slack[p] >= min_break_duration.
//
// We want to apply the constraints above, without the ones that are
// dominated:
// - do not add the same constraint for k' < k, keep the largest k.
// - do not add the constraint for both (pl', pr') and (pl, pr)
// if [pl', pr') is a subset of [pl, pr), keep the smallest interval.
// TODO(user): reduce the number of constraints further;
// for instance if the constraint holds for (k, pl, pr) and (k', pl', pr')
// with pr <= pr', then no need to add the constraint for (k+k', pl, pr').
//
// We need fast access to sum_{p in [pl, pr)} fixed_transit[p].
// This will be sum_transits[pr] - sum_transits[pl]. Note that
// sum_transits[0] = 0, sum_transits[path_size-1] = total_fixed_transit.
std::vector<int64_t> sum_transits(path_size);
{
sum_transits[0] = 0;
for (int pos = 1; pos < path_size; ++pos) {
sum_transits[pos] = sum_transits[pos - 1] + fixed_transit[pos - 1];
}
// To add the slack sum constraints, we need slack sum variables.
// Those are created lazily in a sparse vector, then only those useful
// variables are linked to slack variables after slack sum constraints
// have been added.
std::vector<int> slack_sum_vars(path_size, -1);
// Given a number of breaks k, an interval of path positions [pl, pr),
// returns true if the interbreak constraint triggers for k breaks.
// TODO(user): find tighter end_min/start_max conditions.
// Mind that a break may be longer than min_break_duration.
auto trigger = [&](int k, int pl, int pr) -> bool {
if (k == 1) {
const int64_t span_lb =
current_route_min_cumuls_[pr] - current_route_max_cumuls_[pl];
if (span_lb > limit) return true;
}
return sum_transits[pr] - sum_transits[pl] > k * limit;
};
int min_sum_var_index = path_size;
int max_sum_var_index = -1;
for (int k = 1; k <= min_num_breaks; ++k) {
int pr = 0;
for (int pl = 0; pl < path_size - 1; ++pl) {
pr = std::max(pr, pl + 1);
// Increase pr until transit(pl, pr) > k * limit.
while (pr < path_size && !trigger(k, pl, pr)) ++pr;
if (pr == path_size) break;
// Reduce [pl, pr) from the left.
while (pl < pr && trigger(k, pl + 1, pr)) ++pl;
}
// To add the slack sum constraints, we need slack sum variables.
// Those are created lazily in a sparse vector, then only those useful
// variables are linked to slack variables after slack sum constraints
// have been added.
std::vector<int> slack_sum_vars(path_size, -1);
// Given a number of breaks k, an interval of path positions [pl, pr),
// returns true if the interbreak constraint triggers for k breaks.
// TODO(user): find tighter end_min/start_max conditions.
// Mind that a break may be longer than min_break_duration.
auto trigger = [&](int k, int pl, int pr) -> bool {
const int64_t r_pre_travel = pr < path_size - 1 ? pre_travel[pr] : 0;
const int64_t l_post_travel = pl >= 1 ? post_travel[pl - 1] : 0;
const int64_t extra_travel = CapAdd(r_pre_travel, l_post_travel);
if (k == 1) {
const int64_t span_lb = CapAdd(CapSub(current_route_min_cumuls_[pr],
current_route_max_cumuls_[pl]),
extra_travel);
if (span_lb > limit) return true;
}
return CapAdd(CapSub(sum_transits[pr], sum_transits[pl]), extra_travel) >
CapProd(k, limit);
};
int min_sum_var_index = path_size;
int max_sum_var_index = -1;
for (int k = 1; k <= min_num_breaks; ++k) {
int pr = 0;
for (int pl = 0; pl < path_size - 1; ++pl) {
pr = std::max(pr, pl + 1);
// Increase pr until transit(pl, pr) > k * limit.
while (pr < path_size && !trigger(k, pl, pr)) ++pr;
if (pr == path_size) break;
// Reduce [pl, pr) from the left.
while (pl < pr && trigger(k, pl + 1, pr)) ++pl;
// If k is the largest for this interval, add the constraint.
// The call trigger(k', pl, pr) may hold for both k and k+1 with an
// irreducible interval [pl, pr] when there is a transit > limit at
// the beginning and at the end of the sub-route.
if (k < min_num_breaks && trigger(k + 1, pl, pr)) continue;
// If the solver is CPSAT, experimentation showed that adding slack sum
// constraints made solves worse than doing nothing, and that adding
// these lightweight constraints works better.
if (solver->IsCPSATSolver()) {
// lp_cumuls[pl] + transit(pl, pr) + k * min_break_duration <=
// lp_cumuls[pr].
solver->AddLinearConstraint(
CapAdd(CapSub(sum_transits[pr], sum_transits[pl]),
CapProd(k, min_break_duration)),
kint64max, {{lp_cumuls[pr], 1}, {lp_cumuls[pl], -1}});
} else {
if (slack_sum_vars[pl] == -1) {
slack_sum_vars[pl] = solver->CreateNewPositiveVariable();
min_sum_var_index = std::min(min_sum_var_index, pl);
@@ -2226,32 +2282,26 @@ bool DimensionCumulOptimizerCore::SetRouteCumulConstraints(
slack_sum_vars[pr] = solver->CreateNewPositiveVariable();
max_sum_var_index = std::max(max_sum_var_index, pr);
}
// If k is the largest for this interval, add the constraint.
// The call trigger(k', pl, pr) may hold for both k and k+1 with an
// irreducible interval [pl, pr] when there is a transit > limit at
// the beginning and at the end of the sub-route. sum_slacks[pr] -
// sum_slacks[pl] >= k * min_break_duration.
if (k < min_num_breaks && trigger(k + 1, pl, pr)) continue;
// sum_slacks[pr] - sum_slacks[pl] >= k * min_break_duration.
solver->AddLinearConstraint(
k * min_break_duration, kint64max,
CapProd(k, min_break_duration), kint64max,
{{slack_sum_vars[pr], 1}, {slack_sum_vars[pl], -1}});
}
}
if (min_sum_var_index < max_sum_var_index) {
slack_sum_vars[min_sum_var_index] = solver->AddVariable(0, 0);
int prev_index = min_sum_var_index;
for (int pos = min_sum_var_index + 1; pos <= max_sum_var_index; ++pos) {
if (slack_sum_vars[pos] == -1) continue;
// slack_sum_var[pos] =
// slack_sum_var[prev_index] + sum_{p in [prev_index, pos)} slack[p].
const int ct = solver->AddLinearConstraint(
0, 0,
{{slack_sum_vars[pos], 1}, {slack_sum_vars[prev_index], -1}});
for (int p = prev_index; p < pos; ++p) {
solver->SetCoefficient(ct, lp_slacks[p], -1);
}
prev_index = pos;
}
if (min_sum_var_index < max_sum_var_index) {
slack_sum_vars[min_sum_var_index] = solver->AddVariable(0, 0);
int prev_index = min_sum_var_index;
for (int pos = min_sum_var_index + 1; pos <= max_sum_var_index; ++pos) {
if (slack_sum_vars[pos] == -1) continue;
// slack_sum_var[pos] =
// slack_sum_var[prev_index] + sum_{p in [prev_index, pos)} slack[p].
const int ct = solver->AddLinearConstraint(
0, 0, {{slack_sum_vars[pos], 1}, {slack_sum_vars[prev_index], -1}});
for (int p = prev_index; p < pos; ++p) {
solver->SetCoefficient(ct, lp_slacks[p], -1);
}
prev_index = pos;
}
}
}
@@ -2779,7 +2829,8 @@ void MoveValuesToIndicesFrom(std::vector<T>* out_values,
} // namespace
bool ComputeVehicleToResourceClassAssignmentCosts(
int v, const RoutingModel::ResourceGroup& resource_group,
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,
@@ -2790,6 +2841,8 @@ bool ComputeVehicleToResourceClassAssignmentCosts(
std::vector<int64_t>* assignment_costs,
std::vector<std::vector<int64_t>>* cumul_values,
std::vector<std::vector<int64_t>>* break_values) {
DCHECK_GT(solve_duration_ratio, 0);
DCHECK_LE(solve_duration_ratio, 1);
DCHECK(lp_optimizer != nullptr);
DCHECK(mp_optimizer != nullptr);
DCHECK_NE(assignment_costs, nullptr);
@@ -2853,7 +2906,7 @@ bool ComputeVehicleToResourceClassAssignmentCosts(
std::vector<std::vector<int64_t>> considered_break_values;
std::vector<DimensionSchedulingStatus> statuses =
optimizer->ComputeRouteCumulCostsForResourcesWithoutFixedTransits(
v, next_accessor, transit_accessor, resources,
v, solve_duration_ratio, next_accessor, transit_accessor, resources,
considered_resource_indices, optimize_vehicle_costs,
&considered_assignment_costs,
cumul_values == nullptr ? nullptr : &considered_cumul_values,
@@ -2904,8 +2957,8 @@ bool ComputeVehicleToResourceClassAssignmentCosts(
std::vector<std::vector<int64_t>> mp_cumul_values;
std::vector<std::vector<int64_t>> mp_break_values;
mp_optimizer->ComputeRouteCumulCostsForResourcesWithoutFixedTransits(
v, next_accessor, transit_accessor, resources, mp_resource_indices,
optimize_vehicle_costs, &mp_assignment_costs,
v, solve_duration_ratio, next_accessor, transit_accessor, resources,
mp_resource_indices, optimize_vehicle_costs, &mp_assignment_costs,
cumul_values == nullptr ? nullptr : &mp_cumul_values,
break_values == nullptr ? nullptr : &mp_break_values);
if (!mp_resource_indices.empty() && mp_assignment_costs.empty()) {

View File

@@ -706,7 +706,8 @@ class DimensionCumulOptimizerCore {
// Finds an optimal (or just feasible) solution for the route for the given
// resource. If the resource is null, it is simply ignored.
DimensionSchedulingStatus OptimizeSingleRouteWithResource(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -717,7 +718,8 @@ class DimensionCumulOptimizerCore {
// same model as in OptimizeSingleRouteWithResource() with the addition of
// constraints for cumuls and breaks.
DimensionSchedulingStatus ComputeSingleRouteSolutionCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -732,7 +734,8 @@ class DimensionCumulOptimizerCore {
// If both 'resources' and 'resource_indices' are empty, computes the optimal
// solution for the route itself (without added resource constraints).
std::vector<DimensionSchedulingStatus> OptimizeSingleRouteWithResources(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -766,7 +769,8 @@ class DimensionCumulOptimizerCore {
std::vector<int64_t>* break_values);
DimensionSchedulingStatus OptimizeAndPackSingleRoute(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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);
@@ -841,7 +845,8 @@ class DimensionCumulOptimizerCore {
// ends' cumuls, and then maximizes the starts' cumuls without increasing the
// ends.
DimensionSchedulingStatus PackRoutes(
std::vector<int> vehicles, RoutingLinearSolverWrapper* solver,
std::vector<int> vehicles, double solve_duration_ratio,
RoutingLinearSolverWrapper* solver,
const glop::GlopParameters& packing_parameters);
std::unique_ptr<CumulBoundsPropagator> propagator_;
@@ -897,19 +902,22 @@ class LocalDimensionCumulOptimizer {
// and stores it in "optimal_cost" (if not null).
// Returns true iff the route respects all constraints.
DimensionSchedulingStatus ComputeRouteCumulCost(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
int vehicle, double solve_duration_ratio,
const std::function<int64_t(int64_t)>& next_accessor,
int64_t* optimal_cost);
// Same as ComputeRouteCumulCost, but the cost computed does not contain
// the part of the vehicle span cost due to fixed transits.
DimensionSchedulingStatus ComputeRouteCumulCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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);
std::vector<DimensionSchedulingStatus>
ComputeRouteCumulCostsForResourcesWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -923,7 +931,8 @@ class LocalDimensionCumulOptimizer {
// (if not null), and optimal_breaks, and returns true.
// Returns false if the route is not feasible.
DimensionSchedulingStatus ComputeRouteCumuls(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -932,7 +941,8 @@ class LocalDimensionCumulOptimizer {
// Simple combination of ComputeRouteCumulCostWithoutFixedTransits() and
// ComputeRouteCumuls().
DimensionSchedulingStatus ComputeRouteCumulsAndCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -941,7 +951,8 @@ class LocalDimensionCumulOptimizer {
// If feasible, computes the cost of a given route performed by a vehicle
// defined by its cumuls and breaks.
DimensionSchedulingStatus ComputeRouteSolutionCostWithoutFixedTransits(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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,
@@ -955,7 +966,8 @@ class LocalDimensionCumulOptimizer {
// If 'resource' is non-null, the packed route must also respect its start/end
// time window.
DimensionSchedulingStatus ComputePackedRouteCumuls(
int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
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);
@@ -1056,7 +1068,8 @@ int64_t ComputeBestVehicleToResourceAssignment(
// The cumul and break values corresponding to the assignment of each resource
// are also set in cumul_values and break_values, if non-null.
bool ComputeVehicleToResourceClassAssignmentCosts(
int v, const RoutingModel::ResourceGroup& resource_group,
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,