/* * Kodkod -- Copyright (c) 2005-present, 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.examples.csp; import java.util.ArrayList; import java.util.List; import kodkod.ast.Decls; import kodkod.ast.Expression; import kodkod.ast.Formula; import kodkod.ast.Relation; import kodkod.ast.Variable; import kodkod.ast.operator.Multiplicity; import kodkod.engine.Evaluator; import kodkod.engine.Solution; import kodkod.engine.Solver; import kodkod.engine.config.ConsoleReporter; import kodkod.engine.satlab.SATFactory; import kodkod.instance.Bounds; import kodkod.instance.TupleFactory; import kodkod.instance.TupleSet; import kodkod.instance.Universe; /** * Reads a graph from a file, formatted using the DIMACS or the ASP (http://asparagus.cs.uni-potsdam.de/?action=instances&id=30) graph format, * and finds a Hamiltonian cycle in it if one exists. This encoding is more efficient than, but not as nice as, {@linkplain HamiltonianCycle}. * * @author Emina Torlak */ public final class HamiltonianCycle2 { private final Expression[] pts; private final Relation edges, vertex; private final Bounds bounds; private final Multiplicity ptMult; private HamiltonianCycle2(Bounds bounds, Expression[] pts, Multiplicity ptMult, Relation vertex, Relation edges) { this.pts = pts; this.ptMult = ptMult; this.vertex = vertex; this.edges = edges; this.bounds = bounds; } /** * Returns a log encoded instance of HamiltonianCycle2. * @return a log encoded instance of HamiltonianCycle2. */ public static HamiltonianCycle2 logEncoding(String file, Graph.Format format ) { final Graph<?> graph = format.parse(file); final Relation edges = Relation.binary("edges"); final Relation enc = Relation.binary("enc"); final Relation vertex = Relation.unary("vertex"); final Relation[] pts = new Relation[graph.nodes().size()]; for(int i = 0; i < pts.length; i++) { pts[i] = Relation.unary("p"+i); } final int bits = 33 - Integer.numberOfLeadingZeros(pts.length-1); final List<Object> atoms = new ArrayList<Object>(pts.length + bits); atoms.addAll(graph.nodes()); for(int i = 0; i < bits; i++) { atoms.add(new Bit(i)); } final Bounds bounds = new Bounds(new Universe(atoms)); final TupleFactory f = bounds.universe().factory(); final TupleSet edgeBound = f.noneOf(2); for(Object from : graph.nodes()) { for (Object to : graph.edges(from)) edgeBound.add(f.tuple(from, to)); } bounds.boundExactly(edges, edgeBound); bounds.boundExactly(pts[0], f.setOf(atoms.get(pts.length))); for(int i = 1; i < pts.length; i++) { bounds.bound(pts[i], f.range(f.tuple(atoms.get(pts.length)), f.tuple(atoms.get(atoms.size()-1)))); } bounds.boundExactly(vertex, f.range(f.tuple(atoms.get(0)), f.tuple(atoms.get(pts.length-1)))); final TupleSet encBound = f.noneOf(2); for(int i = 1; i <= pts.length; i++) { final Object iatom = atoms.get(i-1); for(int j = 0; j < bits; j++) { if ((i & (1<<j)) != 0) { encBound.add(f.tuple(iatom, atoms.get(pts.length+j))); } } } bounds.boundExactly(enc, encBound); final Expression[] exprs = new Expression[pts.length]; final Variable v = Variable.unary("v"); final Decls d = v.oneOf(vertex); for(int i = 0; i < exprs.length; i++) { exprs[i] = pts[i].eq(v.join(enc)).comprehension(d); } return new HamiltonianCycle2(bounds, exprs, Multiplicity.SOME, vertex, edges); } /** * Returns an ext encoded instance of HamiltonianCycle2. * @return an ext encoded instance of HamiltonianCycle2. */ public static HamiltonianCycle2 extEncoding(String file, Graph.Format format ) { final Graph<?> graph = format.parse(file); final Relation edges = Relation.binary("edges"); final Relation vertex = Relation.unary("vertex"); final Relation[] pts = new Relation[graph.nodes().size()]; for(int i = 0; i < pts.length; i++) { pts[i] = Relation.unary("p"+i); } final Universe univ = new Universe(graph.nodes()); final Bounds bounds = new Bounds(univ); final TupleFactory f = univ.factory(); final TupleSet edgeBound = f.noneOf(2); for(Object from : graph.nodes()) { for (Object to : graph.edges(from)) edgeBound.add(f.tuple(from, to)); } bounds.boundExactly(edges, edgeBound); bounds.boundExactly(pts[0], f.setOf(graph.start()==null ? univ.atom(0) : graph.start())); for(int i = 1; i < pts.length; i++) { bounds.bound(pts[i], f.range(f.tuple(univ.atom(1)), f.tuple(univ.atom(univ.size()-1)))); } bounds.boundExactly(vertex, f.allOf(1)); return new HamiltonianCycle2(bounds, pts, Multiplicity.ONE, vertex, edges); } private static final class Bit { final int value; Bit(int bit) { this.value = 1<<bit; } public String toString() { return value+""; } } /** * Returns a formula that defines a Hamiltonian cycle. * @return a formula that defines a Hamiltonian cycle */ public final Formula cycleDefinition() { final List<Formula> formulas = new ArrayList<Formula>(); for(Expression e : pts) { formulas.add( e.apply(ptMult) ); } for(int i = 1; i < pts.length; i++) { formulas.add( pts[i-1].product(pts[i]).in(edges) ); } formulas.add( pts[pts.length-1].product(pts[0]).in(edges) ); formulas.add(Expression.union(pts).eq(vertex)); return Formula.and(formulas); } /** * Returns the Bounds for the given cycle instance. * @return Bounds for the given cycle instance. */ public final Bounds bounds( ) { return bounds.unmodifiableView(); } private static void usage() { System.out.println("Usage: examples.classicnp.HamiltonianCycle2 <graph file> <DIMACS | ASP> <EXT | LOG>"); System.exit(1); } /** * Usage: examples.classicnp.HamiltonianCycle2 <graph file> <DIMACS | ASP> <EXT | LOG> */ public static void main(String[] args) { if (args.length!=3) usage(); final HamiltonianCycle2 model; if ("LOG".equals(args[2].toUpperCase())) { model = logEncoding(args[0], Enum.valueOf(Graph.Format.class, args[1].toUpperCase())); } else if ("EXT".equals(args[2].toUpperCase())) { model = extEncoding(args[0], Enum.valueOf(Graph.Format.class, args[1].toUpperCase())); } else { usage(); model = null; } final Formula f = model.cycleDefinition(); final Bounds b = model.bounds(); System.out.println(f); System.out.println(b); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.MiniSat); solver.options().setReporter(new ConsoleReporter()); // solver.options().setFlatten(false); final Solution s = solver.solve(f,b); System.out.println(s.outcome()); System.out.println(s.stats()); if (s.instance()!=null) { final Evaluator eval = new Evaluator(s.instance(), solver.options()); final Expression[] dec = model.pts; System.out.print(eval.evaluate(dec[0])); for(int i = 1; i < dec.length; i++) { System.out.print(" -> " + eval.evaluate(dec[i])); } System.out.println(); } } }