1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 35a3110598 Squashed 'software/chuffed/' content from commit 2ed0c015
git-subtree-dir: software/chuffed
git-subtree-split: 2ed0c01558d2a5c49c1ce57e048d32c17adf92d3
2021-06-18 09:36:35 +10:00

1126 lines
48 KiB
C++

#include <chuffed/core/propagator.h>
#include <chuffed/vars/modelling.h>
// a[i] forms a circuit
// Value consistent propagator
template <int U = 0>
class Circuit : public Propagator {
public:
bool const useCheck; // use the 'check' algorithm
bool const usePrevent; // use the 'prevent' algorithm
bool const useScc; // use the 'scc' algorithm
bool const pruneRoot; // use 'prune root' option for scc algorithm
bool const pruneSkip; // prune arcs that skip subtrees (in scc)
bool const fixReq; // fix required back edges (in scc)
bool const generaliseScc; // use 'prune within' option for scc
int const size; // total number of nodes
IntView<U> * const x; // successor variables
// Persistent state
// Intermediate state
vec<int> new_fixed;
vec<int> prev; // scc alg - vars in last subtree visited
vec<int> earlier; // scc alg - vars in the earlier subtrees
vec<int> later; // scc alg - vars in later subtrees
vec<int> preRoot; // contains indices of all vars collapsed into root node
int root;
// struct for recording propagations to be done
struct PROP
{
Clause* reason;
int var; // the variable whose domain will be changed
int val; // the relevant domain member
bool fix; // true - use setVal, false - use remVal
};
vec<PROP> propQueue;
int *index;
int *lowlink;
int nodesSeen;
// for prevent algorithm
int *end;
int *lengthChain;
Circuit(vec<IntView<U> > _x):
useCheck(so.circuitalg < 4),
usePrevent(so.circuitalg >= 2 && so.circuitalg < 4),
useScc(so.circuitalg >= 3),
pruneRoot(so.sccoptions >= 3),
pruneSkip(true),
fixReq(true),
generaliseScc(so.sccoptions == 2 || so.sccoptions == 4),
size(_x.size()),
x(_x.release())
{
srand(so.rnd_seed);
priority = 5;
new_fixed.reserve(size);
prev.reserve(size);
earlier.reserve(size);
later.reserve(size);
index = (int*) malloc(size * sizeof(int));
lowlink = (int*) malloc(size * sizeof(int));
end = (int*) malloc(size * sizeof(int));
lengthChain = (int*) malloc(size * sizeof(int));
if(useScc)
for (int i = 0; i < size; i++) x[i].attach(this, i, EVENT_C);
else
for (int i = 0; i < size; i++) x[i].attach(this, i, EVENT_F);
}
void wakeup(int i, int c) {
if (c & EVENT_F) new_fixed.push(i);
pushInQueue();
}
// 'prevent' algorithm
// Prunes that partial assigned paths are not completed to cycles
bool cyclePrevent() {
// The path starting at assigned x[i] ends at x[end[j]] which is
// not assigned.
for (int i=0; i < size; i++)
{
end[i]=-1;
lengthChain[i]=-1;
}
for (int unfixedVar=0; unfixedVar < size; unfixedVar++) {
if(!x[unfixedVar].isFixed())
{
// Non-assigned views serve as starting points for assigned paths
// Try all connected values
for (typename IntView<U>::iterator it = x[unfixedVar].begin(); it != x[unfixedVar].end(); ++it)
{
// Starting point for not yet followed assigned path found
int j0=*it;
if (x[j0].isFixed() && (lengthChain[j0] < 0)) {
// Follow assigned path until non-assigned view:
// all assigned view on the paths can be skipped, as
// if x[i] is assigned to j, then x[j] will only have
// x[i] as predecessor due to propagating distinct.
int j = j0;
int chainLength = 1;
do {
j=x[j].getVal();
chainLength++;
} while (x[j].isFixed());
// Now there cannot be a link from x[j] to x[j0]
// so record the chain length and we will propagate afterwards
lengthChain[j0]=chainLength;
end[j0] = j;
}
}
}
}
// now propagate the disallowed links we found
for(int j0 = 0; j0 < size; j0++)
{
if(lengthChain[j0] > 0)
{
Clause* r = NULL;
if (so.lazy) {
if(so.prevexpl == 1)
{
r = Reason_new(lengthChain[j0]);
int j = j0;
for(int index = 1 ; index < lengthChain[j0]; index++)
{
(*r)[index] = x[j].getValLit();
j = x[j].getVal();
}
}
else
{
// find the vars in the start of the chain and those not in the chain
vec<int> inStartChain;
vec<int> outside;
for(int i = 0; i < size; i++)
if(i != end[j0])
outside.push(i);
int j = j0;
for(int index = 1 ; index < lengthChain[j0]; index++)
{
inStartChain.push(j);
outside.remove(j);
j = x[j].getVal();
}
assert(inStartChain.size() + outside.size() + 1 == size);
// reason is nothing in start of chain can reach outside chain
r = Reason_new(inStartChain.size() * outside.size() + 1);
explainAcantreachB(r, 1, 1+inStartChain.size()*outside.size(),inStartChain, outside);
}
}
if (x[end[j0]].remValNotR(j0))
{
//fprintf(stderr, "setting %d != %d\n", end[j0], j0);
if(!x[end[j0]].remVal(j0, r))
return false;
}
}
}
return true;
}
// Add literals to reason to explain that no node in set A
// reaches a node in set B
// If there's a node a1 in A and a node b1 in B, the literal
// involving those nodes is not included
// checks the index after the last literal is added
// (for debugging purposes)
void explainAcantreachB(Clause *reason, int reasonIndex,
int expectedEndIndex, vec<int> A, vec<int> B, int a1=-1, int b1=-1)
{
// fprintf(stderr, "explaining");
bool found = false;
for(int a = 0; a < A.size(); a++)
{
assert(A[a] < size && A[a] >= 0);
for(int b = 0; b < B.size(); b++)
{
assert(B[b] < size && B[b] >= 0);
if(A[a] != a1 || B[b] != b1)
{
assert(!x[A[a]].indomain(B[b]));
// fprintf(stderr, "add %d not equal to %d\n", A[a], B[b]);
(*reason)[reasonIndex++] = ~x[A[a]].getLit(B[b], 0);
}
else found = true;
}
}
assert(found || (a1==-1 && b1==-1));
assert(reasonIndex == expectedEndIndex);
}
void addEqLits(Clause *r)
{
if(so.rootSelection == 5 || so.rootSelection == 6)
{
for(int i = 0; i < preRoot.size(); i++)
(*r)[i+1] = x[preRoot[i]].getValLit();
}
}
void addPropagation(bool fix, int var, int val, Clause *r)
{
PROP newprop;
newprop.reason = r;
newprop.var = var;
newprop.val = val;
newprop.fix = fix;
propQueue.push(newprop);
}
bool exploreSubtree(int thisNode, int startPrevSubtree, int endPrevSubtree, int *backfrom, int *backto, int *numback)
{
//fprintf(stderr,"exploring subtree\n");
index[thisNode] = nodesSeen++;
lowlink[thisNode] = index[thisNode];
bool isFirstChild = true;
int child;
for (typename IntView<U>::iterator i = x[thisNode].begin(); i != x[thisNode].end(); ++i)
{
child = *i;
// If we haven't visited the child yet, do so now
if(index[child] == -1)
{
//fprintf(stderr,"new child %d\n", child);
if(!exploreSubtree(child, startPrevSubtree, endPrevSubtree, backfrom, backto, numback))
return false; // fail if there was an scc contained within this child
if(lowlink[child] < lowlink[thisNode])
lowlink[thisNode] = lowlink[child];
// If this is the first child and its lowlink is equal to the
// parent's index, we can prune the edge from this node to the
// child (as the circuit must come back up through an edge
// from the child subtree to this node and therefore we can't
// touch this node on the way down).
// We can also prune any edges from a node outside the child's
// subtree to this node, with the same reason.
if(generaliseScc && isFirstChild && lowlink[child] == index[thisNode])
{
//fprintf(stderr, "gen\n");
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.prunedGeneralise++;
// The reason is that no node in the child's subtree reaches
// a node outside the subtree that isn't the parent
// First divide the nodes into those inside and outside
// the child's subtree
vec<int> inside;
vec<int> outside;
for(int i = 0; i < size; i++)
if(index[i] >= index[child])
inside.push(i);
else if(i != thisNode)
outside.push(i);
assert(outside.size() > 0);
assert(inside.size() + outside.size() == size - 1);
/*fprintf(stderr, " this node is %d, it's index is %d\n", thisNode, index[thisNode]);
fprintf(stderr, "inside:\n");
for(int i = 0; i < inside.size(); i++)
fprintf(stderr, "node %d with index %d\n", inside[i], index[inside[i]]);
fprintf(stderr, "outside:\n");
for(int i = 0; i < outside.size(); i++)
fprintf(stderr, "node %d with index %d\n", outside[i], index[outside[i]]); */
Clause *r = NULL;
if(so.lazy)
{
r = Reason_new(inside.size() * outside.size()+1+preRoot.size());
addEqLits(r);
explainAcantreachB(r, preRoot.size()+1, inside.size() * outside.size()+1+preRoot.size(), inside, outside);
}
addPropagation(false, thisNode, child, r);
for(int i = 0; i < outside.size(); i++)
{
if(x[outside[i]].remValNotR(thisNode))
{
//fprintf(stderr, "setting x[%d] neq %d\n", outside[i], thisNode);
addPropagation(false, outside[i], thisNode, r);
}
}
}
isFirstChild = false; // We've now visited at least one child
}
else // This is a node we've seen before
{
//fprintf(stderr,"old child %d\n", child);
// If child is within the last subtree we've found a backedge (from this node to the child)
if (index[child] >= startPrevSubtree && index[child] <= endPrevSubtree ){
// fprintf(stderr, "found back edge from %d to %d\n", thisNode, child);
// If this is an edge back to a root node which isn't the first
// then it will be pruned by alldiff later, so just ignore it
if(index[child] != 0 || child == root)
{
(*numback)++;
*backfrom = thisNode;
*backto = child;
}
}
// If w is from a subtree before the previous one prune this edge (if that option is set)
else if (pruneSkip && index[child] < startPrevSubtree ){
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.prunedSkip++;
Clause *r = NULL;
if(so.lazy)
{
// The reason is that no node in an earlier subtree can
// reach the prev or later subtrees, and no node
// in the prev subtree can reach later subtrees.
vec<int> prevAndLater;
prevAndLater.reserve(prev.size() + later.size());
for(int i = 0; i < prev.size(); i++) prevAndLater.push(prev[i]);
for(int i = 0; i < later.size(); i++) prevAndLater.push(later[i]);
r = Reason_new(earlier.size() * prevAndLater.size() + prev.size() * later.size() + 1 + preRoot.size());
addEqLits(r);
int size1 = prev.size() * later.size();
int size2 = earlier.size() * prevAndLater.size();
explainAcantreachB(r, preRoot.size()+1, 1+size1+preRoot.size(), prev, later);
explainAcantreachB(r, preRoot.size()+1+size1, 1+size1+size2+preRoot.size(), earlier, prevAndLater);
}
addPropagation(false, thisNode, child, r);
}
if(index[child] < lowlink[thisNode])
lowlink[thisNode] = index[child];
}
//fprintf(stderr,"lowpoint is %d\n", lowlink[thisNode]);
}
// Fail if there's an scc rooted here
if(lowlink[thisNode] == index[thisNode])
{
//fprintf(stderr, "multi scc\n");
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.multipleSCC++;
Clause *r = NULL;
if(so.lazy)
{
// The reason is that no node in the subtree rooted by this node (including this one)
// reaches a node outside that subtree.
// Start by finding the nodes inside and outside the subtree
vec<int> inside;
vec<int> outside;
for(int i = 0; i < size; i++)
{
if(index[i] >= index[thisNode])
inside.push(i);
else
outside.push(i);
}
/*fprintf(stderr, "nodes inside scc rooted at %d:\n", thisNode);
for(int i = 0; i < inside.size(); i++)
fprintf(stderr, "%d", inside[i]);
fprintf(stderr, "nodes outside scc:\n");
for(int i = 0; i < outside.size(); i++)
fprintf(stderr, "%d with index %d", outside[i], index[i]);*/
r = Reason_new(inside.size() * outside.size()+preRoot.size());
addEqLits(r);
explainAcantreachB(r, 1+preRoot.size(), inside.size() * outside.size()+preRoot.size(), inside, outside, inside[0], outside[0]);
x[inside[0]].setVal(outside[0], r);
}
return false;
}
return true;
}
//(1-first non-fixed, 2-random non-fixed, 3-end of shortest chain, 4-end of longest chain,
// 5- start of shortest chain (+ collapse), 6-start of longest chain (+collapse))
// 7- first (even if fixed), 8 - random (even if fixed), 9-largest domain, 10-all
int chooseRoot() {
vec<int> chainStarts; // indices which no var is fixed to
for(int i = 0; i < size; i++)
chainStarts.push(i);
for(int i = 0; i < size; i++)
if(x[i].isFixed())
chainStarts.remove(x[i].getVal());
// if there are no chain starts everything is fixed, so just return
// if we haven't already run check, do that first
if(chainStarts.size() == 0)
return -1;
// now for each chain find the length and the end variable
vec<int> chainLengths;
vec<int> chainEnds;
for(int i = 0; i < chainStarts.size(); i++)
{
int length = 1;
int currIndex = chainStarts[i];
while(x[currIndex].isFixed())
{
length++;
currIndex = x[currIndex].getVal();
}
chainLengths.push(length);
chainEnds.push(currIndex);
}
root = -1;
int len;
int chosenChain;
switch(so.rootSelection)
{
case 1: // first non-fixed
for(int i = 0; i < size; i++)
if(!x[i].isFixed())
{
root = i;
break;
}
break;
case 2: // random non-fixed
// has to be one of the chain ends
chosenChain = (int) ( ((double)chainEnds.size()) * rand() / (RAND_MAX + 1.0) );
//fprintf(stderr, "chose %d\n", chosenChain);
root = chainEnds[chosenChain];
//fprintf(stderr, "root is %d\n", root);
break;
case 3: // end of shortest chain,
len = chainLengths[0];
root = chainEnds[0];
for(int i = 1; i < chainLengths.size(); i++)
{
if(chainLengths[i] < len)
{
len = chainLengths[i];
root = chainEnds[i];
}
}
break;
case 4: // end of longest chain,
len = chainLengths[0];
root = chainEnds[0];
for(int i = 1; i < chainLengths.size(); i++)
{
if(chainLengths[i] > len)
{
len = chainLengths[i];
root = chainEnds[i];
}
}
break;
case 5: // start of shortest chain (+ collapse)
len = chainLengths[0];
root = chainStarts[0];
chosenChain = 0;
for(int i = 1; i < chainLengths.size(); i++)
{
if(chainLengths[i] < len)
{
len = chainLengths[i];
root = chainStarts[i];
chosenChain = i;
}
}
break;
case 6: // start of longest chain (+collapse)
len = chainLengths[0];
root = chainStarts[0];
chosenChain = 0;
for(int i = 1; i < chainLengths.size(); i++)
{
if(chainLengths[i] > len)
{
len = chainLengths[i];
root = chainStarts[i];
chosenChain = i;
}
}
break;
case 7: // first (even if fixed)
root = 0;
break;
case 8: // random (even if fixed)
root = (int) ( ((double)size) * rand() / (RAND_MAX + 1.0) );
break;
case 9: // largest domain
len = x[0].size();
root = 0;
for(int i=1; i < size; i++)
if(x[i].size() > len)
{
len = x[i].size();
root = i;
}
break;
}
assert(root >=0);
return root;
}
bool circuitSCC(int rootEnd) {
root = rootEnd;
int backfrom = 0; // the last back edge found
int backto = 0;
int numback = 0; // the number of back edges found
vec<int> thisSubtree;
if(so.lazy)
{
thisSubtree.reserve(size);
prev.clear();
earlier.clear();
later.clear();
}
for(int i = 0; i < size; i++)
index[i] = -1; // unvisited
index[root] = 0; // first node visited
lowlink[root] = 0;
nodesSeen = 1; // only seen root node
if(so.rootSelection == 5 || so.rootSelection == 6)
{
preRoot.clear();
while(x[rootEnd].isFixed())
{
//fprintf(stderr, "preroot %d\n", rootEnd);
preRoot.push(rootEnd);
rootEnd = x[rootEnd].getVal();
index[rootEnd] = 0;
nodesSeen++;
lowlink[rootEnd] = 0;
}
}
// fprintf(stderr, "rootEnd %d\n", rootEnd);
propQueue.clear();
// original subtree is just the root node
int startSubtree = 0;
int endSubtree = 0;
// explore children of the root node
for (typename IntView<U>::iterator i = x[rootEnd].begin(); i != x[rootEnd].end(); ++i)
{
int child = *i;
if(index[child] == -1) // if haven't explored this yet
{
numback = 0;
if(!exploreSubtree(child, startSubtree, endSubtree, &backfrom, &backto, &numback))
{
//fprintf(stderr, "failed in subtree\n");
return false; // fail if we found a scc within the child
}
// Find the nodes in the subtree we just explored and the ones still to be explored
if(so.lazy)
{
thisSubtree.clear();
later.clear();
for(int i = 0; i < size; i++)
{
if(index[i] < 0)
later.push(i);
else if(index[i] > endSubtree)
thisSubtree.push(i);
}
}
// If there's no edge from the new subtree back to the previous one, fail
if(numback == 0)
{
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.nobackedge++;
if(so.lazy)
{
// If prev is empty then this is the first subtree,
// and we just need to say that the nodes in this
// subtree can't reach the unexplored nodes or the root.
if(prev.size() == 0)
{
vec<int> alloutside;
for(int i = 0; i < later.size(); i++) alloutside.push(later[i]);
alloutside.push(root);
assert(thisSubtree.size() + alloutside.size() + preRoot.size() == size);
Clause *r = Reason_new(thisSubtree.size() * alloutside.size()+preRoot.size());
addEqLits(r);
// This subtree can't reach outside
explainAcantreachB(r, 1+preRoot.size(), thisSubtree.size() * alloutside.size()+preRoot.size(), thisSubtree, alloutside, thisSubtree[0], alloutside[0]);
x[thisSubtree[0]].setVal(alloutside[0], r);
return false;
}
// Assuming we have at least one previous subtree,
// the reason we're failing is that
// 1. nodes in earlier subtrees can't reach the
// previous one this one or unexplored ones,
// 2. nodes in the previous subtree can't reach
// this one or unexplored ones,
// 3. and nodes in this subtree can't reach the
// previous one or unexplored ones.
vec<int> prev_later;
for(int i = 0; i < prev.size(); i++) prev_later.push(prev[i]);
for(int i = 0; i < later.size(); i++) prev_later.push(later[i]);
vec<int> prev_this_later;
for(int i = 0; i < prev_later.size(); i++) prev_this_later.push(prev_later[i]);
for(int i = 0; i < thisSubtree.size(); i++) prev_this_later.push(thisSubtree[i]);
vec<int> this_later;
for(int i = 0; i < thisSubtree.size(); i++) this_later.push(thisSubtree[i]);
for(int i = 0; i < later.size(); i++) this_later.push(later[i]);
int size1 = earlier.size() * prev_this_later.size();
int size2 = prev.size() * this_later.size();
int size3 = thisSubtree.size() * prev_later.size();
assert(prev_later.size() > 0);
assert(earlier.size() + prev.size() + later.size() + thisSubtree.size() +preRoot.size() == size - 1);
Clause *r = Reason_new(size1 + size2 + size3+preRoot.size());
addEqLits(r);
// 1. Earlier can't reach prev, this or later
explainAcantreachB(r, 1+preRoot.size(), 1+size1+preRoot.size(), earlier, prev_this_later);
// 2. Prev can't reach this or later
explainAcantreachB(r, 1 + size1+preRoot.size(), 1+size1+size2+preRoot.size(), prev, this_later);
// 3. this can't reach prev or unexplored
explainAcantreachB(r, 1 + size1 + size2+preRoot.size(), size1+size2+size3+preRoot.size(), thisSubtree, prev_later, thisSubtree[0], prev_later[0]);
x[thisSubtree[0]].setVal(prev_later[0], r);
}
return false;
}
// If there's only one, the back edge is required (only propagate if the option is set)
if(fixReq && numback == 1)
{
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.fixedBackedge++;
Clause *r = NULL;
if(so.lazy)
{
// If this is the first subtree, the reason we're setting this link is that
// there is no other link between nodes of this subtree to nodes outside the subtree including the root
if(prev.size() == 0)
{
vec<int> alloutside;
for(int i = 0; i < later.size(); i++) alloutside.push(later[i]);
alloutside.push(root);
r = Reason_new(thisSubtree.size() * alloutside.size()+preRoot.size());
addEqLits(r);
assert(thisSubtree.size() + alloutside.size() +preRoot.size()== size);
// This subtree can't reach outside (except backfrom to backto)
explainAcantreachB(r, 1+preRoot.size(), thisSubtree.size() * alloutside.size()+preRoot.size(), thisSubtree, alloutside, backfrom, backto);
//fprintf(stderr, "first subtree\n");
}
else
{
// Otherwise we have at least one previous subtree.
//The reason is the same as when failing above
vec<int> prev_later;
for(int i = 0; i < prev.size(); i++) prev_later.push(prev[i]);
for(int i = 0; i < later.size(); i++) prev_later.push(later[i]);
vec<int> prev_this_later;
for(int i = 0; i < prev_later.size(); i++) prev_this_later.push(prev_later[i]);
for(int i = 0; i < thisSubtree.size(); i++) prev_this_later.push(thisSubtree[i]);
vec<int> this_later;
for(int i = 0; i < thisSubtree.size(); i++) this_later.push(thisSubtree[i]);
for(int i = 0; i < later.size(); i++) this_later.push(later[i]);
int size1 = earlier.size() * prev_this_later.size();
int size2 = prev.size() * this_later.size();
int size3 = thisSubtree.size() * prev_later.size();
assert(prev_later.size() > 0);
r = Reason_new(size1 + size2 + size3+preRoot.size());
addEqLits(r);
// 1. Earlier can't reach prev, this or unexplored
explainAcantreachB(r, 1+preRoot.size(), 1+size1+preRoot.size(), earlier, prev_this_later);
// 2. Prev can't reach this or unexplored
explainAcantreachB(r, 1 + size1+preRoot.size(), 1+size1+size2+preRoot.size(), prev, this_later);
// 3. this can't reach prev or unexplored (except for the one we're setting)
explainAcantreachB(r, 1+size1+size2+preRoot.size(), size1+size2+size3+preRoot.size(), thisSubtree, prev_later, backfrom, backto);
assert(earlier.size() + prev.size() + later.size() + thisSubtree.size() +preRoot.size() == size - 1);
}
}
addPropagation(true, backfrom, backto, r);
}
// When a new subtree has been explored, update the prev, earlier and later vectors (only necessary if explaining)
if(so.lazy)
{
for(int i = 0; i < prev.size(); i++) earlier.push(prev[i]);
prev.clear();
for(int i = 0; i < thisSubtree.size(); i++) prev.push(thisSubtree[i]);
}
// Set the new subtree boundaries
startSubtree = endSubtree + 1;
endSubtree = nodesSeen - 1;
}
}
// If we haven't reached all of the nodes the graph is disconnected so circuit fails
if(nodesSeen != size)
{
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.disconnected++;
Clause* r = NULL;
if (so.lazy) {
// need to say that each seen node doesn't reach any non-seen node
// first find the seen and not-seen nodes
vec<int> seen;
vec<int> notseen;
for(int var = 0; var < size; var++)
{
if(index[var] >= 0)
seen.push(var);
else
notseen.push(var);
}
int numNotSeen = notseen.size();
assert(seen.size() == nodesSeen);
assert(numNotSeen + nodesSeen == size);
// Now build the reason - seen can't reach notseen (but don't include the last literal)
r = Reason_new(nodesSeen * numNotSeen);
explainAcantreachB(r, 1, nodesSeen * numNotSeen, seen, notseen, seen[0], notseen[0]);
x[seen[0]].setVal(notseen[0], r);
}
//fprintf(stderr, "disconnected (seen %d out of %d)\n", nodesSeen, size);
return false;
}
// If we're pruning root edges and there was more than one subtree,
// prune edges from the root to earlier subtrees
if(pruneRoot && startSubtree > 1)
{
// Build the reason if neccessary (it will be the same for all of the pruned edges)
Clause* r = NULL;
if(so.lazy)
{
// First find the nodes in the last subtree and those in the earlier ones
vec<int> lastSubtree;
vec<int> earlierSubtree;
for(int i = 0; i < size; i++)
{
if(index[i] >= startSubtree)
lastSubtree.push(i);
else if(index[i] > 0) // The root is considered in no subtree
earlierSubtree.push(i);
}
// Reason should state that no var in an earlier sub tree reaches a var
// in the last subtree
r = Reason_new(earlierSubtree.size() * lastSubtree.size() + 1+preRoot.size());
addEqLits(r);
explainAcantreachB(r, 1+preRoot.size(), earlierSubtree.size() * lastSubtree.size() + 1+preRoot.size(), earlierSubtree, lastSubtree);
}
// Prune all edges from the root to nodes in earlier subtrees
for (typename IntView<U>::iterator i = x[rootEnd].begin(); i != x[rootEnd].end(); ++i)
{
if(index[*i] < startSubtree)
{
// XXX [AS] Commented following line, because propagator related statistics
// should be collected within the propagator class.
//engine.prunedRoot++;
addPropagation(false, rootEnd, *i, r);
}
}
}
// Perform the propagations
for(int i = 0; i < propQueue.size(); i++)
{
PROP p = propQueue[i];
//fprintf(stderr, "propagating\n");
if(p.fix)
{
if(x[p.var].setValNotR(p.val))
if(!x[p.var].setVal(p.val, p.reason))
return false;
}
else
{
if(x[p.var].remValNotR(p.val))
if(!x[p.var].remVal(p.val, p.reason))
return false;
}
}
return true;
}
bool propagate() {
//fprintf(stderr, "started propagation\n");
/*for(int i = 0; i < new_fixed.size(); i++)
fprintf(stderr, "%d is newly fixed to %d\n", new_fixed[i], x[new_fixed[i]].getVal());*/
assert(useCheck || useScc); // prevent on its own is not sufficient
if(useCheck && !testSmallCycle())
{
//fprintf(stderr, "finished propagation\n");
return false;
}
if(usePrevent && !cyclePrevent())
{
//fprintf(stderr, "finished propagation\n");
return false;
}
if(useScc)
{
if(so.rootSelection == 10)
{
// try all roots
for(int root = 0; root < size; root++)
if(!circuitSCC(root))
return false;
}
else
{
int root = chooseRoot();
if(root < 0) // means all vars are fixed
{
if(!useCheck)
return testSmallCycle();
else
return true;
}
if(!circuitSCC(root))
return false;
}
}
//fprintf(stderr, "finished propagation\n");
return true;
}
bool testSmallCycle()
{
// fprintf(stderr, "testing\n");
bool foundSmallCycle = false;
vec<int> bestCycle;
double bestScore; // we'll choose the highest
// for each newly fixed variable, follow the chain and if you end up back
// at that variable without reaching all nodes you've found a small cycle
vec<int> thisCycle;
double thisScore;
bool thisIsCycle;
while(new_fixed.size() > 0) {
int startVar = new_fixed[0];
int nextVar = startVar;
thisCycle.clear();
thisScore = 0;
thisIsCycle = false;
bool noScoreYet = true;
//fprintf(stderr,"following chain\n");
while(x[nextVar].isFixed())
{//fprintf(stderr,"nextvar %d ", nextVar);
thisCycle.push(nextVar);
new_fixed.remove(nextVar); // seen this one, don't look at it again
// update the score
int level;
double act;
switch(so.checkfailure)
{
case 1: break; // first (no score required)
case 2: break; // smallest cycle
case 3: break; // largest cycle
case 4: // cycle with lowest ave level,
case 5: //cycle with highest ave level,
// XXX [AS] Replaced 'sat.level' by 'sat.trailpos', because it was replaced in rev. 441
// Maybe it shoud be replaced by sat.getLevel(.)?
level = sat.trailpos[var(x[nextVar].getValLit())];
//level = sat.getLevel(var(x[nextVar].getValLit()));
thisScore += level;
break;
case 6: //cycle with lowest ave activity,
case 7: //cycle with highest ave activity
act = sat.activity[var(x[nextVar].getValLit())];
thisScore += act;
break;
case 8: break; // last (no score required)
case 9: // highest min level, so need to calculate min level
// XXX [AS] Replaced 'sat.level' by 'sat.trailpos', because it was replaced in rev. 441
// Maybe it shoud be replaced by sat.getLevel(.)?
level = sat.trailpos[var(x[nextVar].getValLit())];
//level = sat.getLevel(var(x[nextVar].getValLit()));
if(thisScore > level || noScoreYet) thisScore = level;
break;
case 10: // lowest max level, so need to calculate max level
// XXX [AS] Replaced 'sat.level' by 'sat.trailpos', because it was replaced in rev. 441
// Maybe it shoud be replaced by sat.getLevel(.)?
level = sat.trailpos[var(x[nextVar].getValLit())];
//level = sat.getLevel(var(x[nextVar].getValLit()));
if(thisScore < level || noScoreYet) thisScore = level;
break;
}
noScoreYet = false;
//assert(chainLength <= size);
if(x[nextVar].getVal() == startVar)
{
//fprintf(stderr,"found cycle\n");
thisIsCycle = true;
break;
}
else
nextVar = x[nextVar].getVal();
}
//fprintf(stderr,"end chain\n");
if(thisCycle.size() == size)
break; // all variables are included in this cycle
else if(thisIsCycle)
{
// calculate the score for this cycle (we keep the highest)
switch(so.checkfailure)
{
case 1: break; // first cycle (no score required)
case 2: thisScore = -1*thisCycle.size(); break; // smallest cycle
case 3: thisScore = thisCycle.size(); break; // largest cycle
case 4: thisScore = -1 * thisScore / thisCycle.size(); break; // lowest ave level
case 5: thisScore = thisScore / thisCycle.size(); break; //highest ave level
case 6: thisScore = -1 * thisScore / thisCycle.size(); break; //lowest ave activity
case 7: thisScore = thisScore / thisCycle.size(); break; //highest ave activity
// 8-last, 9-highest min level, 10-lowest max level
case 8: break; // last cycle (no score - we just always overwrite the best)
case 9: break; // highest min level - already done
case 10: thisScore = -1*thisScore; // lowest max level
}
//fprintf(stderr, "this score is %f\n", thisScore);
if(!foundSmallCycle || so.checkfailure == 1 || so.checkfailure == 8 || thisScore > bestScore)
{
foundSmallCycle = true;
bestScore = thisScore;
thisCycle.copyTo(bestCycle);
if(so.checkfailure == 1)
break;
}
}
}
// If we found at least one cycle, report failure
// if we're not explaining we'll already return false which is fine
if(foundSmallCycle && so.lazy)
{
Clause* r = NULL;
vec<int> notInCycle;
bool doOutsideIn = false;
bool result;
// create the reason clause
int chainLength = bestCycle.size();
if(so.checkexpl == 1)
{
// equalities
r = Reason_new(chainLength);
//fprintf(stderr, "\nchain:");
for(int j = 1 ; j < chainLength; j++)
(*r)[j] = x[bestCycle[j-1]].getValLit();
result = x[bestCycle.last()].remVal(bestCycle[0], r);
}
else if(so.checkexpl == 6)
{
// inside can't reach out but smaller
// find smallest and highest index anything inside the cycle is equal to
int smallestIn = bestCycle[0];
int largestIn = bestCycle[0];
for(int i = 1; i < chainLength; i++) {
if(bestCycle[i] < smallestIn) smallestIn = bestCycle[i];
if(bestCycle[i] > largestIn) largestIn = bestCycle[i];
}
// find the indices outside the cycle which are btw the smallest and largest inside
vec<int> outsidebetween;
for(int i = smallestIn+1; i < largestIn; i++)
{
// find out if it's in the cycle
bool incycle = false;
for(int j = 0; j < chainLength; j++)
if(bestCycle[j] == i)
incycle = true;
// if not, this is an outside in index
if(!incycle)
outsidebetween.push(i);
}
//fprintf(stderr, "min in is %d, max in is %d\n", smallestIn, largestIn);
//fprintf(stderr, "out ");
//for(int i = 0; i < outsidebetween.size(); i++)
// fprintf(stderr, "%d,", outsidebetween[i]);
//fprintf(stderr, "num in is %d\n", chainLength);
// for each var in the cycle, say that it's larger than or equal to the smallest
// index, less than or equal to the largest index, and not equal to any of the
// outside indices within that range
// leave out the first one, as this is the one we'll set
r = Reason_new(chainLength * (2+outsidebetween.size()));
for(int i = 0; i < chainLength; i++)
{
int start = i * (2+outsidebetween.size());
int varIndex = bestCycle[i];
if(i > 0) // leave out first literal
(*r)[start] = x[varIndex].getLit(smallestIn-1, 3); // x <= smallest-1
(*r)[start+1] = x[varIndex].getLit(largestIn+1, 2); // x >= largest+1
for(int j = 0; j < outsidebetween.size(); j++)
(*r)[start+2+j] = x[varIndex].getLit(outsidebetween[j], 1); // x == k
}
result = x[bestCycle[0]].setMax(smallestIn-1, r);
}
else
{
// find the vars in and not in the cycle
for(int i = 0; i < size; i++)
{
bool cycleContains = false;
for(int j = 0; j < bestCycle.size(); j++)
if(bestCycle[j] == i)
{
cycleContains = true;
break;
}
if(!cycleContains)
notInCycle.push(i);
}
r = Reason_new(bestCycle.size() * notInCycle.size());
if(so.checkexpl == 2) // inside can't reach out
doOutsideIn = false;
else if(so.checkexpl == 3) // outside can't reach in
doOutsideIn = true;
else if(so.checkexpl == 4) // smaller group can't reach bigger group
doOutsideIn = notInCycle.size() < bestCycle.size();
else if(so.checkexpl == 5) // bigger group can't reach smaller group
doOutsideIn = bestCycle.size() < notInCycle.size();
else fprintf(stderr, "Unknown check explanation type\n");
if(doOutsideIn)
{
explainAcantreachB(r, 1, bestCycle.size() * notInCycle.size(), notInCycle, bestCycle, notInCycle[0], bestCycle[0]);
result = x[notInCycle[0]].setVal(bestCycle[0], r);
}
else
{
explainAcantreachB(r, 1, bestCycle.size() * notInCycle.size(), bestCycle, notInCycle, bestCycle[0], notInCycle[0]);
result = x[bestCycle[0]].setVal(notInCycle[0], r);
}
}
assert(!result);
}
return !foundSmallCycle;
}
void clearPropState() {
in_queue = false;
new_fixed.clear();
}
};
void circuit(vec<IntVar*>& _x, int offset) {
//fprintf(stderr,"\ncircuit constraint");
all_different(_x, CL_DOM);
vec<IntView<> > x;
for (int i = 0; i < _x.size(); i++) _x[i]->specialiseToEL();
if (offset == 0) {
for (int i = 0; i < _x.size(); i++) int_rel(_x[i], IRT_NE, i);
for (int i = 0; i < _x.size(); i++) x.push(IntView<>(_x[i]));
new Circuit<0>(x);
}
else {
for (int i = 0; i < _x.size(); i++) int_rel(_x[i], IRT_NE, i+offset);
for (int i = 0; i < _x.size(); i++) x.push(IntView<4>(_x[i], 1, -offset));
new Circuit<4>(x);
}
}
void path(vec<IntVar*>& _x) {
// add dummy node
vec<IntVar*> x;
_x.copyTo(x);
IntVar* dummyVar;
// dummy node can have any original node as its successor
createVar(dummyVar, 0, _x.size()-1, true);
x.push(dummyVar);
// nodes including dummy node must form circuit
circuit(x);
}