/**
* Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*/
package at.iaik.suraq.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import at.iaik.suraq.exceptions.IncomparableTermsException;
import at.iaik.suraq.exceptions.ParseError;
import at.iaik.suraq.exceptions.SuraqException;
import at.iaik.suraq.main.SuraqOptions;
import at.iaik.suraq.parser.LogicParser;
import at.iaik.suraq.parser.SExpParser;
import at.iaik.suraq.proof.AnnotatedProofNode;
import at.iaik.suraq.proof.VeritProofNode;
import at.iaik.suraq.resProof.Clause;
import at.iaik.suraq.resProof.Literal;
import at.iaik.suraq.resProof.ResNode;
import at.iaik.suraq.resProof.ResProof;
import at.iaik.suraq.sexp.SExpression;
import at.iaik.suraq.sexp.SExpressionConstants;
import at.iaik.suraq.sexp.Token;
import at.iaik.suraq.smtlib.SMTLibObject;
import at.iaik.suraq.smtlib.TransformedZ3Proof;
import at.iaik.suraq.smtlib.Z3Proof;
import at.iaik.suraq.smtlib.formula.AndFormula;
import at.iaik.suraq.smtlib.formula.ArrayVariable;
import at.iaik.suraq.smtlib.formula.DomainEq;
import at.iaik.suraq.smtlib.formula.DomainTerm;
import at.iaik.suraq.smtlib.formula.DomainVariable;
import at.iaik.suraq.smtlib.formula.EqualityFormula;
import at.iaik.suraq.smtlib.formula.Formula;
import at.iaik.suraq.smtlib.formula.FormulaTerm;
import at.iaik.suraq.smtlib.formula.FunctionMacro;
import at.iaik.suraq.smtlib.formula.NotFormula;
import at.iaik.suraq.smtlib.formula.OrFormula;
import at.iaik.suraq.smtlib.formula.PropositionalConstant;
import at.iaik.suraq.smtlib.formula.PropositionalEq;
import at.iaik.suraq.smtlib.formula.PropositionalTerm;
import at.iaik.suraq.smtlib.formula.PropositionalVariable;
import at.iaik.suraq.smtlib.formula.Term;
import at.iaik.suraq.smtlib.formula.UninterpretedFunction;
import at.iaik.suraq.smtlib.formula.UninterpretedFunctionInstance;
import at.iaik.suraq.smtlib.formula.UninterpretedPredicateInstance;
import at.iaik.suraq.smtsolver.SMTSolver;
/**
*
* Collection of (static) utility methods.
*
* @author Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*
*/
public final class Util {
private static Map<String, Integer> literalsID = new HashMap<String, Integer>();
private static Map<Integer, ResNode> resNodes = new HashMap<Integer, ResNode>();
private static ResProof resProof;
private static Map<Integer, Formula> literalMap = new HashMap<Integer, Formula>();
public static final DecimalFormat byteAmountFormatter = new DecimalFormat(
"000,000,000,000");
public static final DecimalFormat largeNumberFormatter = new DecimalFormat(
"###,###,###,###");
public static final DecimalFormat veryLargeNumberFormatter = new DecimalFormat(
"###,###,###,###,###,###");
/**
* Counts the number of Tseitin vars that have been introduced so far. This
* makes sure they all get a unique name.
*/
private static int tseitinVarCounter = 0;
public static final Pattern digitsPattern = Pattern
.compile("([0-9]+)(\\z|[^0-9])");
/**
*
* @param partition
* the assert partition with which the variable will be
* associated.
* @return a Tseitin variable with a name that has not been returned before.
*/
public static PropositionalVariable freshTseitinVar(int partition) {
return PropositionalVariable.create("ts!" + Util.tseitinVarCounter++,
partition);
}
/**
* This resets the counter that is used to make Tseitin-variables unique.
* Only call if you know what you are doing (like executing multiple test
* cases with different <code>Suraq</code> objects).
*/
public static void resetTseitinCounter() {
Util.tseitinVarCounter = 0;
}
private static Formula lastFormula = null;
private static final Set<String> lostNames = new HashSet<String>();
public static final String freshVarNameCached(Formula formula, String prefix) {
return Util.freshVarNameCached(formula, prefix, null);
}
public static final String freshVarNameCached(Formula formula,
String prefix, Set<String> instances) {
if (Util.lastFormula != formula) {
// System.out.println("*** New Formula detected. Building lostNames.");
Util.lastFormula = formula;
Util.lostNames.clear();
// hashcode must be varname.hashcode() !!!
Set<SMTLibObject> done = new HashSet<SMTLibObject>();
Set<ArrayVariable> aVars = new HashSet<ArrayVariable>();
formula.getArrayVariables(aVars, done);
done.clear();
Set<PropositionalVariable> pVars = new HashSet<PropositionalVariable>();
formula.getPropositionalVariables(pVars, done);
done.clear();
Set<DomainVariable> dVars = new HashSet<DomainVariable>();
formula.getDomainVariables(dVars, done);
done.clear();
Set<UninterpretedFunction> ufs = new HashSet<UninterpretedFunction>();
formula.getUninterpretedFunctions(ufs, done);
done.clear();
Set<FunctionMacro> macros = new HashSet<FunctionMacro>();
formula.getFunctionMacros(macros, done);
done.clear();
for (ArrayVariable aVar : aVars)
Util.lostNames.add(aVar.getVarName());
for (DomainVariable dVar : dVars)
Util.lostNames.add(dVar.getVarName());
for (PropositionalVariable pVar : pVars)
Util.lostNames.add(pVar.getVarName());
for (UninterpretedFunction uf : ufs)
Util.lostNames.add(uf.getName().toString());
for (FunctionMacro macro : macros)
Util.lostNames.add(macro.getName().toString());
}
int count = -1;
while (++count >= 0) {
String name = prefix + (count > 0 ? ("_fresh" + count) : "");
if (Util.lostNames.contains(name))
continue;
if (instances != null && instances.contains(name))
continue;
Util.lostNames.add(name);
return name;
}
throw new RuntimeException("Could not create fresh variable name.");
}
/**
* Checks whether the given formula contains any of the given tokens as
* identifiers.
*
* @param formula
* the formula to check
* @param names
* the <code>Token</code>s to check the formula against.
* @return <code>true</code> if at least one of the <code>Token</code>s from
* <code>names</code>, occurred in <code>formula</code>,
* <code>false</code> otherwise.
*/
public static final boolean formulaContainsAny(Formula formula,
Set<Token> names) {
Set<SMTLibObject> done = new HashSet<SMTLibObject>();
Set<ArrayVariable> aVars = new HashSet<ArrayVariable>();
formula.getArrayVariables(aVars, done);
done.clear();
Set<PropositionalVariable> pVars = new HashSet<PropositionalVariable>();
formula.getPropositionalVariables(pVars, done);
done.clear();
Set<DomainVariable> dVars = new HashSet<DomainVariable>();
formula.getDomainVariables(dVars, done);
done.clear();
Set<String> ufs = new HashSet<String>();
formula.getUninterpretedFunctionNames(ufs, done);
done.clear();
Set<String> macros = new HashSet<String>();
formula.getFunctionMacroNames(macros, done);
done.clear();
for (Token token : names) {
if (aVars.contains(ArrayVariable.create(token)))
return true;
if (dVars.contains(DomainVariable.create(token)))
return true;
if (pVars.contains(PropositionalVariable.create(token)))
return true;
if (ufs.contains(token.toString()))
return true;
if (macros.contains(token.toString()))
return true;
}
return false;
}
/**
* Checks whether the given term contains any of the given tokens as
* identifiers.
*
* @param term
* the term to check
* @param names
* the <code>Token</code>s to check the formula against.
* @return <code>true</code> if at least one of the <code>Token</code>s from
* <code>names</code>, occurred in <code>term</code>,
* <code>false</code> otherwise.
*/
public static final boolean termContainsAny(Term term, Set<Token> names) {
Set<SMTLibObject> done = new HashSet<SMTLibObject>();
Set<ArrayVariable> aVars = new HashSet<ArrayVariable>();
term.getArrayVariables(aVars, done);
done.clear();
Set<PropositionalVariable> pVars = new HashSet<PropositionalVariable>();
term.getPropositionalVariables(pVars, done);
done.clear();
Set<DomainVariable> dVars = new HashSet<DomainVariable>();
term.getDomainVariables(dVars, done);
done.clear();
Set<String> ufs = new HashSet<String>();
term.getUninterpretedFunctionNames(ufs, done);
done.clear();
Set<String> macros = new HashSet<String>();
term.getFunctionMacroNames(macros, done);
done.clear();
for (Token token : names) {
if (aVars.contains(ArrayVariable.create(token)))
return true;
if (dVars.contains(DomainVariable.create(token)))
return true;
if (pVars.contains(PropositionalVariable.create(token)))
return true;
if (ufs.contains(token.toString()))
return true;
if (macros.contains(token.toString()))
return true;
}
return false;
}
/**
* Increments the given list of (modular) counters.
*
* @param counters
* the list of counters
* @param modulus
* the modulus
* @return <code>true</code> if the counters did not (overall) overflow,
* <code>false</code> otherwise.
*/
public static boolean incrementCounters(List<Integer> counters, int modulus) {
int count = 0;
do {
assert (counters.get(count) < modulus);
if (counters.get(count) == modulus - 1) {
counters.set(count, 0);
count++;
} else {
counters.set(count, counters.get(count) + 1);
return true;
}
} while (count < counters.size());
return false;
}
/**
* Creates a new variable of the given type.
*
* @param name
* the name of the variable
* @param type
* the type of the variable
* @return a new variable with name <code>name</code> and type
* <code>type</code>.
* @throws SuraqException
* if the given <code>type</code> does not exist
*/
public static Term variableFactory(Token name, Token type)
throws SuraqException {
if (type.equals(SExpressionConstants.BOOL_TYPE))
return PropositionalVariable.create(name);
if (type.equals(SExpressionConstants.VALUE_TYPE))
return DomainVariable.create(name);
if (type.equals(SExpressionConstants.ARRAY_TYPE))
return ArrayVariable.create(name);
throw new SuraqException("Cannot create variable of type "
+ type.toString());
}
/**
* Given a clause with a single literal, returns this literal. Fails
* otherwise.
*
* @param clause
* @return the single literal
*/
public static Formula getSingleLiteral(Formula clause) {
if (clause == null)
return null;
if (!(clause instanceof OrFormula)) {
assert (Util.isLiteral(clause));
return clause;
}
assert (clause instanceof OrFormula);
List<Formula> disjuncts = ((OrFormula) clause).getDisjuncts();
assert (disjuncts.size() == 1);
assert (Util.isLiteral(disjuncts.get(0)));
return disjuncts.get(0);
}
/**
* Given a single literal of the form f(a,...)=f(b,...) returns the
* uninterpreted Function that is used. Fails with an assertion error if the
* given literal is not of this form.
*
* @param literal
* a literal in the style of the consequent of a monotonicity
* proof.
* @return the uninterpreted function that is used in the given equality.
*/
public static UninterpretedFunction getUninterpretedFunctionOrPredicate(
Formula literal) {
assert (literal instanceof EqualityFormula);
EqualityFormula equality = (EqualityFormula) literal;
assert (equality.getTerms().size() == 2);
assert ((equality.getTerms().get(0) instanceof UninterpretedFunctionInstance) || (equality
.getTerms().get(0) instanceof PropositionalTerm));
assert ((equality.getTerms().get(1) instanceof UninterpretedFunctionInstance) || (equality
.getTerms().get(1) instanceof PropositionalTerm));
if (equality.getTerms().get(0) instanceof UninterpretedFunctionInstance) {
assert (equality.getTerms().get(1) instanceof UninterpretedFunctionInstance);
UninterpretedFunctionInstance instance1 = (UninterpretedFunctionInstance) (equality
.getTerms().get(0));
UninterpretedFunctionInstance instance2 = (UninterpretedFunctionInstance) (equality
.getTerms().get(1));
UninterpretedFunction function = instance1.getFunction();
assert (function.equals(instance2.getFunction()));
return function;
} else if (equality.getTerms().get(0) instanceof PropositionalTerm) {
assert (equality.getTerms().get(1) instanceof PropositionalTerm);
UninterpretedPredicateInstance instance1 = null;
if (equality.getTerms().get(0) instanceof FormulaTerm) {
FormulaTerm term1 = (FormulaTerm) equality.getTerms().get(0);
instance1 = (UninterpretedPredicateInstance) term1.getFormula();
} else {
assert (equality.getTerms().get(0) instanceof UninterpretedPredicateInstance);
instance1 = (UninterpretedPredicateInstance) equality
.getTerms().get(0);
}
assert (instance1 != null);
UninterpretedPredicateInstance instance2 = null;
if (equality.getTerms().get(1) instanceof FormulaTerm) {
FormulaTerm term2 = (FormulaTerm) equality.getTerms().get(1);
instance2 = (UninterpretedPredicateInstance) term2.getFormula();
} else {
assert (equality.getTerms().get(1) instanceof UninterpretedPredicateInstance);
instance2 = (UninterpretedPredicateInstance) equality
.getTerms().get(1);
}
assert (instance2 != null);
UninterpretedFunction function = instance1.getFunction();
assert (function.equals(instance2.getFunction()));
return function;
}
assert (false);
return null;
}
/**
* @param currentAnnotatedNode
* @return the domain terms occurring in the given node, from left to right.
*/
public static DomainTerm[] getDomainTerms(AnnotatedProofNode node) {
if (node.numPremises() == 0) {
DomainTerm[] result = new DomainTerm[2];
Object[] tmp = ((EqualityFormula) Util.getSingleLiteral((node
.getConsequent().getConsequent()))).getTerms().toArray();
assert (tmp.length == 2);
for (int count = 0; count < 2; count++) {
assert (tmp[count] != null);
assert (tmp[count] instanceof DomainTerm);
result[count] = (DomainTerm) tmp[count];
}
return result;
} else {
assert (node.numPremises() == 3);
Object[] part1 = ((EqualityFormula) Util.getSingleLiteral((node
.getPremise1().getConsequent()))).getTerms().toArray();
Object[] part2 = ((EqualityFormula) Util.getSingleLiteral((node
.getPremise3().getConsequent()))).getTerms().toArray();
DomainTerm[] result = new DomainTerm[4];
result[0] = (DomainTerm) part1[0];
result[1] = (DomainTerm) part1[1];
result[2] = (DomainTerm) part2[0];
result[3] = (DomainTerm) part2[1];
for (int count = 0; count < 4; count++)
assert (result[count] != null);
return result;
}
}
/**
* @param formula1
* @param formula2
* @return <code>true</code> iff the two formulas are of the form a!=b and
* b!=a.
*/
public static boolean checkForFlippedDisequality(Formula formula1,
Formula formula2) {
return Util.checkForFlippedEquality(formula1, formula2, false);
}
/**
* @param formula1
* @param formula2
* @return <code>true</code> iff the two formulas are of the form a!=b and
* b!=a or a==b and b==a.
*/
public static boolean checkForFlippedEqualityOrDisequality(
Formula formula1, Formula formula2) {
if (Util.checkForFlippedEquality(formula1, formula2, true))
return true;
if (Util.checkForFlippedEquality(formula1, formula2, false))
return true;
return false;
}
/**
* @param formula1
* @param formula2
* @param equal
* if <code>true</code>, check for positive equalities, else for
* disequalities.
* @return <code>true</code> iff the two formulas are of the form a R b and
* b R a, where R is either <code>==</code> (if <code>equal</code>
* is true, or <code>!=</code> otherwise.
*/
public static boolean checkForFlippedEquality(Formula formula1,
Formula formula2, boolean equal) {
if (!Util.isLiteral(formula1))
return false;
if (!Util.isLiteral(formula2))
return false;
Formula literal1 = Util.getSingleLiteral(formula1);
Formula literal2 = Util.getSingleLiteral(formula2);
if (!(Util.makeLiteralPositive(literal1) instanceof EqualityFormula))
return false;
if (!(Util.makeLiteralPositive(literal2) instanceof EqualityFormula))
return false;
if (!Util.isNegativeLiteral(literal1) ^ equal)
return false;
if (!Util.isNegativeLiteral(literal2) ^ equal)
return false;
literal1 = Util.makeLiteralPositive(literal1);
literal2 = Util.makeLiteralPositive(literal2);
assert (((EqualityFormula) literal1).isEqual());
assert (((EqualityFormula) literal2).isEqual());
assert (((EqualityFormula) literal1).getTerms().size() == 2);
assert (((EqualityFormula) literal2).getTerms().size() == 2);
Term term1 = ((EqualityFormula) literal1).getTerms().get(0);
Term term2 = ((EqualityFormula) literal1).getTerms().get(1);
if (!term1.equals(((EqualityFormula) literal2).getTerms().get(1)))
return false;
if (!term2.equals(((EqualityFormula) literal2).getTerms().get(0)))
return false;
return true;
}
/**
* @return <code>true</code> if the given formula is only a single literal
* (encapsulated in an OR).
*/
public static boolean isUnitClause(Formula formula) {
if (!(formula instanceof OrFormula))
return false;
OrFormula orFormula = (OrFormula) formula;
return orFormula.getDisjuncts().size() == 1;
}
/**
* Removes negation from literal.
*
* @param literal
* literal to make positive
* @return the resulting atom
*
*/
public static Formula makeLiteralPositive(Formula literal) {
if (!Util.isLiteral(literal))
throw new RuntimeException("given formula should be a literal");
if (literal instanceof NotFormula) {
literal = ((NotFormula) literal).getNegatedFormula();
}
if (!Util.isAtom(literal))
throw new RuntimeException("given literal should be an atom");
return literal;
}
/**
* Invert given literal.
*
* @param literal
* literal to invert
* @return the inverted literal
*
*/
public static Formula invertLiteral(Formula literal) {
if (!Util.isLiteral(literal))
throw new RuntimeException("given formula should be a literal");
literal = Util.getSingleLiteral(literal);
if (literal instanceof NotFormula) {
return ((NotFormula) literal).getNegatedFormula();
} else
return NotFormula.create(literal);
}
/**
* Checks if a given Formula is a literal. A literal is either an atom or a
* negation of an atom. Also handles unit clauses.
*
* @param formula
* formula to check
* @return true, iff formula is an literal or a unit clause
*/
public static boolean isLiteral(Formula formula) {
if (formula instanceof FormulaTerm)
formula = ((FormulaTerm) formula).getFormula();
if (formula instanceof OrFormula) {
if (((OrFormula) formula).getDisjuncts().size() != 1)
return false;
formula = ((OrFormula) formula).getDisjuncts().get(0);
}
if (formula instanceof NotFormula) {
formula = ((NotFormula) formula).getNegatedFormula();
}
return Util.isAtom(formula);
}
/**
* Checks if a given Formula is an atom. An atom is either a
* <code>EqualityFormula</code>, a <code>PropositionalVariable</code>, a
* <code>PropositionalConstant</code> or a
* <code>UninterpretedPredicateInstance</code>.
*
* @param formula
* formula to check
* @return true, iff formula is atom
*
*/
public static boolean isAtom(Formula formula) {
if (formula instanceof FormulaTerm)
formula = ((FormulaTerm) formula).getFormula();
if (formula instanceof EqualityFormula)
return true;
if (formula instanceof PropositionalVariable)
return true;
if (formula instanceof PropositionalConstant)
return true;
if (formula instanceof UninterpretedPredicateInstance)
return true;
return false;
}
public static boolean isNegativeLiteral(Formula formula) {
return Util.isLiteral(formula) && !Util.isAtom(formula);
}
/**
* @param clause1
* @param clause2
* @return the resolving literal in positive form, or <code>null</code> if
* no such literal exists.
*/
public static Formula findResolvingLiteral(OrFormula clause1,
OrFormula clause2) {
for (Formula formula : clause1.getDisjuncts()) {
assert (Util.isLiteral(formula));
Formula inverseLiteral = Util.invertLiteral(formula);
if (clause2.getDisjuncts().contains(inverseLiteral))
return Util.makeLiteralPositive(inverseLiteral);
else if (Util.makeLiteralPositive(inverseLiteral) instanceof EqualityFormula) {
EqualityFormula reverseLiteral = Util
.reverseEquality((EqualityFormula) (Util
.makeLiteralPositive(inverseLiteral)));
Formula reverseInverseLiteral = null;
if (Util.isNegativeLiteral(inverseLiteral))
reverseInverseLiteral = NotFormula.create(reverseLiteral);
else
reverseInverseLiteral = reverseLiteral;
if (clause2.getDisjuncts().contains(reverseInverseLiteral))
return Util.makeLiteralPositive(reverseInverseLiteral);
}
}
return null;
}
/**
* Finds the resolving literal between the given proof nodes.
*
* @param subProofs
* must be of size 2.
* @return the resolving literal (in positive form), or <code>null</code> if
* no such literal exists.
*/
public static Formula findResolvingLiteral(
Collection<VeritProofNode> subProofs) {
assert (subProofs != null);
assert (subProofs.size() == 2);
Iterator<VeritProofNode> iterator = subProofs.iterator();
assert (iterator.hasNext());
VeritProofNode node1 = iterator.next();
assert (iterator.hasNext());
VeritProofNode node2 = iterator.next();
assert (node1 != null);
assert (node2 != null);
assert (!iterator.hasNext());
return Util.findResolvingLiteral(node1.getConclusionsAsOrFormula(),
node2.getConclusionsAsOrFormula());
}
/**
* Checks if these clauses resolve on one of the given obsolete literals. If
* so, the obsolete clause is returned. If no clause is obsolete,
* <code>null</code> is returned.
*
* @param clause1
* @param clause2
* @param obsoleteLiterals
* @return the clause that is obsolete, or <code>null</code> if none.
*/
public static OrFormula findObsoleteClause(OrFormula clause1,
OrFormula clause2, Set<Formula> obsoleteLiterals) {
Formula resolvingLiteral = Util.findResolvingLiteral(clause1, clause2);
Formula obsoleteLiteral = null;
if (obsoleteLiterals.contains(resolvingLiteral)) {
obsoleteLiteral = resolvingLiteral;
} else if (obsoleteLiterals.contains(Util
.invertLiteral(resolvingLiteral))) {
obsoleteLiteral = Util.invertLiteral(resolvingLiteral);
} else
return null;
assert (obsoleteLiteral != null);
if (clause1.getDisjuncts().contains(obsoleteLiteral)) {
assert (!clause2.getDisjuncts().contains(obsoleteLiteral));
assert (!clause2.getDisjuncts().contains(
Util.invertLiteral(obsoleteLiteral)));
return clause1;
} else if (clause2.getDisjuncts().contains(obsoleteLiteral)) {
assert (!clause1.getDisjuncts().contains(obsoleteLiteral));
assert (!clause1.getDisjuncts().contains(
Util.invertLiteral(obsoleteLiteral)));
return clause2;
}
assert (false); // one of the clauses *must* have the literal
return null;
}
public static EqualityFormula reverseEquality(EqualityFormula formula) {
assert (formula.getTerms().size() == 2);
Term term1 = formula.getTerms().get(0);
Term term2 = formula.getTerms().get(1);
List<Term> terms = new ArrayList<Term>(2);
terms.add(term2);
terms.add(term1);
try {
return EqualityFormula.create(terms, formula.isEqual());
} catch (IncomparableTermsException exc) {
throw new RuntimeException(
"Incomparable terms during equality reversal.", exc);
}
}
/**
* Makes an ID string for the given positive literal, so that (a=b) (a!=b)
* (b!=a) (b=a) all have the same ID.
*
* @param formula
* a (positive!) literal
* @return the ID string for <code>formula</code>.
*/
public static String makeIdString(Formula formula) {
assert (!(formula instanceof NotFormula));
assert (Util.isLiteral(formula));
assert (Util.isAtom(formula));
// (a=b) (a!=b) (b!=a) (b=a) should have same IDs
if (formula instanceof EqualityFormula) {
List<String> terms = new ArrayList<String>();
for (Term t : ((EqualityFormula) formula).getTerms())
terms.add(t.toString());
Collections.sort(terms);
return terms.toString().replaceAll("\n", "")
.replaceAll("\\s{2,}", " ");
} else
return formula.toString().replaceAll("\n", "")
.replaceAll("\\s{2,}", " ");
}
/**
* Determines the sign of a given literal.
*
* @param literal
* the literal to check
* @return the sign value of <code>literal</code>.
*/
public static boolean getSignValue(Formula literal) {
assert (Util.isLiteral(literal));
boolean sign = true;
if (literal instanceof NotFormula) {
sign = false;
literal = ((NotFormula) literal).getNegatedFormula();
}
if (literal instanceof EqualityFormula) {
if (((EqualityFormula) literal).isEqual())
return sign;
else
return !sign;
}
return sign;
}
public static final ResProof createResolutionProof(TransformedZ3Proof proof) {
Util.resProof = new ResProof();
Util.literalsID.clear();
Util.resNodes.clear();
ResNode rootNode = Util.createResolutionProofRecursive(proof);
Util.resProof.setRoot(rootNode);
return Util.resProof;
}
public static final Map<Integer, Formula> getLiteralMap() {
return new HashMap<Integer, Formula>(Util.literalMap);
}
private static final ResNode createResolutionProofRecursive(
TransformedZ3Proof proof) {
Token proofType = proof.getProofType();
if (proofType.equals(SExpressionConstants.ASSERTED)) {
Formula clause = proof.getConsequent();
assert (clause instanceof OrFormula);
List<Literal> resClauseLits = new ArrayList<Literal>();
// TODO: check if correct
Set<Integer> resClausePartitions = new HashSet<Integer>();
for (Formula literal : ((OrFormula) clause).getDisjuncts()) {
// assign literal IDs
Formula posLiteral = Util.makeLiteralPositive(literal);
assert (Util.isLiteral(posLiteral));
assert (Util.isAtom(posLiteral));
if (posLiteral.equals(PropositionalConstant.create(false))) {
resClausePartitions.add(-1);
continue;
}
Integer resLiteralID = Util.literalsID.get(Util
.makeIdString(posLiteral));
Set<Integer> partitions = literal.getPartitionsFromSymbols();
if (partitions.size() == 2)
partitions.remove(-1);
assert (partitions.size() == 1);
int partition = partitions.iterator().next();
if (resLiteralID == null) {
resLiteralID = Util.literalsID.size() + 1;
assert (!Util.literalsID.containsValue(new Integer(
resLiteralID)));
Util.literalsID.put(Util.makeIdString(posLiteral),
resLiteralID);
Util.literalMap.put(resLiteralID, posLiteral);
Util.resProof.putVarPart(resLiteralID, partition < 0 ? 0
: partition);
}
resClauseLits.add(Literal.create(resLiteralID,
Util.getSignValue(literal)));
resClausePartitions.add(partition);
}
// build leaf ResNodes
ResNode resLeafNode = Util.resNodes.get(proof.getID() + 1);
if (resLeafNode == null) {
if (resClausePartitions.size() == 2)
resClausePartitions.remove(-1);
assert (resClausePartitions.size() == 1);
int leafPartition = resClausePartitions.iterator().next();
if (proof.isAxiom())
leafPartition = 0; // axioms should go to 0
else if (leafPartition < 0)
if (proof.getAssertPartitionOfThisNode() > 0)
leafPartition = proof.getAssertPartitionOfThisNode();
else
leafPartition = 1; // arbitrary choice
Clause tmpClause = new Clause(resClauseLits);
resLeafNode = Util.resProof.addLeaf(tmpClause, leafPartition);
Util.resNodes.put(proof.getID() + 1, resLeafNode);
}
return resLeafNode;
} else if (proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) {
assert (proof.getSubProofs().size() == 2);
ResNode resIntNode = Util.resNodes.get(proof.getID() + 1);
if (resIntNode == null) {
TransformedZ3Proof child1 = (TransformedZ3Proof) proof
.getSubProofs().get(0);
TransformedZ3Proof child2 = (TransformedZ3Proof) proof
.getSubProofs().get(1);
ResNode resNode1 = Util.createResolutionProofRecursive(child1);
ResNode resNode2 = Util.createResolutionProofRecursive(child2);
// build literal of resolution
Formula posLiteral = Util.makeLiteralPositive(proof
.getLiteral());
Integer literalID = Util.literalsID.get(Util
.makeIdString(posLiteral));
assert (literalID != null);
resIntNode = Util.resProof.addIntNode(null, resNode1, resNode2,
literalID);
Util.resNodes.put(proof.getID() + 1, resIntNode);
}
return resIntNode;
} else
throw new RuntimeException(
"Resolution proof should only consist of asserted and unit-resolution elements");
}
/**
*
*
* @param formula
* @return <code>true</code> iff the given <code>formula</code> is a
* reflexivity (e.g.: a=a).
*/
public static boolean isReflexivity(Formula formula) {
// Formula tmp = formula.transformToConsequentsForm();
if (!Util.isLiteral(formula))
return false;
if (!(formula instanceof DomainEq))
return false;
// tmp = Util.getSingleLiteral(tmp);
// if (!(formula instanceof EqualityFormula))
// return false;
EqualityFormula equality = (EqualityFormula) formula;
if (equality.getTerms().size() != 2)
return false;
if (!equality.isEqual())
return false;
Term term1 = equality.getTerms().get(0);
Term term2 = equality.getTerms().get(1);
assert (term1 != null);
assert (term2 != null);
return (term1.equals(term2));
}
/**
*
* @param literal
* @return <code>true</code> iff the given literal is a negated reflexivity.
*/
public static boolean isNegatedReflexivity(Formula literal) {
if (!Util.isNegativeLiteral(literal))
return false;
Formula positivLiteral = Util.makeLiteralPositive(literal);
return Util.isReflexivity(positivLiteral);
}
/**
* @param equalities
* @return <code>true</code> iff the given chain forms a transitivity chain
*/
public static boolean checkEqualityChain(EqualityFormula[] equalities) {
for (EqualityFormula eq : equalities)
assert (eq.getTerms().size() == 2);
Term match = equalities[0].getTerms().get(1);
for (int count = 1; count < equalities.length; count++) {
Term current = equalities[count].getTerms().get(0);
if (!current.equals(match))
return false;
match = equalities[count].getTerms().get(1);
}
return true;
}
/**
*
* @param formula
* @return <code>true</code> if this is a bad literal (having more than one
* partition). <code>false</code> if it is a good literal, or not a
* literal at all.
*/
public static boolean isBadLiteral(Formula formula) {
if (!Util.isLiteral(formula))
return false;
Set<Integer> partitions = formula.getPartitionsFromSymbols();
partitions.remove(-1);
if (partitions.size() > 1)
return true;
return false;
}
/**
*
* @param clause
* @return <code>true</code> if the given formula contains a bad literal
*/
public static boolean containsBadLiteral(OrFormula clause) {
for (Formula disjunct : clause.getDisjuncts())
if (Util.isBadLiteral(disjunct))
return true;
return false;
}
/**
*
* @param z3proof
* @param hypothesis
* @return if any of the subproofs of <code>z3Proof</code> is a hypothesis
* that has the some consequent as <code>hypothesis</code>, this
* subproof is returned. <code>null</code> otherwise.
*/
public static Z3Proof findHypothesisInSubproofs(Z3Proof z3proof,
Z3Proof hypothesis) {
for (Z3Proof subproof : z3proof.getSubProofs()) {
if (subproof.isHypothesis()) {
if (subproof
.getConsequent()
.transformToConsequentsForm()
.equals(hypothesis.getConsequent()
.transformToConsequentsForm()))
return subproof;
}
}
return null;
}
/**
* Checks the given <code>node</code> for bad literals. Allows bad literals
* only in unit clauses, and if the node deduces false. Also, allows only 2
* subproofs.
*
* @param node
* @return <code>true</code> if the <code>node</code> passed the check
*/
public static boolean checkResolutionNodeForBadLiterals(Z3Proof node) {
if (node.getSubProofs().size() != 2)
return false;
Z3Proof sub1 = node.getSubProofs().get(0);
Z3Proof sub2 = node.getSubProofs().get(1);
if (!Util.containsBadLiteral((OrFormula) sub1.getConsequent())
&& !Util.containsBadLiteral((OrFormula) sub2.getConsequent()))
return true;
if (node.getConsequent().equals(PropositionalConstant.create(false)))
return true;
return false;
}
public static void getModusPonensNonIffChilds(Z3Proof node,
Set<Z3Proof> result) {
assert (result != null);
assert (node != null);
if (node.getConsequent() instanceof PropositionalEq) {
for (Z3Proof child : node.getSubProofs())
Util.getModusPonensNonIffChilds(child, result);
} else {
result.add(node);
}
}
public static void getModusPonensIffLeafs(Z3Proof node, Set<Z3Proof> result) {
assert (result != null);
assert (node != null);
if (node.getConsequent() instanceof PropositionalEq) {
for (Z3Proof child : node.getSubProofs())
Util.getModusPonensIffLeafs(child, result);
if (node.getProofType().equals(SExpressionConstants.ASSERTED))
result.add(node);
}
}
public static void getModusPonensIffChildsComingFromDomainEq(Z3Proof node,
Set<Z3Proof> result) {
assert (result != null);
assert (node != null);
if (node.getConsequent() instanceof PropositionalEq) {
if (node.getProofType().equals(SExpressionConstants.ASSERTED))
return;
if (node.getSubProofs().size() == 1) {
if (node.getSubProofs().get(0).getConsequent() instanceof PropositionalEq) {
Util.getModusPonensIffChildsComingFromDomainEq(node
.getSubProofs().get(0), result);
return;
}
assert (node.getProofType()
.equals(SExpressionConstants.MONOTONICITY));
Z3Proof child = node.getSubProofs().get(0);
Formula childConsequent = child.getConsequent();
assert (Util.isLiteral(childConsequent));
childConsequent = Util.getSingleLiteral(childConsequent);
assert (Util.isAtom(childConsequent));
if (!(childConsequent instanceof DomainEq))
assert (false);
result.add(node);
return;
}
if (node.getSubProofs().size() == 2
&& node.getProofType().equals(
SExpressionConstants.MONOTONICITY)) {
assert (node.getProofType()
.equals(SExpressionConstants.MONOTONICITY));
Z3Proof child1 = node.getSubProofs().get(0);
Z3Proof child2 = node.getSubProofs().get(1);
Formula childConsequent1 = child1.getConsequent();
assert (Util.isLiteral(childConsequent1));
childConsequent1 = Util.getSingleLiteral(childConsequent1);
assert (Util.isAtom(childConsequent1));
if (!(childConsequent1 instanceof DomainEq))
assert (false);
Formula childConsequent2 = child2.getConsequent();
assert (Util.isLiteral(childConsequent2));
childConsequent2 = Util.getSingleLiteral(childConsequent2);
assert (Util.isAtom(childConsequent2));
if (!(childConsequent2 instanceof DomainEq))
assert (false);
result.add(node);
return;
}
for (Z3Proof child : node.getSubProofs())
Util.getModusPonensIffChildsComingFromDomainEq(child, result);
} else {
assert (false); // Unexpected exit path from modus ponens rule
}
}
/**
* Returns the partition of the consequent of this proof. Fails with
* assertion error, if there are local symbols from multiple partitions.
*
* @param transformedZ3Proof
* @return The partition of the symbols of the consequent of the given
* proof, or <code>1</code> if there are only global symbols
*/
public static int getSinglePartitionOfProof(
TransformedZ3Proof transformedZ3Proof) {
Set<Integer> partitions = transformedZ3Proof.getConsequent()
.getPartitionsFromSymbols();
partitions.remove(-1);
if (partitions.size() == 0)
return 1; // arbitrary choice
assert (partitions.size() == 1);
return partitions.iterator().next();
}
/**
* @param badLiteral
* @return
*/
public static String formulaToStringWithoutNewlines(Formula formula) {
return formula.toString().replaceAll("\\s{2,}", " ").replace("\n", "");
}
/**
* Compares to clauses for (logical) equality. I.e., the order or literals
* is immaterial.
*
* @param clause1
* @param clause2
* @return <code>true</code> if both given formulas are clauses and they are
* logically equivalent.
*/
public static boolean equalClauses(Formula clause1, Formula clause2) {
if (!(clause1 instanceof OrFormula))
return false;
if (!(clause2 instanceof OrFormula))
return false;
if (clause1.equals(clause2))
return true;
return (new HashSet<Formula>(((OrFormula) clause1).getDisjuncts()))
.equals((new HashSet<Formula>(((OrFormula) clause2)
.getDisjuncts())));
}
/**
* Constructs a reflexivity formula over the given <code>term</code>.
*
* @param term
* @return a reflexivity formula over the given <code>term</code>.
*/
public static DomainEq createReflexivity(DomainTerm term) {
List<DomainTerm> terms = new ArrayList<DomainTerm>();
terms.add(term);
terms.add(term);
DomainEq result = DomainEq.create(terms, true);
return result;
}
/**
* Tests whether each collection in the given collection of collections has
* exactly one element.
*
* @param collectionOfCollections
* the collection of collections to test
* @return <code>true</code> iff all collections in the
* <code>collectionOfCollections</code> have exactly one element.
*/
public static boolean allElementsSizeOne(
Collection<? extends Collection<?>> collectionOfCollections) {
for (Collection<?> collection : collectionOfCollections) {
if (collection.size() != 1)
return false;
}
return true;
}
/**
* Removes (negative) reflexive literals. They are false anyway. This
* methode modifies the given list.
*
* @param literals
* the list of literals (will be modified)
*/
public static void removeReflexiveLiterals(List<Formula> literals) {
Set<Formula> literalsToRemove = new HashSet<Formula>();
for (Formula literal : literals) {
assert (Util.isLiteral(literal));
if (Util.isNegativeLiteral(literal)) {
Formula positiveLiteral = Util.makeLiteralPositive(literal);
if (Util.isReflexivity(positiveLiteral))
literalsToRemove.add(literal);
}
}
literals.removeAll(literalsToRemove);
}
/**
* Prints the given line to <code>System.out</code> using
* <code>println</code>. The current date/time is prepended.
*
* @param line
* the line to print
*/
public static synchronized void printToSystemOutWithWallClockTimePrefix(
String line) {
DateFormat dateFormat = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM);
String dateTimeString = dateFormat.format(new Date());
System.out.println("[" + dateTimeString + "] " + line);
}
public static synchronized void printMemoryInformation() {
System.out
.println("--------------------------------------------------------------------------------");
System.out.println("MEMORY INFORMATION:");
System.out.println("Total Memory: "
+ Util.byteAmountFormatter.format(Runtime.getRuntime()
.totalMemory()));
System.out.println("Free Memory : "
+ Util.byteAmountFormatter.format(Runtime.getRuntime()
.freeMemory()));
System.out.println("Max. Memory : "
+ Util.byteAmountFormatter.format(Runtime.getRuntime()
.maxMemory()));
System.out
.println("--------------------------------------------------------------------------------");
}
/**
*
* @param term
* @return <code>true</code> iff the given term is a global term.
*/
public static boolean isGlobal(Term term) {
Set<Integer> partitions = term.getPartitionsFromSymbols();
if (partitions.size() > 1)
return false;
partitions.remove(-1);
return partitions.isEmpty();
}
/**
* @param formula
* @return <code>true</code> iff the given formula contains only global
* symbols.
*/
public static boolean isGlobal(Formula formula) {
Set<Integer> partitions = formula.getPartitionsFromSymbols();
partitions.remove(-1);
return partitions.isEmpty();
}
/**
* @param usedLiterals
* @return
*/
public static List<Formula> invertAllLiterals(Collection<Formula> literals) {
List<Formula> result = new ArrayList<Formula>(literals.size());
for (Formula literal : literals) {
result.add(Util.invertLiteral(literal));
}
return result;
}
/**
* Reverses the order of the terms in the given <code>literal</code>.
* <code>literal</code> must be an <code>EqualityFormula</code> with exactly
* two terms. A negated literal is also allowed. If the literal is not an
* equality literal, it is returned unchanged.
*
* @param literal
* @return the literal with terms in reversed order.
*/
public static Formula reverseTermsInLiteral(Formula literal) {
assert (Util.isLiteral(literal));
if (!(Util.makeLiteralPositive(literal) instanceof EqualityFormula))
return literal;
EqualityFormula eq = (EqualityFormula) Util
.makeLiteralPositive(literal);
assert (eq.getTerms().size() == 2);
List<Term> terms = new ArrayList<Term>(2);
terms.add(eq.getTerms().get(1));
terms.add(eq.getTerms().get(0));
EqualityFormula newEq;
try {
newEq = EqualityFormula.create(terms, true);
} catch (IncomparableTermsException exc) {
throw new RuntimeException(
"Unexpected exception while trying to reverse terms in literal.");
}
if (Util.getSignValue(literal))
return newEq;
else
return NotFormula.create(newEq);
}
/**
* Counts how many negative literals are contained in the given collection.
* Positive literals and non-literals are not counted.
*
* @param literals
* @return the number of negative literals in the given Collection.
*/
public static int countNegativeLiterals(
Collection<? extends Formula> literals) {
int count = 0;
for (Formula literal : literals) {
if (Util.isNegativeLiteral(literal))
count++;
}
return count;
}
/**
* Counts how many positive literals are contained in the given collection.
* Negative literals and non-literals are not counted.
*
* @param literals
* @return the number of positive literals in the given Collection.
*/
public static int countPositiveLiterals(
Collection<? extends Formula> literals) {
int count = 0;
for (Formula literal : literals) {
if (Util.isAtom(literal))
count++;
}
return count;
}
/**
* Returns the first positive literal found in the given collection, or
* <code>null</code> if no such literal exists.
*
* @param literals
* @return the first positive literal found in the given collection, or
* <code>null</code> if no such literal exists.
*/
public static Formula findPositiveLiteral(
Collection<? extends Formula> literals) {
for (Formula literal : literals) {
if (Util.isAtom(literal))
return literal;
}
return null;
}
/**
* Returns the implied literal of this collection. Unlike
* <code>findPositiveLiteral</code>, this checks whether there is only one
* positive (implied) literal. This also checks that all other formulas
* actually are literals.
*
* @param literals
* @return the (unique) implied literal of this collection or
* <code>null</code> if no such (unique) literal exists
*/
public static Formula getImpliedLiteral(
Collection<? extends Formula> literals) {
Formula result = null;
for (Formula literal : literals) {
if (!Util.isLiteral(literal))
return null;
if (Util.isAtom(literal)) {
if (result != null)
return null;
else
result = literal;
}
}
return result;
}
/**
* Searches the given collection of <code>literals</code> for a predicate
* instance of the same predicate as <code>predicateInstance</code> but in
* inverse (i.e., negative) polarity. The first matching predicate instance
* found is returned. There is no check done whether there would be more.
* Note that the returned formula will not be the negative literal, but just
* the predicate inside the <code>NotFormula</code>.
*
* @param predicateInstance
* @param literals
* @return the inverse predicate instance, or <code>null</code> if not found
*/
public static UninterpretedPredicateInstance findInversePredicateLiteral(
UninterpretedPredicateInstance predicateInstance,
Collection<? extends Formula> literals) {
assert (predicateInstance != null);
assert (literals != null);
UninterpretedFunction predicate = predicateInstance.getFunction();
for (Formula literal : literals) {
if (Util.isNegativeLiteral(literal)) {
Formula positiveLiteral = Util.makeLiteralPositive(literal);
if (positiveLiteral instanceof UninterpretedPredicateInstance) {
if (((UninterpretedPredicateInstance) positiveLiteral)
.getFunction().equals(predicate)) {
return (UninterpretedPredicateInstance) positiveLiteral;
}
}
}
}
return null;
}
/**
* @param literal1
* @param literal2
* @return
*/
public static boolean areReversedLiterals(Formula literal1, Formula literal2) {
assert (literal1 != null);
assert (literal2 != null);
assert (Util.isLiteral(literal1));
assert (Util.isLiteral(literal2));
Formula positiveLiteral1 = Util.makeLiteralPositive(literal1);
Formula positiveLiteral2 = Util.makeLiteralPositive(literal2);
if (!(positiveLiteral1 instanceof EqualityFormula))
return false;
if (!(positiveLiteral2 instanceof EqualityFormula))
return false;
EqualityFormula equality1 = (EqualityFormula) positiveLiteral1;
EqualityFormula equality2 = (EqualityFormula) positiveLiteral2;
assert (equality1.getTerms().size() == 2);
assert (equality2.getTerms().size() == 2);
if (!equality1.getTerms().get(0).equals(equality2.getTerms().get(1)))
return false;
if (!equality1.getTerms().get(1).equals(equality2.getTerms().get(0)))
return false;
return true;
}
/**
*
* @param conclusions1
* Literals of first subproof
* @param conclusions2
* Literals of second subproof
* @return conclusions for the resolution of <code>literals1</code> and
* <code>literals2</code>.
*/
public static List<Formula> findConclusionsOfResolution(
Collection<? extends Formula> conclusions1,
Collection<? extends Formula> conclusions2) {
List<Formula> result = new ArrayList<Formula>(conclusions1.size()
+ conclusions2.size());
for (Formula literal : conclusions1) {
if (!result.contains(literal)) {
if (!conclusions2.contains(Util.invertLiteral(literal))) {
result.add(literal);
}
}
}
for (Formula literal : conclusions2) {
if (!result.contains(literal)) {
if (!conclusions1.contains(Util.invertLiteral(literal))) {
result.add(literal);
}
}
}
return result;
}
/**
*
* @param literal
* @param literalIds
* @param partitions
* mapping of literal IDs to partitions (0 for global) will be
* stored here.
* @param literalMap
* map from literal IDs (Integers) to <code>Formula</code>
* objects. (will be updated here)
* @return the id of the given literal in the given map, or a fresh id
* (which is stored to the map)
*/
public static int getLiteralId(Formula literal,
Map<String, Integer> literalIds, Map<Integer, Integer> partitions,
Map<Integer, Formula> literalMap) {
assert (Util.isLiteral(literal));
Formula atom = Util.makeLiteralPositive(literal);
String idString = Util.makeIdString(atom);
Integer id = literalIds.get(idString);
if (id == null) {
id = literalIds.size() + 1;
assert (!literalIds.containsKey(id));
literalIds.put(idString, id);
Set<Integer> literalPartitions = literal.getPartitionsFromSymbols();
literalPartitions.remove(-1);
assert (literalPartitions.size() <= 1);
int literalPartition = literalPartitions.isEmpty() ? 0
: literalPartitions.iterator().next();
partitions.put(id, literalPartition);
}
literalMap.put(id, atom);
return id;
}
/**
* Dumps the given metadata to files
*
* @param filePrefix
* @param literalIds
* @param literalMap
* @param partitions
* @param leafPartitions
* @throws IOException
*/
public static void dumpMetaData(String filePrefix,
Map<String, Integer> literalIds, Map<Integer, Formula> literalMap,
Map<Integer, Integer> partitions,
Map<ImmutableSet<Integer>, Integer> leafPartitions)
throws IOException {
Util.printToSystemOutWithWallClockTimePrefix("Starting to dump metadata to files "
+ filePrefix + ".* ...");
Util.dumpLiteralMap(filePrefix + ".literalMap", literalMap);
Util.dumpPartitions(filePrefix + ".partitions", partitions);
Util.dumpLeafPartitions(filePrefix + ".leafPartitions", leafPartitions);
Util.printToSystemOutWithWallClockTimePrefix("Done.");
}
/**
* Dumps literalMap to given file.
*
* @param fileName
* @param literalMap
* @throws IOException
*/
public static void dumpLiteralMap(String fileName,
Map<Integer, Formula> literalIds) throws IOException {
Writer fileStream = new FileWriter(fileName);
BufferedWriter writer = new BufferedWriter(fileStream);
for (Integer id : literalIds.keySet()) {
Formula formula = literalIds.get(id);
assert (formula != null);
writer.write("(");
writer.write(id.toString());
writer.write(" ");
writer.write(formula.toSmtlibV2().toString());
writer.write(")\n");
}
writer.close();
}
/**
*
* @param fileName
* @param parser
* a parser pre-initialized with the correct set of variable and
* function names.
* @return the literal map loaded from the given file
* @throws ParseError
* @throws IOException
* @throws FileNotFoundException
*/
public static Map<Integer, Formula> loadLiteralMap(String fileName,
LogicParser parser) throws ParseError, FileNotFoundException,
IOException {
Map<Integer, Formula> result = new HashMap<Integer, Formula>();
SExpParser sexpParser = new SExpParser(new File(fileName));
sexpParser.parse();
SExpression rootExpr = sexpParser.getRootExpr();
sexpParser = null; // GC
for (SExpression expr : rootExpr.getChildren()) {
if (expr.getChildren().size() != 2)
throw new ParseError("Illegal number of child expressions");
if (!(expr.getChildren().get(0) instanceof Token))
throw new ParseError("First child not a token");
int literal = Integer
.parseInt(expr.getChildren().get(0).toString());
Formula formula = parser
.parseFormulaBody(expr.getChildren().get(1));
result.put(literal, formula);
}
return result;
}
/**
* Dumps the given leafPartitions to the given file.
*
* @param fileName
* @param leafPartitions
* @throws IOException
*/
public static void dumpLeafPartitions(String fileName,
Map<ImmutableSet<Integer>, Integer> leafPartitions)
throws IOException {
Writer fileStream = new FileWriter(fileName);
BufferedWriter writer = new BufferedWriter(fileStream);
for (ImmutableSet<Integer> clause : leafPartitions.keySet()) {
Integer partition = leafPartitions.get(clause);
assert (partition != null);
writer.write(partition.toString());
writer.write(" : ");
for (Integer integer : clause) {
writer.write(integer.toString());
writer.write(" ");
}
writer.write("\n");
}
writer.close();
}
/**
*
* @param fileName
* @return leaf partitions loaded from the given file.
* @throws IOException
*/
public static Map<ImmutableSet<Integer>, Integer> loadLeafPartitions(
String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
Map<ImmutableSet<Integer>, Integer> result = new HashMap<ImmutableSet<Integer>, Integer>();
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split(" ");
assert (tokens.length >= 3);
assert (tokens[1].equals(":"));
int partition = Integer.parseInt(tokens[0]);
Set<Integer> clause = new TreeSet<Integer>();
for (int count = 2; count < tokens.length; count++) {
clause.add(Integer.parseInt(tokens[count]));
}
result.put(ImmutableSet.create(clause), partition);
}
reader.close();
return result;
}
/**
* Dumps the given partitions to the given file.
*
* @param fileName
* @param partitions
* @throws IOException
*/
public static void dumpPartitions(String fileName,
Map<Integer, Integer> partitions) throws IOException {
Writer fileStream = new FileWriter(fileName);
BufferedWriter writer = new BufferedWriter(fileStream);
for (Integer lit : partitions.keySet()) {
Integer partition = partitions.get(lit);
assert (partition != null);
writer.write(lit.toString());
writer.write(" ");
writer.write(partition.toString());
writer.write("\n");
}
writer.close();
}
/**
*
* @param fileName
* @return partitions loaded from the given file.
* @throws IOException
*/
public static Map<Integer, Integer> loadPartitions(String fileName)
throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split(" ");
assert (tokens.length == 2);
int lit = Integer.parseInt(tokens[0]);
int partition = Integer.parseInt(tokens[1]);
result.put(lit, partition);
}
reader.close();
return result;
}
/**
* Writes all declarations required for <code>formula</code> to
* <code>writer.</code>
*
* @param formula
* @param writer
* @throws IOException
*/
public static void writeDeclarations(Formula formula, BufferedWriter writer)
throws IOException {
Set<SMTLibObject> done = new HashSet<SMTLibObject>();
Set<PropositionalVariable> propVars = new HashSet<PropositionalVariable>();
formula.getPropositionalVariables(propVars, done);
done.clear();
for (PropositionalVariable propVar : propVars) {
writer.write("(" + SExpressionConstants.DECLARE_FUN.toString()
+ " " + propVar.getVarName() + " () "
+ SExpressionConstants.BOOL_TYPE.toString() + " )\n");
}
Set<DomainVariable> domainVars = new HashSet<DomainVariable>();
formula.getDomainVariables(domainVars, done);
done.clear();
for (DomainVariable domainVar : domainVars) {
writer.write("(" + SExpressionConstants.DECLARE_FUN.toString()
+ " " + domainVar.getVarName() + " () "
+ SExpressionConstants.VALUE_TYPE.toString() + " )\n");
}
Set<ArrayVariable> arrayVars = new HashSet<ArrayVariable>();
formula.getArrayVariables(arrayVars, done);
done.clear();
for (ArrayVariable arrayVar : arrayVars) {
writer.write("(" + SExpressionConstants.DECLARE_FUN.toString()
+ " " + arrayVar.getVarName() + " () "
+ SExpressionConstants.ARRAY_TYPE.toString() + " )\n");
}
Set<UninterpretedFunction> uninterpretedFunctions = new HashSet<UninterpretedFunction>();
formula.getUninterpretedFunctions(uninterpretedFunctions, done);
done.clear();
for (UninterpretedFunction function : uninterpretedFunctions) {
StringBuilder params = new StringBuilder();
final int numParams = function.getNumParams();
for (int count = 0; count < numParams; count++) {
params.append(SExpressionConstants.VALUE_TYPE.toString());
if (count != numParams - 1)
params.append(" ");
}
writer.write("(" + SExpressionConstants.DECLARE_FUN.toString()
+ " " + function.getName().toString() + " ("
+ params.toString() + ") " + function.getType() + " )\n");
}
}
/**
* @param interpolant
* @param partitionFormulas
* @return <code>true</code> if the given interpolant is a partial
* interpolant with respect to the partition formulas.
*/
public static boolean checkInterpolant(Formula interpolant,
Map<Integer, Formula> partitionFormulas) {
return Util.checkPartialInterpolant(interpolant, null,
partitionFormulas);
}
/**
* @param interpolant
* @param clause
* @param partitionFormulas
* @return <code>true</code> if the given interpolant is a partial
* interpolant with respect to the partition formulas and the given
* clause.
*/
public static boolean checkPartialInterpolant(Formula interpolant,
Formula clause, Map<Integer, Formula> partitionFormulas) {
List<Formula> conjunctsA = new ArrayList<Formula>(
partitionFormulas.size() / 2 + 1);
List<Formula> conjunctsB = new ArrayList<Formula>(
partitionFormulas.size() / 2 + 1);
for (int num : partitionFormulas.keySet()) {
Formula partition = partitionFormulas.get(num);
assert (partition != null);
if ((num - 1) % 2 == 0)
conjunctsA.add(partition);
else
conjunctsB.add(partition);
}
AndFormula partitionsA = AndFormula.generate(conjunctsA);
AndFormula partitionsB = AndFormula.generate(conjunctsB);
Formula impliedByA = interpolant;
Formula impliedByB = NotFormula.create(interpolant);
if (clause != null) {
List<Formula> disjuncts = new ArrayList<Formula>(2);
disjuncts.add(impliedByA);
disjuncts.add(clause);
impliedByA = OrFormula.generate(disjuncts);
List<Formula> disjuncts2 = new ArrayList<Formula>(2);
disjuncts2.add(impliedByB);
disjuncts2.add(clause);
impliedByB = OrFormula.generate(disjuncts2);
}
if (!Util.checkFormulaImplication(partitionsA, impliedByA))
return false;
if (!Util.checkFormulaImplication(partitionsB, impliedByB))
return false;
Set<SMTLibObject> symbolsA = new HashSet<SMTLibObject>();
Set<DomainVariable> domainVarsA = new HashSet<DomainVariable>();
partitionsA
.getDomainVariables(domainVarsA, new HashSet<SMTLibObject>());
symbolsA.addAll(domainVarsA);
Set<PropositionalVariable> propVarsA = new HashSet<PropositionalVariable>();
partitionsA.getPropositionalVariables(propVarsA,
new HashSet<SMTLibObject>());
symbolsA.addAll(propVarsA);
Set<ArrayVariable> arrayVarsA = new HashSet<ArrayVariable>();
partitionsA.getArrayVariables(arrayVarsA, new HashSet<SMTLibObject>());
symbolsA.addAll(arrayVarsA);
Set<UninterpretedFunction> ufsA = new HashSet<UninterpretedFunction>();
partitionsA
.getUninterpretedFunctions(ufsA, new HashSet<SMTLibObject>());
symbolsA.addAll(ufsA);
Set<SMTLibObject> symbolsB = new HashSet<SMTLibObject>();
Set<DomainVariable> domainVarsB = new HashSet<DomainVariable>();
partitionsB
.getDomainVariables(domainVarsB, new HashSet<SMTLibObject>());
symbolsB.addAll(domainVarsB);
Set<PropositionalVariable> propVarsB = new HashSet<PropositionalVariable>();
partitionsB.getPropositionalVariables(propVarsB,
new HashSet<SMTLibObject>());
symbolsB.addAll(propVarsB);
Set<ArrayVariable> arrayVarsB = new HashSet<ArrayVariable>();
partitionsB.getArrayVariables(arrayVarsB, new HashSet<SMTLibObject>());
symbolsB.addAll(arrayVarsB);
Set<UninterpretedFunction> ufsB = new HashSet<UninterpretedFunction>();
partitionsB
.getUninterpretedFunctions(ufsB, new HashSet<SMTLibObject>());
symbolsB.addAll(ufsB);
Set<SMTLibObject> symbolsI = new HashSet<SMTLibObject>();
Set<DomainVariable> domainVarsI = new HashSet<DomainVariable>();
interpolant
.getDomainVariables(domainVarsI, new HashSet<SMTLibObject>());
symbolsI.addAll(domainVarsI);
Set<PropositionalVariable> propVarsI = new HashSet<PropositionalVariable>();
interpolant.getPropositionalVariables(propVarsI,
new HashSet<SMTLibObject>());
symbolsI.addAll(propVarsI);
Set<ArrayVariable> arrayVarsI = new HashSet<ArrayVariable>();
interpolant.getArrayVariables(arrayVarsI, new HashSet<SMTLibObject>());
symbolsI.addAll(arrayVarsI);
Set<UninterpretedFunction> ufsI = new HashSet<UninterpretedFunction>();
interpolant
.getUninterpretedFunctions(ufsI, new HashSet<SMTLibObject>());
symbolsI.addAll(ufsI);
Set<SMTLibObject> symbolsC = new HashSet<SMTLibObject>();
if (clause != null) {
Set<DomainVariable> domainVarsC = new HashSet<DomainVariable>();
clause.getDomainVariables(domainVarsC, new HashSet<SMTLibObject>());
symbolsC.addAll(domainVarsC);
Set<PropositionalVariable> propVarsC = new HashSet<PropositionalVariable>();
clause.getPropositionalVariables(propVarsC,
new HashSet<SMTLibObject>());
symbolsC.addAll(propVarsC);
Set<ArrayVariable> arrayVarsC = new HashSet<ArrayVariable>();
clause.getArrayVariables(arrayVarsC, new HashSet<SMTLibObject>());
symbolsC.addAll(arrayVarsC);
Set<UninterpretedFunction> ufsC = new HashSet<UninterpretedFunction>();
clause.getUninterpretedFunctions(ufsC, new HashSet<SMTLibObject>());
symbolsC.addAll(ufsC);
}
Set<Object> globalSymbols = new HashSet<Object>();
for (Object symbol : symbolsA) {
if (symbolsB.contains(symbol))
globalSymbols.add(symbol);
}
for (Object symbol : symbolsI) {
if (!globalSymbols.contains(symbol) && !symbolsC.contains(symbol))
return false;
}
return true;
}
/**
*
* @param interpolant
* @param theoryLemma
* @return <code>true</code> iff the given <code>interpolant</code> is a
* valid interpolant for <code>theoryLemma</code>.
*/
public static boolean checkTheoryLemmaInterpolant(Formula interpolant,
OrFormula theoryLemma) {
List<Formula> literals = theoryLemma.getDisjuncts();
List<Formula> literalsA = new ArrayList<Formula>(literals.size());
List<Formula> literalsB = new ArrayList<Formula>(literals.size());
for (Formula literal : literals) {
assert (Util.isLiteral(literal));
Set<Integer> partitions = literal.getPartitionsFromSymbols();
partitions.remove(-1);
literal = Util.invertLiteral(literal);
if (partitions.isEmpty()) {
literalsA.add(literal);
literalsB.add(literal);
} else {
assert (partitions.size() == 1);
int partition = partitions.iterator().next();
if ((partition - 1) % 2 == 0)
literalsA.add(literal);
else
literalsB.add(literal);
}
}
AndFormula cubeA = AndFormula.generate(literalsA);
AndFormula cubeB = AndFormula.generate(literalsB);
if (!Util.checkFormulaImplication(cubeA, interpolant))
return false;
if (!Util
.checkFormulaImplication(cubeB, NotFormula.create(interpolant)))
return false;
if (!Util.isGlobal(interpolant))
return false;
return true;
}
/**
* Checks if the given formulas imply each other.
*
* @param formula1
* the first formula
*
* @param formula2
* the second formula
*
* @return returns true, if the two formulas imply each other.
*/
public static boolean checkEquivalenceOfFormulas(Formula formula1,
Formula formula2) {
Set<SMTLibObject> done = new HashSet<SMTLibObject>();
Set<DomainVariable> domainVars1 = new HashSet<DomainVariable>();
formula1.getDomainVariables(domainVars1, done);
done.clear();
Set<PropositionalVariable> propVars1 = new HashSet<PropositionalVariable>();
formula1.getPropositionalVariables(propVars1, done);
done.clear();
Set<UninterpretedFunction> uif1 = new HashSet<UninterpretedFunction>();
formula1.getUninterpretedFunctions(uif1, done);
done.clear();
Set<DomainVariable> domainVars2 = new HashSet<DomainVariable>();
formula2.getDomainVariables(domainVars2, done);
done.clear();
Set<PropositionalVariable> propVars2 = new HashSet<PropositionalVariable>();
formula2.getPropositionalVariables(propVars2, done);
done.clear();
Set<UninterpretedFunction> uif2 = new HashSet<UninterpretedFunction>();
formula2.getUninterpretedFunctions(uif2, done);
done.clear();
// Vars can be different when one formula contains redundant vars (such
// as (x and not x)).
//
// if (!domainVars1.equals(domainVars2))
// return false;
// if (!propVars1.equals(propVars2))
// return false;
// if (!uif1.equals(uif2))
// return false;
if (!Util.checkFormulaImplication(formula1, formula2))
return false;
if (!Util.checkFormulaImplication(formula2, formula1))
return false;
return true;
}
/**
* Checks if the first formula implies the second formula.
*
* @param formula1
* the first formula
*
* @param formula2
* the second formula
*
* @return returns true, the first formula implies the second formula.
*/
public static boolean checkFormulaImplication(Formula formula1,
Formula formula2) {
List<Formula> conjuncts = new ArrayList<Formula>();
conjuncts.add(formula1);
conjuncts.add(NotFormula.create(formula2));
Formula formulaToCheck = AndFormula.generate(conjuncts);
SMTSolver z3 = SMTSolver.create(SMTSolver.z3_type,
SuraqOptions.getZ3Path());
// DebugHelper.getInstance().stringtoFile(smtstr,
// "debug-tseitin-check.txt");
// System.out.print('.');
z3.solve(formulaToCheck);
switch (z3.getState()) {
case SMTSolver.UNSAT:
return true;
case SMTSolver.SAT:
return false;
default:
throw (new RuntimeException(
"Z3 tells us UNKOWN STATE. CHECK ERROR STREAM."));
}
}
/**
*
* @param formula
* @return <code>true</code> iff the given formula is UNSAT.
*/
public static boolean checkUnsat(Formula formula) {
SMTSolver z3 = SMTSolver.create(SMTSolver.z3_type,
SuraqOptions.getZ3Path());
z3.solve(formula);
switch (z3.getState()) {
case SMTSolver.UNSAT:
return true;
case SMTSolver.SAT:
return false;
default:
throw (new RuntimeException(
"Z3 tells us UNKOWN STATE. CHECK ERROR STREAM."));
}
}
/**
* @param aigNodes
* @param done
* @return
*/
public static int nextFreePositiveAigLiteral(
TreeMap<Integer, Integer[]> aigNodes, Map<Formula, Integer> done) {
return aigNodes.isEmpty() ? 2 + done.size() * 2 : ((aigNodes
.descendingKeySet().iterator().next() + 2) / 2) * 2;
}
/**
* Writes the given formula to the given writer, using let expressions.
*
* @param formula
* @param writer
* @throws IOException
*/
public static void writeFormulaUsingLetExpressions(Formula formula,
Writer writer) throws IOException {
BigInteger size = formula
.size(true, new HashMap<Formula, BigInteger>());
if (size.compareTo(new BigInteger("100")) < 0) {
Util.printToSystemOutWithWallClockTimePrefix("Writing small formula without let expressions. (Size: "
+ size.toString() + ")");
formula.writeTo(writer);
return;
}
Map<Formula, Set<Formula>> parents = new HashMap<Formula, Set<Formula>>();
formula.computeParents(parents, new HashSet<Formula>());
Set<Formula> pseudoLeaves = new HashSet<Formula>();
formula.getLiterals(pseudoLeaves, new HashSet<Formula>());
pseudoLeaves.add(PropositionalConstant.create(true));
pseudoLeaves.add(PropositionalConstant.create(false));
Set<Term> terms = new HashSet<Term>();
formula.getTerms(terms, new HashSet<Formula>());
int idCounter = 0;
// Start outermost let-expression (the one defining terms).
writer.write("(let (\n");
Map<SMTLibObject, String> letDefinitions = new HashMap<SMTLibObject, String>();
for (Term term : terms) {
String id = "et!" + Integer.toString(idCounter++);
letDefinitions.put(term, id);
writer.write("(");
writer.write(id);
writer.write(" ");
term.writeTo(writer);
writer.write(")\n");
}
writer.write(")\n("); // opens the formula part of the outermost let
// expression.
Set<Formula> currentFormulasToDefine = new HashSet<Formula>(
pseudoLeaves);
int letLevels = 0;
while (!currentFormulasToDefine.isEmpty()) {
// start the current let expression
letLevels++;
writer.write("let (\n");
for (Formula currentFormula : currentFormulasToDefine) {
writer.write("(");
String id = "ef!" + Integer.toString(idCounter++);
writer.write(id);
writer.write(" ");
currentFormula.writeTo(writer, letDefinitions);
writer.write(")\n");
letDefinitions.put(currentFormula, id);
pseudoLeaves.add(currentFormula);
}
// Compute the next formulas to define
Set<Formula> nextFormulasToDefine = new HashSet<Formula>();
for (Formula currentFormula : currentFormulasToDefine) {
Set<Formula> currentParents = parents.get(currentFormula);
if (currentParents == null
&& !(currentFormula instanceof PropositionalConstant)) {
assert (currentFormula == formula);
assert (currentFormulasToDefine.size() == 1 || (currentFormulasToDefine
.size() <= 3
&& currentFormulasToDefine
.contains(PropositionalConstant
.create(true)) && currentFormulasToDefine
.contains(PropositionalConstant.create(false))));
assert (nextFormulasToDefine.isEmpty());
assert (letDefinitions.get(formula) != null);
currentFormulasToDefine.clear();
break;
}
if (currentParents != null) {
for (Formula parent : currentParents) {
if (parent.dependsOnlyOn(pseudoLeaves))
nextFormulasToDefine.add(parent);
}
} else
assert (currentFormula instanceof PropositionalConstant);
}
if (!nextFormulasToDefine.isEmpty()) {
// open the formula part of the current let expression
writer.write(")\n(");
currentFormulasToDefine = nextFormulasToDefine;
}
}
writer.write(")"); // Close definitions of innermost let
String rootId = letDefinitions.get(formula);
assert (rootId != null);
writer.write(rootId);
// Close parentheses for let expressions and their formulas
StringBuilder builder = new StringBuilder();
for (int count = 0; count < letLevels; count++)
builder.append(")");
writer.write(builder.toString());
// Close outermost let-expression (the one defining terms)
writer.write("\n)");
writer.flush();
}
/**
* Writes the given formula to a file with the given name.
*
* @param formula
* @param name
* @param useLet
* whether or not to use let-expressions
* @param writeDeclarations
*/
public static void writeFormulaToFile(Formula formula, String name,
boolean useLet, boolean writeDeclarations) {
try {
File file = new File(name);
FileWriter fwriter = new FileWriter(file);
BufferedWriter writer = new BufferedWriter(fwriter);
if (writeDeclarations)
Util.writeDeclarations(formula, writer);
if (useLet)
Util.writeFormulaUsingLetExpressions(formula, writer);
else
formula.writeTo(writer);
writer.close();
fwriter.close();
} catch (IOException exc) {
Util.printToSystemOutWithWallClockTimePrefix("Error writing formula to file.");
exc.printStackTrace();
}
}
}