Package kodkod.engine.fol2sat

Source Code of kodkod.engine.fol2sat.FOL2BoolTranslator

/*
* 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.engine.fol2sat;

import java.util.ArrayList;
import java.util.List;

import kodkod.ast.BinaryExpression;
import kodkod.ast.BinaryFormula;
import kodkod.ast.BinaryIntExpression;
import kodkod.ast.ComparisonFormula;
import kodkod.ast.Comprehension;
import kodkod.ast.ConstantExpression;
import kodkod.ast.ConstantFormula;
import kodkod.ast.Decl;
import kodkod.ast.Decls;
import kodkod.ast.ExprToIntCast;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IfExpression;
import kodkod.ast.IfIntExpression;
import kodkod.ast.IntComparisonFormula;
import kodkod.ast.IntConstant;
import kodkod.ast.IntExpression;
import kodkod.ast.IntToExprCast;
import kodkod.ast.MultiplicityFormula;
import kodkod.ast.NaryExpression;
import kodkod.ast.NaryFormula;
import kodkod.ast.NaryIntExpression;
import kodkod.ast.Node;
import kodkod.ast.NotFormula;
import kodkod.ast.ProjectExpression;
import kodkod.ast.QuantifiedFormula;
import kodkod.ast.Relation;
import kodkod.ast.RelationPredicate;
import kodkod.ast.SumExpression;
import kodkod.ast.UnaryExpression;
import kodkod.ast.UnaryIntExpression;
import kodkod.ast.Variable;
import kodkod.ast.operator.ExprCompOperator;
import kodkod.ast.operator.ExprOperator;
import kodkod.ast.operator.FormulaOperator;
import kodkod.ast.operator.Multiplicity;
import kodkod.ast.operator.Quantifier;
import kodkod.ast.visitor.ReturnVisitor;
import kodkod.engine.bool.BooleanAccumulator;
import kodkod.engine.bool.BooleanConstant;
import kodkod.engine.bool.BooleanFactory;
import kodkod.engine.bool.BooleanMatrix;
import kodkod.engine.bool.BooleanValue;
import kodkod.engine.bool.Dimensions;
import kodkod.engine.bool.Int;
import kodkod.engine.bool.Operator;
import kodkod.util.ints.IndexedEntry;
import kodkod.util.ints.IntIterator;
import kodkod.util.ints.IntSet;
import kodkod.util.nodes.AnnotatedNode;
import kodkod.util.nodes.Nodes;

/**
* Translates an annotated node to boolean representation.
* @specfield node: AnnotatedNode<? extends Node> // node to translate
* @specfield interpreter: LeafInterpreter // the interpreter used for translation  
* @specfield env: Environment<BooleanMatrix> // current environment
* @author Emina Torlak
*/
abstract class FOL2BoolTranslator implements ReturnVisitor<BooleanMatrix, BooleanValue, Object, Int> {
 
  /**
   * Translates the given annotated formula or expression into a boolean
   * formula or matrix, using the provided interpreter.
   * @requires interpreter.relations = AnnotatedNode.relations(annotated)
   * @return {transl: T |
   *           annotated.node in Formula => transl in BooleanValue,
   *           annotated.node in Expression => transl in BooleanMatrix,
   *           annotated.node in IntExpression => transl in Int}
   * @throws HigherOrderDeclException - annotated.node contains a higher order declaration
   * @throws UnboundLeafException - annotated.node refers to an undeclared variable
   **/
  @SuppressWarnings("unchecked")
  static final <T> T translate(AnnotatedNode<? extends Node> annotated, LeafInterpreter interpreter) {
    final FOL2BoolCache cache = new FOL2BoolCache(annotated);
    final FOL2BoolTranslator translator = new FOL2BoolTranslator(cache, interpreter) {};
    return (T) annotated.node().accept(translator);
  }

