sync routing code; fix #3975
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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]++;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user