From 7221cc0b36151e6fced4264dfbed2da755cebe6f Mon Sep 17 00:00:00 2001 From: "laurent.perron@gmail.com" Date: Sat, 6 Jul 2013 19:43:21 +0000 Subject: [PATCH] add ability to inject decision builder inside the default search; adapt flatzinc interface --- src/constraint_solver/constraint_solver.h | 15 +- src/constraint_solver/default_search.cc | 129 ++------ src/constraint_solver/search.cc | 3 + src/flatzinc/flatzinc.h | 12 +- src/flatzinc/fz.cc | 86 +++--- src/flatzinc/fz_search.cc | 348 ++++++++++++---------- 6 files changed, 272 insertions(+), 321 deletions(-) diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index 5d792861fa..87dab5a1c3 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -242,21 +242,12 @@ struct DefaultPhaseParameters { VERBOSE = 2 }; - enum SearchStrategy { - CHOOSE_FIRST_UNBOUND_ASSIGN_MIN, - CHOOSE_MIN_SIZE_ASSIGN_MIN, - IMPACT_BASED_SEARCH, - CHOOSE_RANDOM_ASSIGN_MIN, - CHOOSE_RANDOM_ASSIGN_MAX, - }; - static const int kDefaultNumberOfSplits; static const int kDefaultHeuristicPeriod; static const int kDefaultHeuristicNumFailuresLimit; static const int kDefaultSeed; static const double kDefaultRestartLogSize; static const bool kDefaultUseNoGoods; - static const DefaultPhaseParameters::SearchStrategy kDefaultSearchStrategy; DefaultPhaseParameters() : var_selection_schema(CHOOSE_MAX_SUM_IMPACT), @@ -270,7 +261,7 @@ struct DefaultPhaseParameters { restart_log_size(kDefaultRestartLogSize), display_level(NORMAL), use_no_goods(kDefaultUseNoGoods), - search_strategy(kDefaultSearchStrategy) {} + decision_builder(NULL) {} // This parameter describes how the next variable to instantiate // will be chosen. @@ -321,8 +312,8 @@ struct DefaultPhaseParameters { // Should we use Nogoods when restarting. The default is false. bool use_no_goods; - // Used in tests. Disable impacts and run choose first unbound, assign min. - SearchStrategy search_strategy; + // When defined, this override the default impact based decision builder. + DecisionBuilder* decision_builder; }; diff --git a/src/constraint_solver/default_search.cc b/src/constraint_solver/default_search.cc index d4d2b7d75e..bb2cc65518 100644 --- a/src/constraint_solver/default_search.cc +++ b/src/constraint_solver/default_search.cc @@ -43,9 +43,6 @@ const int DefaultPhaseParameters::kDefaultHeuristicNumFailuresLimit = 30; const int DefaultPhaseParameters::kDefaultSeed = 0; const double DefaultPhaseParameters::kDefaultRestartLogSize = -1.0; const bool DefaultPhaseParameters::kDefaultUseNoGoods = true; -const DefaultPhaseParameters::SearchStrategy -DefaultPhaseParameters::kDefaultSearchStrategy = - DefaultPhaseParameters::IMPACT_BASED_SEARCH; class NoGoodManager; @@ -1005,13 +1002,16 @@ class RunHeuristicsAsDives : public Decision { bool RunAllHeuristics(Solver* const solver) { if (run_all_heuristics_) { + LOG(INFO) << "Start"; for (int index = 0; index < heuristics_.size(); ++index) { for (int run = 0; run < heuristics_[index]->runs; ++run) { if (RunOneHeuristic(solver, index)) { + LOG(INFO) << "Success"; return true; } } } + LOG(INFO) << "No Success"; return false; } else { const int index = random_.Uniform(heuristics_.size()); @@ -1162,21 +1162,16 @@ class DefaultIntegerSearch : public DecisionBuilder { return &heuristics_; } - IntVar* var = NULL; - int64 value = 0; - if (FindVarValue(&var, &value)) { - return solver->MakeAssignVariableValue(var, value); - } else { - return NULL; - } + return parameters_.decision_builder != NULL ? + parameters_.decision_builder->Next(solver) : + ImpactNext(solver); } virtual void AppendMonitors(Solver* const solver, std::vector* const extras) { CHECK_NOTNULL(solver); CHECK_NOTNULL(extras); - if (parameters_.search_strategy == - DefaultPhaseParameters::IMPACT_BASED_SEARCH) { + if (parameters_.decision_builder == NULL) { extras->push_back(&impact_recorder_); } if (parameters_.restart_log_size >= 0) { @@ -1195,11 +1190,11 @@ class DefaultIntegerSearch : public DecisionBuilder { virtual string DebugString() const { string out = "DefaultIntegerSearch("; - if (parameters_.search_strategy == - DefaultPhaseParameters::IMPACT_BASED_SEARCH) { + if (parameters_.decision_builder == NULL) { out.append("Impact Based Search, "); } else { - out.append("Choose First Unbound Assing Min, "); + out.append(parameters_.decision_builder->DebugString()); + out.append(", "); } out.append(DebugStringVector(vars_, ", ")); out.append(")"); @@ -1211,8 +1206,7 @@ class DefaultIntegerSearch : public DecisionBuilder { if (init_done_) { return; } - if (parameters_.search_strategy == - DefaultPhaseParameters::IMPACT_BASED_SEARCH) { + if (parameters_.decision_builder == NULL) { // Decide if we are doing impacts, no if one variable is too big. for (int i = 0; i < vars_.size(); ++i) { if (vars_[i]->Max() - vars_[i]->Min() > 0xFFFFFF) { @@ -1220,8 +1214,9 @@ class DefaultIntegerSearch : public DecisionBuilder { LOG(INFO) << "Domains are too large, switching to simple " << "heuristics"; } - parameters_.search_strategy = - DefaultPhaseParameters::CHOOSE_FIRST_UNBOUND_ASSIGN_MIN; + parameters_.decision_builder = + solver->MakePhase(vars_, Solver::CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver::ASSIGN_MIN_VALUE); init_done_ = true; return; } @@ -1232,8 +1227,9 @@ class DefaultIntegerSearch : public DecisionBuilder { LOG(INFO) << "Search space is too small, switching to simple " << "heuristics"; } - parameters_.search_strategy = - DefaultPhaseParameters::CHOOSE_FIRST_UNBOUND_ASSIGN_MIN; + parameters_.decision_builder = + solver->MakePhase(vars_, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); init_done_ = true; return; } @@ -1257,31 +1253,13 @@ class DefaultIntegerSearch : public DecisionBuilder { } } - bool FindVarValue(IntVar** const var, int64* const value) { - switch (parameters_.search_strategy) { - case DefaultPhaseParameters::IMPACT_BASED_SEARCH: - return FindUnboundVarValueWithImpact(var, value); - case DefaultPhaseParameters::CHOOSE_FIRST_UNBOUND_ASSIGN_MIN: - return FindUnboundVarValueNoImpact(var, value); - case DefaultPhaseParameters::CHOOSE_MIN_SIZE_ASSIGN_MIN: - return FindUnboundVarValueMinSize(var, value); - case DefaultPhaseParameters::CHOOSE_RANDOM_ASSIGN_MIN: - return FindUnboundVarValueRandom(var, value, true); - case DefaultPhaseParameters::CHOOSE_RANDOM_ASSIGN_MAX: - return FindUnboundVarValueRandom(var, value, false); - } - return false; - } - // This method will do an exhaustive scan of all domains of all // variables to select the variable with the maximal sum of impacts // per value in its domain, and then select the value with the // minimal impact. - bool FindUnboundVarValueWithImpact(IntVar** const var, int64* const value) { - CHECK_NOTNULL(var); - CHECK_NOTNULL(value); - *var = NULL; - *value = 0; + Decision* ImpactNext(Solver* const solver) { + IntVar* var = NULL; + int64 value = 0; double best_var_impact = -std::numeric_limits::max(); for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { @@ -1293,72 +1271,17 @@ class DefaultIntegerSearch : public DecisionBuilder { parameters_.var_selection_schema, parameters_.value_selection_schema); if (current_var_impact > best_var_impact) { - *var = vars_[i]; - *value = current_value; + var = vars_[i]; + value = current_value; best_var_impact = current_var_impact; } } } - return (*var != NULL); - } - - bool FindUnboundVarValueNoImpact(IntVar** const var, int64* const value) { - CHECK_NOTNULL(var); - CHECK_NOTNULL(value); - *var = NULL; - *value = 0; - for (int i = 0; i < vars_.size(); ++i) { - if (!vars_[i]->Bound()) { - *var = vars_[i];; - *value = vars_[i]->Min(); - return true; - } + if (var == NULL) { + return NULL; + } else { + return solver->MakeAssignVariableValue(var, value); } - return false; - } - - bool FindUnboundVarValueMinSize(IntVar** const found_var, - int64* const value) { - CHECK_NOTNULL(found_var); - CHECK_NOTNULL(value); - *found_var = NULL; - *value = 0; - uint64 best_size = kint64max; - int64 best_min = kint64max; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; - if (!var->Bound()) { - if (var->Size() < best_size || - (var->Size() == best_size && var->Min() < best_min)) { - best_size = var->Size(); - best_min = var->Min(); - *value = var->Min(); - *found_var = var; - } - } - } - return *found_var != NULL; - } - - bool FindUnboundVarValueRandom(IntVar** const found_var, - int64* const value, - bool assign_min) { - CHECK_NOTNULL(found_var); - CHECK_NOTNULL(value); - *found_var = NULL; - *value = 0; - const int size = vars_.size(); - const int shift = heuristics_.Rand32(size); - for (int i = 0; i < size; ++i) { - const int index = (i + shift) < size ? i + shift : i + shift - size; - IntVar* const var = vars_[index]; - if (!var->Bound()) { - *found_var = var; - *value = assign_min ? var->Min() : var->Max(); - return true; - } - } - return false; } // ----- data members ----- diff --git a/src/constraint_solver/search.cc b/src/constraint_solver/search.cc index ed212729b8..3943e69f76 100644 --- a/src/constraint_solver/search.cc +++ b/src/constraint_solver/search.cc @@ -507,6 +507,9 @@ DecisionBuilder* Solver::Compose(DecisionBuilder* const db1, } DecisionBuilder* Solver::Compose(const std::vector& dbs) { + if (dbs.size() == 1) { + return dbs[0]; + } return RevAlloc(new ComposeDecisionBuilder(dbs)); } diff --git a/src/flatzinc/flatzinc.h b/src/flatzinc/flatzinc.h index 3615f37358..c72f2cd654 100644 --- a/src/flatzinc/flatzinc.h +++ b/src/flatzinc/flatzinc.h @@ -73,6 +73,7 @@ struct FlatZincSearchParameters { search_type(MIN_SIZE) {} enum SearchType { + DEFAULT, IBS, FIRST_UNBOUND, MIN_SIZE, @@ -225,9 +226,15 @@ class FlatZincModel { bool HasSolveAnnotations() const; - void CreateDecisionBuilders(const FlatZincSearchParameters& parameters); + DecisionBuilder* CreateDecisionBuilders( + const FlatZincSearchParameters& parameters); + + void ParseSearchAnnotations(bool ignore_unknown, + std::vector* const defined, + std::vector* const defined_vars); + void AddCompletionDecisionBuilders( + std::vector* const builders); - const std::vector& DecisionBuilders() const; const std::vector& PrimaryVariables() const; const std::vector& SecondaryVariables() const; @@ -242,7 +249,6 @@ class FlatZincModel { int set_var_count; scoped_ptr solver_; - std::vector builders_; OptimizeVar* objective_; // Index of the integer variable to optimize diff --git a/src/flatzinc/fz.cc b/src/flatzinc/fz.cc index a872fdb7b2..db3f9a452b 100644 --- a/src/flatzinc/fz.cc +++ b/src/flatzinc/fz.cc @@ -106,6 +106,8 @@ void ParallelRun(char* const file, int worker_id, switch (worker_id) { case 0: { parameters.free_search = false; + parameters.search_type = + operations_research::FlatZincSearchParameters::DEFAULT; parameters.restart_log_size = -1.0; break; } @@ -144,64 +146,70 @@ void ParallelRun(char* const file, int worker_id, Run(file, parameters, parallel_support); } -} // namespace operations_research +void SequentialRun(char* const file) { + FlatZincSearchParameters parameters; + parameters.all_solutions = FLAGS_all; + parameters.free_search = FLAGS_free; + parameters.heuristic_period = FLAGS_heuristic_period; + parameters.ignore_unknown = false; + parameters.log_period = FLAGS_log_period; + parameters.luby_restart = FLAGS_luby_restart; + parameters.num_solutions = FLAGS_num_solutions; + parameters.restart_log_size = FLAGS_restart_log_size; + parameters.simplex_frequency = FLAGS_simplex_frequency; + parameters.threads = FLAGS_workers; + parameters.time_limit_in_ms = FLAGS_time_limit; + parameters.use_log = FLAGS_logging; + parameters.verbose_impact = FLAGS_verbose_impact; + parameters.worker_id = -1; + parameters.search_type = + FLAGS_use_impact ? FlatZincSearchParameters::IBS + : FlatZincSearchParameters::DEFAULT; -int main(int argc, char** argv) { + scoped_ptr parallel_support( + operations_research::MakeSequentialSupport(parameters.all_solutions, + parameters.num_solutions, + FLAGS_verbose_mt)); + Run(file, parameters, parallel_support.get()); +} + +void FixAndParseParameters(int* argc, char*** argv) { FLAGS_log_prefix = false; char all_param[] = "--all"; char free_param[] = "--free"; char workers_param[] = "--workers"; char solutions_param[] = "--num_solutions"; char logging_param[] = "--logging"; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-a") == 0) { - argv[i] = all_param; + for (int i = 1; i < *argc; ++i) { + if (strcmp((*argv)[i], "-a") == 0) { + (*argv)[i] = all_param; } - if (strcmp(argv[i], "-f") == 0) { - argv[i] = free_param; + if (strcmp((*argv)[i], "-f") == 0) { + (*argv)[i] = free_param; } - if (strcmp(argv[i], "-p") == 0) { - argv[i] = workers_param; + if (strcmp((*argv)[i], "-p") == 0) { + (*argv)[i] = workers_param; } - if (strcmp(argv[i], "-n") == 0) { - argv[i] = solutions_param; + if (strcmp((*argv)[i], "-n") == 0) { + (*argv)[i] = solutions_param; } - if (strcmp(argv[i], "-l") == 0) { - argv[i] = logging_param; + if (strcmp((*argv)[i], "-l") == 0) { + (*argv)[i] = logging_param; } } - google::ParseCommandLineFlags( &argc, &argv, true); + google::ParseCommandLineFlags(argc, argv, true); +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::FixAndParseParameters(&argc, &argv); if (argc <= 1) { LOG(ERROR) << "Usage: " << argv[0] << " "; exit(EXIT_FAILURE); } if (FLAGS_workers == 0) { - operations_research::FlatZincSearchParameters parameters; - parameters.all_solutions = FLAGS_all; - parameters.free_search = FLAGS_free; - parameters.heuristic_period = FLAGS_heuristic_period; - parameters.ignore_unknown = false; - parameters.log_period = FLAGS_log_period; - parameters.luby_restart = FLAGS_luby_restart; - parameters.num_solutions = FLAGS_num_solutions; - parameters.restart_log_size = FLAGS_restart_log_size; - parameters.simplex_frequency = FLAGS_simplex_frequency; - parameters.threads = FLAGS_workers; - parameters.time_limit_in_ms = FLAGS_time_limit; - parameters.use_log = FLAGS_logging; - parameters.verbose_impact = FLAGS_verbose_impact; - parameters.worker_id = -1; - parameters.search_type = - FLAGS_use_impact - ? operations_research::FlatZincSearchParameters::IBS - : operations_research::FlatZincSearchParameters::FIRST_UNBOUND; - - scoped_ptr parallel_support( - operations_research::MakeSequentialSupport(parameters.all_solutions, - parameters.num_solutions, - FLAGS_verbose_mt)); - operations_research::Run(argv[1], parameters, parallel_support.get()); + operations_research::SequentialRun(argv[1]); } else { scoped_ptr parallel_support( operations_research::MakeMtSupport(FLAGS_all, FLAGS_verbose_mt)); diff --git a/src/flatzinc/fz_search.cc b/src/flatzinc/fz_search.cc index f585349652..45f59d09ef 100644 --- a/src/flatzinc/fz_search.cc +++ b/src/flatzinc/fz_search.cc @@ -459,11 +459,11 @@ bool FlatZincModel::HasSolveAnnotations() const { return has_annotations; } -void FlatZincModel::CreateDecisionBuilders(const FlatZincSearchParameters& p) { +void FlatZincModel::ParseSearchAnnotations( + bool ignore_unknown, + std::vector* const defined, + std::vector* const active_variables) { const bool has_solve_annotations = HasSolveAnnotations(); - hash_set added; - - VLOG(1) << "Create decision builders"; std::vector flat_annotations; if (has_solve_annotations) { CHECK_NOTNULL(solve_annotations_); @@ -473,21 +473,82 @@ void FlatZincModel::CreateDecisionBuilders(const FlatZincSearchParameters& p) { flat_annotations.push_back(solve_annotations_); } } - search_name_ = p.free_search ? "free" : "defined"; - if (!p.free_search) { - for (unsigned int i = 0; i < flat_annotations.size(); i++) { + VLOG(1) << "Create decision builders from search annotations"; + hash_set added; + for (unsigned int i = 0; i < flat_annotations.size(); i++) { + try { + AstCall* call = flat_annotations[i]->getCall("int_search"); + AstArray* args = call->getArgs(4); + AstArray* vars = args->a[0]->getArray(); + std::vector int_vars; + for (int i = 0; i < vars->a.size(); ++i) { + if (vars->a[i]->isIntVar()) { + IntVar* const to_add = + integer_variables_[vars->a[i]->getIntVar()]->Var(); + if (!ContainsKey(added, to_add)) { + int_vars.push_back(to_add); + active_variables->push_back(to_add); + added.insert(to_add); + } + } + } + Solver::IntVarStrategy str = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN; + if (args->hasAtom("input_order")) { + str = Solver::CHOOSE_FIRST_UNBOUND; + } + if (args->hasAtom("first_fail")) { + str = Solver::CHOOSE_MIN_SIZE; + } + if (args->hasAtom("anti_first_fail")) { + str = Solver::CHOOSE_MAX_SIZE; + } + if (args->hasAtom("smallest")) { + str = Solver::CHOOSE_LOWEST_MIN; + } + if (args->hasAtom("largest")) { + str = Solver::CHOOSE_HIGHEST_MAX; + } + if (args->hasAtom("max_regret")) { + str = Solver::CHOOSE_MAX_REGRET_ON_MIN; + } + if (args->hasAtom("occurrence")) { + SortVariableByDegree(solver_.get(), &int_vars); + str = Solver::CHOOSE_FIRST_UNBOUND; + } + Solver::IntValueStrategy vstr = Solver::ASSIGN_MIN_VALUE; + if (args->hasAtom("indomain_max")) { + vstr = Solver::ASSIGN_MAX_VALUE; + } + if (args->hasAtom("indomain_median") || + args->hasAtom("indomain_middle")) { + vstr = Solver::ASSIGN_CENTER_VALUE; + } + if (args->hasAtom("indomain_random")) { + vstr = Solver::ASSIGN_RANDOM_VALUE; + } + if (args->hasAtom("indomain_split")) { + vstr = Solver::SPLIT_LOWER_HALF; + } + if (args->hasAtom("indomain_reverse_split")) { + vstr = Solver::SPLIT_UPPER_HALF; + } + defined->push_back(solver_->MakePhase(int_vars, str, vstr)); + } + catch (AstTypeError & e) { + (void) e; try { - AstCall* call = flat_annotations[i]->getCall("int_search"); + AstCall* call = flat_annotations[i]->getCall("bool_search"); AstArray* args = call->getArgs(4); AstArray* vars = args->a[0]->getArray(); - std::vector int_vars; + std::vector bool_vars; for (int i = 0; i < vars->a.size(); ++i) { - if (vars->a[i]->isIntVar()) { + if (vars->a[i]->isBoolVar()) { IntVar* const to_add = - integer_variables_[vars->a[i]->getIntVar()]->Var(); + boolean_variables_[vars->a[i]->getBoolVar()]->Var(); if (!ContainsKey(added, to_add)) { - int_vars.push_back(to_add); + bool_vars.push_back(to_add); + active_variables->push_back(to_add); added.insert(to_add); } } @@ -496,162 +557,140 @@ void FlatZincModel::CreateDecisionBuilders(const FlatZincSearchParameters& p) { if (args->hasAtom("input_order")) { str = Solver::CHOOSE_FIRST_UNBOUND; } - if (args->hasAtom("first_fail")) { - str = Solver::CHOOSE_MIN_SIZE; - } - if (args->hasAtom("anti_first_fail")) { - str = Solver::CHOOSE_MAX_SIZE; - } - if (args->hasAtom("smallest")) { - str = Solver::CHOOSE_LOWEST_MIN; - } - if (args->hasAtom("largest")) { - str = Solver::CHOOSE_HIGHEST_MAX; - } - if (args->hasAtom("max_regret")) { - str = Solver::CHOOSE_MAX_REGRET_ON_MIN; - } if (args->hasAtom("occurrence")) { - SortVariableByDegree(solver_.get(), &int_vars); + SortVariableByDegree(solver_.get(), &bool_vars); str = Solver::CHOOSE_FIRST_UNBOUND; } - Solver::IntValueStrategy vstr = Solver::ASSIGN_MIN_VALUE; - if (args->hasAtom("indomain_max")) { - vstr = Solver::ASSIGN_MAX_VALUE; - } - if (args->hasAtom("indomain_median") || - args->hasAtom("indomain_middle")) { - vstr = Solver::ASSIGN_CENTER_VALUE; + Solver::IntValueStrategy vstr = Solver::ASSIGN_MAX_VALUE; + if (args->hasAtom("indomain_min")) { + vstr = Solver::ASSIGN_MIN_VALUE; } if (args->hasAtom("indomain_random")) { vstr = Solver::ASSIGN_RANDOM_VALUE; } - if (args->hasAtom("indomain_split")) { - vstr = Solver::SPLIT_LOWER_HALF; - } - if (args->hasAtom("indomain_reverse_split")) { - vstr = Solver::SPLIT_UPPER_HALF; - } - builders_.push_back(solver_->MakePhase(int_vars, str, vstr)); + defined->push_back(solver_->MakePhase(bool_vars, str, vstr)); } catch (AstTypeError & e) { (void) e; try { - AstCall* call = flat_annotations[i]->getCall("bool_search"); + AstCall* call = flat_annotations[i]->getCall("set_search"); AstArray* args = call->getArgs(4); - AstArray* vars = args->a[0]->getArray(); - std::vector bool_vars; - for (int i = 0; i < vars->a.size(); ++i) { - if (vars->a[i]->isBoolVar()) { - IntVar* const to_add = - boolean_variables_[vars->a[i]->getBoolVar()]->Var(); - if (!ContainsKey(added, to_add)) { - bool_vars.push_back(to_add); - added.insert(to_add); - } - } - } - Solver::IntVarStrategy str = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN; - if (args->hasAtom("input_order")) { - str = Solver::CHOOSE_FIRST_UNBOUND; - } - if (args->hasAtom("occurrence")) { - SortVariableByDegree(solver_.get(), &bool_vars); - str = Solver::CHOOSE_FIRST_UNBOUND; - } - Solver::IntValueStrategy vstr = Solver::ASSIGN_MAX_VALUE; - if (args->hasAtom("indomain_min")) { - vstr = Solver::ASSIGN_MIN_VALUE; - } - if (args->hasAtom("indomain_random")) { - vstr = Solver::ASSIGN_RANDOM_VALUE; - } - builders_.push_back(solver_->MakePhase(bool_vars, str, vstr)); + args->a[0]->getArray(); + LOG(FATAL) << "Search on set variables not supported"; } catch (AstTypeError & e) { (void) e; - try { - AstCall* call = flat_annotations[i]->getCall("set_search"); - AstArray* args = call->getArgs(4); - args->a[0]->getArray(); - LOG(FATAL) << "Search on set variables not supported"; - } - catch (AstTypeError & e) { - (void) e; - if (!p.ignore_unknown) { - LOG(WARNING) << "Warning, ignored search annotation: " - << flat_annotations[i]->DebugString(); - } + if (!ignore_unknown) { + LOG(WARNING) << "Warning, ignored search annotation: " + << flat_annotations[i]->DebugString(); } } } } + } - // Add completion goals to be robust to incomplete search. - - // First on active varialbes, create the variable array, push - // smaller variable first. - std::vector vars; + if (defined->empty() || (defined->size() == 1 && method_ != SAT)) { + active_variables->clear(); + // Create the variable array, push smaller variable first. for (int i = 0; i < active_variables_.size(); ++i) { IntVar* const var = active_variables_[i]; if (var->Size() < 0xFFFF) { - vars.push_back(var); + active_variables->push_back(var); } } for (int i = 0; i < active_variables_.size(); ++i) { IntVar* const var = active_variables_[i]; if (var->Size() >= 0xFFFF) { - vars.push_back(var); + active_variables->push_back(var); } } + } +} - // Then introduced variables. - builders_.push_back(solver_->MakePhase(vars, Solver::CHOOSE_FIRST_UNBOUND, + // Add completion goals to be robust to incomplete search specifications. +void FlatZincModel::AddCompletionDecisionBuilders( + std::vector* const builders) { + const bool has_solve_annotations = HasSolveAnnotations(); + // Add introduced variables. + // builders->push_back(solver_->MakePhase(vars, Solver::CHOOSE_FIRST_UNBOUND, + // Solver::ASSIGN_MIN_VALUE)); + // Then fixed variables. + if (!introduced_variables_.empty() && !has_solve_annotations) { + // Better safe than sorry. + builders->push_back(solver_->MakePhase(introduced_variables_, + Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE)); - // Then fixed variables. - if (!introduced_variables_.empty() && !has_solve_annotations) { - // Better safe than sorry. - builders_.push_back(solver_->MakePhase(introduced_variables_, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE)); - } - if (!one_constraint_variables_.empty()) { - // Better safe than sorry. - builders_.push_back( - solver_->RevAlloc(new AssignToBounds(one_constraint_variables_))); + } + if (!one_constraint_variables_.empty()) { + // Better safe than sorry. + builders->push_back( + solver_->RevAlloc(new AssignToBounds(one_constraint_variables_))); + } +} + +DecisionBuilder* FlatZincModel::CreateDecisionBuilders( + const FlatZincSearchParameters& p) { + VLOG(1) << "Create decision builders"; + // Fill builders_ with predefined search. + std::vector defined; + std::vector active_variables; + ParseSearchAnnotations(p.ignore_unknown, &defined, &active_variables); + + // We collect the decision builder linked to the objective. + DecisionBuilder* obj_db = NULL; + if (defined.size() > 0 && method_ != SAT) { + obj_db = defined.back(); + defined.pop_back(); + if (defined.size() > 0) { + // We need to remove the objective variables from the list of + // defined_vars. + active_variables.pop_back(); } } + search_name_ = + defined.empty() ? "automatic" : (p.free_search ? "free" : "defined"); - if (p.free_search || builders_.size() == 0 || - (builders_.size() == 1 && method_ != SAT)) { - search_name_ = "automatic"; - // We collect the objective linked decision builder. - DecisionBuilder* const obj_db = - builders_.size() == 1 ? builders_.back() : NULL; - builders_.clear(); - + // We fill builders with information from search (flags, annotations). + std::vector builders; + if (!p.free_search && !defined.empty()) { + builders = defined; + } else { DefaultPhaseParameters parameters; + DecisionBuilder* inner_builder = NULL; switch (p.search_type) { - case FlatZincSearchParameters::IBS: - parameters.search_strategy = - DefaultPhaseParameters::IMPACT_BASED_SEARCH; + case FlatZincSearchParameters::DEFAULT: { + if (defined.empty()) { + inner_builder = solver_->MakePhase(active_variables, + Solver::CHOOSE_MIN_SIZE, + Solver::ASSIGN_MIN_VALUE); + } else { + inner_builder = solver_->Compose(defined); + } break; - case FlatZincSearchParameters::FIRST_UNBOUND: - parameters.search_strategy = - DefaultPhaseParameters::CHOOSE_FIRST_UNBOUND_ASSIGN_MIN; + } + case FlatZincSearchParameters::IBS: { break; - case FlatZincSearchParameters::MIN_SIZE: - parameters.search_strategy = - DefaultPhaseParameters::CHOOSE_MIN_SIZE_ASSIGN_MIN; + } + case FlatZincSearchParameters::FIRST_UNBOUND: { + inner_builder = solver_->MakePhase( + active_variables, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); break; - case FlatZincSearchParameters::RANDOM_MIN: - parameters.search_strategy = - DefaultPhaseParameters::CHOOSE_RANDOM_ASSIGN_MIN; - break; - case FlatZincSearchParameters::RANDOM_MAX: - parameters.search_strategy = - DefaultPhaseParameters::CHOOSE_RANDOM_ASSIGN_MAX; + } + case FlatZincSearchParameters::MIN_SIZE: { + inner_builder = solver_->MakePhase( + active_variables, Solver::CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver::ASSIGN_MIN_VALUE); break; + } + case FlatZincSearchParameters::RANDOM_MIN: { + inner_builder = solver_->MakePhase( + active_variables, Solver::CHOOSE_RANDOM, Solver::ASSIGN_MIN_VALUE); + } + case FlatZincSearchParameters::RANDOM_MAX: { + inner_builder = solver_->MakePhase( + active_variables, Solver::CHOOSE_RANDOM, Solver::ASSIGN_MAX_VALUE); + } } parameters.run_all_heuristics = true; parameters.heuristic_period = @@ -663,50 +702,31 @@ void FlatZincModel::CreateDecisionBuilders(const FlatZincSearchParameters& p) { p.use_log ? (p.verbose_impact ? DefaultPhaseParameters::VERBOSE : DefaultPhaseParameters::NORMAL) : DefaultPhaseParameters::NONE; - parameters.use_no_goods = true; + parameters.use_no_goods = (p.restart_log_size > 0); parameters.var_selection_schema = DefaultPhaseParameters::CHOOSE_MAX_SUM_IMPACT; parameters.value_selection_schema = DefaultPhaseParameters::SELECT_MIN_IMPACT; parameters.random_seed = p.random_seed; - // Create the variable array, push smaller variable first. - std::vector vars; - for (int i = 0; i < active_variables_.size(); ++i) { - IntVar* const var = active_variables_[i]; - if (var->Size() < 0xFFFF) { - vars.push_back(var); - } - } - for (int i = 0; i < active_variables_.size(); ++i) { - IntVar* const var = active_variables_[i]; - if (var->Size() >= 0xFFFF) { - vars.push_back(var); - } - } - builders_.push_back(solver_->MakeDefaultPhase(vars, parameters)); - if (!introduced_variables_.empty() && !has_solve_annotations) { - // Better safe than sorry. - builders_.push_back(solver_->MakePhase(introduced_variables_, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE)); - } - if (!one_constraint_variables_.empty()) { - // Better safe than sorry. - builders_.push_back( - solver_->RevAlloc(new AssignToBounds(one_constraint_variables_))); - } - if (obj_db != NULL) { - builders_.push_back(obj_db); + if (inner_builder == NULL) { + CHECK_EQ(FlatZincSearchParameters::IBS, p.search_type); + parameters.decision_builder = NULL; + } else { + parameters.decision_builder = inner_builder; } + builders.push_back(solver_->MakeDefaultPhase(active_variables, parameters)); + } + // Add completion decision builders to be more robust. + AddCompletionDecisionBuilders(&builders); + // Add finally the objective decision builder. + if (obj_db != NULL) { + builders.push_back(obj_db); } // Reporting - for (int i = 0; i < builders_.size(); ++i) { - VLOG(1) << " - adding decision builder = " << builders_[i]->DebugString(); + for (int i = 0; i < builders.size(); ++i) { + VLOG(1) << " - adding decision builder = " << builders[i]->DebugString(); } -} - -const std::vector& FlatZincModel::DecisionBuilders() const { - return builders_; + return solver_->Compose(builders); } const std::vector& FlatZincModel::PrimaryVariables() const { @@ -723,7 +743,7 @@ void FlatZincModel::Solve(FlatZincSearchParameters p, return; } - CreateDecisionBuilders(p); + DecisionBuilder* const db = CreateDecisionBuilders(p); bool print_last = false; if (p.all_solutions && p.num_solutions == 0) { p.num_solutions = kint32max; @@ -779,7 +799,7 @@ void FlatZincModel::Solve(FlatZincSearchParameters p, bool breaked = false; string solution_string; const int64 build_time = solver_->wall_time(); - solver_->NewSearch(solver_->Compose(builders_), monitors); + solver_->NewSearch(db, monitors); while (solver_->NextSolution()) { if (output_ != NULL && !parallel_support->ShouldFinish()) { solution_string.clear();