package kodkod.test.unit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.engine.Solution;
import kodkod.engine.Solver;
import kodkod.engine.Statistics;
import kodkod.instance.Bounds;
import kodkod.instance.Instance;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
import kodkod.instance.Universe;
import org.junit.Before;
import org.junit.Test;
/**
* Tests symmetry breaking code for total orderings and
* acyclic relations.
*
* @author Emina Torlak
*/
public class SymmetryBreakingTest {
private static final int USIZE = 10;
private final TupleFactory factory;
private final Relation to1, ord1, first1, last1,
to2, ord2, first2, last2,
to3, ord3, first3, last3,
ac1, ac2, ac3, r1, r2;
private Bounds bounds;
private int pVars, iVars, clauses;
private final Solver solver;
public SymmetryBreakingTest() {
this.solver = new Solver();
List<String> atoms = new ArrayList<String>(USIZE);
for (int i = 0; i < USIZE; i++) {
atoms.add(""+i);
}
final Universe universe = new Universe(atoms);
this.factory = universe.factory();
to1 = Relation.binary("to1");
ord1 = Relation.unary("ord1");
first1 = Relation.unary("first1");
last1 = Relation.unary("last1");
to2 = Relation.binary("to2");
ord2 = Relation.unary("ord2");
first2 = Relation.unary("first2");
last2 = Relation.unary("last2");
to3 = Relation.binary("to3");
ord3 = Relation.unary("ord3");
first3 = Relation.unary("first3");
last3 = Relation.unary("last3");
ac1 = Relation.binary("ac1");
ac2 = Relation.binary("ac2");
ac3 = Relation.binary("ac3");
r1 = Relation.unary("r1");
r2 = Relation.binary("r2");
}
@Before
public void setUp() throws Exception {
bounds = new Bounds(factory.universe());
}
private Instance solve(Formula f, Bounds b) {
final Solution sol = solver.solve(f, b);
final Statistics stats = sol.stats();
pVars = stats.primaryVariables();
iVars = stats.variables() - pVars;
clauses = stats.clauses();
return sol.instance();
}
private Instance solve(Formula f) {
return solve(f, bounds);
}
private void assertPrimVarNum(int primVars) {
assertEquals(primVars, pVars);
}
private void assertAuxVarNum(int auxVars) {
assertEquals(auxVars, iVars);
}
private void assertClauseNum(int clauses) {
assertEquals(clauses, this.clauses);
}
@Test
public final void testTotalOrdering() {
bounds.bound(to1, factory.area(factory.tuple("0","0"), factory.tuple("4","4")));
bounds.bound(ord1, factory.setOf("0","1","2","3", "4"));
bounds.bound(first1, bounds.upperBound(ord1));
bounds.bound(last1, bounds.upperBound(ord1));
final Formula ordered1 = to1.totalOrder(ord1,first1,last1);
assertNotNull(solve(to1.some().and(ordered1)));
assertPrimVarNum(0); assertAuxVarNum(0); assertClauseNum(0);
bounds.bound(r1, factory.range(factory.tuple("0"), factory.tuple("4")));
assertNotNull(solve(to1.join(r1).some().and(ordered1)));
assertPrimVarNum(bounds.upperBound(r1).size());
bounds.boundExactly(r1, bounds.upperBound(r1));
assertNotNull(solve(to1.join(r1).some().and(ordered1)));
assertPrimVarNum(0);
bounds.bound(to2, factory.setOf("5","6","7","8","9").product(factory.setOf("5","7","8")));
bounds.bound(ord2, factory.setOf("5","7","8"));
bounds.bound(first2, bounds.upperBound(ord2));
bounds.bound(last2, bounds.upperBound(ord2));
final Formula ordered2 = to2.totalOrder(ord2,first2,last2);
assertNotNull(solve(to1.difference(to2).some().and(ordered2).and(ordered1)));
assertPrimVarNum(0); assertAuxVarNum(0); assertClauseNum(0);
bounds.bound(to3, factory.allOf(2));
bounds.bound(ord3, factory.allOf(1));
bounds.bound(first3, factory.setOf("9"));
bounds.bound(last3, factory.setOf("8"));
final Formula ordered3 = to3.totalOrder(ord3,first3,last3);
assertNotNull(solve(to3.product(to1).some().and(ordered1).and(ordered3)));
assertPrimVarNum(bounds.upperBound(to3).size() + bounds.upperBound(ord3).size() + 2);
// SAT solver takes a while
// bounds.boundExactly(r2, factory.setOf(factory.tuple("9","8")));
// assertNotNull(solve(r2.in(to3).and(ordered3)));
bounds.bound(to3, factory.allOf(2));
bounds.bound(ord3, factory.setOf("3"));
bounds.bound(first3, factory.allOf(1));
bounds.bound(last3, factory.allOf(1));
Instance instance = solve(ordered3);
assertNotNull(instance);
assertTrue(instance.tuples(to3).isEmpty());
assertTrue(instance.tuples(ord3).equals(bounds.upperBound(ord3)));
assertTrue(instance.tuples(first3).equals(bounds.upperBound(ord3)));
assertTrue(instance.tuples(last3).equals(bounds.upperBound(ord3)));
}
@Test
public final void testAcyclic() {
bounds.bound(ac1, factory.area(factory.tuple("0","0"), factory.tuple("4","4")));
assertNotNull(solve(ac1.some().and(ac1.acyclic())));
assertPrimVarNum(10);
bounds.bound(r1, factory.range(factory.tuple("0"), factory.tuple("4")));
assertNotNull(solve(ac1.join(r1).some().and(ac1.acyclic())));
assertPrimVarNum(10 + bounds.upperBound(r1).size());
TupleSet ac2b = factory.setOf("5","6","7","8");
ac2b = ac2b.product(ac2b);
bounds.bound(ac2, ac2b);
assertNotNull(solve(ac1.difference(ac2).some().and(ac1.acyclic()).and(ac2.acyclic())));
assertPrimVarNum(10 + 6);
bounds.boundExactly(r2, factory.setOf(factory.tuple("5", "6")));
assertNotNull(solve(ac2.join(r2).some().and(ac2.acyclic())));
final TupleSet ac3Bound = factory.allOf(2);
ac3Bound.remove(factory.tuple("9", "9"));
bounds.bound(ac3, ac3Bound);
assertNotNull(solve(ac1.difference(ac2).union(ac3).some().and(ac1.acyclic()).and(ac2.acyclic())));
assertPrimVarNum(ac3Bound.size() + 10 + 6);
bounds.bound(to3, factory.allOf(2));
bounds.bound(ord3, factory.setOf("0","1","2"));
bounds.bound(first3, bounds.upperBound(ord3));
bounds.bound(last3, bounds.upperBound(ord3));
assertNotNull(solve(to3.product(ac1).some().and(ac1.acyclic()).and(to3.totalOrder(ord3,first3,last3))));
assertPrimVarNum(bounds.upperBound(ac1).size());
}
}