/** * Author: Bettina Koenighofer <bettina.koenighofer@iaik.tugraz.at> */ package at.iaik.suraq.smtlib; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import at.iaik.suraq.exceptions.IncomparableTermsException; import at.iaik.suraq.exceptions.WrongFunctionTypeException; import at.iaik.suraq.exceptions.WrongNumberOfParametersException; import at.iaik.suraq.proof.AnnotatedProofNode; import at.iaik.suraq.proof.AnnotatedProofNodes; import at.iaik.suraq.resProof.Literal; import at.iaik.suraq.resProof.ResNode; import at.iaik.suraq.sexp.SExpression; import at.iaik.suraq.sexp.SExpressionConstants; import at.iaik.suraq.sexp.Token; import at.iaik.suraq.smtlib.formula.DomainEq; import at.iaik.suraq.smtlib.formula.DomainTerm; 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.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.PropositionalIte; 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.util.DagOperationManager; import at.iaik.suraq.util.ImmutableSet; import at.iaik.suraq.util.TransitivityChainBuilder; import at.iaik.suraq.util.Util; /** * * Should only contain the following proof rules: Asserted, Symmetry, (simple, * i.e., 2-subproof) Transitivity, Monotonicity and (simple, i.e. 2-subproof) * Resolution. Hypothesis-Lemma-Structures are also still allowed. * * Formulas for consequents should have the following structure: - each atom is * either a positive equality of two terms, a propositional variable, or an * uninterpreted predicate - each literal is either an atom or a negation of an * atom - formula is always an or formula which consists of at least one literal * * Formulas for literals should be positive atoms as defined above. * * @author Bettina Koenighofer <bettina.koenighofer@iaik.tugraz.at>, Georg * Hofferek <georg.hofferek@iaik.tugraz.at> */ public class TransformedZ3Proof extends Z3Proof { /** * */ private static final long serialVersionUID = -3623762531459362582L; @Deprecated public static TransformedZ3Proof debugNode = null; /** * This maps IDs of Z3Proofs to their TransformedZ3Proof counterparts. This * is to avoid DAG unwinding during conversion. */ public static Map<Integer, TransformedZ3Proof> proofMap = new HashMap<Integer, TransformedZ3Proof>(); /** * The "literal" on which resolution is applied. This could e.g. be an * equality of the form (a=b), or (f(a)=c). It could also be a propositional * variable, or an (uninterpreted) predicate instance. <code>literal</code> * will be <code>null</code> for leaves of the proof. In non-leave nodes, * <code>literal</code> should store the positive (=non-negated) form of the * resolution literal. I.e., <code>literal</code> should not be of type * <code>NotFormula</code>. */ private Formula literal = null; /** * Indicates that this proof object is a hypothesis. This implies that it is * also a leave. */ private boolean hypothesis = false; /** * This flag stores whether this node has already been made local, at some * point in time. * * Caveat: If something is changed in somewhere in the sub-graph below this * node, this flag might indicate the wrong value. Thus, be careful! */ private boolean hasBeenMadeLocal = false; private static long numberOfRemovedObsoloteResolutionSteps = 0; /** * Storage for annotated nodes used during proof conversion. */ private static Deque<AnnotatedProofNodes> annotatedNodesStack = new ArrayDeque<AnnotatedProofNodes>(); /** * Counts how often the method <code>convertToTransformedZ3Proof</code> has * been called. */ private static long conversionCounter = 0; /** * Creates a new <code>TransformedZ3Proof</code>. * * @param proofType * type of the proof. * @param subProofs * the subproofs. * @param consequent * the consequent. * */ public TransformedZ3Proof(Token proofType, List<TransformedZ3Proof> subProofs, Formula consequent) { super(proofType, subProofs, consequent.transformToConsequentsForm() .deepFormulaCopy()); this.recomputeObsoleteLiterals(); this.computeHasBeenMadeLocal(); assert (proofType.equals(SExpressionConstants.ASSERTED) || proofType.equals(SExpressionConstants.TRANSITIVITY) || proofType.equals(SExpressionConstants.SYMMETRY) || proofType.equals(SExpressionConstants.MONOTONICITY) || proofType.equals(SExpressionConstants.UNIT_RESOLUTION) || proofType.equals(SExpressionConstants.LEMMA) || proofType .equals(SExpressionConstants.HYPOTHESIS)); // if (this.id == 548) // this.checkZ3ProofNodeRecursive(); // assert (this.checkZ3ProofNode()); } /** * Creates a new <code>TransformedZ3Proof</code>. * * @param proofType * type of the proof. * @param subProofs * the subproofs. * @param literal * the literal of the resolution proof. * @param consequent * the consequent. * */ public TransformedZ3Proof(Token proofType, List<TransformedZ3Proof> subProofs, Formula literal, Formula consequent) { super(proofType, subProofs, consequent.transformToConsequentsForm() .deepFormulaCopy()); this.literal = literal == null ? null : Util.getSingleLiteral(literal .deepFormulaCopy()); this.recomputeObsoleteLiterals(); this.computeHasBeenMadeLocal(); assert (literal == null || this.proofType .equals(SExpressionConstants.UNIT_RESOLUTION)); assert (this.checkLiteralOccurenceInSubProofs()); assert (proofType.equals(SExpressionConstants.ASSERTED) || proofType.equals(SExpressionConstants.TRANSITIVITY) || proofType.equals(SExpressionConstants.SYMMETRY) || proofType.equals(SExpressionConstants.MONOTONICITY) || proofType .equals(SExpressionConstants.UNIT_RESOLUTION)); // if (this.id == 548) // this.checkZ3ProofNodeRecursive(); // assert (this.checkZ3ProofNode()); } public TransformedZ3Proof(Token proofType, List<TransformedZ3Proof> subProofs, Formula consequent, int assertPartition, boolean axiom) { super(proofType, subProofs, consequent.transformToConsequentsForm() .deepFormulaCopy(), assertPartition, axiom); this.recomputeObsoleteLiterals(); this.computeHasBeenMadeLocal(); assert (proofType.equals(SExpressionConstants.ASSERTED) || proofType.equals(SExpressionConstants.TRANSITIVITY) || proofType.equals(SExpressionConstants.SYMMETRY) || proofType.equals(SExpressionConstants.MONOTONICITY) || proofType .equals(SExpressionConstants.UNIT_RESOLUTION)); } /** * Creates a new <code>TransformedZ3Proof</code>. * * @param proofType * type of the proof. * @param subProof1 * the first subproof. * @param subProof2 * the second subproof. * @param literal * the literal of the resolution proof. * @param consequent * the consequent. * */ public TransformedZ3Proof(Token proofType, Z3Proof subProof1, Z3Proof subProof2, Formula literal, Formula consequent) { super(proofType, subProof1, subProof2, consequent .transformToConsequentsForm().deepFormulaCopy()); this.literal = literal == null ? null : Util.makeLiteralPositive(Util .getSingleLiteral(literal.deepFormulaCopy())); this.recomputeObsoleteLiterals(); this.computeHasBeenMadeLocal(); assert (proofType.equals(SExpressionConstants.ASSERTED) || proofType.equals(SExpressionConstants.TRANSITIVITY) || proofType.equals(SExpressionConstants.SYMMETRY) || proofType.equals(SExpressionConstants.MONOTONICITY) || proofType .equals(SExpressionConstants.UNIT_RESOLUTION)); // if (this.id == 548 || subProof1.id == 548 || subProof2.id == 548) // this.checkZ3ProofNodeRecursive(); // assert (this.checkZ3ProofNode()); } public TransformedZ3Proof(ResNode node, Map<Integer, Formula> literalMap, Map<ResNode, TransformedZ3Proof> cache) { assert (cache != null); assert (!cache.containsKey(node)); if (!node.isLeaf()) { // CREATE RESOLUTION NODE this.proofType = SExpressionConstants.UNIT_RESOLUTION; this.literal = literalMap.get(node.getPivot()); TransformedZ3Proof left = cache.get(node.getLeft()); this.subProofs.add(left == null ? new TransformedZ3Proof(node .getLeft(), literalMap, cache) : left); TransformedZ3Proof right = cache.get(node.getRight()); this.subProofs.add(right == null ? new TransformedZ3Proof(node .getRight(), literalMap, cache) : right); } else { // CREATE ASSERTED NODE this.proofType = SExpressionConstants.ASSERTED; } // build consequent from clause List<Formula> disjuncts = new ArrayList<Formula>(); for (Literal literal : node.getClause()) { Formula elem = literalMap.get(literal.id()); if (!literal.isPos()) elem = NotFormula.create(elem); disjuncts.add(elem); } if (disjuncts.size() == 0) disjuncts.add(PropositionalConstant.create(false)); this.consequent = (OrFormula.generate(disjuncts)) .transformToConsequentsForm(); // this.recomputeObsoleteLiterals(); // What's this for?? // TODO Check: Should this be set for leafs only? // assign global clauses to part 1 (arbitrary choice) this.assertPartition = node.getPart() == 0 ? 1 : node.getPart(); assert (proofType.equals(SExpressionConstants.ASSERTED) || proofType .equals(SExpressionConstants.UNIT_RESOLUTION)); assert (this.assertPartition > 0 || !this.proofType .equals(SExpressionConstants.ASSERTED)); cache.put(node, this); } private void computeHasBeenMadeLocal() { if (subProofs.size() == 0) { // Leafs must be marked false at construction time. this.hasBeenMadeLocal = false; return; } if (Util.containsBadLiteral(this.consequent .transformToConsequentsForm())) { this.hasBeenMadeLocal = false; return; } for (Z3Proof child : subProofs) { assert (child instanceof TransformedZ3Proof); if (!((TransformedZ3Proof) child).hasBeenMadeLocal) { this.hasBeenMadeLocal = false; return; } } this.hasBeenMadeLocal = true; if (this.hasSingleLiteralConsequent()) { TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(this)); } return; } @Override protected void takeValuesFrom(Z3Proof proof) { assert (proof instanceof TransformedZ3Proof); super.takeValuesFrom(proof); TransformedZ3Proof transformedProof = (TransformedZ3Proof) proof; this.literal = transformedProof.literal == null ? null : transformedProof.literal.deepFormulaCopy(); this.hypothesis = transformedProof.hypothesis; this.obsoleteLiterals = transformedProof.obsoleteLiterals; this.hasBeenMadeLocal = transformedProof.hasBeenMadeLocal; } public static TransformedZ3Proof convertToTransformedZ3Proof(Z3Proof z3Proof) { TransformedZ3Proof.conversionCounter++; if (TransformedZ3Proof.conversionCounter % 1000 == 0) { System.out .println("PROGRESS-INFO: Conversion counter is at " + Z3Proof.myFormatter .format(TransformedZ3Proof.conversionCounter) + "."); System.out.println("PROGRESS-INFO: proofMap contains " + Z3Proof.myFormatter.format(TransformedZ3Proof.proofMap .size()) + " nodes."); } // TransformedZ3Proof.debugNode = TransformedZ3Proof.proofMap.get(307); if (TransformedZ3Proof.proofMap.containsKey(z3Proof.id)) return TransformedZ3Proof.proofMap.get(z3Proof.id); // TransformedZ3Proof.proofMap.remove(z3Proof.id); // Go through all possible cases of z3 proof rules Token proofType = z3Proof.getProofType(); if (proofType.equals(SExpressionConstants.AND_ELIM) || proofType.equals(SExpressionConstants.NOT_OR_ELIM) || proofType.equals(SExpressionConstants.REWRITE) || proofType.equals(SExpressionConstants.ASSERTED) || proofType.equals(SExpressionConstants.COMMUTATIVITY)) { // Treat this as a leave. // Relies on the assumption that and-elim (not-or-elim) is only used // for things that have been asserted, and not on things are are // proven separately. assert (z3Proof.subProofs.size() == 0); if (z3Proof instanceof TransformedZ3Proof) return (TransformedZ3Proof) z3Proof; TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.ASSERTED, new ArrayList<TransformedZ3Proof>(), z3Proof .getConsequent().transformToConsequentsForm()); if (z3Proof.assertPartition > 0) result.assertPartition = z3Proof.assertPartition; assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.HYPOTHESIS)) { // Treat this as a leave. assert (z3Proof.subProofs.isEmpty()); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.ASSERTED, new ArrayList<TransformedZ3Proof>(), z3Proof .getConsequent().transformToConsequentsForm()); result.hypothesis = true; result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.AXIOM) || proofType.equals(SExpressionConstants.REFLEXIVITY)) { // Treat this as a leave. // It should be a propositional tautology. assert (z3Proof.subProofs.isEmpty()); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.ASSERTED, new ArrayList<TransformedZ3Proof>(), z3Proof .getConsequent().transformToConsequentsForm()); if (z3Proof.assertPartition > 0) result.assertPartition = z3Proof.assertPartition; result.axiom = true; result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.MODUS_PONENS)) { // Recursive conversion calls will be done inside // handleModusPonens(Z3Proof). assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); TransformedZ3Proof result = TransformedZ3Proof .convertModusPonens(z3Proof); assert (result.getConsequent().equals(z3Proof.getConsequent() .transformToConsequentsForm())); result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) { List<Z3Proof> z3SubProofs = z3Proof.getSubProofs(); assert (z3SubProofs.size() >= 2); TransformedZ3Proof transformedAntecedent = TransformedZ3Proof .convertToTransformedZ3Proof(z3SubProofs.get(0)); assert (transformedAntecedent.consequent instanceof OrFormula); OrFormula remainingFormula = (OrFormula) transformedAntecedent.consequent; for (int count = 1; count < z3SubProofs.size() - 1; count++) { List<Formula> newDisjuncts = remainingFormula.getDisjuncts(); Formula resolutionAssociate = Util.getSingleLiteral(z3SubProofs .get(count).getConsequent() .transformToConsequentsForm()); if (!(Util.isLiteral(resolutionAssociate))) throw new RuntimeException( "Resolution associate should be a literal"); Formula invLiteral = Util.invertLiteral(resolutionAssociate); Formula posLiteral = Util .makeLiteralPositive(resolutionAssociate); if (!newDisjuncts.remove(invLiteral)) throw new RuntimeException( "Problem in Unit Resolution Transformation:\n" + "Literal was not present in the remaining formula:\n " + "List of Literals: " + remainingFormula + "given Literal: " + invLiteral); remainingFormula = (OrFormula.generate(newDisjuncts)) .transformToConsequentsForm(); transformedAntecedent = new TransformedZ3Proof( SExpressionConstants.UNIT_RESOLUTION, TransformedZ3Proof .convertToTransformedZ3Proof(z3SubProofs .get(count)), transformedAntecedent, posLiteral, remainingFormula.transformToConsequentsForm()); } List<TransformedZ3Proof> subproofs = new ArrayList<TransformedZ3Proof>(); subproofs.add(transformedAntecedent); subproofs.add(TransformedZ3Proof .convertToTransformedZ3Proof(z3SubProofs.get(z3SubProofs .size() - 1))); TransformedZ3Proof result = new TransformedZ3Proof( proofType = SExpressionConstants.UNIT_RESOLUTION, subproofs, z3Proof.getConsequent() .transformToConsequentsForm()); Formula resolutionAssociate = z3SubProofs .get(z3SubProofs.size() - 1).getConsequent() .transformToConsequentsForm(); assert (resolutionAssociate instanceof OrFormula); result.literal = Util.findResolvingLiteral( (OrFormula) resolutionAssociate, transformedAntecedent .getConsequent().transformToConsequentsForm()); result.consequent = z3Proof.getConsequent() .transformToConsequentsForm(); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.LEMMA)) { List<Z3Proof> z3SubProofs = z3Proof.getSubProofs(); if (z3SubProofs.size() != 1) throw new RuntimeException( "Lemma proof with not exactly one child. This should not happen!"); Z3Proof hypotheticalProof = z3SubProofs.get(0); // assert (hypotheticalProof.checkZ3ProofNodeRecursive()); // hypotheticalProof.removeLocalSubProofs(); // redundant with call // in main (on main proof)? // assert (hypotheticalProof.checkZ3ProofNodeRecursive()); TransformedZ3Proof transformedHypotheticalProof = TransformedZ3Proof .convertToTransformedZ3Proof(hypotheticalProof); List<TransformedZ3Proof> list = new ArrayList<TransformedZ3Proof>(1); list.add(transformedHypotheticalProof); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.LEMMA, list, z3Proof.consequent.deepFormulaCopy()); result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.TRANSITIVITY)) { assert (z3Proof.subProofs.size() >= 2); TransformedZ3Proof current1 = TransformedZ3Proof .convertToTransformedZ3Proof(z3Proof.subProofs.get(0)); for (int count = 1; count < z3Proof.subProofs.size(); count++) { TransformedZ3Proof current2 = TransformedZ3Proof .convertToTransformedZ3Proof(z3Proof.subProofs .get(count)); List<TransformedZ3Proof> currentSubProofs = new ArrayList<TransformedZ3Proof>( 2); currentSubProofs.add(current1); currentSubProofs.add(current2); current1 = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(currentSubProofs); assert (current1.subProofs.size() == 2); } assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); current1.obsoleteLiterals = current1.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); TransformedZ3Proof.proofMap.put(z3Proof.id, current1); return current1; } else if (proofType.equals(SExpressionConstants.SYMMETRY)) { List<TransformedZ3Proof> subProofs = new ArrayList<TransformedZ3Proof>(); for (Z3Proof proof : z3Proof.subProofs) { subProofs.add(TransformedZ3Proof .convertToTransformedZ3Proof(proof)); } TransformedZ3Proof result = new TransformedZ3Proof(proofType, subProofs, z3Proof.getConsequent() .transformToConsequentsForm()); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else if (proofType.equals(SExpressionConstants.MONOTONICITY)) { // Add missing reflexivity proofs. Having them makes some // intermediate step easier, although they are removed later on. z3Proof.addMissingReflexivityProofs(); List<TransformedZ3Proof> subProofs = new ArrayList<TransformedZ3Proof>(); for (Z3Proof proof : z3Proof.subProofs) { subProofs.add(TransformedZ3Proof .convertToTransformedZ3Proof(proof)); } TransformedZ3Proof result = new TransformedZ3Proof(proofType, subProofs, z3Proof.getConsequent() .transformToConsequentsForm()); assert (!TransformedZ3Proof.proofMap.containsKey(z3Proof.id)); result.obsoleteLiterals = result.obsoleteLiterals .addAllToCopy(z3Proof.obsoleteLiterals); TransformedZ3Proof.proofMap.put(z3Proof.id, result); return result; } else { throw new RuntimeException("Encountered unexpected proof rule " + proofType.toString() + " while trying to rewrite z3 proof."); } } public AnnotatedProofNodes toLocalProof() { return this.toLocalProof(null); } public AnnotatedProofNodes toLocalProof(String operationName) { // Actually, stack seems not to be needed. // To avoid refactoring, just don't add a new frame, // if there already is one. if (TransformedZ3Proof.annotatedNodesStack.size() < 1) TransformedZ3Proof.annotatedNodesStack .addFirst(new AnnotatedProofNodes()); assert (TransformedZ3Proof.annotatedNodesStack.size() == 1); long operationId = operationName != null ? DagOperationManager .startDAGOperation(operationName) : DagOperationManager .startDAGOperation(); if (operationName != null) DagOperationManager.setMessageInterval(operationId, 100); this.toLocalProofRecursion(operationId); DagOperationManager.endDAGOperation(operationId); return TransformedZ3Proof.annotatedNodesStack.peekFirst(); } /** * Transforms the proof into a local resolution proof (in place). */ private void toLocalProofRecursion(long operationId) { if (this.wasVisitedByDAGOperation(operationId)) { // The fact that this node was visited does not imply that it is now // local. It could have been "detached" from the branch by which it // was visited, because, e.g., it contains a bad literal. return; } if (this.hasBeenMadeLocal) { // Do a quick, sound, but non-complete check // At least, the direct parents must also have the flag set. for (Z3Proof child : subProofs) { assert (child instanceof TransformedZ3Proof); assert (((TransformedZ3Proof) child).hasBeenMadeLocal); } visitedByDAGOperation(operationId); return; } visitedByDAGOperation(operationId); if (obsoleteLiterals == null) obsoleteLiterals = ImmutableSet.create(new HashSet<Formula>()); for (int count = 0; count < subProofs.size(); count++) { Z3Proof child = subProofs.get(count); assert (child instanceof TransformedZ3Proof); TransformedZ3Proof subProof = (TransformedZ3Proof) child; subProof.toLocalProofRecursion(operationId); if (subProof.hasSingleLiteralConsequent() && !this.proofType.equals(SExpressionConstants.LEMMA)) { // Use a localized version of this subproof Set<Formula> newObsoleteLiterals = new HashSet<Formula>(); TransformedZ3Proof update = TransformedZ3Proof.annotatedNodesStack .peekFirst() .getNodeWithConsequent(subProof.consequent, subProof.getHypothesisFormulas(), newObsoleteLiterals).getConsequent(); if (subProof != update) { subProof.removeParent(this); this.subProofs.set(count, update); update.addParent(this); Z3Proof.hypModCount++; this.markHypCacheDirty(); this.obsoleteLiterals = this.obsoleteLiterals .addAllToCopy(newObsoleteLiterals); } } assert (obsoleteLiterals != null); obsoleteLiterals = obsoleteLiterals .addAllToCopy(subProof.obsoleteLiterals); } // Recursive calls are completed. Now handle this particular node. // ------------------------------------------------------------- if (this.proofType.equals(SExpressionConstants.LEMMA)) { // Nothing more to do for lemmas with multiple-literal consequents. if (!this.hasSingleLiteralConsequent()) { assert (!Util.containsBadLiteral(this.consequent .transformToConsequentsForm())); this.setHasBeenMadeLocal(); return; } } // ------------------------------------------------------------- if (this.proofType.equals(SExpressionConstants.SYMMETRY)) { assert (this.hasSingleLiteralConsequent()); assert (subProofs.size() == 1); handleSymmetry(); return; } // ------------------------------------------------------------- if (this.proofType.equals(SExpressionConstants.TRANSITIVITY)) { assert (this.hasSingleLiteralConsequent()); handleTransitivity(); return; } // ------------------------------------------------------------- if (this.proofType.equals(SExpressionConstants.MONOTONICITY)) { assert (this.hasSingleLiteralConsequent()); handleMonotonicity(); return; } // ------------------------------------------------------------- if (!this.hasSingleLiteralConsequent() || this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) { // The following is based on the assumption that resolution uses // "bad" (=non-local) literals only if it directly concludes // "false". // // This is justified as follows: // The original formula does not use bad literals. // Theory rules produce only single-literal consequents. // Therefore, bad literals can only occur in unit clauses. // TODO Check if this assumption is valid. If not, change code // accordingly! if (!(this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION) || this.proofType .equals(SExpressionConstants.ASSERTED))) assert (false); if (this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) { // Update subproofs assert (subProofs.size() == 2); // Multi-resolution should // already have been removed assert (Util.checkResolutionNodeForBadLiterals(this)); if (Util.isLiteral(subProofs.get(0).consequent)) { Set<Formula> newObsoleteLiterals = new HashSet<Formula>(); AnnotatedProofNode annotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst() .getNodeWithConsequent( Util.getSingleLiteral(subProofs.get(0).consequent), subProofs.get(0).getHypothesisFormulas(), newObsoleteLiterals); if (annotatedNode != null) { TransformedZ3Proof update = annotatedNode .getConsequent(); if (annotatedNode.numPremises() == 3) { List<TransformedZ3Proof> transSubProofs = new ArrayList<TransformedZ3Proof>( 3); transSubProofs.add(annotatedNode.getPremise1()); transSubProofs.add(annotatedNode.getPremise2()); transSubProofs.add(annotatedNode.getPremise3()); update = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(transSubProofs); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(new AnnotatedProofNode(update)); update.obsoleteLiterals = update.obsoleteLiterals .addAllToCopy(newObsoleteLiterals); } // Too expensive? // assert (((TransformedZ3Proof) update).isLocal()); if (subProofs.get(0) != update) { subProofs.get(0).removeParent(this); subProofs.set(0, update); update.addParent(this); Z3Proof.hypModCount++; this.markHypCacheDirty(); recomputeObsoleteLiterals(); } } } if (Util.isLiteral(subProofs.get(1).consequent)) { Set<Formula> newObsoleteLiterals = new HashSet<Formula>(); AnnotatedProofNode annotatedNode2 = TransformedZ3Proof.annotatedNodesStack .peekFirst() .getNodeWithConsequent( Util.getSingleLiteral(subProofs.get(1).consequent), subProofs.get(1).getHypothesisFormulas(), newObsoleteLiterals); if (annotatedNode2 != null) { TransformedZ3Proof update = annotatedNode2 .getConsequent(); if (annotatedNode2.numPremises() == 3) { List<TransformedZ3Proof> transSubProofs = new ArrayList<TransformedZ3Proof>( 3); transSubProofs.add(annotatedNode2.getPremise1()); transSubProofs.add(annotatedNode2.getPremise2()); transSubProofs.add(annotatedNode2.getPremise3()); update = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(transSubProofs); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(new AnnotatedProofNode(update)); update.obsoleteLiterals = update.obsoleteLiterals .addAllToCopy(newObsoleteLiterals); } // Too expensive? // assert (((TransformedZ3Proof) update).isLocal()); if (subProofs.get(1) != update) { subProofs.get(1).removeParent(this); subProofs.set(1, update); update.addParent(this); Z3Proof.hypModCount++; this.markHypCacheDirty(); recomputeObsoleteLiterals(); } } } // Remove obsolete resolution steps assert (subProofs.get(0).consequent instanceof OrFormula); assert (subProofs.get(1).consequent instanceof OrFormula); OrFormula obsoleteClause = Util.findObsoleteClause( (OrFormula) subProofs.get(0).consequent, (OrFormula) subProofs.get(1).consequent, obsoleteLiterals); if (obsoleteClause != null) { TransformedZ3Proof.numberOfRemovedObsoloteResolutionSteps++; Formula literal = Util.findResolvingLiteral( (OrFormula) subProofs.get(0).consequent, (OrFormula) subProofs.get(1).consequent); Collection<Formula> newObsoleteLiterals = obsoleteClause .getDisjuncts(); if (!newObsoleteLiterals.remove(literal)) if (!newObsoleteLiterals.remove(Util .invertLiteral(literal))) assert (false); // One of the polarities must be // contained Z3Proof nonObsoleteProof = null; if (obsoleteClause.equals(subProofs.get(0).consequent)) { nonObsoleteProof = subProofs.get(1); } else { assert (obsoleteClause .equals(subProofs.get(1).consequent)); nonObsoleteProof = subProofs.get(0); } assert (nonObsoleteProof != null); assert (nonObsoleteProof instanceof TransformedZ3Proof); this.takeValuesFrom(nonObsoleteProof); obsoleteLiterals = obsoleteLiterals .addAllToCopy(newObsoleteLiterals); } } this.setHasBeenMadeLocal(); if (!this.hasSingleLiteralConsequent()) // For single literals, we still need to add an annotated node return; } if (this.hasSingleLiteralConsequent()) { // Add a new annotated node for this single-literal-consequent // node. AnnotatedProofNode annotatedNode = createNewAnnotatedNodeFromProofWithSingleLiteralConsequent(this); if (annotatedNode != null) TransformedZ3Proof.annotatedNodesStack.peekFirst().add( annotatedNode); return; } // If we reach here, we are in case that was not foreseen. assert (false); } /** * (Re-)computes the set of obsolete literals as the union of obsolete * literals of the subproofs. Existing obsolete literals are overwritten and * will not be present any more, unless they also occur in a subproof. */ private void recomputeObsoleteLiterals() { obsoleteLiterals = ImmutableSet.create(new HashSet<Formula>()); for (Z3Proof subproof : subProofs) { assert (subproof instanceof TransformedZ3Proof); obsoleteLiterals = obsoleteLiterals .addAllToCopy(((TransformedZ3Proof) subproof).obsoleteLiterals); } } private AnnotatedProofNode createNewAnnotatedNodeFromProofWithSingleLiteralConsequent( TransformedZ3Proof proof) { assert (proof.hasSingleLiteralConsequent()); Formula literal = Util.getSingleLiteral(proof.consequent); if (literal.equals(PropositionalConstant.create(false))) // We do not need an AnnotatedNode for false. return null; Set<Integer> partitions = literal.getPartitionsFromSymbols(); assert (partitions.size() > 0); partitions.remove(-1); if (partitions.size() > 1) // this is a bad literal. We ignore it. It will be // removed anyway return null; int partition; if (partitions.size() == 0) partition = 1; // put global stuff in partition 1 (arbitrary // choice) else { assert (partitions.iterator().hasNext()); assert (partitions.size() == 1); partition = partitions.iterator().next(); } AnnotatedProofNode annotatedNode = new AnnotatedProofNode(partition, partition, proof, null, null, null); return annotatedNode; } private void handleSymmetry() { Z3Proof subproof = subProofs.get(0); Formula premiseLiteral = Util.getSingleLiteral(subproof.consequent); Set<Formula> newObsoleteLiterals = new HashSet<Formula>(); AnnotatedProofNode annotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst().getNodeWithConsequent(premiseLiteral, subproof.getHypothesisFormulas(), newObsoleteLiterals); obsoleteLiterals = obsoleteLiterals.addAllToCopy(newObsoleteLiterals); if (annotatedNode == null) throw new RuntimeException( "No annotated proof node found when there should be one."); int numPremises = annotatedNode.numPremises(); if (numPremises != 0 && numPremises != 3) throw new RuntimeException( "Unexpected number of premises for annotated symmetry node: " + (new Integer(numPremises)).toString()); if (numPremises == 0) { assert (this.subProofs.size() == 1); Z3Proof premise = this.subProofs.get(0); assert (premise.hasSingleLiteralConsequent()); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(annotatedNode.getPartition(), annotatedNode.getPartition(), this, null, null, null)); } else { assert (numPremises == 3); TransformedZ3Proof newSymmetry1 = TransformedZ3Proof .createSymmetryProof(annotatedNode.getPremise3()); AnnotatedProofNode newPremise1 = new AnnotatedProofNode( newSymmetry1); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise1); TransformedZ3Proof newSymmetry2 = TransformedZ3Proof .createSymmetryProof(annotatedNode.getPremise2()); AnnotatedProofNode newPremise2 = new AnnotatedProofNode( newSymmetry2); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise2); TransformedZ3Proof newSymmetry3 = TransformedZ3Proof .createSymmetryProof(annotatedNode.getPremise1()); AnnotatedProofNode newPremise3 = new AnnotatedProofNode( newSymmetry3); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise3); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(annotatedNode.getRightPartition(), annotatedNode.getLeftPartition(), this, newPremise1, newPremise2, newPremise3)); } } private void handleMonotonicity() { assert (subProofs.size() >= 1); Formula consequentLiteral = Util.getSingleLiteral(consequent); if (!(consequentLiteral instanceof EqualityFormula)) assert (false); EqualityFormula consequentEq = (EqualityFormula) consequentLiteral; Term leftTerm = consequentEq.getTerms().get(0); Term rightTerm = consequentEq.getTerms().get( consequentEq.getTerms().size() - 1); Set<Integer> leftPartitions = leftTerm.getPartitionsFromSymbols(); leftPartitions.remove(-1); assert (leftPartitions.size() <= 1); int leftPartition; Iterator<Integer> leftIterator = leftPartitions.iterator(); if (leftIterator.hasNext()) leftPartition = leftIterator.next(); else leftPartition = 1; // arbitrary choice Set<Integer> rightPartitions = rightTerm.getPartitionsFromSymbols(); rightPartitions.remove(-1); assert (rightPartitions.size() <= 1); int rightPartition; Iterator<Integer> rightIterator = rightPartitions.iterator(); if (rightIterator.hasNext()) rightPartition = rightIterator.next(); else rightPartition = leftPartition; // arbitrary choice if (leftPartition == rightPartition) { // this is a local node Set<Formula> newObsoleteLiterals = new HashSet<Formula>(); for (int count = 0; count < subProofs.size(); count++) { assert (subProofs.get(count) instanceof TransformedZ3Proof); Z3Proof subProof = subProofs.get(count); Set<Formula> currentNewObsoleteLiterals = new HashSet<Formula>(); AnnotatedProofNode currentAnnotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst().getNodeWithConsequent(subProof.consequent, subProof.getHypothesisFormulas(), currentNewObsoleteLiterals); newObsoleteLiterals.addAll(currentNewObsoleteLiterals); if (currentAnnotatedNode.numPremises() == 3) { List<TransformedZ3Proof> proofs = new ArrayList<TransformedZ3Proof>( 3); proofs.add(currentAnnotatedNode.getPremise1()); proofs.add(currentAnnotatedNode.getPremise2()); proofs.add(currentAnnotatedNode.getPremise3()); TransformedZ3Proof newProof = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(proofs); this.subProofs.set(count, newProof); newProof.addParent(this); newProof.setHasBeenMadeLocal(); Z3Proof.hypModCount++; this.markHypCacheDirty(); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(newProof)); } } this.recomputeObsoleteLiterals(); this.obsoleteLiterals = this.obsoleteLiterals .addAllToCopy(newObsoleteLiterals); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(this)); return; } List<DomainTerm> newLeftTerms = new ArrayList<DomainTerm>(); List<DomainTerm> newRightTerms = new ArrayList<DomainTerm>(); List<TransformedZ3Proof[]> proofs = new ArrayList<TransformedZ3Proof[]>(); for (int count = 0; count < subProofs.size(); count++) { AnnotatedProofNode currentAnnotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst().getNodeWithConsequent( subProofs.get(count).consequent, subProofs.get(count).getHypothesisFormulas(), new HashSet<Formula>()); assert (currentAnnotatedNode != null); DomainTerm[] oldTerms = Util.getDomainTerms(currentAnnotatedNode); DomainTerm currentLeftTerm = computeCurrentLeftTermForMonotonicity( leftPartition, currentAnnotatedNode); newLeftTerms.add(currentLeftTerm); DomainTerm currentRightTerm = computeCurrentRightTermForMonotonicity( rightPartition, currentAnnotatedNode); newRightTerms.add(currentRightTerm); TransformedZ3Proof newTransitivityProofNode = null; if (currentAnnotatedNode.numPremises() == 3) { DomainTerm[] currentTerms = Util .getDomainTerms(currentAnnotatedNode); assert (currentTerms.length == 4); List<TransformedZ3Proof> currentSubProofs = new ArrayList<TransformedZ3Proof>(); currentSubProofs .add(currentTerms[0].equals(currentLeftTerm) ? currentAnnotatedNode .getPremise1() : TransformedZ3Proof .createReflexivityProof(currentLeftTerm)); currentSubProofs.add(currentAnnotatedNode.getPremise2()); currentSubProofs .add(currentTerms[3].equals(currentRightTerm) ? currentAnnotatedNode .getPremise3() : TransformedZ3Proof .createReflexivityProof(currentRightTerm)); newTransitivityProofNode = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(currentSubProofs); } TransformedZ3Proof[] proofsForCurrentTerms = createProofForCurrentTerms( oldTerms[0], currentLeftTerm, oldTerms[oldTerms.length - 1], currentRightTerm, newTransitivityProofNode, currentAnnotatedNode); assert (proofsForCurrentTerms.length == 3); proofs.add(proofsForCurrentTerms); } // create local monotonicity proofs List<TransformedZ3Proof> proofs1 = new ArrayList<TransformedZ3Proof>( proofs.size()); List<TransformedZ3Proof> proofs2 = new ArrayList<TransformedZ3Proof>( proofs.size()); List<TransformedZ3Proof> proofs3 = new ArrayList<TransformedZ3Proof>( proofs.size()); for (TransformedZ3Proof[] currentProofs : proofs) { assert (currentProofs.length == 3); proofs1.add(currentProofs[0]); proofs2.add(currentProofs[1]); proofs3.add(currentProofs[2]); } TransformedZ3Proof proof1 = null; TransformedZ3Proof proof2 = null; TransformedZ3Proof proof3 = null; if (consequentEq.getTerms().get(0) instanceof UninterpretedFunctionInstance) { UninterpretedFunction function = Util .getUninterpretedFunctionOrPredicate(consequentLiteral); proof1 = TransformedZ3Proof.createMonotonicityProof(proofs1, function); proof2 = TransformedZ3Proof.createMonotonicityProof(proofs2, function); proof3 = TransformedZ3Proof.createMonotonicityProof(proofs3, function); } else { Formula leftFormula = null; if (consequentEq.getTerms().get(0) instanceof FormulaTerm) leftFormula = ((FormulaTerm) consequentEq.getTerms().get(0)) .getFormula(); else { assert (consequentEq.getTerms().get(0) instanceof PropositionalTerm); leftFormula = ((PropositionalTerm) consequentEq.getTerms().get( 0)); } assert (leftFormula != null); Formula rightFormula = null; if (consequentEq.getTerms().get(1) instanceof FormulaTerm) rightFormula = ((FormulaTerm) consequentEq.getTerms().get(1)) .getFormula(); else { assert (consequentEq.getTerms().get(1) instanceof PropositionalTerm); rightFormula = ((PropositionalTerm) consequentEq.getTerms() .get(1)); } assert (rightFormula != null); if (leftFormula instanceof UninterpretedPredicateInstance) { assert (rightFormula instanceof UninterpretedPredicateInstance); UninterpretedFunction function = Util .getUninterpretedFunctionOrPredicate(consequentLiteral); proof1 = TransformedZ3Proof.createMonotonicityProof(proofs1, function); proof2 = TransformedZ3Proof.createMonotonicityProof(proofs2, function); proof3 = TransformedZ3Proof.createMonotonicityProof(proofs3, function); } else { assert (leftFormula instanceof DomainEq); assert (rightFormula instanceof DomainEq); proof1 = TransformedZ3Proof .createMonotonicityProofForEquality(proofs1); proof2 = TransformedZ3Proof .createMonotonicityProofForEquality(proofs2); proof3 = TransformedZ3Proof .createMonotonicityProofForEquality(proofs3); } } assert (proof1 != null); assert (proof2 != null); assert (proof3 != null); AnnotatedProofNode newPremise1 = new AnnotatedProofNode(proof1); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise1); AnnotatedProofNode newPremise2 = new AnnotatedProofNode(proof2); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise2); AnnotatedProofNode newPremise3 = new AnnotatedProofNode(proof3); TransformedZ3Proof.annotatedNodesStack.peekFirst().add(newPremise3); // put things together, add new annotated node TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(leftPartition, rightPartition, this, newPremise1, newPremise2, newPremise3)); } /** * Creates the new proof objects during monotonicity handling * * @param leftTerm * r_k * @param newLeftTerm * r'_k * @param rightTerm * s_k * @param newRightTerm * s'_k * @param newProofNode * proof for r'_k=s'_k, if there was a 3-premise annotated node * as an antecedent. <code>null</code> otherwise. * @param annotatedNode * the annotated antecedent node. * @return */ private TransformedZ3Proof[] createProofForCurrentTerms( DomainTerm leftTerm, DomainTerm newLeftTerm, DomainTerm rightTerm, DomainTerm newRightTerm, TransformedZ3Proof newProofNode, AnnotatedProofNode annotatedNode) { TransformedZ3Proof[] result = new TransformedZ3Proof[3]; result[0] = null; result[1] = null; result[2] = null; // result[0] if (leftTerm.equals(newLeftTerm)) result[0] = TransformedZ3Proof.createReflexivityProof(newLeftTerm); else if (annotatedNode.numPremises() == 3) result[0] = annotatedNode.getPremise1(); else result[0] = annotatedNode.getConsequent(); // result[1] if (newLeftTerm.equals(newRightTerm)) { result[1] = TransformedZ3Proof.createReflexivityProof(newLeftTerm); } else if (annotatedNode.numPremises() == 3) { assert (newProofNode != null); result[1] = newProofNode; } else { assert (annotatedNode.numPremises() == 0); if (leftTerm.equals(newLeftTerm)) { if (!rightTerm.equals(newRightTerm)) assert (false); result[1] = annotatedNode.getConsequent(); } else { assert (!rightTerm.equals(newRightTerm)); result[1] = TransformedZ3Proof .createSymmetryProof(annotatedNode.getConsequent()); } } // result[2] if (rightTerm.equals(newRightTerm)) result[2] = TransformedZ3Proof.createReflexivityProof(newRightTerm); else if (annotatedNode.numPremises() == 3) result[2] = annotatedNode.getPremise3(); else result[2] = annotatedNode.getConsequent(); return result; } /** * Computes the right term s'_k during monotonicity handling. * * @param rightPartition * @param currentAnnotatedNode * @return */ private DomainTerm computeCurrentRightTermForMonotonicity( int rightPartition, AnnotatedProofNode currentAnnotatedNode) { if (currentAnnotatedNode.getRightPartition() != rightPartition) { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getConsequent().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(1) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(1); } else if (currentAnnotatedNode.numPremises() == 3) { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getPremise3().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(0) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(0); } else { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getConsequent().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(0) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(0); } } /** * Computes the left term r'_k during monotonicity handling. * * @param leftPartition * @param currentAnnotatedNode * @return */ private DomainTerm computeCurrentLeftTermForMonotonicity(int leftPartition, AnnotatedProofNode currentAnnotatedNode) { if (currentAnnotatedNode.getLeftPartition() != leftPartition) { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getConsequent().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(0) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(0); } else if (currentAnnotatedNode.numPremises() == 3) { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getPremise1().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(1) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(1); } else { Formula formula = Util.getSingleLiteral(currentAnnotatedNode .getConsequent().consequent); assert (formula instanceof DomainEq); DomainEq eqFormula = (DomainEq) formula; assert (eqFormula.getTerms().size() == 2); assert (eqFormula.getTerms().get(1) instanceof DomainTerm); return (DomainTerm) eqFormula.getTerms().get(1); } } /** * Converts a modus ponens node into something usable. Also, converts the * necessary parents into local (transformed) proofs. * * @param z3Proof * a modus ponens node * @return the conversion result */ private static TransformedZ3Proof convertModusPonens(Z3Proof z3Proof) { assert (z3Proof.proofType.equals(SExpressionConstants.MODUS_PONENS)); assert (z3Proof.getSubProofs().size() == 2); assert (z3Proof.hasSingleLiteralConsequent()); Z3Proof child1 = z3Proof.getSubProofs().get(0); // Simple case of flipped equality or disequality if (Util.checkForFlippedEqualityOrDisequality(z3Proof.consequent, child1.consequent)) { TransformedZ3Proof transformedChild1 = TransformedZ3Proof .convertToTransformedZ3Proof(child1); List<TransformedZ3Proof> subProofs = new ArrayList<TransformedZ3Proof>( 1); subProofs.add(transformedChild1); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.SYMMETRY, subProofs, z3Proof.consequent); // assert (z3Proof.getHypotheses().size() == result.getHypotheses() // .size()); // DEBUG return result; } boolean chainOverEqualityAsPredicate = false; // Case of a transitivity chain if (Util.makeLiteralPositive(Util.getSingleLiteral(z3Proof.consequent)) instanceof DomainEq) { TransitivityChainBuilder chainBuilder = new TransitivityChainBuilder( z3Proof); Set<Z3Proof> children = new HashSet<Z3Proof>(); for (Z3Proof child : z3Proof.subProofs) Util.getModusPonensNonIffChilds(child, children); Set<TransformedZ3Proof> transformedChildren = new HashSet<TransformedZ3Proof>(); for (Z3Proof child : children) { // Recursive call for child TransformedZ3Proof transformedChild = TransformedZ3Proof .convertToTransformedZ3Proof(child); transformedChildren.add(transformedChild); chainBuilder.addProofNode(transformedChild); } List<TransformedZ3Proof> proofList = chainBuilder.getChain(); if (proofList != null) { TransformedZ3Proof transProof = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(proofList); assert (z3Proof.consequent.transformToConsequentsForm() .equals(transProof.consequent .transformToConsequentsForm())); // If we have three subproofs, we need to split them, // because conversion to local proof cannot deal with // three subproofs. // NOTE: This should (meanwhile) have been implemented in // createTransititivityProof directly! // Thus, the code following this assert is commented out. assert (transProof.subProofs.size() <= 2); // if (subProofs.size() == 3) { // assert (this.proofType == SExpressionConstants.TRANSITIVITY); // Z3Proof intermediate = Z3Proof // .createTransitivityProof(new ArrayList<Z3Proof>( // subProofs.subList(0, 2))); // Z3Proof rest = subProofs.get(2); // subProofs.clear(); // subProofs.add(intermediate); // subProofs.add(rest); // } return transProof; } else { // Could not create a "normal" transitivity chain // Try viewing equality as a predicate instead, and // construct a chain to convert to resolution. chainOverEqualityAsPredicate = true; } } if (Util.makeLiteralPositive(Util.getSingleLiteral(z3Proof.consequent)) instanceof UninterpretedPredicateInstance || chainOverEqualityAsPredicate) { assert (Util .makeLiteralPositive(Util .getSingleLiteral(z3Proof.subProofs.get(0) .getConsequent())) instanceof UninterpretedPredicateInstance || chainOverEqualityAsPredicate); assert (!chainOverEqualityAsPredicate || Util .makeLiteralPositive(Util .getSingleLiteral(z3Proof.subProofs.get(0) .getConsequent())) instanceof EqualityFormula); boolean predicatePolarity = Util .isAtom(Util.getSingleLiteral(z3Proof.subProofs.get(0) .getConsequent())); assert (predicatePolarity || Util .isNegativeLiteral(Util.getSingleLiteral(z3Proof.subProofs .get(0).getConsequent()))); // Collect relevant parents Set<Z3Proof> leafs = new HashSet<Z3Proof>(); Set<Z3Proof> iffsComingFromDomainEq = new HashSet<Z3Proof>(); assert (z3Proof.subProofs.size() == 2); for (Z3Proof child : z3Proof.subProofs) { Util.getModusPonensIffLeafs(child, leafs); if (!(Util.makeLiteralPositive(Util .getSingleLiteral(child.consequent)) instanceof PropositionalEq)) continue; Util.getModusPonensIffChildsComingFromDomainEq(child, iffsComingFromDomainEq); } Set<TransformedZ3Proof> relevantChildren = new HashSet<TransformedZ3Proof>(); for (Z3Proof child : leafs) { TransformedZ3Proof transformedChild = TransformedZ3Proof .convertToTransformedZ3Proof(child); relevantChildren.add(transformedChild); } for (Z3Proof child : iffsComingFromDomainEq) { TransformedZ3Proof transformedChild = TransformedZ3Proof .convertToTransformedZ3Proof(child); relevantChildren.add(transformedChild); } // Build transitivity chain for the propositional equalities Term startTerm = FormulaTerm .create(Util.makeLiteralPositive(Util .getSingleLiteral(z3Proof.subProofs.get(0) .getConsequent()))); Term endTerm = FormulaTerm.create(Util.makeLiteralPositive(Util .getSingleLiteral(z3Proof.consequent))); TransitivityChainBuilder chainBuilder = new TransitivityChainBuilder( startTerm, endTerm); for (TransformedZ3Proof child : relevantChildren) { chainBuilder.addProofNode(child); } TransformedZ3Proof resolutionChild = chainBuilder .convertToResolutionChain(predicatePolarity); assert (resolutionChild != null); List<TransformedZ3Proof> subProofs = new ArrayList<TransformedZ3Proof>( 2); subProofs.add(TransformedZ3Proof .convertToTransformedZ3Proof(child1)); subProofs.add(resolutionChild); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.UNIT_RESOLUTION, subProofs, z3Proof.consequent); assert (result.checkZ3ProofNode()); // DEBUG return result; } // If we reach here, this modus ponens node as an unforeseen structure // that we cannot handle. throw new RuntimeException("Unable to handle modus ponens node " + z3Proof.id); } /** * Deals with transforming a transitivity node to a local proof. */ private void handleTransitivity() { // At this point, there should be only simple (2 subproof) // transitivities. 3-subproof transitivities may be added later (by // conversion from annotated nodes). Our transformation rules cannot // deal with 3-subproof transitivities at the moment, thus DO NOT REMOVE // THE FOLLOWING ASSERT! assert (subProofs.size() == 2); AnnotatedProofNode firstAnnotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst().getNodeWithConsequent(subProofs.get(0).consequent, subProofs.get(0).getHypothesisFormulas(), new HashSet<Formula>()); assert (firstAnnotatedNode != null); AnnotatedProofNode secondAnnotatedNode = TransformedZ3Proof.annotatedNodesStack .peekFirst().getNodeWithConsequent(subProofs.get(1).consequent, subProofs.get(1).getHypothesisFormulas(), new HashSet<Formula>()); assert (secondAnnotatedNode != null); if (firstAnnotatedNode.numPremises() == 0 && secondAnnotatedNode.numPremises() == 0) { if (firstAnnotatedNode.getPartition() == secondAnnotatedNode .getPartition()) handleTransitivityCase1(); else handleTransitivityCase2(firstAnnotatedNode, secondAnnotatedNode); } else if (firstAnnotatedNode.numPremises() == 3 && secondAnnotatedNode.numPremises() == 0) { if (firstAnnotatedNode.getRightPartition() == secondAnnotatedNode .getPartition()) handleTransitivityCase3(firstAnnotatedNode, secondAnnotatedNode); else handleTransitivityCase5(firstAnnotatedNode, secondAnnotatedNode); } else if (firstAnnotatedNode.numPremises() == 0 && secondAnnotatedNode.numPremises() == 3) { if (firstAnnotatedNode.getLeftPartition() == secondAnnotatedNode .getLeftPartition()) handleTransitivityCase4(firstAnnotatedNode, secondAnnotatedNode); else handleTransitivityCase6(firstAnnotatedNode, secondAnnotatedNode); } else if (firstAnnotatedNode.numPremises() == 3 && secondAnnotatedNode.numPremises() == 3) { handleTransitivityCase7(firstAnnotatedNode, secondAnnotatedNode); } else assert (false); } /** * Deals with the case that both equalities are from the same partition and * have annotated nodes with 0 premises. * */ private void handleTransitivityCase1() { TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(this)); } /** * Deals with the case that both equalities have annotated nodes with 0 * premises, but from different partitions. * */ private void handleTransitivityCase2( AnnotatedProofNode firstAnnotatedProofNode, AnnotatedProofNode secondAnnotatedProofNode) { assert (Util .makeLiteralPositive(Util.getSingleLiteral(subProofs.get(0).consequent)) instanceof EqualityFormula); EqualityFormula formula = (EqualityFormula) Util .makeLiteralPositive(Util.getSingleLiteral(subProofs.get(0).consequent)); assert (formula.getTerms().size() == 2); Term term = formula.getTerms().get(1); assert (Util .makeLiteralPositive(Util.getSingleLiteral(subProofs.get(1).consequent)) instanceof EqualityFormula); EqualityFormula formula2 = (EqualityFormula) Util .makeLiteralPositive(Util.getSingleLiteral(subProofs.get(1).consequent)); assert (formula2.getTerms().size() == 2); Term term2 = formula2.getTerms().get(0); assert (term.equals(term2)); TransformedZ3Proof reflexivity = TransformedZ3Proof .createReflexivityProof(term); AnnotatedProofNode annotatedReflexivity = new AnnotatedProofNode( reflexivity); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( annotatedReflexivity); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedProofNode.getPartition(), secondAnnotatedProofNode.getPartition(), this, firstAnnotatedProofNode, annotatedReflexivity, secondAnnotatedProofNode)); } /** * Deals with the case that the first equalities has an annotated nodes with * 3 premises, the second one has an annotated node with 0 premises, and the * partition of the second node equals the right partition of the first * node. * * @param firstAnnotatedNode * @param secondAnnotatedNode * */ private void handleTransitivityCase3(AnnotatedProofNode firstAnnotatedNode, AnnotatedProofNode secondAnnotatedNode) { List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>( 3); newSubProofs.add(firstAnnotatedNode.getPremise3()); newSubProofs.add(secondAnnotatedNode.getConsequent()); TransformedZ3Proof newProofNode = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs); newProofNode.setHasBeenMadeLocal(); AnnotatedProofNode newAnnotatedNode = new AnnotatedProofNode( newProofNode); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(newAnnotatedNode); // assert (newProofNode.checkZ3ProofNode()); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedNode.getLeftPartition(), firstAnnotatedNode.getRightPartition(), this, firstAnnotatedNode.getAnnotatedPremise1(), firstAnnotatedNode.getAnnotatedPremise2(), newAnnotatedNode)); } /** * Deals with the case that the first equalities has an annotated nodes with * 0 premises, the second one has an annotated node with 3 premises, and the * partition of the first node equals the left partition of the second node. * * @param firstAnnotatedNode * @param secondAnnotatedNode * */ private void handleTransitivityCase4(AnnotatedProofNode firstAnnotatedNode, AnnotatedProofNode secondAnnotatedNode) { List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>(); newSubProofs.add(firstAnnotatedNode.getConsequent()); newSubProofs.add(secondAnnotatedNode.getPremise1()); TransformedZ3Proof newProofNode = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs); newProofNode.setHasBeenMadeLocal(); AnnotatedProofNode newAnnotatedNode = new AnnotatedProofNode( newProofNode); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(newAnnotatedNode); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedNode.getPartition(), secondAnnotatedNode.getRightPartition(), this, newAnnotatedNode, secondAnnotatedNode .getAnnotatedPremise2(), secondAnnotatedNode .getAnnotatedPremise3())); } /** * Deals with the case that the first equalities has an annotated nodes with * 3 premises, the second one has an annotated node with 0 premises, and the * right partition of the first node is different from the partition of the * second node. * * @param firstAnnotatedNode * @param secondAnnotatedNode * */ private void handleTransitivityCase5(AnnotatedProofNode firstAnnotatedNode, AnnotatedProofNode secondAnnotatedNode) { List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>(); newSubProofs.add(firstAnnotatedNode.getPremise2()); newSubProofs.add(firstAnnotatedNode.getPremise3()); TransformedZ3Proof newProofNode = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs); newProofNode.setHasBeenMadeLocal(); AnnotatedProofNode newAnnotatedNode = new AnnotatedProofNode( newProofNode); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(newAnnotatedNode); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedNode.getLeftPartition(), secondAnnotatedNode.getPartition(), this, firstAnnotatedNode.getAnnotatedPremise1(), newAnnotatedNode, secondAnnotatedNode)); } /** * Deals with the case that the first equalities has an annotated nodes with * 0 premises, the second one has an annotated node with 3 premises, and the * partition of the first node is different from the left partition of the * second node. * * @param firstAnnotatedNode * @param secondAnnotatedNode * */ private void handleTransitivityCase6(AnnotatedProofNode firstAnnotatedNode, AnnotatedProofNode secondAnnotatedNode) { List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>(); newSubProofs.add(secondAnnotatedNode.getPremise1()); newSubProofs.add(secondAnnotatedNode.getPremise2()); TransformedZ3Proof newProofNode = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs); newProofNode.setHasBeenMadeLocal(); AnnotatedProofNode newAnnotatedNode = new AnnotatedProofNode( newProofNode); TransformedZ3Proof.annotatedNodesStack.peekFirst() .add(newAnnotatedNode); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedNode.getPartition(), secondAnnotatedNode.getRightPartition(), this, firstAnnotatedNode, newAnnotatedNode, secondAnnotatedNode.getAnnotatedPremise3())); } /** * Deals with the case that both annotated nodes have 3 premises. * * @param firstAnnotatedNode * @param secondAnnotatedNode */ private void handleTransitivityCase7(AnnotatedProofNode firstAnnotatedNode, AnnotatedProofNode secondAnnotatedNode) { List<TransformedZ3Proof> newSubProofs1 = new ArrayList<TransformedZ3Proof>(); newSubProofs1.add(firstAnnotatedNode.getPremise3()); newSubProofs1.add(secondAnnotatedNode.getPremise1()); TransformedZ3Proof newProofNode1 = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs1); newProofNode1.setHasBeenMadeLocal(); List<TransformedZ3Proof> newSubProofs2 = new ArrayList<TransformedZ3Proof>(); newSubProofs2.add(firstAnnotatedNode.getPremise2()); newSubProofs2.add(newProofNode1); newSubProofs2.add(secondAnnotatedNode.getPremise2()); TransformedZ3Proof newProofNode2 = TransformedZ3Proof .createTransitivityProofForTransformedZ3Proofs(newSubProofs2); newProofNode2.setHasBeenMadeLocal(); AnnotatedProofNode newAnnotatedNode1 = new AnnotatedProofNode( newProofNode1); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( newAnnotatedNode1); AnnotatedProofNode newAnnotatedNode2 = new AnnotatedProofNode( newProofNode2); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( newAnnotatedNode2); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(firstAnnotatedNode.getLeftPartition(), secondAnnotatedNode.getRightPartition(), this, firstAnnotatedNode.getAnnotatedPremise1(), newAnnotatedNode2, secondAnnotatedNode .getAnnotatedPremise3())); } /** * * @return A list of all leafs of this proof. */ @Deprecated public List<TransformedZ3Proof> getLeafs() { long operationId = DagOperationManager.startDAGOperation(); List<TransformedZ3Proof> result = this.getLeafsRecursion(operationId); DagOperationManager.endDAGOperation(operationId); return result; } @Deprecated private List<TransformedZ3Proof> getLeafsRecursion(long operationId) { visitedByDAGOperation(operationId); List<TransformedZ3Proof> result = new LinkedList<TransformedZ3Proof>(); for (Z3Proof child : subProofs) { if (!(child instanceof TransformedZ3Proof)) throw new RuntimeException( "Base class z3Proof appears in tree of derived class TransformedZ3Proof. This should not happen!"); TransformedZ3Proof subProof = (TransformedZ3Proof) child; if (subProof.isLeaf()) result.add(subProof); else if (!subProof.wasVisitedByDAGOperation(operationId)) result.addAll(subProof.getLeafsRecursion(operationId)); } return result; } /** * Creates a symmetry proof for the given premise. * * @param premise * must have a single literal as a consequence * @return a symmetry proof for the given premise. */ public static TransformedZ3Proof createSymmetryProof( TransformedZ3Proof premise) { Z3Proof z3Proof = Z3Proof.createSymmetryProof(premise); List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>( 3); for (Z3Proof subProof : z3Proof.subProofs) { assert (subProof instanceof TransformedZ3Proof); newSubProofs.add((TransformedZ3Proof) subProof); } TransformedZ3Proof result = new TransformedZ3Proof(z3Proof.proofType, newSubProofs, z3Proof.consequent); return result; } /** * Creates a reflexivity proof for the given term. * * @param term * @return a reflexivity proof for the given term. */ public static TransformedZ3Proof createReflexivityProof(Term term) { List<Term> terms = new ArrayList<Term>(); terms.add(term); terms.add(term); Formula formula = null; try { formula = EqualityFormula.create(terms, true); } catch (IncomparableTermsException exc) { throw new RuntimeException( "Incomparable terms while creating reflexivity proof.", exc); } TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.ASSERTED, new ArrayList<TransformedZ3Proof>(), formula); result.axiom = true; // Reflexivity is axiomatically true. So it's safe to add // it at any time. if (TransformedZ3Proof.annotatedNodesStack.size() == 0) TransformedZ3Proof.annotatedNodesStack .addFirst(new AnnotatedProofNodes()); TransformedZ3Proof.annotatedNodesStack.peekFirst().add( new AnnotatedProofNode(result)); return result; } /** * Creates a transitivity proof for the given list of subproofs. The list * must have exactly two or three elements, which match a transitivity * premise of the form [(a=b), (b=c)] or [(a=b), (b=c), (c=d)]. * * @param subProofs * the subproofs * @return a transitivity proof for the given term. */ public static TransformedZ3Proof createTransitivityProofForTransformedZ3Proofs( List<TransformedZ3Proof> subProofs) { subProofs = new ArrayList<TransformedZ3Proof>(subProofs); Z3Proof z3Proof = Z3Proof.createTransitivityProof(subProofs); List<TransformedZ3Proof> newSubProofs = new ArrayList<TransformedZ3Proof>( 2); for (Z3Proof subProof : z3Proof.subProofs) { assert (subProof instanceof TransformedZ3Proof); newSubProofs.add((TransformedZ3Proof) subProof); } return new TransformedZ3Proof(z3Proof.proofType, newSubProofs, z3Proof.consequent); } /** * Creates a monotonicity proof. * * @param subProofs * the equality proofs for the arguments * @param function * the function which should be applied on the arguments * @return a monotonicity proof for the given parameters. */ public static TransformedZ3Proof createMonotonicityProof( List<TransformedZ3Proof> subProofs, UninterpretedFunction function) { List<DomainTerm> leftParams = new ArrayList<DomainTerm>(); List<DomainTerm> rightParams = new ArrayList<DomainTerm>(); for (Z3Proof subProof : subProofs) { Formula literal = Util.getSingleLiteral(subProof.consequent); assert (literal instanceof DomainEq); DomainEq eqLiteral = (DomainEq) literal; assert (eqLiteral.getTerms().size() == 2); assert (eqLiteral.getTerms().get(0) instanceof DomainTerm); assert (eqLiteral.getTerms().get(1) instanceof DomainTerm); leftParams.add((DomainTerm) eqLiteral.getTerms().get(0)); rightParams.add((DomainTerm) eqLiteral.getTerms().get(1)); } EqualityFormula consequent = null; if (function.getType().equals(SExpressionConstants.VALUE_TYPE)) { try { UninterpretedFunctionInstance leftInstance = UninterpretedFunctionInstance .create(function, leftParams); UninterpretedFunctionInstance rightInstance = UninterpretedFunctionInstance .create(function, rightParams); List<DomainTerm> functionInstances = new ArrayList<DomainTerm>(); functionInstances.add(leftInstance); functionInstances.add(rightInstance); consequent = EqualityFormula.create(functionInstances, true); } catch (WrongNumberOfParametersException exc) { throw new RuntimeException( "Wrong number of function parameters while creating monotonicity proof.", exc); } catch (WrongFunctionTypeException exc) { throw new RuntimeException( "Wrong function type while creating monotonicity proof.", exc); } catch (IncomparableTermsException exc) { throw new RuntimeException( "Incomparable terms while creating monotonicity proof.", exc); } } else { assert (function.getType().equals(SExpressionConstants.BOOL_TYPE)); try { UninterpretedPredicateInstance leftInstance = UninterpretedPredicateInstance .create(function, leftParams); UninterpretedPredicateInstance rightInstance = UninterpretedPredicateInstance .create(function, rightParams); List<UninterpretedPredicateInstance> functionInstances = new ArrayList<UninterpretedPredicateInstance>(); functionInstances.add(leftInstance); functionInstances.add(rightInstance); consequent = EqualityFormula.create(functionInstances, true); } catch (WrongNumberOfParametersException exc) { throw new RuntimeException( "Wrong number of function parameters while creating monotonicity proof.", exc); } catch (WrongFunctionTypeException exc) { throw new RuntimeException( "Wrong function type while creating monotonicity proof.", exc); } catch (IncomparableTermsException exc) { throw new RuntimeException( "Incomparable terms while creating monotonicity proof.", exc); } } TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.MONOTONICITY, subProofs, consequent); return result; } /** * Creates a monotonicity proof with equality as the "function". * * @param subProofs * the equality proofs for the arguments * @return a monotonicity proof for the given parameters. */ public static TransformedZ3Proof createMonotonicityProofForEquality( List<TransformedZ3Proof> subProofs) { if (subProofs.size() != 2) assert (false); assert (Util.getSingleLiteral(subProofs.get(0).consequent) instanceof DomainEq); assert (Util.getSingleLiteral(subProofs.get(1).consequent) instanceof DomainEq); List<DomainTerm> leftParams = new ArrayList<DomainTerm>(2); List<DomainTerm> rightParams = new ArrayList<DomainTerm>(2); for (TransformedZ3Proof subProof : subProofs) { Formula literal = Util.getSingleLiteral(subProof.consequent); assert (literal instanceof DomainEq); DomainEq eqLiteral = (DomainEq) literal; assert (eqLiteral.getTerms().size() == 2); assert (eqLiteral.getTerms().get(0) instanceof DomainTerm); assert (eqLiteral.getTerms().get(1) instanceof DomainTerm); leftParams.add((DomainTerm) eqLiteral.getTerms().get(0)); rightParams.add((DomainTerm) eqLiteral.getTerms().get(1)); } assert (leftParams.size() == 2); assert (rightParams.size() == 2); DomainEq leftEq; DomainEq rightEq; try { leftEq = (DomainEq) EqualityFormula.create(leftParams, true); rightEq = (DomainEq) EqualityFormula.create(rightParams, true); } catch (IncomparableTermsException exc) { throw new RuntimeException("Incomparable terms!", exc); } List<PropositionalTerm> propTerms = new ArrayList<PropositionalTerm>(2); propTerms.add(FormulaTerm.create(leftEq)); propTerms.add(FormulaTerm.create(rightEq)); Formula consequent = PropositionalEq.create(propTerms, true); TransformedZ3Proof result = new TransformedZ3Proof( SExpressionConstants.MONOTONICITY, subProofs, consequent); return result; } /** * @return the <code>literal</code> */ public Formula getLiteral() { return literal; } /** * @return if is an axiom. */ public boolean isAxiom() { return this.axiom; } /** * @return the <code>consequent</code> */ @Override public Formula getConsequent() { return consequent; } /** * * @see at.iaik.suraq.smtlib.Z3Proof#isHypothesis() */ @Override public boolean isHypothesis() { return hypothesis || proofType.equals(SExpressionConstants.HYPOTHESIS); } /** * Computes the set of hypotheses on which this proof is based. Also, the * proof is on-the-fly restructured so that it has no more hypotheses. * * @return The set of hypotheses that were removed from the proof. */ private Set<Formula> removeHypotheses() { Set<Formula> result = new HashSet<Formula>(); List<Z3Proof> hypotheses = new ArrayList<Z3Proof>(this.getHypotheses()); Collections.sort(hypotheses); for (Z3Proof hypothesis : hypotheses) { assert (Util.isLiteral(hypothesis.consequent)); result.add(hypothesis.consequent); // update the DAG with the negated literal Set<Z3Proof> nodes = nodesOnPathToHypothesisFormula(hypothesis.consequent); Formula negatedLiteral = Util.invertLiteral(hypothesis.consequent); for (Z3Proof z3ProofNode : nodes) { // update the node. assert (z3ProofNode instanceof TransformedZ3Proof); TransformedZ3Proof node = (TransformedZ3Proof) z3ProofNode; assert (!node.isHypothesis()); assert (node.consequent instanceof OrFormula); if (((OrFormula) node.consequent).getDisjuncts().contains( negatedLiteral)) // Literal is already present. No need to add it. continue; assert (node.subProofs.size() == 2); List<Formula> newDisjuncts = ((OrFormula) node.consequent) .getDisjuncts(); assert (!newDisjuncts.contains(negatedLiteral)); newDisjuncts.remove(PropositionalConstant.create(false)); newDisjuncts.add(negatedLiteral); node.consequent = (OrFormula.generate(newDisjuncts)) .transformToConsequentsForm(); Z3Proof foundHypothesis = Util.findHypothesisInSubproofs(node, hypothesis); if (foundHypothesis != null) { Z3Proof other = node.subProofs.get(node.subProofs .indexOf(foundHypothesis) == 0 ? 1 : 0); assert (other instanceof TransformedZ3Proof); TransformedZ3Proof otherChild = (TransformedZ3Proof) other; node.takeValuesFrom(otherChild); // Consequent must be overwritten with new value containing // the hypothesis node.consequent = (OrFormula.generate(newDisjuncts)) .transformToConsequentsForm(); } } } return result; } /** * Transforms a proof with transitivity, monotonicity, resolution and * symmetry into a pure resolution proof * * @param operationId * TODO */ public void toResolutionProof() { long operationId = DagOperationManager.startDAGOperation(); this.toResolutionProofRecursion(operationId); DagOperationManager.endDAGOperation(operationId); } private void toResolutionProofRecursion(long operationId) { if (this.wasVisitedByDAGOperation(operationId)) return; this.visitedByDAGOperation(operationId); if (this.proofType.equals(SExpressionConstants.LEMMA)) { assert (this.subProofs.size() == 1); assert (this.subProofs.get(0) instanceof TransformedZ3Proof); TransformedZ3Proof hypotheticalProof = (TransformedZ3Proof) this.subProofs .get(0); assert (hypotheticalProof.consequent.equals((PropositionalConstant .create(false)).transformToConsequentsForm()) || Util .equalClauses(this.consequent, hypotheticalProof.consequent)); if (hypotheticalProof.consequent.equals(PropositionalConstant .create(false).transformToConsequentsForm())) { hypotheticalProof.toResolutionProofRecursion(operationId); hypotheticalProof.removeHypotheses(); } else { assert (Util.equalClauses(this.consequent, hypotheticalProof.consequent)); } this.takeValuesFrom(hypotheticalProof); if (!(this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION) || this.proofType .equals(SExpressionConstants.ASSERTED))) assert (false); assert (!this.hypothesis); return; } if (this.proofType.equals(SExpressionConstants.MONOTONICITY) || this.proofType.equals(SExpressionConstants.TRANSITIVITY)) { if (this.subProofs.size() < 1) throw new RuntimeException( "Monotonicity/Transitivity proof with less than one child. This should not happen!"); Set<Z3Proof> redundantSubproofs = new HashSet<Z3Proof>(); for (Z3Proof subProof : this.subProofs) { if (Util.isReflexivity(subProof.consequent)) redundantSubproofs.add(subProof); } subProofs.removeAll(redundantSubproofs); List<Formula> axiomParts = new ArrayList<Formula>(); for (Z3Proof subProof : this.subProofs) { axiomParts.add((NotFormula.create(subProof.consequent))); } axiomParts.add(this.consequent); OrFormula axiomFormula = (OrFormula.generate(axiomParts)) .transformToConsequentsForm(); Z3Proof remainingAxiom = new TransformedZ3Proof( SExpressionConstants.ASSERTED, new ArrayList<TransformedZ3Proof>(), null, axiomFormula.transformToConsequentsForm()); for (int count = 0; count < this.subProofs.size() - 1; count++) { TransformedZ3Proof currentEquality = (TransformedZ3Proof) this.subProofs .get(count); currentEquality.toResolutionProofRecursion(operationId); Formula literal = Util.getSingleLiteral(axiomParts.remove(0) .transformToConsequentsForm()); assert (Util.isLiteral(literal)); remainingAxiom = new TransformedZ3Proof( SExpressionConstants.UNIT_RESOLUTION, currentEquality, remainingAxiom, literal.transformToConsequentsForm(), (OrFormula.generate(axiomParts)) .transformToConsequentsForm()); } TransformedZ3Proof currentEquality = (TransformedZ3Proof) this.subProofs .get(this.subProofs.size() - 1); currentEquality.toResolutionProofRecursion(operationId); this.subProofs = new ArrayList<Z3Proof>(); this.subProofs.add(currentEquality); this.subProofs.add(remainingAxiom); this.literal = Util.getSingleLiteral(currentEquality .getConsequent().transformToConsequentsForm()); this.proofType = SExpressionConstants.UNIT_RESOLUTION; return; } else if (proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) { for (Z3Proof child : subProofs) { assert (child instanceof TransformedZ3Proof); ((TransformedZ3Proof) child) .toResolutionProofRecursion(operationId); } return; } else if (proofType.equals(SExpressionConstants.SYMMETRY)) { // Ignore symmetry. a=b and b=a should be treated as the // same object by later steps anyway. // NOTE (GH): Not sure if this is actually a correct assumption. if (this.subProofs.size() != 1) throw new RuntimeException( "Symmetry proof with not exactly one child. This should not happen!"); TransformedZ3Proof z3SubProof = (TransformedZ3Proof) this.subProofs .get(0); z3SubProof.toResolutionProofRecursion(operationId); this.takeValuesFrom(z3SubProof); return; } else if (proofType.equals(SExpressionConstants.ASSERTED) || this.isHypothesis()) { assert (this.consequent instanceof OrFormula); // Too expensive? // assert (this.isLocal()); assert (this.subProofs.size() == 0); return; } else { throw new RuntimeException("Encountered unexpected proof rule " + proofType.toString() + " while trying to rewrite z3 proof."); } } /** * Converts this proof into an s-expression compatible with SMTLIBv2. Only * the proof itself is converted. No variable/function/macro declarations * are included. * * @return this proof as an SMTLIBv2 s-expression. */ @Override public SExpression toSmtlibV2() { List<SExpression> children = new ArrayList<SExpression>(); if (this.proofType == SExpressionConstants.UNIT_RESOLUTION) { if (this.literal != null) { String child = this.proofType + "{" + this.literal.toString().replaceAll("\n", "") .replaceAll("\\s{2,}", " ") + "}"; if (this.assertPartition != 0) child += "(p:" + this.assertPartition + ")"; children.add(Token.generate(child)); } else throw new RuntimeException( "resolution proof always needs a literal."); } else children.add(this.proofType); for (Z3Proof subProof : this.subProofs) children.add(subProof.toSmtlibV2()); children.add(this.consequent.toSmtlibV2()); return new SExpression(children); } /** * * @return <code>true</code> if this is a local proof, <code>false</code> if * it contains bad literals. */ public boolean isLocal() { long operationId = DagOperationManager.startDAGOperation(); boolean result = this.isLocalRecursion(operationId); DagOperationManager.endDAGOperation(operationId); return result; } private boolean isLocalRecursion(long operationId) { if (this.wasVisitedByDAGOperation(operationId)) return true; visitedByDAGOperation(operationId); if (Util.containsBadLiteral((OrFormula) consequent)) return false; for (Z3Proof child : subProofs) { if (child.wasVisitedByDAGOperation(operationId)) continue; assert (child instanceof TransformedZ3Proof); if (!((TransformedZ3Proof) child).isLocal()) return false; } return true; } public Map<PropositionalVariable, Formula> createITETrees( List<PropositionalVariable> ctrlSignals, Map<PropositionalVariable, Formula> tseitinEncoding) { Map<PropositionalVariable, Formula> trees = new HashMap<PropositionalVariable, Formula>(); // remove local parts of tree this.removeLocalPartsBeforeInterpolation(); System.out.println("After removing local parts:"); System.out.println("Proof DAG size: " + this.size(false)); System.out .println("Proof size after unwinding DAG: " + this.size(true)); // create ITE tree for each signal for (int signalNum = 0; signalNum < ctrlSignals.size(); signalNum++) { PropositionalVariable signal = ctrlSignals.get(signalNum); Formula tree = createITETree(signalNum); // Replace Tseitin variables Map<Token, Term> substitutionsMap = new HashMap<Token, Term>(); for (PropositionalVariable tseitinVar : tseitinEncoding.keySet()) substitutionsMap.put(Token.generate(tseitinVar.getVarName()), FormulaTerm.create(tseitinEncoding.get(tseitinVar))); trees.put(signal, tree.substituteFormula(substitutionsMap, new HashMap<SMTLibObject, SMTLibObject>())); } return trees; } public void removeLocalPartsBeforeInterpolation() { // FIXME Don't unwind the DAG! if (this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) { assert (this.literal != null); Set<Integer> literalPartitions = this.literal .getPartitionsFromSymbols(); assert (literalPartitions.size() == 1 || literalPartitions.size() == 2); literalPartitions.remove(-1); assert (literalPartitions.size() == 0 || literalPartitions.size() == 1); if (literalPartitions.size() == 1) { assert (literalPartitions.iterator().next() > 0); // This is resolution over a local literal. // ==> This node can be converted to ASSERTED // All parents should only resolve on locals as well. // TODO: Check parents! this.subProofs.clear(); this.proofType = SExpressionConstants.ASSERTED; this.literal = null; this.assertPartition = literalPartitions.iterator().next(); } else { // call recursive assert (subProofs.size() == 2); TransformedZ3Proof left = (TransformedZ3Proof) subProofs.get(0); TransformedZ3Proof right = (TransformedZ3Proof) subProofs .get(1); left.removeLocalPartsBeforeInterpolation(); right.removeLocalPartsBeforeInterpolation(); } } else if (this.proofType.equals(SExpressionConstants.ASSERTED)) { return; } else throw new RuntimeException("encountered illegal proof type."); } private Formula createITETree(int signalNum) { // FIXME Don't unwind DAG! if (this.proofType == SExpressionConstants.UNIT_RESOLUTION) { // call recursive Formula leftResult = ((TransformedZ3Proof) subProofs.get(0)) .createITETree(signalNum); Formula rightResult = ((TransformedZ3Proof) subProofs.get(1)) .createITETree(signalNum); if (leftResult instanceof PropositionalConstant && rightResult instanceof PropositionalConstant) { if (leftResult.equals(rightResult)) return leftResult; } // handle result of recursion OrFormula leftConsequent = ((OrFormula) subProofs.get(0) .getConsequent()); OrFormula rightConsequent = ((OrFormula) subProofs.get(1) .getConsequent()); if (checkPresence(leftConsequent, this.literal)) { if (!checkPresence(rightConsequent, this.literal)) { // NOTE: Pudlak's "sel" works exactly opposite to "ite". return PropositionalIte.create(this.literal, rightResult, leftResult); } } else if (!checkPresence(leftConsequent, this.literal)) { if (checkPresence(rightConsequent, this.literal)) { // NOTE: Pudlak's "sel" works exactly opposite to "ite". return PropositionalIte.create(this.literal, leftResult, rightResult); } } else throw new RuntimeException("found invalid unit-resolution."); } else if (this.proofType.equals(SExpressionConstants.ASSERTED)) { int partition = this.assertPartition; if (partition <= 0) { assert (partition == -1 || partition == 0); // this must be a "global clause", coming from an axiom // arbitrarily assign it to the first partition. partition = 1; } assert (partition > 0); BitSet bits = TransformedZ3Proof.bitSetFromLong(partition - 1); boolean isSet = bits.get(signalNum); return PropositionalConstant.create(isSet); } else throw new RuntimeException( "only resolution and asserted proof types allowed here."); return null; } public static BitSet bitSetFromLong(long value) { assert (value >= 0); BitSet bits = new BitSet(); int index = 0; while (value != 0L) { if (value % 2L != 0) { bits.set(index); } ++index; value = value >>> 1; } return bits; } /** * * @return <code>true</code> if this is not a resolution node, * <code>literal</code> is <code>null</code>, or if the exactly 2 * subproofs contain the resolving literal in opposite polarity. */ private boolean checkLiteralOccurenceInSubProofs() { if (!this.proofType.equals(SExpressionConstants.UNIT_RESOLUTION)) return true; if (literal == null) return true; if (this.subProofs.size() != 2) return false; assert (subProofs.get(0).consequent instanceof OrFormula); assert (subProofs.get(1).consequent instanceof OrFormula); OrFormula premise1 = (OrFormula) subProofs.get(0).consequent; OrFormula premise2 = (OrFormula) subProofs.get(0).consequent; Formula negatedLiteral = NotFormula.create(literal); int polarity = 0; for (Formula literalFromPremise1 : premise1.getDisjuncts()) { literalFromPremise1 = Util.getSingleLiteral(literalFromPremise1); if (literal.equals(literalFromPremise1)) { polarity = 1; break; } if (negatedLiteral.equals(literalFromPremise1)) { polarity = -1; break; } } if (polarity == 0) return false; for (Formula literalFromPremise2 : premise2.getDisjuncts()) { literalFromPremise2 = Util.getSingleLiteral(literalFromPremise2); assert (polarity != 0); if (polarity < 0) { if (literal.equals(literalFromPremise2)) return true; } else { assert (polarity > 0); if (negatedLiteral.equals(literalFromPremise2)) return true; } } return false; } private boolean checkPresence(OrFormula haystack, Formula needle) { for (Formula disjunct : haystack.getDisjuncts()) { if (disjunct instanceof NotFormula) {// unwrap Formula tmp = ((NotFormula) disjunct).getNegatedFormula(); if (tmp.equals(needle)) return false; } else { if (disjunct.equals(needle)) return true; } } throw new RuntimeException( "Neither literal nor negated literal found! This should not happen!!"); } /** * @return the <code>numberOfRemovedObsoloteResolutionSteps</code> */ public static long getNumberOfRemovedObsoloteResolutionSteps() { return TransformedZ3Proof.numberOfRemovedObsoloteResolutionSteps; } /** * @return the <code>hasBeenMadeLocal</code> */ public boolean isHasBeenMadeLocal() { return hasBeenMadeLocal; } /** * Sets the <code>hasBeenMadeLocalFlag</code> to <code>true</code>. */ public void setHasBeenMadeLocal() { // Do a quick, sound, but non-complete check // At least, the direct parents must also have the flag set. for (Z3Proof child : subProofs) { assert (child instanceof TransformedZ3Proof); if (!(((TransformedZ3Proof) child).hasBeenMadeLocal)) assert (false); } this.hasBeenMadeLocal = true; } }