/** If ex is a simple combination of Relations, then return that combination, else return null. */ private Expression sim(Expr ex) { while (ex instanceof ExprUnary) { ExprUnary u = (ExprUnary) ex; if (u.op != ExprUnary.Op.NOOP && u.op != ExprUnary.Op.EXACTLYOF) break; ex = u.sub; } if (ex instanceof ExprBinary) { ExprBinary b = (ExprBinary) ex; if (b.op == ExprBinary.Op.ARROW || b.op == ExprBinary.Op.PLUS || b.op == ExprBinary.Op.JOIN) { Expression left = sim(b.left); if (left == null) return null; Expression right = sim(b.right); if (right == null) return null; if (b.op == ExprBinary.Op.ARROW) return left.product(right); if (b.op == ExprBinary.Op.PLUS) return left.union(right); else return left.join(right); } } if (ex instanceof ExprConstant) { switch (((ExprConstant) ex).op) { case EMPTYNESS: return Expression.NONE; } } if (ex == Sig.NONE) return Expression.NONE; if (ex == Sig.SIGINT) return Expression.INTS; if (ex instanceof Sig) return sol.a2k((Sig) ex); if (ex instanceof Field) return sol.a2k((Field) ex); return null; }
/** Allocate relations for SubsetSig top-down. */ private Expression allocateSubsetSig(SubsetSig sig) throws Err { // We must not visit the same SubsetSig more than once, so if we've been here already, then // return the old value right away Expression sum = sol.a2k(sig); if (sum != null && sum != Expression.NONE) return sum; // Recursively form the union of all parent expressions TupleSet ts = factory.noneOf(1); for (Sig parent : sig.parents) { Expression p = (parent instanceof PrimSig) ? sol.a2k(parent) : allocateSubsetSig((SubsetSig) parent); ts.addAll(sol.query(true, p, false)); if (sum == null) sum = p; else sum = sum.union(p); } // If subset is exact, then just use the "sum" as is if (sig.exact) { sol.addSig(sig, sum); return sum; } // Allocate a relation for this subset sig, then bound it rep.bound("Sig " + sig + " in " + ts + "\n"); Relation r = sol.addRel(sig.label, null, ts); sol.addSig(sig, r); // Add a constraint that it is INDEED a subset of the union of its parents sol.addFormula(r.in(sum), sig.isSubset); return r; }
/** * Helper method that returns the constraint that the sig has exactly "n" elements, or at most "n" * elements */ private Formula size(Sig sig, int n, boolean exact) { Expression a = sol.a2k(sig); if (n <= 0) return a.no(); if (n == 1) return exact ? a.one() : a.lone(); Formula f = exact ? Formula.TRUE : null; Decls d = null; Expression sum = null; while (n > 0) { n--; Variable v = Variable.unary(""); kodkod.ast.Decl dd = v.oneOf(a); if (d == null) d = dd; else d = dd.and(d); if (sum == null) sum = v; else { if (f != null) f = v.intersection(sum).no().and(f); sum = v.union(sum); } } if (f != null) return sum.eq(a).and(f).forSome(d); else return a.no().or(sum.eq(a).forSome(d)); }
/** Allocate relations for nonbuiltin PrimSigs bottom-up. */ private Expression allocatePrimSig(PrimSig sig) throws Err { // Recursively allocate all children expressions, and form the union of them Expression sum = null; for (PrimSig child : sig.children()) { Expression childexpr = allocatePrimSig(child); if (sum == null) { sum = childexpr; continue; } // subsigs are disjoint sol.addFormula(sum.intersection(childexpr).no(), child.isSubsig); sum = sum.union(childexpr); } TupleSet lower = lb.get(sig).clone(), upper = ub.get(sig).clone(); if (sum == null) { // If sig doesn't have children, then sig should make a fresh relation for itself sum = sol.addRel(sig.label, lower, upper); } else if (sig.isAbstract == null) { // If sig has children, and sig is not abstract, then create a new relation to act as the // remainder. for (PrimSig child : sig.children()) { // Remove atoms that are KNOWN to be in a subsig; // it's okay to mistakenly leave some atoms in, since we will never solve for the // "remainder" relation directly; // instead, we union the remainder with the children, then solve for the combined solution. // (Thus, the more we can remove, the more efficient it gets, but it is not crucial for // correctness) TupleSet childTS = sol.query(false, sol.a2k(child), false); lower.removeAll(childTS); upper.removeAll(childTS); } sum = sum.union(sol.addRel(sig.label + " remainder", lower, upper)); } sol.addSig(sig, sum); return sum; }
/** * Computes the bounds for sigs/fields, then construct a BoundsComputer object that you can query. */ private BoundsComputer(A4Reporter rep, A4Solution sol, ScopeComputer sc, Iterable<Sig> sigs) throws Err { this.sc = sc; this.factory = sol.getFactory(); this.rep = rep; this.sol = sol; // Figure out the sig bounds final Universe universe = factory.universe(); final int atomN = universe.size(); final List<Tuple> atoms = new ArrayList<Tuple>(atomN); for (int i = atomN - 1; i >= 0; i--) atoms.add(factory.tuple(universe.atom(i))); for (Sig s : sigs) if (!s.builtin && s.isTopLevel()) computeLowerBound(atoms, (PrimSig) s); for (Sig s : sigs) if (!s.builtin && s.isTopLevel()) computeUpperBound((PrimSig) s); // Bound the sigs for (Sig s : sigs) if (!s.builtin && s.isTopLevel()) allocatePrimSig((PrimSig) s); for (Sig s : sigs) if (s instanceof SubsetSig) allocateSubsetSig((SubsetSig) s); // Bound the fields again: for (Sig s : sigs) { while (s.isOne != null && s.getFieldDecls().size() == 2 && s.getFields().size() == 2 && s.getFacts().size() == 1) { // Let's check whether this is a total ordering on an enum... Expr fact = s.getFacts().get(0).deNOP(), b1 = s.getFieldDecls().get(0).expr.deNOP(), b2 = s.getFieldDecls().get(1).expr.deNOP(), b3; if (!(fact instanceof ExprList) || !(b1 instanceof ExprUnary) || !(b2 instanceof ExprBinary)) break; ExprList list = (ExprList) fact; if (list.op != ExprList.Op.TOTALORDER || list.args.size() != 3) break; if (((ExprUnary) b1).op != ExprUnary.Op.SETOF) break; else b1 = ((ExprUnary) b1).sub.deNOP(); if (((ExprBinary) b2).op != ExprBinary.Op.ARROW) break; else { b3 = ((ExprBinary) b2).right.deNOP(); b2 = ((ExprBinary) b2).left.deNOP(); } if (!(b1 instanceof PrimSig) || b1 != b2 || b1 != b3) break; PrimSig sub = (PrimSig) b1; Field f1 = s.getFields().get(0), f2 = s.getFields().get(1); if (sub.isEnum == null || !list.args.get(0).isSame(sub) || !list.args.get(1).isSame(s.join(f1)) || !list.args.get(2).isSame(s.join(f2))) break; // Now, we've confirmed it is a total ordering on an enum. Let's pre-bind the relations TupleSet me = sol.query(true, sol.a2k(s), false), firstTS = factory.noneOf(2), lastTS = null, nextTS = factory.noneOf(3); if (me.size() != 1 || me.arity() != 1) break; int n = sub.children().size(); for (PrimSig c : sub.children()) { TupleSet TS = sol.query(true, sol.a2k(c), false); if (TS.size() != 1 || TS.arity() != 1) { firstTS = factory.noneOf(2); nextTS = factory.noneOf(3); break; } if (lastTS == null) { firstTS = me.product(TS); lastTS = TS; continue; } nextTS.addAll(me.product(lastTS).product(TS)); lastTS = TS; } if (firstTS.size() != (n > 0 ? 1 : 0) || nextTS.size() != n - 1) break; sol.addField(f1, sol.addRel(s.label + "." + f1.label, firstTS, firstTS)); sol.addField(f2, sol.addRel(s.label + "." + f2.label, nextTS, nextTS)); rep.bound("Field " + s.label + "." + f1.label + " == " + firstTS + "\n"); rep.bound("Field " + s.label + "." + f2.label + " == " + nextTS + "\n"); continue again; } for (Field f : s.getFields()) { boolean isOne = s.isOne != null; if (isOne && f.decl().expr.mult() == ExprUnary.Op.EXACTLYOF) { Expression sim = sim(f.decl().expr); if (sim != null) { rep.bound("Field " + s.label + "." + f.label + " defined to be " + sim + "\n"); sol.addField(f, sol.a2k(s).product(sim)); continue; } } Type t = isOne ? Sig.UNIV.type().join(f.type()) : f.type(); TupleSet ub = factory.noneOf(t.arity()); for (List<PrimSig> p : t.fold()) { TupleSet upper = null; for (PrimSig b : p) { TupleSet tmp = sol.query(true, sol.a2k(b), false); if (upper == null) upper = tmp; else upper = upper.product(tmp); } ub.addAll(upper); } Relation r = sol.addRel(s.label + "." + f.label, null, ub); sol.addField(f, isOne ? sol.a2k(s).product(r) : r); } } // Add any additional SIZE constraints for (Sig s : sigs) if (!s.builtin) { Expression exp = sol.a2k(s); TupleSet upper = sol.query(true, exp, false), lower = sol.query(false, exp, false); final int n = sc.sig2scope(s); if (s.isOne != null && (lower.size() != 1 || upper.size() != 1)) { rep.bound("Sig " + s + " in " + upper + " with size==1\n"); sol.addFormula(exp.one(), s.isOne); continue; } if (s.isSome != null && lower.size() < 1) sol.addFormula(exp.some(), s.isSome); if (s.isLone != null && upper.size() > 1) sol.addFormula(exp.lone(), s.isLone); if (n < 0) continue; // This means no scope was specified if (lower.size() == n && upper.size() == n && sc.isExact(s)) { rep.bound("Sig " + s + " == " + upper + "\n"); } else if (sc.isExact(s)) { rep.bound("Sig " + s + " in " + upper + " with size==" + n + "\n"); sol.addFormula(size(s, n, true), Pos.UNKNOWN); } else if (upper.size() <= n) { rep.bound("Sig " + s + " in " + upper + "\n"); } else { rep.bound("Sig " + s + " in " + upper + " with size<=" + n + "\n"); sol.addFormula(size(s, n, false), Pos.UNKNOWN); } } }