  /**
   * Translates the given annotated formula into a boolean
   * accumulator with respect to the given interpreter and logs the translation events to the given logger. 
   * @requires interpreter.relations = AnnotatedNode.relations(annotated)
   * @requires annotated.source[annotated.sourceSensitiveRoots()] = Nodes.roots(annotated.source[annotated.node])
   * @return BooleanAccumulator that is the meaning of the given annotated formula with respect to the given interpreter
   * @effects log.records' contains the translation events that occurred while generating the returned value
   * @throws HigherOrderDeclException - annotated.node contains a higher order declaration
   * @throws UnboundLeafException - annotated.node refers to an undeclared variable
   **/
  static final BooleanAccumulator translate(final AnnotatedNode<Formula> annotated, LeafInterpreter interpreter, final TranslationLogger logger) {
    final FOL2BoolCache cache = new FOL2BoolCache(annotated);
    final FOL2BoolTranslator translator = new FOL2BoolTranslator(cache, interpreter) {
      BooleanValue cache(Formula formula, BooleanValue translation) {
        logger.log(formula, translation, super.env);
        return super.cache(formula, translation);
     
    };
    final BooleanAccumulator acc = BooleanAccumulator.treeGate(Operator.AND);
   
    for(Formula root : Nodes.conjuncts(annotated.node())) {  
      acc.add(root.accept(translator));
    }
    logger.close();
    return acc;
  }
 
  /**
   * Translates the given annotated expression into a boolean
   * matrix that is a least sound upper bound on the expression's
   * value, given the leaf and variable bindings in the
   * the provided interpreter and environment.
   * @requires interpreter.relations = AnnotatedNode.relations(annotated)
   * @return a boolean matrix that is a least sound upper bound on the expression's value
   * @throws HigherOrderDeclException - annotated.node contains a higher order declaration
   * @throws UnboundLeafException - annotated.node refers to a variable that neither declared nor bound in env
   **/
  @SuppressWarnings("unchecked")
  static final BooleanMatrix approximate(AnnotatedNode<Expression> annotated, LeafInterpreter interpreter, Environment<BooleanMatrix> env) {
    final FOL2BoolTranslator approximator = new FOL2BoolTranslator(new FOL2BoolCache(annotated), interpreter, env) {
      public final BooleanMatrix visit(BinaryExpression binExpr) {
        final BooleanMatrix ret = lookup(binExpr);
        if (ret!=null) return ret;
        switch(binExpr.op()){
        case DIFFERENCE  : return cache(binExpr, binExpr.left().accept(this));
        case OVERRIDE    : return cache(binExpr, binExpr.left().accept(this).or(binExpr.right().accept(this)));
        default      : return super.visit(binExpr);
        }
      }
      public final BooleanMatrix visit(Comprehension cexpr) {
        final BooleanMatrix ret = lookup(cexpr);
        return ret!=null ? ret : cache(cexpr, super.visit((Comprehension)Formula.TRUE.comprehension(cexpr.decls())));
      }
      public BooleanMatrix visit(IfExpression ifExpr) {
        final BooleanMatrix ret = lookup(ifExpr);
        return ret!=null ? ret : cache(ifExpr, ifExpr.thenExpr().accept(this).or(ifExpr.elseExpr().accept(this)));
      }
      public BooleanMatrix visit(IntToExprCast castExpr) {
        BooleanMatrix ret = lookup(castExpr);
        if (ret!=null) return ret;
        switch(castExpr.op()) {
        case INTCAST  : return cache(castExpr, Expression.INTS.accept(this));
        case BITSETCAST  :
          final BooleanFactory factory = super.interpreter.factory();
          ret = factory.matrix(Dimensions.square(super.interpreter.universe().size(), 1));
          final IntSet ints = super.interpreter.ints();
          final int msb = factory.bitwidth()-1;
          // handle all bits but the sign bit
          for(int i = 0; i < msb; i++) {
            int pow2 = 1<<i;
            if (ints.contains(pow2)) {
              ret.set(super.interpreter.interpret(pow2), BooleanConstant.TRUE);
            }
          }
          // handle the sign bit
          if (ints.contains(-1<<msb)) {
            ret.set(super.interpreter.interpret(-1<<msb), BooleanConstant.TRUE);
          }
          return cache(castExpr, ret);
        default : throw new IllegalArgumentException("Unknown operator: " + castExpr.op());
        }

      } 
    };
    return annotated.node().accept(approximator);
  }

 
  /*---------------------------------------------------------*/
  private final LeafInterpreter interpreter;
  /* When visiting the body of a quantified formula or a comprehension, this
   * environment contains the current values of the enclosing quantified variable(s) */
  private Environment<BooleanMatrix> env;

  private final FOL2BoolCache cache;

  /**
   * Constructs a new translator that will use the given translation cache
   * and interpreter to perform the translation.
   * @effects this.node' = manager.node
   */  
  private FOL2BoolTranslator(FOL2BoolCache cache,  LeafInterpreter interpreter) {
    this.interpreter = interpreter;
    this.env = Environment.empty();
    this.cache = cache;
  }

  /**
   * Constructs a new translator that will use the given translation cache,
   * interpreter and environment to perform the translation.
   * @effects this.node' = manager.node
   */  
  private FOL2BoolTranslator(FOL2BoolCache cache,  LeafInterpreter interpreter, Environment<BooleanMatrix> env) {
    this.interpreter = interpreter;
    this.env = env;
    this.cache = cache;
  }

  /**
   * Retrieves the cached translation for the given node, if any.
   * Otherwise returns null.
   * @return the cached translation for the given node, if any.
   * Otherwise returns null.
   */
  @SuppressWarnings("unchecked")
  final <T> T lookup(Node node) {
    return (T) cache.lookup(node, env);
  }

  /**
   * The translation is cached, if necessary, and returned.
   * @return translation
   * @effects the translation may be cached
   */
  final <T> T cache(Node node, T translation) {
    return cache.cache(node, translation, env);
  }

  /**
   * The translation is cached, if necessary, and returned.
   * @return translation
   * @effects the translation may be cached
   */
  BooleanValue cache(Formula formula, BooleanValue translation) {
    return cache.cache(formula, translation, env);
  }
 
  /**
   * Calls lookup(decls) and returns the cached value, if any. 
   * If a translation has not been cached, translates decls into a list
   * of translations of its children,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(decls) |
   *   some t => t, cache(decl, decls.declarations.expression.accept(this))
   */
  public final List<BooleanMatrix> visit(Decls decls) {
    List<BooleanMatrix> ret = lookup(decls);
    if (ret!=null) return ret;
    ret = new ArrayList<BooleanMatrix>(decls.size());
    for(Decl decl : decls) {
      ret.add(visit(decl));
    }
    return cache(decls, ret);
  }

  /**
   * Calls lookup(decl) and returns the cached value, if any. 
   * If a translation has not been cached, translates decl.expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(decl) |
   *   some t => t, cache(decl, decl.expression.accept(this))
   */
  public final BooleanMatrix visit(Decl decl) {
    BooleanMatrix matrix = lookup(decl);
    if (matrix!=null) return matrix;
    if (decl.multiplicity()!=Multiplicity.ONE)
      throw new HigherOrderDeclException(decl);
    return cache(decl, decl.expression().accept(this));
  }

  /**
   * Calls this.env.lookup(variable) and returns the current binding for the
   * given variable. If no binding is found, an UnboundLeafException is thrown.
   * @return this.env.lookup(variable)
   * @throws UnboundLeafException - no this.env.lookup(variable)
   */
  public final BooleanMatrix visit(Variable variable) {
    final BooleanMatrix ret = env.lookup(variable);
    if (ret != null) return ret;
    else throw new UnboundLeafException("Unbound variable", variable);
  }

  /**
   * Returns this.interpreter.interpret(relation).
   * @return this.interpreter.interpret(relation)
   */
  public final BooleanMatrix visit(Relation relation) {
    return interpreter.interpret(relation);
  }

  /**
   * Returns this.interpreter.interpret(constExpr).
   * @return this.interpreter.interpret(constExpr).
   */
  public final BooleanMatrix visit(ConstantExpression constExpr) {
    return interpreter.interpret(constExpr);
  }

  /**
   * Calls lookup(binExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(binExpr) | some t => t,
   *      let op = (binExpr.op).(UNION->or + INTERSECTION->and + DIFFERENCE->difference + OVERRIDE->override + JOIN->dot + PRODUCT->cross) |
   *       cache(binExpr, op(binExpr.left.accept(this), binExpr.right.accept(this)))
   */
  public BooleanMatrix visit(BinaryExpression binExpr) {
    BooleanMatrix ret = lookup(binExpr);
    if (ret!=null) return ret;

    final BooleanMatrix left = binExpr.left().accept(this);
    final BooleanMatrix right = binExpr.right().accept(this);
    final ExprOperator op = binExpr.op();

    switch(op) {
    case UNION          : ret = left.or(right); break;
    case INTERSECTION  : ret = left.and(right); break;
    case DIFFERENCE   : ret = left.difference(right); break;
    case OVERRIDE     : ret = left.override(right); break;
    case JOIN       : ret = left.dot(right); break;
    case PRODUCT    : ret = left.cross(right); break;
    default :
      throw new IllegalArgumentException("Unknown operator: " + op);
    }

    return cache(binExpr, ret);
  }
 
  /**
   * Calls lookup(expr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(expr) | some t => t,
   *      let op = (expr.op).(UNION->or + INTERSECTION->and + DIFFERENCE->difference + OVERRIDE->override + JOIN->dot + PRODUCT->cross) |
   *       cache(expr, op(expr.left.accept(this), expr.right.accept(this)))
   */
  public BooleanMatrix visit(NaryExpression expr) {
    BooleanMatrix ret = lookup(expr);
    if (ret!=null) return ret;

    final ExprOperator op = expr.op();
    final BooleanMatrix first = expr.child(0).accept(this);   
    final BooleanMatrix[] rest = new BooleanMatrix[expr.size()-1];
    for(int i = 0; i < rest.length; i++) {   rest[i] = expr.child(i+1).accept(this); }
   
    switch(op) {
    case UNION          : ret = first.or(rest); break;
    case INTERSECTION  : ret = first.and(rest); break;
    case OVERRIDE     : ret = first.override(rest); break;
    case PRODUCT    : ret = first.cross(rest); break;
    default :
      throw new IllegalArgumentException("Unknown associative operator: " + op);
    }

    return cache(expr, ret);
  }
 
  /**
   * Calls lookup(unaryExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(unaryExpr) | some t => t,
   *      let op = (unaryExpr.op).(TRANSPOSE->transpose + CLOSURE->closure + REFLEXIVE_CLOSURE->(lambda(m)(m.closure().or(iden))) |
   *       cache(unaryExpr, op(unaryExpr.child))
   */
  public final BooleanMatrix visit(UnaryExpression unaryExpr) {
    BooleanMatrix ret = lookup(unaryExpr);
    if (ret!=null) return ret;

    final BooleanMatrix child = unaryExpr.expression().accept(this);
    final ExprOperator op = unaryExpr.op();

    switch(op) {
    case TRANSPOSE           : ret = child.transpose(); break;
    case CLOSURE             : ret = child.closure(); break;
    case REFLEXIVE_CLOSURE  : ret = child.closure().or(visit((ConstantExpression)Expression.IDEN)); break;
    default :
      throw new IllegalArgumentException("Unknown operator: " + op);
    }
    return cache(unaryExpr,ret);
  }

  /**
   * Translates the given comprehension as follows
   * (where A_0...A_|A| stand for boolean variables that represent the
   * tuples of the expression A, etc.):
   * let comprehension = "{ a: A, b: B, ..., x: X | F(a, b, ..., x) }" |
   *     { a: A, b: B, ..., x: X | a in A && b in B && ... && x in X && F(a, b, ..., x) }.
   * @param decls the declarations comprehension
   * @param param formula the body of the comprehension
   * @param currentDecl currently processed declaration; should be 0 initially
   * @param declConstraints the constraints implied by the declarations; should be Boolean.TRUE intially
   * @param partialIndex partial index into the provided matrix; should be 0 initially
   * @param matrix boolean matrix that will retain the final results; should be an empty matrix of dimensions universe.size^decls.length initially
   * @effects the given matrix contains the translation of the comprehension "{ decls | formula }"
   */
  private final void comprehension(Decls decls, Formula formula, int currentDecl,
      BooleanValue declConstraints, int partialIndex, BooleanMatrix matrix) {
    final BooleanFactory factory = interpreter.factory();

    if (currentDecl==decls.size()) {
      matrix.set(partialIndex, factory.and(declConstraints, formula.accept(this)));
      return;
    }

    final Decl decl = decls.get(currentDecl);
    final BooleanMatrix declTransl = visit(decl);
    final int position = (int)StrictMath.pow(interpreter.universe().size(), decls.size()-currentDecl-1);
    final BooleanMatrix groundValue = factory.matrix(declTransl.dimensions());
    env = env.extend(decl.variable(), groundValue);
    for(IndexedEntry<BooleanValue> entry : declTransl) {
      groundValue.set(entry.index(), BooleanConstant.TRUE);
      comprehension(decls, formula, currentDecl+1, factory.and(entry.value(), declConstraints),
          partialIndex + entry.index()*position, matrix);
      groundValue.set(entry.index(), BooleanConstant.FALSE)
    }
    env = env.parent();
  }

  /**
   * Calls lookup(cexpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(cexpr) | some t => t,
   *      cache(cexpr, translate(cexpr))
   */
  public BooleanMatrix visit(Comprehension cexpr) {
    BooleanMatrix ret = lookup(cexpr);
    if (ret!=null) return ret;

    ret = interpreter.factory().matrix(Dimensions.square(interpreter.universe().size(), cexpr.decls().size()));
    comprehension(cexpr.decls(), cexpr.formula(), 0, BooleanConstant.TRUE, 0, ret);

    return cache(cexpr,ret);
  }

  /**
   * Calls lookup(ifExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(ifExpr) | some t => t,
   *       cache(ifExpr, ifExpr.condition.accept(this).choice(ifExpr.then.accept(this), ifExpr.else.accept(this)))
   */
  public BooleanMatrix visit(IfExpression ifExpr) {
    BooleanMatrix ret = lookup(ifExpr);
    if (ret!=null) return ret;

    final BooleanValue condition = ifExpr.condition().accept(this);
    final BooleanMatrix thenExpr = ifExpr.thenExpr().accept(this);
    final BooleanMatrix elseExpr = ifExpr.elseExpr().accept(this);
    ret = thenExpr.choice(condition, elseExpr);

    return cache(ifExpr,ret);
  }

  /**
   * Calls lookup(project) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(project) | some t => t,
   *       cache(project, project.expression.accept(this).project(translate(project.columns))
   */
  public final BooleanMatrix visit(ProjectExpression project) {
    BooleanMatrix ret = lookup(project);
    if (ret!=null) return ret;

    final Int[] cols = new Int[project.arity()];
    for(int i = 0, arity = project.arity(); i < arity; i++) {
      cols[i] = project.column(i).accept(this);
    }

    return cache(project, project.expression().accept(this).project(cols));
  }


  /**
   * @return constant = ConstantFormula.TRUE => BooleanConstant.TRUE, BooleanConstant.FALSE
   */
  public final BooleanValue visit(ConstantFormula constant) {
    return cache(constant, BooleanConstant.constant(constant.booleanValue()));
  }

  /**
   * Translates the given universally quantified formula as follows
   * (where A_0...A_|A| stand for boolean variables that represent the
   * tuples of the expression A, etc.):
   * let quantFormula = "all a: A, b: B, ..., x: X | F(a, b, ..., x)" |
   *     (A_0 && B_0 && ... && X_0 => translate(F(A_0, B_0, ..., X_0))) && ... &&
   *     (A_|A| && B_|B| && ... && X_|X| => translate(F(A_|A|, B_|B|, ..., X_|X|))
   * @param decls formula declarations
   * @param formula the formula body
   * @param currentDecl currently processed declaration; should be 0 initially
   * @param declConstraints the constraints implied by the declarations; should be Boolean.FALSE intially
   * @param acc the accumulator that contains the top level conjunction; should be an empty AND accumulator initially
   * @effects the given accumulator contains the translation of the formula "all decls | formula"
   */
  private void all(Decls decls, Formula formula, int currentDecl, BooleanValue declConstraints, BooleanAccumulator acc) {
    if (acc.isShortCircuited()) return;
    final BooleanFactory factory = interpreter.factory();

    if (decls.size()==currentDecl) {
      acc.add(factory.or(declConstraints, formula.accept(this)));
      return;
    }

    final Decl decl = decls.get(currentDecl);
    final BooleanMatrix declTransl = visit(decl);
    final BooleanMatrix groundValue = factory.matrix(declTransl.dimensions());
    env = env.extend(decl.variable(), groundValue);
    for(IndexedEntry<BooleanValue> entry : declTransl) {
      groundValue.set(entry.index(), BooleanConstant.TRUE);
      all(decls, formula, currentDecl+1, factory.or(factory.not(entry.value()), declConstraints), acc);
      groundValue.set(entry.index(), BooleanConstant.FALSE)
    }
    env = env.parent();
   
  }

  /**
   * Translates the given existentially quantified formula as follows
   * (where A_0...A_|A| stand for boolean variables that represent the
   * tuples of the expression A, etc.):
   * let quantFormula = "some a: A, b: B, ..., x: X | F(a, b, ..., x)" |
   *     (A_0 && B_0 && ... && X_0 && translate(F(A_0, B_0, ..., X_0))) || ... ||
   *     (A_|A| && B_|B| && ... && X_|X| && translate(F(A_|A|, B_|B|, ..., X_|X|))
   * @param decls formula declarations
   * @param formula the formula body
   * @param currentDecl currently processed declaration; should be 0 initially
   * @param declConstraints the constraints implied by the declarations; should be Boolean.TRUE intially
   * @param acc the accumulator that contains the top level conjunction; should be an empty OR accumulator initially
   * @effects the given accumulator contains the translation of the formula "some decls | formula"
   */
  private void some(Decls decls, Formula formula, int currentDecl, BooleanValue declConstraints, BooleanAccumulator acc) {
    if (acc.isShortCircuited()) return;
    final BooleanFactory factory = interpreter.factory();

    if (decls.size()==currentDecl) {
      acc.add(factory.and(declConstraints, formula.accept(this)));
      return;
    }

    final Decl decl = decls.get(currentDecl);
    final BooleanMatrix declTransl = visit(decl);
    final BooleanMatrix groundValue = factory.matrix(declTransl.dimensions());
    env = env.extend(decl.variable(), groundValue);
    for(IndexedEntry<BooleanValue> entry : declTransl) {
      groundValue.set(entry.index(), BooleanConstant.TRUE);
      some(decls, formula, currentDecl+1, factory.and(entry.value(), declConstraints), acc);
      groundValue.set(entry.index(), BooleanConstant.FALSE)
    }
    env = env.parent();

  }

  /**
   * Calls lookup(quantFormula) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(quantFormula) | some t => t,
   *   cache(quantFormula, translate(quantFormula))
   */
  public final BooleanValue visit(QuantifiedFormula quantFormula) {
    BooleanValue ret = lookup(quantFormula);
    if (ret!=null) return ret;

    final Quantifier quantifier = quantFormula.quantifier();

    switch(quantifier) {
    case ALL    :
      final BooleanAccumulator and = BooleanAccumulator.treeGate(Operator.AND);
      all(quantFormula.decls(), quantFormula.formula(), 0, BooleanConstant.FALSE, and);
      ret = interpreter.factory().accumulate(and);
      break;
    case SOME  :
      final BooleanAccumulator or = BooleanAccumulator.treeGate(Operator.OR);
      some(quantFormula.decls(), quantFormula.formula(), 0, BooleanConstant.TRUE, or);
      ret = interpreter.factory().accumulate(or);
      break;
    default :
      throw new IllegalArgumentException("Unknown quantifier: " + quantifier);
    }

    return cache(quantFormula,ret);
  }
 
  /**
   * Calls lookup(formula) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(formula) | some t => t,
   *   cache(formula, formula.op(formula.left.accept(this), formula.right.accept(this))
   */
  public final BooleanValue visit(NaryFormula formula) {
    final BooleanValue ret = lookup(formula);
    if (ret!=null) return ret;

    final FormulaOperator op = formula.op();   
    final Operator.Nary boolOp;
   
    switch(op) {
    case AND : boolOp = Operator.AND; break;
    case OR  : boolOp = Operator.OR;  break;
    default   : throw new IllegalArgumentException("Unknown nary operator: " + op);
    }
   
    final BooleanAccumulator acc = BooleanAccumulator.treeGate(boolOp);
    final BooleanValue shortCircuit = boolOp.shortCircuit();
    for(Formula child : formula) {
      if (acc.add(child.accept(this))==shortCircuit)
        break;
    }
   
    return cache(formula, interpreter.factory().accumulate(acc));
  }

 

  /**
   * Calls lookup(binFormula) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(binFormula) | some t => t,
   *   cache(binFormula, binFormula.op(binFormula.left.accept(this), binFormula.right.accept(this))
   */
  public final BooleanValue visit(BinaryFormula binFormula) {
    BooleanValue ret = lookup(binFormula);
    if (ret!=null) return ret;

    final BooleanValue left = binFormula.left().accept(this);
    final BooleanValue right = binFormula.right().accept(this);
    final FormulaOperator op = binFormula.op();
    final BooleanFactory f = interpreter.factory();

    switch(op) {
    case AND    : ret = f.and(left, right); break;
    case OR      : ret = f.or(left, right); break;
    case IMPLIES  : ret = f.implies(left, right); break;
    case IFF    : ret = f.iff(left, right); break;
    default :
      throw new IllegalArgumentException("Unknown operator: " + op);
    }
    return cache(binFormula, ret);
  }

  /**
   * Calls lookup(not) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(not) | some t => t,
   *   cache(not, !not.formula.accept(this))
   */
  public final BooleanValue visit(NotFormula not) {
    BooleanValue ret = lookup(not);
    return ret==null ?
        cache(not, interpreter.factory().not(not.formula().accept(this))) : ret;
  }

  /**
   * Calls lookup(compFormula) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(compFormula) | some t => t,
   *      let op = (binExpr.op).(SUBSET->subset + EQUALS->eq) |
   *       cache(compFormula, op(compFormula.left.accept(this), compFormula.right.accept(this)))
   */
  public final BooleanValue visit(ComparisonFormula compFormula) {
    BooleanValue ret = lookup(compFormula);
    if (ret!=null) return ret;

    final BooleanMatrix left = compFormula.left().accept(this);
    final BooleanMatrix right = compFormula.right().accept(this);
    final ExprCompOperator op = compFormula.op();

    switch(op) {
    case SUBSET  : ret = left.subset(right); break;
    case EQUALS  : ret = left.eq(right); break;
    default :
      throw new IllegalArgumentException("Unknown operator: " + compFormula.op());
    }
    return cache(compFormula,ret);
  }

  /**
   * Calls lookup(multFormula) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(multFormula) | some t => t,
   *      let op = (multFormula.mult).(NO->none + SOME->some + ONE->one + LONE->lone) |
   *       cache(multFormula, op(multFormula.expression.accept(this)))
   */
  public final BooleanValue visit(MultiplicityFormula multFormula) {
    BooleanValue ret = lookup(multFormula);
    if (ret!=null) return ret;

    final BooleanMatrix child = multFormula.expression().accept(this);
    final Multiplicity mult = multFormula.multiplicity();

    switch(mult) {
    case NO   : ret = child.none(); break;
    case SOME  : ret = child.some(); break;
    case ONE   : ret = child.one()break;
    case LONE   : ret = child.lone(); break;
    default :
      throw new IllegalArgumentException("Unknown multiplicity: " + mult);
    }

    return cache(multFormula, ret);
  }

  /**
   * Calls lookup(pred) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(pred) | some t => t,
   *   cache(pred, pred.toConstraints().accept(this))
   */
  public final BooleanValue visit(RelationPredicate pred) {
    BooleanValue ret = lookup(pred);
    return ret != null ? ret : cache(pred, pred.toConstraints().accept(this));
  }

  /**
   * Calls lookup(castExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(castExpr) | some t => t,
   *   cache(castExpr, translate(castExpr))
   */
  public BooleanMatrix visit(IntToExprCast castExpr) {
    BooleanMatrix ret = lookup(castExpr);
    if (ret!=null) return ret;

    final Int child = castExpr.intExpr().accept(this);
    final BooleanFactory factory =  interpreter.factory();
    final IntSet ints = interpreter.ints();
   
    ret = factory.matrix(Dimensions.square(interpreter.universe().size(), 1));
   
    switch(castExpr.op()) {
    case INTCAST :  
      for(IntIterator iter = ints.iterator(); iter.hasNext(); ) {
        int i = iter.next();
        int atomIndex = interpreter.interpret(i);
        ret.set(atomIndex, factory.or(ret.get(atomIndex), child.eq(factory.integer(i))));
      }
      break;
    case BITSETCAST :
      final List<BooleanValue> twosComplement = child.twosComplementBits();
      final int msb = twosComplement.size()-1;
      // handle all bits but the sign bit
      for(int i = 0; i < msb; i++) {
        int pow2 = 1<<i;
        if (ints.contains(pow2)) {
          ret.set(interpreter.interpret(pow2), twosComplement.get(i));
        }
      }
      // handle the sign bit
      if (ints.contains(-1<<msb)) {
        ret.set(interpreter.interpret(-1<<msb), twosComplement.get(msb));
      }
      break;
    default :
      throw new IllegalArgumentException("Unknown cast operator: " + castExpr.op());
    }
   
    return cache(castExpr, ret);
 

  /**
   * @return this.interpreter.factory.integer(intConst.value, this.encoding)
   */
  public final Int visit(IntConstant intConst) {
    return interpreter.factory().integer(intConst.value());
  }

  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *       cache(intExpr, intExpr.condition.accept(this).choice(intExpr.then.accept(this), intExpr.else.accept(this)))
   */
  public final Int visit(IfIntExpression intExpr) {
    Int ret = lookup(intExpr);
    if (ret!=null) return ret;

    final BooleanValue condition = intExpr.condition().accept(this);
    final Int thenExpr = intExpr.thenExpr().accept(this);
    final Int elseExpr = intExpr.elseExpr().accept(this);
    ret = thenExpr.choice(condition, elseExpr);

    return cache(intExpr, ret);
  }

  /**
   * Returns an Int that represents the sum of all the integers that
   * correspond to non-FALSE entries in the given matrix.
   * @param iter an iterator over all the bound integers.  Initial should be this.manager.ints().iterator().
   * @param lo the first element of the current partial sum. Initial should be 0.
   * @param hi the last element of the current partial sum.  Initial should be size-1, where size is the total
   * number of elements returned by the iterator.
   * @return  an Int that represents the sum of all the integers that
   * correspond to non-FALSE entries in the given matrix.
   */
  private final Int sum(BooleanMatrix m, IntIterator iter, int low, int high) {
    if (low > high)
      return interpreter.factory().integer(0);
    else if (low==high) {
      int i = iter.next();
      return interpreter.factory().integer(i, m.get(interpreter.interpret(i)));
    } else {
      final int mid = (low + high) / 2;
      final Int lsum = sum(m, iter, low, mid);
      final Int hsum = sum(m, iter, mid+1, high);
      return lsum.plus(hsum);
    }
  }

  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *   cache(intExpr, translate(intExpr))
   */
  public final Int visit(ExprToIntCast intExpr) {
    Int ret = lookup(intExpr);
    if (ret!=null) return ret;
    switch(intExpr.op()) {
    case CARDINALITY :
      ret = intExpr.expression().accept(this).cardinality(); break;
    case SUM         :
      final IntSet ints = interpreter.ints();
      ret = sum(intExpr.expression().accept(this), ints.iterator(), 0, ints.size()-1); break;
    default:
      throw new IllegalArgumentException("unknown operator: " + intExpr.op());
    }
    return cache(intExpr, ret);
  }
 
  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *   cache(intExpr, intExpr.left.accept(this) intExpr.op intExpr.right.accept(this))
   */
  public final Int visit(BinaryIntExpression intExpr) {
    Int ret = lookup(intExpr);
    if (ret!=null) return ret;
    final Int left = intExpr.left().accept(this);
    final Int right = intExpr.right().accept(this);
    switch(intExpr.op()) {
    case PLUS      : ret = left.plus(right); break;
    case MINUS     : ret = left.minus(right); break;
    case MULTIPLY   : ret = left.multiply(right); break;
    case DIVIDE   : ret = left.divide(right); break;
    case MODULO    : ret = left.modulo(right); break;
    case AND    : ret = left.and(right); break;
    case OR      : ret = left.or(right); break;
    case XOR    : ret = left.xor(right); break;
    case SHL    : ret = left.shl(right); break;
    case SHR    : ret = left.shr(right); break;
    case SHA    : ret = left.sha(right); break;
    default    :
      throw new IllegalArgumentException("Unknown operator: " + intExpr.op());
    }
    return cache(intExpr, ret);
  }
 
  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *   cache(intExpr, intExpr.left.accept(this) intExpr.op intExpr.right.accept(this))
   */
  public final Int visit(NaryIntExpression intExpr) {
    Int ret = lookup(intExpr);
    if (ret!=null) return ret;
    final Int first = intExpr.child(0).accept(this);
    final Int[] rest = new Int[intExpr.size()-1];
    for(int i = 0; i < rest.length; i++) {   rest[i] = intExpr.child(i+1).accept(this); }
    switch(intExpr.op()) {
    case PLUS      : ret = first.plus(rest); break;
    case MULTIPLY   : ret = first.multiply(rest); break;
    case AND    : ret = first.and(rest); break;
    case OR      : ret = first.or(rest); break;
    default    :
      throw new IllegalArgumentException("Unknown nary operator: " + intExpr.op());
    }
    return cache(intExpr, ret);
  }
 
  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *   cache(intExpr, intExpr.op(intExpr.expression.accept(this)))
   */
  public final Int visit(UnaryIntExpression intExpr) {
    Int ret = lookup(intExpr);
    if (ret!=null) return ret;
    final Int child = intExpr.intExpr().accept(this);
    switch(intExpr.op()) {
    case NEG   : ret = child.negate(); break;
    case NOT   : ret = child.not(); break;
    case ABS   : ret = child.abs(); break;
    case SGN   : ret = child.sgn(); break;
    default :
      throw new IllegalArgumentException("Unknown operator: " + intExpr.op());
    }
    return cache(intExpr, ret);
  }

  /**
   * Translates the given sum expression as follows
   * (where A_0...A_|A| stand for boolean variables that represent the
   * tuples of the expression A, etc.):
   * let sum = "sum a: A, b: B, ..., x: X | IE(a, b, ..., x) " |
   *     sum a: A, b: B, ..., x: X | if (a in A && b in B && ... && x in X) then IE(a, b, ..., x) else 0 }.
   * @param decls intexpr declarations
   * @param formula the formula body
   * @param currentDecl currently processed declaration; should be 0 initially
   * @param declConstraints the constraints implied by the declarations; should be Boolean.TRUE intially
   * @param values integer values computed so far
   */
  private final void sum(Decls decls, IntExpression expr, int currentDecl, BooleanValue declConstraints,
      List<Int> values) {
    final BooleanFactory factory = interpreter.factory();
    if (decls.size()==currentDecl) {
      values.add( expr.accept(this).choice(declConstraints, factory.integer(0)) );
      return;
    }

    final Decl decl = decls.get(currentDecl);
    final BooleanMatrix declTransl = visit(decl);
    final BooleanMatrix groundValue = factory.matrix(declTransl.dimensions());
    env = env.extend(decl.variable(), groundValue);
    for(IndexedEntry<BooleanValue> entry : declTransl) {
      groundValue.set(entry.index(), BooleanConstant.TRUE);
      sum(decls, expr, currentDecl+1, factory.and(entry.value(), declConstraints), values);
      groundValue.set(entry.index(), BooleanConstant.FALSE)
    }
    env = env.parent();
  }

  /**
   * Calls lookup(intExpr) and returns the cached value, if any. 
   * If a translation has not been cached, translates the expression,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intExpr) | some t => t,
   *   cache(intExpr, translate(intExpr))
   */
  public final Int visit(SumExpression intExpr) {
    final Int ret = lookup(intExpr);
    if (ret!=null) return ret;
    final List<Int> values = new ArrayList<Int>();
    sum(intExpr.decls(), intExpr.intExpr(), 0, BooleanConstant.TRUE, values);
    for(int sums = values.size(); sums > 1; sums -= sums/2) {
      final int max = sums-1;
      for(int i = 0; i < max; i += 2) {
        values.set(i/2, values.get(i).plus(values.get(i+1)));
      }
      if (max%2==0) { // even max => odd number of entries
        values.set(max/2, values.get(max));
      }
    }
    return cache(intExpr, values.isEmpty() ? interpreter.factory().integer(0) : values.get(0));
  }

  /**
   * Calls lookup(intComp) and returns the cached value, if any. 
   * If a translation has not been cached, translates the formula,
   * calls cache(...) on it and returns it.
   * @return let t = lookup(intComp) | some t => t,
   *   cache(intComp, intComp.left.accept(this) intComp.op intComp.right.accept(this))
   */
  public final BooleanValue visit(IntComparisonFormula intComp) {
    BooleanValue ret = lookup(intComp);
    if (ret!=null) return ret;
    final Int left = intComp.left().accept(this);
    final Int right = intComp.right().accept(this);
    switch(intComp.op()) {
    case EQ  : ret = left.eq(right); break;
    case LT  : ret = left.lt(right); break;
    case LTE : ret = left.lte(right); break;
    case GT  : ret = left.gt(right); break;
    case GTE : ret = left.gte(right); break;
    default:
      throw new IllegalArgumentException("Unknown operator: " + intComp.op());
    }
    return cache(intComp, ret);
  }
}
TOP

Related Classes of kodkod.engine.fol2sat.FOL2BoolTranslator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.