diff --git a/src/constraint_solver/constraint_solver.cc b/src/constraint_solver/constraint_solver.cc index 7e90962051..053d9dee1d 100644 --- a/src/constraint_solver/constraint_solver.cc +++ b/src/constraint_solver/constraint_solver.cc @@ -861,7 +861,8 @@ class Search { decision_builder_(NULL), created_by_solve_(false), selector_(NULL), search_depth_(0), left_search_depth_(0), should_restart_(false), should_finish_(false), - sentinel_pushed_(0), jmpbuf_filled_(false) {} + sentinel_pushed_(0), jmpbuf_filled_(false), + restore_(true) {} // Constructor for a dummy search. The only difference between a dummy search // and a regular one is that the search depth and left search depth is @@ -871,7 +872,8 @@ class Search { decision_builder_(NULL), created_by_solve_(false), selector_(NULL), search_depth_(-1), left_search_depth_(-1), should_restart_(false), should_finish_(false), - sentinel_pushed_(0), jmpbuf_filled_(false) {} + sentinel_pushed_(0), jmpbuf_filled_(false), + restore_(true) {} ~Search() { STLDeleteElements(&marker_stack_); @@ -918,6 +920,8 @@ class Search { void RightMove() { search_depth_++; } + bool restore() const { return restore_; } + void set_restore(bool restore) { restore_ = restore; } int search_depth() const { return search_depth_; } void set_search_depth(int d) { search_depth_ = d; } int left_search_depth() const { return left_search_depth_; } @@ -954,6 +958,7 @@ class Search { bool should_finish_; int sentinel_pushed_; bool jmpbuf_filled_; + bool restore_; }; // Backtrack is implemented using 3 primitives: @@ -1090,6 +1095,7 @@ void Search::Clear() { search_depth_ = 0; left_search_depth_ = 0; selector_.reset(NULL); + restore_ = true; } void Search::EnterSearch() { @@ -1885,18 +1891,31 @@ void Solver::NewSearch(DecisionBuilder* const db, int size) { // TODO(user) : reset statistics + // ----- gets or creates the search object ----- + CHECK_NOTNULL(db); DCHECK_GE(size, 0); + const bool nested = state_ == IN_SEARCH; - if (state_ == IN_SEARCH || state_ == IN_ROOT_NODE) { - LOG(FATAL) << "Use NestedSolve() inside search"; + if (state_ == IN_ROOT_NODE) { + LOG(FATAL) << "Cannot start new searches here."; } - // Check state and go to OUTSIDE_SEARCH. - Search* const search = searches_.back(); + + Search* const search = nested ? new Search(this) : searches_.back(); search->set_created_by_solve(false); // default behavior. - BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); - state_ = OUTSIDE_SEARCH; + // ----- jumps to correct state ----- + + if (nested) { + DCHECK_GE(searches_.size(), 2); + searches_.push_back(search); + } else { + DCHECK_EQ(2, searches_.size()); + BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); + state_ = OUTSIDE_SEARCH; + } + + // ----- manages all monitors ----- // Always install the main propagation monitor. propagation_monitor_->Install(); @@ -1921,22 +1940,25 @@ void Solver::NewSearch(DecisionBuilder* const db, // Install the print trace if needed. // The print_trace needs to be last to detect propagation from the objective. if (FLAGS_cp_trace_propagation) { - print_trace_ = BuildPrintTrace(this); + if (!nested) { // Build trace objet at top level. + print_trace_ = BuildPrintTrace(this); + } print_trace_->Install(); } else { // This is useful to trace the exact behavior of the search. // The '######## ' prefix is the same as the progagation trace. - if (FLAGS_cp_trace_search) { + if (FLAGS_cp_trace_search && !nested) { SearchMonitor* const trace = MakeSearchTrace("######## "); trace->Install(); } print_trace_ = NULL; } + // ----- enters search ----- + search->EnterSearch(); // Push sentinel and set decision builder. - DCHECK_EQ(2, searches_.size()); PushSentinel(INITIAL_SEARCH_SENTINEL); search->set_decision_builder(db); } @@ -2281,22 +2303,33 @@ bool Solver::NextSolution() { } void Solver::EndSearch() { - CHECK_EQ(2, searches_.size()); Search* const search = searches_.back(); - BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); + if (search->restore()) { + BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); + } else { + CHECK_GT(searches_.size(), 2); + if (search->sentinel_pushed_ > 0) { + JumpToSentinelWhenNested(); + } + } search->ExitSearch(); search->Clear(); - state_ = OUTSIDE_SEARCH; - if (!FLAGS_cp_profile_file.empty()) { - LOG(INFO) << "Exporting profile to " << FLAGS_cp_profile_file; - ExportProfilingOverview(FLAGS_cp_profile_file); + if (2 == searches_.size()) { // Post top level search actions. + state_ = OUTSIDE_SEARCH; + if (!FLAGS_cp_profile_file.empty()) { + LOG(INFO) << "Exporting profile to " << FLAGS_cp_profile_file; + ExportProfilingOverview(FLAGS_cp_profile_file); + } + } else { // We clean the nested Search. + delete search; + searches_.pop_back(); } } bool Solver::CheckAssignment(Assignment* const solution) { CHECK(solution); if (state_ == IN_SEARCH || state_ == IN_ROOT_NODE) { - LOG(FATAL) << "Use NestedSolve() inside search"; + LOG(FATAL) << "CheckAssignment is only available at the top level."; } // Check state and go to OUTSIDE_SEARCH. Search* const search = searches_.back(); @@ -2373,93 +2406,52 @@ bool Solver::CheckConstraint(Constraint* const ct) { return Solve(MakeConstraintAdder(ct)); } -bool Solver::NestedSolve(DecisionBuilder* const db, - bool restore, - const std::vector& monitors) { - return NestedSolve(db, restore, monitors.data(), monitors.size()); +bool Solver::SolveAndCommit(DecisionBuilder* const db, + const std::vector& monitors) { + return SolveAndCommit(db, monitors.data(), monitors.size()); } -bool Solver::NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1) { +bool Solver::SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1) { std::vector monitors; monitors.push_back(m1); - return NestedSolve(db, restore, monitors.data(), monitors.size()); + return SolveAndCommit(db, monitors.data(), monitors.size()); } -bool Solver::NestedSolve(DecisionBuilder* const db, bool restore) { - return NestedSolve(db, restore, NULL, Zero()); +bool Solver::SolveAndCommit(DecisionBuilder* const db) { + return SolveAndCommit(db, NULL, Zero()); } -bool Solver::NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1, - SearchMonitor* const m2) { +bool Solver::SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1, + SearchMonitor* const m2) { std::vector monitors; monitors.push_back(m1); monitors.push_back(m2); - return NestedSolve(db, restore, monitors.data(), monitors.size()); + return SolveAndCommit(db, monitors.data(), monitors.size()); } -bool Solver::NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1, - SearchMonitor* const m2, - SearchMonitor* const m3) { +bool Solver::SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1, + SearchMonitor* const m2, + SearchMonitor* const m3) { std::vector monitors; monitors.push_back(m1); monitors.push_back(m2); monitors.push_back(m3); - return NestedSolve(db, restore, monitors.data(), monitors.size()); + return SolveAndCommit(db, monitors.data(), monitors.size()); } -bool Solver::NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const * monitors, - int size) { - Search new_search(this); - searches_.push_back(&new_search); - // Always install the main propagation monitor. - propagation_monitor_->Install(); - // Install the demon monitor if needed. - if (demon_profiler_ != NULL) { - InstallDemonProfiler(demon_profiler_); - } - - for (int i = 0; i < size; ++i) { - if (monitors[i] != NULL) { - monitors[i]->Install(); - } - } - std::vector extras; - db->AppendMonitors(this, &extras); - for (ConstIter > it(extras); !it.at_end(); ++it) { - SearchMonitor* const monitor = *it; - if (monitor != NULL) { - monitor->Install(); - } - } - // Install the print trace if needed. - if (print_trace_ != NULL) { - print_trace_->Install(); - } - +bool Solver::SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const * monitors, + int size) { + NewSearch(db, monitors, size); searches_.back()->set_created_by_solve(true); // Overwrites default. - new_search.EnterSearch(); - PushSentinel(INITIAL_SEARCH_SENTINEL); - new_search.set_decision_builder(db); - bool res = NextSolution(); - if (res) { - if (restore) { - BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); - } else { - JumpToSentinelWhenNested(); - } - } - new_search.ExitSearch(); - new_search.Clear(); - searches_.pop_back(); - return res; + searches_.back()->set_restore(false); + NextSolution(); + const bool solution_found = searches_.back()->solution_counter() > 0; + EndSearch(); + return solution_found; } void Solver::Fail() { diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index 54f119db3e..248b20460e 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -908,8 +908,8 @@ class Solver { // assigning any variable to any value is a solution, unless the root node // propagation discovers that the model is infeasible. // - // These function must be called from outside of search, meaning that - // state() == OUTSIDE_SEARCH. + // These function must be called from either from outside of search, + // or withing the Next() method of a decion builder. // // Solve will terminate whenever any of the following event arise: // * A search monitor asks the solver to terminate the search by calling @@ -951,13 +951,14 @@ class Solver { // @} // @{ - // Decomposed top level search. - // The code should look like + // Decomposed search. + // The code for a top level search should look like // solver->NewSearch(db); // while (solver->NextSolution()) { // //.. use the current solution // } // solver()->EndSearch(); + void NewSearch(DecisionBuilder* const db, const std::vector& monitors); void NewSearch(DecisionBuilder* const db, @@ -982,37 +983,34 @@ class Solver { void EndSearch(); // @} - - // Nested solve using a decision builder and up to three + // SolveAndCommit using a decision builder and up to three // search monitors, usually one for the objective, one for the limits // and one to collect solutions. - // The restore parameter indicates if the search should backtrack completely - // after completion, even in case of success. - bool NestedSolve(DecisionBuilder* const db, - bool restore, - const std::vector& monitors); - bool NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const * monitors, - int size); - bool NestedSolve(DecisionBuilder* const db, bool restore); - bool NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1); - bool NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1, SearchMonitor* const m2); - bool NestedSolve(DecisionBuilder* const db, - bool restore, - SearchMonitor* const m1, - SearchMonitor* const m2, - SearchMonitor* const m3); + // + // The difference between a SolveAndCommit() and a Solve() method + // call is the fact that SolveAndCommit will not backtrack all + // modifications at the end of the search. This method is only + // usable during the Next() method of a decision builder. + bool SolveAndCommit(DecisionBuilder* const db, + const std::vector& monitors); + bool SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const * monitors, + int size); + bool SolveAndCommit(DecisionBuilder* const db); + bool SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1); + bool SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1, SearchMonitor* const m2); + bool SolveAndCommit(DecisionBuilder* const db, + SearchMonitor* const m1, + SearchMonitor* const m2, + SearchMonitor* const m3); // Checks whether the given assignment satisfies all the relevant constraints. bool CheckAssignment(Assignment* const assignment); // Checks whether adding this constraint will lead to an immediate - // failure. It will return true if the model is already + // failure. It will return false if the model is already // inconsistent, or if adding the constraint makes it inconsistent. bool CheckConstraint(Constraint* const constraint); @@ -1513,9 +1511,7 @@ class Solver { // |{i | v[i] == value}| == count - Constraint* MakeCount(const std::vector& v, - int64 value, - int64 count); + Constraint* MakeCount(const std::vector& v, int64 value, int64 count); // |{i | v[i] == value}| == count Constraint* MakeCount(const std::vector& v, int64 value, IntVar* const count); @@ -2765,8 +2761,7 @@ class Solver { int SearchLeftDepth() const; // Gets the number of nested searches. It returns 0 outside search, - // 1 during the top level search, 2 if one level of NestedSolve() is - // used, and more if more solves are nested. + // 1 during the top level search, 2 or more in case of nested searched. int SolveDepth() const; // Sets the given branch selector on the current active search. diff --git a/src/constraint_solver/constraint_solver.swig b/src/constraint_solver/constraint_solver.swig index fecfc377b2..b4953373ce 100644 --- a/src/constraint_solver/constraint_solver.swig +++ b/src/constraint_solver/constraint_solver.swig @@ -924,7 +924,6 @@ static void SetPythonFlags(bool trace_propagation, PhaseStrategy str); %ignore Solver::Solve(DecisionBuilder* const db, SearchMonitor* const * monitors, int size); -%ignore Solver::NestedSolve; %ignore Solver::Compose(DecisionBuilder* const db1, DecisionBuilder* const db2); %ignore Solver::Compose(DecisionBuilder* const db1, diff --git a/src/constraint_solver/default_search.cc b/src/constraint_solver/default_search.cc index 3e0b4fae32..907660f6d9 100644 --- a/src/constraint_solver/default_search.cc +++ b/src/constraint_solver/default_search.cc @@ -394,8 +394,8 @@ class ImpactRecorder { } // Reset the number of impacts initialized. init_count_ = 0; - // Use NestedSolve() to scan all values of one variable. - solver->NestedSolve(init_decision_builder, true); + // Use Solve() to scan all values of one variable. + solver->Solve(init_decision_builder); // If we have not initialized all values, then they can be removed. // As the iterator is not stable w.r.t. deletion, we need to store @@ -1069,7 +1069,7 @@ class ImpactDecisionBuilder : public DecisionBuilder { HeuristicWrapper* const wrapper = heuristics_[index]; const bool result = - solver->NestedSolve(wrapper->phase, false, heuristic_limit_); + solver->SolveAndCommit(wrapper->phase, heuristic_limit_); if (result && parameters_.display_level != DefaultPhaseParameters::NONE) { LOG(INFO) << "Solution found by heuristic " << wrapper->name; } diff --git a/src/constraint_solver/expressions.cc b/src/constraint_solver/expressions.cc index 615254e9bb..2bf05a8da5 100644 --- a/src/constraint_solver/expressions.cc +++ b/src/constraint_solver/expressions.cc @@ -4377,6 +4377,7 @@ IntVar* Solver::MakeIntConst(int64 val) { namespace { string IndexedName(const string& prefix, int index, int max_index) { +#if 0 #if defined(_MSC_VER) const int digits = max_index > 0 ? static_cast(log(1.0L * max_index) / log(10.0L)) + 1 : @@ -4385,6 +4386,9 @@ string IndexedName(const string& prefix, int index, int max_index) { const int digits = max_index > 0 ? static_cast(log10(max_index)) + 1: 1; #endif return StringPrintf("%s%0*d", prefix.c_str(), digits, index); +#else + return StringPrintf("%s%d", prefix.c_str(), index); +#endif } } // namespace diff --git a/src/constraint_solver/gcc.cc b/src/constraint_solver/gcc.cc index e6300e936d..fd81324d1c 100644 --- a/src/constraint_solver/gcc.cc +++ b/src/constraint_solver/gcc.cc @@ -108,13 +108,13 @@ class GccConstraint : public Constraint { for (i = 2; i < count + 2; i++) { sum_[i + 1] = sum_[i] + elements[i - 2]; } - sum_[i + 1] = sum_[i] + 1; - sum_[i + 2] = sum_[i + 1] + 1; + sum_[count + 3] = sum_[i] + 1; + sum_[count + 4] = sum_[i + 1] + 1; i = count + 3; j = i; while (i > 0) { - while (sum_[i] == sum_[i-1]) { + while (sum_[i] == sum_[i - 1]) { ds_[i--] = j; } ds_[j] = i--; @@ -144,10 +144,10 @@ class GccConstraint : public Constraint { int64 Sum(int64 from, int64 to) const { if (from <= to) { DCHECK((offset_ <= from) && (to <= last_value_)); - return sum_[to - offset_] - sum_[from - offset_ - 1]; + return sum_[to - offset_] - sum_[from -1 - offset_]; } else { DCHECK((offset_ <= to) && (from <= last_value_)); - return sum_[to - offset_ - 1] - sum_[from - offset_]; + return sum_[to - 1 - offset_] - sum_[from - offset_]; } } @@ -418,30 +418,30 @@ class GccConstraint : public Constraint { bounds_[0] = last; // merge sorted_by_min_[] and sorted_by_max_[] into bounds_[] - int64 i = 0; - int64 j = 0; + int64 min_index = 0; + int64 max_index = 0; int64 active_index = 0; for (;;) { // make sure sorted_by_min_ exhausted first - if (i < size_ && min <= max) { + if (min_index < size_ && min <= max) { if (min != last) { bounds_[++active_index] = min; last = min; } - sorted_by_min_[i]->min_rank = active_index; - if (++i < size_) { - min = sorted_by_min_[i]->min_value; + sorted_by_min_[min_index]->min_rank = active_index; + if (++min_index < size_) { + min = sorted_by_min_[min_index]->min_value; } } else { if (max != last) { bounds_[++active_index] = max; last = max; } - sorted_by_max_[j]->max_rank = active_index; - if (++j == size_) { + sorted_by_max_[max_index]->max_rank = active_index; + if (++max_index == size_) { break; } - max = sorted_by_max_[j]->max_value + 1; + max = sorted_by_max_[max_index]->max_value + 1; } } active_size_ = active_index; @@ -477,7 +477,7 @@ class GccConstraint : public Constraint { solver()->Fail(); } if (hall_[x] > x) { - int64 w = PathMax(hall_, hall_[x]); + const int64 w = PathMax(hall_, hall_[x]); sorted_by_max_[i]->min_value = bounds_[w]; PathSet(&hall_, x, w, w); changed = true; diff --git a/src/constraint_solver/local_search.cc b/src/constraint_solver/local_search.cc index 0030ee85b3..a1cde44bf5 100644 --- a/src/constraint_solver/local_search.cc +++ b/src/constraint_solver/local_search.cc @@ -2870,7 +2870,7 @@ Decision* FindOneNeighbor::Next(Solver* const solver) { solver->filtered_neighbors_ += 1; assignment_copy->Copy(reference_assignment_.get()); assignment_copy->Copy(delta); - if (solver->NestedSolve(restore, false)) { + if (solver->SolveAndCommit(restore)) { solver->accepted_neighbors_ += 1; assignment_->Store(); neighbor_found_ = true; @@ -3081,11 +3081,18 @@ NestedSolveDecision::NestedSolveDecision(DecisionBuilder* const db, void NestedSolveDecision::Apply(Solver* const solver) { CHECK(NULL != solver); - if (solver->NestedSolve(db_, restore_, - monitors_.data(), monitors_.size())) { - solver->SaveAndSetValue(&state_, static_cast(DECISION_FOUND)); + if (restore_) { + if (solver->Solve(db_, monitors_.data(), monitors_.size())) { + solver->SaveAndSetValue(&state_, static_cast(DECISION_FOUND)); + } else { + solver->SaveAndSetValue(&state_, static_cast(DECISION_FAILED)); + } } else { - solver->SaveAndSetValue(&state_, static_cast(DECISION_FAILED)); + if (solver->SolveAndCommit(db_, monitors_.data(), monitors_.size())) { + solver->SaveAndSetValue(&state_, static_cast(DECISION_FOUND)); + } else { + solver->SaveAndSetValue(&state_, static_cast(DECISION_FAILED)); + } } } diff --git a/src/constraint_solver/mtsearch.cc b/src/constraint_solver/mtsearch.cc index 875bbcb95d..7c21ce5e3a 100644 --- a/src/constraint_solver/mtsearch.cc +++ b/src/constraint_solver/mtsearch.cc @@ -133,6 +133,7 @@ class MtSolveSupport : public ParallelSolveSupport { void LockMutex(); // Unlocks the internal mutex. void UnlockMutex(); + private: bool CheckTermination(); void Reset(); diff --git a/src/constraint_solver/search.cc b/src/constraint_solver/search.cc index 9282f6184b..f32c14020f 100644 --- a/src/constraint_solver/search.cc +++ b/src/constraint_solver/search.cc @@ -4138,7 +4138,7 @@ class SolveOnce : public DecisionBuilder { virtual ~SolveOnce() {} virtual Decision* Next(Solver* s) { - bool res = s->NestedSolve(db_, false, monitors_); + bool res = s->SolveAndCommit(db_, monitors_); if (!res) { s->Fail(); } @@ -4255,7 +4255,7 @@ class NestedOptimize : public DecisionBuilder { } virtual Decision* Next(Solver* solver) { - solver->NestedSolve(db_, true, monitors_); + solver->Solve(db_, monitors_); if (collector_->solution_count() == 0) { solver->Fail(); }