/* * Kodkod -- Copyright (c) 2005-2012, Emina Torlak * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package kodkod.test.unit; import static kodkod.engine.Solution.Outcome.SATISFIABLE; import static kodkod.engine.Solution.Outcome.TRIVIALLY_SATISFIABLE; import static kodkod.engine.Solution.Outcome.TRIVIALLY_UNSATISFIABLE; import static kodkod.engine.Solution.Outcome.UNSATISFIABLE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import kodkod.ast.Formula; import kodkod.ast.Relation; import kodkod.engine.Evaluator; import kodkod.engine.IncrementalSolver; import kodkod.engine.Solution; import kodkod.engine.Solution.Outcome; import kodkod.engine.config.Options; import kodkod.engine.satlab.SATFactory; import kodkod.instance.Bounds; import kodkod.instance.Instance; import kodkod.instance.TupleFactory; import kodkod.instance.Universe; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import kodkod.test.util.Solvers; /** * Tests for {@link IncrementalSolver incremental solving}. * * @author Emina Torlak */ @RunWith(Parameterized.class) public final class IncrementalSolverTest { private final IncrementalSolver solver; /** * Constructs an incremental solver test. */ public IncrementalSolverTest(SATFactory solverOpt) { final Options opt = new Options(); opt.setSolver(solverOpt); this.solver = IncrementalSolver.solver(opt); } @Parameters public static Collection<Object[]> solversToTestWith() { final Collection<Object[]> ret = new ArrayList<Object[]>(); for(SATFactory factory : Solvers.allAvailableSolvers()) { if (factory.incremental()) ret.add(new Object[]{factory}); } return ret; } /** * @throws java.lang.Exception */ @After public void tearDown() throws Exception { solver.free(); } @Test public void testBadOptions() { final Options opt = new Options(); opt.setLogTranslation(1); try { IncrementalSolver.solver(opt); fail("Expected an IllegalArgumentException when logging is enabled."); } catch (IllegalArgumentException iae) { // fine } opt.setLogTranslation(0); opt.setSolver(SATFactory.plingeling()); try { IncrementalSolver.solver(opt); fail("Expected an IllegalArgumentException when using a non-incremental solver."); } catch (IllegalArgumentException iae) { // fine } } @Test public void testOneTriviallySatisfiableStep() { assertTrue(solver.usable()); final Solution sol = solver.solve(Formula.TRUE, new Bounds(new Universe("A0"))); assertTrue(solver.usable()); assertEquals(TRIVIALLY_SATISFIABLE, sol.outcome()); } @Test public void testOneTriviallyUnsatisfiableStep() { assertTrue(solver.usable()); final Bounds b = new Bounds(new Universe("A0")); final Solution sol = solver.solve(Formula.FALSE, b); assertFalse(solver.usable()); assertEquals(TRIVIALLY_UNSATISFIABLE, sol.outcome()); try { solver.solve(Formula.TRUE, b); fail("Expected an IllegalStateException when trying to call a solver that has returned UNSAT."); } catch (IllegalStateException iae) { // finle } } @Test public void testBadBounds() { final Bounds b = new Bounds(new Universe("A0", "A1", "A2")); final TupleFactory t = b.universe().factory(); final Relation r0 = Relation.unary("r0"); final Relation r1 = Relation.unary("r1"); b.bound(r0, t.setOf("A0")); final Solution sol = solver.solve(r0.some(), b); assertEquals(SATISFIABLE, sol.outcome()); b.bound(r1, t.setOf("A1")); try { solver.solve(r1.some(), b); fail("Expected an IllegalArgumentException when solving with bounds that do not induce a coarser set of symmetries than the initial bounds."); } catch (IllegalArgumentException iae) { // fine } assertFalse(solver.usable()); } private Solution checkModel(Solution s, Formula...formulas) { final Instance i = s.instance(); assertNotNull(i); final Evaluator eval = new Evaluator(i, solver.options()); assertTrue(eval.evaluate(Formula.and(formulas))); return s; } private Solution checkOutcomeAndStats(Solution s, Outcome expectedOutcome, int expectedPrimaryVars) { assertEquals(expectedOutcome, s.outcome()); assertEquals(expectedPrimaryVars, s.stats().primaryVariables()); return s; } @Test public void testSimpleIncrementalSequence() { final Bounds b = new Bounds(new Universe("A0", "A1", "A2")); final TupleFactory t = b.universe().factory(); final Relation r0 = Relation.unary("r0"); final Relation r1 = Relation.unary("r1"); final Relation r2 = Relation.unary("r2"); b.bound(r0, t.setOf("A0","A1")); b.bound(r1, t.setOf("A1","A2")); final Formula[] f = { r0.some(), r1.some(), r0.intersection(r1).no(), r2.in(r0.union(r1)) }; checkModel(solver.solve(f[0], b), f[0]); b.relations().clear(); checkModel(solver.solve(f[1], b), f[0], f[1]); checkModel(solver.solve(f[2], b), f[0], f[1], f[2]); b.bound(r2, t.allOf(1)); checkModel(solver.solve(f[3], b), f[0], f[1], f[2], f[3]); } @Test public void testIncrementalFullSymmetryBreaking() { final Bounds b = new Bounds(new Universe("A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9")); final TupleFactory t = b.universe().factory(); final Relation ord = Relation.binary("ord"), univ03 = Relation.unary("univ[0..3]"), first = Relation.unary("first"), last = Relation.unary("last"); final Relation next47 = Relation.binary("next43"), univ47 = Relation.unary("univ[4..7]"); final Relation next09 = Relation.binary("next09"); b.bound(univ03, t.setOf("A0", "A1", "A2", "A3")); b.bound(first, b.upperBound(univ03)); b.bound(last, b.upperBound(univ03)); b.bound(ord, b.upperBound(univ03).product(b.upperBound(univ03))); b.boundExactly(univ47, t.setOf("A4", "A5", "A6", "A7")); final Formula[] f = { ord.totalOrder(univ03, first, last), ord.acyclic(), next47.acyclic(), next09.acyclic() }; checkOutcomeAndStats(checkModel(solver.solve(f[0], b), f[0]), TRIVIALLY_SATISFIABLE, 0); b.relations().clear(); checkOutcomeAndStats(checkModel(solver.solve(f[1], b), f[0], f[1]), TRIVIALLY_SATISFIABLE, 0); b.bound(next47, t.setOf("A4", "A5", "A6", "A7").product(t.setOf("A4", "A5", "A6", "A7"))); checkOutcomeAndStats(checkModel(solver.solve(f[2], b), f[0], f[1], f[2]), TRIVIALLY_SATISFIABLE, 0); b.relations().clear(); b.bound(next09, t.allOf(2)); // expecting 6 variables for the symmetry-reduced upper bound of next47 and 100 for the the full upper bound of next09 checkOutcomeAndStats(checkModel(solver.solve(f[3], b), f[0], f[1], f[2], f[3]), SATISFIABLE, 100 + 6); } @Test public void testIncrementalPartialSymmetryBreaking() { final Bounds b = new Bounds(new Universe("A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9")); final TupleFactory t = b.universe().factory(); final Relation ord = Relation.binary("ord"), univ03 = Relation.unary("univ[0..3]"), first = Relation.unary("first"), last = Relation.unary("last"); final Relation fun = Relation.binary("fun"), univ47 = Relation.unary("univ[4..7]"), univ89 = Relation.unary("univ[8..9]"); b.bound(univ47, t.setOf("A4", "A5", "A6", "A7")); b.bound(univ89, t.setOf("A8", "A9")); b.bound(fun, b.upperBound(univ47).product(b.upperBound(univ89))); final Formula[] f = { fun.function(univ47, univ89), ord.totalOrder(univ03, first, last), ord.acyclic(), last.product(first).in(ord) }; final int vars0 = 4 + 2 + 4*2; // univ47, univ89, fun checkOutcomeAndStats(checkModel(solver.solve(f[0], b), f[0]), SATISFIABLE, vars0); b.relations().clear(); b.bound(univ03, t.setOf("A0", "A1", "A2", "A3")); b.bound(first, b.upperBound(univ03)); b.bound(last, b.upperBound(univ03)); b.bound(ord, b.upperBound(univ03).product(b.upperBound(univ03))); final int vars1 = vars0 + 4*3 + 4*4; // univ03, first, last, ord checkOutcomeAndStats(checkModel(solver.solve(f[1], b), f[0], f[1]), SATISFIABLE, vars1); b.relations().clear(); checkOutcomeAndStats(checkModel(solver.solve(f[2], b), f[0], f[1], f[2]), SATISFIABLE, vars1); checkOutcomeAndStats(solver.solve(f[3], b), UNSATISFIABLE, vars1); //final Solver whole = new Solver(solver.options()); //System.out.println(whole.solve(Formula.and(f[0], f[1], f[2]), b)); } }