/** * Author: Christoph Hillebold <c.hillebold@student.tugraz.at> */ package at.iaik.suraq.main; import java.io.File; import java.io.FileWriter; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Set; import at.iaik.suraq.proof.VeritProof; import at.iaik.suraq.proof.VeritProofNode; import at.iaik.suraq.sexp.Token; import at.iaik.suraq.smtlib.formula.AndFormula; import at.iaik.suraq.smtlib.formula.Formula; import at.iaik.suraq.smtlib.formula.NotFormula; import at.iaik.suraq.util.Util; /** * EXPERIMENTAL Class to deal with BadLiterals in VeriTProofs * * @author chillebold * */ @Deprecated public class VeriTProofAnalyzer { /** * The internal stored proof, set by the constructor. */ private final VeritProof proof; /** * Public Constructor, stores the proof internally * * @param proof */ public VeriTProofAnalyzer(VeritProof proof) { this.proof = proof; } /** * This method tries to remove Bad literals from the proof. It only calles * VeriTProofAnalyzer.removeBadLiteralsTransitive(...) in a loop. */ public void removeBadLiterals() { // removes bad literals as long as we were able to remove any. // The removeBadLiteralsTransitive(null) does all the work. // The rest is only for debugging issues. // You can filter, what you want to remove by giving a Type instead of // null // As an example see the blocks beneath the following block. { System.out.println(">> removeBadLiterals (NULL): proof-size: " + proof.getProofNodes().size() + " literals:" + proof.getLiteralConclusionsCount() + " bad: " + this.findBadLiterals(true).size() + " <<"); while (removeBadLiteralsTransitive(null)) { System.out.println(">> removeBadLiterals (NULL): proof-size: " + proof.getProofNodes().size() + " literals:" + proof.getLiteralConclusionsCount() + " bad: " + this.findBadLiterals(true).size() + " <<"); } } // Removes bad literals but only TRANSITIVITIES /* * { System.out.println(">> removeBadLiterals (TRANS): proof-size: " + * proof.getProofNodes().size() + " literals:" + * proof.getLiteralConclusionsCount() + " bad: " + * this.findBadLiterals(true).size() + " <<"); while * (removeBadLiteralsTransitive(VeriTToken.EQ_TRANSITIVE)) { * System.out.println(">> removeBadLiterals (TRANS): proof-size: " + * proof.getProofNodes().size() + " literals:" + * proof.getLiteralConclusionsCount() + " bad: " + * this.findBadLiterals(true).size() + " <<"); } } */ // Removes bad literals but only CONGRUENCES /* * { System.out.println(">> removeBadLiterals (CONGR): proof-size: " + * proof.getProofNodes().size() + " literals:" + * proof.getLiteralConclusionsCount() + " bad: " + * this.findBadLiterals(true).size() + " <<"); while * (removeBadLiteralsTransitive(VeriTToken.EQ_CONGRUENT)) { * System.out.println(">> removeBadLiterals (CONGR): proof-size: " + * proof.getProofNodes().size() + " literals:" + * proof.getLiteralConclusionsCount() + " bad: " + * this.findBadLiterals(true).size() + " <<"); } } */ } /** * Removes bad literals if we can do so. * * @param type * filter: which type of ProofNodes shall be removed? null if we * should not filter. * @return */ @SuppressWarnings("deprecation") private boolean removeBadLiteralsTransitive(Token type) { // in the first step, we only look for eq_transitive and eq_congruent, // which only occurs as leafs (no subProofs) // Set<Formula> badLiterals = this.findBadLiterals(false); // Hashtable<Formula, List<VeritProofNode>> badLiterals = // findBadLiteralsWithProofNode(false); // VeriTProofNode: The ProofNode where the implication is defined // AbstractMap.SimpleEntry: PlaceHolder to Store two Elements: // - Formula and // - a List of Formulas // Example: // a ^ b ^ c => d is the same as: // !a v !b v !c v d (because of that, I call it implications) // --------------------------------------------------------- // the Formula is: d // The list of Formulas is: [!a, !b, !c] // The VeritProofNode is the node, where the badLiteral occurs without // Children and defines something like the example Hashtable<VeritProofNode, AbstractMap.SimpleEntry<Formula, List<Formula>>> implications = findImplicationDefinitionsWithBadLiteralsOnlyinPositiveForm( false, type); System.out .println("\n Found " + implications.size() + " bad literals with no subProofs that only had bad literals in the positive form."); // Iterate over all badLiterals (They have no subProofs) for (VeritProofNode start_node : implications.keySet()) { Formula badLiteral = implications.get(start_node).getKey(); List<Formula> conditions = implications.get(start_node).getValue(); // ignore if we got something with subProofs if (start_node.getSubProofs() != null) { continue; } // System.out.print(start_node.getName()+"("+start_node.getType()+"): "); // 1. propagate to the parents till the (positive) badLiteral // vanishes List<VeritProofNode> ends = new ArrayList<VeritProofNode>(); replaceBadLiteralToParents(start_node, conditions, badLiteral, ends); // remove the start_node from the temporary ends-list. ends.remove(start_node); // System.out.print(" Ends: "+ends.size()+". "); // 2. propagate to the parents till the (negative) badLiteral // vanishes // TODO: split the result in several steps NotFormula negatedBadLiteral = NotFormula.create(badLiteral); for (VeritProofNode end : ends) { replaceBadLiteralToSubProofs(end, conditions, negatedBadLiteral); } // 3. delete current node from proof proof.removeProofSet(start_node); } return implications.size() > 0; } /** * Gets a List of Formulas, if there are NotFormulas inside, they are * removed. * * @param formulas * a list of negated Formulas (still positive if they was * positive) * @return a list of positive Formulas */ private List<Formula> removeNotFormulas(List<Formula> formulas) { ArrayList<Formula> tmp = new ArrayList<Formula>(); for (Formula formula : formulas) { if (formula instanceof NotFormula) tmp.add(((NotFormula) formula).getNegatedFormula()); else tmp.add(formula); } return tmp; } /** * Propagate to the subProofs (parents) * * @param node * The node to start * @param conditions * conditions in negated form * @param badLiteral * badLiteral in negated form */ private void replaceBadLiteralToSubProofs(VeritProofNode node, List<Formula> conditions, Formula badLiteral) { if (node.getSubProofs() == null) { // System.out.print('0'); return; } // Formula badLiteralPositive = // ((NotFormula)badLiteral).getNegatedFormula(); // System.out.print('('); for (VeritProofNode child : node.getSubProofs()) { // following command does NOT return a copy!!! // so we can modify the list directly List<Formula> formulas = child.getLiteralConclusions(); // no copy! int index = formulas.indexOf(badLiteral); if (index == -1) { // the badLiteral does not occur any more :-) } else { // System.out.print('-'); assert (formulas.remove(badLiteral)); // :-) formulas.addAll(conditions); replaceBadLiteralToSubProofs(child, conditions, badLiteral); } // if(formulas.contains(badLiteralPositive)) // we have reached e.g. an other EQ_TRANSITIVE, that also defines // this badLiteral } // System.out.print(')'); } /** * Propagate to the Parents * * @param node * Node to start with * @param conditions * conditions in negated Form * @param badLiteral * badLiteral in positive Form * @param ends */ @SuppressWarnings("deprecation") private void replaceBadLiteralToParents(VeritProofNode node, List<Formula> conditions, Formula badLiteral, List<VeritProofNode> ends) { // badLiteral is in Positive Form, conditions are in Negated Form if (node.getParents() == null) { // System.out.print(node.getName()+"."); ends.add(node); return; } // NotFormula badLiteralNegative = NotFormula.create(badLiteral); // System.out.print('('); for (VeritProofNode parent : node.getParents()) { // following IS ALLOWED, the node could already be removed from the // proof // assert(parent.getSubProofs().contains(node)); List<Formula> formulas = parent.getLiteralConclusions(); // no copy! int position = formulas.indexOf(badLiteral); if (position != -1) { if (conditions.size() == 1) { // unfortunately never occurs System.out.print('/'); formulas.set(position, removeNotFormulas(conditions).get(0)); replaceBadLiteralToParents(parent, conditions, badLiteral, ends); } else { // badLiteral is contained in in positive form if (node.getLiteralConclusions().containsAll( parent.getLiteralConclusions()) && parent.getLiteralConclusions().containsAll( node.getLiteralConclusions())) // if(node.getLiteralConclusions().equals(parent.getLiteralConclusions())) { if (parent.getLiteralConclusions().containsAll( conditions) && parent.getLiteralConclusions().contains( badLiteral)) { // also delete this node, because it is the same as // the one we want to replace // this is probably of type 'conclusion' this.proof.removeProofSet(parent); } else { // does not occur System.out.print('?'); } } else { System.out.print('+'); // just testing how much remains // formulas.remove(position); formulas.set(position, AndFormula .generate(removeNotFormulas(conditions))); } replaceBadLiteralToParents(parent, conditions, badLiteral, ends); } } else { // we have reached the end of the down-propagation // System.out.print(parent.getName()+","); ends.add(parent); } // should not occur and does not occur :-) // if(formulas.contains(badLiteralNegative)) // throw new RuntimeException("Unexpected but not bad #2"); } // System.out.print(')'); } /** * Prints some statistics about badLiterals into a file. * * @param fileBadLiterals * file to print the statistics. */ public void analyzeBadLiteralsSat(File fileBadLiterals) { Set<Formula> badLiterals = findBadLiterals(false); System.out.println("\nThere were " + badLiterals.size() + " unique bad literals. Hash: " + badLiterals.hashCode()); // remove badLiterals, that are once positive and once negative defined. // keep badLiterals that are no Literals! Set<Formula> badLiterals2 = satisfyBadLiterals(badLiterals); System.out .println("There were " + badLiterals2.size() + " bad literals after satisfying positive-negative ones. Hash: " + badLiterals2.hashCode()); if (badLiterals2.size() != 0) { int literals = 0; int noliterals = 0; for (Formula badLiteral : badLiterals2) { if (Util.isLiteral(badLiteral)) literals++; else noliterals++; } System.err.println("These bad literals were " + literals + " literals and " + noliterals + " not literals."); } try { System.out.println("Writing bad literals to file: " + fileBadLiterals); FileWriter fstream = new FileWriter(fileBadLiterals); for (Formula badLiteral : badLiterals) { fstream.write("\n--------------------------------\n"); fstream.write(badLiteral.toString()); } fstream.close(); } catch (Exception ex) { throw new RuntimeException(ex); } } /** * ONLY FOR DEBUG & STATISTICS. Returns badLiterals that are "no literals" * and negative bad literals that does not occur as positive bad literals * somewhere * * @param badLiterals * @return badLiterals that are "no literals" and negative bad literals that * does not occur as positive bad literals somewhere */ public Set<Formula> satisfyBadLiterals(Set<Formula> badLiterals) { Set<Formula> filteredBadLiterals = new HashSet<Formula>(); int positiveElements = 0; Set<Formula> veryBadLiterals = new HashSet<Formula>(); for (Formula badLiteral : badLiterals) { if (badLiteral instanceof NotFormula) { filteredBadLiterals.add(Util.makeLiteralPositive(badLiteral)); } else if (!Util.isLiteral(badLiteral)) { // very bad "literals"!!! if (badLiteral instanceof NotFormula) { filteredBadLiterals.add(((NotFormula) badLiteral) .getNegatedFormula()); } else veryBadLiterals.add(badLiteral); } else positiveElements++; // else: that's ok } System.out.println(" removed " + positiveElements + " elements because they were positive"); // filteredBadLiterals are positive now int removed_elements = 0; int not_removed_elements = 0; for (Formula badLiteral : badLiterals) { if (filteredBadLiterals.remove(badLiteral)) removed_elements++; else if (!(badLiteral instanceof NotFormula)) not_removed_elements++; } System.out.println(" removed " + removed_elements + " elements on filtering and " + not_removed_elements + " were not to remove."); System.out.println(" There were " + veryBadLiterals.size() + " Literals, that were no literals and no NOT-formula"); filteredBadLiterals.addAll(veryBadLiterals); return filteredBadLiterals; } /** * Finds bad literals and stores to each one, in which nodes it occured * * @param areSubproofsAllowed * defines if found elements may contain subproofs * @return */ public Hashtable<Formula, List<VeritProofNode>> findBadLiteralsWithProofNode( boolean areSubproofsAllowed) { Hashtable<Formula, List<VeritProofNode>> badLiterals = new Hashtable<Formula, List<VeritProofNode>>(); for (VeritProofNode node : proof.getProofNodes()) { for (Formula litConclusion : node.getLiteralConclusions()) { Set<Integer> partitions = litConclusion .getPartitionsFromSymbols(); partitions.remove(-1); if (partitions.size() > 1) { if (areSubproofsAllowed || node.getSubProofs() == null) { List<VeritProofNode> proofNodes = badLiterals .get(litConclusion); if (proofNodes == null) { proofNodes = new ArrayList<VeritProofNode>(); badLiterals.put(litConclusion, proofNodes); } proofNodes.add(node); } } if (!Util.isLiteral(litConclusion)) { // throw new // RuntimeException("litConclusion is no literal."); // System.err.println("litConclusion is no literal:"+litConclusion); } } } return badLiterals; } /** * Searches and returns for BadLiterals, that look like Implications (see * example), where the badLiteral is only in the positive form (see * example). * * @param areSubproofsAllowed * controls if subProofs should be allowed or not * @param filter * only that type of ProodNode is returned, if filter != null * @return */ public Hashtable<VeritProofNode, AbstractMap.SimpleEntry<Formula, List<Formula>>> findImplicationDefinitionsWithBadLiteralsOnlyinPositiveForm( boolean areSubproofsAllowed, Token filter) { // example: // a ^ b ^ c => d is the same as: // !a v !b v !c v d (because of that, I call it implications) // so d may contain bad Literals, but a,b,c mustnot contains badLiterals Hashtable<VeritProofNode, AbstractMap.SimpleEntry<Formula, List<Formula>>> implies = new Hashtable<VeritProofNode, AbstractMap.SimpleEntry<Formula, List<Formula>>>(); Set<Integer> partitions; // Iterate through all proofNodes: for (VeritProofNode node : proof.getProofNodes()) { // If a filter is active, perform the filter: if (filter != null && !node.getType().equals(filter)) continue; // if Subproofs are not allowed, filter out those with subProofs: if (!areSubproofsAllowed && node.getSubProofs() != null && node.getSubProofs().size() > 0) continue; // check if this is an implication (TRANSITIVE or CONGRUENCE) // negated terms mustnot contain badLiterals // normal terms must contain a badLiteral (else we have nothing to // do) boolean ok = true; List<Formula> goodLiterals = new ArrayList<Formula>(); List<Formula> badLiterals = new ArrayList<Formula>(); // check if good literals are good: for (Formula litConclusion : node.getLiteralConclusions()) { partitions = litConclusion.getPartitionsFromSymbols(); partitions.remove(-1); boolean contains_badliterals = partitions.size() > 1; // negated terms mustnot contain badLiterals if (litConclusion instanceof NotFormula && !contains_badliterals) { goodLiterals.add(litConclusion); } // normal terms must contain a badLiteral else if (!(litConclusion instanceof NotFormula) && contains_badliterals) { badLiterals.add(litConclusion); } // we cannot do anything about that until now else if (contains_badliterals) { // System.err.println(node.toString()); ok = false; break; } // we have no bad literals else { ok = false; break; } } // if we have an implication, exactly one can contain badLiterals if (ok && badLiterals.size() != 1) ok = false; // at least one mustnot be a badliteral if (ok && goodLiterals.size() < 1) ok = false; if (ok) { // the implication could already exists // I think they are sometimes multiple defined. // we could then also delete the node we found right now // and relink the parents to the given one // but then we should additionally check for the other // parameters implies.put(node, new AbstractMap.SimpleEntry<Formula, List<Formula>>( badLiterals.get(0), goodLiterals)); } } return implies; } /** * Returns all unique badLiterals (Formulas) if any occurs in a Formula. * This may result in duplicate entries in the List for one VeriTProofNode. * * @param areSubproofsAllowed * if false, only nodes with no SubProofs are counted * @return a HashSet of unique Formulas that contains badLiterals */ public Set<Formula> findBadLiterals(boolean areSubproofsAllowed) { // should be equal to: // findBadLiteralsWithProofNode(areSubproofsAllowed).keySet(); Set<Formula> badLiterals = new HashSet<Formula>(); for (VeritProofNode node : proof.getProofNodes()) { for (Formula litConclusion : node.getLiteralConclusions()) { Set<Integer> partitions = litConclusion .getPartitionsFromSymbols(); partitions.remove(-1); if (partitions.size() > 1) { if (areSubproofsAllowed || node.getSubProofs() == null) { badLiterals.add(litConclusion); } } } } return badLiterals; } /** * Analyzes the badLiterals and writes the badLiterals in three files (see * parameters). Also counts some statistics and prints them to console. * * @param erroraLiteralFile * contains all Formulas that contains badLiterals, also * duplicate. * @param errorClauselFile * all VeriTNodes that contains badLiterals * @param errorNoChildren * only writes nodes, that have no parents. */ public void analyzePartitions(File erroraLiteralFile, File errorClauselFile, File errorNoChildren) { System.out .println("* writing assert partition (literal) errors after veriT to '" + erroraLiteralFile + "'"); System.out .println("* writing assert partition (clauses) errors after veriT to '" + errorClauselFile + "'"); System.out .println("* writing assert partition (noChildren) errors after veriT to '" + errorNoChildren + "'"); FileWriter fstream = null; FileWriter fstream_more = null; FileWriter fstream_nochildren = null; try { fstream = new FileWriter(erroraLiteralFile); fstream_more = new FileWriter(errorClauselFile); fstream_nochildren = new FileWriter(errorNoChildren); int formulas = 0; int errors = 0; int errors_unique = 0; int errors_no_childs = 0; for (VeritProofNode node : proof.getProofNodes()) { int i = 0; boolean first_error = false; for (Formula litConclusion : node.getLiteralConclusions()) { formulas++; i++; Set<Integer> partitions = litConclusion .getPartitionsFromSymbols(); partitions.remove(-1); if (partitions.size() > 1) { errors++; fstream.write("--------------------------------------------------\n"); fstream.write("Formula '" + node.getName() + "' #" + i + " part:" + partitions + "\n"); fstream.write(litConclusion.toString()); fstream.write("\n"); if (!first_error) { first_error = true; errors_unique++; fstream_more .write("\n\n--------------------------------------------------"); fstream_more.write("\nFormula '" + node.getName() + // "' of type '" + node.getType() + "'. Conclusion (in OR-Format):\n"); fstream_more.write(node.getConclusionsAsOrFormula() .toString()); if (node.getSubProofs() == null) { errors_no_childs++; fstream_nochildren .write("\n\n--------------------------------------------------\n"); fstream_nochildren.write(node.toString()); } } // System.out.println("Formula '"+node.getName() + // "' #"+i+" p:"+partitions); } } } // Statistics: System.out.println("* There are " + errors + " errors in " + errors_unique + " clauses of " + formulas + " formulas (" + errors_no_childs + " errors without childs)."); } catch (Exception ex) { ex.printStackTrace(); } finally { try { if (fstream != null) fstream.close(); if (fstream_more != null) fstream_more.close(); if (fstream_nochildren != null) fstream_nochildren.close(); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } } } }