/* * Kodkod -- Copyright (c) 2005-2007, 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.alloy; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import kodkod.ast.Formula; import kodkod.ast.IntConstant; 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.Tuple; import kodkod.instance.TupleFactory; import kodkod.instance.TupleSet; import kodkod.instance.Universe; /** * Kodkod encoding of the group scheduling problem. * * @author Emina Torlak * */ public final class GroupScheduling { private final Relation person, group, round, assign; private final int ng; public GroupScheduling(int numGroups) { assert numGroups > 0; this.person = Relation.unary("Person"); this.group = Relation.unary("Group"); this.round = Relation.unary("Round"); this.assign = Relation.ternary("assign"); this.ng = numGroups; } public Bounds bounds() { final int p = ng*ng, r = ng + 1; final List<String> a = new ArrayList<String>((ng+1)*(ng+1)); for(int i = 0; i < p; i++) { a.add("p"+i); } for(int i = 0; i < ng; i++) { a.add("g"+i); } for(int i = 0; i < r; i++) { a.add("r"+i); } final Universe u = new Universe(a); final TupleFactory f = u.factory(); final Bounds b = new Bounds(u); b.boundExactly(person, f.range(f.tuple("p0"), f.tuple("p" + (p-1)))); b.boundExactly(group, f.range(f.tuple("g0"), f.tuple("g" + (ng-1)))); b.boundExactly(round, f.range(f.tuple("r0"), f.tuple("r" + (r-1)))); b.bound(assign, b.upperBound(person).product(b.upperBound(round)).product(b.upperBound(group))); final TupleSet low = f.noneOf(3); for(int i = 0; i < r; i++) { low.add(f.tuple("p0", "r"+i, "g0")); final int start = i*(ng-1) + 1, end = (i+1)*(ng-1); low.addAll(f.range(f.tuple("p"+start), f.tuple("p"+end)).product(f.setOf("r"+i)).product(f.setOf("g0"))); } final TupleSet high = f.noneOf(3); high.addAll(low); high.addAll(f.range(f.tuple("p1"), f.tuple("p" + (p-1))).product(b.upperBound(round)).product(b.upperBound(group))); b.bound(assign, low, high); return b; } public Formula schedule() { final Variable p = Variable.unary("p"), r = Variable.unary("r"), g = Variable.unary("g"); final Formula f0 = r.join(p.join(assign)).one().forAll(p.oneOf(person).and(r.oneOf(round))); final Formula f1 = assign.join(g).join(r).count().eq(IntConstant.constant(ng)).forAll(r.oneOf(round).and(g.oneOf(group))); final Variable pp = Variable.unary("p'"); final Formula f2 = p.join(assign).intersection(pp.join(assign)).some().forAll(p.oneOf(person).and(pp.oneOf(person.difference(p)))); return Formula.and(f0, f1, f2); } public static void main(String[] args) { final int ng = Integer.parseInt(args[0]); final GroupScheduling model = new GroupScheduling(ng); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.plingeling()); solver.options().setReporter(new ConsoleReporter()); solver.options().setSymmetryBreaking(ng*ng*ng*ng); final Formula f = model.schedule(); final Bounds b = model.bounds(); final Solution sol = solver.solve(f, b); final Map<String, List<Tuple>> m = new LinkedHashMap<String, List<Tuple>>(); final int p = ng*ng; for(Tuple t : sol.instance().tuples(model.assign)) { List<Tuple> l = m.get(t.atom(1)); if (l==null) { l = new ArrayList<Tuple>(p); m.put((String) t.atom(1), l); } l.add(t); } System.out.println(sol); for(Map.Entry<?, List<Tuple>> e : m.entrySet()) { System.out.println("ROUND " + e.getKey()); for(Tuple t : e.getValue()) { System.out.println(" " + t.atom(0) + " -> " + t.atom(2)); } } } }