git-subtree-dir: software/chuffed git-subtree-split: 2ed0c01558d2a5c49c1ce57e048d32c17adf92d3
455 lines
13 KiB
C++
455 lines
13 KiB
C++
#include <algorithm>
|
|
#include <chuffed/vars/int-var.h>
|
|
#include <chuffed/core/sat.h>
|
|
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
extern std::map<IntVar*, std::string> intVarString;
|
|
|
|
IntVarEL::IntVarEL(const IntVar& other) :
|
|
IntVar(other)
|
|
, lit_min(INT_MIN)
|
|
, lit_max(INT_MIN)
|
|
, base_vlit(INT_MIN)
|
|
, base_blit(INT_MIN)
|
|
, uiv_no(-1)
|
|
{
|
|
initVals();
|
|
initVLits();
|
|
initBLits();
|
|
setVLearnable(should_be_learnable);
|
|
setBLearnable(should_be_learnable);
|
|
setVDecidable(should_be_decidable);
|
|
setBDecidable(should_be_decidable);
|
|
if (!should_be_learnable) {
|
|
setVDecidable(false);
|
|
setBDecidable(false);
|
|
}
|
|
|
|
if (so.debug) {
|
|
std::cerr << "created integer variable " << intVarString[(IntVar*)(&other)] << "\n";
|
|
if (intVarString[(IntVar*)(&other)] == "")
|
|
abort();
|
|
}
|
|
|
|
for (int v = lit_min ; v <= lit_max ; v++) {
|
|
std::string label;
|
|
std::stringstream ss;
|
|
ss << intVarString[(IntVar*)(&other)];
|
|
std::stringstream ssv;
|
|
ssv << v;
|
|
std::string val = ssv.str();
|
|
label = ss.str(); label.append("!="); label.append(val);
|
|
litString.insert(std::pair<int,std::string>(base_vlit+2*v, label));
|
|
label = ss.str(); label.append("=="); label.append(val);
|
|
litString.insert(std::pair<int,std::string>(base_vlit+2*v+1, label));
|
|
label = ss.str(); label.append(">="); label.append(val);
|
|
litString.insert(std::pair<int,std::string>(base_blit+2*v, label));
|
|
label = ss.str(); label.append("<="); label.append(val);
|
|
litString.insert(std::pair<int,std::string>(base_blit+2*v+1, label));
|
|
}
|
|
// The extra <= min-1 and >= max+1 bounds literals (both
|
|
// obviously false).
|
|
std::string label;
|
|
std::stringstream ss;
|
|
ss << intVarString[(IntVar*)(&other)] << "<=" << (lit_min-1);
|
|
litString.insert(std::pair<int,std::string>(base_blit+2*(lit_min-1)+1, ss.str()));
|
|
ss.str("");
|
|
ss << intVarString[(IntVar*)(&other)] << ">=" << (lit_max+1);
|
|
litString.insert(std::pair<int,std::string>(base_blit+2*(lit_max+1), ss.str()));
|
|
}
|
|
|
|
// lit_min is the smallest value for which we can get a literal for
|
|
// this variable. For example, if we already have "n" SAT variables,
|
|
// and lit_min=3 and lit_max=6, the SAT variables are arranged like
|
|
// this:
|
|
//
|
|
// n-5 n-4 n-3 n-2 n-1 n n+1 n+2 n+3 n+4
|
|
// X X X X X X 3 4 5 6
|
|
//
|
|
// where X is an already-in-use SAT variable, and 3,4,5.. are the SAT
|
|
// variables representing this integer variable. There are two
|
|
// literals for each variable, so the literals are arranged like this:
|
|
//
|
|
// SAT variable value literal literal index
|
|
// n-2 X
|
|
//
|
|
// n-1 X
|
|
//
|
|
// n 3 !=3 2*n
|
|
// =3 2*n+1
|
|
// n+1 4 !=4 2*(n+1)
|
|
// =4 2*(n+1)+1
|
|
// n+2 5 !=5 2*(n+2)
|
|
// =5 2*(n+2)+1
|
|
// n+3 6 !=6 2*(n+3)
|
|
// =6 2*(n+3)+1
|
|
//
|
|
// base_vlit is chosen such that base_vlit+2*v gives the !=v literal,
|
|
// i.e. base_vlit = 2*(n - lit_min).
|
|
|
|
void IntVarEL::initVLits() {
|
|
if (base_vlit != INT_MIN) return;
|
|
initVals();
|
|
if (lit_min == INT_MIN) { lit_min = min; lit_max = max; }
|
|
base_vlit = 2*(sat.nVars()-lit_min);
|
|
sat.newVar(lit_max-lit_min+1, ChannelInfo(var_id, 1, 0, lit_min));
|
|
for (int i = lit_min; i <= lit_max; i++) {
|
|
if (!indomain(i)) sat.cEnqueue(getNELit(i), NULL);
|
|
}
|
|
if (isFixed()) sat.cEnqueue(getEQLit(min), NULL);
|
|
}
|
|
|
|
// Bounds literals are arranged similarly to the value/equality
|
|
// literals above. There is one more SAT variable than there are
|
|
// values in the integer variable.
|
|
//
|
|
// SAT variable value literal literal index
|
|
// n-2 X
|
|
//
|
|
// n-1 X
|
|
//
|
|
// n 3 <3 <=2 2*n
|
|
// >=3 2*n+1
|
|
// n+1 4 <4 <=3 2*(n+1)
|
|
// >=4 2*(n+1)+1
|
|
// n+2 5 <5 <=4 2*(n+2)
|
|
// >=5 2*(n+2)+1
|
|
// n+3 6 <6 <=5 2*(n+3)
|
|
// >=6 2*(n+3)+1
|
|
// n+4 7 <7 <=6 2*(n+4)
|
|
// >=7 2*(n+4)+1
|
|
//
|
|
// base_blit is chosen such that base_blit+2*v gives the >=v literal,
|
|
// and base_blit+2*v+1 gives the <=v literal. Note that these are not
|
|
// negations of one another, and that base_vlit is odd!
|
|
// base_blit = 2*(n - lit_min) + 1.
|
|
|
|
void IntVarEL::initBLits() {
|
|
if (base_blit != INT_MIN) return;
|
|
if (lit_min == INT_MIN) { lit_min = min; lit_max = max; }
|
|
base_blit = 2*(sat.nVars()-lit_min)+1;
|
|
sat.newVar(lit_max-lit_min+2, ChannelInfo(var_id, 1, 1, lit_min-1));
|
|
for (int i = lit_min; i <= min; i++) {
|
|
sat.cEnqueue(getGELit(i), NULL);
|
|
}
|
|
for (int i = max; i <= lit_max; i++) {
|
|
sat.cEnqueue(getLELit(i), NULL);
|
|
}
|
|
}
|
|
|
|
void IntVarEL::setVLearnable(bool b) {
|
|
for (int i = lit_min; i <= lit_max; i++) {
|
|
sat.flags[base_vlit/2+i].setLearnable(b);
|
|
sat.flags[base_vlit/2+i].setUIPable(b);
|
|
}
|
|
}
|
|
|
|
void IntVarEL::setBLearnable(bool b) {
|
|
for (int i = lit_min; i <= lit_max+1; i++) {
|
|
sat.flags[(base_blit-1)/2+i].setLearnable(b);
|
|
sat.flags[(base_blit-1)/2+i].setUIPable(b);
|
|
}
|
|
}
|
|
|
|
void IntVarEL::setVDecidable(bool b) {
|
|
for (int i = lit_min; i <= lit_max; i++) {
|
|
sat.flags[base_vlit/2+i].setDecidable(b);
|
|
}
|
|
}
|
|
|
|
void IntVarEL::setBDecidable(bool b) {
|
|
for (int i = lit_min; i <= lit_max+1; i++) {
|
|
sat.flags[(base_blit-1)/2+i].setDecidable(b);
|
|
}
|
|
}
|
|
|
|
|
|
Lit IntVarEL::getLit(int64_t v, int t) {
|
|
// std::cerr << "IntVarEL::getLit\n";
|
|
if (v < lit_min) return toLit(1^(t&1)); // 1, 0, 1, 0
|
|
if (v > lit_max) return toLit(((t-1)>>1)&1); // 1, 0, 0, 1
|
|
switch (t) {
|
|
case 0: return getNELit(v);
|
|
case 1: return getEQLit(v);
|
|
case 2: return getGELit(v);
|
|
case 3: return getLELit(v);
|
|
default: NEVER;
|
|
}
|
|
}
|
|
|
|
// Use when you've just set [x >= v]
|
|
inline void IntVarEL::channelMin(int v) {
|
|
// Set [x >= v-1] to [x >= min+1] using [x >= i] \/ ![x >= v]
|
|
// Set [x != v-1] to [x != min] using [x != i] \/ ![x >= v]
|
|
Reason r(~getGELit(v));
|
|
for (int i = v-1; i > min; i--) {
|
|
sat.cEnqueue(getGELit(i), r);
|
|
if (vals[i]) sat.cEnqueue(getNELit(i), r);
|
|
}
|
|
assert(vals[min]);
|
|
sat.cEnqueue(getNELit(min), r);
|
|
}
|
|
|
|
// Use when you've just set [x <= v]
|
|
inline void IntVarEL::channelMax(int v) {
|
|
// Set [x <= v+1] to [x <= max-1] to using [x <= i] \/ ![x <= v]
|
|
// Set [x != v+1] to [x != max] to using ![x = i] \/ ![x <= v]
|
|
Reason r(~getLELit(v));
|
|
for (int i = v+1; i < max; i++) {
|
|
sat.cEnqueue(getLELit(i), r);
|
|
if (vals[i]) sat.cEnqueue(getNELit(i), r);
|
|
}
|
|
assert(vals[max]);
|
|
sat.cEnqueue(getNELit(max), r);
|
|
}
|
|
|
|
// Use when you've just set [x = v]
|
|
inline void IntVarEL::channelFix(int v) {
|
|
Reason r(getNELit(v));
|
|
if (min < v) {
|
|
// Set [x >= v] using [x >= v] \/ ![x = v]
|
|
sat.cEnqueue(getGELit(v), r);
|
|
channelMin(v);
|
|
}
|
|
if (max > v) {
|
|
// Set [x <= v] using [x <= v] \/ ![x = v]
|
|
sat.cEnqueue(getLELit(v), r);
|
|
channelMax(v);
|
|
}
|
|
}
|
|
|
|
#if INT_DOMAIN_LIST
|
|
inline void IntVarEL::updateMin(int v, int i) {
|
|
for (; v < i; ++v) {
|
|
// Set [x >= v+1] using [x >= v+1] \/ [x <= v-1] \/ [x = v]
|
|
Reason r(getLELit(v-1), getEQLit(v));
|
|
sat.cEnqueue(getGELit(v+1), r);
|
|
}
|
|
min = v; changes |= EVENT_C | EVENT_L;
|
|
}
|
|
|
|
inline void IntVarEL::updateMax(int v, int i) {
|
|
for (; v > i; --v) {
|
|
// Set [x <= v-1] using [x <= v-1] \/ [x >= v+1] \/ [x = v]
|
|
Reason r(getGELit(v+1), getEQLit(v));
|
|
sat.cEnqueue(getLELit(v-1), r);
|
|
}
|
|
max = v; changes |= EVENT_C | EVENT_U;
|
|
}
|
|
#else
|
|
inline void IntVarEL::updateMin() {
|
|
int v = min;
|
|
while (!vals[v]) {
|
|
// Set [x >= v+1] using [x >= v+1] \/ [x <= v-1] \/ [x = v]
|
|
Reason r(getLELit(v-1), getEQLit(v));
|
|
sat.cEnqueue(getGELit(v+1), r);
|
|
v++;
|
|
}
|
|
if (v > min) { min = v; changes |= EVENT_L; }
|
|
}
|
|
|
|
inline void IntVarEL::updateMax() {
|
|
int v = max;
|
|
while (!vals[v]) {
|
|
// Set [x <= v-1] using [x <= v-1] \/ [x >= v+1] \/ [x = v]
|
|
Reason r(getGELit(v+1), getEQLit(v));
|
|
sat.cEnqueue(getLELit(v-1), r);
|
|
v--;
|
|
}
|
|
if (v < max) { max = v; changes |= EVENT_U; }
|
|
}
|
|
#endif
|
|
|
|
inline void IntVarEL::updateFixed() {
|
|
if (isFixed()) {
|
|
int v = min;
|
|
// Set [x = v] using [x = v] \/ [x <= v-1] \/ [x >= v+1]
|
|
Reason r(getLELit(v-1), getGELit(v+1));
|
|
sat.cEnqueue(getEQLit(v), r);
|
|
changes |= EVENT_F;
|
|
}
|
|
}
|
|
|
|
bool IntVarEL::setMin(int64_t v, Reason r, bool channel) {
|
|
assert(setMinNotR(v));
|
|
if (channel) sat.cEnqueue(getLit(v, 2), r);
|
|
if (v > max) { assert(sat.confl); return false; }
|
|
channelMin(v);
|
|
#if INT_DOMAIN_LIST
|
|
int i;
|
|
int j = vals_count;
|
|
for (i = min; i < v; i = vals_list[2*i+1])
|
|
--j;
|
|
updateMin(v, i);
|
|
vals_count = j;
|
|
#else
|
|
min = v; changes |= EVENT_C | EVENT_L;
|
|
updateMin();
|
|
#endif
|
|
updateFixed();
|
|
pushInQueue();
|
|
return true;
|
|
}
|
|
|
|
bool IntVarEL::setMax(int64_t v, Reason r, bool channel) {
|
|
assert(setMaxNotR(v));
|
|
if (channel) sat.cEnqueue(getLit(v, 3), r);
|
|
if (v < min) { assert(sat.confl); return false; }
|
|
channelMax(v);
|
|
#if INT_DOMAIN_LIST
|
|
int i;
|
|
int j = vals_count;
|
|
for (i = max; i > v; i = vals_list[2*i])
|
|
--j;
|
|
updateMax(v, i);
|
|
vals_count = j;
|
|
#else
|
|
max = v; changes |= EVENT_C | EVENT_U;
|
|
updateMax();
|
|
#endif
|
|
updateFixed();
|
|
pushInQueue();
|
|
return true;
|
|
}
|
|
|
|
bool IntVarEL::setVal(int64_t v, Reason r, bool channel) {
|
|
assert(setValNotR(v));
|
|
if (channel) sat.cEnqueue(getLit(v, 1), r);
|
|
if (!indomain(v)) { assert(sat.confl); return false; }
|
|
changes |= EVENT_C | EVENT_F;
|
|
channelFix(v);
|
|
if (min < v) { min = v; changes |= EVENT_L; }
|
|
if (max > v) { max = v; changes |= EVENT_U; }
|
|
#if INT_DOMAIN_LIST
|
|
vals_count = 1;
|
|
#endif
|
|
pushInQueue();
|
|
return true;
|
|
}
|
|
|
|
bool IntVarEL::remVal(int64_t v, Reason r, bool channel) {
|
|
assert(remValNotR(v));
|
|
assert(vals);
|
|
if (channel) sat.cEnqueue(getLit(v, 0), r);
|
|
if (isFixed()) { assert(sat.confl); return false; }
|
|
#if INT_DOMAIN_LIST
|
|
if (v == min)
|
|
updateMin(min, vals_list[2*min+1]);
|
|
else if (v == max)
|
|
updateMax(max, vals_list[2*max]);
|
|
else {
|
|
vals[v] = 0;
|
|
vals_list[vals_list[2*v]*2+1] = vals_list[2*v+1];
|
|
vals_list[vals_list[2*v+1]*2] = vals_list[2*v];
|
|
changes |= EVENT_C;
|
|
}
|
|
--vals_count;
|
|
#else
|
|
changes |= EVENT_C;
|
|
vals[v] = 0;
|
|
updateMin();
|
|
updateMax();
|
|
#endif
|
|
updateFixed();
|
|
pushInQueue();
|
|
return true;
|
|
}
|
|
|
|
Lit IntVarEL::createSetLit(vec<Lit>& head) {
|
|
// lb, ub and holes, which together cause the conflict
|
|
int lower_bound = lit_min;
|
|
int upper_bound = lit_max;
|
|
vec<int> holes;
|
|
|
|
std::sort((Lit*) head, (Lit*) head + head.size());
|
|
|
|
// process bounds lits first
|
|
for (int i = 0; i < head.size(); i++) {
|
|
ChannelInfo& ci = sat.c_info[var(head[i])];
|
|
if (ci.val_type == 0) continue;
|
|
int v = ci.val;
|
|
if (sign(head[i])) {
|
|
if (v < upper_bound) upper_bound = v;
|
|
} else {
|
|
if (v+1 > lower_bound) lower_bound = v+1;
|
|
}
|
|
}
|
|
|
|
// process val lits
|
|
for (int i = 0; i < head.size(); i++) {
|
|
ChannelInfo& ci = sat.c_info[var(head[i])];
|
|
if (ci.val_type == 1) continue;
|
|
int v = ci.val;
|
|
if (sign(head[i])) {
|
|
if (v < lower_bound || v > upper_bound) continue;
|
|
if (v == lower_bound) {
|
|
lower_bound++; continue;
|
|
}
|
|
if (v == upper_bound) {
|
|
upper_bound--; continue;
|
|
}
|
|
holes.push(v);
|
|
} else {
|
|
return getNELit(v);
|
|
}
|
|
}
|
|
|
|
if (lower_bound > upper_bound) {
|
|
printf("Domain wipeout??\n");
|
|
assert(false);
|
|
}
|
|
|
|
// printf("lower_bound = %d, upper_bound = %d\n", lower_bound, upper_bound);
|
|
// for (int i = 0; i < holes.size(); i++) printf("%d ", holes[i]); printf("\n");
|
|
|
|
fflush(stdout);
|
|
|
|
// Check cases where an original lit is sufficient
|
|
|
|
if (lower_bound == lit_min && holes.size() == 0) {
|
|
return getGELit(upper_bound+1);
|
|
}
|
|
if (upper_bound == lit_max && holes.size() == 0) {
|
|
return getLELit(lower_bound-1);
|
|
}
|
|
|
|
// Create new lit that complements failure set
|
|
|
|
Lit q = Lit(sat.getLazyVar(ci_null), true);
|
|
sat.flags[var(q)].setUIPable(false);
|
|
sat.flags[var(q)].setLearnable(false);
|
|
|
|
vec<Lit> ps(2);
|
|
ps[1] = ~q;
|
|
|
|
if (lower_bound == lit_min) {
|
|
// can use bounds lit for lower bound
|
|
sat.addClause(getGELit(holes[0]), ~q);
|
|
lower_bound = holes[0];
|
|
// printf("GE: %d\n", holes[0]);
|
|
}
|
|
if (upper_bound == lit_max) {
|
|
// can use bounds lit for upper bound
|
|
sat.addClause(getLELit(holes.last()), ~q);
|
|
upper_bound = holes.last();
|
|
// printf("LE: %d\n", holes.last());
|
|
}
|
|
int j = 0;
|
|
for (int i = lower_bound; i <= upper_bound; i++) {
|
|
if (j < holes.size() && i == holes[j]) { j++; continue; }
|
|
ps[0] = getNELit(i);
|
|
sat.addClause(*Clause_new(ps), true);
|
|
// printf("NE: %d\n", i);
|
|
}
|
|
|
|
// printf("New lit = %d\n", toInt(q));
|
|
|
|
return q;
|
|
}
|
|
|
|
|
|
|