package kodkod.examples.csp;
import static kodkod.ast.Expression.UNIV;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.ast.Variable;
import kodkod.engine.Solution;
import kodkod.engine.Solver;
import kodkod.engine.satlab.SATFactory;
import kodkod.instance.Bounds;
import kodkod.instance.Tuple;
import kodkod.instance.TupleFactory;
import kodkod.instance.Universe;
/**
* A KK encoding of the Latin squares problem.
*
* @author Emina Torlak
*/
public final class LatinSquare {
private final Relation square;
/**
* Constructs a model of a Latin square.
*/
public LatinSquare() {
square = Relation.ternary("square");
}
/**
* Returns a KK encoding of the qg5 problem (http://4c.ucc.ie/~tw/csplib/prob/prob003/spec.html)
*
* @return a KK encoding of the qg5 problem.
*/
public final Formula qg5() {
//((b*a)*b)*b = a.
final Variable a = Variable.unary("a");
final Variable b = Variable.unary("b");
final Expression e0 = a.join(b.join(square)); // b*a
final Expression e1 = b.join(e0.join(square)); // (b*a)*b
final Expression e2 = b.join(e1.join(square)); // ((b*a)*b)*b
return e2.intersection(a).some().forAll(a.oneOf(UNIV).and(b.oneOf(UNIV)));
}
/**
* Returns a formula stating that the square relation is a latin square.
* @return a formula stating that the square relation is a latin square.
*/
public final Formula latin() {
final Variable x = Variable.unary("x");
final Variable y = Variable.unary("y");
final Formula f0 = y.join(x.join(square)).one().forAll(x.oneOf(UNIV).and(y.oneOf(UNIV)));
final Variable z = Variable.unary("z");
final Formula row = UNIV.in(UNIV.join(z.join(square)));
final Formula col = UNIV.in(z.join(UNIV.join(square)));
return f0.and((row.and(col)).forAll(z.oneOf(UNIV)));
}
/**
* Returns a formula stating that the square relation is idempotent.
* @return a formula stating that the square relation is idempotent.
*/
public final Formula idempotent() {
final Variable x = Variable.unary("x");
return x.product(x).product(x).in(square).forAll(x.oneOf(UNIV));
}
/**
* Returns the bounds for a Latin square of the given order.
* @requires n > 0
* @return the bounds for a Latin square of the given order.
*/
public final Bounds bounds(int n) {
assert n>0;
final List<String> nums = new ArrayList<String>(n);
for(int i = 0; i < n; i++)
nums.add(String.valueOf(i));
final Universe u = new Universe(nums);
final Bounds b = new Bounds(u);
final TupleFactory f = u.factory();
b.bound(square, f.allOf(3));
return b;
}
private static void usage() {
System.out.println("java examples.LatinSquare [order]");
System.exit(1);
}
/**
* Usage: java examples.LatinSquare [order]
*/
public static void main(String[] args) {
if (args.length < 1)
usage();
try {
final int n = Integer.parseInt(args[0]);
if (n < 1)
usage();
final LatinSquare model = new LatinSquare();
final Solver solver = new Solver();
solver.options().setSolver(SATFactory.MiniSat);
solver.options().setSymmetryBreaking(n*n*n);
final Formula f = model.latin().and(model.qg5()).and(model.idempotent());
final Bounds b = model.bounds(n);
System.out.println(f);
final Solution sol = solver.solve(f, b);
if (sol.instance()==null) {
System.out.println(sol);
} else {
System.out.println(sol.stats());
final Iterator<Tuple> iter = sol.instance().tuples(model.square).iterator();
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
System.out.print(iter.next().atom(2));
System.out.print("\t");
}
System.out.println();
}
}
} catch (NumberFormatException nfe) {
usage();
}
}
}