change distance callback on node in routing to NodeEvaluator2; add disjunction of decision builders.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(¤t_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 -----
|
||||
|
||||
Reference in New Issue
Block a user