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.satlab.SATFactory; import kodkod.instance.Bounds; import kodkod.instance.TupleFactory; import kodkod.instance.TupleSet; import kodkod.instance.Universe; /** * A kodkod encoding of netconfig.als: * <pre> * module internal/netconfig2 open util/ordering[Time] as tick * * sig Time {} * * abstract sig Site {} * sig HQ, Sub extends Site {} * * sig Router { * site: Site, * satellite: Router -> Time, * lineOfSight: Router -> Time * } * * pred invariants() { * -- every site has at least one Router Site in Router.site * -- links are symmetric and non-reflexive * all t: Time | lineOfSight.t = ~(lineOfSight.t) && no lineOfSight.t & iden * all t: Time | satellite.t = ~(satellite.t) && no satellite.t & iden * * -- no two Routers are connected with two both a satellite and lineOfSight * -- link at the same Time * all t: Time | no satellite.t & lineOfSight.t * * -- there is at most one satellite link at any given Time: a resource constraint * all t: Time | no satellite || some r1, r2: Router | r1->r2 + r2->r1 = satellite.t * } * * pred subsConnectedToHQ(endTime: Time) { * -- every Sub location is connected to an HQ location at the given Time * all subRouter: site.Sub | some hqRouter: site.HQ | * subRouter->hqRouter in (satellite + lineOfSight).endTime * } * * pred connectedSites(sites: set Site, endTime: Time) { * -- all sites in the given set are connected to each other at the given Time * all s: sites | some r: site.s | * sites - s in (r.^((satellite + lineOfSight).endTime)).site * } * * pred addSatelliteLink(r1, r2: Router, t: Time) { r1->r2 in satellite.t } * * pred addLineOfSightLink(r1, r2: Router, t: Time) { * r1->r2 in satellite.tick/prev(t) && r1->r2 in lineOfSight.t } * * pred continuedConnection(r1, r2: Router, t: Time) { * r1->r2 & lineOfSight.tick/prev(t) = r1->r2 & lineOfSight.t } * * pred lostConnection(r1, r2: Router, t: Time) { * r1->r2 in lineOfSight.tick/prev(t) && no r1->r2 & lineOfSight.t } * * pred traces() { * all r1, r2: Router, t: Time | * addSatelliteLink(r1, r2, t) || addLineOfSightLink(r1, r2, t) || * continuedConnection(r1, r2, t) || lostConnection(r1, r2, t) } * * pred show() { * invariants() && subsConnectedToHQ(tick/last()) && * connectedSites(Site, tick/last()) && traces() } * * --75.03 secs with Berkmin, p cnf 36512 499783 * run show for exactly 1 HQ, exactly 9 Sub, exactly 10 Router, exactly 9 Time * </pre> * @author Emina Torlak */ public class Netconfig { // sigs private final Relation Time, Router, Site, HQ, Sub; // fields private final Relation site, satellite, lineOfSight, start, end, tick; /** * Constructs an instance of the Netconfig problem. */ public Netconfig() { Time = Relation.unary("Time"); start = Relation.unary("start"); end = Relation.unary("end"); tick = Relation.binary("tick"); Router = Relation.unary("Router"); Site = Relation.unary("Site"); HQ = Relation.unary("HQ"); Sub = Relation.unary("Sub"); site = Relation.binary("site"); satellite = Relation.ternary("satellite"); lineOfSight = Relation.ternary("lineOfSight"); } /** * Returns a formula stating that r is symmetric and non reflexive. * @requires r.arity = 2 * @return r = ~r && no r & iden */ private final Formula symmNonRefl(Expression r) { assert r.arity() == 2; return r.eq(r.transpose()).and(r.intersection(Expression.IDEN).no()); } /** * Returns the constraints implicit in signature and field declarations. * @return the constraints implicit in signature and field declarations. */ 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); // satellite in Router->Router->Time && lineOfSight in Router->Router->Time final Expression rrt = Router.product(Router).product(Time); final Formula satLos = satellite.in(rrt).and(lineOfSight.in(rrt)); // tick is a total ordering on time final Formula tord = tick.totalOrder(Time, start, end); return hqSub.and(siteFun).and(satLos).and(tord); } /** * Returns the invariants predicate. * @return invariants */ public Formula invariants() { final Variable t = Variable.unary("t"); final Expression losAtT = lineOfSight.join(t); final Expression satAtT = satellite.join(t); final Formula symNonRefl = symmNonRefl(losAtT).and(symmNonRefl(satAtT)); final Formula noSatAndLos = satAtT.intersection(losAtT).no(); final Variable r1 = Variable.unary("r1"); final Variable r2 = Variable.unary("r2"); final Expression productUnion = r1.product(r2).union(r2.product(r1)); final Formula someSatAtT = productUnion.eq(satAtT).forSome(r1.oneOf(Router).and(r2.oneOf(Router))); final Formula loneSatAtT = satellite.no().or(someSatAtT); return symNonRefl.and(noSatAndLos).and(loneSatAtT).forAll(t.oneOf(Time)); } /** * Returns the subsConnectedToHQ predicate. * @return subsConnectedToHQ * } */ public Formula subsConnectedToHQ(Expression endTime) { final Variable subRouter = Variable.unary("subRouter"); final Variable hqRouter = Variable.unary("hqRouter"); final Formula f = subRouter.product(hqRouter).in(satellite.union(lineOfSight).join(endTime)); return f.forSome(hqRouter.oneOf(site.join(HQ))).forAll(subRouter.oneOf(site.join(Sub))); } /** * Returns the connectedSites predicate. * @return connectedSites */ public Formula connectedSites(Expression sites, Expression endTime) { final Variable s = Variable.unary("s"); final Variable r = Variable.unary("r"); final Expression linksAtEndTime = satellite.union(lineOfSight).join(endTime); final Formula f = sites.difference(s).in(r.join(linksAtEndTime.closure()).join(site)); return f.forSome(r.oneOf(site.join(s))).forAll(s.oneOf(sites)); } /** * Returns the addSatelliteLink predicate * @return addSatelliteLink */ public Formula addSatelliteLink(Expression r1, Expression r2, Expression t) { return r1.product(r2).in(satellite.join(t)); } /** * Returns the addLineOfSightLink predicate. * @return addLineOfSightLink */ public Formula addLineOfSightLink(Expression r1, Expression r2, Expression t) { final Expression link = r1.product(r2); return link.in(satellite.join(tick.join(t))).and(link.in(lineOfSight.join(t))); } /** * Returns the continuedConnection predicate. * @return continuedConnection */ public Formula continuedConnection(Expression r1, Expression r2, Expression t) { final Expression link = r1.product(r2); return link.intersection(lineOfSight.join(tick.join(t))).eq(link.intersection(lineOfSight.join(t))); } /** * Returns the lostConnection predicate. * @return lostConnection */ public Formula lostConnection(Expression r1, Expression r2, Expression t) { final Expression link = r1.product(r2); return link.in(lineOfSight.join(tick.join(t))).and(link.intersection(lineOfSight.join(t)).no()); } /** * Returns the traces predicate. * @return traces */ public Formula traces() { final Variable r1 = Variable.unary("r1"); final Variable r2 = Variable.unary("r2"); final Variable t = Variable.unary("t"); final Formula f = addSatelliteLink(r1, r2, t).or(addLineOfSightLink(r1, r2, t)). or(continuedConnection(r1, r2, t)).or(lostConnection(r1, r2, t)); return f.forAll(r1.oneOf(Router).and(r2.oneOf(Router)).and(t.oneOf(Time))); } /** * Returns the show predicate. * @return show */ public Formula show() { return declarations().and(invariants()).and(subsConnectedToHQ(end)). and(connectedSites(Site, end)).and(traces()); } /** * Returns a bounds object that constructs the 'scope' for analyzing * the commands, using the given values for the number of sites, * routers, and the length of time. * @requires all arguments are positive and hqNum <= siteNum * @return a bounds for the model */ public Bounds bounds(int siteNum, int hqNum, int routerNum, int timeLength) { assert siteNum > 0 && hqNum > 0 && hqNum <= siteNum && routerNum > 0 && timeLength > 0; final List<String> atoms = new ArrayList<String>(siteNum+routerNum+timeLength); for(int i = 0; i < siteNum; i++) { atoms.add("Site"+i); } for(int i = 0; i < routerNum; i++) { atoms.add("Router"+i); } for(int i = 0; i < timeLength; i++) { atoms.add("Time"+i); } final Universe u = new Universe(atoms); final TupleFactory f = u.factory(); final Bounds b = new Bounds(u); final String site0 = "Site0"; final String siteN = "Site" + (siteNum-1); final TupleSet sBound = f.range(f.tuple(site0), f.tuple(siteN)); b.boundExactly(Site, sBound); b.boundExactly(HQ, f.range(f.tuple(site0), f.tuple("Site" + (hqNum-1)))); if (hqNum < siteNum) { b.boundExactly(Sub, f.range(f.tuple("Site" + hqNum), f.tuple(siteN))); } else { b.bound(Sub, f.noneOf(1)); } final TupleSet tBound = f.range(f.tuple("Time0"), f.tuple("Time" + (timeLength-1))); b.bound(Time, tBound); b.bound(start, tBound); b.bound(end, tBound); b.bound(tick, tBound.product(tBound)); final TupleSet rBound = f.range(f.tuple("Router0"), f.tuple("Router"+(routerNum-1))); b.boundExactly(Router, rBound); //b.bound(site, rBound.product(sBound)); b.bound(satellite, rBound.product(rBound).product(tBound)); b.bound(lineOfSight, b.upperBound(satellite)); assert siteNum == routerNum; final TupleSet siteBound = f.noneOf(2); for(int i = 0; i < siteNum; i++) siteBound.add(f.tuple("Router"+i, "Site"+i)); b.boundExactly(site, siteBound);//rBound.product(sBound)); return b; } private static final void usage() { System.out.println("Usage: java examples.Netconfig [# sites] [# hq] [# routers] [# time steps]"); System.exit(1); } /** * Usage: java examples.Netconfig [# sites] [# hq] [# routers] [# time steps] */ public static void main(String[] args) { if (args.length < 4) usage(); final Netconfig model = new Netconfig(); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.MiniSat); try { final Formula show = model.show(); final Solution sol = solver.solve(show, model.bounds(Integer.parseInt(args[0]),Integer.parseInt(args[1]),Integer.parseInt(args[2]),Integer.parseInt(args[3]))); // System.out.println(show); //System.out.println("p cnf " + (solver.numberOfIntermediateVariables()+solver.numberOfPrimaryVariables()) + " " + solver.numberOfClauses()); System.out.println(sol.outcome()); System.out.println(sol.stats()); } catch (NumberFormatException nfe) { usage(); } } }