sync routing code; fix #3975

This commit is contained in:
Laurent Perron
2023-11-09 10:28:39 +01:00
parent 35684c95f4
commit 0557eae2fb
16 changed files with 257 additions and 259 deletions

View File

@@ -1683,20 +1683,26 @@ class LocalSearchState {
class Variable;
// Adds a variable to this state, return a handler to the new variable.
int AddVariable(int64_t initial_min, int64_t initial_max);
void ChangeInitialVariableBounds(int variable_index, int64_t min,
void ChangeRelaxedVariableBounds(int variable_index, int64_t min,
int64_t max);
// Makes an object with restricted operations on the variable identified by
// variable_index: only Relax, Tighten and read operations are available.
Variable MakeVariable(int variable_index);
void Commit();
void Revert();
bool StateIsValid() const { return state_is_valid_; }
bool StateIsFeasible() const {
return state_all_variable_bounds_are_correct_ &&
num_committed_empty_domains_ == 0;
}
private:
struct Bounds {
int64_t min;
int64_t max;
};
bool BoundsIntersectionIsEmpty(const Bounds& b1, const Bounds& b2) const {
return b1.max < b2.min || b2.max < b1.min;
}
void RelaxVariableBounds(int variable_index);
bool TightenVariableMin(int variable_index, int64_t value);
@@ -1705,11 +1711,17 @@ class LocalSearchState {
int64_t VariableMax(int variable_index) const;
// TODO(user): turn these into strong vectors.
std::vector<Bounds> initial_variable_bounds_;
std::vector<Bounds> relaxed_variable_bounds_;
std::vector<Bounds> variable_bounds_;
std::vector<std::pair<Bounds, int>> saved_variable_bounds_trail_;
std::vector<std::pair<Bounds, int>> trailed_variable_bounds_;
std::vector<bool> variable_is_relaxed_;
bool state_is_valid_ = true;
// True iff all variable have their variable_bounds_ min <= max.
bool state_all_variable_bounds_are_correct_ = true;
bool state_has_relaxed_variables_ = false;
// Number of variables v for which the intersection of
// variable_bounds_[v] and relaxed_variable_bounds_[v] is empty.
int num_committed_empty_domains_ = 0;
int trailed_num_committed_empty_domains_ = 0;
};
// A LocalSearchVariable can only be created by a LocalSearchState, then it is

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python3
# Copyright 2010-2022 Google LLC
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.

View File

@@ -12,35 +12,35 @@
// limitations under the License.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <numeric>
#include <optional>
#include <random>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/memory/memory.h"
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/random/distributions.h"
#include "absl/random/random.h"
#include "absl/strings/str_cat.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/hash.h"
#include "absl/strings/str_format.h"
#include "ortools/base/iterator_adaptors.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/base/types.h"
#include "ortools/base/timer.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/graph/hamiltonian_path.h"
#include "ortools/util/bitset.h"
#include "ortools/util/saturated_arithmetic.h"
ABSL_FLAG(int, cp_local_search_sync_frequency, 16,
@@ -319,9 +319,7 @@ class IncrementValue : public ChangeValue {
explicit IncrementValue(const std::vector<IntVar*>& vars)
: ChangeValue(vars) {}
~IncrementValue() override {}
int64_t ModifyValue(int64_t index, int64_t value) override {
return value + 1;
}
int64_t ModifyValue(int64_t, int64_t value) override { return value + 1; }
std::string DebugString() const override { return "IncrementValue"; }
};
@@ -333,9 +331,7 @@ class DecrementValue : public ChangeValue {
explicit DecrementValue(const std::vector<IntVar*>& vars)
: ChangeValue(vars) {}
~DecrementValue() override {}
int64_t ModifyValue(int64_t index, int64_t value) override {
return value - 1;
}
int64_t ModifyValue(int64_t, int64_t value) override { return value - 1; }
std::string DebugString() const override { return "DecrementValue"; }
};
@@ -1437,7 +1433,7 @@ class MakeChainInactiveOperator : public PathOperator {
}
protected:
bool OnSamePathAsPreviousBase(int64_t base_index) override {
bool OnSamePathAsPreviousBase(int64_t) override {
// Start and end of chain (defined by both base nodes) must be on the same
// path.
return true;
@@ -2162,9 +2158,7 @@ int64_t CompoundOperatorNoRestart(int size, int active_index,
: operator_index - active_index;
}
int64_t CompoundOperatorRestart(int active_index, int operator_index) {
return 0;
}
int64_t CompoundOperatorRestart(int, int) { return 0; }
} // namespace
LocalSearchOperator* Solver::ConcatenateOperators(
@@ -2633,12 +2627,10 @@ namespace {
class AcceptFilter : public LocalSearchFilter {
public:
std::string DebugString() const override { return "AcceptFilter"; }
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t obj_min, int64_t obj_max) override {
bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
return true;
}
void Synchronize(const Assignment* assignment,
const Assignment* delta) override {}
void Synchronize(const Assignment*, const Assignment*) override {}
};
} // namespace
@@ -2651,12 +2643,10 @@ namespace {
class RejectFilter : public LocalSearchFilter {
public:
std::string DebugString() const override { return "RejectFilter"; }
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t obj_min, int64_t obj_max) override {
bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
return false;
}
void Synchronize(const Assignment* assignment,
const Assignment* delta) override {}
void Synchronize(const Assignment*, const Assignment*) override {}
};
} // namespace
@@ -2829,12 +2819,10 @@ class PathStateFilter : public LocalSearchFilter {
PathStateFilter(std::unique_ptr<PathState> path_state,
const std::vector<IntVar*>& nexts);
void Relax(const Assignment* delta, const Assignment* deltadelta) override;
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t objective_min, int64_t objective_max) override {
bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
return true;
}
void Synchronize(const Assignment* delta,
const Assignment* deltadelta) override{};
void Synchronize(const Assignment*, const Assignment*) override{};
void Commit(const Assignment* assignment, const Assignment* delta) override;
void Revert() override;
void Reset() override;
@@ -2902,8 +2890,7 @@ PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
path_has_changed_.assign(path_state_->NumPaths(), false);
}
void PathStateFilter::Relax(const Assignment* delta,
const Assignment* deltadelta) {
void PathStateFilter::Relax(const Assignment* delta, const Assignment*) {
path_state_->Revert();
changed_arcs_.clear();
for (const IntVarElement& var_value : delta->IntVarContainer().elements()) {
@@ -3452,13 +3439,11 @@ class DimensionFilter : public LocalSearchFilter {
: checker_(std::move(checker)),
name_(absl::StrCat("DimensionFilter(", dimension_name, ")")) {}
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t objective_min, int64_t objective_max) override {
bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
return checker_->Check();
}
void Synchronize(const Assignment* assignment,
const Assignment* delta) override {
void Synchronize(const Assignment*, const Assignment*) override {
checker_->Commit();
}
@@ -3487,16 +3472,13 @@ class VariableDomainFilter : public LocalSearchFilter {
~VariableDomainFilter() override {}
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t objective_min, int64_t objective_max) override;
void Synchronize(const Assignment* assignment,
const Assignment* delta) override {}
void Synchronize(const Assignment*, const Assignment*) override {}
std::string DebugString() const override { return "VariableDomainFilter"; }
};
bool VariableDomainFilter::Accept(const Assignment* delta,
const Assignment* deltadelta,
int64_t objective_min,
int64_t objective_max) {
bool VariableDomainFilter::Accept(const Assignment* delta, const Assignment*,
int64_t, int64_t) {
const Assignment::IntContainer& container = delta->IntVarContainer();
const int size = container.Size();
for (int i = 0; i < size; ++i) {
@@ -3642,7 +3624,7 @@ class SumObjectiveFilter : public IntVarLocalSearchFilter {
bool incremental_;
private:
void OnSynchronize(const Assignment* delta) override {
void OnSynchronize(const Assignment*) override {
synchronized_sum_ = 0;
for (int i = 0; i < primary_vars_size_; ++i) {
const int64_t cost = CostOfSynchronizedVariable(i);
@@ -3779,14 +3761,14 @@ IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
Solver::LocalSearchFilterBound filter_enum) {
switch (filter_enum) {
case Solver::LE: {
auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
auto filter = [](int64_t value, int64_t, int64_t max_value) {
return value <= max_value;
};
return RevAlloc(new BinaryObjectiveFilter<decltype(filter)>(
vars, std::move(values), std::move(filter)));
}
case Solver::GE: {
auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
auto filter = [](int64_t value, int64_t min_value, int64_t) {
return value >= min_value;
};
return RevAlloc(new BinaryObjectiveFilter<decltype(filter)>(
@@ -3812,14 +3794,14 @@ IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
Solver::LocalSearchFilterBound filter_enum) {
switch (filter_enum) {
case Solver::LE: {
auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
auto filter = [](int64_t value, int64_t, int64_t max_value) {
return value <= max_value;
};
return RevAlloc(new TernaryObjectiveFilter<decltype(filter)>(
vars, secondary_vars, std::move(values), std::move(filter)));
}
case Solver::GE: {
auto filter = [](int64_t value, int64_t min_value, int64_t max_value) {
auto filter = [](int64_t value, int64_t min_value, int64_t) {
return value >= min_value;
};
return RevAlloc(new TernaryObjectiveFilter<decltype(filter)>(
@@ -3840,9 +3822,9 @@ IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
}
int LocalSearchState::AddVariable(int64_t initial_min, int64_t initial_max) {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK_LE(initial_min, initial_max);
initial_variable_bounds_.push_back({initial_min, initial_max});
relaxed_variable_bounds_.push_back({initial_min, initial_max});
variable_bounds_.push_back({initial_min, initial_max});
variable_is_relaxed_.push_back(false);
return variable_bounds_.size() - 1;
@@ -3853,80 +3835,103 @@ LocalSearchState::Variable LocalSearchState::MakeVariable(int variable_index) {
}
void LocalSearchState::RelaxVariableBounds(int variable_index) {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
if (!state_has_relaxed_variables_) {
trailed_num_committed_empty_domains_ = num_committed_empty_domains_;
}
state_has_relaxed_variables_ = true;
if (!variable_is_relaxed_[variable_index]) {
variable_is_relaxed_[variable_index] = true;
saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
variable_index);
variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
trailed_variable_bounds_.emplace_back(variable_bounds_[variable_index],
variable_index);
if (BoundsIntersectionIsEmpty(relaxed_variable_bounds_[variable_index],
variable_bounds_[variable_index])) {
DCHECK_GT(num_committed_empty_domains_, 0);
--num_committed_empty_domains_;
}
variable_bounds_[variable_index] = relaxed_variable_bounds_[variable_index];
}
}
int64_t LocalSearchState::VariableMin(int variable_index) const {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
return variable_bounds_[variable_index].min;
}
int64_t LocalSearchState::VariableMax(int variable_index) const {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
return variable_bounds_[variable_index].max;
}
bool LocalSearchState::TightenVariableMin(int variable_index,
int64_t min_value) {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK(variable_is_relaxed_[variable_index]);
DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
Bounds& bounds = variable_bounds_[variable_index];
if (bounds.max < min_value) {
state_is_valid_ = false;
state_all_variable_bounds_are_correct_ = false;
}
bounds.min = std::max(bounds.min, min_value);
return state_is_valid_;
return state_all_variable_bounds_are_correct_;
}
bool LocalSearchState::TightenVariableMax(int variable_index,
int64_t max_value) {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK(variable_is_relaxed_[variable_index]);
DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
Bounds& bounds = variable_bounds_[variable_index];
if (bounds.min > max_value) {
state_is_valid_ = false;
state_all_variable_bounds_are_correct_ = false;
}
bounds.max = std::min(bounds.max, max_value);
return state_is_valid_;
return state_all_variable_bounds_are_correct_;
}
void LocalSearchState::ChangeInitialVariableBounds(int variable_index,
void LocalSearchState::ChangeRelaxedVariableBounds(int variable_index,
int64_t min, int64_t max) {
DCHECK(state_is_valid_);
DCHECK(state_all_variable_bounds_are_correct_);
DCHECK_GE(variable_index, 0);
DCHECK_LT(variable_index, variable_bounds_.size());
DCHECK(!variable_is_relaxed_[variable_index]);
initial_variable_bounds_[variable_index] = {min, max};
const bool domain_was_empty =
BoundsIntersectionIsEmpty(relaxed_variable_bounds_[variable_index],
variable_bounds_[variable_index]);
relaxed_variable_bounds_[variable_index] = {min, max};
const bool domain_is_empty =
BoundsIntersectionIsEmpty({min, max}, variable_bounds_[variable_index]);
if (!domain_was_empty && domain_is_empty) {
num_committed_empty_domains_++;
} else if (domain_was_empty && !domain_is_empty) {
num_committed_empty_domains_--;
}
}
// TODO(user): When the class has more users, find a threshold ratio of
// saved/total variables under which a sparse clear would be more efficient
// for both Commit() and Revert().
void LocalSearchState::Commit() {
DCHECK(state_is_valid_);
saved_variable_bounds_trail_.clear();
DCHECK(StateIsFeasible());
state_has_relaxed_variables_ = false;
trailed_variable_bounds_.clear();
variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
}
void LocalSearchState::Revert() {
for (const auto& bounds_index : saved_variable_bounds_trail_) {
for (const auto& bounds_index : trailed_variable_bounds_) {
DCHECK(variable_is_relaxed_[bounds_index.second]);
variable_bounds_[bounds_index.second] = bounds_index.first;
}
saved_variable_bounds_trail_.clear();
trailed_variable_bounds_.clear();
state_has_relaxed_variables_ = false;
variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
state_is_valid_ = true;
state_all_variable_bounds_are_correct_ = true;
num_committed_empty_domains_ = trailed_num_committed_empty_domains_;
}
// ----- LocalSearchProfiler -----
@@ -4047,7 +4052,7 @@ class LocalSearchProfiler : public LocalSearchMonitor {
std::string overview;
size_t max_name_size = 0;
ParseFirstSolutionStatistics(
[&max_name_size](const std::string& name, double duration_seconds) {
[&max_name_size](const std::string& name, double) {
max_name_size = std::max(max_name_size, name.length());
});
if (max_name_size > 0) {
@@ -4062,13 +4067,11 @@ class LocalSearchProfiler : public LocalSearchMonitor {
});
}
max_name_size = 0;
ParseLocalSearchOperatorStatistics(
[&max_name_size](const std::string& name, int64_t num_neighbors,
int64_t num_filtered_neighbors,
int64_t num_accepted_neighbors,
double duration_seconds) {
max_name_size = std::max(max_name_size, name.length());
});
ParseLocalSearchOperatorStatistics([&max_name_size](const std::string& name,
int64_t, int64_t,
int64_t, double) {
max_name_size = std::max(max_name_size, name.length());
});
if (max_name_size > 0) {
absl::StrAppendFormat(
&overview,
@@ -4097,9 +4100,8 @@ class LocalSearchProfiler : public LocalSearchMonitor {
}
max_name_size = 0;
ParseLocalSearchFilterStatistics(
[&max_name_size](const std::string& context, const std::string& name,
int64_t num_calls, int64_t num_rejects,
double duration_seconds) {
[&max_name_size](const std::string&, const std::string& name, int64_t,
int64_t, double) {
max_name_size = std::max(max_name_size, name.length());
});
if (max_name_size > 0) {
@@ -4152,20 +4154,19 @@ class LocalSearchProfiler : public LocalSearchMonitor {
}
}
void EndMakeNextNeighbor(const LocalSearchOperator* op, bool neighbor_found,
const Assignment* delta,
const Assignment* deltadelta) override {
const Assignment*, const Assignment*) override {
if (neighbor_found) {
operator_stats_[op->Self()].neighbors++;
}
}
void BeginFilterNeighbor(const LocalSearchOperator* op) override {}
void BeginFilterNeighbor(const LocalSearchOperator*) override {}
void EndFilterNeighbor(const LocalSearchOperator* op,
bool neighbor_found) override {
if (neighbor_found) {
operator_stats_[op->Self()].filtered_neighbors++;
}
}
void BeginAcceptNeighbor(const LocalSearchOperator* op) override {}
void BeginAcceptNeighbor(const LocalSearchOperator*) override {}
void EndAcceptNeighbor(const LocalSearchOperator* op,
bool neighbor_found) override {
if (neighbor_found) {
@@ -4909,7 +4910,7 @@ void NestedSolveDecision::Apply(Solver* const solver) {
}
}
void NestedSolveDecision::Refute(Solver* const solver) {}
void NestedSolveDecision::Refute(Solver* const) {}
} // namespace
// ----- Local search decision builder -----
@@ -5202,7 +5203,7 @@ class DefaultSolutionPool : public SolutionPool {
assignment->CopyIntersection(reference_assignment_.get());
}
bool SyncNeeded(Assignment* const local_assignment) override { return false; }
bool SyncNeeded(Assignment* const) override { return false; }
std::string DebugString() const override { return "DefaultSolutionPool"; }

View File

@@ -33,6 +33,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/logging.h"
#include "ortools/base/mathutil.h"
@@ -2192,7 +2193,7 @@ class CumulativeConstraint : public Constraint {
CumulativeConstraint(Solver* const s,
const std::vector<IntervalVar*>& intervals,
const std::vector<int64_t>& demands,
IntVar* const capacity, const std::string& name)
IntVar* const capacity, absl::string_view name)
: Constraint(s),
capacity_(capacity),
intervals_(intervals),
@@ -2388,7 +2389,7 @@ class VariableDemandCumulativeConstraint : public Constraint {
const std::vector<IntervalVar*>& intervals,
const std::vector<IntVar*>& demands,
IntVar* const capacity,
const std::string& name)
absl::string_view name)
: Constraint(s),
capacity_(capacity),
intervals_(intervals),

View File

@@ -33,9 +33,11 @@
#include "absl/container/flat_hash_set.h"
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/base/small_map.h"
#include "ortools/base/strong_vector.h"
#include "ortools/base/types.h"
@@ -48,7 +50,6 @@
#include "ortools/util/bitset.h"
#include "ortools/util/piecewise_linear_function.h"
#include "ortools/util/saturated_arithmetic.h"
#include "ortools/util/sorted_interval_list.h"
ABSL_FLAG(bool, routing_strong_debug_checks, false,
"Run stronger checks in debug; these stronger tests might change "
@@ -1089,18 +1090,6 @@ class PathCumulFilter : public BasePathFilter {
(has_breaks || filter_objective_cost_);
}
bool FilterDimensionForbiddenIntervals() const {
for (const SortedDisjointIntervalList& intervals :
dimension_.forbidden_intervals()) {
// TODO(user): Change the following test to check intervals within
// the domain of the corresponding variables.
if (intervals.NumIntervals() > 0) {
return true;
}
}
return false;
}
int64_t GetCumulPiecewiseLinearCost(int64_t node, int64_t cumul_value) const;
bool FilterCumulSoftLowerBounds() const {
@@ -2130,7 +2119,7 @@ void AppendLightWeightDimensionFilters(
dimension->GetUnaryTransitEvaluator(vehicle);
if (unary_evaluator != nullptr) {
transits[vehicle_class] = [&unary_evaluator, dimension, num_slacks](
int64_t node, int64_t next) -> Interval {
int64_t node, int64_t) -> Interval {
if (node >= num_slacks) return {0, 0};
const int64_t min_transit = unary_evaluator(node);
const int64_t max_transit =
@@ -2476,13 +2465,15 @@ class VehicleVarFilter : public BasePathFilter {
std::vector<int64_t> start_to_vehicle_;
std::vector<IntVar*> vehicle_vars_;
const int64_t unconstrained_vehicle_var_domain_size_;
SparseBitset<int> touched_;
};
VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model)
: BasePathFilter(routing_model.Nexts(),
routing_model.Size() + routing_model.vehicles()),
vehicle_vars_(routing_model.VehicleVars()),
unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
unconstrained_vehicle_var_domain_size_(routing_model.vehicles()),
touched_(routing_model.Nexts().size()) {
start_to_vehicle_.resize(Size(), -1);
for (int i = 0; i < routing_model.vehicles(); ++i) {
start_to_vehicle_[routing_model.Start(i)] = i;
@@ -2491,12 +2482,14 @@ VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model)
bool VehicleVarFilter::AcceptPath(int64_t path_start, int64_t chain_start,
int64_t chain_end) {
touched_.SparseClearAll();
const int64_t vehicle = start_to_vehicle_[path_start];
int64_t node = chain_start;
while (node != chain_end) {
if (!vehicle_vars_[node]->Contains(vehicle)) {
if (touched_[node] || !vehicle_vars_[node]->Contains(vehicle)) {
return false;
}
touched_.Set(node);
node = GetNext(node);
}
return vehicle_vars_[node]->Contains(vehicle);
@@ -3165,20 +3158,19 @@ class PathEnergyCostFilter : public LocalSearchFilter {
public:
std::string DebugString() const override { return name_; }
PathEnergyCostFilter(std::unique_ptr<PathEnergyCostChecker> checker,
const std::string& energy_name)
absl::string_view energy_name)
: checker_(std::move(checker)),
name_(absl::StrCat("PathEnergyCostFilter(", energy_name, ")")) {}
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64_t objective_min, int64_t objective_max) override {
bool Accept(const Assignment*, const Assignment*, int64_t objective_min,
int64_t objective_max) override {
if (objective_max > kint64max / 2) return true;
if (!checker_->Check()) return false;
const int64_t cost = checker_->AcceptedCost();
return objective_min <= cost && cost <= objective_max;
}
void Synchronize(const Assignment* assignment,
const Assignment* delta) override {
void Synchronize(const Assignment*, const Assignment*) override {
checker_->Commit();
}

View File

@@ -14,18 +14,19 @@
#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#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_lp_scheduling.h"
#include "ortools/constraint_solver/routing_parameters.pb.h"
#include "ortools/constraint_solver/routing_types.h"
#include "ortools/util/bitset.h"
namespace operations_research {
@@ -170,14 +171,11 @@ class BasePathFilter : public IntVarLocalSearchFilter {
virtual bool DisableFiltering() const { return false; }
virtual void OnBeforeSynchronizePaths() {}
virtual void OnAfterSynchronizePaths() {}
virtual void OnSynchronizePathFromStart(int64_t start) {}
virtual void OnSynchronizePathFromStart(int64_t) {}
virtual bool InitializeAcceptPath() { return true; }
virtual bool AcceptPath(int64_t path_start, int64_t chain_start,
int64_t chain_end) = 0;
virtual bool FinalizeAcceptPath(int64_t objective_min,
int64_t objective_max) {
return true;
}
virtual bool FinalizeAcceptPath(int64_t, int64_t) { return true; }
/// Detects path starts, used to track which node belongs to which path.
void ComputePathStarts(std::vector<int64_t>* path_starts,
std::vector<int>* index_to_path);

View File

@@ -101,9 +101,6 @@ class RoutingIndexManager {
/// complete.
int num_unique_depots() const { return num_unique_depots_; }
std::vector<NodeIndex> GetIndexToNodeMap() const { return index_to_node_; }
absl::StrongVector<NodeIndex, int64_t> GetNodeToIndexMap() const {
return node_to_index_;
}
private:
void Initialize(

View File

@@ -32,6 +32,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
@@ -2873,7 +2874,7 @@ std::string VariablesToString(
absl::flat_hash_map<std::string, std::vector<int>>& variable_instances,
absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>&
variable_childs,
const sat::CpSolverResponse& response_, const std::string& variable,
const sat::CpSolverResponse& response_, absl::string_view variable,
std::string prefix = "") {
if (variable.empty()) {
std::string s = "";
@@ -2888,7 +2889,7 @@ std::string VariablesToString(
}
const auto& instances = variable_instances[variable];
std::string variable_display = variable;
std::string variable_display(variable);
std::size_t bracket_pos = variable.find_last_of(')');
if (bracket_pos != std::string::npos) {
variable_display = variable.substr(bracket_pos + 1);

View File

@@ -19,11 +19,13 @@
#include <utility>
#include <vector>
#include "absl/log/check.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_types.h"
#include "ortools/constraint_solver/routing_utils.h"
#include "ortools/util/saturated_arithmetic.h"
namespace operations_research {
@@ -1001,20 +1003,11 @@ bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() {
return false;
}
RelocateSubtrip::RelocateSubtrip(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars,
std::function<int(int64_t)> start_empty_path_class,
std::function<const std::vector<int>&(int, int)> get_neighbors,
const std::vector<PickupDeliveryPair>& pairs)
: PathOperator(
vars, secondary_vars,
/*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
/*skip_locally_optimal_paths=*/true, /*accept_path_end_base=*/false,
std::move(start_empty_path_class), std::move(get_neighbors)) {
is_pickup_node_.resize(number_of_nexts_, false);
is_delivery_node_.resize(number_of_nexts_, false);
pair_of_node_.resize(number_of_nexts_, -1);
PickupAndDeliveryData::PickupAndDeliveryData(
int num_nodes, const std::vector<PickupDeliveryPair>& pairs)
: is_pickup_node_(num_nodes, false),
is_delivery_node_(num_nodes, false),
pair_of_node_(num_nodes, -1) {
for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
for (const int node : pairs[pair_index].pickup_alternatives) {
is_pickup_node_[node] = true;
@@ -1025,7 +1018,27 @@ RelocateSubtrip::RelocateSubtrip(
pair_of_node_[node] = pair_index;
}
}
opened_pairs_bitset_.resize(pairs.size(), false);
}
RelocateSubtrip::RelocateSubtrip(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars,
std::function<int(int64_t)> start_empty_path_class,
std::function<const std::vector<int>&(int, int)> get_neighbors,
const std::vector<PickupDeliveryPair>& pairs)
: PathOperator(vars, secondary_vars,
/*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
/*skip_locally_optimal_paths=*/true,
/*accept_path_end_base=*/false,
std::move(start_empty_path_class), std::move(get_neighbors)),
pd_data_(number_of_nexts_, pairs) {
opened_pairs_set_.resize(pairs.size(), false);
}
void RelocateSubtrip::SetPath(const std::vector<int64_t>& path, int path_id) {
for (int i = 1; i < path.size(); ++i) {
SetNext(path[i - 1], path[i], path_id);
}
}
bool RelocateSubtrip::RelocateSubTripFromPickup(const int64_t chain_first_node,
@@ -1041,21 +1054,21 @@ bool RelocateSubtrip::RelocateSubTripFromPickup(const int64_t chain_first_node,
int current = chain_first_node;
do {
if (current == insertion_node) {
// opened_pairs_bitset_ must be all false when we leave this function.
opened_pairs_bitset_.assign(opened_pairs_bitset_.size(), false);
// opened_pairs_set_ must be all false when we leave this function.
opened_pairs_set_.assign(opened_pairs_set_.size(), false);
return false;
}
const int pair = pair_of_node_[current];
if (IsDeliveryNode(current) && !opened_pairs_bitset_[pair]) {
const int pair = pd_data_.GetPairOfNode(current);
if (pd_data_.IsDeliveryNode(current) && !opened_pairs_set_[pair]) {
rejected_nodes_.push_back(current);
} else {
subtrip_nodes_.push_back(current);
if (IsPickupNode(current)) {
if (pd_data_.IsPickupNode(current)) {
++num_opened_pairs;
opened_pairs_bitset_[pair] = true;
} else if (IsDeliveryNode(current)) {
opened_pairs_set_[pair] = true;
} else if (pd_data_.IsDeliveryNode(current)) {
--num_opened_pairs;
opened_pairs_bitset_[pair] = false;
opened_pairs_set_[pair] = false;
}
}
current = Next(current);
@@ -1065,14 +1078,8 @@ bool RelocateSubtrip::RelocateSubTripFromPickup(const int64_t chain_first_node,
subtrip_nodes_.push_back(Next(insertion_node));
// Set new paths.
const int64_t rejected_path = Path(chain_first_node);
for (int i = 1; i < rejected_nodes_.size(); ++i) {
SetNext(rejected_nodes_[i - 1], rejected_nodes_[i], rejected_path);
}
const int64_t insertion_path = Path(insertion_node);
for (int i = 1; i < subtrip_nodes_.size(); ++i) {
SetNext(subtrip_nodes_[i - 1], subtrip_nodes_[i], insertion_path);
}
SetPath(rejected_nodes_, Path(chain_first_node));
SetPath(subtrip_nodes_, Path(insertion_node));
return true;
}
@@ -1080,8 +1087,8 @@ bool RelocateSubtrip::RelocateSubTripFromDelivery(
const int64_t chain_last_node, const int64_t insertion_node) {
if (IsPathEnd(insertion_node)) return false;
// opened_pairs_bitset_ should be all false.
DCHECK(std::none_of(opened_pairs_bitset_.begin(), opened_pairs_bitset_.end(),
// opened_pairs_set_ should be all false.
DCHECK(std::none_of(opened_pairs_set_.begin(), opened_pairs_set_.end(),
[](bool value) { return value; }));
int num_opened_pairs = 0;
// Split chain into subtrip and rejected nodes. Store nodes in reverse order.
@@ -1090,20 +1097,20 @@ bool RelocateSubtrip::RelocateSubTripFromDelivery(
int current = chain_last_node;
do {
if (current == insertion_node) {
opened_pairs_bitset_.assign(opened_pairs_bitset_.size(), false);
opened_pairs_set_.assign(opened_pairs_set_.size(), false);
return false;
}
const int pair = pair_of_node_[current];
if (IsPickupNode(current) && !opened_pairs_bitset_[pair]) {
const int pair = pd_data_.GetPairOfNode(current);
if (pd_data_.IsPickupNode(current) && !opened_pairs_set_[pair]) {
rejected_nodes_.push_back(current);
} else {
subtrip_nodes_.push_back(current);
if (IsDeliveryNode(current)) {
if (pd_data_.IsDeliveryNode(current)) {
++num_opened_pairs;
opened_pairs_bitset_[pair] = true;
} else if (IsPickupNode(current)) {
opened_pairs_set_[pair] = true;
} else if (pd_data_.IsPickupNode(current)) {
--num_opened_pairs;
opened_pairs_bitset_[pair] = false;
opened_pairs_set_[pair] = false;
}
}
current = Prev(current);
@@ -1119,23 +1126,17 @@ bool RelocateSubtrip::RelocateSubTripFromDelivery(
std::reverse(subtrip_nodes_.begin(), subtrip_nodes_.end());
// Set new paths.
const int64_t rejected_path = Path(chain_last_node);
for (int i = 1; i < rejected_nodes_.size(); ++i) {
SetNext(rejected_nodes_[i - 1], rejected_nodes_[i], rejected_path);
}
const int64_t insertion_path = Path(insertion_node);
for (int i = 1; i < subtrip_nodes_.size(); ++i) {
SetNext(subtrip_nodes_[i - 1], subtrip_nodes_[i], insertion_path);
}
SetPath(rejected_nodes_, Path(chain_last_node));
SetPath(subtrip_nodes_, Path(insertion_node));
return true;
}
bool RelocateSubtrip::MakeNeighbor() {
const auto do_move = [this](int64_t node, int64_t insertion_node) {
if (IsInactive(node)) return false;
if (IsPickupNode(node)) {
if (pd_data_.IsPickupNode(node)) {
return RelocateSubTripFromPickup(node, insertion_node);
} else if (IsDeliveryNode(node)) {
} else if (pd_data_.IsDeliveryNode(node)) {
return RelocateSubTripFromDelivery(node, insertion_node);
} else {
return false;
@@ -1153,24 +1154,12 @@ ExchangeSubtrip::ExchangeSubtrip(
std::function<int(int64_t)> start_empty_path_class,
std::function<const std::vector<int>&(int, int)> get_neighbors,
const std::vector<PickupDeliveryPair>& pairs)
: PathOperator(
vars, secondary_vars,
/*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
/*skip_locally_optimal_paths=*/true, /*accept_path_end_base=*/false,
std::move(start_empty_path_class), std::move(get_neighbors)) {
is_pickup_node_.resize(number_of_nexts_, false);
is_delivery_node_.resize(number_of_nexts_, false);
pair_of_node_.resize(number_of_nexts_, -1);
for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
for (const int node : pairs[pair_index].pickup_alternatives) {
is_pickup_node_[node] = true;
pair_of_node_[node] = pair_index;
}
for (const int node : pairs[pair_index].delivery_alternatives) {
is_delivery_node_[node] = true;
pair_of_node_[node] = pair_index;
}
}
: PathOperator(vars, secondary_vars,
/*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
/*skip_locally_optimal_paths=*/true,
/*accept_path_end_base=*/false,
std::move(start_empty_path_class), std::move(get_neighbors)),
pd_data_(number_of_nexts_, pairs) {
opened_pairs_set_.resize(pairs.size(), false);
}
@@ -1193,11 +1182,12 @@ bool ExchangeSubtrip::MakeNeighbor() {
const int64_t node = BaseNode(0);
const int64_t neighbor = GetNeighborForBaseNode(0);
if (IsInactive(neighbor)) return false;
if (IsDeliveryNode(node) && IsDeliveryNode(Prev(neighbor))) {
if (pd_data_.IsDeliveryNode(node) &&
pd_data_.IsDeliveryNode(Prev(neighbor))) {
node0 = node;
node1 = Prev(neighbor);
} else if (IsPickupNode(neighbor) && !IsPathEnd(Next(node)) &&
IsPickupNode(Next(node))) {
} else if (pd_data_.IsPickupNode(neighbor) && !IsPathEnd(Next(node)) &&
pd_data_.IsPickupNode(Next(node))) {
node0 = Next(node);
node1 = neighbor;
} else {
@@ -1208,8 +1198,8 @@ bool ExchangeSubtrip::MakeNeighbor() {
node1 = BaseNode(1);
}
if (pair_of_node_[node0] == -1) return false;
if (pair_of_node_[node1] == -1) return false;
if (pd_data_.GetPairOfNode(node0) == -1) return false;
if (pd_data_.GetPairOfNode(node1) == -1) return false;
// Break symmetry: a move generated from (node0, node1) is the
// same as from (node1, node0): no need to do it twice.
if (node0 >= node1) return false;
@@ -1240,12 +1230,12 @@ bool ExchangeSubtrip::MakeNeighbor() {
const bool concatenated01 = last0 == subtrip1_.front();
const bool concatenated10 = last1 == subtrip0_.front();
if (IsDeliveryNode(node0)) std::swap(subtrip1_, rejects0_);
if (pd_data_.IsDeliveryNode(node0)) std::swap(subtrip1_, rejects0_);
path0_.insert(path0_.end(), subtrip1_.begin(), subtrip1_.end());
path0_.insert(path0_.end(), rejects0_.begin(), rejects0_.end());
path0_.push_back(last0);
if (IsDeliveryNode(node1)) std::swap(subtrip0_, rejects1_);
if (pd_data_.IsDeliveryNode(node1)) std::swap(subtrip0_, rejects1_);
path1_.insert(path1_.end(), subtrip0_.begin(), subtrip0_.end());
path1_.insert(path1_.end(), rejects1_.begin(), rejects1_.end());
path1_.push_back(last1);
@@ -1272,20 +1262,21 @@ bool ExchangeSubtrip::ExtractChainsAndCheckCanonical(
int64_t base_node, std::vector<int64_t>* rejects,
std::vector<int64_t>* subtrip) {
const bool extracted =
IsPickupNode(base_node)
pd_data_.IsPickupNode(base_node)
? ExtractChainsFromPickup(base_node, rejects, subtrip)
: ExtractChainsFromDelivery(base_node, rejects, subtrip);
if (!extracted) return false;
// Check canonicality.
return !IsDeliveryNode(base_node) ||
pair_of_node_[subtrip->front()] != pair_of_node_[subtrip->back()] ||
return !pd_data_.IsDeliveryNode(base_node) ||
pd_data_.GetPairOfNode(subtrip->front()) !=
pd_data_.GetPairOfNode(subtrip->back()) ||
!rejects->empty();
}
bool ExchangeSubtrip::ExtractChainsFromPickup(int64_t base_node,
std::vector<int64_t>* rejects,
std::vector<int64_t>* subtrip) {
DCHECK(IsPickupNode(base_node));
DCHECK(pd_data_.IsPickupNode(base_node));
DCHECK(rejects->empty());
DCHECK(subtrip->empty());
// Iterate from base_node forwards while maintaining the set of opened pairs.
@@ -1294,15 +1285,15 @@ bool ExchangeSubtrip::ExtractChainsFromPickup(int64_t base_node,
int num_opened_pairs = 0;
int current = base_node;
do {
const int pair = pair_of_node_[current];
if (IsDeliveryNode(current) && !opened_pairs_set_[pair]) {
const int pair = pd_data_.GetPairOfNode(current);
if (pd_data_.IsDeliveryNode(current) && !opened_pairs_set_[pair]) {
rejects->push_back(current);
} else {
subtrip->push_back(current);
if (IsPickupNode(current)) {
if (pd_data_.IsPickupNode(current)) {
++num_opened_pairs;
opened_pairs_set_[pair] = true;
} else if (IsDeliveryNode(current)) {
} else if (pd_data_.IsDeliveryNode(current)) {
--num_opened_pairs;
opened_pairs_set_[pair] = false;
}
@@ -1315,7 +1306,7 @@ bool ExchangeSubtrip::ExtractChainsFromPickup(int64_t base_node,
bool ExchangeSubtrip::ExtractChainsFromDelivery(int64_t base_node,
std::vector<int64_t>* rejects,
std::vector<int64_t>* subtrip) {
DCHECK(IsDeliveryNode(base_node));
DCHECK(pd_data_.IsDeliveryNode(base_node));
DCHECK(rejects->empty());
DCHECK(subtrip->empty());
// Iterate from base_node backwards while maintaining the set of opened pairs.
@@ -1324,15 +1315,15 @@ bool ExchangeSubtrip::ExtractChainsFromDelivery(int64_t base_node,
int num_opened_pairs = 0;
int current = base_node;
do {
const int pair = pair_of_node_[current];
if (IsPickupNode(current) && !opened_pairs_set_[pair]) {
const int pair = pd_data_.GetPairOfNode(current);
if (pd_data_.IsPickupNode(current) && !opened_pairs_set_[pair]) {
rejects->push_back(current);
} else {
subtrip->push_back(current);
if (IsDeliveryNode(current)) {
if (pd_data_.IsDeliveryNode(current)) {
++num_opened_pairs;
opened_pairs_set_[pair] = true;
} else if (IsPickupNode(current)) {
} else if (pd_data_.IsPickupNode(current)) {
--num_opened_pairs;
opened_pairs_set_[pair] = false;
}

View File

@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
#include "absl/log/check.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/constraint_solver/routing_types.h"
@@ -613,6 +614,30 @@ bool PairNodeSwapActiveOperator<swap_first>::MakeNeighbor() {
}
}
/// A utility class to maintain pickup and delivery information of nodes.
class PickupAndDeliveryData {
public:
PickupAndDeliveryData(int num_nodes,
const std::vector<PickupDeliveryPair>& pairs);
bool IsPickupNode(int64_t node) const {
DCHECK_LT(node, is_pickup_node_.size());
return is_pickup_node_[node];
}
bool IsDeliveryNode(int64_t node) const {
DCHECK_LT(node, is_delivery_node_.size());
return is_delivery_node_[node];
}
int GetPairOfNode(int64_t node) const {
DCHECK_LT(node, pair_of_node_.size());
return pair_of_node_[node];
}
private:
std::vector<bool> is_pickup_node_;
std::vector<bool> is_delivery_node_;
std::vector<int> pair_of_node_;
};
/// Tries to move subtrips after an insertion node.
/// A subtrip is a subsequence that contains only matched pickup and delivery
/// nodes, or pickup-only nodes, i.e. it cannot contain a pickup without a
@@ -649,24 +674,13 @@ class RelocateSubtrip : public PathOperator {
/// Relocates the subtrip ending at chain_first_node. It must be a delivery.
bool RelocateSubTripFromDelivery(int64_t chain_last_node,
int64_t insertion_node);
bool IsPickupNode(int64_t node) const {
DCHECK_LT(node, is_pickup_node_.size());
DCHECK_GE(node, 0);
return is_pickup_node_[node];
}
bool IsDeliveryNode(int64_t node) const {
DCHECK_LT(node, is_delivery_node_.size());
DCHECK_GE(node, 0);
return is_delivery_node_[node];
}
void SetPath(const std::vector<int64_t>& path, int path_id);
std::vector<bool> is_pickup_node_;
std::vector<bool> is_delivery_node_;
std::vector<int> pair_of_node_;
const PickupAndDeliveryData pd_data_;
// Represents the set of pairs that have been opened during a call to
// MakeNeighbor(). This vector must be all false before and after calling
// RelocateSubTripFromPickup() and RelocateSubTripFromDelivery().
std::vector<bool> opened_pairs_bitset_;
std::vector<bool> opened_pairs_set_;
std::vector<int64_t> rejected_nodes_;
std::vector<int64_t> subtrip_nodes_;
@@ -718,21 +732,8 @@ class ExchangeSubtrip : public PathOperator {
std::vector<int64_t>* rejects,
std::vector<int64_t>* subtrip);
void SetPath(const std::vector<int64_t>& path, int path_id);
bool IsPickupNode(int64_t node) const {
DCHECK_LT(node, is_pickup_node_.size());
DCHECK_GE(node, 0);
return is_pickup_node_[node];
}
bool IsDeliveryNode(int64_t node) const {
DCHECK_LT(node, is_delivery_node_.size());
DCHECK_GE(node, 0);
return is_delivery_node_[node];
}
// Precompute some information about nodes.
std::vector<bool> is_pickup_node_;
std::vector<bool> is_delivery_node_;
std::vector<int> pair_of_node_;
const PickupAndDeliveryData pd_data_;
// Represents the set of opened pairs during ExtractChainsFromXXX().
std::vector<bool> opened_pairs_set_;
// Keep internal structures under hand to avoid reallocation.

View File

@@ -16,13 +16,14 @@
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <ostream>
#include <utility>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/log/check.h"
#include "absl/time/time.h"
#include "ortools/base/map_util.h"
#include "ortools/constraint_solver/constraint_solver.h"
@@ -37,6 +38,7 @@
#include "ortools/util/bitset.h"
#include "ortools/util/optional_boolean.pb.h"
#include "ortools/util/saturated_arithmetic.h"
#include "ortools/util/time_limit.h"
namespace operations_research {
namespace sat {
@@ -111,7 +113,8 @@ struct Arc {
}
};
using ArcVarMap = std::map<Arc, int>; // needs to be stable when iterating
using ArcVarMap =
absl::btree_map<Arc, int>; // needs to be stable when iterating
void AddSoftCumulBounds(const RoutingDimension* dimension, int index, int cumul,
int64_t cumul_min, int64_t cumul_max,
@@ -290,7 +293,7 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
: model.UnperformedPenalty(tail);
if (cost == std::numeric_limits<int64_t>::max()) continue;
const Arc arc = {tail_index, head_index};
if (gtl::ContainsKey(arc_vars, arc)) continue;
if (arc_vars.contains(arc)) continue;
const int index = AddVariable(cp_model, 0, 1);
gtl::InsertOrDie(&arc_vars, arc, index);
cp_model->mutable_objective()->add_vars(index);
@@ -619,7 +622,7 @@ void AddGeneralizedPickupDeliveryConstraints(
for (int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
const Arc vehicle_start_delivery_arc = {
static_cast<int>(model.Start(vehicle) + 1), cp_delivery};
if (gtl::ContainsKey(arc_vars, vehicle_start_delivery_arc)) {
if (arc_vars.contains(vehicle_start_delivery_arc)) {
// Forbid vehicle_start -> delivery arc.
AddLinearConstraint(cp_model, 0, 0,
{{arc_vars.at(vehicle_start_delivery_arc), 1}});
@@ -629,7 +632,7 @@ void AddGeneralizedPickupDeliveryConstraints(
for (const int pickup : pickups) {
const int cp_pickup = pickup + 1;
const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
if (gtl::ContainsKey(arc_vars, delivery_pickup_arc)) {
if (arc_vars.contains(delivery_pickup_arc)) {
// Forbid delivery -> pickup arc.
AddLinearConstraint(cp_model, 0, 0,
{{arc_vars.at(delivery_pickup_arc), 1}});
@@ -693,13 +696,13 @@ ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
const int cp_start = model.Start(vehicle) + 1;
const Arc start_arc = {depot, cp_start};
const int start_arc_var = AddVariable(cp_model, 1, 1);
DCHECK(!gtl::ContainsKey(arc_vars, start_arc));
DCHECK(!arc_vars.contains(start_arc));
arc_vars.insert({start_arc, start_arc_var});
const int cp_end = model.End(vehicle) + 1;
const Arc end_arc = {cp_end, depot};
const int end_arc_var = AddVariable(cp_model, 1, 1);
DCHECK(!gtl::ContainsKey(arc_vars, end_arc));
DCHECK(!arc_vars.contains(end_arc));
arc_vars.insert({end_arc, end_arc_var});
vehicle_performs_node[vehicle][cp_start] = start_arc_var;
@@ -753,7 +756,7 @@ ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
is_unperformed[disjunction_indices[0] + 1] == -1) {
const int cp_node = disjunction_indices[0] + 1;
const Arc arc = {cp_node, cp_node};
DCHECK(!gtl::ContainsKey(arc_vars, arc));
DCHECK(!arc_vars.contains(arc));
is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
arc_vars.insert({arc, is_unperformed[cp_node]});
cp_model->mutable_objective()->add_vars(is_unperformed[cp_node]);
@@ -769,7 +772,7 @@ ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
// Node can be unperformed.
if (is_unperformed[cp_node] == -1) {
const Arc arc = {cp_node, cp_node};
DCHECK(!gtl::ContainsKey(arc_vars, arc));
DCHECK(!arc_vars.contains(arc));
is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
arc_vars.insert({arc, is_unperformed[cp_node]});
}
@@ -821,7 +824,7 @@ ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
if (!feasible) continue;
const Arc arc = {cp_tail, cp_head};
DCHECK(!gtl::ContainsKey(arc_vars, arc));
DCHECK(!arc_vars.contains(arc));
const int arc_var = AddVariable(cp_model, 0, 1);
arc_vars.insert({arc, arc_var});
}

View File

@@ -386,9 +386,6 @@ class CheapestInsertionFilteredHeuristic : public RoutingFilteredHeuristic {
SeedQueue* sq);
// clang-format on
/// Creates and returns a Seed corresponding to the given node/pair_index.
Seed CreateSeed(int index, bool is_pair_index, StartEndValue start_end_value);
/// Adds a Seed corresponding to the given 'node' to sq.priority_queue, based
/// on the last entry in its 'start_end_distances' (from which it's deleted).
void AddSeedNodeToQueue(int node,

View File

@@ -83,6 +83,7 @@ def print_solution(data, manager, routing, solution):
print(plan_output)
max_route_distance = max(route_distance, max_route_distance)
print(f"Maximum of the route distances: {max_route_distance}m")
# [END solution_printer]

View File

@@ -84,6 +84,7 @@ def print_solution(
print(plan_output)
total_distance += route_distance
print(f"Total Distance of all routes: {total_distance}m")
# [END solution_callback_printer]
@@ -111,6 +112,7 @@ class SolutionCallback:
self._counter += 1
if self._counter > self._counter_limit:
self._routing_model.solver().FinishCurrentSearch()
# [END solution_callback]

View File

@@ -28,6 +28,7 @@
#include <vector>
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/types.h"
@@ -63,7 +64,7 @@ class TreeArrayConstraint : public Constraint {
root_node_ = &tree_[0][0];
}
std::string DebugStringInternal(const std::string& name) const {
std::string DebugStringInternal(absl::string_view name) const {
return absl::StrFormat("Cover(%s) == %s", JoinDebugStringPtr(vars_, ", "),
target_var_->DebugString());
}

View File

@@ -19,6 +19,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "ortools/base/hash.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
@@ -513,7 +514,7 @@ class PrintModelVisitor : public ModelVisitor {
return result;
}
void set_prefix(const std::string& prefix) { prefix_ = prefix; }
void set_prefix(absl::string_view prefix) { prefix_ = prefix; }
int indent_;
std::string prefix_;
@@ -685,15 +686,15 @@ class ModelStatisticsVisitor : public ModelVisitor {
}
}
void AddConstraintType(const std::string& constraint_type) {
void AddConstraintType(absl::string_view constraint_type) {
constraint_types_[constraint_type]++;
}
void AddExpressionType(const std::string& expression_type) {
void AddExpressionType(absl::string_view expression_type) {
expression_types_[expression_type]++;
}
void AddExtensionType(const std::string& extension_type) {
void AddExtensionType(absl::string_view extension_type) {
extension_types_[extension_type]++;
}