diff --git a/constraint_solver/table.cc b/constraint_solver/table.cc index 4007f5b7e2..291909336c 100644 --- a/constraint_solver/table.cc +++ b/constraint_solver/table.cc @@ -26,6 +26,7 @@ DEFINE_bool(cp_use_compact_table, true, "Use compact table constraint when possible."); namespace operations_research { +namespace { // ----- Positive Table Constraint ----- @@ -43,8 +44,6 @@ namespace operations_research { // - Or scan removed tuples and mark variables for reexam // - Do a bitset intersection between modified parts of the bitset // and variable-value masks. - - class BasePositiveTableConstraint : public Constraint { public: BasePositiveTableConstraint(Solver* const s, @@ -55,10 +54,7 @@ class BasePositiveTableConstraint : public Constraint { : Constraint(s), tuple_count_(tuple_count), arity_(arity), - length_(BitLength64(tuple_count)), vars_(new IntVar*[arity]), - actives_(new uint64[length_]), - stamps_(new uint64[length_]), holes_(new IntVarIterator*[arity]), iterators_(new IntVarIterator*[arity]) { // Copy vars. @@ -76,10 +72,7 @@ class BasePositiveTableConstraint : public Constraint { : Constraint(s), tuple_count_(tuples.size()), arity_(vars.size()), - length_(BitLength64(tuple_count_)), vars_(new IntVar*[arity_]), - actives_(new uint64[length_]), - stamps_(new uint64[length_]), holes_(new IntVarIterator*[arity_]), iterators_(new IntVarIterator*[arity_]) { // Copy vars. @@ -95,10 +88,7 @@ class BasePositiveTableConstraint : public Constraint { protected: const int tuple_count_; const int arity_; - const int length_; scoped_array vars_; - scoped_array actives_; - scoped_array stamps_; scoped_array holes_; scoped_array iterators_; vector to_remove_; @@ -111,7 +101,10 @@ class PositiveTableConstraint : public BasePositiveTableConstraint { const int64* const * tuples, int tuple_count, int arity) - : BasePositiveTableConstraint(s, vars, tuples, tuple_count, arity) { + : BasePositiveTableConstraint(s, vars, tuples, tuple_count, arity), + length_(BitLength64(tuple_count)), + actives_(new uint64[length_]), + stamps_(new uint64[length_]) { masks_.clear(); masks_.resize(arity_); for (int i = 0; i < tuple_count_; ++i) { @@ -122,7 +115,10 @@ class PositiveTableConstraint : public BasePositiveTableConstraint { PositiveTableConstraint(Solver* const s, const vector & vars, const vector >& tuples) - : BasePositiveTableConstraint(s, vars, tuples) { + : BasePositiveTableConstraint(s, vars, tuples), + length_(BitLength64(tuples.size())), + actives_(new uint64[length_]), + stamps_(new uint64[length_]) { masks_.clear(); masks_.resize(arity_); for (int i = 0; i < tuple_count_; ++i) { @@ -292,6 +288,9 @@ class PositiveTableConstraint : public BasePositiveTableConstraint { } } + const int length_; + scoped_array actives_; + scoped_array stamps_; vector > masks_; }; @@ -305,6 +304,9 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { int tuple_count, int arity) : BasePositiveTableConstraint(s, vars, tuples, tuple_count, arity), + length_(BitLength64(tuple_count)), + actives_(new uint64[length_]), + stamps_(new uint64[length_]), tuples_(new int64*[tuple_count_]), original_min_(new int64[arity_]), temp_mask_(new uint64[length_]), @@ -320,6 +322,9 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { const vector & vars, const vector >& tuples) : BasePositiveTableConstraint(s, vars, tuples), + length_(BitLength64(tuples.size())), + actives_(new uint64[length_]), + stamps_(new uint64[length_]), tuples_(new int64*[tuple_count_]), original_min_(new int64[arity_]), temp_mask_(new uint64[length_]), @@ -391,7 +396,7 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { const int64 span = vars_[i]->Max() - original_min_[i] + 1; masks_[i].resize(span); for (int j = 0; j < span; ++j) { - masks_[i][j] = GG_ULONGLONG(0); + masks_[i][j] = NULL; } } for (int tuple_index = 0; tuple_index < tuple_count_; ++tuple_index) { @@ -601,6 +606,9 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { virtual string DebugString() const { return "PositiveTableConstraint"; } private: + const int length_; + scoped_array actives_; + scoped_array stamps_; scoped_array tuples_; vector > masks_; scoped_array original_min_; @@ -611,8 +619,224 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { Demon* demon_; }; -namespace { -bool HasCompactDomains(const IntVar* const * vars, int arity) { +// ----- Small Compact Table. ----- + +class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint { + public: + SmallCompactPositiveTableConstraint(Solver* const s, + const IntVar* const * vars, + const int64* const * tuples, + int tuple_count, + int arity) + : BasePositiveTableConstraint(s, vars, tuples, tuple_count, arity), + actives_(0), + stamp_(0), + tuples_(new int64*[tuple_count_]), + original_min_(new int64[arity_]), + demon_(NULL) { + // Copy tuples + for (int i = 0; i < tuple_count_; ++i) { + tuples_[i] = new int64[arity_]; + memcpy(tuples_[i], tuples[i], arity_ * sizeof(*tuples[i])); + } + } + + SmallCompactPositiveTableConstraint(Solver* const s, + const vector & vars, + const vector >& tuples) + : BasePositiveTableConstraint(s, vars, tuples), + actives_(0), + stamp_(0), + tuples_(new int64*[tuple_count_]), + original_min_(new int64[arity_]), + demon_(NULL) { + CHECK_LT(tuples.size(), sizeof(uint64)); + // Copy tuples + for (int i = 0; i < tuple_count_; ++i) { + CHECK_EQ(arity_, tuples[i].size()); + tuples_[i] = new int64[arity_]; + memcpy(tuples_[i], tuples[i].data(), arity_ * sizeof(tuples[i][0])); + } + } + + virtual ~SmallCompactPositiveTableConstraint() { + for (int i = 0; i < tuple_count_; ++i) { + delete[] tuples_[i]; + tuples_[i] = NULL; + } + for (int i = 0; i < arity_; ++i) { + delete [] masks_[i]; + masks_[i] = NULL; + } + } + + virtual void Post() { + demon_ = MakeDelayedConstraintDemon0( + solver(), + this, + &SmallCompactPositiveTableConstraint::Propagate, + "Propagate"); + DCHECK_GE(stamp, 1); + for (int i = 0; i < arity_; ++i) { + if (!vars_[i]->Bound()) { + Demon* const u = MakeConstraintDemon1( + solver(), + this, + &SmallCompactPositiveTableConstraint::Update, + "Update", + i); + vars_[i]->WhenDomain(u); + } + } + stamp_ = solver()->stamp() - 1; + actives_ = 0; + } + + virtual void InitialPropagate() { + // Build masks. + masks_.clear(); + masks_.resize(arity_); + for (int i = 0; i < arity_; ++i) { + original_min_[i] = vars_[i]->Min(); + const int64 span = vars_[i]->Max() - original_min_[i] + 1; + masks_[i] = new uint64[span]; + memset(masks_[i], 0, span * sizeof(masks_[i][0])); + } + + // Compute actives_ and update masks. + for (int tuple_index = 0; tuple_index < tuple_count_; ++tuple_index) { + bool ok = true; + for (int var_index = 0; var_index < arity_; ++var_index) { + const int64 value = tuples_[tuple_index][var_index]; + if (!vars_[var_index]->Contains(value)) { + ok = false; + break; + } + } + if (ok) { + actives_ |= OneBit64(tuple_index); + for (int var_index = 0; var_index < arity_; ++var_index) { + const int64 value_index = + tuples_[tuple_index][var_index] - original_min_[var_index]; + masks_[var_index][value_index] |= OneBit64(tuple_index); + } + } + } + if (!actives_) { + solver()->Fail(); + } + + // remove unreached values. + for (int var_index = 0; var_index < arity_; ++var_index) { + IntVar* const var = vars_[var_index]; + const int64 original_min = original_min_[var_index]; + to_remove_.clear(); + IntVarIterator* const it = iterators_[var_index]; + for (it->Init(); it->Ok(); it->Next()) { + const int64 value = it->Value(); + if (!masks_[var_index][value - original_min]) { + to_remove_.push_back(value); + } + } + if (to_remove_.size() > 0) { + var->RemoveValues(to_remove_); + } + } + } + + void SaveActives() { + const uint64 current_stamp = solver()->stamp(); + if (stamp_ < current_stamp) { + stamp_ = current_stamp; + solver()->SaveValue(&actives_); + } + } + + void Propagate() { + // This methods scans all values of all variables to see if they + // are still supported. + // This method is not attached to any particular variable, but is pushed + // at a delayed priority and awakened by Update(var_index). + + // We cache actives_. + const uint64 actives = actives_; + + // We scan all variables and check their domains. + for (int var_index = 0; var_index < arity_; ++var_index) { + const uint64* const var_mask = masks_[var_index]; + const int64 original_min = original_min_[var_index]; + IntVar* const var = vars_[var_index]; + if (var->Bound()) { + if ((var_mask[var->Min() - original_min] & actives) == 0) { + solver()->Fail(); + } + } else { + to_remove_.clear(); + IntVarIterator* const it = iterators_[var_index]; + for (it->Init(); it->Ok(); it->Next()) { + const int64 value = it->Value(); + if ((var_mask[value - original_min] & actives) == 0) { + to_remove_.push_back(value); + } + } + if (to_remove_.size() == var->Size()) { + solver()->Fail(); + } else if (to_remove_.size() > 0) { + vars_[var_index]->RemoveValues(to_remove_); + } + } + } + } + + void Update(int var_index) { + // This method will update the set of active tuples by masking out all + // tuples attached to values of the variables that have been removed. + + // We first collect the complete set of tuples to blank out in temp_mask. + IntVar* const var = vars_[var_index]; + const int64 original_min = original_min_[var_index]; + uint64 temp_mask = 0; + const uint64* const var_mask = masks_[var_index]; + const int64 oldmax = var->OldMax(); + const int64 vmin = var->Min(); + const int64 vmax = var->Max(); + + for (int64 value = var->OldMin(); value < vmin; ++value) { + temp_mask |= var_mask[value - original_min]; + } + IntVarIterator* const hole = holes_[var_index]; + for (hole->Init(); hole->Ok(); hole->Next()) { + temp_mask |= var_mask[hole->Value() - original_min]; + } + for (int64 value = vmax + 1; value <= oldmax; ++value) { + temp_mask |= var_mask[value - original_min]; + } + // Then we apply this mask to actives_. + if (temp_mask & actives_) { + SaveActives(); + actives_ &= ~temp_mask; + if (actives_) { + Enqueue(demon_); + } else { + solver()->Fail(); + } + } + } + + virtual string DebugString() const { + return "SmallCompactPositiveTableConstraint"; + } + private: + uint64 actives_; + uint64 stamp_; + scoped_array tuples_; + vector masks_; + scoped_array original_min_; + Demon* demon_; +}; + + +bool HasSmallCompactDomains(const IntVar* const * vars, int arity) { int64 sum_of_spans = 0LL; int64 sum_of_sizes = 0LL; for (int i = 0; i < arity; ++i) { @@ -629,7 +853,7 @@ Constraint* Solver::MakeAllowedAssignments(const IntVar* const * vars, const int64* const * tuples, int tuple_count, int arity) { - if (FLAGS_cp_use_compact_table && HasCompactDomains(vars, arity)) { + if (FLAGS_cp_use_compact_table && HasSmallCompactDomains(vars, arity)) { return RevAlloc(new CompactPositiveTableConstraint(this, vars, tuples, @@ -646,8 +870,13 @@ Constraint* Solver::MakeAllowedAssignments(const IntVar* const * vars, Constraint* Solver::MakeAllowedAssignments( const vector& vars, const vector >& tuples) { if (FLAGS_cp_use_compact_table - && HasCompactDomains(vars.data(), vars.size())) { - return RevAlloc(new CompactPositiveTableConstraint(this, vars, tuples)); + && HasSmallCompactDomains(vars.data(), vars.size())) { + if (tuples.size() < sizeof(uint64)) { + return RevAlloc( + new SmallCompactPositiveTableConstraint(this, vars, tuples)); + } else { + return RevAlloc(new CompactPositiveTableConstraint(this, vars, tuples)); + } } return RevAlloc(new PositiveTableConstraint(this, vars, tuples)); }