2014-07-09 11:10:20 +00:00
|
|
|
// Copyright 2010-2014 Google
|
2010-09-15 12:42:33 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
//
|
|
|
|
|
// This file implements the table constraints.
|
|
|
|
|
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include "base/hash.h"
|
2015-08-13 16:00:54 +02:00
|
|
|
#include <memory>
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
#include "base/commandlineflags.h"
|
|
|
|
|
#include "base/integral_types.h"
|
|
|
|
|
#include "base/logging.h"
|
2011-09-21 15:16:48 +00:00
|
|
|
#include "base/stringprintf.h"
|
2014-05-13 12:56:44 +00:00
|
|
|
#include "base/join.h"
|
2013-10-10 15:23:20 +00:00
|
|
|
#include "base/map_util.h"
|
2011-09-21 15:16:48 +00:00
|
|
|
#include "constraint_solver/constraint_solver.h"
|
2010-09-15 12:42:33 +00:00
|
|
|
#include "constraint_solver/constraint_solveri.h"
|
2015-04-29 20:05:25 +02:00
|
|
|
#include "constraint_solver/sat_constraint.h"
|
2010-09-15 12:42:33 +00:00
|
|
|
#include "util/bitset.h"
|
2013-07-16 03:43:11 +00:00
|
|
|
#include "util/string_array.h"
|
2012-03-15 16:22:13 +00:00
|
|
|
#include "util/tuple_set.h"
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
2014-05-24 21:10:35 +00:00
|
|
|
// External table code.
|
|
|
|
|
Constraint* BuildAc4TableConstraint(Solver* const solver,
|
|
|
|
|
const IntTupleSet& tuples,
|
|
|
|
|
const std::vector<IntVar*>& vars);
|
|
|
|
|
|
|
|
|
|
Constraint* BuildSatTableConstraint(Solver* solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
const IntTupleSet& tuples);
|
|
|
|
|
|
2014-07-01 15:00:04 +00:00
|
|
|
Constraint* BuildAc4MddResetTableConstraint(Solver* const solver,
|
|
|
|
|
const IntTupleSet& tuples,
|
|
|
|
|
const std::vector<IntVar*>& vars);
|
|
|
|
|
|
2010-11-08 13:00:46 +00:00
|
|
|
namespace {
|
2013-08-01 00:48:02 +00:00
|
|
|
// ----- Presolve helpers -----
|
2013-10-10 15:23:20 +00:00
|
|
|
// TODO(user): Move this out of this file.
|
2013-08-01 00:48:02 +00:00
|
|
|
struct AffineTransformation { // y == a*x + b.
|
|
|
|
|
AffineTransformation() : a(1), b(0) {}
|
|
|
|
|
AffineTransformation(int64 aa, int64 bb) : a(aa), b(bb) { CHECK_NE(a, 0); }
|
|
|
|
|
int64 a;
|
|
|
|
|
int64 b;
|
|
|
|
|
|
|
|
|
|
bool Reverse(int64 value, int64* const reverse) const {
|
|
|
|
|
const int64 temp = value - b;
|
|
|
|
|
if (temp % a == 0) {
|
|
|
|
|
*reverse = temp / a;
|
2013-08-01 05:35:29 +00:00
|
|
|
DCHECK_EQ(Forward(*reverse), value);
|
2013-08-01 00:48:02 +00:00
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
int64 Forward(int64 value) const { return value * a + b; }
|
2013-08-01 00:48:02 +00:00
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
int64 UnsafeReverse(int64 value) const { return (value - b) / a; }
|
2013-08-01 05:35:29 +00:00
|
|
|
|
2013-08-01 00:48:02 +00:00
|
|
|
void Clear() {
|
|
|
|
|
a = 1;
|
|
|
|
|
b = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string DebugString() const {
|
2013-10-10 15:23:20 +00:00
|
|
|
return StringPrintf("(%" GG_LL_FORMAT "d * x + %" GG_LL_FORMAT "d)", a, b);
|
2013-08-01 00:48:02 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
// TODO(user): Move this out too.
|
2013-08-01 00:48:02 +00:00
|
|
|
class VarLinearizer : public ModelParser {
|
|
|
|
|
public:
|
|
|
|
|
VarLinearizer() : target_var_(nullptr), transformation_(nullptr) {}
|
2015-04-16 15:49:51 +02:00
|
|
|
~VarLinearizer() override {}
|
2013-08-01 00:48:02 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void VisitIntegerVariable(const IntVar* const variable,
|
|
|
|
|
const std::string& operation, int64 value,
|
|
|
|
|
IntVar* const delegate) override {
|
2013-08-01 00:48:02 +00:00
|
|
|
if (operation == ModelVisitor::kSumOperation) {
|
|
|
|
|
AddConstant(value);
|
|
|
|
|
delegate->Accept(this);
|
|
|
|
|
} else if (operation == ModelVisitor::kDifferenceOperation) {
|
|
|
|
|
AddConstant(value);
|
|
|
|
|
PushMultiplier(-1);
|
|
|
|
|
delegate->Accept(this);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
} else if (operation == ModelVisitor::kProductOperation) {
|
|
|
|
|
PushMultiplier(value);
|
|
|
|
|
delegate->Accept(this);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
} else if (operation == ModelVisitor::kTraceOperation) {
|
2013-08-01 00:49:29 +00:00
|
|
|
*target_var_ = const_cast<IntVar*>(variable);
|
|
|
|
|
transformation_->a = multipliers_.back();
|
2013-08-01 00:48:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void VisitIntegerVariable(const IntVar* const variable,
|
|
|
|
|
IntExpr* const delegate) override {
|
2013-08-01 00:48:02 +00:00
|
|
|
*target_var_ = const_cast<IntVar*>(variable);
|
|
|
|
|
transformation_->a = multipliers_.back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Visit(const IntVar* const var, IntVar** const target_var,
|
|
|
|
|
AffineTransformation* const transformation) {
|
|
|
|
|
target_var_ = target_var;
|
|
|
|
|
transformation_ = transformation;
|
|
|
|
|
transformation->Clear();
|
|
|
|
|
PushMultiplier(1);
|
|
|
|
|
var->Accept(this);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
CHECK(multipliers_.empty());
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override { return "VarLinearizer"; }
|
2013-08-01 00:48:02 +00:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void AddConstant(int64 constant) {
|
|
|
|
|
transformation_->b += constant * multipliers_.back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushMultiplier(int64 multiplier) {
|
|
|
|
|
if (multipliers_.empty()) {
|
|
|
|
|
multipliers_.push_back(multiplier);
|
|
|
|
|
} else {
|
|
|
|
|
multipliers_.push_back(multiplier * multipliers_.back());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PopMultiplier() { multipliers_.pop_back(); }
|
|
|
|
|
|
|
|
|
|
std::vector<int64> multipliers_;
|
|
|
|
|
IntVar** target_var_;
|
|
|
|
|
AffineTransformation* transformation_;
|
|
|
|
|
};
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
static const int kBitsInUint64 = 64;
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ----- Positive Table Constraint -----
|
|
|
|
|
|
|
|
|
|
// Structure of the constraint:
|
|
|
|
|
|
|
|
|
|
// Tuples are indexed, we maintain a bitset for active tuples.
|
|
|
|
|
|
|
|
|
|
// For each var and each value, we maintain a bitset mask of tuples
|
|
|
|
|
// containing this value for this variable.
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
// Propagation: When a value is removed, blank all active tuples using the
|
|
|
|
|
// var-value mask.
|
|
|
|
|
// Then we scan all other variable/values to see if there is an active
|
|
|
|
|
// tuple that supports it.
|
2013-08-01 05:35:29 +00:00
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
class BasePositiveTableConstraint : public Constraint {
|
|
|
|
|
public:
|
2013-07-30 21:41:29 +00:00
|
|
|
BasePositiveTableConstraint(Solver* const s, const std::vector<IntVar*>& vars,
|
2012-03-15 16:22:13 +00:00
|
|
|
const IntTupleSet& tuples)
|
2011-07-11 20:13:14 +00:00
|
|
|
: Constraint(s),
|
2013-08-01 05:35:29 +00:00
|
|
|
tuple_count_(tuples.NumTuples()),
|
2010-09-15 12:42:33 +00:00
|
|
|
arity_(vars.size()),
|
2013-08-01 05:35:29 +00:00
|
|
|
vars_(arity_),
|
|
|
|
|
holes_(arity_),
|
|
|
|
|
iterators_(arity_),
|
|
|
|
|
tuples_(tuples),
|
|
|
|
|
transformations_(arity_) {
|
2013-08-01 00:48:02 +00:00
|
|
|
// This constraint is intensive on domain and holes iterations on
|
|
|
|
|
// variables. Thus we can visit all variables to get to the
|
|
|
|
|
// boolean or domain int var beneath it. Then we can reverse
|
|
|
|
|
// process the tupleset to move in parallel to the simplifications
|
2013-10-10 15:23:20 +00:00
|
|
|
// of the variables. This way, we can keep the memory efficient
|
|
|
|
|
// nature of shared tuplesets (especially important for
|
|
|
|
|
// transitions constraints which are a chain of table
|
|
|
|
|
// constraints). The cost in running time is small as the tuples
|
|
|
|
|
// are read only once to construct the bitset data structures.
|
2013-08-01 00:48:02 +00:00
|
|
|
VarLinearizer linearizer;
|
|
|
|
|
for (int i = 0; i < arity_; ++i) {
|
2013-08-01 05:35:29 +00:00
|
|
|
linearizer.Visit(vars[i], &vars_[i], &transformations_[i]);
|
2013-08-01 00:48:02 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
// Create hole iterators
|
|
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
holes_[i] = vars_[i]->MakeHoleIterator(true);
|
|
|
|
|
iterators_[i] = vars_[i]->MakeDomainIterator(true);
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~BasePositiveTableConstraint() override {}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2012-06-11 12:46:09 +00:00
|
|
|
return StringPrintf("AllowedAssignments(arity = %d, tuple_count = %d)",
|
2013-07-30 21:41:29 +00:00
|
|
|
arity_, tuple_count_);
|
2011-07-11 20:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kAllowedAssignments, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2013-07-30 21:41:29 +00:00
|
|
|
visitor->VisitIntegerMatrixArgument(ModelVisitor::kTuplesArgument, tuples_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kAllowedAssignments, this);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2013-08-01 05:35:29 +00:00
|
|
|
bool TupleValue(int tuple_index, int var_index, int64* const value) const {
|
2016-06-02 13:19:10 +02:00
|
|
|
return transformations_[var_index].Reverse(
|
|
|
|
|
tuples_.Value(tuple_index, var_index), value);
|
2013-08-01 05:35:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64 UnsafeTupleValue(int tuple_index, int var_index) const {
|
2016-06-02 13:19:10 +02:00
|
|
|
return transformations_[var_index].UnsafeReverse(
|
|
|
|
|
tuples_.Value(tuple_index, var_index));
|
2013-08-01 05:35:29 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
bool IsTupleSupported(int tuple_index) {
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
int64 value = 0;
|
|
|
|
|
if (!TupleValue(tuple_index, var_index, &value) ||
|
|
|
|
|
!vars_[var_index]->Contains(value)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-01 05:35:29 +00:00
|
|
|
const int tuple_count_;
|
2010-09-15 12:42:33 +00:00
|
|
|
const int arity_;
|
2013-08-01 05:35:29 +00:00
|
|
|
std::vector<IntVar*> vars_;
|
|
|
|
|
std::vector<IntVarIterator*> holes_;
|
|
|
|
|
std::vector<IntVarIterator*> iterators_;
|
2011-05-17 20:38:55 +00:00
|
|
|
std::vector<int64> to_remove_;
|
2013-08-01 05:35:29 +00:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// All allowed tuples.
|
|
|
|
|
const IntTupleSet tuples_;
|
|
|
|
|
// The set of affine transformations that describe the
|
|
|
|
|
// simplification of the variables.
|
|
|
|
|
std::vector<AffineTransformation> transformations_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class PositiveTableConstraint : public BasePositiveTableConstraint {
|
|
|
|
|
public:
|
2016-07-13 14:51:35 -07:00
|
|
|
typedef hash_map<int, std::vector<uint64>> ValueBitset;
|
2011-09-17 08:49:48 +00:00
|
|
|
|
2013-07-30 21:41:29 +00:00
|
|
|
PositiveTableConstraint(Solver* const s, const std::vector<IntVar*>& vars,
|
2012-03-15 16:22:13 +00:00
|
|
|
const IntTupleSet& tuples)
|
2011-07-11 20:13:14 +00:00
|
|
|
: BasePositiveTableConstraint(s, vars, tuples),
|
2016-07-13 14:51:35 -07:00
|
|
|
word_length_(BitLength64(tuples.NumTuples())),
|
|
|
|
|
active_tuples_(tuples.NumTuples()) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
~PositiveTableConstraint() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-07-30 21:41:29 +00:00
|
|
|
Demon* d = MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &PositiveTableConstraint::Propagate, "Propagate");
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
vars_[i]->WhenDomain(d);
|
2013-07-30 21:41:29 +00:00
|
|
|
Demon* u = MakeConstraintDemon1(
|
|
|
|
|
solver(), this, &PositiveTableConstraint::Update, "Update", i);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenDomain(u);
|
|
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
// Initialize masks.
|
|
|
|
|
masks_.clear();
|
|
|
|
|
masks_.resize(arity_);
|
|
|
|
|
for (int i = 0; i < tuple_count_; ++i) {
|
|
|
|
|
InitializeMask(i);
|
|
|
|
|
}
|
|
|
|
|
// Initialize the active tuple bitset.
|
|
|
|
|
std::vector<uint64> actives(word_length_, 0);
|
|
|
|
|
for (int tuple_index = 0; tuple_index < tuple_count_; ++tuple_index) {
|
|
|
|
|
if (IsTupleSupported(tuple_index)) {
|
|
|
|
|
SetBit64(actives.data(), tuple_index);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
active_tuples_.Init(solver(), actives);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
// Build active_ structure.
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2014-04-15 09:38:36 +00:00
|
|
|
for (const auto& it : masks_[var_index]) {
|
|
|
|
|
if (!vars_[var_index]->Contains(it.first)) {
|
2016-07-13 14:51:35 -07:00
|
|
|
active_tuples_.RevSubtract(solver(), it.second);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
if (active_tuples_.Empty()) {
|
2010-09-15 12:42:33 +00:00
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
// Remove unreached values.
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2011-09-17 08:49:48 +00:00
|
|
|
const ValueBitset& mask = masks_[var_index];
|
2010-09-15 12:42:33 +00:00
|
|
|
IntVar* const var = vars_[var_index];
|
|
|
|
|
to_remove_.clear();
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (!ContainsKey(mask, value)) {
|
|
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 05:35:29 +00:00
|
|
|
if (to_remove_.size() > 0) {
|
|
|
|
|
var->RemoveValues(to_remove_);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Propagate() {
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
IntVar* const var = vars_[var_index];
|
|
|
|
|
to_remove_.clear();
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (!Supported(var_index, value)) {
|
|
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 05:35:29 +00:00
|
|
|
if (to_remove_.size() > 0) {
|
|
|
|
|
var->RemoveValues(to_remove_);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update(int index) {
|
2016-07-13 14:51:35 -07:00
|
|
|
const ValueBitset& var_masks = masks_[index];
|
2010-09-15 12:42:33 +00:00
|
|
|
IntVar* const var = vars_[index];
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 old_max = var->OldMax();
|
2010-09-15 12:42:33 +00:00
|
|
|
const int64 vmin = var->Min();
|
|
|
|
|
const int64 vmax = var->Max();
|
|
|
|
|
for (int64 value = var->OldMin(); value < vmin; ++value) {
|
2016-07-13 14:51:35 -07:00
|
|
|
const auto& it = var_masks.find(value);
|
|
|
|
|
if (it != var_masks.end()) {
|
|
|
|
|
BlankActives(it->second);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(holes_[index])) {
|
2016-07-13 14:51:35 -07:00
|
|
|
const auto& it = var_masks.find(value);
|
|
|
|
|
if (it != var_masks.end()) {
|
|
|
|
|
BlankActives(it->second);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-07-30 21:41:29 +00:00
|
|
|
for (int64 value = vmax + 1; value <= old_max; ++value) {
|
2016-07-13 14:51:35 -07:00
|
|
|
const auto& it = var_masks.find(value);
|
|
|
|
|
if (it != var_masks.end()) {
|
|
|
|
|
BlankActives(it->second);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
void BlankActives(const std::vector<uint64>& mask) {
|
|
|
|
|
if (!mask.empty()) {
|
|
|
|
|
active_tuples_.RevSubtract(solver(), mask);
|
|
|
|
|
if (active_tuples_.Empty()) {
|
2010-09-15 12:42:33 +00:00
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Supported(int var_index, int64 value) {
|
|
|
|
|
DCHECK_GE(var_index, 0);
|
|
|
|
|
DCHECK_LT(var_index, arity_);
|
|
|
|
|
DCHECK(ContainsKey(masks_[var_index], value));
|
2016-07-13 14:51:35 -07:00
|
|
|
const std::vector<uint64>& mask = masks_[var_index][value];
|
|
|
|
|
int tmp = 0;
|
|
|
|
|
return active_tuples_.Intersects(mask, &tmp);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2014-05-15 20:11:39 +00:00
|
|
|
return StringPrintf("PositiveTableConstraint([%s], %d tuples)",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
protected:
|
2012-03-15 16:22:13 +00:00
|
|
|
void InitializeMask(int tuple_index) {
|
2013-08-01 05:35:29 +00:00
|
|
|
std::vector<int64> cache(arity_);
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
if (!TupleValue(tuple_index, var_index, &cache[var_index])) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2013-08-01 05:35:29 +00:00
|
|
|
const int64 value = cache[var_index];
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<uint64>& mask = masks_[var_index][value];
|
|
|
|
|
if (mask.empty()) {
|
|
|
|
|
mask.assign(word_length_, 0);
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
SetBit64(mask.data(), tuple_index);
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
const int word_length_;
|
|
|
|
|
UnsortedNullableRevBitset active_tuples_;
|
2011-09-17 08:49:48 +00:00
|
|
|
std::vector<ValueBitset> masks_;
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<uint64> temp_mask_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2014-07-21 04:53:24 +00:00
|
|
|
// ----- Compact Tables -----
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
class CompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
|
|
|
|
public:
|
2014-05-21 13:01:04 +00:00
|
|
|
CompactPositiveTableConstraint(Solver* const s, const std::vector<IntVar*>& vars,
|
2012-03-15 16:22:13 +00:00
|
|
|
const IntTupleSet& tuples)
|
2011-07-11 20:13:14 +00:00
|
|
|
: BasePositiveTableConstraint(s, vars, tuples),
|
2016-07-13 14:51:35 -07:00
|
|
|
word_length_(BitLength64(tuples.NumTuples())),
|
|
|
|
|
active_tuples_(tuples.NumTuples()),
|
|
|
|
|
masks_(arity_),
|
|
|
|
|
mask_starts_(arity_),
|
|
|
|
|
mask_ends_(arity_),
|
|
|
|
|
original_min_(arity_, 0),
|
|
|
|
|
supports_(arity_),
|
2013-10-10 15:23:20 +00:00
|
|
|
demon_(nullptr),
|
2013-07-29 23:57:50 +00:00
|
|
|
touched_var_(-1),
|
2016-07-13 14:51:35 -07:00
|
|
|
var_sizes_(arity_, 0) {}
|
2011-05-20 14:01:28 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~CompactPositiveTableConstraint() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2011-11-16 17:32:24 +00:00
|
|
|
demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
2013-07-30 21:41:29 +00:00
|
|
|
solver(), this, &CompactPositiveTableConstraint::Propagate,
|
2011-11-16 17:32:24 +00:00
|
|
|
"Propagate"));
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int i = 0; i < arity_; ++i) {
|
2012-09-07 14:18:09 +00:00
|
|
|
Demon* const u = MakeConstraintDemon1(
|
2013-07-30 21:41:29 +00:00
|
|
|
solver(), this, &CompactPositiveTableConstraint::Update, "Update", i);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenDomain(u);
|
|
|
|
|
}
|
2013-07-29 23:57:50 +00:00
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
var_sizes_.SetValue(solver(), i, vars_[i]->Size());
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-11-15 15:09:52 +00:00
|
|
|
BuildMasks();
|
2016-07-13 14:51:35 -07:00
|
|
|
FillMasksAndActiveTuples();
|
2010-11-15 15:09:52 +00:00
|
|
|
RemoveUnsupportedValues();
|
2016-07-13 14:51:35 -07:00
|
|
|
ComputeMasksBoundaries();
|
|
|
|
|
BuildSupports();
|
2010-11-15 15:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
2014-07-19 17:04:44 +00:00
|
|
|
// ----- Propagation -----
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
void Propagate() {
|
2013-08-01 06:15:37 +00:00
|
|
|
// Reset touch_var_ if in mode (more than 1 variable was modified).
|
|
|
|
|
if (touched_var_ == -2) {
|
|
|
|
|
touched_var_ = -1;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
// 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
|
2013-07-16 04:11:11 +00:00
|
|
|
// at a delayed priority after Update(var_index) is called.
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2013-08-01 06:15:37 +00:00
|
|
|
// 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.
|
2013-07-16 03:43:11 +00:00
|
|
|
if (var_index == touched_var_) {
|
2013-08-01 06:15:37 +00:00
|
|
|
touched_var_ = -1; // Clean now, it is a 1 time flag.
|
2013-07-16 03:43:11 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2013-07-29 23:57:50 +00:00
|
|
|
IntVar* const var = vars_[var_index];
|
2013-07-30 04:11:31 +00:00
|
|
|
const int64 original_min = original_min_[var_index];
|
2013-07-30 21:59:35 +00:00
|
|
|
const int64 var_size = var->Size();
|
2013-08-01 06:15:37 +00:00
|
|
|
// The domain iterator is very slow, let's try to see if we can
|
|
|
|
|
// work our way around.
|
2013-07-30 21:59:35 +00:00
|
|
|
switch (var_size) {
|
2013-07-30 04:11:31 +00:00
|
|
|
case 1: {
|
|
|
|
|
if (!Supported(var_index, var->Min() - original_min)) {
|
|
|
|
|
solver()->Fail();
|
2013-07-16 04:11:11 +00:00
|
|
|
}
|
2013-07-30 04:11:31 +00:00
|
|
|
break;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-07-30 04:11:31 +00:00
|
|
|
case 2: {
|
|
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
|
|
|
|
const bool min_support = Supported(var_index, var_min - original_min);
|
|
|
|
|
const bool max_support = Supported(var_index, var_max - original_min);
|
|
|
|
|
if (!min_support) {
|
|
|
|
|
if (!max_support) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
} else {
|
|
|
|
|
var->SetValue(var_max);
|
2014-07-21 04:53:24 +00:00
|
|
|
var_sizes_.SetValue(solver(), var_index, 1);
|
2013-07-30 04:11:31 +00:00
|
|
|
}
|
|
|
|
|
} else if (!max_support) {
|
|
|
|
|
var->SetValue(var_min);
|
2014-07-21 04:53:24 +00:00
|
|
|
var_sizes_.SetValue(solver(), var_index, 1);
|
2013-07-30 04:11:31 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
to_remove_.clear();
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
2013-07-31 01:34:35 +00:00
|
|
|
int64 new_min = var_min;
|
|
|
|
|
int64 new_max = var_max;
|
2013-08-01 06:15:37 +00:00
|
|
|
// If the domain of a variable is an interval, it is much
|
|
|
|
|
// faster to iterate on that interval instead of using the
|
|
|
|
|
// iterator.
|
2013-07-30 21:59:35 +00:00
|
|
|
if (var_max - var_min + 1 == var_size) {
|
2013-07-31 01:34:35 +00:00
|
|
|
for (; new_min <= var_max; ++new_min) {
|
|
|
|
|
if (Supported(var_index, new_min - original_min)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; new_max >= new_min; --new_max) {
|
|
|
|
|
if (Supported(var_index, new_max - original_min)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var->SetRange(new_min, new_max);
|
|
|
|
|
for (int64 value = new_min + 1; value < new_max; ++value) {
|
2013-07-30 21:41:29 +00:00
|
|
|
if (!Supported(var_index, value - original_min)) {
|
|
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
} else { // Domain is sparse.
|
2013-10-17 08:58:26 +00:00
|
|
|
// Let's not collect all values below the first supported
|
|
|
|
|
// value as this can easily and more rapidly be taken care
|
2014-05-15 20:11:39 +00:00
|
|
|
// of by a SetRange() call.
|
2013-08-01 05:35:29 +00:00
|
|
|
new_min = kint64max; // escape value.
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2013-07-30 21:41:29 +00:00
|
|
|
if (!Supported(var_index, value - original_min)) {
|
2013-08-01 05:35:29 +00:00
|
|
|
to_remove_.push_back(value);
|
2013-07-30 21:41:29 +00:00
|
|
|
} else {
|
2013-08-01 05:35:29 +00:00
|
|
|
if (new_min == kint64max) {
|
2013-07-30 21:41:29 +00:00
|
|
|
new_min = value;
|
2013-08-01 05:35:29 +00:00
|
|
|
// This will be covered by the SetRange.
|
|
|
|
|
to_remove_.clear();
|
2013-07-30 21:41:29 +00:00
|
|
|
}
|
|
|
|
|
new_max = value;
|
2013-07-30 04:11:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 05:35:29 +00:00
|
|
|
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--;
|
2013-07-31 23:52:09 +00:00
|
|
|
}
|
2013-08-01 05:35:29 +00:00
|
|
|
to_remove_.resize(index + 1);
|
2013-07-30 04:11:31 +00:00
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
var->RemoveValues(to_remove_);
|
2014-07-21 04:53:24 +00:00
|
|
|
var_sizes_.SetValue(solver(), var_index, var->Size());
|
2013-07-16 04:58:02 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update(int var_index) {
|
2014-07-21 04:53:24 +00:00
|
|
|
if (vars_[var_index]->Size() == var_sizes_.Value(var_index)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
// 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];
|
|
|
|
|
bool changed = false;
|
2013-07-30 01:59:23 +00:00
|
|
|
const int64 omin = original_min_[var_index];
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 var_size = var->Size();
|
2013-08-01 00:48:02 +00:00
|
|
|
|
2013-08-01 05:35:29 +00:00
|
|
|
switch (var_size) {
|
|
|
|
|
case 1: {
|
|
|
|
|
SetTempMask(var_index, var->Min() - omin);
|
2013-08-01 06:15:37 +00:00
|
|
|
changed = AndTempMaskWithActive();
|
2013-08-01 05:35:29 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 2: {
|
|
|
|
|
SetTempMask(var_index, var->Min() - omin);
|
|
|
|
|
OrTempMask(var_index, var->Max() - omin);
|
2013-08-01 06:15:37 +00:00
|
|
|
changed = AndTempMaskWithActive();
|
2013-08-01 05:35:29 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
ClearTempMask();
|
2014-07-19 17:04:44 +00:00
|
|
|
const int64 estimated_hole_size =
|
|
|
|
|
var_sizes_.Value(var_index) - var_size;
|
2013-08-01 05:35:29 +00:00
|
|
|
const int64 old_min = var->OldMin();
|
|
|
|
|
const int64 old_max = var->OldMax();
|
|
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
2013-08-01 06:15:37 +00:00
|
|
|
// Rough estimation of the number of operation if we scan
|
|
|
|
|
// deltas in the domain of the variable.
|
|
|
|
|
const int64 number_of_operations =
|
2014-07-19 17:04:44 +00:00
|
|
|
estimated_hole_size + var_min - old_min + old_max - var_max;
|
2013-08-01 06:15:37 +00:00
|
|
|
if (number_of_operations < var_size) {
|
|
|
|
|
// Let's scan the removed values since last run.
|
2013-08-01 05:35:29 +00:00
|
|
|
for (int64 value = old_min; value < var_min; ++value) {
|
2013-07-30 01:59:23 +00:00
|
|
|
OrTempMask(var_index, value - omin);
|
|
|
|
|
}
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(holes_[var_index])) {
|
|
|
|
|
OrTempMask(var_index, value - omin);
|
2013-08-01 05:35:29 +00:00
|
|
|
}
|
|
|
|
|
for (int64 value = var_max + 1; value <= old_max; ++value) {
|
2013-07-31 23:52:09 +00:00
|
|
|
OrTempMask(var_index, value - omin);
|
2013-07-30 01:59:23 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
// Then we Subtract this mask from the active_tuples_.
|
|
|
|
|
changed = SubtractTempMaskFromActive();
|
2013-08-01 05:35:29 +00:00
|
|
|
} else {
|
2013-08-01 06:15:37 +00:00
|
|
|
// Let's build the mask of supported tuples from the current
|
|
|
|
|
// domain.
|
|
|
|
|
if (var_max - var_min + 1 == var_size) { // Contiguous.
|
2013-08-01 05:35:29 +00:00
|
|
|
for (int64 value = var_min; value <= var_max; ++value) {
|
|
|
|
|
OrTempMask(var_index, value - omin);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
|
|
|
|
OrTempMask(var_index, value - omin);
|
2013-08-01 05:35:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
// Then we and this mask with active_tuples_.
|
|
|
|
|
changed = AndTempMaskWithActive();
|
2013-07-30 01:59:23 +00:00
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
// We maintain the size of the variables incrementally (when it
|
|
|
|
|
// is > 2).
|
2013-08-01 05:35:29 +00:00
|
|
|
var_sizes_.SetValue(solver(), var_index, var_size);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2010-11-10 17:28:25 +00:00
|
|
|
// And check active_tuples_ is still not empty, we fail otherwise.
|
2013-07-29 23:57:50 +00:00
|
|
|
if (changed) {
|
2016-07-13 14:51:35 -07:00
|
|
|
if (active_tuples_.Empty()) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
} else {
|
|
|
|
|
// We push the propagate method only if something has changed.
|
|
|
|
|
if (touched_var_ == -1 || touched_var_ == var_index) {
|
|
|
|
|
touched_var_ = var_index;
|
|
|
|
|
} else {
|
|
|
|
|
touched_var_ = -2; // more than one var.
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
EnqueueDelayedDemon(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2014-05-15 20:11:39 +00:00
|
|
|
return StringPrintf("CompactPositiveTableConstraint([%s], %d tuples)",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
private:
|
2014-07-19 17:04:44 +00:00
|
|
|
// ----- Initialization -----
|
|
|
|
|
|
|
|
|
|
void BuildMasks() {
|
|
|
|
|
// Build masks.
|
|
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
original_min_[i] = vars_[i]->Min();
|
|
|
|
|
const int64 span = vars_[i]->Max() - original_min_[i] + 1;
|
2016-07-13 14:51:35 -07:00
|
|
|
masks_[i].resize(span);
|
2014-07-19 17:04:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
void FillMasksAndActiveTuples() {
|
|
|
|
|
std::vector<uint64> actives(word_length_, 0);
|
|
|
|
|
for (int tuple_index = 0; tuple_index < tuple_count_; ++tuple_index) {
|
|
|
|
|
if (IsTupleSupported(tuple_index)) {
|
|
|
|
|
SetBit64(actives.data(), tuple_index);
|
|
|
|
|
// Fill in all masks.
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
const int64 value = UnsafeTupleValue(tuple_index, var_index);
|
|
|
|
|
const int64 value_index = value - original_min_[var_index];
|
|
|
|
|
DCHECK_GE(value_index, 0);
|
|
|
|
|
DCHECK_LT(value_index, masks_[var_index].size());
|
|
|
|
|
if (masks_[var_index][value_index].empty()) {
|
|
|
|
|
masks_[var_index][value_index].assign(word_length_, 0);
|
2014-07-19 17:04:44 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
SetBit64(masks_[var_index][value_index].data(), tuple_index);
|
2014-07-19 17:04:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
active_tuples_.Init(solver(), actives);
|
2014-07-19 17:04:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RemoveUnsupportedValues() {
|
|
|
|
|
// remove unreached values.
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
IntVar* const var = vars_[var_index];
|
|
|
|
|
to_remove_.clear();
|
|
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2016-07-13 14:51:35 -07:00
|
|
|
if (masks_[var_index][value - original_min_[var_index]].empty()) {
|
2014-07-19 17:04:44 +00:00
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (to_remove_.size() > 0) {
|
|
|
|
|
var->RemoveValues(to_remove_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
void ComputeMasksBoundaries() {
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
mask_starts_[var_index].resize(masks_[var_index].size());
|
|
|
|
|
mask_ends_[var_index].resize(masks_[var_index].size());
|
|
|
|
|
for (int value_index = 0; value_index < masks_[var_index].size();
|
|
|
|
|
++value_index) {
|
|
|
|
|
const std::vector<uint64>& mask = masks_[var_index][value_index];
|
|
|
|
|
if (mask.empty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
int start = 0;
|
|
|
|
|
while (start < word_length_ && mask[start] == 0) {
|
|
|
|
|
start++;
|
|
|
|
|
}
|
|
|
|
|
DCHECK_LT(start, word_length_);
|
|
|
|
|
int end = word_length_ - 1;
|
|
|
|
|
while (end > start && mask[end] == 0) {
|
|
|
|
|
end--;
|
|
|
|
|
}
|
|
|
|
|
DCHECK_LE(start, end);
|
|
|
|
|
DCHECK_NE(mask[start], 0);
|
|
|
|
|
DCHECK_NE(mask[end], 0);
|
|
|
|
|
mask_starts_[var_index][value_index] = start;
|
|
|
|
|
mask_ends_[var_index][value_index] = end;
|
|
|
|
|
}
|
2014-07-19 02:56:09 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildSupports() {
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
|
|
|
|
supports_[var_index].resize(masks_[var_index].size());
|
2014-07-19 02:56:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
// ----- Helpers during propagation -----
|
2014-07-19 17:04:44 +00:00
|
|
|
|
2013-08-01 06:15:37 +00:00
|
|
|
bool AndTempMaskWithActive() {
|
2016-07-13 14:51:35 -07:00
|
|
|
for (int i = 0; i < temp_mask_.size(); ++i) {
|
|
|
|
|
temp_mask_[i] = ~temp_mask_[i];
|
2013-08-01 06:15:37 +00:00
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
return active_tuples_.RevSubtract(solver(), temp_mask_);
|
2013-08-01 06:15:37 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
bool SubtractTempMaskFromActive() {
|
|
|
|
|
return active_tuples_.RevSubtract(solver(), temp_mask_);
|
2013-08-01 06:15:37 +00:00
|
|
|
}
|
|
|
|
|
|
2013-07-30 21:41:29 +00:00
|
|
|
bool Supported(int var_index, int64 value_index) {
|
|
|
|
|
DCHECK_GE(var_index, 0);
|
|
|
|
|
DCHECK_LT(var_index, arity_);
|
|
|
|
|
DCHECK_GE(value_index, 0);
|
2016-07-13 14:51:35 -07:00
|
|
|
DCHECK(!masks_[var_index][value_index].empty());
|
|
|
|
|
const std::vector<uint64>& mask = masks_[var_index][value_index];
|
|
|
|
|
return active_tuples_.Intersects(mask, &supports_[var_index][value_index]);
|
2013-07-30 21:41:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrTempMask(int var_index, int64 value_index) {
|
2016-07-13 14:51:35 -07:00
|
|
|
const std::vector<uint64>& mask = masks_[var_index][value_index];
|
|
|
|
|
if (!mask.empty()) {
|
|
|
|
|
for (int offset = mask_starts_[var_index][value_index];
|
|
|
|
|
offset <= mask_ends_[var_index][value_index]; ++offset) {
|
2013-07-30 21:41:29 +00:00
|
|
|
temp_mask_[offset] |= mask[offset];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetTempMask(int var_index, int64 value_index) {
|
2016-07-13 14:51:35 -07:00
|
|
|
temp_mask_ = masks_[var_index][value_index];
|
2013-07-30 21:41:29 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
void ClearTempMask() { temp_mask_.assign(word_length_, 0); }
|
2011-12-16 21:02:59 +00:00
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
// The length in 64 bit words of the number of tuples.
|
|
|
|
|
int64 word_length_;
|
|
|
|
|
// The active bitset.
|
|
|
|
|
UnsortedNullableRevBitset active_tuples_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The masks per value per variable.
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<std::vector<std::vector<uint64>>> masks_;
|
|
|
|
|
// The range of active indices in the masks.
|
|
|
|
|
std::vector<std::vector<int>> mask_starts_;
|
|
|
|
|
std::vector<std::vector<int>> mask_ends_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The min on the vars at creation time.
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<int64> original_min_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// A temporary mask use for computation.
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<uint64> temp_mask_;
|
|
|
|
|
// The index of the word in the active bitset supporting each value per
|
|
|
|
|
// variable.
|
2014-07-19 17:04:44 +00:00
|
|
|
std::vector<std::vector<int>> supports_;
|
2010-09-15 12:42:33 +00:00
|
|
|
Demon* demon_;
|
2013-07-16 03:43:11 +00:00
|
|
|
int touched_var_;
|
2013-07-29 23:57:50 +00:00
|
|
|
RevArray<int64> var_sizes_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2010-11-08 13:00:46 +00:00
|
|
|
// ----- Small Compact Table. -----
|
|
|
|
|
|
2010-11-15 15:09:52 +00:00
|
|
|
// TODO(user): regroup code with CompactPositiveTableConstraint.
|
|
|
|
|
|
2010-11-08 13:00:46 +00:00
|
|
|
class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint {
|
|
|
|
|
public:
|
|
|
|
|
SmallCompactPositiveTableConstraint(Solver* const s,
|
2013-07-30 21:41:29 +00:00
|
|
|
const std::vector<IntVar*>& vars,
|
2012-03-15 16:22:13 +00:00
|
|
|
const IntTupleSet& tuples)
|
2011-07-11 20:13:14 +00:00
|
|
|
: BasePositiveTableConstraint(s, vars, tuples),
|
2010-11-10 17:28:25 +00:00
|
|
|
active_tuples_(0),
|
2010-11-08 13:00:46 +00:00
|
|
|
stamp_(0),
|
2016-07-13 14:51:35 -07:00
|
|
|
masks_(arity_),
|
|
|
|
|
original_min_(arity_, 0),
|
2013-10-10 15:23:20 +00:00
|
|
|
demon_(nullptr),
|
2013-07-30 01:32:56 +00:00
|
|
|
touched_var_(-1) {
|
2010-11-10 17:28:25 +00:00
|
|
|
CHECK_GE(tuple_count_, 0);
|
|
|
|
|
CHECK_GE(arity_, 0);
|
2012-03-15 16:22:13 +00:00
|
|
|
CHECK_LE(tuples.NumTuples(), kBitsInUint64);
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 14:51:35 -07:00
|
|
|
~SmallCompactPositiveTableConstraint() override {}
|
2010-11-08 13:00:46 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2011-11-16 17:32:24 +00:00
|
|
|
demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
2013-07-30 21:41:29 +00:00
|
|
|
solver(), this, &SmallCompactPositiveTableConstraint::Propagate,
|
2011-11-16 17:32:24 +00:00
|
|
|
"Propagate"));
|
2010-11-08 13:00:46 +00:00
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
if (!vars_[i]->Bound()) {
|
2010-11-10 17:28:25 +00:00
|
|
|
Demon* const update_demon = MakeConstraintDemon1(
|
2013-07-30 21:41:29 +00:00
|
|
|
solver(), this, &SmallCompactPositiveTableConstraint::Update,
|
|
|
|
|
"Update", i);
|
2010-11-10 17:28:25 +00:00
|
|
|
vars_[i]->WhenDomain(update_demon);
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-16 21:02:59 +00:00
|
|
|
stamp_ = 0;
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
void InitMasks() {
|
2010-11-08 13:00:46 +00:00
|
|
|
// Build masks.
|
|
|
|
|
for (int i = 0; i < arity_; ++i) {
|
|
|
|
|
original_min_[i] = vars_[i]->Min();
|
|
|
|
|
const int64 span = vars_[i]->Max() - original_min_[i] + 1;
|
2016-07-13 14:51:35 -07:00
|
|
|
masks_[i].assign(span, 0);
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
2010-11-10 17:28:25 +00:00
|
|
|
}
|
2010-11-08 13:00:46 +00:00
|
|
|
|
2010-11-15 15:09:52 +00:00
|
|
|
bool IsTupleSupported(int tuple_index) {
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2013-08-01 05:35:29 +00:00
|
|
|
int64 value = 0;
|
|
|
|
|
if (!TupleValue(tuple_index, var_index, &value) ||
|
|
|
|
|
!vars_[var_index]->Contains(value)) {
|
2010-11-15 15:09:52 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
void ComputeActiveTuples() {
|
2010-11-15 15:09:52 +00:00
|
|
|
active_tuples_ = 0;
|
2011-07-11 20:13:14 +00:00
|
|
|
// Compute active_tuples_ and update masks.
|
2010-11-08 13:00:46 +00:00
|
|
|
for (int tuple_index = 0; tuple_index < tuple_count_; ++tuple_index) {
|
2010-11-15 15:09:52 +00:00
|
|
|
if (IsTupleSupported(tuple_index)) {
|
|
|
|
|
const uint64 local_mask = OneBit64(tuple_index);
|
|
|
|
|
active_tuples_ |= local_mask;
|
2010-11-08 13:00:46 +00:00
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2013-08-01 05:35:29 +00:00
|
|
|
const int64 value = UnsafeTupleValue(tuple_index, var_index);
|
|
|
|
|
masks_[var_index][value - original_min_[var_index]] |= local_mask;
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-10 17:28:25 +00:00
|
|
|
if (!active_tuples_) {
|
2010-11-08 13:00:46 +00:00
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
2010-11-10 17:28:25 +00:00
|
|
|
}
|
2010-11-08 13:00:46 +00:00
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
void RemoveUnsupportedValues() {
|
2010-11-08 13:00:46 +00:00
|
|
|
// 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();
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2010-11-15 15:09:52 +00:00
|
|
|
if (masks_[var_index][value - original_min] == 0) {
|
2010-11-08 13:00:46 +00:00
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 05:35:29 +00:00
|
|
|
if (to_remove_.size() > 0) {
|
|
|
|
|
var->RemoveValues(to_remove_);
|
|
|
|
|
}
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-11-10 17:28:25 +00:00
|
|
|
InitMasks();
|
|
|
|
|
ComputeActiveTuples();
|
|
|
|
|
RemoveUnsupportedValues();
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-08 13:00:46 +00:00
|
|
|
void Propagate() {
|
2010-11-10 17:28:25 +00:00
|
|
|
// This methods scans all the values of all the variables to see if they
|
2010-11-08 13:00:46 +00:00
|
|
|
// 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).
|
|
|
|
|
|
2013-08-01 06:15:37 +00:00
|
|
|
// Reset touch_var_ if in mode (more than 1 variable was modified).
|
|
|
|
|
if (touched_var_ == -2) {
|
|
|
|
|
touched_var_ = -1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
// We cache active_tuples_.
|
|
|
|
|
const uint64 actives = active_tuples_;
|
2010-11-08 13:00:46 +00:00
|
|
|
|
|
|
|
|
// We scan all variables and check their domains.
|
|
|
|
|
for (int var_index = 0; var_index < arity_; ++var_index) {
|
2013-08-01 06:15:37 +00:00
|
|
|
// 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.
|
2013-07-30 01:32:56 +00:00
|
|
|
if (var_index == touched_var_) {
|
2013-08-01 06:15:37 +00:00
|
|
|
touched_var_ = -1; // Clean it, it is a one time flag.
|
2013-07-30 01:32:56 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2016-07-13 14:51:35 -07:00
|
|
|
const std::vector<uint64>& var_mask = masks_[var_index];
|
2010-11-08 13:00:46 +00:00
|
|
|
const int64 original_min = original_min_[var_index];
|
2013-08-01 06:15:37 +00:00
|
|
|
IntVar* const var = vars_[var_index];
|
2013-07-30 21:59:35 +00:00
|
|
|
const int64 var_size = var->Size();
|
|
|
|
|
switch (var_size) {
|
2013-07-30 01:32:56 +00:00
|
|
|
case 1: {
|
|
|
|
|
if ((var_mask[var->Min() - original_min] & actives) == 0) {
|
2013-08-01 06:15:37 +00:00
|
|
|
// 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.
|
2013-07-30 01:32:56 +00:00
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
break;
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
2013-07-30 01:32:56 +00:00
|
|
|
case 2: {
|
|
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
2013-07-30 04:11:31 +00:00
|
|
|
const bool min_support =
|
|
|
|
|
(var_mask[var_min - original_min] & actives) != 0;
|
|
|
|
|
const bool max_support =
|
|
|
|
|
(var_mask[var_max - original_min] & actives) != 0;
|
2013-08-01 06:15:37 +00:00
|
|
|
if (!min_support && !max_support) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
} else if (!min_support) {
|
|
|
|
|
var->SetValue(var_max);
|
2013-08-01 01:01:17 +00:00
|
|
|
} else if (!max_support) {
|
|
|
|
|
var->SetValue(var_min);
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
2013-07-30 01:32:56 +00:00
|
|
|
break;
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
2013-07-30 01:32:56 +00:00
|
|
|
default: {
|
|
|
|
|
to_remove_.clear();
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
2013-07-31 01:34:35 +00:00
|
|
|
int64 new_min = var_min;
|
|
|
|
|
int64 new_max = var_max;
|
2013-07-30 21:59:35 +00:00
|
|
|
if (var_max - var_min + 1 == var_size) {
|
2013-08-01 06:15:37 +00:00
|
|
|
// Contiguous case.
|
2013-07-31 01:34:35 +00:00
|
|
|
for (; new_min <= var_max; ++new_min) {
|
|
|
|
|
if ((var_mask[new_min - original_min] & actives) != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; new_max >= new_min; --new_max) {
|
|
|
|
|
if ((var_mask[new_max - original_min] & actives) != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var->SetRange(new_min, new_max);
|
|
|
|
|
for (int64 value = new_min + 1; value < new_max; ++value) {
|
2013-07-30 21:41:29 +00:00
|
|
|
if ((var_mask[value - original_min] & actives) == 0) {
|
|
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-08-01 06:15:37 +00:00
|
|
|
bool min_set = false;
|
|
|
|
|
int last_size = 0;
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
2013-08-01 06:15:37 +00:00
|
|
|
// The iterator is not safe w.r.t. deletion. Thus we
|
|
|
|
|
// postpone all value removals.
|
2013-07-30 21:41:29 +00:00
|
|
|
if ((var_mask[value - original_min] & actives) == 0) {
|
2013-08-01 06:15:37 +00:00
|
|
|
if (min_set) {
|
|
|
|
|
to_remove_.push_back(value);
|
|
|
|
|
}
|
2013-07-31 01:53:54 +00:00
|
|
|
} else {
|
2013-08-01 06:15:37 +00:00
|
|
|
if (!min_set) {
|
2013-07-31 01:53:54 +00:00
|
|
|
new_min = value;
|
2013-08-01 06:15:37 +00:00
|
|
|
min_set = true;
|
2013-07-31 01:53:54 +00:00
|
|
|
}
|
|
|
|
|
new_max = value;
|
2013-08-01 06:15:37 +00:00
|
|
|
last_size = to_remove_.size();
|
2013-07-30 21:41:29 +00:00
|
|
|
}
|
2013-07-30 01:32:56 +00:00
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
if (min_set) {
|
|
|
|
|
var->SetRange(new_min, new_max);
|
|
|
|
|
} else {
|
|
|
|
|
solver()->Fail();
|
2013-07-31 23:52:09 +00:00
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
to_remove_.resize(last_size);
|
2013-07-30 01:32:56 +00:00
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
var->RemoveValues(to_remove_);
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update(int var_index) {
|
2010-11-10 17:28:25 +00:00
|
|
|
// This method updates the set of active tuples by masking out all
|
2010-11-08 13:00:46 +00:00
|
|
|
// tuples attached to values of the variables that have been removed.
|
|
|
|
|
|
|
|
|
|
IntVar* const var = vars_[var_index];
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 original_min = original_min_[var_index];
|
2013-08-01 06:15:37 +00:00
|
|
|
const int64 var_size = var->Size();
|
|
|
|
|
switch (var_size) {
|
2013-07-30 01:46:45 +00:00
|
|
|
case 1: {
|
2013-08-01 06:15:37 +00:00
|
|
|
ApplyMask(var_index, masks_[var_index][var->Min() - original_min]);
|
|
|
|
|
return;
|
2013-07-29 23:57:50 +00:00
|
|
|
}
|
2013-07-30 01:46:45 +00:00
|
|
|
case 2: {
|
2013-08-01 06:15:37 +00:00
|
|
|
ApplyMask(var_index, masks_[var_index][var->Min() - original_min] |
|
2014-11-07 14:30:53 +00:00
|
|
|
masks_[var_index][var->Max() - original_min]);
|
2013-08-01 06:15:37 +00:00
|
|
|
return;
|
2013-07-29 23:57:50 +00:00
|
|
|
}
|
2013-07-30 01:46:45 +00:00
|
|
|
default: {
|
|
|
|
|
// We first collect the complete set of tuples to blank out in
|
|
|
|
|
// temp_mask.
|
2016-07-13 14:51:35 -07:00
|
|
|
const std::vector<uint64>& var_mask = masks_[var_index];
|
2013-07-30 21:41:29 +00:00
|
|
|
const int64 old_min = var->OldMin();
|
|
|
|
|
const int64 old_max = var->OldMax();
|
|
|
|
|
const int64 var_min = var->Min();
|
|
|
|
|
const int64 var_max = var->Max();
|
2013-08-01 06:15:37 +00:00
|
|
|
const bool contiguous = var_size == var_max - var_min + 1;
|
2014-07-21 04:53:24 +00:00
|
|
|
const bool nearly_contiguous =
|
|
|
|
|
var_size > (var_max - var_min + 1) * 7 / 10;
|
2013-07-30 01:46:45 +00:00
|
|
|
|
|
|
|
|
// Count the number of masks to collect to compare the deduction
|
|
|
|
|
// vs the construction of the new active bitset.
|
2013-08-01 06:15:37 +00:00
|
|
|
// 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;
|
2014-07-19 04:21:03 +00:00
|
|
|
if (!contiguous) {
|
|
|
|
|
for (const int64 value : InitAndGetValues(holes_[var_index])) {
|
|
|
|
|
hole_mask |= var_mask[value - original_min];
|
|
|
|
|
}
|
2013-07-30 01:46:45 +00:00
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
const int64 hole_operations = var_min - old_min + old_max - var_max;
|
2013-08-01 06:15:37 +00:00
|
|
|
// We estimate the domain iterator to be 4x slower.
|
|
|
|
|
const int64 domain_operations = contiguous ? var_size : 4 * var_size;
|
|
|
|
|
if (hole_operations < domain_operations) {
|
2013-07-30 21:41:29 +00:00
|
|
|
for (int64 value = old_min; value < var_min; ++value) {
|
2013-08-01 06:15:37 +00:00
|
|
|
hole_mask |= var_mask[value - original_min];
|
2013-07-30 01:46:45 +00:00
|
|
|
}
|
2013-07-30 21:41:29 +00:00
|
|
|
for (int64 value = var_max + 1; value <= old_max; ++value) {
|
2013-08-01 06:15:37 +00:00
|
|
|
hole_mask |= var_mask[value - original_min];
|
2013-08-01 05:35:29 +00:00
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
// We reverse the mask as this was negative information.
|
|
|
|
|
ApplyMask(var_index, ~hole_mask);
|
2013-08-01 05:35:29 +00:00
|
|
|
} else {
|
2013-08-01 06:15:37 +00:00
|
|
|
uint64 domain_mask = 0;
|
|
|
|
|
if (contiguous) {
|
|
|
|
|
for (int64 value = var_min; value <= var_max; ++value) {
|
|
|
|
|
domain_mask |= var_mask[value - original_min];
|
|
|
|
|
}
|
2014-07-21 04:53:24 +00:00
|
|
|
} else if (nearly_contiguous) {
|
|
|
|
|
for (int64 value = var_min; value <= var_max; ++value) {
|
|
|
|
|
if (var->Contains(value)) {
|
|
|
|
|
domain_mask |= var_mask[value - original_min];
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
} else {
|
2014-05-21 13:01:04 +00:00
|
|
|
for (const int64 value : InitAndGetValues(iterators_[var_index])) {
|
|
|
|
|
domain_mask |= var_mask[value - original_min];
|
2013-07-30 01:46:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 06:15:37 +00:00
|
|
|
ApplyMask(var_index, domain_mask);
|
2013-07-30 00:00:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-01 00:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2014-05-15 20:11:39 +00:00
|
|
|
return StringPrintf("SmallCompactPositiveTableConstraint([%s], %d tuples)",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-01 00:48:02 +00:00
|
|
|
private:
|
2013-08-01 06:15:37 +00:00
|
|
|
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();
|
|
|
|
|
}
|
2013-07-31 23:52:09 +00:00
|
|
|
}
|
2010-11-08 13:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
// Bitset of active tuples.
|
|
|
|
|
uint64 active_tuples_;
|
|
|
|
|
// Stamp of the active_tuple bitset.
|
2010-11-08 13:00:46 +00:00
|
|
|
uint64 stamp_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The masks per value per variable.
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<std::vector<uint64>> masks_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The min on the vars at creation time.
|
2016-07-13 14:51:35 -07:00
|
|
|
std::vector<int64> original_min_;
|
2010-11-08 13:00:46 +00:00
|
|
|
Demon* demon_;
|
2013-07-30 01:32:56 +00:00
|
|
|
int touched_var_;
|
2010-11-08 13:00:46 +00:00
|
|
|
};
|
|
|
|
|
|
2014-05-15 20:11:39 +00:00
|
|
|
bool HasCompactDomains(const std::vector<IntVar*>& vars) {
|
2016-07-13 14:51:35 -07:00
|
|
|
return true; // Always assume compact table.
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-05-20 14:01:28 +00:00
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
// ---------- Deterministic Finite Automaton ----------
|
2010-11-07 18:46:11 +00:00
|
|
|
|
2010-11-15 15:09:52 +00:00
|
|
|
// This constraint implements a finite automaton when transitions are
|
|
|
|
|
// the values of the variables in the array.
|
|
|
|
|
// that is state[i+1] = transition[var[i]][state[i]] if
|
|
|
|
|
// (state[i], var[i], state[i+1]) in the transition table.
|
|
|
|
|
// There is only one possible transition for a state/value pair.
|
2010-11-07 18:46:11 +00:00
|
|
|
class TransitionConstraint : public Constraint {
|
|
|
|
|
public:
|
2010-11-10 17:28:25 +00:00
|
|
|
static const int kStatePosition;
|
|
|
|
|
static const int kNextStatePosition;
|
|
|
|
|
static const int kTransitionTupleSize;
|
2013-07-30 21:41:29 +00:00
|
|
|
TransitionConstraint(Solver* const s, const std::vector<IntVar*>& vars,
|
|
|
|
|
const IntTupleSet& transition_table, int64 initial_state,
|
2011-05-17 20:38:55 +00:00
|
|
|
const std::vector<int64>& final_states)
|
2010-11-07 18:46:11 +00:00
|
|
|
: Constraint(s),
|
|
|
|
|
vars_(vars),
|
2012-03-15 16:22:13 +00:00
|
|
|
transition_table_(transition_table),
|
2010-11-07 18:46:11 +00:00
|
|
|
initial_state_(initial_state),
|
2012-03-15 16:22:13 +00:00
|
|
|
final_states_(final_states) {}
|
2010-11-07 18:46:11 +00:00
|
|
|
|
2013-07-30 21:41:29 +00:00
|
|
|
TransitionConstraint(Solver* const s, const std::vector<IntVar*>& vars,
|
|
|
|
|
const IntTupleSet& transition_table, int64 initial_state,
|
2011-05-20 14:01:28 +00:00
|
|
|
const std::vector<int>& final_states)
|
|
|
|
|
: Constraint(s),
|
|
|
|
|
vars_(vars),
|
2012-03-15 16:22:13 +00:00
|
|
|
transition_table_(transition_table),
|
2011-05-20 14:01:28 +00:00
|
|
|
initial_state_(initial_state),
|
|
|
|
|
final_states_(final_states.size()) {
|
|
|
|
|
for (int i = 0; i < final_states.size(); ++i) {
|
|
|
|
|
final_states_[i] = final_states[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~TransitionConstraint() override {}
|
2010-11-07 18:46:11 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-11-07 18:46:11 +00:00
|
|
|
Solver* const s = solver();
|
|
|
|
|
int64 state_min = kint64max;
|
|
|
|
|
int64 state_max = kint64min;
|
|
|
|
|
const int nb_vars = vars_.size();
|
2012-03-15 16:22:13 +00:00
|
|
|
for (int i = 0; i < transition_table_.NumTuples(); ++i) {
|
2015-08-13 16:00:54 +02:00
|
|
|
state_max =
|
|
|
|
|
std::max(state_max, transition_table_.Value(i, kStatePosition));
|
2013-07-30 21:41:29 +00:00
|
|
|
state_max =
|
|
|
|
|
std::max(state_max, transition_table_.Value(i, kNextStatePosition));
|
2015-08-13 16:00:54 +02:00
|
|
|
state_min =
|
|
|
|
|
std::min(state_min, transition_table_.Value(i, kStatePosition));
|
2013-07-30 21:41:29 +00:00
|
|
|
state_min =
|
|
|
|
|
std::min(state_min, transition_table_.Value(i, kNextStatePosition));
|
2010-11-07 18:46:11 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 20:38:55 +00:00
|
|
|
std::vector<IntVar*> states;
|
2010-11-07 18:46:11 +00:00
|
|
|
states.push_back(s->MakeIntConst(initial_state_));
|
|
|
|
|
for (int var_index = 1; var_index < nb_vars; ++var_index) {
|
|
|
|
|
states.push_back(s->MakeIntVar(state_min, state_max));
|
|
|
|
|
}
|
2010-11-15 15:09:52 +00:00
|
|
|
states.push_back(s->MakeIntVar(final_states_));
|
2010-11-07 18:46:11 +00:00
|
|
|
CHECK_EQ(nb_vars + 1, states.size());
|
|
|
|
|
|
2014-07-01 15:00:04 +00:00
|
|
|
const int num_tuples = transition_table_.NumTuples();
|
|
|
|
|
|
2010-11-07 18:46:11 +00:00
|
|
|
for (int var_index = 0; var_index < nb_vars; ++var_index) {
|
2014-07-21 04:53:24 +00:00
|
|
|
std::vector<IntVar*> tmp_vars(3);
|
|
|
|
|
tmp_vars[0] = states[var_index];
|
|
|
|
|
tmp_vars[1] = vars_[var_index];
|
|
|
|
|
tmp_vars[2] = states[var_index + 1];
|
2013-07-30 21:41:29 +00:00
|
|
|
// We always build the compact versions of the tables.
|
2016-03-08 05:07:54 -08:00
|
|
|
const ConstraintSolverParameters& params = solver()->parameters();
|
2014-07-21 04:53:24 +00:00
|
|
|
if (num_tuples <= kBitsInUint64) {
|
2013-07-30 02:13:15 +00:00
|
|
|
s->AddConstraint(s->RevAlloc(new SmallCompactPositiveTableConstraint(
|
|
|
|
|
s, tmp_vars, transition_table_)));
|
2016-03-08 05:07:54 -08:00
|
|
|
} else if (params.use_sat_table() &&
|
|
|
|
|
num_tuples > params.ac4r_table_threshold()) {
|
2014-07-01 15:00:04 +00:00
|
|
|
s->AddConstraint(
|
|
|
|
|
BuildSatTableConstraint(s, tmp_vars, transition_table_));
|
2016-03-08 05:07:54 -08:00
|
|
|
} else if (params.use_mdd_table() &&
|
|
|
|
|
num_tuples > params.ac4r_table_threshold()) {
|
2014-07-01 15:00:04 +00:00
|
|
|
s->AddConstraint(
|
|
|
|
|
BuildAc4MddResetTableConstraint(s, transition_table_, tmp_vars));
|
2013-07-30 02:13:15 +00:00
|
|
|
} else {
|
|
|
|
|
s->AddConstraint(s->RevAlloc(new CompactPositiveTableConstraint(
|
|
|
|
|
s, tmp_vars, transition_table_)));
|
|
|
|
|
}
|
2010-11-07 18:46:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kTransition, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2013-07-30 21:41:29 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kInitialState, initial_state_);
|
2011-08-11 05:15:18 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kFinalStatesArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
final_states_);
|
2011-08-11 05:15:18 +00:00
|
|
|
visitor->VisitIntegerMatrixArgument(ModelVisitor::kTuplesArgument,
|
2012-03-15 16:22:13 +00:00
|
|
|
transition_table_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kTransition, this);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2013-07-30 21:41:29 +00:00
|
|
|
return StringPrintf(
|
|
|
|
|
"TransitionConstraint([%s], %d transitions, initial = %" GG_LL_FORMAT
|
|
|
|
|
"d, final = [%s])",
|
2014-01-08 12:01:58 +00:00
|
|
|
JoinDebugStringPtr(vars_, ", ").c_str(), transition_table_.NumTuples(),
|
2014-05-13 12:56:44 +00:00
|
|
|
initial_state_, strings::Join(final_states_, ", ").c_str());
|
2012-08-14 18:00:08 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2010-11-07 18:46:11 +00:00
|
|
|
private:
|
2010-11-10 17:28:25 +00:00
|
|
|
// Variable representing transitions between states. See header file.
|
2011-05-17 20:38:55 +00:00
|
|
|
const std::vector<IntVar*> vars_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The transition as tuples (state, value, next_state).
|
2012-03-15 16:22:13 +00:00
|
|
|
const IntTupleSet transition_table_;
|
2010-11-10 17:28:25 +00:00
|
|
|
// The initial state before the first transition.
|
2010-11-07 18:46:11 +00:00
|
|
|
const int64 initial_state_;
|
2010-11-15 15:09:52 +00:00
|
|
|
// Vector of final state after the last transision.
|
2011-05-20 14:01:28 +00:00
|
|
|
std::vector<int64> final_states_;
|
2010-11-07 18:46:11 +00:00
|
|
|
};
|
|
|
|
|
|
2010-11-10 17:28:25 +00:00
|
|
|
const int TransitionConstraint::kStatePosition = 0;
|
|
|
|
|
const int TransitionConstraint::kNextStatePosition = 2;
|
|
|
|
|
const int TransitionConstraint::kTransitionTupleSize = 3;
|
2011-08-11 05:15:18 +00:00
|
|
|
} // namespace
|
2010-11-10 17:28:25 +00:00
|
|
|
|
2012-08-21 15:03:47 +00:00
|
|
|
// --------- API ----------
|
|
|
|
|
|
2013-07-30 21:41:29 +00:00
|
|
|
Constraint* Solver::MakeAllowedAssignments(const std::vector<IntVar*>& vars,
|
|
|
|
|
const IntTupleSet& tuples) {
|
2016-03-08 05:07:54 -08:00
|
|
|
if (parameters_.use_sat_table()) {
|
2014-05-23 16:54:17 +00:00
|
|
|
return BuildSatTableConstraint(this, vars, tuples);
|
|
|
|
|
}
|
2016-03-08 05:07:54 -08:00
|
|
|
if (parameters_.use_compact_table() && HasCompactDomains(vars)) {
|
|
|
|
|
if (tuples.NumTuples() < kBitsInUint64 && parameters_.use_small_table()) {
|
2012-08-21 15:03:47 +00:00
|
|
|
return RevAlloc(
|
|
|
|
|
new SmallCompactPositiveTableConstraint(this, vars, tuples));
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new CompactPositiveTableConstraint(this, vars, tuples));
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-08 05:07:54 -08:00
|
|
|
if (tuples.NumTuples() > parameters_.ac4r_table_threshold()) {
|
|
|
|
|
if (parameters_.use_mdd_table()) {
|
2014-07-01 15:00:04 +00:00
|
|
|
return BuildAc4MddResetTableConstraint(this, tuples, vars);
|
|
|
|
|
} else {
|
|
|
|
|
return BuildAc4TableConstraint(this, tuples, vars);
|
|
|
|
|
}
|
2013-07-30 21:41:29 +00:00
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new PositiveTableConstraint(this, vars, tuples));
|
|
|
|
|
}
|
2012-08-21 15:03:47 +00:00
|
|
|
}
|
|
|
|
|
|
2010-11-07 18:46:11 +00:00
|
|
|
Constraint* Solver::MakeTransitionConstraint(
|
2013-07-30 21:41:29 +00:00
|
|
|
const std::vector<IntVar*>& vars, const IntTupleSet& transition_table,
|
|
|
|
|
int64 initial_state, const std::vector<int64>& final_states) {
|
|
|
|
|
return RevAlloc(new TransitionConstraint(this, vars, transition_table,
|
|
|
|
|
initial_state, final_states));
|
2010-11-07 18:46:11 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-20 14:01:28 +00:00
|
|
|
Constraint* Solver::MakeTransitionConstraint(
|
2013-07-30 21:41:29 +00:00
|
|
|
const std::vector<IntVar*>& vars, const IntTupleSet& transition_table,
|
|
|
|
|
int64 initial_state, const std::vector<int>& final_states) {
|
|
|
|
|
return RevAlloc(new TransitionConstraint(this, vars, transition_table,
|
|
|
|
|
initial_state, final_states));
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
} // namespace operations_research
|