fastest and cleanest version of the table to date

This commit is contained in:
lperron@google.com
2013-08-01 06:15:37 +00:00
parent 9cf4787e6c
commit 05818f5cef

View File

@@ -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.