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.TupleSet; import kodkod.instance.Universe; /** * A kodkod encoding of bigconfig.als: * * <pre> * module internal/bigconfig * * abstract sig Site {} * sig HQ, Sub extends Site {} * * sig Router { * site: Site, * link: set Router * } * * pred invariants() { * -- every site has at least one router * Site in Router.site * * -- links are symmetric and non-reflexive * link = ~link * no link & iden * } * * pred subsConnectedToHQ() { * -- every sub location is connected to an HQ location at the given time * site.Sub in (site.HQ).link * } * * pred connectedSites(sites: set Site) { * -- all sites in the given set are connected to each other * all s: sites | sites - s in ((site.s).ˆlink).site * } * * pred show() { * invariants() && subsConnectedToHQ() && connectedSites(Site) * } * </pre> * * @author Emina Torlak */ public class Bigconfig { // sigs private final Relation Router, Site, HQ, Sub; // fields private final Relation site, link; private final int closureApprox; /** * Constructs an instance of the BigConfig example. */ public Bigconfig(int closureApprox) { Router = Relation.unary("Router"); Site = Relation.unary("Site"); HQ = Relation.unary("HQ"); Sub = Relation.unary("Sub"); site = Relation.binary("site"); link = Relation.binary("link"); this.closureApprox = closureApprox; } /** * Returns the constraints implicit in signature and field declarations. * @return * abstract sig Site {} * sig HQ, Sub extends Site {} * * sig Router { * site: Site, * link: set Router * } */ public Formula declarations() { // HQ + Sub in Site && no HQ & Sub final Formula hqSub = HQ.union(Sub).in(Site).and(HQ.intersection(Sub).no()); // site is a function from Router to Site final Formula siteFun = site.function(Router, Site); // link in Router->Router final Formula links = link.in(Router.product(Router)); return hqSub.and(siteFun).and(links); } /** * Returns the invariants predicate. * @return pred invariants() { * -- every site has at least one router * Site in Router.site * * -- links are symmetric and non-reflexive * link = ~link * no link & iden * } */ public Formula invariants() { Formula atLeastARouter = Site.in(Router.join(site)); Formula linksSymmetric = link.eq(link.transpose()); Formula linksNotReflexive = link.intersection(Expression.IDEN).no(); return atLeastARouter.and(linksSymmetric).and(linksNotReflexive); } /** * Returns the subsConnectedToHQ predicate. * @return pred subsConnectedToHQ() { * -- every sub location is connected to an HQ location at the given time * site.Sub in (site.HQ).link * } */ public Formula subsConnectedToHQ() { return site.join(Sub).in(site.join(HQ).join(link)); } /** * Returns the connectedSites predicate. * @return pred connectedSites(sites: set Site) { * -- all sites in the given set are connected to each other * all s: sites | sites - s in ((site.s).^link).site * } */ public Formula connectedSites(Expression sites) { final Variable s = Variable.unary("s"); Expression closed; if (closureApprox > 0) { closed = link; for(int i = 1; i < closureApprox; i *= 2) { closed = closed.union(closed.join(closed)); } } else { closed = link.closure(); } final Expression sreachable = site.join(s).join(closed).join(site); final Formula f = sites.difference(s).in(sreachable); return f.forAll(s.oneOf(sites)); } /** * Returns the show predicate. * @return pred show() { * invariants() && subsConnectedToHQ() && connectedSites(Site) * } */ public Formula show() { return declarations().and(invariants()).and(subsConnectedToHQ()).and(connectedSites(Site)); } /** * @return a universe with n routers and n * sites. The first n atoms are sites. */ private Universe universe(int n) { final List<String> atoms = new ArrayList<String>(n*2); for(int i = 0; i < n; i++) { atoms.add("Site"+i); } for(int i = 0; i < n; i++) { atoms.add("Router"+i); } return new Universe(atoms); } /** * Returns a bounds with the * given number of hqs and subs, constructed using the * given universe. * @requires hqNum > 0 && subNum > 0 * @requires u contains at least (hqNum+sub) Router atoms and * as many Site atoms * @return bounds */ private Bounds bounds(int hqNum, int subNum, Universe u) { final TupleFactory f = u.factory(); final Bounds b = new Bounds(u); final int siteMax = hqNum + subNum - 1; final String site0 = "Site0"; final String siteN = "Site" + siteMax; final String siteHQ = "Site" + (hqNum-1); final String siteSub = "Site" + hqNum; final String router0 = "Router0"; final String routerN = "Router" + siteMax; final TupleSet sites = f.range(f.tuple(site0), f.tuple(siteN)); b.boundExactly(Site, sites); b.boundExactly(HQ, f.range(f.tuple(site0), f.tuple(siteHQ))); b.boundExactly(Sub, f.range(f.tuple(siteSub), f.tuple(siteN))); final TupleSet routers = f.range(f.tuple(router0), f.tuple(routerN)); b.boundExactly(Router, routers); b.bound(link, routers.product(routers)); // b.bound(site, routers.product(sites)); final TupleSet routerLocations = f.noneOf(2); for(int i = 0; i <= siteMax; i++) { routerLocations.add(f.tuple("Router"+i, "Site"+i)); } b.boundExactly(site, routerLocations); return b; } /** * Returns a bounds with the * given number of hqs and subs. The parameter routerNum * designates the total number of routers in the universe. * @requires hqNum > 0 && subNum > 0 * @requires hqNum + subNum <= routers * @return bounds */ public Bounds bounds(int hqNum, int subNum, int routerNum) { assert hqNum > 0 && subNum > 0 && hqNum + subNum <= routerNum; return bounds(hqNum, subNum, universe(routerNum)); } private static void usage() { System.out.println("Usage: java tests.Bigconfig [# hq] [# sub] [# closure unwindings, 0 for true closure] [size of partial instance, 0 default]"); System.exit(1); } /** * Usage: java tests.Bigconfig [# hq] [# sub] [# closure unwindings, 0 for true closure] [size of partial instance, 0 default] * @throws InterruptedException */ public static void main(String[] args) { if (args.length < 3) usage(); final Bigconfig model = new Bigconfig(Integer.parseInt(args[2])); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.MiniSat); try { final int hq = Integer.parseInt(args[0]); final int sub = Integer.parseInt(args[1]); final int partial = args.length > 3 ? Integer.parseInt(args[3]) : 0; final Formula show = model.show(); if (partial>0) { Bounds bounds = model.bounds(hq, sub-partial, hq+sub); Solution sol = solver.solve(show, bounds); System.out.println("Solved for " + hq + " hq and " + (sub-partial) + " subs."); System.out.println(sol.outcome()); System.out.println(sol.stats()); System.out.println("Solving again with a partial instance: " + hq + " hq and " + sub + " subs."); bounds = model.bounds(hq, sub, bounds.universe()); bounds.bound(model.link, sol.instance().tuples(model.link), bounds.upperBound(model.link)); sol = solver.solve(show, bounds); System.out.println(sol.outcome()); System.out.println(sol.stats()); } else { solver.options().setReporter(new ConsoleReporter()); final Bounds bounds = model.bounds(hq, sub, hq + sub); final Solution sol = solver.solve(show, bounds); System.out.println(sol.outcome()); System.out.println(sol.stats()); } } catch (NumberFormatException nfe) { usage(); } } }