/**
* Author: Bettina Koenighofer <bettina.koenighofer@iaik.tugraz.at>
*/
package at.iaik.suraq.parser;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
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 at.iaik.suraq.exceptions.ParseError;
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.formula.AndFormula;
import at.iaik.suraq.smtlib.formula.ArrayVariable;
import at.iaik.suraq.smtlib.formula.DomainVariable;
import at.iaik.suraq.smtlib.formula.Formula;
import at.iaik.suraq.smtlib.formula.NotFormula;
import at.iaik.suraq.smtlib.formula.OrFormula;
import at.iaik.suraq.smtlib.formula.PropositionalVariable;
import at.iaik.suraq.smtlib.formula.UninterpretedFunction;
import at.iaik.suraq.util.Timer;
import at.iaik.suraq.util.Util;
/**
* @author Bettina Koenighofer <bettina.koenighofer@iaik.tugraz.at>
*
*/
public class TseitinParser extends SMTLibParser {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* The formula that results from parsing.
*/
private Formula rootFormula = null;
/**
* Stores the partition for which this parser is used.
*/
private final int partition;
/**
*
* Constructs a new <code>TseitinParser</code>.
*
* @param root
* the root expression to parse.
* @param domainVars
* proof domain variables
* @param propsitionalVars
* proof propositional variables
* @param arrayVars
* proof array variables
* @param uninterpretedFunctions
* proof uninterpreted Functions
* @param partition
* the partition for which this parser is used (1 to n).
*/
public TseitinParser(SExpression root, Set<DomainVariable> domainVars,
Set<PropositionalVariable> propsitionalVars,
Set<ArrayVariable> arrayVars,
Set<UninterpretedFunction> uninterpretedFunctions, int partition) {
this.boolVariables = propsitionalVars;
this.arrayVariables = arrayVars;
this.domainVariables = domainVars;
this.functions = uninterpretedFunctions;
this.rootExpr = root;
this.partition = partition;
}
/**
* Parses the root s-expression into a formula, which can then be retrieved
* using <code>getFormula</code>.
*
* @see at.iaik.suraq.parser.Parser#parse()
*/
@Override
public void parse() throws ParseError {
assert (rootExpr.getChildren().size() == 1);
SExpression goalsExpr = rootExpr.getChildren().get(0);
assert (goalsExpr.size() == 2);
assert (goalsExpr.getChildren().get(0)
.equals(SExpressionConstants.GOALS));
SExpression goalExpr = goalsExpr.getChildren().get(1);
assert (goalExpr.size() >= 2);
assert (goalExpr.getChildren().get(0).equals(SExpressionConstants.GOAL));
goalExpr.getChildren().remove(0);
int numChildren = goalExpr.size();
assert (goalExpr.getChildren().get(numChildren - 4).equals(Token
.generate(":precision")));
assert (goalExpr.getChildren().get(numChildren - 3).equals(Token
.generate("precise")));
assert (goalExpr.getChildren().get(numChildren - 2).equals(Token
.generate(":depth")));
goalExpr.removeChild(numChildren - 1);
goalExpr.removeChild(numChildren - 2);
goalExpr.removeChild(numChildren - 3);
goalExpr.removeChild(numChildren - 4);
// System.out.print(""+goalExpr.getChildren().size());
List<Formula> clauses = new ArrayList<Formula>();
List<SExpression> tmp = goalExpr.getChildren();
int size = tmp.size();
int step = size / 100 + 1;
// chillebold: here are to many saved reads (> 2Mrd.) on DomainVar
for (int i = 0; i < size; i++) {
if (i % step == 0) {
System.out.print(" " + (i / step) + "%");
if ((i / step) % 10 == 9)
System.out.print("\n");
}
SExpression expr = tmp.get(i);
clauses.add(parseFormulaBody(expr));
}
Map<Token, PropositionalVariable> renameMap = new HashMap<Token, PropositionalVariable>();
List<PropositionalVariable> tmpList = new ArrayList<PropositionalVariable>(
tseitinVariables.size());
for (PropositionalVariable var : tseitinVariables) {
PropositionalVariable newVar = PropositionalVariable.create(var
.getVarName() + "_p!" + partition);
renameMap.put(Token.generate(var.getVarName()), newVar);
tmpList.add(newVar);
}
tseitinVariables.clear();
tseitinVariables.addAll(tmpList);
rootFormula = (AndFormula.generate(clauses)).substituteFormula(
renameMap, new HashMap<SMTLibObject, SMTLibObject>());
parsingSuccessfull = true;
}
/**
* Returns the root formula that resulted from parsing, or <code>null</code>
* if parsing was not successful.
*
* @return the formula that resulted from parsing, or <code>null</code> if
* parsing was not successful.
*/
public Formula getRootFormula() {
if (!wasParsingSuccessfull())
return null;
return rootFormula;
}
/**
* Calculates the corresponding formula for each tseitin variable.
*
* @return the map of tseitin variables with corresponding formula.
*/
public Map<PropositionalVariable, Formula> getTseitinEncoding() {
Map<PropositionalVariable, Formula> tseitinEncoding = new HashMap<PropositionalVariable, Formula>();
assert (rootFormula instanceof AndFormula);
List<Formula> clauses = ((AndFormula) rootFormula).getConjuncts();
try {
FileWriter fstream = new FileWriter("clauses.smt2");
BufferedWriter buffer = new BufferedWriter(fstream);
for (Formula clause : clauses) {
buffer.write(clause.toString().replaceAll("\\s{2,}", " ")
.replace("\n", ""));
buffer.newLine();
}
buffer.flush();
buffer.close();
fstream.close();
} catch (IOException exc) {
System.err.println("Error while writing to file clauses.smt2. ");
exc.printStackTrace();
}
int currTseitinIndex = 0;
List<Formula> currClauses = new ArrayList<Formula>();
boolean finishCurrTseitinDef = false;
for (int i = 0; i < clauses.size(); i++) {
Formula clause = clauses.get(i);
if (clause instanceof OrFormula) {
List<Integer> tseitinIndices = getTseitinVariablesFromFormula(clause);
int numTseitinVars = tseitinIndices.size();
if (numTseitinVars > 0) {
// check if current tseitin variable occurs in first or in
// the last literal
Formula firstLiteral = ((OrFormula) clause).getDisjuncts()
.get(0);
List<Integer> indicesFirstLit = getTseitinVariablesFromFormula(firstLiteral);
assert (indicesFirstLit.size() <= 1);
int numLiterals = ((OrFormula) clause).getDisjuncts()
.size();
Formula lastLiteral = ((OrFormula) clause).getDisjuncts()
.get(numLiterals - 1);
List<Integer> indicesLastLit = getTseitinVariablesFromFormula(lastLiteral);
assert (indicesLastLit.size() <= 1);
if (indicesFirstLit.contains(currTseitinIndex)
|| indicesLastLit.contains(currTseitinIndex)) {
// check if clause doesn`t contain any larger idx
Collections.sort(tseitinIndices);
if (tseitinIndices.get(numTseitinVars - 1) <= currTseitinIndex) {
if (currClauses.size() == 0) {
/*
* System.out
* .println("INFO: Encoding Tseitin variable " +
* this.tseitinVariables.get( currTseitinIndex)
* .getVarName());
*/
currClauses.add(clause);
} else
finishCurrTseitinDef = true;
} else
finishCurrTseitinDef = true;
} else
finishCurrTseitinDef = true;
} else
finishCurrTseitinDef = true;
if (finishCurrTseitinDef == true) {
finishCurrTseitinDef = false;
if (currClauses.size() > 0) {
PropositionalVariable currTseitinVar = this.tseitinVariables
.get(currTseitinIndex);
Formula formula = buildTseitinFormula(currClauses,
currTseitinVar);
// calc partitions of tseitin variable
Set<Integer> partitions = formula
.getPartitionsFromSymbols();
int partition;
if (partitions.size() == 2) {
assert (partitions.remove(-1));
partition = partitions.iterator().next();
} else {
assert (partitions.size() == 1);
partition = partitions.iterator().next();
}
assert (partition != 0);
tseitinEncoding
.put(PropositionalVariable.create(
currTseitinVar.getVarName(), partition),
formula);
currTseitinIndex++;
currClauses = new ArrayList<Formula>();
i--; // re-check clause
}
}
}
}
return tseitinEncoding;
}
/**
* Build the formula representing a tseiting variable.
*
* @param CurrClauses
* clauses, from which the formula is build.
* @param tseitinVar
* tseitin variable, for which a formula is build
*
* @return the formula representing the tseitin variable
*/
private Formula buildTseitinFormula(List<Formula> CurrClauses,
PropositionalVariable tseitinVar) {
// System.out.println("start build tseitin formula.");
Timer buildTseitinFormulaTimer = new Timer();
buildTseitinFormulaTimer.start();
List<Formula> clauses = new ArrayList<Formula>(CurrClauses);
Formula posFormula = buildPositiveFormula(clauses, tseitinVar);
Formula negFormula = buildNegativeFormula(clauses, tseitinVar);
assert (posFormula != null);
assert (negFormula != null);
while (!Util.checkEquivalenceOfFormulas(posFormula, negFormula)) {
clauses.remove(clauses.size() - 1); // remove last element from list
posFormula = buildPositiveFormula(clauses, tseitinVar);
negFormula = buildNegativeFormula(clauses, tseitinVar);
assert (posFormula != null);
assert (negFormula != null);
}
buildTseitinFormulaTimer.stop();
// System.out.println("finished build tseitin formula in "
// + buildTseitinFormulaTimer + ".\n");
return posFormula;
}
/**
* Builds the formula, which is implied by the current tseitin variable.
*
* @param clauses
* clauses from which the formula is constructed
*
* @param currTseitinVar
* current tseitin variable.
*
* @return formula, which is implied by the current tseitin variable.
*/
private Formula buildNegativeFormula(List<Formula> clauses,
PropositionalVariable currTseitinVar) {
List<Formula> conjuncts = new ArrayList<Formula>();
Boolean first = null;
Boolean neg = null;
for (Formula clause : clauses) {
boolean processed = false;
assert (clause instanceof OrFormula);
List<Formula> literals = ((OrFormula) clause).getDisjuncts();
int numLiterals = literals.size();
Formula firstLiteral = literals.get(0);
Formula lastLiteral = literals.get(numLiterals - 1);
if (firstLiteral instanceof PropositionalVariable) {
if (firstLiteral.equals(currTseitinVar)) {
first = true;
neg = false;
processed = true;
}
}
if (firstLiteral instanceof NotFormula && processed == false) {
firstLiteral = ((NotFormula) firstLiteral).getNegatedFormula();
if (firstLiteral instanceof PropositionalVariable) {
if (firstLiteral.equals(currTseitinVar)) {
first = true;
neg = true;
processed = true;
}
}
}
if (lastLiteral instanceof PropositionalVariable
&& processed == false) {
if (lastLiteral.equals(currTseitinVar)) {
first = false;
neg = false;
processed = true;
}
}
if (lastLiteral instanceof NotFormula && processed == false) {
lastLiteral = ((NotFormula) lastLiteral).getNegatedFormula();
if (lastLiteral instanceof PropositionalVariable) {
if (lastLiteral.equals(currTseitinVar)) {
first = false;
neg = true;
processed = true;
}
}
}
if (!processed)
throw new RuntimeException(
"In the definition of a tseitin variable, the variable should occurr in the beginning or in the end of the clause");
if (!neg)
continue;
if (first)
literals.remove(0);
else
literals.remove(numLiterals - 1);
conjuncts.add(OrFormula.generate(literals));
}
return AndFormula.generate(conjuncts);
}
/**
* Builds the formula, which implies current tseitin variable.
*
* @param clauses
* clauses from which the formula is constructed
*
* @param currTseitinVar
* current tseitin variable.
*
* @return formula, which implies current tseitin variable
*/
private Formula buildPositiveFormula(List<Formula> clauses,
PropositionalVariable currTseitinVar) {
List<Formula> disjuncts = new ArrayList<Formula>();
Boolean first = null;
Boolean neg = null;
for (Formula clause : clauses) {
boolean processed = false;
assert (clause instanceof OrFormula);
List<Formula> literals = ((OrFormula) clause).getDisjuncts();
int numLiterals = literals.size();
Formula firstLiteral = literals.get(0);
Formula lastLiteral = literals.get(numLiterals - 1);
if (firstLiteral instanceof PropositionalVariable) {
if (firstLiteral.equals(currTseitinVar)) {
first = true;
neg = false;
processed = true;
}
}
if (firstLiteral instanceof NotFormula && processed == false) {
firstLiteral = ((NotFormula) firstLiteral).getNegatedFormula();
if (firstLiteral instanceof PropositionalVariable) {
if (firstLiteral.equals(currTseitinVar)) {
first = true;
neg = true;
processed = true;
}
}
}
if (lastLiteral instanceof PropositionalVariable
&& processed == false) {
if (lastLiteral.equals(currTseitinVar)) {
first = false;
neg = false;
processed = true;
}
}
if (lastLiteral instanceof NotFormula && processed == false) {
lastLiteral = ((NotFormula) lastLiteral).getNegatedFormula();
if (lastLiteral instanceof PropositionalVariable) {
if (lastLiteral.equals(currTseitinVar)) {
first = false;
neg = true;
processed = true;
}
}
}
if (!processed)
throw new RuntimeException(
"In the definition of a tseitin variable, the variable should occurr in the beginning or in the end of the clause");
if (neg)
continue;
if (first)
literals.remove(0);
else
literals.remove(numLiterals - 1);
disjuncts.add(NotFormula.create(OrFormula.generate(literals)));
}
return OrFormula.generate(disjuncts);
}
/**
* Returns the indices of the found tseitin variables contained by the
* formula.
*
* @param formula
* formula, to extract variable indices from
*
* @return indices of found tseitin variables
*/
private List<Integer> getTseitinVariablesFromFormula(Formula formula) {
List<Integer> tseitinIndices = new ArrayList<Integer>();
Set<PropositionalVariable> propVars = new HashSet<PropositionalVariable>();
formula.getPropositionalVariables(propVars, new HashSet<SMTLibObject>());
Iterator<PropositionalVariable> iterator = propVars.iterator();
while (iterator.hasNext()) {
PropositionalVariable propVar = iterator.next();
int idx = tseitinVariables.indexOf(propVar);
if (idx >= 0)
tseitinIndices.add(idx);
}
return tseitinIndices;
}
/**
* @see at.iaik.suraq.parser.SMTLibParser#parseFormulaBody(at.iaik.suraq.sexp.SExpression)
*/
@Override
public Formula parseFormulaBody(SExpression expression) throws ParseError {
if (isLet(expression))
return handleLet(expression);
else
return super.parseFormulaBody(expression);
}
}