radsz/jacop

Consistency of element constraint is sometimes incorrect.

Closed this issue · 1 comments

Using JaCop 4.6.0 on Java.

The following code snippet illustrates a case where the element constraint gives an incorrect result:

// Step1: Create the store + variables
Store cp = new Store();

IntVar   index = new IntVar(cp, 0, 4);
IntVar[]  vars = new IntVar[]{new IntVar(cp, -4, 4)};
IntVar   value = new IntVar(cp, 0, 0);

// Step 2: punch some holes in the domains
cp.setLevel(cp.level + 1);
index.dom().subtractAdapt(-2, -1);
index.dom().subtractAdapt(1, 3);
if ( !cp.consistency() ) {
    // rest assured, this is never reached.
    System.err.println("WHAT ?!");
}

// Step3: Impose an element constraint on our variables
cp.setLevel(cp.level + 1);
cp.impose(Element.choose(index, vars, value));
if ( !cp.consistency() ) {
    // This time, it fails even though index = {0}, vars = [{0}], value = {0} is a solution.
    System.err.println("Ahem, this is wrong !");
}
radsz commented

Hi,

Unfortunately your program has few issues that cause you problems.

First, the most important, you should not use the functions of the domain to change the domains of the variables after they have been created.
index.dom().subtract(-2, -1) will never work properly.

JaCoP works properly when you first define your domains the way you want it. If you define domains too large and use them to create variables then the only way to reduce their domain is by imposing constraints. I have tried to adapt your program. The program below is just adaptation of yours not a recommendation of how to write CP programs in JaCoP. For recommendation check the numerous examples provided.

Second issue, element starts indexing variables with 1 so your solution index=0, vars=[{0}], value=0 is not a proper solution unless you use an extra parameter shift and set it to -1. If you do so then the solution is found.

    // Step1: Create the store + variables
    Store cp = new Store();

    IntVar   index = new IntVar(cp, "index",0, 4);
    IntVar[]  vars = new IntVar[]{new IntVar(cp, "var[0]", -4, 4)};
    IntVar   value = new IntVar(cp, "value", 0, 0);

    // Step 2: punch some holes in the domains
    cp.setLevel(cp.level + 1);
    //index.dom().subtractAdapt(-2, -1);
    // Or is never an encouraged approach I just wanted to be sure I transform incorrect calls into           //  something correct.
    cp.impose(new Or(new XgtC(index, -1), new XltC(index, -2)));

    //index.dom().subtractAdapt(2, 3);
    cp.impose(new Or(new XgtC(index, 3), new XltC(index, 1)));

    if ( !cp.consistency() ) {
        // rest assured, this is never reached.
        System.err.println("WHAT ?!");
    }

    // Step3: Impose an element constraint on our variables
    cp.setLevel(cp.level + 1);
    cp.impose(Element.choose(index, vars, value, -1));
    if ( !cp.consistency() ) {
        // This time, it fails even though index = {0}, vars = [{0}], value = {0} is a solution.
        System.err.println("Ahem, this is wrong !");
    }

// the functionality below is defined and available in file SingleConstraintTest.
int noOfSolutions = noOfAllSolutions(cp, vars, new IntVar[] {index, value});
System.out.println(noOfSolutions);

And I am able to find a solution.
Starting test: testElement
Labeling has finished with return value of false
Depth First Search DFS1
[var[0] = 0, index = 0, value = 0]

Solution : [var[0]=0, index=0, value=0]
Nodes : 0
Decisions : 0
Wrong Decisions : 0
Backtracks : 0
Max Depth : 0

Final remark, playing with levels cp.setLevel() or calling cp.consistency() function and doing no cleanup in case there was failure is a recipy for disaster (solver reporting solutions when there are none). For new users, we encourage using DepthFirstSearch to search for solution and just configure it to the way you want it. CP can be consistent even if there is no solution as long as you have not assigned all variables (tried to search for a solution), so even on that ground alone your report may have been incorrect.