continue rearchitecture LS and routing

This commit is contained in:
Laurent Perron
2019-08-09 11:53:20 -07:00
parent 0e893aca56
commit a39b544fde
9 changed files with 172 additions and 359 deletions

View File

@@ -732,7 +732,6 @@ class Solver {
typedef std::function<int64(const IntVar* v, int64 id)> VariableValueSelector;
typedef std::function<bool(int64, int64, int64)> VariableValueComparator;
typedef std::function<void(int64)> ObjectiveWatcher;
typedef std::function<DecisionModification()> BranchSelector;
// TODO(user): wrap in swig.
typedef std::function<void(Solver*)> Action;
@@ -2714,19 +2713,9 @@ class Solver {
IntVarLocalSearchFilter* MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars, IndexEvaluator2 values,
Solver::LocalSearchFilterBound filter_enum);
IntVarLocalSearchFilter* MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars, IndexEvaluator2 values,
ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum);
IntVarLocalSearchFilter* MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars,
Solver::IndexEvaluator3 values,
Solver::LocalSearchFilterBound filter_enum);
IntVarLocalSearchFilter* MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars,
Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback,
const std::vector<IntVar*>& secondary_vars, IndexEvaluator3 values,
Solver::LocalSearchFilterBound filter_enum);
/// Performs PeriodicCheck on the top-level search; for instance, can be

View File

