continue rearchitecture LS and routing
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 -----
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user