package kodkod.examples.alloy;
import java.util.ArrayList;
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.config.ConsoleReporter;
import kodkod.engine.satlab.SATFactory;
import kodkod.instance.Bounds;
import kodkod.instance.TupleFactory;
import kodkod.instance.Universe;
/**
* KodKod encoding of shakehands.als.
*
* @author Emina Torlak
*/
public final class Handshake {
private final Relation Person, Hilary, Jocelyn, shaken, spouse;
public Handshake() {
Person = Relation.unary("Person");
Hilary = Relation.unary("Hilary");
Jocelyn = Relation.unary("Jocelyn");
shaken = Relation.binary("shaken");
spouse = Relation.binary("spouse");
}
/**
* Returns the declarations
* @return
* <pre>
* sig Person {spouse: Person, shaken: set Person}
* one sig Jocelyn, Hilary extends Person {}
* </pre>
*/
public Formula declarations() {
final Formula f0 = spouse.function(Person, Person);
final Formula f1 = shaken.in(Person.product(Person));
final Formula f2 = Hilary.one().and(Jocelyn.one());
return f0.and(f1).and(f2);
}
/**
* Returns the ShakingProtocol fact.
* @return
* <pre>
* fact ShakingProtocol {
* // nobody shakes own or spouse's hand
* all p: Person | no (p + p.spouse) & p.shaken
* // if p shakes q, q shakes p
* all p, q: Person | p in q.shaken => q in p.shaken
* }
* </pre>
*/
public Formula shakingProtocol() {
final Variable p = Variable.unary("p");
final Variable q = Variable.unary("q");
final Formula f1 = p.union(p.join(spouse)).intersection(p.join(shaken)).no().forAll(p.oneOf(Person));
final Formula f2 = p.in(q.join(shaken)).implies(q.in(p.join(shaken))).forAll(p.oneOf(Person).and(q.oneOf(Person)));
return f1.and(f2);
}
/**
* Returns the Spouses fact.
* @return
* <pre>
* fact Spouses {
* all disj p, q: Person {
* // if q is p's spouse, p is q's spouse
* p.spouse = q => q.spouse = p
* // no spouse sharing
* p.spouse != q.spouse
* }
* all p: Person {
* // a person is his or her spouse's spouse
* p.spouse.spouse = p
* // nobody is his or her own spouse
* p != p.spouse
* }
* }
* </pre>
*/
public Formula spouses() {
final Variable p = Variable.unary("p");
final Variable q = Variable.unary("q");
final Formula f1 = p.join(spouse).eq(q).implies(q.join(spouse).eq(p));
final Formula f2 = p.join(spouse).eq(q.join(spouse)).not();
final Formula f3 = p.intersection(q).no().implies(f1.and(f2)).forAll(p.oneOf(Person).and(q.oneOf(Person)));
final Formula f4 = p.join(spouse).join(spouse).eq(p).and(p.eq(p.join(spouse)).not()).forAll(p.oneOf(Person));
return f3.and(f4);
}
/**
* Returns the Puzzle predicate
* @return
* <pre>
* pred Puzzle() {
* // everyone but Jocelyn has shaken a different number of hands
* all disj p,q: Person - Jocelyn | #p.shaken != #q.shaken
* // Hilary's spouse is Jocelyn
* Hilary.spouse = Jocelyn
* }
* </pre>
*/
public Formula puzzle() {
final Variable p = Variable.unary("p");
final Variable q = Variable.unary("q");
final Formula f = p.eq(q).not().implies(p.join(shaken).count().eq(q.join(shaken).count()).not());
final Expression e = Person.difference(Jocelyn);
return f.forAll(p.oneOf(e).and(q.oneOf(e))).and(Hilary.join(spouse).eq(Jocelyn));
}
/**
* Returns the conjunction of the facts and the puzzle predicate.
* @return declarations().and(shakingProtocol()).and(spouses()).and(puzzle())
*/
public Formula runPuzzle() {
return declarations().and(shakingProtocol()).and(spouses()).and(puzzle());
}
/**
* Returns a bounds for the given number of persons.
* @return a bounds for the given number of persons.
*/
public Bounds bounds(int persons) {
final List<String> atoms = new ArrayList<String>(persons);
atoms.add("Hilary");
atoms.add("Jocelyn");
for(int i = 2; i < persons; i ++) {
atoms.add("Person" + i);
}
final Universe u = new Universe(atoms);
final TupleFactory f = u.factory();
final Bounds b = new Bounds(u);
b.boundExactly(Person, f.allOf(1));
b.boundExactly(Hilary, f.setOf("Hilary"));
b.boundExactly(Jocelyn, f.setOf("Jocelyn"));
b.bound(spouse, f.allOf(2));
b.bound(shaken, f.allOf(2));
return b;
}
private static void usage() {
System.out.println("Usage: java examples.Handshake [# persons, must be >= 2]");
System.exit(1);
}
/**
* Usage: java examples.Handshake [# persons, must be >= 2]
*/
public static void main(String[] args) {
if (args.length < 1)
usage();
final Handshake model = new Handshake();
final Solver solver = new Solver();
try {
final int persons = Integer.parseInt(args[0]);
if (persons<2) usage();
solver.options().setBitwidth(6);
// solver.options().setSolver(SATFactory.ZChaff);
solver.options().setSolver(SATFactory.MiniSat);
solver.options().setSymmetryBreaking(0);
solver.options().setBitwidth(32 - Integer.numberOfLeadingZeros(persons));
solver.options().setReporter(new ConsoleReporter());
final Bounds b = model.bounds(persons);
final Formula f = model.runPuzzle();//.and(model.Person.count().eq(IntConstant.constant(persons)));
Solution sol = solver.solve(f, b);
System.out.println(sol);
} catch (NumberFormatException nfe) {
usage();
}
}
}