Skip to content

Commit

Permalink
routing: Export from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Aug 21, 2023
1 parent 63357b2 commit 8ce89a6
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 21 deletions.
88 changes: 79 additions & 9 deletions ortools/constraint_solver/routing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include "ortools/constraint_solver/routing_parameters.pb.h"
#include "ortools/constraint_solver/routing_search.h"
#include "ortools/constraint_solver/routing_types.h"
#include "ortools/constraint_solver/routing_utils.h"
#include "ortools/constraint_solver/solver_parameters.pb.h"
#include "ortools/graph/connected_components.h"
#include "ortools/graph/ebert_graph.h"
Expand Down Expand Up @@ -3999,7 +4000,8 @@ void RoutingModel::CreateNeighborhoodOperators(
parameters.local_cheapest_insertion_pickup_delivery_strategy(),
GetOrCreateLocalSearchFilterManager(
parameters,
{/*filter_objective=*/false, /*filter_with_cp_solver=*/false}));
{/*filter_objective=*/false, /*filter_with_cp_solver=*/false}),
bin_capacities_.get());
};
local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
Expand Down Expand Up @@ -4409,6 +4411,60 @@ LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
return local_search_filter_manager;
}

std::unique_ptr<BinCapacities> MakeBinCapacities(
const std::vector<RoutingDimension*>& dimensions,
const PathsMetadata& paths_metadata) {
const int num_vehicles = paths_metadata.NumPaths();
auto bin_capacities = std::make_unique<BinCapacities>(num_vehicles);
std::vector<BinCapacities::LoadLimit> load_limits;
for (const RoutingDimension* dimension : dimensions) {
// If the dimension is not unary, skip.
if (dimension->GetUnaryTransitEvaluator(0) == nullptr) continue;
// If the dimension has no constant-signed transit evaluator, skip.
if (dimension->AllTransitEvaluatorSignsAreUnknown()) continue;
// For each vehicle, if the sign of its evaluator is constant,
// set a transit evaluator to pass to BinCapacities.
load_limits.assign(num_vehicles, {.max_load = kint64max,
.soft_max_load = 0,
.cost_above_soft_max_load = 0});
for (int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
const RoutingModel::TransitEvaluatorSign sign =
dimension->GetTransitEvaluatorSign(vehicle);
if (sign == RoutingModel::kTransitEvaluatorSignUnknown) continue;
// Vehicle load changes monotonically along the route.
// If transit signs are >= 0, the min load is at start, the max at end.
// If transit signs are <= 0, the max load is at start, the min at end.
// The encoding into BinCapacities associates a bin dimension with this
// routing dimension, with bin capacity = vehicle capacity - min load,
// and bin item size = abs(transit(node)).
int64_t min_node = paths_metadata.Starts()[vehicle];
int64_t max_node = paths_metadata.Ends()[vehicle];
if (sign == RoutingModel::kTransitEvaluatorSignNegativeOrZero) {
std::swap(min_node, max_node);
}
const int64_t load_min =
std::max<int64_t>(0, dimension->CumulVar(min_node)->Min());
const int64_t load_max =
std::min(dimension->vehicle_capacities()[vehicle],
dimension->CumulVar(max_node)->Max());
load_limits[vehicle].max_load = CapSub(load_max, load_min);
if (dimension->HasCumulVarSoftUpperBound(max_node)) {
load_limits[vehicle].soft_max_load =
CapSub(dimension->GetCumulVarSoftUpperBound(max_node), load_min);
load_limits[vehicle].cost_above_soft_max_load =
dimension->GetCumulVarSoftUpperBoundCoefficient(max_node);
}
}
bin_capacities->AddDimension(
[dimension](int node, int vehicle) {
return CapAbs(dimension->GetUnaryTransitEvaluator(vehicle)(node));
},
load_limits);
}
if (bin_capacities->NumDimensions() == 0) bin_capacities.reset(nullptr);
return bin_capacities;
}

