fastest and cleanest version of the table to date
This commit is contained in:
@@ -567,17 +567,28 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
}
|
||||
|
||||
void Propagate() {
|
||||
// Reset touch_var_ if in mode (more than 1 variable was modified).
|
||||
if (touched_var_ == -2) {
|
||||
touched_var_ = -1;
|
||||
}
|
||||
// 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 after Update(var_index) is called.
|
||||
for (int var_index = 0; var_index < arity_; ++var_index) {
|
||||
// This demons runs in low priority. Thus we know all the
|
||||
// variables that have changed since the last time it was run.
|
||||
// In that case, if only one var was touched, as propagation is
|
||||
// exact, we do not need to recheck that variable.
|
||||
if (var_index == touched_var_) {
|
||||
touched_var_ = -1; // Clean now, it is a 1 time flag.
|
||||
continue;
|
||||
}
|
||||
IntVar* const var = vars_[var_index];
|
||||
const int64 original_min = original_min_[var_index];
|
||||
const int64 var_size = var->Size();
|
||||
// The domain iterator is very slow, let's try to see if we can
|
||||
// work our way around.
|
||||
switch (var_size) {
|
||||
case 1: {
|
||||
if (!Supported(var_index, var->Min() - original_min)) {
|
||||
@@ -607,6 +618,9 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
const int64 var_max = var->Max();
|
||||
int64 new_min = var_min;
|
||||
int64 new_max = var_max;
|
||||
// If the domain of a variable is an interval, it is much
|
||||
// faster to iterate on that interval instead of using the
|
||||
// iterator.
|
||||
if (var_max - var_min + 1 == var_size) {
|
||||
for (; new_min <= var_max; ++new_min) {
|
||||
if (Supported(var_index, new_min - original_min)) {
|
||||
@@ -624,7 +638,10 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
to_remove_.push_back(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else { // Domain is sparse.
|
||||
// Let's not collect all values below the first supported
|
||||
// value as this can easily and more rapidly be taken care
|
||||
// of by a SetRange() call.
|
||||
new_min = kint64max; // escape value.
|
||||
IntVarIterator* const it = iterators_[var_index];
|
||||
for (it->Init(); it->Ok(); it->Next()) {
|
||||
@@ -664,7 +681,6 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
}
|
||||
}
|
||||
}
|
||||
touched_var_ = -1;
|
||||
}
|
||||
|
||||
void Update(int var_index) {
|
||||
@@ -680,37 +696,28 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
switch (var_size) {
|
||||
case 1: {
|
||||
SetTempMask(var_index, var->Min() - omin);
|
||||
// Then we apply this mask to active_tuples_.
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((~temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, temp_mask_[offset]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
changed = AndTempMaskWithActive();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
SetTempMask(var_index, var->Min() - omin);
|
||||
OrTempMask(var_index, var->Max() - omin);
|
||||
// Then we apply this mask to active_tuples_.
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((~temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, temp_mask_[offset]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
changed = AndTempMaskWithActive();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ClearTempMask();
|
||||
const int64 count = var_sizes_.Value(var_index) - var_size;
|
||||
// Rough estimation of the number of operation if we scan
|
||||
// deltas in the domain of the variable.
|
||||
const int64 old_min = var->OldMin();
|
||||
const int64 old_max = var->OldMax();
|
||||
const int64 var_min = var->Min();
|
||||
const int64 var_max = var->Max();
|
||||
if (count + var_min - old_min + old_max - var_max < var_size) {
|
||||
// Rough estimation of the number of operation if we scan
|
||||
// deltas in the domain of the variable.
|
||||
const int64 number_of_operations =
|
||||
count + var_min - old_min + old_max - var_max;
|
||||
if (number_of_operations < var_size) {
|
||||
// Let's scan the removed values since last run.
|
||||
for (int64 value = old_min; value < var_min; ++value) {
|
||||
OrTempMask(var_index, value - omin);
|
||||
}
|
||||
@@ -721,33 +728,26 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
for (int64 value = var_max + 1; value <= old_max; ++value) {
|
||||
OrTempMask(var_index, value - omin);
|
||||
}
|
||||
// Then we apply this mask to active_tuples_.
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, ~temp_mask_[offset]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Then we substract this mask from the active_tuples_.
|
||||
changed = SubstractTempMaskFromActive();
|
||||
} else {
|
||||
if (var_max - var_min + 1 == var_size) {
|
||||
// Let's build the mask of supported tuples from the current
|
||||
// domain.
|
||||
if (var_max - var_min + 1 == var_size) { // Contiguous.
|
||||
for (int64 value = var_min; value <= var_max; ++value) {
|
||||
OrTempMask(var_index, value - omin);
|
||||
}
|
||||
} else {
|
||||
IntVarIterator* const it = iterators_[var_index];
|
||||
for (it->Init(); it->Ok(); it->Next()) {
|
||||
const int64 value = it->Value();
|
||||
OrTempMask(var_index, value - omin);
|
||||
}
|
||||
}
|
||||
// Then we apply this mask to active_tuples_.
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((~temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, temp_mask_[offset]);
|
||||
changed = true;
|
||||
OrTempMask(var_index, it->Value() - omin);
|
||||
}
|
||||
}
|
||||
// Then we and this mask with active_tuples_.
|
||||
changed = AndTempMaskWithActive();
|
||||
}
|
||||
// We maintain the size of the variables incrementally (when it
|
||||
// is > 2).
|
||||
var_sizes_.SetValue(solver(), var_index, var_size);
|
||||
}
|
||||
}
|
||||
@@ -756,7 +756,7 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if (active_tuples_[offset]) {
|
||||
// We push the propagate method only if something has changed.
|
||||
if (touched_var_ == -1) {
|
||||
if (touched_var_ == -1 || touched_var_ == var_index) {
|
||||
touched_var_ = var_index;
|
||||
} else {
|
||||
touched_var_ = -2; // more than one var.
|
||||
@@ -770,6 +770,28 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
}
|
||||
|
||||
private:
|
||||
bool AndTempMaskWithActive() {
|
||||
bool changed = false;
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((~temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, temp_mask_[offset]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool SubstractTempMaskFromActive() {
|
||||
bool changed = false;
|
||||
for (int offset = 0; offset < length_; ++offset) {
|
||||
if ((temp_mask_[offset] & active_tuples_[offset]) != 0) {
|
||||
AndActiveTuples(offset, ~temp_mask_[offset]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool Supported(int var_index, int64 value_index) {
|
||||
DCHECK_GE(var_index, 0);
|
||||
DCHECK_LT(var_index, arity_);
|
||||
@@ -958,21 +980,35 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
// This method is not attached to any particular variable, but is pushed
|
||||
// at a delayed priority and awakened by Update(var_index).
|
||||
|
||||
// Reset touch_var_ if in mode (more than 1 variable was modified).
|
||||
if (touched_var_ == -2) {
|
||||
touched_var_ = -1;
|
||||
}
|
||||
|
||||
// We cache active_tuples_.
|
||||
const uint64 actives = active_tuples_;
|
||||
|
||||
// We scan all variables and check their domains.
|
||||
for (int var_index = 0; var_index < arity_; ++var_index) {
|
||||
// This demons runs in low priority. Thus we know all the
|
||||
// variables that have changed since the last time it was run.
|
||||
// In that case, if only one var was touched, as propagation is
|
||||
// exact, we do not need to recheck that variable.
|
||||
if (var_index == touched_var_) {
|
||||
touched_var_ = -1; // Clean it, it is a one time flag.
|
||||
continue;
|
||||
}
|
||||
IntVar* const var = vars_[var_index];
|
||||
const uint64* const var_mask = masks_[var_index];
|
||||
const int64 original_min = original_min_[var_index];
|
||||
IntVar* const var = vars_[var_index];
|
||||
const int64 var_size = var->Size();
|
||||
switch (var_size) {
|
||||
case 1: {
|
||||
if ((var_mask[var->Min() - original_min] & actives) == 0) {
|
||||
// The difference with the non-small version of the table
|
||||
// is that checking the validity of the resulting active
|
||||
// tuples is cheap. Therefore we do not delay the check
|
||||
// code.
|
||||
solver()->Fail();
|
||||
}
|
||||
break;
|
||||
@@ -984,13 +1020,10 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
(var_mask[var_min - original_min] & actives) != 0;
|
||||
const bool max_support =
|
||||
(var_mask[var_max - original_min] & actives) != 0;
|
||||
|
||||
if (!min_support) {
|
||||
if (!max_support) {
|
||||
solver()->Fail();
|
||||
} else {
|
||||
var->SetValue(var_max);
|
||||
}
|
||||
if (!min_support && !max_support) {
|
||||
solver()->Fail();
|
||||
} else if (!min_support) {
|
||||
var->SetValue(var_max);
|
||||
} else if (!max_support) {
|
||||
var->SetValue(var_min);
|
||||
}
|
||||
@@ -1003,6 +1036,7 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
int64 new_min = var_min;
|
||||
int64 new_max = var_max;
|
||||
if (var_max - var_min + 1 == var_size) {
|
||||
// Contiguous case.
|
||||
for (; new_min <= var_max; ++new_min) {
|
||||
if ((var_mask[new_min - original_min] & actives) != 0) {
|
||||
break;
|
||||
@@ -1020,28 +1054,32 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_min = kint64max; // escape value.
|
||||
bool min_set = false;
|
||||
int last_size = 0;
|
||||
IntVarIterator* const it = iterators_[var_index];
|
||||
for (it->Init(); it->Ok(); it->Next()) {
|
||||
// The iterator is not safe w.r.t. deletion. Thus we
|
||||
// postpone all value removals.
|
||||
const int64 value = it->Value();
|
||||
if ((var_mask[value - original_min] & actives) == 0) {
|
||||
to_remove_.push_back(value);
|
||||
if (min_set) {
|
||||
to_remove_.push_back(value);
|
||||
}
|
||||
} else {
|
||||
if (new_min == kint64max) {
|
||||
if (!min_set) {
|
||||
new_min = value;
|
||||
// This will be covered by the SetRange.
|
||||
to_remove_.clear();
|
||||
min_set = true;
|
||||
}
|
||||
new_max = value;
|
||||
last_size = to_remove_.size();
|
||||
}
|
||||
}
|
||||
var->SetRange(new_min, new_max);
|
||||
// Trim the to_remove vector.
|
||||
int index = to_remove_.size() - 1;
|
||||
while (index >= 0 && to_remove_[index] > new_max) {
|
||||
index--;
|
||||
if (min_set) {
|
||||
var->SetRange(new_min, new_max);
|
||||
} else {
|
||||
solver()->Fail();
|
||||
}
|
||||
to_remove_.resize(index + 1);
|
||||
to_remove_.resize(last_size);
|
||||
}
|
||||
switch (to_remove_.size()) {
|
||||
case 0: {
|
||||
@@ -1052,17 +1090,12 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (to_remove_.size() == var_size) {
|
||||
solver()->Fail();
|
||||
} else {
|
||||
var->RemoveValues(to_remove_);
|
||||
}
|
||||
var->RemoveValues(to_remove_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
touched_var_ = -1;
|
||||
}
|
||||
|
||||
void Update(int var_index) {
|
||||
@@ -1071,117 +1104,94 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
||||
|
||||
IntVar* const var = vars_[var_index];
|
||||
const int64 original_min = original_min_[var_index];
|
||||
switch (var->Size()) {
|
||||
const int64 var_size = var->Size();
|
||||
switch (var_size) {
|
||||
case 1: {
|
||||
const uint64 temp_mask = masks_[var_index][var->Min() - original_min];
|
||||
if ((~temp_mask & active_tuples_) != 0) {
|
||||
AndActiveTuples(temp_mask);
|
||||
if (active_tuples_ != 0) {
|
||||
UpdateTouchedVar(var_index);
|
||||
EnqueueDelayedDemon(demon_);
|
||||
} else {
|
||||
ClearTouchedVar();
|
||||
solver()->Fail();
|
||||
}
|
||||
}
|
||||
break;
|
||||
ApplyMask(var_index, masks_[var_index][var->Min() - original_min]);
|
||||
return;
|
||||
}
|
||||
case 2: {
|
||||
const uint64 temp_mask = masks_[var_index][var->Min() - original_min] |
|
||||
masks_[var_index][var->Max() - original_min];
|
||||
if ((~temp_mask & active_tuples_) != 0) {
|
||||
AndActiveTuples(temp_mask);
|
||||
if (active_tuples_ != 0) {
|
||||
UpdateTouchedVar(var_index);
|
||||
EnqueueDelayedDemon(demon_);
|
||||
} else {
|
||||
ClearTouchedVar();
|
||||
solver()->Fail();
|
||||
}
|
||||
}
|
||||
break;
|
||||
ApplyMask(var_index, masks_[var_index][var->Min() - original_min] |
|
||||
masks_[var_index][var->Max() - original_min]);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
// We first collect the complete set of tuples to blank out in
|
||||
// temp_mask.
|
||||
uint64 temp_mask = 0;
|
||||
const uint64* const var_mask = masks_[var_index];
|
||||
const int64 old_min = var->OldMin();
|
||||
const int64 old_max = var->OldMax();
|
||||
const int64 var_min = var->Min();
|
||||
const int64 var_max = var->Max();
|
||||
const bool contiguous = var_size == var_max - var_min + 1;
|
||||
|
||||
// Count the number of masks to collect to compare the deduction
|
||||
// vs the construction of the new active bitset.
|
||||
int count = 0;
|
||||
// TODO(user): Implement HolesSize() on IntVar* and use it
|
||||
// to remove this code and the var_sizes in the non_small
|
||||
// version.
|
||||
uint64 hole_mask = 0;
|
||||
IntVarIterator* const hole = holes_[var_index];
|
||||
for (hole->Init(); hole->Ok(); hole->Next()) {
|
||||
count++;
|
||||
hole_mask |= var_mask[hole->Value() - original_min];
|
||||
}
|
||||
if (count + var_min - old_min + old_max - var_max < var->Size()) {
|
||||
const int64 hole_operations =
|
||||
var_min - old_min + old_max - var_max;
|
||||
// We estimate the domain iterator to be 4x slower.
|
||||
const int64 domain_operations = contiguous ? var_size : 4 * var_size;
|
||||
if (hole_operations < domain_operations) {
|
||||
for (int64 value = old_min; value < var_min; ++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];
|
||||
hole_mask |= var_mask[value - original_min];
|
||||
}
|
||||
for (int64 value = var_max + 1; value <= old_max; ++value) {
|
||||
temp_mask |= var_mask[value - original_min];
|
||||
}
|
||||
// Then we apply this mask to active_tuples_.
|
||||
if (temp_mask & active_tuples_) {
|
||||
AndActiveTuples(~temp_mask);
|
||||
if (active_tuples_) {
|
||||
UpdateTouchedVar(var_index);
|
||||
EnqueueDelayedDemon(demon_);
|
||||
} else {
|
||||
ClearTouchedVar();
|
||||
solver()->Fail();
|
||||
}
|
||||
hole_mask |= var_mask[value - original_min];
|
||||
}
|
||||
// We reverse the mask as this was negative information.
|
||||
ApplyMask(var_index, ~hole_mask);
|
||||
} else {
|
||||
IntVarIterator* const it = iterators_[var_index];
|
||||
for (it->Init(); it->Ok(); it->Next()) {
|
||||
const int64 value = it->Value();
|
||||
temp_mask |= var_mask[value - original_min];
|
||||
}
|
||||
// Then we apply this mask to active_tuples_.
|
||||
if ((~temp_mask & active_tuples_) != 0) {
|
||||
AndActiveTuples(temp_mask);
|
||||
if (active_tuples_) {
|
||||
UpdateTouchedVar(var_index);
|
||||
EnqueueDelayedDemon(demon_);
|
||||
} else {
|
||||
ClearTouchedVar();
|
||||
solver()->Fail();
|
||||
uint64 domain_mask = 0;
|
||||
if (contiguous) {
|
||||
for (int64 value = var_min; value <= var_max; ++value) {
|
||||
domain_mask |= var_mask[value - original_min];
|
||||
}
|
||||
} else {
|
||||
IntVarIterator* const it = iterators_[var_index];
|
||||
for (it->Init(); it->Ok(); it->Next()) {
|
||||
domain_mask |= var_mask[it->Value() - original_min];
|
||||
}
|
||||
}
|
||||
ApplyMask(var_index, domain_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateTouchedVar(int var_index) {
|
||||
if (touched_var_ == -1) {
|
||||
touched_var_ = var_index;
|
||||
} else {
|
||||
touched_var_ = -2; // more than one var.
|
||||
void ApplyMask(int var_index, uint64 mask) {
|
||||
if ((~mask & active_tuples_) != 0) {
|
||||
// Check if we need to save the active_tuples in this node.
|
||||
const uint64 current_stamp = solver()->stamp();
|
||||
if (stamp_ < current_stamp) {
|
||||
stamp_ = current_stamp;
|
||||
solver()->SaveValue(&active_tuples_);
|
||||
}
|
||||
active_tuples_ &= mask;
|
||||
if (active_tuples_) {
|
||||
// Maintain touched_var_.
|
||||
if (touched_var_ == -1 || touched_var_ == var_index) {
|
||||
touched_var_ = var_index;
|
||||
} else {
|
||||
touched_var_ = -2; // more than one var.
|
||||
}
|
||||
EnqueueDelayedDemon(demon_);
|
||||
} else {
|
||||
// Clean it before failing.
|
||||
touched_var_ = -1;
|
||||
solver()->Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearTouchedVar() { touched_var_ = -1; }
|
||||
|
||||
void AndActiveTuples(uint64 mask) {
|
||||
const uint64 current_stamp = solver()->stamp();
|
||||
if (stamp_ < current_stamp) {
|
||||
stamp_ = current_stamp;
|
||||
solver()->SaveValue(&active_tuples_);
|
||||
}
|
||||
active_tuples_ &= mask;
|
||||
}
|
||||
|
||||
// Bitset of active tuples.
|
||||
uint64 active_tuples_;
|
||||
// Stamp of the active_tuple bitset.
|
||||
|
||||
Reference in New Issue
Block a user