/****************************************************************************** * Copyright (c) 2009 - 2015 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *****************************************************************************/ /** * */ package com.ibm.wala.memsat.util; import static kodkod.ast.Expression.NONE; import static kodkod.ast.Expression.product; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import kodkod.ast.ComparisonFormula; import kodkod.ast.Expression; import kodkod.ast.Formula; import kodkod.ast.MultiplicityFormula; import kodkod.ast.Node; import kodkod.ast.Relation; import kodkod.ast.Variable; import kodkod.ast.operator.ExprCompOperator; import kodkod.ast.visitor.AbstractReplacer; import kodkod.instance.Bounds; import kodkod.instance.TupleFactory; import kodkod.instance.TupleSet; /** * A collection of helper methods for constructing * Kodkod nodes (expression, formulas, and int expressions). * * * @author Emina Torlak */ public final class Nodes { static final Expression NONE2 = NONE.product(NONE); private Nodes() {} // singleton /** * Returns a unary tupleset, created by the given factory, consisting * of tuples that contain the given atoms. * @requires atoms in tuples.universe.atoms[int] * @return a unary tupleset, created by the given factory, consisting * of tuples that contain the given atoms. */ public static TupleSet tupleset(TupleFactory tuples, Collection<?> atoms) { final TupleSet ret = tuples.noneOf(1); for(Object atom : atoms) { ret.add( tuples.tuple(atom) ); } return ret; } /** * Returns an empty expression of the given arity. * @requires arity > 0 * @return Expression.NONE^arity */ public static Expression empty(int arity) { assert arity > 0; switch(arity) { case 1 : return NONE; case 2 : return NONE2; default : return product(Collections.nCopies(arity, NONE)); } } /** * Simplifies the argument formula, if possible, * using logic equivalence laws. * @return simplified formula */ public static Formula simplify(Formula formula) { // System.out.println("-----ORIGINAL-----"); // System.out.println(Strings.prettyPrint(formula, 2, 200)); final Set<Formula> conjuncts = kodkod.util.nodes.Nodes.roots(formula); final PartialCannonicalizer canonicalizer = new PartialCannonicalizer(); final Formula simplified = Formula.and(conjuncts).accept(canonicalizer); // System.out.println("-----SIMPLIFIED-----"); // System.out.println(Strings.prettyPrint(simplified, 2, 200)); return Propagator.apply(simplified, canonicalizer); } /** * Simplifies the given formula and bounds using logic equivalence laws. * This method assumes that the given bounds object is modifiable. * @effects simplifies the given bounds (if possible) by bounding some relations exactly * @return simplified formula */ @SuppressWarnings("unchecked") public static Formula simplify(final Formula formula, final Bounds bounds) { final AbstractReplacer replacer = new AbstractReplacer(Collections.EMPTY_SET) { protected <N extends Node> N cache(N node, N replacement) { cache.put(node, replacement); return replacement; } public Formula visit(ComparisonFormula formula) { Formula ret = lookup(formula); if (ret!=null) return ret; if (formula.left() instanceof Relation && formula.right() instanceof Relation) { final Relation left = (Relation) formula.left(); final Relation right = (Relation) formula.right(); final TupleSet ll = bounds.lowerBound(left), lu = bounds.upperBound(left); final TupleSet rl = bounds.lowerBound(right), ru = bounds.upperBound(right); if (ll.equals(lu) && rl.equals(ru)) { if (formula.op()==ExprCompOperator.EQUALS) return cache(formula, ll.equals(rl) ? Formula.TRUE : Formula.FALSE); else return cache(formula, rl.containsAll(ll) ? Formula.TRUE : Formula.FALSE); } } return cache(formula, formula); } }; final Set<Formula> conjuncts = kodkod.util.nodes.Nodes.roots(simplify(formula.accept(replacer))); for(Iterator<Formula> itr = conjuncts.iterator(); itr.hasNext(); ) { final Formula next = itr.next(); if (next instanceof MultiplicityFormula) { final MultiplicityFormula multFormula = (MultiplicityFormula) next; if (multFormula.expression() instanceof Relation) { final Relation r = (Relation) multFormula.expression(); final TupleSet u = bounds.upperBound(r); final TupleSet l = bounds.lowerBound(r); if (u.size()==1 && l.isEmpty()) { switch(multFormula.multiplicity()) { case ONE : case SOME : bounds.boundExactly(r, u); break; case NO : bounds.boundExactly(r, l); break; case LONE : break; // do nothing default : throw new AssertionError("Unknown multiplicity: " + multFormula.multiplicity()); } itr.remove(); } } } } return Formula.and(conjuncts); } /** * Returns a formula that evaluates to true iff r is a total order * over the set s. * @requires r.arity = 2 and s.arity = 1 * @return a formula that evaluates to true iff r is reflexive, complete, * transitive and antisymmetric over the set s. */ public static Formula totalOrder(Expression r, Expression s) { assert r.arity() == 2 && s.arity() == 1; final List<Formula> to = new ArrayList<Formula>(); // reflexive and complete over s to.add( s.product(s).in(r.union(r.transpose())) ); // transitive to.add( r.join(r).in(r) ); // antisymmetric to.add( r.intersection(r.transpose()).in(Expression.IDEN) ); return Formula.and(to); } /** * Returns an expression that evaluates to the transitive reduction of r. * @requires r.arity = 2 * @return an expression that evaluates to the transitive reduction of r. */ public static Expression transitiveReduction(Expression r) { final Variable a = Variable.unary("a"); final Variable b = Variable.unary("b"); final Expression dom = r.join(Expression.UNIV); final Expression ran = a.join(r); final Formula reduct = a.join(r).intersection(r.join(b)).in(a.union(b));//r.difference(a.product(b)).closure().eq(r.closure()).not(); return reduct.comprehension(a.oneOf(dom).and(b.oneOf(ran))); } /** * Returns a node generated by replacing the nodes in replacements.keySet() * with their corresponding values. * @requires all n: replacements.keySet() | * n in Formula => replacements.get(n) in Formula && * n in Decl => replacements.get(n) in Decl && * n in Decls => replacements.get(n) in Decls && * n in IntExpression => replacements.get(n) in IntExpression && * n in Expression => replacements.get(n) in Expression && n.arity = replacements.get(n).arity * @return a node generated by replacing the nodes in replacement.keySet() * with their corresponding values. */ @SuppressWarnings("unchecked") public static <N extends Node> N replaceAll(N node, final Map<? extends Node, ? extends Node> replacements) { final AbstractReplacer replacer = new AbstractReplacer(Collections.EMPTY_SET) { protected <T extends Node> T cache(T node, T replacement) { if (replacements.containsKey(node)) { replacement = (T) replacements.get(node); } super.cache.put(node, replacement); return replacement; } }; return (N) node.accept(replacer); } }