namespace {
bool AllTransitsPositive(const RoutingDimension& dimension) {
for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
Expand Down Expand Up @@ -4846,17 +4902,19 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders(
lci_pair_strategy,
GetOrCreateLocalSearchFilterManager(
search_parameters, {/*filter_objective=*/false,
/*filter_with_cp_solver=*/false}));
/*filter_with_cp_solver=*/false}),
bin_capacities_.get());
IntVarFilteredDecisionBuilder* const strong_lci =
CreateIntVarFilteredDecisionBuilder<
LocalCheapestInsertionFilteredHeuristic>(
[this](int64_t i, int64_t j, int64_t vehicle) {
return GetArcCostForVehicle(i, j, vehicle);
},
lci_pair_strategy,
GetOrCreateLocalSearchFilterManager(
search_parameters, {/*filter_objective=*/false,
/*filter_with_cp_solver=*/true}));
GetOrCreateLocalSearchFilterManager(search_parameters,
{/*filter_objective=*/false,
/*filter_with_cp_solver=*/true}),
bin_capacities_.get());
first_solution_decision_builders_
[FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
first_solution_filtered_decision_builders_
Expand All @@ -4876,14 +4934,16 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders(
/*evaluator=*/nullptr, lcci_pair_strategy,
GetOrCreateLocalSearchFilterManager(
search_parameters, {/*filter_objective=*/true,
/*filter_with_cp_solver=*/false}));
/*filter_with_cp_solver=*/false}),
bin_capacities_.get());
IntVarFilteredDecisionBuilder* const strong_lcci =
CreateIntVarFilteredDecisionBuilder<
LocalCheapestInsertionFilteredHeuristic>(
/*evaluator=*/nullptr, lcci_pair_strategy,
GetOrCreateLocalSearchFilterManager(
search_parameters, {/*filter_objective=*/true,
/*filter_with_cp_solver=*/true}));
GetOrCreateLocalSearchFilterManager(search_parameters,
{/*filter_objective=*/true,
/*filter_with_cp_solver=*/true}),
bin_capacities_.get());
first_solution_decision_builders_
[FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] = solver_->Try(
first_solution_filtered_decision_builders_
Expand Down Expand Up @@ -5897,6 +5957,16 @@ int64_t RoutingDimension::GetTransitValue(int64_t from_index, int64_t to_index,
return transit_evaluator(vehicle)(from_index, to_index);
}

bool RoutingDimension::AllTransitEvaluatorSignsAreUnknown() const {
for (const int evaluator_index : class_evaluators_) {
if (model()->transit_evaluator_sign_[evaluator_index] !=
RoutingModel::kTransitEvaluatorSignUnknown) {
return false;
}
}
return true;
}

SortedDisjointIntervalList RoutingDimension::GetAllowedIntervalsInRange(
int64_t index, int64_t min_value, int64_t max_value) const {
SortedDisjointIntervalList allowed;
Expand Down
12 changes: 12 additions & 0 deletions ortools/constraint_solver/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
#include "ortools/constraint_solver/routing_index_manager.h"
#include "ortools/constraint_solver/routing_parameters.pb.h"
#include "ortools/constraint_solver/routing_types.h"
#include "ortools/constraint_solver/routing_utils.h"
#include "ortools/graph/graph.h"
#include "ortools/sat/theta_tree.h"
#include "ortools/util/bitset.h"
Expand Down Expand Up @@ -236,6 +237,8 @@ class PathsMetadata {
int GetPath(int64_t start_or_end_node) const {
return path_of_node_[start_or_end_node];
}
int NumPaths() const { return start_of_path_.size(); }
const std::vector<int64_t>& Paths() const { return path_of_node_; }
const std::vector<int64_t>& Starts() const { return start_of_path_; }
const std::vector<int64_t>& Ends() const { return end_of_path_; }

Expand Down Expand Up @@ -1750,6 +1753,11 @@ class RoutingModel {
DecisionBuilder* MakeSelfDependentDimensionFinalizer(
const RoutingDimension* dimension);

const PathsMetadata& GetPathsMetadata() const { return paths_metadata_; }
#ifndef SWIG
BinCapacities* GetBinCapacities() { return bin_capacities_.get(); }
#endif // SWIG

private:
/// Local search move operator usable in routing.
enum RoutingLocalSearchOperator {
Expand Down Expand Up @@ -2410,6 +2418,9 @@ class RoutingModel {
std::vector<std::unique_ptr<StateDependentTransitCallbackCache>>
state_dependent_transit_evaluators_cache_;

// Returns global BinCapacities state, may be nullptr.
std::unique_ptr<BinCapacities> bin_capacities_;

friend class RoutingDimension;
friend class RoutingModelInspector;
friend class ResourceGroup::Resource;
Expand Down Expand Up @@ -2963,6 +2974,7 @@ class RoutingDimension {
return model()->transit_evaluator_sign_[evaluator_index] ==
RoutingModel::kTransitEvaluatorSignPositiveOrZero;
}
bool AllTransitEvaluatorSignsAreUnknown() const;
RoutingModel::TransitEvaluatorSign GetTransitEvaluatorSign(
int vehicle) const {
const int evaluator_index = class_evaluators_[vehicle_to_class_[vehicle]];
Expand Down
2 changes: 1 addition & 1 deletion ortools/constraint_solver/routing_parameters.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
#include "google/protobuf/descriptor.h"
#include "google/protobuf/duration.pb.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
#include "ortools/base/logging.h"
#include "ortools/base/protoutil.h"
#include "ortools/base/types.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
#include "ortools/constraint_solver/routing_parameters.pb.h"
#include "ortools/constraint_solver/solver_parameters.pb.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/optional_boolean.pb.h"
Expand Down
10 changes: 7 additions & 3 deletions ortools/constraint_solver/routing_sat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

#include "absl/container/flat_hash_map.h"
#include "absl/time/time.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/routing.h"
Expand All @@ -34,6 +33,7 @@
#include "ortools/sat/integer.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/bitset.h"
#include "ortools/util/optional_boolean.pb.h"
#include "ortools/util/saturated_arithmetic.h"

Expand Down Expand Up @@ -758,13 +758,17 @@ ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(

// Set literals for vehicle performing node.
for (int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
const int routing_index = cp_node - 1;
// For starts and ends nodes vehicle_performs_node variables already set.
if (model.IsStart(cp_node - 1) || model.IsEnd(cp_node - 1)) continue;
if (model.IsStart(routing_index) || model.IsEnd(routing_index)) continue;
// Each node should be performed by 1 vehicle, or be unperformed.
// SUM(vehicle)(vehicle_performs_node[vehicle][cp_node]) + loop(cp_node) = 1
std::vector<std::pair<int, double>> var_coeffs;
for (int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
vehicle_performs_node[vehicle][cp_node] = AddVariable(cp_model, 0, 1);
vehicle_performs_node[vehicle][cp_node] =
model.VehicleVar(routing_index)->Contains(vehicle)
? AddVariable(cp_model, 0, 1)
: AddVariable(cp_model, 0, 0);
var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
}
var_coeffs.push_back({is_unperformed[cp_node], 1});
Expand Down
2 changes: 1 addition & 1 deletion ortools/constraint_solver/routing_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ void IntVarFilteredHeuristic::ResetSolution() {
SynchronizeFilters();
}

Assignment* const IntVarFilteredHeuristic::BuildSolution() {
Assignment* IntVarFilteredHeuristic::BuildSolution() {
// Initialize must be called before the state of the heuristic is changed, in
// particular before InitializeSolution() and BuildSolutionInternal().
Initialize();
Expand Down
8 changes: 1 addition & 7 deletions ortools/constraint_solver/routing_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include <deque>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
Expand All @@ -36,16 +35,11 @@
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
#include "ortools/base/adjustable_priority_queue.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/mathutil.h"
#include "ortools/base/types.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_index_manager.h"
#include "ortools/constraint_solver/routing_utils.h"
#include "ortools/util/bitset.h"

namespace operations_research {

Expand Down Expand Up @@ -182,7 +176,7 @@ class IntVarFilteredHeuristic {

/// Builds a solution. Returns the resulting assignment if a solution was
/// found, and nullptr otherwise.
Assignment* const BuildSolution();
Assignment* BuildSolution();

/// Returns statistics on search, number of decisions sent to filters, number
/// of decisions rejected by filters.
Expand Down
1 change: 1 addition & 0 deletions ortools/constraint_solver/routing_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class BinCapacities {
void AddDimension(
std::function<int64_t(int, int)> load_demand_of_item_for_bin,
std::vector<LoadLimit> load_limit_per_bin);
int NumDimensions() const { return load_demands_per_dimension_.size(); }

// Checks whether adding item(s) is feasible w.r.t. dimensions.
bool CheckAdditionFeasibility(int item, int bin) const;
Expand Down

0 comments on commit 8ce89a6

Please sign in to comment.