diff --git a/constraint_solver/constraint_solver.cc b/constraint_solver/constraint_solver.cc index 0c79d8f9be..32d328deaf 100644 --- a/constraint_solver/constraint_solver.cc +++ b/constraint_solver/constraint_solver.cc @@ -37,7 +37,10 @@ #include "constraint_solver/model.pb.h" #include "util/const_int_array.h" -DEFINE_bool(cp_trace_demons, false, "trace all demon executions."); +DEFINE_bool(cp_trace_propagation, + false, + "Trace propagation events(constraint and demon executions," + "variable modifications)."); DEFINE_bool(cp_show_constraints, false, "show all constraints added to the solver."); DEFINE_bool(cp_print_model, false, @@ -48,7 +51,6 @@ DEFINE_string(cp_export_file, "", "Export model to file using CPModelProto."); DEFINE_bool(cp_no_solve, false, "Force failure at the beginning of a search."); DEFINE_string(cp_profile_file, "", "Export profiling overview to file."); DEFINE_bool(cp_verbose_fail, false, "Verbose output when failing."); -DEFINE_bool(cp_trace_variables, false, "Trace propagation on all variables."); void ConstraintSolverFailsHere() { VLOG(3) << "Fail"; @@ -78,13 +80,19 @@ extern void DeleteDemonMonitor(DemonMonitor* const monitor); // We need the double test because parameters are set too late when using // python in the open source. This is the cheapest work-around. bool Solver::InstrumentsDemons() const { + return parameters_.profile_level != SolverParameters::NO_PROFILING || + FLAGS_cp_trace_propagation || + !FLAGS_cp_profile_file.empty(); +} + +bool Solver::IsProfilingEnabled() const { return parameters_.profile_level != SolverParameters::NO_PROFILING || !FLAGS_cp_profile_file.empty(); } bool Solver::InstrumentsVariables() const { return parameters_.trace_level != SolverParameters::NO_TRACE || - FLAGS_cp_trace_variables; + FLAGS_cp_trace_propagation; } // ------------------ Demon class ---------------- @@ -243,10 +251,6 @@ class Queue { Demon* const demon = containers_[prio]->NextDemon(); // A NULL demon will just be ignored if (demon != NULL) { - if (FLAGS_cp_trace_demons) { - LG << "### Running demon (" << prio << "):" - << demon->DebugString() << " ###"; - } demon->set_stamp(stamp_ - 1); DCHECK_EQ(prio, demon->priority()); solver_->demon_runs_[prio]++; @@ -770,48 +774,48 @@ void Solver::InternalSaveValue(bool* valptr) { trail_->rev_bool_value_.push_back(*valptr); } -int* Solver::SafeRevAlloc(int* ptr) { - check_alloc_state(); - trail_->rev_int_memory_.push_back(ptr); - return ptr; -} - -int64* Solver::SafeRevAlloc(int64* ptr) { - check_alloc_state(); - trail_->rev_int64_memory_.push_back(ptr); - return ptr; -} - -uint64* Solver::SafeRevAlloc(uint64* ptr) { - check_alloc_state(); - trail_->rev_int64_memory_.push_back(reinterpret_cast(ptr)); - return ptr; -} - BaseObject* Solver::SafeRevAlloc(BaseObject* ptr) { check_alloc_state(); trail_->rev_object_memory_.push_back(ptr); return ptr; } -BaseObject** Solver::SafeRevAlloc(BaseObject** ptr) { +int* Solver::SafeRevAllocArray(int* ptr) { + check_alloc_state(); + trail_->rev_int_memory_.push_back(ptr); + return ptr; +} + +int64* Solver::SafeRevAllocArray(int64* ptr) { + check_alloc_state(); + trail_->rev_int64_memory_.push_back(ptr); + return ptr; +} + +uint64* Solver::SafeRevAllocArray(uint64* ptr) { + check_alloc_state(); + trail_->rev_int64_memory_.push_back(reinterpret_cast(ptr)); + return ptr; +} + +BaseObject** Solver::SafeRevAllocArray(BaseObject** ptr) { check_alloc_state(); trail_->rev_object_array_memory_.push_back(ptr); return ptr; } -IntVar** Solver::SafeRevAlloc(IntVar** ptr) { - BaseObject** in = SafeRevAlloc(reinterpret_cast(ptr)); +IntVar** Solver::SafeRevAllocArray(IntVar** ptr) { + BaseObject** in = SafeRevAllocArray(reinterpret_cast(ptr)); return reinterpret_cast(in); } -IntExpr** Solver::SafeRevAlloc(IntExpr** ptr) { - BaseObject** in = SafeRevAlloc(reinterpret_cast(ptr)); +IntExpr** Solver::SafeRevAllocArray(IntExpr** ptr) { + BaseObject** in = SafeRevAllocArray(reinterpret_cast(ptr)); return reinterpret_cast(in); } -Constraint** Solver::SafeRevAlloc(Constraint** ptr) { - BaseObject** in = SafeRevAlloc(reinterpret_cast(ptr)); +Constraint** Solver::SafeRevAllocArray(Constraint** ptr) { + BaseObject** in = SafeRevAllocArray(reinterpret_cast(ptr)); return reinterpret_cast(in); } @@ -1337,7 +1341,7 @@ Solver::Solver(const string& name, const SolverParameters& parameters) additional_constraint_index_(0), model_cache_(NULL), dependency_graph_(NULL), - trace_(NULL) { + propagation_monitor_(NULL) { Init(); } @@ -1370,13 +1374,14 @@ Solver::Solver(const string& name) additional_constraint_index_(0), model_cache_(NULL), dependency_graph_(NULL), - trace_(NULL) { + propagation_monitor_(NULL) { Init(); } extern ModelCache* BuildModelCache(Solver* const solver); extern DependencyGraph* BuildDependencyGraph(Solver* const solver); extern PropagationMonitor* BuildTrace(); +extern PropagationMonitor* BuildPrintTrace(); void Solver::Init() { for (int i = 0; i < kNumPriorities; ++i) { @@ -1390,8 +1395,11 @@ void Solver::Init() { timer_->Restart(); model_cache_.reset(BuildModelCache(this)); dependency_graph_.reset(BuildDependencyGraph(this)); - trace_.reset(BuildTrace()); + propagation_monitor_.reset(BuildTrace()); AddPropagationMonitor(reinterpret_cast(demon_monitor_)); + if (FLAGS_cp_trace_propagation) { + AddPropagationMonitor(RevAlloc(BuildPrintTrace())); + } } Solver::~Solver() { @@ -1703,9 +1711,9 @@ void Solver::ProcessConstraints() { constraint_index_ < constraints_size; ++constraint_index_) { Constraint* const constraint = constraints_list_[constraint_index_]; - trace_->StartConstraintInitialPropagation(constraint); + propagation_monitor_->BeginConstraintInitialPropagation(constraint); constraint->PostAndPropagate(); - trace_->EndConstraintInitialPropagation(constraint); + propagation_monitor_->EndConstraintInitialPropagation(constraint); } CHECK_EQ(constraints_list_.size(), constraints_size); @@ -1718,9 +1726,10 @@ void Solver::ProcessConstraints() { const int parent_index = additional_constraints_parent_list_[additional_constraint_index_]; const Constraint* const parent = constraints_list_[parent_index]; - trace_->StartNestedConstraintInitialPropagation(parent, nested); + propagation_monitor_->BeginNestedConstraintInitialPropagation(parent, + nested); nested->PostAndPropagate(); - trace_->EndNestedConstraintInitialPropagation(parent, nested); + propagation_monitor_->EndNestedConstraintInitialPropagation(parent, nested); } } @@ -1952,7 +1961,7 @@ void Solver::RestartSearch() { PushSentinel(INITIAL_SEARCH_SENTINEL); } - trace_->RestartSearch(); + propagation_monitor_->RestartSearch(); search->RestartSearch(); } @@ -2074,9 +2083,11 @@ bool Solver::NextSolution() { case OUTSIDE_SEARCH: { state_ = IN_ROOT_NODE; search->BeginInitialPropagation(); + propagation_monitor_->BeginInitialPropagation(); CP_TRY(search) { ProcessConstraints(); search->EndInitialPropagation(); + propagation_monitor_->EndInitialPropagation(); PushSentinel(ROOT_NODE_SENTINEL); state_ = IN_SEARCH; search->ClearBuffer(); @@ -2109,9 +2120,11 @@ bool Solver::NextSolution() { search->left_search_depth()); // 1 for right branch PushState(CHOICE_POINT, i1); search->RefuteDecision(fd); + propagation_monitor_->RefuteDecision(fd); branches_++; fd->Refute(this); search->AfterDecision(fd, false); + propagation_monitor_->AfterDecision(fd); search->RightMove(); fd = NULL; } @@ -2137,20 +2150,24 @@ bool Solver::NextSolution() { search->left_search_depth()); // 0 for left branch PushState(CHOICE_POINT, i2); search->ApplyDecision(d); + propagation_monitor_->ApplyDecision(d); branches_++; d->Apply(this); search->AfterDecision(d, true); + propagation_monitor_->AfterDecision(d); search->LeftMove(); break; } case KEEP_LEFT: { search->ApplyDecision(d); + propagation_monitor_->ApplyDecision(d); d->Apply(this); search->AfterDecision(d, true); break; } case KEEP_RIGHT: { search->RefuteDecision(d); + propagation_monitor_->RefuteDecision(d); d->Refute(this); search->AfterDecision(d, false); break; @@ -2165,6 +2182,7 @@ bool Solver::NextSolution() { } if (search->AcceptSolution()) { search->IncrementSolutionCounter(); + propagation_monitor_->FindSolution(); if (!search->AtSolution() || !CurrentlyInSolve()) { result = true; finish = true; @@ -2217,7 +2235,7 @@ void Solver::EndSearch() { Search* const search = searches_.back(); BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); search->ExitSearch(); - trace_->ExitSearch(); + propagation_monitor_->ExitSearch(); search->Clear(); state_ = OUTSIDE_SEARCH; if (!FLAGS_cp_profile_file.empty()) { @@ -2240,18 +2258,20 @@ bool Solver::CheckAssignment(Assignment* const solution) { // Push monitors and enter search. search->EnterSearch(); - trace_->EnterSearch(); + propagation_monitor_->EnterSearch(); // Push sentinel and set decision builder. DCHECK_EQ(1, searches_.size()); PushSentinel(INITIAL_SEARCH_SENTINEL); search->BeginInitialPropagation(); + propagation_monitor_->BeginInitialPropagation(); CP_TRY(search) { state_ = IN_ROOT_NODE; DecisionBuilder * const restore = MakeRestoreAssignment(solution); restore->Next(this); ProcessConstraints(); search->EndInitialPropagation(); + propagation_monitor_->EndInitialPropagation(); BacktrackToSentinel(INITIAL_SEARCH_SENTINEL); search->ClearBuffer(); state_ = OUTSIDE_SEARCH; @@ -2385,11 +2405,8 @@ void Solver::Fail() { } ConstraintSolverFailsHere(); fails_++; - trace_->RaiseFailure(); + propagation_monitor_->RaiseFailure(); searches_.back()->BeginFail(); - if (FLAGS_cp_trace_demons || FLAGS_cp_verbose_fail) { - LOG(INFO) << "### Failure ###"; - } searches_.back()->JumpBack(); } @@ -2403,7 +2420,13 @@ string Solver::GetName(const PropagationBaseObject* object) const { const IntegerCastInfo* const cast_info = FindOrNull(cast_information_, object); if (cast_info != NULL && cast_info->expression != NULL) { - return StringPrintf("Var<%s>", cast_info->expression->name().c_str()); + if (cast_info->expression->HasName()) { + return StringPrintf("Var<%s>", + cast_info->expression->name().c_str()); + } else { + return StringPrintf("Var<%s>", + cast_info->expression->DebugString().c_str()); + } } return empty_name_; } diff --git a/constraint_solver/constraint_solver.h b/constraint_solver/constraint_solver.h index 37086eeb86..3ae5f2a97a 100644 --- a/constraint_solver/constraint_solver.h +++ b/constraint_solver/constraint_solver.h @@ -54,7 +54,6 @@ // demons Run = 25, // Run time = 0 ms) // -// More infos: go/operations_research // // TODO(user): Remove C-style API and update following comment. // Global remark: many functions and methods in this file can take as argument @@ -819,23 +818,19 @@ class Solver { // factory methods (e.g., MakeIntVar(...), MakeAllDifferent(...) already take // care of the registration. template T* RevAlloc(T* object) { - // Note that if class MyObject inherits from BaseObject and has a default - // constructor, then: - // solver.RevAlloc(new MyObject()); compiles and does what you expect, - // solver.RevAlloc(new MyObject[26]); compiles but should not be used: - // it will NOT delete the array. - // solver.RevAlloc(new MyObject*[53]); does not compile, because MyObject* - // does not match BaseObject*. - // - // TODO(user): either make that solver.RevAlloc(new MyObject[26]) does - // not compile, or make it not leak. - // TODO(user): Split between a function that takes an array as argument and - // a version that takes a pointer on a BaseObject, and rename to something - // more explicit like RegisterReversibleObject / RegisterReversibleArray or - // similar. Check whether this split fixes the leak mentioned above. return reinterpret_cast(SafeRevAlloc(object)); } + // Like RevAlloc() above, but for an array of objects: the array + // must have been allocated with the new[] operator. The entire array + // will be deleted when backtracking out of the current state. + // + // This method is valid for arrays of int, int64, uint64, bool, + // BaseObject*, IntVar*, IntExpr*, and Constraint*. + template T* RevAllocArray(T* object) { + return reinterpret_cast(SafeRevAllocArray(object)); + } + // propagation // Adds the constraint 'c' to the model. @@ -2723,14 +2718,16 @@ class Solver { ModelCache* Cache() const; // Returns wether we are instrumenting demons. bool InstrumentsDemons() const; + // Returns wether we are profiling the solver. + bool IsProfilingEnabled() const; // Returns wether we are tracing variables. bool InstrumentsVariables() const; // Returns the name of the model. string model_name() const; // Returns the dependency graph of the solver. DependencyGraph* Graph() const; - // Returns the main trace object. - PropagationMonitor* Trace() const; + // Returns the propagation monitor. + PropagationMonitor* GetPropagationMonitor() const; // Adds the propagation monitor to the solver. This should be done // before the search. void AddPropagationMonitor(PropagationMonitor* const monitor); @@ -2748,6 +2745,7 @@ class Solver { friend void InternalSaveBooleanVarValue(Solver* const, IntVar* const); friend void SetQueueCleanerOnFail(Solver* const, IntVar* const); template friend class SimpleRevFIFO; + template friend class RevImmutableMultiMap; #endif private: @@ -2779,14 +2777,15 @@ class Solver { InternalSaveValue(reinterpret_cast(valptr)); } - int* SafeRevAlloc(int* ptr); - int64* SafeRevAlloc(int64* ptr); - uint64* SafeRevAlloc(uint64* ptr); BaseObject* SafeRevAlloc(BaseObject* ptr); - BaseObject** SafeRevAlloc(BaseObject** ptr); - IntVar** SafeRevAlloc(IntVar** ptr); - IntExpr** SafeRevAlloc(IntExpr** ptr); - Constraint** SafeRevAlloc(Constraint** ptr); + + int* SafeRevAllocArray(int* ptr); + int64* SafeRevAllocArray(int64* ptr); + uint64* SafeRevAllocArray(uint64* ptr); + BaseObject** SafeRevAllocArray(BaseObject** ptr); + IntVar** SafeRevAllocArray(IntVar** ptr); + IntExpr** SafeRevAllocArray(IntExpr** ptr); + Constraint** SafeRevAllocArray(Constraint** ptr); // UnsafeRevAlloc is used internally for cells in SimpleRevFIFO // and other structures like this. void* UnsafeRevAllocAux(void* ptr); @@ -2860,7 +2859,7 @@ class Solver { scoped_ptr model_cache_; scoped_ptr dependency_graph_; - scoped_ptr trace_; + scoped_ptr propagation_monitor_; DISALLOW_COPY_AND_ASSIGN(Solver); }; diff --git a/constraint_solver/constraint_solver.swig b/constraint_solver/constraint_solver.swig index f3ec753696..e8702be8a2 100644 --- a/constraint_solver/constraint_solver.swig +++ b/constraint_solver/constraint_solver.swig @@ -22,13 +22,12 @@ #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" -DECLARE_bool(cp_trace_demons); +DECLARE_bool(cp_trace_propagation); DECLARE_bool(cp_print_model); DECLARE_bool(cp_model_stats); DECLARE_string(cp_export_file); DECLARE_bool(cp_no_solve); DECLARE_string(cp_profile_file); -DECLARE_bool(cp_trace_variables); struct FailureProtect { jmp_buf exception_buffer; @@ -226,8 +225,8 @@ namespace operations_research { %pythoncode { import gflags FLAGS=gflags.FLAGS -gflags.DEFINE_boolean('cp_trace_demons', False, - 'trace all demon executions.') +gflags.DEFINE_boolean('cp_trace_propagation', False, + 'trace all propagation events.') gflags.DEFINE_boolean('cp_print_model', False, 'prints the model before solving it.') gflags.DEFINE_boolean('cp_model_stats', False, @@ -238,8 +237,6 @@ gflags.DEFINE_boolean('cp_no_solve', False, 'force failures at the beginning of a search.') gflags.DEFINE_string('cp_profile_file', '', 'exports profiling overview to file.') -gflags.DEFINE_boolean('cp_trace_variables', False, - 'trace all variables modifications.') } %pythoncode { @@ -882,31 +879,28 @@ namespace operations_research { // Indentation is critical here as the code is copied verbatim in the // python code. %feature("pythonappend") Solver::Solver %{ - Solver.SetPythonFlags(FLAGS.cp_trace_demons, + Solver.SetPythonFlags(FLAGS.cp_trace_propagation, FLAGS.cp_print_model, FLAGS.cp_model_stats, FLAGS.cp_export_file, FLAGS.cp_no_solve, - FLAGS.cp_profile_file, - FLAGS.cp_trace_variables) + FLAGS.cp_profile_file) %} %extend Solver { -static void SetPythonFlags(bool trace_demon, +static void SetPythonFlags(bool trace_propagation, bool print_model, bool model_stats, const string& export_file, bool no_solve, - const string& profile_file, - bool trace_variables) { - FLAGS_cp_trace_demons = trace_demon; + const string& profile_file) { + FLAGS_cp_trace_propagation = trace_propagation; FLAGS_cp_print_model = print_model; FLAGS_cp_model_stats = model_stats; FLAGS_cp_export_file = export_file; FLAGS_cp_no_solve = no_solve; FLAGS_cp_profile_file = profile_file; - FLAGS_cp_trace_variables = trace_variables; } Constraint* TreeNoCycle(const std::vector& nexts, diff --git a/constraint_solver/constraint_solveri.h b/constraint_solver/constraint_solveri.h index 10c55b3560..29018b3a20 100644 --- a/constraint_solver/constraint_solveri.h +++ b/constraint_solver/constraint_solveri.h @@ -22,6 +22,7 @@ // A reversible data structure is a data structure that reverts its // modifications when the search is going up in the search tree, usually // after a failure occurs. +// - RevImmutableMultiMap a reversible immutable multimap. // - MakeConstraintDemon and MakeDelayedConstraintDemon to wrap methods // of a constraint as a demon. // - LocalSearchOperator, IntVarLocalSearchOperator, ChangeValue and @@ -65,6 +66,8 @@ #include "base/map-util.h" #include "base/hash.h" #include "constraint_solver/constraint_solver.h" +#include "util/const_int_array.h" +#include "util/const_ptr_array.h" #include "util/vector_map.h" template class ResultCallback; @@ -210,6 +213,189 @@ template class SimpleRevFIFO { Chunk *chunks_; int pos_; }; + +// ---------- Reversible Hash Table ---------- + +// ----- Hash functions ----- +// TODO(user): use murmurhash. +inline uint64 Hash1(uint64 value) { + value = (~value) + (value << 21); // value = (value << 21) - value - 1; + value ^= value >> 24; + value += (value << 3) + (value << 8); // value * 265 + value ^= value >> 14; + value += (value << 2) + (value << 4); // value * 21 + value ^= value >> 28; + value += (value << 31); + return value; +} + +inline uint64 Hash1(uint32 value) { + uint64 a = value; + a = (a + 0x7ed55d16) + (a << 12); + a = (a ^ 0xc761c23c) ^ (a >> 19); + a = (a + 0x165667b1) + (a << 5); + a = (a + 0xd3a2646c) ^ (a << 9); + a = (a + 0xfd7046c5) + (a << 3); + a = (a ^ 0xb55a4f09) ^ (a >> 16); + return a; +} + +inline uint64 Hash1(int64 value) { + return Hash1(static_cast(value)); +} + +inline uint64 Hash1(int value) { + return Hash1(static_cast(value)); +} + +inline uint64 Hash1(void* const ptr) { +#if defined(ARCH_K8) + return Hash1(reinterpret_cast(ptr)); +#else + return Hash1(reinterpret_cast(ptr)); +#endif +} + +inline uint64 Hash1(ConstIntArray* const values) { + if (values->size() == 0) { + return 0; + } else if (values->size() == 1) { + return Hash1(values->get(0)); + } else { + uint64 hash = Hash1(values->get(0)); + for (int i = 1; i < values->size(); ++i) { + hash = hash * i + Hash1(values->get(i)); + } + return hash; + } +} + +template uint64 Hash1(ConstPtrArray* const ptrs) { + if (ptrs->size() == 0) { + return 0; + } else if (ptrs->size() == 1) { + return Hash1(ptrs->get(0)); + } else { + uint64 hash = Hash1(ptrs->get(0)); + for (int i = 1; i < ptrs->size(); ++i) { + hash = hash * i + Hash1(ptrs->get(i)); + } + return hash; + } +} + +// ----- Immutable Multi Map ----- + +// Reversible Immutable MultiMap class. +// Represents an immutable multi-map that backstracks with the solver. +template class RevImmutableMultiMap { + public: + RevImmutableMultiMap(Solver* const solver, int initial_size) + : solver_(solver), + array_(solver->UnsafeRevAllocArray(new Cell*[initial_size])), + size_(initial_size), + num_items_(0) { + memset(array_, 0, sizeof(*array_) * size_); + } + + ~RevImmutableMultiMap() {} + + int num_items() const { return num_items_; } + + // Returns true if the multi-map contains at least one instance of 'key'. + bool ContainsKey(const K& key) const { + uint64 code = Hash1(key) % size_; + Cell* tmp = array_[code]; + while (tmp) { + if (tmp->key() == key) { + return true; + } + tmp = tmp->next(); + } + return false; + } + + // Returns one value attached to 'key', or 'defaut_value' if 'key' + // is not in the multi-map. The actual value returned if more than one + // values is attached to the same key is not specified. + const V& FindWithDefault(const K& key, const V& default_value) const { + uint64 code = Hash1(key) % size_; + Cell* tmp = array_[code]; + while (tmp) { + if (tmp->key() == key) { + return tmp->value(); + } + tmp = tmp->next(); + } + return default_value; + } + + // Inserts (key, value) in the multi-map. + void Insert(const K& key, const V& value) { + const int position = Hash1(key) % size_; + Cell* const cell = + solver_->UnsafeRevAlloc(new Cell(key, value, array_[position])); + solver_->SaveAndSetValue(reinterpret_cast(&array_[position]), + reinterpret_cast(cell)); + solver_->SaveAndAdd(&num_items_, 1); + if (num_items_ > 2 * size_) { + Double(); + } + } + + private: + class Cell { + public: + Cell(const K& key, const V& value, Cell* const next) + : key_(key), value_(value), next_(next) {} + + void SetRevNext(Solver* const solver, Cell* const next) { + solver->SaveAndSetValue(reinterpret_cast(&next_), + reinterpret_cast(next)); + } + + Cell* next() const { return next_; } + + const K& key() const { return key_; } + + const V& value() const { return value_; } + + private: + const K key_; + const V value_; + Cell* next_; + }; + + void Double() { + Cell** const old_cell_array = array_; + const int old_size = size_; + solver_->SaveAndAdd(&size_, size_); + solver_->SaveAndSetValue( + reinterpret_cast(&array_), + reinterpret_cast( + solver_->UnsafeRevAllocArray(new Cell*[size_]))); + memset(array_, 0, size_ * sizeof(*array_)); + for (int i = 0; i < old_size; ++i) { + Cell* tmp = old_cell_array[i]; + while (tmp != NULL) { + Cell* const to_reinsert = tmp; + tmp = tmp->next(); + const uint64 new_position = Hash1(to_reinsert->key()) % size_; + to_reinsert->SetRevNext(solver_, array_[new_position]); + solver_->SaveAndSetValue( + reinterpret_cast(&array_[new_position]), + reinterpret_cast(to_reinsert)); + } + } + } + + Solver* const solver_; + Cell** array_; + int size_; + int num_items_; + // TODO(user): Experiment with stamping 'array_'. +}; + // @{ // These methods represent generic demons that will call back a // method on the constraint during their Run method. @@ -795,25 +981,64 @@ class IntVarLocalSearchFilter : public LocalSearchFilter { class PropagationMonitor : public BaseObject { public: - virtual void StartInitialPropagation() = 0; + // Propagation events. + virtual void BeginInitialPropagation() = 0; virtual void EndInitialPropagation() = 0; - virtual void StartConstraintInitialPropagation( + virtual void BeginConstraintInitialPropagation( const Constraint* const constraint) = 0; virtual void EndConstraintInitialPropagation( const Constraint* const constraint) = 0; - virtual void StartNestedConstraintInitialPropagation( + virtual void BeginNestedConstraintInitialPropagation( const Constraint* const parent, const Constraint* const nested) = 0; virtual void EndNestedConstraintInitialPropagation( const Constraint* const parent, const Constraint* const nested) = 0; virtual void RegisterDemon(const Demon* const demon) = 0; - virtual void StartDemonRun(const Demon* const demon) = 0; + virtual void BeginDemonRun(const Demon* const demon) = 0; virtual void EndDemonRun(const Demon* const demon) = 0; virtual void RaiseFailure() = 0; + virtual void FindSolution() = 0; virtual void EnterSearch() = 0; virtual void ExitSearch() = 0; virtual void RestartSearch() = 0; + virtual void ApplyDecision(Decision* const decision) = 0; + virtual void RefuteDecision(Decision* const decision) = 0; + virtual void AfterDecision(Decision* const decision) = 0; + // IntExpr modifiers. + virtual void SetMin(IntExpr* const expr, int64 new_min) = 0; + virtual void SetMax(IntExpr* const expr, int64 new_max) = 0; + virtual void SetRange(IntExpr* const expr, int64 new_min, int64 new_max) = 0; + // IntVar modifiers. + virtual void SetMin(IntVar* const var, int64 new_min) = 0; + virtual void SetMax(IntVar* const var, int64 new_max) = 0; + virtual void SetRange(IntVar* const var, int64 new_min, int64 new_max) = 0; + virtual void RemoveValue(IntVar* const var, int64 value) = 0; + virtual void SetValue(IntVar* const var, int64 value) = 0; + virtual void RemoveInterval(IntVar* const var, int64 imin, int64 imax) = 0; + virtual void SetValues(IntVar* const var, + const int64* const values, + int size) = 0; + virtual void RemoveValues(IntVar* const var, + const int64* const values, + int size) = 0; + // IntervalVar modifiers. + virtual void SetStartMin(IntervalVar* const var, int64 new_min) = 0; + virtual void SetStartMax(IntervalVar* const var, int64 new_max) = 0; + virtual void SetStartRange(IntervalVar* const var, + int64 new_min, + int64 new_max) = 0; + virtual void SetEndMin(IntervalVar* const var, int64 new_min) = 0; + virtual void SetEndMax(IntervalVar* const var, int64 new_max) = 0; + virtual void SetEndRange(IntervalVar* const var, + int64 new_min, + int64 new_max) = 0; + virtual void SetDurationMin(IntervalVar* const var, int64 new_min) = 0; + virtual void SetDurationMax(IntervalVar* const var, int64 new_max) = 0; + virtual void SetDurationRange(IntervalVar* const var, + int64 new_min, + int64 new_max) = 0; + virtual void SetPerformed(IntervalVar* const var, bool value) = 0; }; // ---------- SymmetryBreaker ---------- diff --git a/constraint_solver/demon_profiler.cc b/constraint_solver/demon_profiler.cc index 02c52e8d7d..684d0e284d 100644 --- a/constraint_solver/demon_profiler.cc +++ b/constraint_solver/demon_profiler.cc @@ -37,10 +37,12 @@ namespace operations_research { // after the end of a search. class DemonMonitor : public PropagationMonitor { public: - DemonMonitor() - : active_constraint_(NULL), - active_demon_(NULL), + DemonMonitor(Solver* const solver) + : solver_(solver), + active_constraint_(NULL), + active_demon_(NULL), start_time_(WallTimer::GetTimeInMicroSeconds()) {} + virtual ~DemonMonitor() { STLDeleteContainerPairSecondPointers(constraint_map_.begin(), constraint_map_.end()); @@ -50,8 +52,12 @@ start_time_(WallTimer::GetTimeInMicroSeconds()) {} return WallTimer::GetTimeInMicroSeconds() - start_time_; } - virtual void StartConstraintInitialPropagation( + virtual void BeginConstraintInitialPropagation( const Constraint* const constraint) { + if (solver_->state() == Solver::IN_SEARCH) { + return; + } + CHECK(active_constraint_ == NULL); CHECK(active_demon_ == NULL); CHECK_NOTNULL(constraint); @@ -69,15 +75,20 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; CHECK_NOTNULL(constraint); CHECK_EQ(constraint, active_constraint_); ConstraintRuns* const ct_run = constraint_map_[constraint]; - CHECK_NOTNULL(ct_run); - ct_run->add_initial_propagation_end_time(CurrentTime()); - ct_run->set_failures(0); + if (ct_run != NULL) { + ct_run->add_initial_propagation_end_time(CurrentTime()); + ct_run->set_failures(0); + } active_constraint_ = NULL; } - virtual void StartNestedConstraintInitialPropagation( + virtual void BeginNestedConstraintInitialPropagation( const Constraint* const constraint, const Constraint* const delayed) { + if (solver_->state() == Solver::IN_SEARCH) { + return; + } + CHECK(active_constraint_ == NULL); CHECK(active_demon_ == NULL); CHECK_NOTNULL(constraint); @@ -96,13 +107,18 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; CHECK_NOTNULL(delayed); CHECK_EQ(constraint, active_constraint_); ConstraintRuns* const ct_run = constraint_map_[constraint]; - CHECK_NOTNULL(ct_run); - ct_run->add_initial_propagation_end_time(CurrentTime()); - ct_run->set_failures(0); + if (ct_run != NULL) { + ct_run->add_initial_propagation_end_time(CurrentTime()); + ct_run->set_failures(0); + } active_constraint_ = NULL; } virtual void RegisterDemon(const Demon* const demon) { + if (solver_->state() == Solver::IN_SEARCH) { + return; + } + if (demon_map_.find(demon) == demon_map_.end()) { CHECK_NOTNULL(active_constraint_); CHECK(active_demon_ == NULL); @@ -116,44 +132,99 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; } } - virtual void StartDemonRun(const Demon* const demon) { + virtual void BeginDemonRun(const Demon* const demon) { CHECK(active_demon_ == NULL); CHECK_NOTNULL(demon); active_demon_ = demon; DemonRuns* const demon_run = demon_map_[active_demon_]; - demon_run->add_start_time(CurrentTime()); + if (demon_run != NULL) { + demon_run->add_start_time(CurrentTime()); + } } virtual void EndDemonRun(const Demon* const demon) { CHECK_EQ(active_demon_, demon); CHECK_NOTNULL(demon); DemonRuns* const demon_run = demon_map_[active_demon_]; - CHECK_NOTNULL(demon_run); - demon_run->add_end_time(CurrentTime()); + if (demon_run != NULL) { + demon_run->add_end_time(CurrentTime()); + } active_demon_ = NULL; } virtual void RaiseFailure() { if (active_demon_ != NULL) { DemonRuns* const demon_run = demon_map_[active_demon_]; - demon_run->add_end_time(CurrentTime()); - demon_run->set_failures(demon_run->failures() + 1); + if (demon_run != NULL) { + demon_run->add_end_time(CurrentTime()); + demon_run->set_failures(demon_run->failures() + 1); + } active_demon_ = NULL; // active_constraint_ can be non null in case of initial propagation. active_constraint_ = NULL; } else if (active_constraint_ != NULL) { ConstraintRuns* const ct_run = constraint_map_[active_constraint_]; - CHECK_NOTNULL(ct_run); - ct_run->add_initial_propagation_end_time(CurrentTime()); - ct_run->set_failures(1); + if (ct_run != NULL) { + ct_run->add_initial_propagation_end_time(CurrentTime()); + ct_run->set_failures(1); + } active_constraint_ = NULL; } } - virtual void StartInitialPropagation() {} + virtual void FindSolution() {} + virtual void ApplyDecision(Decision* const decision) {} + virtual void RefuteDecision(Decision* const decision) {} + virtual void AfterDecision(Decision* const decision) {} + + + // Restarts a search and clears all previously collected information. + virtual void RestartSearch() { + STLDeleteContainerPairSecondPointers(constraint_map_.begin(), + constraint_map_.end()); + constraint_map_.clear(); + demon_map_.clear(); + demons_per_constraint_.clear(); + } + + virtual void BeginInitialPropagation() {} virtual void EndInitialPropagation() {} virtual void EnterSearch() {} virtual void ExitSearch() {} + // IntExpr modifiers. + virtual void SetMin(IntExpr* const expr, int64 new_min) {} + virtual void SetMax(IntExpr* const expr, int64 new_max) {} + virtual void SetRange(IntExpr* const expr, int64 new_min, int64 new_max) {} + // IntVar modifiers. + virtual void SetMin(IntVar* const var, int64 new_min) {} + virtual void SetMax(IntVar* const var, int64 new_max) {} + virtual void SetRange(IntVar* const var, int64 new_min, int64 new_max) {} + virtual void RemoveValue(IntVar* const var, int64 value) {} + virtual void SetValue(IntVar* const var, int64 value) {} + virtual void RemoveInterval(IntVar* const var, int64 imin, int64 imax) {} + virtual void SetValues(IntVar* const var, + const int64* const values, + int size) {} + virtual void RemoveValues(IntVar* const var, + const int64* const values, + int size) {} + // IntervalVar modifiers. + virtual void SetStartMin(IntervalVar* const var, int64 new_min) {} + virtual void SetStartMax(IntervalVar* const var, int64 new_max) {} + virtual void SetStartRange(IntervalVar* const var, + int64 new_min, + int64 new_max) {} + virtual void SetEndMin(IntervalVar* const var, int64 new_min) {} + virtual void SetEndMax(IntervalVar* const var, int64 new_max) {} + virtual void SetEndRange(IntervalVar* const var, + int64 new_min, + int64 new_max) {} + virtual void SetDurationMin(IntervalVar* const var, int64 new_min) {} + virtual void SetDurationMax(IntervalVar* const var, int64 new_max) {} + virtual void SetDurationRange(IntervalVar* const var, + int64 new_min, + int64 new_max) {} + virtual void SetPerformed(IntervalVar* const var, bool value) {} // Useful for unit tests. void AddFakeRun(const Demon* const demon, @@ -243,15 +314,6 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; file->Close(); } - // Restarts a search and clears all previously collected information. - void RestartSearch() { - STLDeleteContainerPairSecondPointers(constraint_map_.begin(), - constraint_map_.end()); - constraint_map_.clear(); - demon_map_.clear(); - demons_per_constraint_.clear(); - } - // Export Information void ExportInformation(const Constraint* const constraint, int64* const fails, @@ -341,6 +403,7 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; } private: + Solver* const solver_; const Constraint* active_constraint_; const Demon* active_demon_; const int64 start_time_; @@ -353,17 +416,15 @@ return WallTimer::GetTimeInMicroSeconds() - start_time_; // track the usage and perfomance of the demons. class DemonProfiler : public Demon { public: - DemonProfiler(Demon* const demon, DemonMonitor* const monitor) - : demon_(demon), monitor_(monitor) { + explicit DemonProfiler(Demon* const demon) : demon_(demon) { CHECK_NOTNULL(demon); - CHECK_NOTNULL(monitor); } // This is the main callback of the demon. - void Run(Solver* const s) { - monitor_->StartDemonRun(demon_); - demon_->Run(s); - monitor_->EndDemonRun(demon_); + void Run(Solver* const solver) { + solver->GetPropagationMonitor()->BeginDemonRun(demon_); + demon_->Run(solver); + solver->GetPropagationMonitor()->EndDemonRun(demon_); } // Returns the priority of the demon. @@ -373,12 +434,11 @@ class DemonProfiler : public Demon { // DebugString of the contained demon. string DebugString() const { - return StringPrintf("demon_profiler<%s>", demon_->DebugString().c_str()); + return demon_->DebugString(); } private: Demon* const demon_; - DemonMonitor* const monitor_; }; @@ -391,8 +451,8 @@ void Solver::ExportProfilingOverview(const string& filename) { // ----- Exported Functions ----- DemonMonitor* BuildDemonMonitor(Solver* const solver) { - if (solver->InstrumentsDemons()) { - return new DemonMonitor; + if (solver->IsProfilingEnabled()) { + return new DemonMonitor(solver); } else { return NULL; } @@ -404,10 +464,9 @@ void DeleteDemonMonitor(DemonMonitor* const monitor) { Demon* Solver::RegisterDemon(Demon* const demon) { CHECK_NOTNULL(demon); - if (InstrumentsDemons() && state_ != IN_SEARCH) { - CHECK_NOTNULL(demon_monitor_); - demon_monitor_->RegisterDemon(demon); - return RevAlloc(new DemonProfiler(demon, demon_monitor_)); + if (InstrumentsDemons()) { + propagation_monitor_->RegisterDemon(demon); + return RevAlloc(new DemonProfiler(demon)); } else { return demon; } @@ -419,7 +478,7 @@ void BuildDemonProfiler(Solver* const solver, Demon* const demon, DemonMonitor* const monitor) { monitor->RegisterDemon(demon); - solver->RevAlloc(new DemonProfiler(demon, monitor)); + solver->RevAlloc(new DemonProfiler(demon)); } void DeleteDemonProfiler(DemonProfiler* const profiler) { @@ -449,9 +508,9 @@ void DemonMonitorExportInformation(DemonMonitor* const monitor, demon_count); } -void DemonMonitorStartInitialPropagation(DemonMonitor* const monitor, +void DemonMonitorBeginInitialPropagation(DemonMonitor* const monitor, const Constraint* const constraint) { - monitor->StartConstraintInitialPropagation(constraint); + monitor->BeginConstraintInitialPropagation(constraint); } void DemonMonitorEndInitialPropagation(DemonMonitor* const monitor, diff --git a/constraint_solver/interval.cc b/constraint_solver/interval.cc index f569576bb2..90f23f9966 100644 --- a/constraint_solver/interval.cc +++ b/constraint_solver/interval.cc @@ -1414,6 +1414,9 @@ IntExpr* IntervalVar::StartExpr() { solver()->SaveValue(reinterpret_cast(&start_expr_)); start_expr_ = solver()->RegisterIntExpr( solver()->RevAlloc(new IntervalVarStartExpr(this))); + if (HasName()) { + start_expr_->set_name(StringPrintf("start(%s)", name().c_str())); + } } return start_expr_; } @@ -1423,6 +1426,9 @@ IntExpr* IntervalVar::DurationExpr() { solver()->SaveValue(reinterpret_cast(&duration_expr_)); duration_expr_ = solver()->RegisterIntExpr( solver()->RevAlloc(new IntervalVarDurationExpr(this))); + if (HasName()) { + duration_expr_->set_name(StringPrintf("duration(%s)", name().c_str())); + } } return duration_expr_; } @@ -1432,6 +1438,9 @@ IntExpr* IntervalVar::EndExpr() { solver()->SaveValue(reinterpret_cast(&end_expr_)); end_expr_ = solver()->RegisterIntExpr( solver()->RevAlloc(new IntervalVarEndExpr(this))); + if (HasName()) { + end_expr_->set_name(StringPrintf("end(%s)", name().c_str())); + } } return end_expr_; } @@ -1441,6 +1450,9 @@ IntExpr* IntervalVar::PerformedExpr() { solver()->SaveValue(reinterpret_cast(&performed_expr_)); performed_expr_ = solver()->RegisterIntExpr( solver()->RevAlloc(new IntervalVarPerformedExpr(this))); + if (HasName()) { + performed_expr_->set_name(StringPrintf("performed(%s)", name().c_str())); + } } return performed_expr_; } diff --git a/constraint_solver/model_cache.cc b/constraint_solver/model_cache.cc index 0cb716444d..bf86a8e776 100644 --- a/constraint_solver/model_cache.cc +++ b/constraint_solver/model_cache.cc @@ -54,69 +54,6 @@ template bool IsEqual(ConstPtrArray* const a1, return a1->Equals(*a2); } -uint64 Hash1(uint64 value) { - value = (~value) + (value << 21); // value = (value << 21) - value - 1; - value = value ^ (value >> 24); - value = (value + (value << 3)) + (value << 8); // value * 265 - value = value ^ (value >> 14); - value = (value + (value << 2)) + (value << 4); // value * 21 - value = value ^ (value >> 28); - value = value + (value << 31); - return value; -} - -uint64 Hash1(uint32 value) { - uint64 a = value; - a = (a + 0x7ed55d16) + (a << 12); - a = (a ^ 0xc761c23c) ^ (a >> 19); - a = (a + 0x165667b1) + (a << 5); - a = (a + 0xd3a2646c) ^ (a << 9); - a = (a + 0xfd7046c5) + (a << 3); - a = (a ^ 0xb55a4f09) ^ (a >> 16); - return a; -} - -uint64 Hash1(int64 value) { - return Hash1(static_cast(value)); -} - -uint64 Hash1(void* const ptr) { -#if defined(ARCH_K8) - return Hash1(reinterpret_cast(ptr)); -#else - return Hash1(reinterpret_cast(ptr)); -#endif -} - -uint64 Hash1(ConstIntArray* const values) { - if (values->size() == 0) { - return 0; - } else if (values->size() == 1) { - return Hash1(values->get(0)); - } else { - uint64 hash = Hash1(values->get(0)); - for (int i = 1; i < values->size(); ++i) { - hash = hash * i + Hash1(values->get(i)); - } - return hash; - } -} - -template -uint64 Hash1(ConstPtrArray* const ptrs) { - if (ptrs->size() == 0) { - return 0; - } else if (ptrs->size() == 1) { - return Hash1(ptrs->get(0)); - } else { - uint64 hash = Hash1(ptrs->get(0)); - for (int i = 1; i < ptrs->size(); ++i) { - hash = hash * i + Hash1(ptrs->get(i)); - } - return hash; - } -} - template uint64 Hash2(const A1& a1, const A2& a2) { uint64 a = Hash1(a1); uint64 b = GG_ULONGLONG(0xe08c1d668b756f82); // more of the golden ratio diff --git a/constraint_solver/routing.cc b/constraint_solver/routing.cc index e443941d7c..f2a655038a 100644 --- a/constraint_solver/routing.cc +++ b/constraint_solver/routing.cc @@ -117,6 +117,177 @@ template<> size_t hash_value( namespace operations_research { +// Pair-based neighborhood operators, designed to move nodes by pairs (pairs +// are static and given). These neighborhoods are very useful for Pickup and +// Delivery problems where pickup and delivery nodes must remain on the same +// route. +// TODO(user): Add option to prune neighbords where the order of node pairs +// is violated (ie precedence between pickup and delivery nodes). +// TODO(user): Move this to local_search.cc if it's generic enough. +// TODO(user): Detect pairs automatically by parsing the constraint model; +// we could then get rid of the pair API in the RoutingModel +// class. + +// Operator which inserts pairs of inactive nodes into a path. +// Possible neighbors for the path 1 -> 2 -> 3 with pair (A, B) inactive +// (where 1 and 3 are first and last nodes of the path) are: +// 1 -> [A] -> [B] -> 2 -> 3 +// 1 -> [B] -> 2 -> [A] -> 3 +// 1 -> [A] -> 2 -> [B] -> 3 +// 1 -> 2 -> [A] -> [B] -> 3 +// Note that this operator does not expicitely insert the nodes of a pair one +// after the other which forbids the following solutions: +// 1 -> [B] -> [A] -> 2 -> 3 +// 1 -> 2 -> [B] -> [A] -> 3 +// which can only be obtained by inserting A after B. +class MakePairActiveOperator : public PathOperator { + public: + MakePairActiveOperator(const IntVar* const* vars, + const IntVar* const* secondary_vars, + const RoutingModel::NodePairs& pairs, + int size) + : PathOperator(vars, secondary_vars, size, 2), + inactive_pair_(0), + pairs_(pairs) {} + virtual ~MakePairActiveOperator() {} + virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta); + virtual bool MakeNeighbor(); + + private: + virtual void OnNodeInitialization(); + + int inactive_pair_; + RoutingModel::NodePairs pairs_; +}; + +void MakePairActiveOperator::OnNodeInitialization() { + for (int i = 0; i < pairs_.size(); ++i) { + if (IsInactive(pairs_[i].first) && IsInactive(pairs_[i].second)) { + inactive_pair_ = i; + return; + } + } + inactive_pair_ = pairs_.size(); +} + +bool MakePairActiveOperator::MakeNextNeighbor(Assignment* delta, + Assignment* deltadelta) { + while (inactive_pair_ < pairs_.size()) { + if (!IsInactive(pairs_[inactive_pair_].first) + || !IsInactive(pairs_[inactive_pair_].second) + || !PathOperator::MakeNextNeighbor(delta, deltadelta)) { + ResetPosition(); + ++inactive_pair_; + } else { + return true; + } + } + return false; +} + +bool MakePairActiveOperator::MakeNeighbor() { + if (StartNode(0) != StartNode(1)) { + return false; + } + // Inserting the second node of the pair before the first one which ensures + // that the only solutions where both nodes are next to each other have the + // first node before the second (the move is not symmetric and doing it this + // way ensures that a potential precedence constraint between the nodes of the + // pair is not violated). + return MakeActive(pairs_[inactive_pair_].second, BaseNode(1)) + && MakeActive(pairs_[inactive_pair_].first, BaseNode(0)); +} + +LocalSearchOperator* MakePairActive(Solver* const solver, + const IntVar* const* vars, + const IntVar* const* secondary_vars, + const RoutingModel::NodePairs& pairs, + int size) { + return solver->RevAlloc(new MakePairActiveOperator(vars, + secondary_vars, + pairs, + size)); +} + +// Operator which moves a pair of nodes to another position. +// Possible neighbors for the path 1 -> A -> B -> 2 -> 3 (where (1, 3) are +// first and last nodes of the path and can therefore not be moved, and (A, B) +// is a pair of nodes): +// 1 -> [A] -> 2 -> [B] -> 3 +// 1 -> 2 -> [A] -> [B] -> 3 +// 1 -> [B] -> [A] -> 2 -> 3 +// 1 -> [B] -> 2 -> [A] -> 3 +// 1 -> 2 -> [B] -> [A] -> 3 +class MakePairRelocateOperator : public PathOperator { + public: + MakePairRelocateOperator(const IntVar* const* vars, + const IntVar* const* secondary_vars, + const RoutingModel::NodePairs& pairs, + int size) + : PathOperator(vars, secondary_vars, size, 3) { + int64 index_max = 0; + for (int i = 0; i < size; ++i) { + index_max = std::max(index_max, vars[i]->Max()); + } + prevs_.resize(index_max + 1, -1); + int max_pair_index = -1; + for (int i = 0; i < pairs.size(); ++i) { + max_pair_index = std::max(max_pair_index, pairs[i].first); + max_pair_index = std::max(max_pair_index, pairs[i].second); + } + pairs_.resize(max_pair_index + 1, -1); + for (int i = 0; i < pairs.size(); ++i) { + pairs_[pairs[i].first] = pairs[i].second; + pairs_[pairs[i].second] = pairs[i].first; + } + } + virtual ~MakePairRelocateOperator() {} + virtual bool MakeNeighbor(); + + private: + virtual void OnNodeInitialization(); + + std::vector pairs_; + std::vector prevs_; +}; + +bool MakePairRelocateOperator::MakeNeighbor() { + if (StartNode(1) != StartNode(2)) { + return false; + } + const int64 prev = prevs_[BaseNode(0)]; + if (prev < 0) { + return false; + } + const int sibling = BaseNode(0) < pairs_.size() ? pairs_[BaseNode(0)] : -1; + if (sibling < 0) { + return false; + } + const int64 prev_sibling = prevs_[sibling]; + if (prev_sibling < 0) { + return false; + } + return MoveChain(prev_sibling, sibling, BaseNode(1)) + && MoveChain(prev, BaseNode(0), BaseNode(2)); +} + +void MakePairRelocateOperator::OnNodeInitialization() { + for (int i = 0; i < Size(); ++i) { + prevs_[Next(i)] = i; + } +} + +LocalSearchOperator* MakePairRelocate(Solver* const solver, + const IntVar* const* vars, + const IntVar* const* secondary_vars, + const RoutingModel::NodePairs& pairs, + int size) { + return solver->RevAlloc(new MakePairRelocateOperator(vars, + secondary_vars, + pairs, + size)); +} + // Cached callbacks class RoutingCache { @@ -842,6 +1013,27 @@ void RoutingModel::CloseModel() { SetUpSearch(); } +// Decision builder to build a solution with all nodes inactive. It does no +// branching and may fail if some nodes cannot be made inactive. + +class AllUnperformed : public DecisionBuilder { + public: + // Does not take ownership of model. + explicit AllUnperformed(RoutingModel* const model) : model_(model) {} + virtual ~AllUnperformed() {} + virtual Decision* Next(Solver* const solver) { + for (int i = 0; i < model_->Size(); ++i) { + if (!model_->IsStart(i)) { + model_->ActiveVar(i)->SetValue(0); + } + } + return NULL; + } + + private: + RoutingModel* const model_; +}; + // Flags override strategy selection RoutingModel::RoutingStrategy RoutingModel::GetSelectedFirstSolutionStrategy() const { @@ -851,6 +1043,8 @@ RoutingModel::GetSelectedFirstSolutionStrategy() const { return ROUTING_LOCAL_CHEAPEST_ARC; } else if (FLAGS_routing_first_solution.compare("PathCheapestArc") == 0) { return ROUTING_PATH_CHEAPEST_ARC; + } else if (FLAGS_routing_first_solution.compare("AllUnperformed") == 0) { + return ROUTING_ALL_UNPERFORMED; } return first_solution_strategy_; } @@ -1628,6 +1822,20 @@ void RoutingModel::SetUpSearch() { kint64max); std::vector operators = extra_operators_; + if (pickup_delivery_pairs_.size() > 0) { + const IntVar* const* vehicle_vars = + homogeneous_costs_ ? NULL : vehicle_vars_.get(); + operators.push_back(MakePairActive(solver_.get(), + nexts_.get(), + vehicle_vars, + pickup_delivery_pairs_, + size)); + operators.push_back(MakePairRelocate(solver_.get(), + nexts_.get(), + vehicle_vars, + pickup_delivery_pairs_, + size)); + } if (vehicles_ > 1) { if (!FLAGS_routing_no_relocate) { CP_ROUTING_PUSH_BACK_OPERATOR(Solver::RELOCATE); @@ -1676,6 +1884,7 @@ void RoutingModel::SetUpSearch() { CP_ROUTING_PUSH_BACK_OPERATOR(Solver::UNACTIVELNS); } } + LocalSearchOperator* local_search_operator = solver_->ConcatenateOperators(operators); @@ -1726,6 +1935,10 @@ void RoutingModel::SetUpSearch() { case ROUTING_DEFAULT_STRATEGY: LG << "Using DEFAULT"; break; + case ROUTING_ALL_UNPERFORMED: + first_solution = + solver_->RevAlloc(new AllUnperformed(this)); + break; default: LOG(WARNING) << "Unknown argument for routing_first_solution, " "using default"; @@ -1876,7 +2089,7 @@ IntVar** RoutingModel::GetOrMakeCumuls(int64 capacity, const string& name) { std::vector cumuls; const int size = Size() + vehicles_; solver_->MakeIntVarArray(size, 0LL, capacity, name, &cumuls); - IntVar** cumul_array = solver_->RevAlloc(new IntVar*[size]); + IntVar** cumul_array = solver_->RevAllocArray(new IntVar*[size]); memcpy(cumul_array, cumuls.data(), cumuls.size() * sizeof(*cumuls.data())); cumuls_[name] = cumul_array; return cumul_array; @@ -1899,7 +2112,7 @@ IntVar** RoutingModel::GetOrMakeTransits(NodeEvaluator2* evaluator, if (named_transits == NULL) { std::vector transits; const int size = Size(); - IntVar** transit_array = solver_->RevAlloc(new IntVar*[size]); + IntVar** transit_array = solver_->RevAllocArray(new IntVar*[size]); for (int i = 0; i < size; ++i) { IntVar* fixed_transit = solver_->MakeElement(NewPermanentCallback( @@ -1914,7 +2127,7 @@ IntVar** RoutingModel::GetOrMakeTransits(NodeEvaluator2* evaluator, IntVar* slack_var = solver_->MakeIntVar(0, slack_max, "slack"); transit_array[i] = solver_->MakeSum(slack_var, fixed_transit)->Var(); } - transit_array[i]->SetMin(0); + transit_array[i]->SetMin(-capacity); transit_array[i]->SetMax(capacity); } transits_[name] = transit_array; diff --git a/constraint_solver/routing.h b/constraint_solver/routing.h index d962017b15..9cc07f7c45 100644 --- a/constraint_solver/routing.h +++ b/constraint_solver/routing.h @@ -179,31 +179,61 @@ class RoutingModel { public: // First solution strategies, used as starting point of local search. enum RoutingStrategy { - ROUTING_DEFAULT_STRATEGY, // choose first unbound, assign min value + // 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). + ROUTING_DEFAULT_STRATEGY, + // Iteratively connect two nodes which produce the cheapest route segment. ROUTING_GLOBAL_CHEAPEST_ARC, + // Select the first node with an unbound successor and connect it to the + // node which produces the cheapest route segment. ROUTING_LOCAL_CHEAPEST_ARC, + // Starting from a route "start" node, connect it to the node which produces + // the cheapest route segment, then extend the route by iterating on the + // last node added to the route. ROUTING_PATH_CHEAPEST_ARC, - ROUTING_EVALUATOR_STRATEGY + // Same as ROUTING_PATH_CHEAPEST_ARC, except that arc costs are evaluated + // using the function passed to RoutingModel::SetFirstSolutionEvaluator(). + ROUTING_EVALUATOR_STRATEGY, + // Make all node inactive. Only finds a solution if nodes are optional (are + // element of a disjunction constraint with a finite penalty cost). + ROUTING_ALL_UNPERFORMED }; - // Metaheuristics, to escape local minima. + // Metaheuristics used to guide the search. Apart greedy descent, they will + // try to escape local minima. enum RoutingMetaheuristic { - ROUTING_GREEDY_DESCENT, // default + // Accepts improving (cost-reducing) local search neighbors until a local + // minimum is reached. This is the default heuristic. + ROUTING_GREEDY_DESCENT, + // Uses guided local search to escape local minima + // (cf. http://en.wikipedia.org/wiki/Guided_Local_Search); this is + // generally the most efficient metaheuristic for vehicle routing. ROUTING_GUIDED_LOCAL_SEARCH, + // Uses simulated annealing to escape local minima + // (cf. http://en.wikipedia.org/wiki/Simulated_annealing). ROUTING_SIMULATED_ANNEALING, + // Uses tabu search to escape local minima + // (cf. http://en.wikipedia.org/wiki/Tabu_search). ROUTING_TABU_SEARCH }; // Status of the search. enum Status { + // Problem not solved yet (before calling RoutingModel::Solve()). ROUTING_NOT_SOLVED, + // Problem solved successfully after calling RoutingModel::Solve(). ROUTING_SUCCESS, + // No solution found to the problem after calling RoutingModel::Solve(). ROUTING_FAIL, + // Time limit reached before finding a solution with RoutingModel::Solve(). ROUTING_FAIL_TIMEOUT }; typedef _RoutingModel_NodeIndex NodeIndex; typedef ResultCallback2 NodeEvaluator2; + typedef std::vector > NodePairs; // Constants with an index of the first node (to be used in for loops for // iteration), and a special index to signalize an invalid/unused value. @@ -292,6 +322,23 @@ class RoutingModel { AddDisjunction(nodes, penalty); } #endif // SWIGPYTHON + // Notifies that node1 and node2 form a pair of nodes which should belong + // to the same route. This methods helps the search find better solutions, + // especially in the local search phase. + // It should be called each time you have an equality constraint linking + // the vehicle variables of two node (including for instance pickup and + // delivery problems): + // Solver* const solver = routing.solver(); + // solver->AddConstraint(solver->MakeEquality( + // routing.VehicleVar(routing.NodeToIndex(node1)), + // routing.VehicleVar(routing.NodeToIndex(node2)))); + // solver->AddPickupAndDelivery(node1, node2); + // + // TODO(user): Remove this when model introspection detects linked nodes. + void AddPickupAndDelivery(NodeIndex node1, NodeIndex node2) { + pickup_delivery_pairs_.push_back(std::make_pair(NodeToIndex(node1), + NodeToIndex(node2))); + } // Makes 'depot' the starting node of all routes. void SetDepot(NodeIndex depot); // Sets the cost function of the model such that the cost of a segment of a @@ -619,6 +666,7 @@ class RoutingModel { std::vector routing_caches_; std::vector disjunctions_; hash_map node_to_disjunction_; + NodePairs pickup_delivery_pairs_; IntVar* cost_; std::vector fixed_costs_; int nodes_; diff --git a/constraint_solver/table.cc b/constraint_solver/table.cc index 9790ed8e8b..a302a951cd 100644 --- a/constraint_solver/table.cc +++ b/constraint_solver/table.cc @@ -567,7 +567,7 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { DCHECK_LT(value_index, masks_[var_index].size()); uint64* mask = masks_[var_index][value_index]; if (!mask) { - mask = solver()->RevAlloc(new uint64[length_]); + mask = solver()->RevAllocArray(new uint64[length_]); memset(mask, 0, length_ * sizeof(*mask)); masks_[var_index][value_index] = mask; } diff --git a/constraint_solver/trace.cc b/constraint_solver/trace.cc index dd8bf7f3ab..4e5404275b 100644 --- a/constraint_solver/trace.cc +++ b/constraint_solver/trace.cc @@ -48,7 +48,7 @@ class TraceIntVar : public IntVar { virtual void SetMin(int64 m) { if (m > inner_->Min()) { - LOG(INFO) << "SetMin(" << inner_->DebugString() << ", " << m << ")"; + solver()->GetPropagationMonitor()->SetMin(inner_, m); inner_->SetMin(m); } } @@ -57,7 +57,7 @@ class TraceIntVar : public IntVar { virtual void SetMax(int64 m) { if (m < inner_->Max()) { - LOG(INFO) << "SetMax(" << inner_->DebugString() << ", " << m << ")"; + solver()->GetPropagationMonitor()->SetMax(inner_, m); inner_->SetMax(m); } } @@ -68,8 +68,7 @@ class TraceIntVar : public IntVar { virtual void SetRange(int64 l, int64 u) { if (l > inner_->Min() || u < inner_->Max()) { - LOG(INFO) << "SetRange(" << inner_->DebugString() << ", [" - << l << ".." << u << "])"; + solver()->GetPropagationMonitor()->SetRange(inner_, l, u); inner_->SetRange(l, u); } } @@ -88,31 +87,28 @@ class TraceIntVar : public IntVar { virtual void RemoveValue(int64 v) { if (inner_->Contains(v)) { - LOG(INFO) << "RemoveValue(" << inner_->DebugString() << ", " << v << ")"; + solver()->GetPropagationMonitor()->RemoveValue(inner_, v); inner_->RemoveValue(v); } } virtual void SetValue(int64 v) { - LOG(INFO) << "SetValue(" << inner_->DebugString() << ", " << v << ")"; + solver()->GetPropagationMonitor()->SetValue(inner_, v); inner_->SetValue(v); } virtual void RemoveInterval(int64 l, int64 u) { - LOG(INFO) << "RemoveInterval(" << inner_->DebugString() << ", [" - << l << ".." << u << "])"; + solver()->GetPropagationMonitor()->RemoveInterval(inner_, l, u); inner_->RemoveInterval(l, u); } virtual void RemoveValues(const int64* const values, int size) { - LOG(INFO) << "RemoveValues(" << inner_->DebugString() << ", [" - << Int64ArrayToString(values, size, ", ") << "])"; + solver()->GetPropagationMonitor()->RemoveValues(inner_, values, size); inner_->RemoveValues(values, size); } virtual void SetValues(const int64* const values, int size) { - LOG(INFO) << "SetValues(" << inner_->DebugString() << ", [" - << Int64ArrayToString(values, size, ", ") << "])"; + solver()->GetPropagationMonitor()->SetValues(inner_, values, size); inner_->SetValues(values, size); } @@ -184,14 +180,14 @@ class TraceIntExpr : public IntExpr { virtual int64 Min() const { return inner_->Min(); } virtual void SetMin(int64 m) { - LOG(INFO) << "SetMin(" << inner_->DebugString() << ", " << m << ")"; + solver()->GetPropagationMonitor()->SetMin(inner_, m); inner_->SetMin(m); } virtual int64 Max() const { return inner_->Max(); } virtual void SetMax(int64 m) { - LOG(INFO) << "SetMax(" << inner_->DebugString() << ", " << m << ")"; + solver()->GetPropagationMonitor()->SetMax(inner_, m); inner_->SetMax(m); } @@ -201,8 +197,7 @@ class TraceIntExpr : public IntExpr { virtual void SetRange(int64 l, int64 u) { if (l > inner_->Min() || u < inner_->Max()) { - LOG(INFO) << "SetRange(" << inner_->DebugString() << ", [" - << l << ".." << u << "])"; + solver()->GetPropagationMonitor()->SetRange(inner_, l, u); inner_->SetRange(l, u); } } @@ -238,168 +233,160 @@ class TraceIntExpr : public IntExpr { class TraceIntervalVar : public IntervalVar { public: - TraceIntervalVar(Solver* const solver, IntervalVar* const interval) - : IntervalVar(solver, ""), interval_(interval) { - if (interval->HasName()) { - set_name(interval->name()); + TraceIntervalVar(Solver* const solver, IntervalVar* const inner) + : IntervalVar(solver, ""), inner_(inner) { + if (inner->HasName()) { + set_name(inner->name()); } } virtual ~TraceIntervalVar() {} virtual int64 StartMin() const { - return interval_->StartMin(); + return inner_->StartMin(); } virtual int64 StartMax() const { - return interval_->StartMax(); + return inner_->StartMax(); } virtual void SetStartMin(int64 m) { - if (m > interval_->StartMin()) { - LOG(INFO) << "SetStartMin(" << interval_->DebugString() << ", " - << m << ")"; - interval_->SetStartMin(m); + if (m > inner_->StartMin()) { + solver()->GetPropagationMonitor()->SetStartMin(inner_, m); + inner_->SetStartMin(m); } } virtual void SetStartMax(int64 m) { - if (m < interval_->StartMax()) { - LOG(INFO) << "SetStartMax(" << interval_->DebugString() << ", " - << m << ")"; - interval_->SetStartMax(m); + if (m < inner_->StartMax()) { + solver()->GetPropagationMonitor()->SetStartMax(inner_, m); + inner_->SetStartMax(m); } } virtual void SetStartRange(int64 mi, int64 ma) { - if (mi > interval_->StartMin() || ma < interval_->StartMax()) { - LOG(INFO) << "SetStartRange(" << interval_->DebugString() << ", [" - << mi << ".." << ma << "])"; - interval_->SetStartRange(mi, ma); + if (mi > inner_->StartMin() || ma < inner_->StartMax()) { + solver()->GetPropagationMonitor()->SetStartRange(inner_, mi, ma); + inner_->SetStartRange(mi, ma); } } virtual void WhenStartRange(Demon* const d) { - interval_->WhenStartRange(d); + inner_->WhenStartRange(d); } virtual void WhenStartBound(Demon* const d) { - interval_->WhenStartBound(d); + inner_->WhenStartBound(d); } virtual int64 EndMin() const { - return interval_->EndMin(); + return inner_->EndMin(); } virtual int64 EndMax() const { - return interval_->EndMax(); + return inner_->EndMax(); } virtual void SetEndMin(int64 m) { - if (m > interval_->EndMin()) { - LOG(INFO) << "SetEndMin(" << interval_->DebugString() << ", " << m << ")"; - interval_->SetEndMin(m); + if (m > inner_->EndMin()) { + solver()->GetPropagationMonitor()->SetEndMin(inner_, m); + inner_->SetEndMin(m); } } virtual void SetEndMax(int64 m) { - if (m < interval_->EndMax()) { - LOG(INFO) << "SetEndMax(" << interval_->DebugString() << ", " << m << ")"; - interval_->SetEndMax(m); + if (m < inner_->EndMax()) { + solver()->GetPropagationMonitor()->SetEndMax(inner_, m); + inner_->SetEndMax(m); } } virtual void SetEndRange(int64 mi, int64 ma) { - if (mi > interval_->EndMin() || ma < interval_->EndMax()) { - LOG(INFO) << "SetEndRange(" << interval_->DebugString() << ", [" - << mi << ".." << ma << "])"; - interval_->SetEndRange(mi, ma); + if (mi > inner_->EndMin() || ma < inner_->EndMax()) { + solver()->GetPropagationMonitor()->SetEndRange(inner_, mi, ma); + inner_->SetEndRange(mi, ma); } } virtual void WhenEndRange(Demon* const d) { - interval_->WhenEndRange(d); + inner_->WhenEndRange(d); } virtual void WhenEndBound(Demon* const d) { - interval_->WhenStartBound(d); + inner_->WhenStartBound(d); } virtual int64 DurationMin() const { - return interval_->DurationMin(); + return inner_->DurationMin(); } virtual int64 DurationMax() const { - return interval_->DurationMax(); + return inner_->DurationMax(); } virtual void SetDurationMin(int64 m) { - if (m > interval_->DurationMin()) { - LOG(INFO) << "SetDurationMin(" << interval_->DebugString() << ", " - << m << ")"; - interval_->SetDurationMin(m); + if (m > inner_->DurationMin()) { + solver()->GetPropagationMonitor()->SetDurationMin(inner_, m); + inner_->SetDurationMin(m); } } virtual void SetDurationMax(int64 m) { - if (m < interval_->DurationMax()) { - LOG(INFO) << "SetDurationMax(" << interval_->DebugString() << ", " - << m << ")"; - interval_->SetDurationMax(m); + if (m < inner_->DurationMax()) { + solver()->GetPropagationMonitor()->SetDurationMax(inner_, m); + inner_->SetDurationMax(m); } } virtual void SetDurationRange(int64 mi, int64 ma) { - if (mi > interval_->DurationMin() || ma < interval_->DurationMax()) { - LOG(INFO) << "SetDurationRange(" << interval_->DebugString() << ", [" - << mi << ".." << ma << "])"; - interval_->SetDurationRange(mi, ma); + if (mi > inner_->DurationMin() || ma < inner_->DurationMax()) { + solver()->GetPropagationMonitor()->SetDurationRange(inner_, mi, ma); + inner_->SetDurationRange(mi, ma); } } virtual void WhenDurationRange(Demon* const d) { - interval_->WhenDurationRange(d); + inner_->WhenDurationRange(d); } virtual void WhenDurationBound(Demon* const d) { - interval_->WhenDurationBound(d); + inner_->WhenDurationBound(d); } virtual bool MustBePerformed() const { - return interval_->MustBePerformed(); + return inner_->MustBePerformed(); } virtual bool MayBePerformed() const { - return interval_->MayBePerformed(); + return inner_->MayBePerformed(); } - virtual void SetPerformed(bool val) { - if ((val && !interval_->MustBePerformed()) || - (!val && interval_->MayBePerformed())) { - LOG(INFO) << "SetPerformed(" << interval_->DebugString() << ", " - << val << ")"; - interval_->SetPerformed(val); + virtual void SetPerformed(bool value) { + if ((value && !inner_->MustBePerformed()) || + (!value && inner_->MayBePerformed())) { + solver()->GetPropagationMonitor()->SetPerformed(inner_, value); + inner_->SetPerformed(value); } } virtual void WhenPerformedBound(Demon* const d) { - interval_->WhenPerformedBound(d); + inner_->WhenPerformedBound(d); } virtual void Accept(ModelVisitor* const visitor) const { - interval_->Accept(visitor); + inner_->Accept(visitor); } private: - IntervalVar* const interval_; + IntervalVar* const inner_; }; // ---------- Trace ---------- class Trace : public PropagationMonitor { public: - virtual void StartInitialPropagation() { + virtual void BeginInitialPropagation() { for (int i = 0; i < monitors_.size(); ++i) { - monitors_[i]->StartInitialPropagation(); + monitors_[i]->BeginInitialPropagation(); } } @@ -409,10 +396,10 @@ class Trace : public PropagationMonitor { } } - virtual void StartConstraintInitialPropagation( + virtual void BeginConstraintInitialPropagation( const Constraint* const constraint) { for (int i = 0; i < monitors_.size(); ++i) { - monitors_[i]->StartConstraintInitialPropagation(constraint); + monitors_[i]->BeginConstraintInitialPropagation(constraint); } } @@ -423,11 +410,11 @@ class Trace : public PropagationMonitor { } } - virtual void StartNestedConstraintInitialPropagation( + virtual void BeginNestedConstraintInitialPropagation( const Constraint* const parent, const Constraint* const nested) { for (int i = 0; i < monitors_.size(); ++i) { - monitors_[i]->StartNestedConstraintInitialPropagation(parent, nested); + monitors_[i]->BeginNestedConstraintInitialPropagation(parent, nested); } } @@ -445,9 +432,9 @@ class Trace : public PropagationMonitor { } } - virtual void StartDemonRun(const Demon* const demon) { + virtual void BeginDemonRun(const Demon* const demon) { for (int i = 0; i < monitors_.size(); ++i) { - monitors_[i]->StartDemonRun(demon); + monitors_[i]->BeginDemonRun(demon); } } @@ -463,6 +450,30 @@ class Trace : public PropagationMonitor { } } + virtual void FindSolution() { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->FindSolution(); + } + } + + virtual void ApplyDecision(Decision* const decision) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->ApplyDecision(decision); + } + } + + virtual void RefuteDecision(Decision* const decision) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->RefuteDecision(decision); + } + } + + virtual void AfterDecision(Decision* const decision) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->AfterDecision(decision); + } + } + virtual void EnterSearch() { for (int i = 0; i < monitors_.size(); ++i) { monitors_[i]->EnterSearch(); @@ -481,6 +492,145 @@ class Trace : public PropagationMonitor { } } + // IntExpr modifiers. + virtual void SetMin(IntExpr* const expr, int64 new_min) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetMin(expr, new_min); + } + } + + virtual void SetMax(IntExpr* const expr, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetMax(expr, new_max); + } + } + + virtual void SetRange(IntExpr* const expr, int64 new_min, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetRange(expr, new_min, new_max); + } + } + + // IntVar modifiers. + virtual void SetMin(IntVar* const var, int64 new_min) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetMin(var, new_min); + } + } + + virtual void SetMax(IntVar* const var, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetMax(var, new_max); + } + } + + virtual void SetRange(IntVar* const var, int64 new_min, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetRange(var, new_min, new_max); + } + } + + virtual void RemoveValue(IntVar* const var, int64 value) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->RemoveValue(var, value); + } + } + + virtual void SetValue(IntVar* const var, int64 value) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetValue(var, value); + } + } + + virtual void RemoveInterval(IntVar* const var, int64 imin, int64 imax) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->RemoveInterval(var, imin, imax); + } + } + + virtual void SetValues(IntVar* const var, + const int64* const values, + int size) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetValues(var, values, size); + } + } + + virtual void RemoveValues(IntVar* const var, + const int64* const values, + int size) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->RemoveValues(var, values, size); + } + } + + // IntervalVar modifiers. + virtual void SetStartMin(IntervalVar* const var, int64 new_min) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetStartMin(var, new_min); + } + } + + virtual void SetStartMax(IntervalVar* const var, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetStartMax(var, new_max); + } + } + + virtual void SetStartRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetStartRange(var, new_min, new_max); + } + } + + virtual void SetEndMin(IntervalVar* const var, int64 new_min) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetEndMin(var, new_min); + } + } + + virtual void SetEndMax(IntervalVar* const var, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetEndMax(var, new_max); + } + } + + virtual void SetEndRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetEndRange(var, new_min, new_max); + } + } + + virtual void SetDurationMin(IntervalVar* const var, int64 new_min) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetDurationMin(var, new_min); + } + } + + virtual void SetDurationMax(IntervalVar* const var, int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetDurationMax(var, new_max); + } + } + + virtual void SetDurationRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetDurationRange(var, new_min, new_max); + } + } + + virtual void SetPerformed(IntervalVar* const var, bool value) { + for (int i = 0; i < monitors_.size(); ++i) { + monitors_[i]->SetPerformed(var, value); + } + } + void Add(PropagationMonitor* const monitor) { if (monitor != NULL) { monitors_.push_back(monitor); @@ -490,6 +640,303 @@ class Trace : public PropagationMonitor { private: std::vector monitors_; }; + +// ---------- PrintTrace ---------- + +class PrintTrace : public PropagationMonitor { + public: + PrintTrace() : indent_(0) {} + virtual ~PrintTrace() {} + + // Propagation events. + virtual void BeginInitialPropagation() { + CheckNoDelayed(); + Display("Initial Propagation {"); + IncreaseIndent(); + } + virtual void EndInitialPropagation() { + DecreaseIndent(); + Display("} Starting Search"); + } + + virtual void BeginConstraintInitialPropagation( + const Constraint* const constraint) { + DelayPrintAndIndent(StringPrintf("InitialPropagate(%s)", + constraint->DebugString().c_str())); + } + + virtual void EndConstraintInitialPropagation( + const Constraint* const constraint) { + DelayCloseAndUnindent(); + } + + virtual void BeginNestedConstraintInitialPropagation( + const Constraint* const parent, + const Constraint* const nested) { + DelayPrintAndIndent(StringPrintf("InitialPropagate(%s)", + nested->DebugString().c_str())); + } + virtual void EndNestedConstraintInitialPropagation( + const Constraint* const parent, + const Constraint* const nested) { + DelayCloseAndUnindent(); + } + + virtual void RegisterDemon(const Demon* const demon) {} + + virtual void BeginDemonRun(const Demon* const demon) { + in_demon_ = true; + DelayPrintAndIndent(StringPrintf("Run(%s)", demon->DebugString().c_str())); + } + + virtual void EndDemonRun(const Demon* const demon) { + in_demon_ = false; + DelayCloseAndUnindent(); + } + + virtual void RaiseFailure() { + in_demon_ = false; + const bool top_level = indent_ == 0; + DelayCloseAndUnindent(); + ClearIndent(); + if (top_level) { + Display(" -------------------- Failure --------------------"); + } else { + Display("} -------------------- Failure --------------------"); + } + } + + virtual void FindSolution() { + Display("++++++++++++++++++++ Solution ++++++++++++++++++++"); + } + + virtual void ApplyDecision(Decision* const decision) { + Display(StringPrintf("----- Apply(%s) {", decision->DebugString().c_str())); + IncreaseIndent(); + } + + virtual void RefuteDecision(Decision* const decision) { + Display(StringPrintf("----- Refute(%s) {", + decision->DebugString().c_str())); + IncreaseIndent(); + } + + virtual void AfterDecision(Decision* const decision) { + DecreaseIndent(); + Display("}"); + } + + virtual void EnterSearch() { + ClearIndent(); + } + + virtual void ExitSearch() { + DCHECK_EQ(0, indent_); + } + + virtual void RestartSearch() { + DCHECK_EQ(0, indent_); + } + + // IntExpr modifiers. + virtual void SetMin(IntExpr* const expr, int64 new_min) { + DisplayModification(StringPrintf("SetMin(%s, %lld)", + expr->DebugString().c_str(), + new_min)); + } + + virtual void SetMax(IntExpr* const expr, int64 new_max) { + DisplayModification(StringPrintf("SetMax(%s, %lld)", + expr->DebugString().c_str(), + new_max)); + } + + virtual void SetRange(IntExpr* const expr, int64 new_min, int64 new_max) { + DisplayModification(StringPrintf("SetRange(%s, [%lld .. %lld])", + expr->DebugString().c_str(), + new_min, + new_max)); + } + + // IntVar modifiers. + virtual void SetMin(IntVar* const var, int64 new_min) { + DisplayModification(StringPrintf("SetMin(%s, %lld)", + var->DebugString().c_str(), + new_min)); + } + + virtual void SetMax(IntVar* const var, int64 new_max) { + DisplayModification(StringPrintf("SetMax(%s, %lld)", + var->DebugString().c_str(), + new_max)); + } + + virtual void SetRange(IntVar* const var, int64 new_min, int64 new_max) { + DisplayModification(StringPrintf("SetRange(%s, [%lld .. %lld])", + var->DebugString().c_str(), + new_min, + new_max)); + } + + virtual void RemoveValue(IntVar* const var, int64 value) { + DisplayModification(StringPrintf("RemoveValue(%s, %lld)", + var->DebugString().c_str(), + value)); + } + + virtual void SetValue(IntVar* const var, int64 value) { + DisplayModification(StringPrintf("SetValue(%s, %lld)", + var->DebugString().c_str(), + value)); + } + + virtual void RemoveInterval(IntVar* const var, int64 imin, int64 imax) { + DisplayModification(StringPrintf("RemoveInterval(%s, [%lld .. %lld])", + var->DebugString().c_str(), + imin, + imax)); + } + + virtual void SetValues(IntVar* const var, + const int64* const values, + int size) {} + + virtual void RemoveValues(IntVar* const var, + const int64* const values, + int size) {} + // IntervalVar modifiers. + virtual void SetStartMin(IntervalVar* const var, int64 new_min) { + DisplayModification(StringPrintf("SetStartMin(%s, %lld)", + var->DebugString().c_str(), + new_min)); + } + + virtual void SetStartMax(IntervalVar* const var, int64 new_max) { + DisplayModification(StringPrintf("SetStartMax(%s, %lld)", + var->DebugString().c_str(), + new_max)); + } + + virtual void SetStartRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + DisplayModification(StringPrintf("SetStartRange(%s, [%lld .. %lld])", + var->DebugString().c_str(), + new_min, + new_max)); + } + + virtual void SetEndMin(IntervalVar* const var, int64 new_min) { + DisplayModification(StringPrintf("SetEndMin(%s, %lld)", + var->DebugString().c_str(), + new_min)); + } + + virtual void SetEndMax(IntervalVar* const var, int64 new_max) { + DisplayModification(StringPrintf("SetEndMax(%s, %lld)", + var->DebugString().c_str(), + new_max)); + } + + virtual void SetEndRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + DisplayModification(StringPrintf("SetEndRange(%s, [%lld .. %lld])", + var->DebugString().c_str(), + new_min, + new_max)); + } + + virtual void SetDurationMin(IntervalVar* const var, int64 new_min) { + DisplayModification(StringPrintf("SetDurationMin(%s, %lld)", + var->DebugString().c_str(), + new_min)); + } + + virtual void SetDurationMax(IntervalVar* const var, int64 new_max) { + DisplayModification(StringPrintf("SetDurationMax(%s, %lld)", + var->DebugString().c_str(), + new_max)); + } + + virtual void SetDurationRange(IntervalVar* const var, + int64 new_min, + int64 new_max) { + DisplayModification(StringPrintf("SetDurationRange(%s, [%lld .. %lld])", + var->DebugString().c_str(), + new_min, + new_max)); + } + + virtual void SetPerformed(IntervalVar* const var, bool value) { + DisplayModification(StringPrintf("SetPerformed(%s, %d)", + var->DebugString().c_str(), + value)); + } + + private: + void DelayPrintAndIndent(const string& delayed) { + CHECK(delayed_string_.empty()); + delayed_string_ = delayed; + } + + void DelayCloseAndUnindent() { + if (delayed_string_.empty() && indent_ > 0) { + DecreaseIndent(); + Display("}"); + } else { + delayed_string_ = ""; + } + } + + void CheckNoDelayed() { + CHECK(delayed_string_.empty()); + } + + void DisplayModification(const string& to_print) { + if (!delayed_string_.empty()) { + LOG(INFO) << Indent() << delayed_string_ << " {"; + IncreaseIndent(); + delayed_string_ = ""; + } + if (in_demon_) { // Inside a demon, normal print. + LOG(INFO) << Indent() << to_print; + } else if (indent_ == 0) { // Top level, modification pushed by the + // objective. + LOG(INFO) << Indent() << "Objective: " << to_print; + } else { // Not top level, but not in a demon -> Decision. + LOG(INFO) << Indent() << "Decision: " << to_print; + } + } + + void Display(const string& to_print) { + LOG(INFO) << Indent() << to_print; + } + + string Indent() { + string output = " @ "; + for (int i = 0; i < indent_; ++i) { + output.append(" "); + } + return output; + } + + void IncreaseIndent() { + indent_++; + } + + void DecreaseIndent() { + indent_--; + } + + void ClearIndent() { + indent_ = 0; + } + + int indent_; + string delayed_string_; + bool in_demon_; +}; } // namespace IntExpr* Solver::RegisterIntExpr(IntExpr* const expr) { @@ -525,12 +972,16 @@ PropagationMonitor* BuildTrace() { return new Trace(); } -PropagationMonitor* Solver::Trace() const { - return trace_.get(); -} - void Solver::AddPropagationMonitor(PropagationMonitor* const monitor) { // TODO(user): Check solver state? - reinterpret_cast(trace_.get())->Add(monitor); + reinterpret_cast(propagation_monitor_.get())->Add(monitor); +} + +PropagationMonitor* Solver::GetPropagationMonitor() const { + return propagation_monitor_.get(); +} + +PropagationMonitor* BuildPrintTrace() { + return new PrintTrace(); } } // namespace operations_research