@@ -1552,8 +1552,6 @@ class LocalSearchFilterManager : public LocalSearchFilter {
class IntVarLocalSearchFilter : public LocalSearchFilter {
public:
IntVarLocalSearchFilter(const std::vector<IntVar*>& vars,
Solver::ObjectiveWatcher objective_callback);
explicit IntVarLocalSearchFilter(const std::vector<IntVar*>& vars);
~IntVarLocalSearchFilter() override;
/// This method should not be overridden. Override OnSynchronize() instead
@@ -1570,10 +1568,6 @@ class IntVarLocalSearchFilter : public LocalSearchFilter {
return *index != kUnassigned;
}
virtual void InjectObjectiveValue(int64 objective_value) {
injected_objective_value_ = objective_value;
}
/// Add variables to "track" to the filter.
void AddVars(const std::vector<IntVar*>& vars);
int Size() const { return vars_.size(); }
@@ -1588,23 +1582,12 @@ class IntVarLocalSearchFilter : public LocalSearchFilter {
virtual void OnSynchronize(const Assignment* delta) {}
void SynchronizeOnAssignment(const Assignment* assignment);
bool CanPropagateObjectiveValue() const {
return objective_callback_ != nullptr;
}
void PropagateObjectiveValue(int64 objective_value) {
if (objective_callback_ != nullptr) {
objective_callback_(objective_value);
}
}
int64 injected_objective_value_;
private:
std::vector<IntVar*> vars_;
std::vector<int64> values_;
std::vector<bool> var_synced_;
std::vector<int> var_index_to_index_;
static const int kUnassigned;
Solver::ObjectiveWatcher objective_callback_;
};
class PropagationMonitor : public SearchMonitor {

View File

@@ -840,6 +840,9 @@ class TwoOpt : public PathOperator {
// Both base nodes have to be on the same path.
return true;
}
int64 GetBaseNodeRestartPosition(int base_index) override {
return (base_index == 0) ? StartNode(0) : BaseNode(0);
}
private:
void OnNodeInitialization() override { last_ = -1; }
@@ -2391,17 +2394,10 @@ LocalSearchFilter* Solver::MakeVariableDomainFilter() {
const int IntVarLocalSearchFilter::kUnassigned = -1;
IntVarLocalSearchFilter::IntVarLocalSearchFilter(
const std::vector<IntVar*>& vars,
Solver::ObjectiveWatcher objective_callback)
: injected_objective_value_(0),
objective_callback_(std::move(objective_callback)) {
const std::vector<IntVar*>& vars) {
AddVars(vars);
}
IntVarLocalSearchFilter::IntVarLocalSearchFilter(
const std::vector<IntVar*>& vars)
: IntVarLocalSearchFilter(vars, nullptr) {}
void IntVarLocalSearchFilter::AddVars(const std::vector<IntVar*>& vars) {
if (!vars.empty()) {
for (int i = 0; i < vars.size(); ++i) {
@@ -2466,9 +2462,8 @@ namespace {
class SumObjectiveFilter : public IntVarLocalSearchFilter {
public:
SumObjectiveFilter(const std::vector<IntVar*>& vars,
Solver::ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum)
: IntVarLocalSearchFilter(vars, std::move(delta_objective_callback)),
: IntVarLocalSearchFilter(vars),
primary_vars_size_(vars.size()),
synchronized_costs_(new int64[vars.size()]),
delta_costs_(new int64[vars.size()]),
@@ -2510,8 +2505,6 @@ class SumObjectiveFilter : public IntVarLocalSearchFilter {
}
incremental_ = true;
}
PropagateObjectiveValue(CapAdd(delta_sum_, injected_objective_value_));
switch (filter_enum_) {
case Solver::LE: {
return delta_sum_ <= objective_max;
@@ -2566,8 +2559,6 @@ class SumObjectiveFilter : public IntVarLocalSearchFilter {
}
delta_sum_ = synchronized_sum_;
incremental_ = false;
PropagateObjectiveValue(
CapAdd(synchronized_sum_, injected_objective_value_));
}
int64 CostOfChanges(const Assignment* changes, const int64* const old_costs,
bool cache_delta_values) {
@@ -2597,10 +2588,8 @@ class BinaryObjectiveFilter : public SumObjectiveFilter {
public:
BinaryObjectiveFilter(const std::vector<IntVar*>& vars,
Solver::IndexEvaluator2 value_evaluator,
Solver::ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum)
: SumObjectiveFilter(vars, std::move(delta_objective_callback),
filter_enum),
: SumObjectiveFilter(vars, filter_enum),
value_evaluator_(std::move(value_evaluator)) {}
~BinaryObjectiveFilter() override {}
int64 CostOfSynchronizedVariable(int64 index) override {
@@ -2635,10 +2624,8 @@ class TernaryObjectiveFilter : public SumObjectiveFilter {
TernaryObjectiveFilter(const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars,
Solver::IndexEvaluator3 value_evaluator,
Solver::ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum)
: SumObjectiveFilter(vars, std::move(delta_objective_callback),
filter_enum),
: SumObjectiveFilter(vars, filter_enum),
secondary_vars_offset_(vars.size()),
value_evaluator_(std::move(value_evaluator)) {
IntVarLocalSearchFilter::AddVars(secondary_vars);
@@ -2695,34 +2682,15 @@ IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
Solver::LocalSearchFilterBound filter_enum) {
return RevAlloc(
new BinaryObjectiveFilter(vars, std::move(values), nullptr, filter_enum));
}
IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum) {
return RevAlloc(new BinaryObjectiveFilter(vars, std::move(values),
std::move(delta_objective_callback),
filter_enum));
new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
}
IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
Solver::LocalSearchFilterBound filter_enum) {
return RevAlloc(new TernaryObjectiveFilter(
vars, secondary_vars, std::move(values), nullptr, filter_enum));
}
IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter(
const std::vector<IntVar*>& vars,
const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
ObjectiveWatcher delta_objective_callback,
Solver::LocalSearchFilterBound filter_enum) {
return RevAlloc(new TernaryObjectiveFilter(
vars, secondary_vars, std::move(values),
std::move(delta_objective_callback), filter_enum));
return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars,
std::move(values), filter_enum));
}
// ----- LocalSearchProfiler -----

View File

@@ -732,7 +732,7 @@ namespace operations_research {
%unignore Solver::SolveAndCommit;
%unignore Solver::FinishCurrentSearch;
%unignore Solver::RestartCurrentSearch;
// TOOD(lperron): Support Action in python.
// TODO(user): Support Action in python.
// %unignore Solver::AddBacktrackAction;
// Solver: Debug and performance counters.

View File

@@ -4240,7 +4240,6 @@ LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
const std::vector<LocalSearchFilter*>&
RoutingModel::GetOrCreateLocalSearchFilters() {
// Note on objective injection from one filter to another.
// As of 2013/01, three filters evaluate sub-parts of the objective
// function:
// - NodeDisjunctionFilter: takes disjunction penalty costs into account,
@@ -4250,177 +4249,114 @@ RoutingModel::GetOrCreateLocalSearchFilters() {
// related to amortized linear and quadratic vehicle cost factors.
// - LocalSearchObjectiveFilter, which takes dimension "arc" costs into
// account.
// To be able to filter cost values properly, a filter needs to be aware of
// cost bounds computed by other filters before it (for the same delta).
// Communication of cost between filters is done through callbacks,
// VehicleAmortizedCostFilter sending the total "vehicle cost" to
// LocalSearchObjectiveFilter, itself sending this vehicle cost + total arc
// costs to NodeDisjunctionFilter, in turn sending this cost + total penalty
// cost to PathCumulFilters (if you have several of these, they send updated
// costs to each other too).
// Note that since the VehicleAmortizedCostFilter is the only filter with a
// possible negative contribution to the objective, it has to be the first
// filter called so it propagates this value to all other filters.
// Callbacks are called on OnSynchronize to send the cost of the current
// solution and on Accept to send the cost of solution deltas.
if (filters_.empty()) {
// NOTE: We first sort the dimensions by decreasing complexity of filtering:
// Dimensions with a global span cost coefficient and/or precedences will
// have a global LP created to filter feasibility and costs.
// We want these filters to be called last, hence putting them in the
// beginning of the vector, since it will be reversed.
std::vector<RoutingDimension*> sorted_dimensions = dimensions_.get();
std::sort(sorted_dimensions.begin(), sorted_dimensions.end(),
[](const RoutingDimension* d1, const RoutingDimension* d2) {
return (d1->global_span_cost_coefficient() >
d2->global_span_cost_coefficient()) ||
(d1->GetNodePrecedences().size() >
d2->GetNodePrecedences().size());
});
if (!filters_.empty()) return filters_;
std::vector<IntVarLocalSearchFilter*> cumul_filters;
IntVarLocalSearchFilter* path_cumul_filter = nullptr;
for (const RoutingDimension* dimension : sorted_dimensions) {
Solver::ObjectiveWatcher objective_callback = nullptr;
if (path_cumul_filter != nullptr) {
objective_callback = [path_cumul_filter](int64 value) {
return path_cumul_filter->InjectObjectiveValue(value);
};
}
const std::vector<IntVarLocalSearchFilter*> dimension_cumul_filters =
MakeCumulFilters(*dimension, objective_callback,
/*filtering_objective*/ true);
cumul_filters.insert(cumul_filters.end(), dimension_cumul_filters.begin(),
dimension_cumul_filters.end());
// NOTE: We use the last element of cumul_filters to inject later costs
// because MakeCumulFilter() sets the internal injections between the
// LPCumulFilter and PathCumulFilter when necessary.
path_cumul_filter = cumul_filters.back();
}
// Due to the way cost injection is setup, path filters have to be
// called in reverse order.
std::reverse(cumul_filters.begin(), cumul_filters.end());
IntVarLocalSearchFilter* node_disjunction_filter = nullptr;
if (!disjunctions_.empty()) {
Solver::ObjectiveWatcher objective_callback = nullptr;
if (path_cumul_filter != nullptr) {
objective_callback = [path_cumul_filter](int64 value) {
return path_cumul_filter->InjectObjectiveValue(value);
};
}
node_disjunction_filter =
MakeNodeDisjunctionFilter(*this, objective_callback);
}
Solver::ObjectiveWatcher objective_callback = nullptr;
if (node_disjunction_filter != nullptr) {
objective_callback = [node_disjunction_filter](int64 value) {
return node_disjunction_filter->InjectObjectiveValue(value);
};
} else if (path_cumul_filter != nullptr) {
objective_callback = [path_cumul_filter](int64 value) {
return path_cumul_filter->InjectObjectiveValue(value);
};
}
IntVarLocalSearchFilter* local_search_objective_filter = nullptr;
if (CostsAreHomogeneousAcrossVehicles()) {
local_search_objective_filter = solver_->MakeSumObjectiveFilter(
nexts_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
objective_callback, Solver::LE);
} else {
local_search_objective_filter = solver_->MakeSumObjectiveFilter(
nexts_, vehicle_vars_,
[this](int64 i, int64 j, int64 k) {
return GetArcCostForVehicle(i, j, k);
},
objective_callback, Solver::LE);
}
if (vehicle_amortized_cost_factors_set_) {
objective_callback = [local_search_objective_filter](int64 value) {
return local_search_objective_filter->InjectObjectiveValue(value);
};
filters_.push_back(
MakeVehicleAmortizedCostFilter(*this, objective_callback));
}
// Must be added after VehicleAmortizedCostFilter.
filters_.push_back(local_search_objective_filter);
filters_.push_back(solver_->MakeVariableDomainFilter());
if (node_disjunction_filter != nullptr) {
// Must be added after ObjectiveFilter.
filters_.push_back(node_disjunction_filter);
}
if (!pickup_delivery_pairs_.empty()) {
filters_.push_back(MakePickupDeliveryFilter(
*this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
}
if (HasTypeRegulations()) {
filters_.push_back(MakeTypeRegulationsFilter(*this));
}
filters_.push_back(MakeVehicleVarFilter(*this));
// Must be added after NodeDisjunctionFilter and ObjectiveFilter.
filters_.insert(filters_.end(), cumul_filters.begin(), cumul_filters.end());
for (const RoutingDimension* dimension : dimensions_) {
if (dimension->HasBreakConstraints()) {
IntVarLocalSearchFilter* breaks_filter =
MakeVehicleBreaksFilter(*this, *dimension);
filters_.push_back(breaks_filter);
}
}
filters_.insert(filters_.end(), extra_filters_.begin(),
extra_filters_.end());
// VehicleAmortizedCostFilter can have a negative value, so it must be first.
if (vehicle_amortized_cost_factors_set_) {
filters_.push_back(MakeVehicleAmortizedCostFilter(*this));
}
// The SumObjectiveFilter has the best reject/second ratio in practice,
// so it is the earliest.
if (CostsAreHomogeneousAcrossVehicles()) {
filters_.push_back(solver_->MakeSumObjectiveFilter(
nexts_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
Solver::LE));
} else {
filters_.push_back(solver_->MakeSumObjectiveFilter(
nexts_, vehicle_vars_,
[this](int64 i, int64 j, int64 k) {
return GetArcCostForVehicle(i, j, k);
},
Solver::LE));
}
filters_.push_back(solver_->MakeVariableDomainFilter());
if (!disjunctions_.empty()) {
filters_.push_back(MakeNodeDisjunctionFilter(*this));
}
if (!pickup_delivery_pairs_.empty()) {
filters_.push_back(MakePickupDeliveryFilter(
*this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
}
if (HasTypeRegulations()) {
filters_.push_back(MakeTypeRegulationsFilter(*this));
}
filters_.push_back(MakeVehicleVarFilter(*this));
// NOTE: We first sort the dimensions by increasing complexity of filtering:
// Dimensions with a global span cost coefficient and/or precedences will
// have a global LP created to filter feasibility and costs.
std::vector<RoutingDimension*> sorted_dimensions = dimensions_.get();
std::sort(sorted_dimensions.begin(), sorted_dimensions.end(),
[](const RoutingDimension* d1, const RoutingDimension* d2) {
return (d1->global_span_cost_coefficient() <=
d2->global_span_cost_coefficient()) &&
(d1->GetNodePrecedences().size() <=
d2->GetNodePrecedences().size());
});
for (const RoutingDimension* dimension : sorted_dimensions) {
const std::vector<IntVarLocalSearchFilter*> cumul_filters =
MakeCumulFilters(*dimension, /*filtering_objective*/ true);
filters_.insert(filters_.end(), cumul_filters.begin(), cumul_filters.end());
}
for (const RoutingDimension* dimension : dimensions_) {
if (!dimension->HasBreakConstraints()) continue;
filters_.push_back(MakeVehicleBreaksFilter(*this, *dimension));
}
filters_.insert(filters_.end(), extra_filters_.begin(), extra_filters_.end());
return filters_;
}
const std::vector<LocalSearchFilter*>&
RoutingModel::GetOrCreateFeasibilityFilters() {
if (feasibility_filters_.empty()) {
if (!disjunctions_.empty()) {
feasibility_filters_.push_back(MakeNodeDisjunctionFilter(*this, nullptr));
}
feasibility_filters_.push_back(solver_->MakeVariableDomainFilter());
if (!pickup_delivery_pairs_.empty()) {
feasibility_filters_.push_back(MakePickupDeliveryFilter(
*this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
}
if (HasTypeRegulations()) {
feasibility_filters_.push_back(MakeTypeRegulationsFilter(*this));
}
feasibility_filters_.push_back(MakeVehicleVarFilter(*this));
if (!feasibility_filters_.empty()) return feasibility_filters_;
// NOTE: We sort the dimensions by decreasing complexity of filtering:
// Dimensions with precedences will have a global LP created to filter
// feasibility, so we want these filters to be called last.
std::vector<RoutingDimension*> sorted_dimensions = dimensions_.get();
std::sort(sorted_dimensions.begin(), sorted_dimensions.end(),
[](const RoutingDimension* d1, const RoutingDimension* d2) {
return (d1->GetNodePrecedences().size() <
d2->GetNodePrecedences().size());
});
for (const RoutingDimension* const dimension : sorted_dimensions) {
const std::vector<IntVarLocalSearchFilter*> dimension_cumul_filters =
MakeCumulFilters(*dimension, nullptr, /*filtering_objective*/ false);
feasibility_filters_.insert(feasibility_filters_.end(),
dimension_cumul_filters.begin(),
dimension_cumul_filters.end());
}
for (const RoutingDimension* dimension : dimensions_) {
if (dimension->HasBreakConstraints()) {
IntVarLocalSearchFilter* breaks_filter =
MakeVehicleBreaksFilter(*this, *dimension);
feasibility_filters_.push_back(breaks_filter);
}
}
feasibility_filters_.insert(feasibility_filters_.end(),
extra_filters_.begin(), extra_filters_.end());
if (!disjunctions_.empty()) {
feasibility_filters_.push_back(MakeNodeDisjunctionFilter(*this));
}
feasibility_filters_.push_back(solver_->MakeVariableDomainFilter());
if (!pickup_delivery_pairs_.empty()) {
feasibility_filters_.push_back(MakePickupDeliveryFilter(
*this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
}
if (HasTypeRegulations()) {
feasibility_filters_.push_back(MakeTypeRegulationsFilter(*this));
}
feasibility_filters_.push_back(MakeVehicleVarFilter(*this));
// NOTE: We sort the dimensions by decreasing complexity of filtering:
// Dimensions with precedences will have a global LP created to filter
// feasibility, so we want these filters to be called last.
std::vector<RoutingDimension*> sorted_dimensions = dimensions_.get();
std::sort(sorted_dimensions.begin(), sorted_dimensions.end(),
[](const RoutingDimension* d1, const RoutingDimension* d2) {
return (d1->GetNodePrecedences().size() <
d2->GetNodePrecedences().size());
});
for (const RoutingDimension* const dimension : sorted_dimensions) {
const std::vector<IntVarLocalSearchFilter*> dimension_cumul_filters =
MakeCumulFilters(*dimension, /*filtering_objective*/ false);
feasibility_filters_.insert(feasibility_filters_.end(),
dimension_cumul_filters.begin(),
dimension_cumul_filters.end());
}
for (const RoutingDimension* dimension : dimensions_) {
if (dimension->HasBreakConstraints()) {
IntVarLocalSearchFilter* breaks_filter =
MakeVehicleBreaksFilter(*this, *dimension);
feasibility_filters_.push_back(breaks_filter);
}
}
feasibility_filters_.insert(feasibility_filters_.end(),
extra_filters_.begin(), extra_filters_.end());
return feasibility_filters_;
}

View File

@@ -3187,8 +3187,7 @@ bool SolveModelWithSat(const RoutingModel& model,
class BasePathFilter : public IntVarLocalSearchFilter {
public:
BasePathFilter(const std::vector<IntVar*>& nexts, int next_domain_size,
std::function<void(int64)> objective_callback);
BasePathFilter(const std::vector<IntVar*>& nexts, int next_domain_size);
~BasePathFilter() override {}
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64 objective_min, int64 objective_max) override;
@@ -3279,23 +3278,18 @@ class CPFeasibilityFilter : public IntVarLocalSearchFilter {
#if !defined(SWIG)
IntVarLocalSearchFilter* MakeNodeDisjunctionFilter(
const RoutingModel& routing_model,
std::function<void(int64)> objective_callback);
const RoutingModel& routing_model);
IntVarLocalSearchFilter* MakeVehicleAmortizedCostFilter(
const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback);
const RoutingModel& routing_model);
IntVarLocalSearchFilter* MakeTypeRegulationsFilter(
const RoutingModel& routing_model);
std::vector<IntVarLocalSearchFilter*> MakeCumulFilters(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback, bool filter_objective_cost);
IntVarLocalSearchFilter* MakePathCumulFilter(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool propagate_own_objective_value, bool filter_objective_cost);
const RoutingDimension& dimension, bool filter_objective_cost);
IntVarLocalSearchFilter* MakePathCumulFilter(const RoutingDimension& dimension,
bool propagate_own_objective_value,
bool filter_objective_cost);
IntVarLocalSearchFilter* MakeGlobalLPCumulFilter(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback, bool filter_objective_cost);
const RoutingDimension& dimension, bool filter_objective_cost);
IntVarLocalSearchFilter* MakePickupDeliveryFilter(
const RoutingModel& routing_model, const RoutingModel::IndexPairs& pairs,
const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies);

View File

@@ -558,7 +558,7 @@ class VehicleBreaksFilter : public BasePathFilter {
VehicleBreaksFilter::VehicleBreaksFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension)
: BasePathFilter(routing_model.Nexts(),
routing_model.Size() + routing_model.vehicles(), nullptr),
routing_model.Size() + routing_model.vehicles()),
model_(routing_model),
dimension_(dimension) {
DCHECK(dimension_.HasBreakConstraints());

View File

@@ -539,24 +539,27 @@ bool DimensionCumulOptimizerCore::ComputeRouteCumulBounds(
}
}
// Refine cumul bounds using cumul[i] + fixed_transit[i] <= cumul[i+1].
// TODO(user): Refine further using
// cumul[i] + fixed_transit[i] + slack[i].min() <= cumul[i+1].
// Refine cumul bounds using
// cumul[i+1] >= cumul[i] + fixed_transit[i] + slack[i].
for (int pos = 1; pos < route_size; ++pos) {
const int64 slack_min = dimension_->SlackVar(route[pos - 1])->Min();
current_route_min_cumuls_[pos] = std::max(
current_route_min_cumuls_[pos],
CapAdd(current_route_min_cumuls_[pos - 1], fixed_transits[pos - 1]));
CapAdd(
CapAdd(current_route_min_cumuls_[pos - 1], fixed_transits[pos - 1]),
slack_min));
}
// TODO(user): Refine further using
// cumul[i+1] <= cumul[i] + fixed_transit[i] + slack[i].max().
for (int pos = route_size - 2; pos >= 0; --pos) {
// If cumul_max[pos+1] is kint64max, it will be translated to
// double +infinity, so it must not constrain cumul_max[pos].
if (current_route_max_cumuls_[pos + 1] < kint64max) {
const int64 slack_min = dimension_->SlackVar(route[pos])->Min();
current_route_max_cumuls_[pos] = std::min(
current_route_max_cumuls_[pos],
CapSub(current_route_max_cumuls_[pos + 1], fixed_transits[pos]));
CapSub(
CapSub(current_route_max_cumuls_[pos + 1], fixed_transits[pos]),
slack_min));
}
}
return true;

View File

@@ -50,10 +50,8 @@ namespace {
class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
public:
NodeDisjunctionFilter(const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback)
: IntVarLocalSearchFilter(routing_model.Nexts(),
std::move(objective_callback)),
explicit NodeDisjunctionFilter(const RoutingModel& routing_model)
: IntVarLocalSearchFilter(routing_model.Nexts()),
routing_model_(routing_model),
active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
@@ -116,7 +114,6 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
disjunction_active_delta.first);
// Too many active nodes.
if (active_nodes > max_cardinality) {
PropagateObjectiveValue(0);
return false;
}
}
@@ -141,7 +138,6 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
if (penalty < 0) {
// Nodes are mandatory, i.e. exactly max_cardinality nodes must be
// performed, so the move is not acceptable.
PropagateObjectiveValue(0);
return false;
} else if (current_inactive_nodes <= max_inactive_cardinality) {
// Add penalty if there were not too many inactive nodes before the
@@ -157,10 +153,8 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
}
}
}
if (lns_detected) accepted_objective_value_ = 0;
PropagateObjectiveValue(
CapAdd(accepted_objective_value_, injected_objective_value_));
if (lns_detected) {
accepted_objective_value_ = 0;
return true;
} else {
// Only compare to max as a cost lower bound is computed.
@@ -204,8 +198,6 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
CapAdd(synchronized_objective_value_, penalty);
}
}
PropagateObjectiveValue(
CapAdd(injected_objective_value_, synchronized_objective_value_));
}
const RoutingModel& routing_model_;
@@ -218,10 +210,9 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
} // namespace
IntVarLocalSearchFilter* MakeNodeDisjunctionFilter(
const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback) {
const RoutingModel& routing_model) {
return routing_model.solver()->RevAlloc(
new NodeDisjunctionFilter(routing_model, std::move(objective_callback)));
new NodeDisjunctionFilter(routing_model));
}
LocalSearchFilterManager::LocalSearchFilterManager(
@@ -278,9 +269,8 @@ void LocalSearchFilterManager::Synchronize(const Assignment* assignment,
const int64 BasePathFilter::kUnassigned = -1;
BasePathFilter::BasePathFilter(const std::vector<IntVar*>& nexts,
int next_domain_size,
Solver::ObjectiveWatcher objective_callback)
: IntVarLocalSearchFilter(nexts, std::move(objective_callback)),
int next_domain_size)
: IntVarLocalSearchFilter(nexts),
node_path_starts_(next_domain_size, kUnassigned),
paths_(nexts.size(), -1),
new_synchronized_unperformed_nodes_(nexts.size()),
@@ -294,7 +284,6 @@ bool BasePathFilter::Accept(const Assignment* delta,
const Assignment* deltadelta, int64 objective_min,
int64 objective_max) {
if (IsDisabled()) return true;
PropagateObjectiveValue(injected_objective_value_);
for (const int touched : delta_touched_) {
new_nexts_[touched] = kUnassigned;
}
@@ -412,7 +401,6 @@ void BasePathFilter::SynchronizeFullAssignment() {
// Subclasses of BasePathFilter might not propagate injected objective values
// so making sure it is done here (can be done again by the subclass if
// needed).
PropagateObjectiveValue(injected_objective_value_);
ComputePathStarts(&starts_, &paths_);
for (int64 index = 0; index < Size(); index++) {
if (IsVarSynced(index) && Value(index) == index &&
@@ -457,7 +445,6 @@ void BasePathFilter::OnSynchronize(const Assignment* delta) {
// Subclasses of BasePathFilter might not propagate injected objective values
// so making sure it is done here (can be done again by the subclass if
// needed).
PropagateObjectiveValue(injected_objective_value_);
// This code supposes that path starts didn't change.
DCHECK(!FLAGS_routing_strong_debug_checks || !HavePathsChanged());
const Assignment::IntContainer& container = delta->IntVarContainer();
@@ -517,8 +504,7 @@ namespace {
class VehicleAmortizedCostFilter : public BasePathFilter {
public:
VehicleAmortizedCostFilter(const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback);
explicit VehicleAmortizedCostFilter(const RoutingModel& routing_model);
~VehicleAmortizedCostFilter() override {}
std::string DebugString() const override {
return "VehicleAmortizedCostFilter";
@@ -550,11 +536,9 @@ class VehicleAmortizedCostFilter : public BasePathFilter {
};
VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback)
const RoutingModel& routing_model)
: BasePathFilter(routing_model.Nexts(),
routing_model.Size() + routing_model.vehicles(),
std::move(objective_callback)),
routing_model.Size() + routing_model.vehicles()),
current_vehicle_cost_(0),
delta_vehicle_cost_(0),
current_route_lengths_(Size(), -1),
@@ -603,8 +587,6 @@ void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
current_vehicle_cost_ = CapAdd(
current_vehicle_cost_, CapSub(linear_cost_factor, route_length_cost));
}
PropagateObjectiveValue(
CapAdd(current_vehicle_cost_, injected_objective_value_));
}
void VehicleAmortizedCostFilter::InitializeAcceptPath() {
@@ -662,18 +644,15 @@ bool VehicleAmortizedCostFilter::AcceptPath(int64 path_start, int64 chain_start,
bool VehicleAmortizedCostFilter::FinalizeAcceptPath(const Assignment* delta,
int64 objective_min,
int64 objective_max) {
PropagateObjectiveValue(
CapAdd(injected_objective_value_, delta_vehicle_cost_));
return delta_vehicle_cost_ <= objective_max;
}
} // namespace
IntVarLocalSearchFilter* MakeVehicleAmortizedCostFilter(
const RoutingModel& routing_model,
Solver::ObjectiveWatcher objective_callback) {
return routing_model.solver()->RevAlloc(new VehicleAmortizedCostFilter(
routing_model, std::move(objective_callback)));
const RoutingModel& routing_model) {
return routing_model.solver()->RevAlloc(
new VehicleAmortizedCostFilter(routing_model));
}
namespace {
@@ -703,7 +682,7 @@ class TypeRegulationsFilter : public BasePathFilter {
};
TypeRegulationsFilter::TypeRegulationsFilter(const RoutingModel& model)
: BasePathFilter(model.Nexts(), model.Size() + model.vehicles(), nullptr),
: BasePathFilter(model.Nexts(), model.Size() + model.vehicles()),
routing_model_(model),
start_to_vehicle_(model.Size(), -1),
temporal_incompatibility_checker_(model,
@@ -844,8 +823,7 @@ int64 GetNextValueFromForbiddenIntervals(
class ChainCumulFilter : public BasePathFilter {
public:
ChainCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback);
const RoutingDimension& dimension);
~ChainCumulFilter() override {}
std::string DebugString() const override {
return "ChainCumulFilter(" + name_ + ")";
@@ -870,10 +848,8 @@ class ChainCumulFilter : public BasePathFilter {
};
ChainCumulFilter::ChainCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback)
: BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
std::move(objective_callback)),
const RoutingDimension& dimension)
: BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
cumuls_(dimension.cumuls()),
evaluators_(routing_model.vehicles(), nullptr),
vehicle_capacities_(dimension.vehicle_capacities()),
@@ -958,7 +934,6 @@ class PathCumulFilter : public BasePathFilter {
public:
PathCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool propagate_own_objective_value,
bool filter_objective_cost);
~PathCumulFilter() override {}
@@ -1201,11 +1176,9 @@ class PathCumulFilter : public BasePathFilter {
PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool propagate_own_objective_value,
bool filter_objective_cost)
: BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
std::move(objective_callback)),
: BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
routing_model_(routing_model),
dimension_(dimension),
cumuls_(dimension.cumuls()),
@@ -1512,14 +1485,6 @@ void PathCumulFilter::OnBeforeSynchronizePaths() {
CapProd(global_span_cost_coefficient_,
CapSub(current_max_end_.cumul_value,
current_min_start_.cumul_value)));
if (CanPropagateObjectiveValue()) {
if (propagate_own_objective_value_) {
PropagateObjectiveValue(
CapAdd(injected_objective_value_, synchronized_objective_value_));
} else {
PropagateObjectiveValue(injected_objective_value_);
}
}
}
bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start,
@@ -1677,7 +1642,6 @@ bool PathCumulFilter::FinalizeAcceptPath(const Assignment* delta,
!FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts() &&
!FilterPrecedences() && !FilterSoftSpanCost()) ||
lns_detected_) {
PropagateObjectiveValue(injected_objective_value_);
return true;
}
if (FilterPrecedences()) {
@@ -1778,13 +1742,6 @@ bool PathCumulFilter::FinalizeAcceptPath(const Assignment* delta,
accepted_objective_value_ =
CapAdd(cumul_cost_delta_, CapProd(global_span_cost_coefficient_,
CapSub(new_max_end, new_min_start)));
if (propagate_own_objective_value_) {
PropagateObjectiveValue(
CapAdd(injected_objective_value_, accepted_objective_value_));
} else {
PropagateObjectiveValue(injected_objective_value_);
}
// Only compare to max as a cost lower bound is computed.
return accepted_objective_value_ <= objective_max;
}
@@ -1906,14 +1863,12 @@ int64 PathCumulFilter::ComputePathMaxStartFromEndCumul(
} // namespace
IntVarLocalSearchFilter* MakePathCumulFilter(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool propagate_own_objective_value, bool filter_objective_cost) {
IntVarLocalSearchFilter* MakePathCumulFilter(const RoutingDimension& dimension,
bool propagate_own_objective_value,
bool filter_objective_cost) {
RoutingModel& model = *dimension.model();
return model.solver()->RevAlloc(new PathCumulFilter(
model, dimension, objective_callback, propagate_own_objective_value,
filter_objective_cost));
model, dimension, propagate_own_objective_value, filter_objective_cost));
}
namespace {
@@ -1948,31 +1903,25 @@ bool DimensionHasCumulConstraint(const RoutingDimension& dimension) {
} // namespace
std::vector<IntVarLocalSearchFilter*> MakeCumulFilters(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback, bool filter_objective_cost) {
const RoutingDimension& dimension, bool filter_objective_cost) {
std::vector<IntVarLocalSearchFilter*> filters;
if (!dimension.GetNodePrecedences().empty() ||
(filter_objective_cost && dimension.global_span_cost_coefficient() > 0)) {
IntVarLocalSearchFilter* lp_cumul_filter = MakeGlobalLPCumulFilter(
dimension, objective_callback, filter_objective_cost);
filters.push_back(lp_cumul_filter);
objective_callback = [lp_cumul_filter](int64 value) {
return lp_cumul_filter->InjectObjectiveValue(value);
};
}
// NOTE: We always add the PathCumulFilter to filter each route's feasibility
// separately to try and cut bad decisions earlier in the search, but we don't
// propagate the computed cost if the LPCumulFilter is already doing it.
const bool propagate_path_cumul_filter_objective_value = filters.empty();
const bool use_global_lp_filter =
!dimension.GetNodePrecedences().empty() ||
(filter_objective_cost && dimension.global_span_cost_coefficient() > 0);
if (DimensionHasCumulConstraint(dimension)) {
filters.push_back(MakePathCumulFilter(
dimension, objective_callback,
propagate_path_cumul_filter_objective_value, filter_objective_cost));
filters.push_back(MakePathCumulFilter(dimension, !use_global_lp_filter,
filter_objective_cost));
} else {
filters.push_back(dimension.model()->solver()->RevAlloc(
new ChainCumulFilter(*dimension.model(), dimension)));
}
if (use_global_lp_filter) {
filters.push_back(
dimension.model()->solver()->RevAlloc(new ChainCumulFilter(
*dimension.model(), dimension, objective_callback)));
MakeGlobalLPCumulFilter(dimension, filter_objective_cost));
}
return filters;
}
@@ -2008,7 +1957,7 @@ PickupDeliveryFilter::PickupDeliveryFilter(
const std::vector<IntVar*>& nexts, int next_domain_size,
const RoutingModel::IndexPairs& pairs,
const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
: BasePathFilter(nexts, next_domain_size, nullptr),
: BasePathFilter(nexts, next_domain_size),
pair_firsts_(next_domain_size, kUnassigned),
pair_seconds_(next_domain_size, kUnassigned),
pairs_(pairs),
@@ -2193,7 +2142,7 @@ class VehicleVarFilter : public BasePathFilter {
VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model)
: BasePathFilter(routing_model.Nexts(),
routing_model.Size() + routing_model.vehicles(), nullptr),
routing_model.Size() + routing_model.vehicles()),
vehicle_vars_(routing_model.VehicleVars()),
unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
start_to_vehicle_.resize(Size(), -1);
@@ -2266,9 +2215,7 @@ namespace {
class LPCumulFilter : public IntVarLocalSearchFilter {
public:
LPCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool filter_objective_cost);
const RoutingDimension& dimension, bool filter_objective_cost);
bool Accept(const Assignment* delta, const Assignment* deltadelta,
int64 objective_min, int64 objective_max) override;
int64 GetAcceptedObjectiveValue() const override;
@@ -2289,10 +2236,8 @@ class LPCumulFilter : public IntVarLocalSearchFilter {
LPCumulFilter::LPCumulFilter(const RoutingModel& routing_model,
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback,
bool filter_objective_cost)
: IntVarLocalSearchFilter(routing_model.Nexts(),
std::move(objective_callback)),
: IntVarLocalSearchFilter(routing_model.Nexts()),
optimizer_(&dimension),
filter_objective_cost_(filter_objective_cost),
synchronized_cost_without_transit_(-1),
@@ -2322,18 +2267,16 @@ bool LPCumulFilter::Accept(const Assignment* delta,
if (!filter_objective_cost_) {
// No need to compute the cost of the LP, only verify its feasibility.
delta_cost_without_transit_ = -1;
delta_cost_without_transit_ = 0;
return optimizer_.IsFeasible(next_accessor);
}
if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
next_accessor, &delta_cost_without_transit_)) {
// Infeasible.
delta_cost_without_transit_ = -1;
delta_cost_without_transit_ = kint64max;
return false;
}
PropagateObjectiveValue(
CapAdd(injected_objective_value_, delta_cost_without_transit_));
return delta_cost_without_transit_ <= objective_max;
}
@@ -2351,8 +2294,6 @@ void LPCumulFilter::OnSynchronize(const Assignment* delta) {
// DCHECK the fail wasn't due to an infeasible model.
synchronized_cost_without_transit_ = 0;
}
PropagateObjectiveValue(
CapAdd(injected_objective_value_, synchronized_cost_without_transit_));
}
int64 LPCumulFilter::GetSynchronizedObjectiveValue() const {
@@ -2362,11 +2303,10 @@ int64 LPCumulFilter::GetSynchronizedObjectiveValue() const {
} // namespace
IntVarLocalSearchFilter* MakeGlobalLPCumulFilter(
const RoutingDimension& dimension,
Solver::ObjectiveWatcher objective_callback, bool filter_objective_cost) {
const RoutingDimension& dimension, bool filter_objective_cost) {
const RoutingModel* model = dimension.model();
return model->solver()->RevAlloc(new LPCumulFilter(
*model, dimension, std::move(objective_callback), filter_objective_cost));
return model->solver()->RevAlloc(
new LPCumulFilter(*model, dimension, filter_objective_cost));
}
const int64 CPFeasibilityFilter::kUnassigned = -1;