change distance callback on node in routing to NodeEvaluator2; add disjunction of decision builders.

This commit is contained in:
lperron@google.com
2012-02-20 12:10:32 +00:00
parent 7ffaf62f03
commit 936746cf06
6 changed files with 687 additions and 257 deletions

View File

@@ -2215,7 +2215,14 @@ class Solver {
const std::vector<int64>& values);
Decision* MakeFailDecision();
// Sequential composition of Decision Builders
// Creates a decision builder which sequentially composes decision builders.
// At each leaf of a decision builder, the next decision builder is therefore
// called. For instance Compose(db1, db2) will result in the following tree:
// d1 tree |
// / | \ |
// db1 leaves |
// / | \ |
// db2 tree db2 tree db2 tree |
DecisionBuilder* Compose(DecisionBuilder* const db1,
DecisionBuilder* const db2);
DecisionBuilder* Compose(DecisionBuilder* const db1,
@@ -2227,6 +2234,34 @@ class Solver {
DecisionBuilder* const db4);
DecisionBuilder* Compose(const std::vector<DecisionBuilder*>& dbs);
// Creates a decision builder which will create a search tree where each
// decision builder is called from the top of the search tree. For instance
// the decision builder Try(db1, db2) will entirely explore the search tree
// of db1 then the one of db2, resulting in the following search tree:
// Tree root |
// / \ |
// db1 tree db2 tree |
//
// This is very handy to try a decision builder which partially explores the
// search space and if it fails to try another decision builder.
//
// TODO(user): The search tree can be balanced by using binary
// "Try"-builders "recursively". For instance, Try(a,b,c,d) will give a tree
// unbalanced to the right, whereas Try(Try(a,b), Try(b,c)) will give a
// balanced tree. Investigate if we should only provide the binary version
// and/or if we should balance automatically.
DecisionBuilder* Try(DecisionBuilder* const db1,
DecisionBuilder* const db2);
DecisionBuilder* Try(DecisionBuilder* const db1,
DecisionBuilder* const db2,
DecisionBuilder* const db3);
DecisionBuilder* Try(DecisionBuilder* const db1,
DecisionBuilder* const db2,
DecisionBuilder* const db3,
DecisionBuilder* const db4);
DecisionBuilder* Try(const std::vector<DecisionBuilder*>& dbs);
// Phases on IntVar arrays.
DecisionBuilder* MakePhase(const std::vector<IntVar*>& vars,
IntVarStrategy var_str,

View File

@@ -50,8 +50,7 @@ namespace operations_research {
// Utility methods to ensure the communication between local search and the
// search.
// Returns true if a local optimum has been reached and that it cannot be
// improved.
// Returns true if a local optimum has been reached and cannot be improved.
bool LocalOptimumReached(Search* const search);
// Returns true if the search accepts the delta (actually checking this by

View File

@@ -1151,9 +1151,133 @@ void RoutingModel::CloseModel() {
cost_ = solver_->MakeSum(cost_elements)->Var();
cost_->set_name("Cost");
SetUpSearch();
SetupSearch();
}
// Decision builder building a solution with a single path without propagating.
// Has a very high probability of failing if the problem contains other
// constraints than path-related constraints.
// Based on an addition heuristics extending a path from its start node with
// the cheapest arc according to an evaluator.
class FastOnePathBuilder : public DecisionBuilder {
public:
// Takes ownership of evaluator.
FastOnePathBuilder(RoutingModel* const model,
ResultCallback2<int64, int64, int64>* evaluator)
: model_(model), evaluator_(evaluator) {
evaluator_->CheckIsRepeatable();
}
virtual ~FastOnePathBuilder() {}
virtual Decision* Next(Solver* const solver) {
int64 index = -1;
if (!FindPathStart(&index)) {
return NULL;
}
IntVar** nexts = model_->Nexts();
// Need to allocate in a reversible way so that if restoring the assignment
// fails, the assignment gets de-allocated.
Assignment* assignment = solver->MakeAssignment();
int64 next = FindCheapestValue(index, *assignment);
while (next >= 0) {
assignment->Add(nexts[index]);
assignment->SetValue(nexts[index], next);
index = next;
std::vector<int> alternates;
model_->GetDisjunctionIndicesFromIndex(index, &alternates);
for (int alternate_index = 0;
alternate_index < alternates.size();
++alternate_index) {
const int alternate = alternates[alternate_index];
if (index != alternate) {
assignment->Add(nexts[alternate]);
assignment->SetValue(nexts[alternate], alternate);
}
}
next = FindCheapestValue(index, *assignment);
}
// Make unassigned nexts loop to themselves.
// TODO(user): Make finalization more robust, might have some next
// variables non-instantiated.
for (int index = 0; index < model_->Size(); ++index) {
IntVar* const next = nexts[index];
if (!assignment->Contains(next)) {
assignment->Add(next);
if (next->Contains(index)) {
assignment->SetValue(next, index);
}
}
}
assignment->Restore();
return NULL;
}
private:
bool FindPathStart(int64* index) const {
IntVar** nexts = model_->Nexts();
const int size = model_->Size();
// Try to extend an existing path
for (int i = size - 1; i >= 0; --i) {
if (nexts[i]->Bound()) {
const int next = nexts[i]->Value();
if (next < size && !nexts[next]->Bound()) {
*index = next;
return true;
}
}
}
// Pick path start
for (int i = size - 1; i >= 0; --i) {
if (!nexts[i]->Bound()) {
bool has_possible_prev = false;
for (int j = 0; j < size; ++j) {
if (nexts[j]->Contains(i)) {
has_possible_prev = true;
break;
}
}
if (!has_possible_prev) {
*index = i;
return true;
}
}
}
// Pick first unbound
for (int i = 0; i < size; ++i) {
if (!nexts[i]->Bound()) {
*index = i;
return true;
}
}
return false;
}
int64 FindCheapestValue(int index, const Assignment& assignment) const {
IntVar** nexts = model_->Nexts();
const int size = model_->Size();
int64 best_evaluation = kint64max;
int64 best_value = -1;
if (index < size) {
IntVar* const next = nexts[index];
scoped_ptr<IntVarIterator> it(next->MakeDomainIterator(false));
for (it->Init(); it->Ok(); it->Next()) {
const int value = it->Value();
if (value != index
&& (value >= size || !assignment.Contains(nexts[value]))) {
const int64 evaluation = evaluator_->Run(index, value);
if (evaluation <= best_evaluation) {
best_evaluation = evaluation;
best_value = value;
}
}
}
}
return best_value;
}
RoutingModel* const model_;
scoped_ptr<ResultCallback2<int64, int64, int64> > evaluator_;
};
// Decision builder to build a solution with all nodes inactive. It does no
// branching and may fail if some nodes cannot be made inactive.
@@ -1188,6 +1312,8 @@ RoutingModel::GetSelectedFirstSolutionStrategy() const {
return ROUTING_ALL_UNPERFORMED;
} else if (FLAGS_routing_first_solution.compare("BestInsertion") == 0) {
return ROUTING_BEST_INSERTION;
} else if (FLAGS_routing_first_solution.compare("FastOnePath") == 0) {
return ROUTING_FAST_ONE_PATH;
}
return first_solution_strategy_;
}
@@ -1745,6 +1871,17 @@ int64 RoutingModel::NodeToIndex(NodeIndex node) const {
return node_to_index_[node];
}
void RoutingModel::GetDisjunctionIndicesFromIndex(int64 index,
std::vector<int>* indices) const {
CHECK_NOTNULL(indices);
int disjunction = -1;
if (FindCopy(node_to_disjunction_, index, &disjunction)) {
*indices = disjunctions_[disjunction].nodes;
} else {
indices->clear();
}
}
int64 RoutingModel::GetArcCost(int64 i, int64 j, int64 vehicle) {
if (vehicle < 0) {
// If a node is inactive, vehicle will be set to -1; handle this situation
@@ -1852,6 +1989,75 @@ void RoutingModel::CheckDepot() {
}
}
Assignment* RoutingModel::GetOrCreateAssignment() {
if (assignment_ == NULL) {
const int size = Size();
assignment_ = solver_->RevAlloc(new Assignment(solver_.get()));
assignment_->Add(nexts_.get(), size);
if (!homogeneous_costs_) {
assignment_->Add(vehicle_vars_.get(), size + vehicles_);
}
assignment_->AddObjective(cost_);
}
return assignment_;
}
SearchLimit* RoutingModel::GetOrCreateLimit() {
if (limit_ == NULL) {
limit_ = solver_->MakeLimit(time_limit_ms_,
kint64max,
kint64max,
FLAGS_routing_solution_limit,
true);
}
return limit_;
}
SearchLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
if (ls_limit_ == NULL) {
ls_limit_ = solver_->MakeLimit(time_limit_ms_,
kint64max,
kint64max,
1,
true);
}
return ls_limit_;
}
SearchLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
if (lns_limit_ == NULL) {
lns_limit_ = solver_->MakeLimit(lns_time_limit_ms_,
kint64max,
kint64max,
kint64max);
}
return lns_limit_;
}
LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
const int size = Size();
if (pickup_delivery_pairs_.size() > 0) {
const IntVar* const* vehicle_vars =
homogeneous_costs_ ? NULL : vehicle_vars_.get();
return MakePairActive(solver_.get(),
nexts_.get(),
vehicle_vars,
pickup_delivery_pairs_,
size);
} else {
if (homogeneous_costs_) {
return solver_->MakeOperator(nexts_.get(),
size,
Solver::MAKEACTIVE);
} else {
return solver_->MakeOperator(nexts_.get(),
vehicle_vars_.get(),
size,
Solver::MAKEACTIVE);
}
}
}
#define CP_ROUTING_PUSH_BACK_OPERATOR(operator_type) \
if (homogeneous_costs_) { \
operators.push_back(solver_->MakeOperator(nexts_.get(), \
@@ -1878,119 +2084,17 @@ void RoutingModel::CheckDepot() {
operator_type)); \
}
void RoutingModel::SetUpSearch() {
LocalSearchOperator* RoutingModel::CreateNeighborhoodOperators() {
const int size = Size();
assignment_ = solver_->RevAlloc(new Assignment(solver_.get()));
assignment_->Add(nexts_.get(), size);
if (!homogeneous_costs_) {
assignment_->Add(vehicle_vars_.get(), size + vehicles_);
}
assignment_->AddObjective(cost_);
Assignment* full_assignment =
solver_->RevAlloc(new Assignment(solver_.get()));
for (ConstIter<VarMap> it(cumuls_); !it.at_end(); ++it) {
full_assignment->Add(it->second, size + vehicles_);
}
for (int i = 0; i < extra_vars_.size(); ++i) {
full_assignment->Add(extra_vars_[i]);
}
full_assignment->Add(nexts_.get(), size);
full_assignment->Add(active_.get(), size);
full_assignment->Add(vehicle_vars_.get(), size + vehicles_);
full_assignment->AddObjective(cost_);
collect_assignments_ =
solver_->MakeBestValueSolutionCollector(full_assignment, false);
monitors_.push_back(collect_assignments_);
SearchMonitor* optimize;
switch (GetSelectedMetaheuristic()) {
case ROUTING_GUIDED_LOCAL_SEARCH:
LG << "Using Guided Local Search";
if (homogeneous_costs_) {
optimize = solver_->MakeGuidedLocalSearch(
false,
cost_,
NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost),
FLAGS_routing_optimization_step,
nexts_.get(), size,
FLAGS_routing_guided_local_search_lamda_coefficient);
} else {
optimize = solver_->MakeGuidedLocalSearch(
false,
cost_,
BuildCostCallback(),
FLAGS_routing_optimization_step,
nexts_.get(), vehicle_vars_.get(), size,
FLAGS_routing_guided_local_search_lamda_coefficient);
}
break;
case ROUTING_SIMULATED_ANNEALING:
LG << "Using Simulated Annealing";
optimize =
solver_->MakeSimulatedAnnealing(false, cost_,
FLAGS_routing_optimization_step,
100);
break;
case ROUTING_TABU_SEARCH:
LG << "Using Tabu Search";
optimize = solver_->MakeTabuSearch(false, cost_,
FLAGS_routing_optimization_step,
nexts_.get(), size,
10, 10, .8);
break;
default:
LG << "Using greedy descent";
optimize = solver_->MakeMinimize(cost_, FLAGS_routing_optimization_step);
}
monitors_.push_back(optimize);
limit_ = solver_->MakeLimit(time_limit_ms_,
kint64max,
kint64max,
FLAGS_routing_solution_limit,
true);
monitors_.push_back(limit_);
ls_limit_ = solver_->MakeLimit(time_limit_ms_,
kint64max,
kint64max,
1,
true);
lns_limit_ = solver_->MakeLimit(lns_time_limit_ms_,
kint64max,
kint64max,
kint64max);
std::vector<LocalSearchOperator*> operators = extra_operators_;
LocalSearchOperator* insertion_operator = NULL;
if (pickup_delivery_pairs_.size() > 0) {
const IntVar* const* vehicle_vars =
homogeneous_costs_ ? NULL : vehicle_vars_.get();
insertion_operator = MakePairActive(solver_.get(),
nexts_.get(),
vehicle_vars,
pickup_delivery_pairs_,
size);
operators.push_back(insertion_operator);
operators.push_back(MakePairRelocate(solver_.get(),
nexts_.get(),
vehicle_vars,
pickup_delivery_pairs_,
size));
} else {
if (homogeneous_costs_) {
insertion_operator = solver_->MakeOperator(nexts_.get(),
size,
Solver::MAKEACTIVE);
} else {
insertion_operator = solver_->MakeOperator(nexts_.get(),
vehicle_vars_.get(),
size,
Solver::MAKEACTIVE);
}
}
if (vehicles_ > 1) {
if (!FLAGS_routing_no_relocate) {
@@ -2016,7 +2120,11 @@ void RoutingModel::SetUpSearch() {
}
if (!FLAGS_routing_no_make_active && disjunctions_.size() != 0) {
CP_ROUTING_PUSH_BACK_OPERATOR(Solver::MAKEINACTIVE);
CP_ROUTING_PUSH_BACK_OPERATOR(Solver::MAKEACTIVE);
// TODO(user): On cases where we have a mix of node pairs and
// individual nodes, only pairs are going to be made active. In practice
// such cases should not appear, but we might want to be robust to them
// anyway.
operators.push_back(CreateInsertionOperator());
if (!FLAGS_routing_use_extended_swap_active) {
CP_ROUTING_PUSH_BACK_OPERATOR(Solver::SWAPACTIVE);
} else {
@@ -2040,70 +2148,75 @@ void RoutingModel::SetUpSearch() {
CP_ROUTING_PUSH_BACK_OPERATOR(Solver::UNACTIVELNS);
}
}
return solver_->ConcatenateOperators(operators);
}
LocalSearchOperator* local_search_operator =
solver_->ConcatenateOperators(operators);
#undef CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR
#undef CP_ROUTING_PUSH_BACK_OPERATOR
DecisionBuilder* finalize_solution =
solver_->MakePhase(nexts_.get(), size,
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
std::vector<LocalSearchFilter*> filters;
if (FLAGS_routing_use_objective_filter) {
if (homogeneous_costs_) {
LocalSearchFilter* filter =
solver_->MakeLocalSearchObjectiveFilter(
nexts_.get(),
size,
NewPermanentCallback(this,
&RoutingModel::GetHomogeneousFilterCost),
cost_,
Solver::EQ,
Solver::SUM);
filters.push_back(filter);
} else {
LocalSearchFilter* filter =
solver_->MakeLocalSearchObjectiveFilter(
nexts_.get(),
vehicle_vars_.get(),
size,
NewPermanentCallback(this, &RoutingModel::GetFilterCost),
cost_,
Solver::EQ,
Solver::SUM);
filters.push_back(filter);
}
}
if (FLAGS_routing_use_pickup_and_delivery_filter
&& pickup_delivery_pairs_.size() > 0) {
filters.push_back(solver_->RevAlloc(new NodePrecedenceFilter(
nexts_.get(),
Size(),
Size() + vehicles_,
pickup_delivery_pairs_,
"")));
}
if (FLAGS_routing_use_path_cumul_filter) {
for (ConstIter<VarMap> iter(cumuls_); !iter.at_end(); ++iter) {
const string& name = iter->first;
filters.push_back(solver_->RevAlloc(
new PathCumulFilter(nexts_.get(),
Size(),
iter->second,
Size() + vehicles_,
transit_evaluators_[name],
name)));
const std::vector<LocalSearchFilter*>&
RoutingModel::GetOrCreateLocalSearchFilters() {
if (filters_.empty()) {
const int size = Size();
if (FLAGS_routing_use_objective_filter) {
if (homogeneous_costs_) {
LocalSearchFilter* filter =
solver_->MakeLocalSearchObjectiveFilter(
nexts_.get(),
size,
NewPermanentCallback(this,
&RoutingModel::GetHomogeneousFilterCost),
cost_,
Solver::EQ,
Solver::SUM);
filters_.push_back(filter);
} else {
LocalSearchFilter* filter =
solver_->MakeLocalSearchObjectiveFilter(
nexts_.get(),
vehicle_vars_.get(),
size,
NewPermanentCallback(this, &RoutingModel::GetFilterCost),
cost_,
Solver::EQ,
Solver::SUM);
filters_.push_back(filter);
}
}
if (FLAGS_routing_use_pickup_and_delivery_filter
&& pickup_delivery_pairs_.size() > 0) {
filters_.push_back(solver_->RevAlloc(new NodePrecedenceFilter(
nexts_.get(),
Size(),
Size() + vehicles_,
pickup_delivery_pairs_,
"")));
}
if (FLAGS_routing_use_path_cumul_filter) {
for (ConstIter<VarMap> iter(cumuls_); !iter.at_end(); ++iter) {
const string& name = iter->first;
filters_.push_back(solver_->RevAlloc(
new PathCumulFilter(nexts_.get(),
Size(),
iter->second,
Size() + vehicles_,
transit_evaluators_[name],
name)));
}
}
}
return filters_;
}
LocalSearchPhaseParameters* parameters =
solver_->MakeLocalSearchPhaseParameters(
local_search_operator,
solver_->MakeSolveOnce(finalize_solution, lns_limit_),
ls_limit_,
filters);
DecisionBuilder* RoutingModel::CreateSolutionFinalizer() {
return solver_->MakePhase(nexts_.get(), Size(),
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
}
DecisionBuilder* RoutingModel::CreateFirstSolutionDecisionBuilder() {
const int size = Size();
DecisionBuilder* finalize_solution = CreateSolutionFinalizer();
DecisionBuilder* first_solution = finalize_solution;
switch (GetSelectedFirstSolutionStrategy()) {
case ROUTING_GLOBAL_CHEAPEST_ARC:
@@ -2158,28 +2271,37 @@ void RoutingModel::SetUpSearch() {
kint64max,
true);
DecisionBuilder* const finalize =
solver_->MakeSolveOnce(finalize_solution, lns_limit_);
solver_->MakeSolveOnce(finalize_solution,
GetOrCreateLargeNeighborhoodSearchLimit());
LocalSearchPhaseParameters* const insertion_parameters =
solver_->MakeLocalSearchPhaseParameters(
insertion_operator,
CreateInsertionOperator(),
finalize,
ls_limit,
filters);
GetOrCreateLocalSearchFilters());
std::vector<SearchMonitor*> monitors;
monitors.push_back(limit_);
monitors.push_back(GetOrCreateLimit());
first_solution = solver_->MakeNestedOptimize(
solver_->MakeLocalSearchPhase(
nexts_.get(),
size,
solver_->RevAlloc(new AllUnperformed(this)),
insertion_parameters),
assignment_,
GetOrCreateAssignment(),
false,
FLAGS_routing_optimization_step,
monitors);
first_solution = solver_->Compose(first_solution, finalize);
break;
}
case ROUTING_FAST_ONE_PATH:
first_solution =
solver_->RevAlloc(new FastOnePathBuilder(
this,
NewPermanentCallback(
this,
&RoutingModel::GetFirstSolutionCost)));
break;
default:
LOG(WARNING) << "Unknown argument for routing_first_solution, "
"using default";
@@ -2189,40 +2311,129 @@ void RoutingModel::SetUpSearch() {
solver_->MakeApplyBranchSelector(NewPermanentCallback(&LeftDive));
first_solution = solver_->Compose(apply, first_solution);
}
return first_solution;
}
if (FLAGS_routing_dfs) {
solve_db_ = finalize_solution;
LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters() {
return solver_->MakeLocalSearchPhaseParameters(
CreateNeighborhoodOperators(),
solver_->MakeSolveOnce(CreateSolutionFinalizer(),
GetOrCreateLargeNeighborhoodSearchLimit()),
GetOrCreateLocalSearchLimit(),
GetOrCreateLocalSearchFilters());
}
DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder() {
const int size = Size();
DecisionBuilder* first_solution = CreateFirstSolutionDecisionBuilder();
LocalSearchPhaseParameters* parameters = CreateLocalSearchParameters();
if (homogeneous_costs_) {
return solver_->MakeLocalSearchPhase(nexts_.get(),
size,
first_solution,
parameters);
} else {
if (homogeneous_costs_) {
solve_db_ = solver_->MakeLocalSearchPhase(nexts_.get(),
size,
first_solution,
parameters);
} else {
const int all_size = size + size + vehicles_;
scoped_array<IntVar*> all_vars(new IntVar*[all_size]);
for (int i = 0; i < size; ++i) {
all_vars[i] = nexts_[i];
}
for (int i = size; i < all_size; ++i) {
all_vars[i] = vehicle_vars_[i - size];
}
solve_db_ = solver_->MakeLocalSearchPhase(all_vars.get(),
all_size,
first_solution,
parameters);
const int all_size = size + size + vehicles_;
scoped_array<IntVar*> all_vars(new IntVar*[all_size]);
for (int i = 0; i < size; ++i) {
all_vars[i] = nexts_[i];
}
for (int i = size; i < all_size; ++i) {
all_vars[i] = vehicle_vars_[i - size];
}
return solver_->MakeLocalSearchPhase(all_vars.get(),
all_size,
first_solution,
parameters);
}
}
void RoutingModel::SetupDecisionBuilders() {
if (FLAGS_routing_dfs) {
solve_db_ = CreateFirstSolutionDecisionBuilder();
} else {
solve_db_ = CreateLocalSearchDecisionBuilder();
}
CHECK_NOTNULL(preassignment_);
DecisionBuilder* restore_preassignment =
solver_->MakeRestoreAssignment(preassignment_);
solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
improve_db_ =
solver_->Compose(restore_preassignment,
solver_->MakeLocalSearchPhase(assignment_, parameters));
solver_->MakeLocalSearchPhase(
GetOrCreateAssignment(),
CreateLocalSearchParameters()));
restore_assignment_ =
solver_->Compose(solver_->MakeRestoreAssignment(assignment_),
finalize_solution);
solver_->Compose(solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
CreateSolutionFinalizer());
}
void RoutingModel::SetupMetaheuristics() {
const int size = Size();
SearchMonitor* optimize;
switch (GetSelectedMetaheuristic()) {
case ROUTING_GUIDED_LOCAL_SEARCH:
LG << "Using Guided Local Search";
if (homogeneous_costs_) {
optimize = solver_->MakeGuidedLocalSearch(
false,
cost_,
NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost),
FLAGS_routing_optimization_step,
nexts_.get(), size,
FLAGS_routing_guided_local_search_lamda_coefficient);
} else {
optimize = solver_->MakeGuidedLocalSearch(
false,
cost_,
BuildCostCallback(),
FLAGS_routing_optimization_step,
nexts_.get(), vehicle_vars_.get(), size,
FLAGS_routing_guided_local_search_lamda_coefficient);
}
break;
case ROUTING_SIMULATED_ANNEALING:
LG << "Using Simulated Annealing";
optimize =
solver_->MakeSimulatedAnnealing(false, cost_,
FLAGS_routing_optimization_step,
100);
break;
case ROUTING_TABU_SEARCH:
LG << "Using Tabu Search";
optimize = solver_->MakeTabuSearch(false, cost_,
FLAGS_routing_optimization_step,
nexts_.get(), size,
10, 10, .8);
break;
default:
LG << "Using greedy descent";
optimize = solver_->MakeMinimize(cost_, FLAGS_routing_optimization_step);
}
monitors_.push_back(optimize);
}
void RoutingModel::SetupAssignmentCollector() {
const int size = Size();
Assignment* full_assignment =
solver_->RevAlloc(new Assignment(solver_.get()));
for (ConstIter<VarMap> it(cumuls_); !it.at_end(); ++it) {
full_assignment->Add(it->second, size + vehicles_);
}
for (int i = 0; i < extra_vars_.size(); ++i) {
full_assignment->Add(extra_vars_[i]);
}
full_assignment->Add(nexts_.get(), size);
full_assignment->Add(active_.get(), size);
full_assignment->Add(vehicle_vars_.get(), size + vehicles_);
full_assignment->AddObjective(cost_);
collect_assignments_ =
solver_->MakeBestValueSolutionCollector(full_assignment, false);
monitors_.push_back(collect_assignments_);
}
void RoutingModel::SetupTrace() {
if (FLAGS_routing_trace) {
const int kLogPeriod = 10000;
SearchMonitor* trace = solver_->MakeSearchLog(kLogPeriod, cost_);
@@ -2234,8 +2445,18 @@ void RoutingModel::SetUpSearch() {
}
}
#undef CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR
#undef CP_ROUTING_PUSH_BACK_OPERATOR
void RoutingModel::SetupSearchMonitors() {
monitors_.push_back(GetOrCreateLimit());
SetupMetaheuristics();
SetupAssignmentCollector();
SetupTrace();
}
void RoutingModel::SetupSearch() {
SetupDecisionBuilders();
SetupSearchMonitors();
}
IntVar* RoutingModel::CumulVar(int64 index, const string& name) const {
IntVar** named_cumuls = FindPtrOrNull(cumuls_, name);

View File

@@ -182,7 +182,7 @@ class RoutingModel {
// Select the first node with an unbound successor and connect it to the
// first available node.
// This is equivalent to the CHOOSE_FIRST_UNBOUND strategy combined with
// ASSIGN_MIN_VALUE (cf. constraint_soler.h).
// ASSIGN_MIN_VALUE (cf. constraint_solver.h).
ROUTING_DEFAULT_STRATEGY,
// Iteratively connect two nodes which produce the cheapest route segment.
ROUTING_GLOBAL_CHEAPEST_ARC,
@@ -202,7 +202,13 @@ class RoutingModel {
// Iteratively build a solution by inserting nodes at their cheapest (best)
// position. As of 2/2012, only works on models with optional nodes
// (with finite penalty costs).
ROUTING_BEST_INSERTION
ROUTING_BEST_INSERTION,
// Builds a solution with a single path without propagating. Is very fast
// but has a very high probability of failing if the problem contains other
// constraints than path-related constraints.
// Based on an addition heuristics extending a path from its start node with
// the cheapest arc according to an evaluator.
ROUTING_FAST_ONE_PATH
};
// Metaheuristics used to guide the search. Apart greedy descent, they will
@@ -577,6 +583,9 @@ class RoutingModel {
// considered a failure case. Clients who need start and end
// variable indices should use RoutingModel::Start and RoutingModel::End.
int64 NodeToIndex(NodeIndex node) const;
// Returns the variable indices of the nodes in the same disjunction as the
// node corresponding to the variable of index 'index'.
void GetDisjunctionIndicesFromIndex(int64 index, std::vector<int>* indices) const;
// Time limits
// Returns the current time limit used in the search.
@@ -644,7 +653,26 @@ class RoutingModel {
CloseModel();
}
}
void SetUpSearch();
// Sets up search objects, such as decision builders and monitors.
void SetupSearch();
// Set of auxiliary methods used to setup the search.
// TODO(user): Document each auxiliary method.
Assignment* GetOrCreateAssignment();
SearchLimit* GetOrCreateLimit();
SearchLimit* GetOrCreateLocalSearchLimit();
SearchLimit* GetOrCreateLargeNeighborhoodSearchLimit();
LocalSearchOperator* CreateInsertionOperator();
LocalSearchOperator* CreateNeighborhoodOperators();
const std::vector<LocalSearchFilter*>& GetOrCreateLocalSearchFilters();
DecisionBuilder* CreateSolutionFinalizer();
DecisionBuilder* CreateFirstSolutionDecisionBuilder();
LocalSearchPhaseParameters* CreateLocalSearchParameters();
DecisionBuilder* CreateLocalSearchDecisionBuilder();
void SetupDecisionBuilders();
void SetupMetaheuristics();
void SetupAssignmentCollector();
void SetupTrace();
void SetupSearchMonitors();
IntVar** GetOrMakeCumuls(int64 capacity, const string& name);
IntVar** GetOrMakeTransits(NodeEvaluator2* evaluator,
@@ -701,6 +729,7 @@ class RoutingModel {
Assignment* preassignment_;
std::vector<IntVar*> extra_vars_;
std::vector<LocalSearchOperator*> extra_operators_;
std::vector<LocalSearchFilter*> filters_;
int64 time_limit_ms_;
int64 lns_time_limit_ms_;

View File

@@ -183,39 +183,33 @@ static int64 PyCallback2NodeIndexNodeIndex(
}
#endif
#ifdef SWIGJAVA
%module(directors="1") main
%feature("director") NodeEvaluator2;
%{
class NodeIndexResultCallback2
: public ResultCallback2<int64,
operations_research::RoutingModel::NodeIndex,
operations_research::RoutingModel::NodeIndex> {
namespace operations_research {
class NodeEvaluator2 {
public:
NodeIndexResultCallback2(ResultCallback2<int64, int64, int64>* callback)
: callback_(callback) {
CHECK_NOTNULL(callback_);
int64 runAux(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) {
return run(i.value(), j.value());
}
virtual ~NodeIndexResultCallback2() {}
virtual bool IsRepeatable() const { return true; }
virtual int64 Run(operations_research::RoutingModel::NodeIndex i,
operations_research::RoutingModel::NodeIndex j) {
return callback_->Run(i.value(), j.value());
virtual int64 run(int i, int j) = 0;
RoutingModel::NodeEvaluator2* GetPermanentCallback() {
return NewPermanentCallback(this, &NodeEvaluator2::runAux);
}
scoped_ptr<ResultCallback2<int64, int64, int64> > callback_;
virtual ~NodeEvaluator2() {}
};
} // namespace operations_research
%}
%typemap(jstype) RoutingModel::NodeEvaluator2* "LongResultCallback2";
%typemap(javain) RoutingModel::NodeEvaluator2* "SWIGTYPE_p_ResultCallback2Tlong_long_long_long_long_long_t.getCPtr($javainput.GetPermanentCallback())";
%typemap(in) RoutingModel::NodeEvaluator2* {
if ($input) {
ResultCallback2<int64, int64, int64>* const callback =
(ResultCallback2<int64, int64, int64>*)$input;
$1 = new NodeIndexResultCallback2(callback);
} else {
SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException,
"null evaluator");
return $null;
}
}
class NodeEvaluator2 {
public:
virtual ~NodeEvaluator2();
virtual int64 run(int i, int j) = 0;
operations_research::RoutingModel::NodeEvaluator2* GetPermanentCallback();
};
%typemap(jstype) RoutingModel::NodeEvaluator2* "NodeEvaluator2";
%typemap(javain) RoutingModel::NodeEvaluator2* "SWIGTYPE_p_ResultCallback2T_long_long__RoutingModel_NodeIndex__RoutingModel_NodeIndex_t.getCPtr($javainput.GetPermanentCallback())";
#endif // SWIGJAVA
#ifdef SWIGCSHARP
%module(directors="1") main

View File

@@ -365,47 +365,82 @@ SearchMonitor* Solver::MakeSearchTrace(const string& prefix) {
return RevAlloc(new SearchTrace(this, prefix));
}
// ---------- Composite Decision Builder --------
namespace {
class CompositeDecisionBuilder : public DecisionBuilder {
public:
CompositeDecisionBuilder();
explicit CompositeDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
virtual ~CompositeDecisionBuilder();
void Add(DecisionBuilder* const db);
virtual void AppendMonitors(Solver* const solver,
std::vector<SearchMonitor*>* const monitors);
virtual void Accept(ModelVisitor* const visitor) const;
protected:
std::vector<DecisionBuilder*> builders_;
};
CompositeDecisionBuilder::CompositeDecisionBuilder() {}
CompositeDecisionBuilder::CompositeDecisionBuilder(
const std::vector<DecisionBuilder*>& dbs) {
for (int i = 0; i < dbs.size(); ++i) {
Add(dbs[i]);
}
}
CompositeDecisionBuilder::~CompositeDecisionBuilder() {}
void CompositeDecisionBuilder::Add(DecisionBuilder* const db) {
if (db != NULL) {
builders_.push_back(db);
}
}
void CompositeDecisionBuilder::AppendMonitors(
Solver* const solver,
std::vector<SearchMonitor*>* const monitors) {
for (ConstIter<std::vector<DecisionBuilder*> > it(builders_);
!it.at_end();
++it) {
(*it)->AppendMonitors(solver, monitors);
}
}
void CompositeDecisionBuilder::Accept(ModelVisitor* const visitor) const {
for (ConstIter<std::vector<DecisionBuilder*> > it(builders_);
!it.at_end();
++it) {
(*it)->Accept(visitor);
}
}
} // namespace
// ---------- Compose Decision Builder ----------
namespace {
class ComposeDecisionBuilder : public DecisionBuilder {
class ComposeDecisionBuilder : public CompositeDecisionBuilder {
public:
ComposeDecisionBuilder();
explicit ComposeDecisionBuilder(const std::vector<DecisionBuilder*>& dbs)
: start_index_(0) {
for (int i = 0; i < dbs.size(); ++i) {
Add(dbs[i]);
}
}
virtual ~ComposeDecisionBuilder() {}
explicit ComposeDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
~ComposeDecisionBuilder();
virtual Decision* Next(Solver* const s);
virtual string DebugString() const;
void Add(DecisionBuilder* const db);
virtual void AppendMonitors(Solver* const solver,
std::vector<SearchMonitor*>* const monitors) {
for (ConstIter<std::vector<DecisionBuilder*> > it(builders_);
!it.at_end();
++it) {
(*it)->AppendMonitors(solver, monitors);
}
}
virtual void Accept(ModelVisitor* const visitor) const {
for (ConstIter<std::vector<DecisionBuilder*> > it(builders_);
!it.at_end();
++it) {
(*it)->Accept(visitor);
}
}
private:
std::vector<DecisionBuilder*> builders_;
int start_index_;
};
ComposeDecisionBuilder::ComposeDecisionBuilder() : start_index_(0) {}
ComposeDecisionBuilder::ComposeDecisionBuilder(
const std::vector<DecisionBuilder*>& dbs)
: CompositeDecisionBuilder(dbs), start_index_(0) {}
ComposeDecisionBuilder::~ComposeDecisionBuilder() {}
Decision* ComposeDecisionBuilder::Next(Solver* const s) {
const int size = builders_.size();
for (int i = start_index_; i < size; ++i) {
@@ -424,12 +459,6 @@ string ComposeDecisionBuilder::DebugString() const {
"ComposeDecisionBuilder(%s)",
DebugStringArray(builders_.data(), builders_.size(), ", ").c_str());
}
void ComposeDecisionBuilder::Add(DecisionBuilder* const db) {
if (db != NULL) {
builders_.push_back(db);
}
}
} // namespace
DecisionBuilder* Solver::Compose(DecisionBuilder* const db1,
@@ -466,6 +495,129 @@ DecisionBuilder* Solver::Compose(const std::vector<DecisionBuilder*>& dbs) {
return RevAlloc(new ComposeDecisionBuilder(dbs));
}
// ---------- Try Decision Builder ----------
namespace {
class TryDecisionBuilder;
class TryDecision : public Decision {
public:
explicit TryDecision(TryDecisionBuilder* const try_builder);
virtual ~TryDecision();
virtual void Apply(Solver* const solver);
virtual void Refute(Solver* const solver);
virtual string DebugString() const {
return "TryDecision";
}
private:
TryDecisionBuilder* const try_builder_;
};
class TryDecisionBuilder : public CompositeDecisionBuilder {
public:
TryDecisionBuilder();
explicit TryDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
virtual ~TryDecisionBuilder();
virtual Decision* Next(Solver* const s);
virtual string DebugString() const;
void AdvanceToNextBuilder(Solver* const solver);
private:
TryDecision try_decision_;
int current_builder_;
bool start_new_builder_;
};
TryDecision::TryDecision(TryDecisionBuilder* const try_builder)
: try_builder_(try_builder) {}
TryDecision::~TryDecision() {}
void TryDecision::Apply(Solver* const solver) {}
void TryDecision::Refute(Solver* const solver) {
try_builder_->AdvanceToNextBuilder(solver);
}
TryDecisionBuilder::TryDecisionBuilder()
: CompositeDecisionBuilder(),
try_decision_(this),
current_builder_(-1),
start_new_builder_(true) {}
TryDecisionBuilder::TryDecisionBuilder(const std::vector<DecisionBuilder*>& dbs)
: CompositeDecisionBuilder(dbs),
try_decision_(this),
current_builder_(-1),
start_new_builder_(true) {}
TryDecisionBuilder::~TryDecisionBuilder() {}
Decision* TryDecisionBuilder::Next(Solver* const solver) {
if (current_builder_ < 0) {
solver->SaveAndSetValue(&current_builder_, 0);
start_new_builder_ = true;
}
if (start_new_builder_) {
start_new_builder_ = false;
return &try_decision_;
} else {
return builders_[current_builder_]->Next(solver);
}
}
string TryDecisionBuilder::DebugString() const {
return StringPrintf(
"TryDecisionBuilder(%s)",
DebugStringArray(builders_.data(), builders_.size(), ", ").c_str());
}
void TryDecisionBuilder::AdvanceToNextBuilder(Solver* const solver) {
++current_builder_;
start_new_builder_ = true;
if (current_builder_ >= builders_.size()) {
solver->Fail();
}
}
} // namespace
DecisionBuilder* Solver::Try(DecisionBuilder* const db1,
DecisionBuilder* const db2) {
TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
try_db->Add(db1);
try_db->Add(db2);
return try_db;
}
DecisionBuilder* Solver::Try(DecisionBuilder* const db1,
DecisionBuilder* const db2,
DecisionBuilder* const db3) {
TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
try_db->Add(db1);
try_db->Add(db2);
try_db->Add(db3);
return try_db;
}
DecisionBuilder* Solver::Try(DecisionBuilder* const db1,
DecisionBuilder* const db2,
DecisionBuilder* const db3,
DecisionBuilder* const db4) {
TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
try_db->Add(db1);
try_db->Add(db2);
try_db->Add(db3);
try_db->Add(db4);
return try_db;
}
DecisionBuilder* Solver::Try(const std::vector<DecisionBuilder*>& dbs) {
return RevAlloc(new TryDecisionBuilder(dbs));
}
// ---------- Variable Assignments ----------
// ----- BaseAssignmentSelector -----