/*****************************************************************************/ /*! * \file search_theorem_producer.cpp * \brief Implementation of the proof rules for the simple search engine * * Author: Sergey Berezin * * Created: Mon Feb 24 14:51:51 2003 * *
* * License to use, copy, modify, sell and/or distribute this software * and its documentation for any purpose is hereby granted without * royalty, subject to the terms and conditions defined in the \ref * LICENSE file provided with this distribution. * *
* */ /*****************************************************************************/ #include "search.h" #include "theory_core.h" #include "theorem_manager.h" #include "common_proof_rules.h" #define _CVC3_TRUSTED_ #include "search_theorem_producer.h" using namespace std; using namespace CVC3; ///////////////////////////////////////////////////////////////////////////// // class SearchEngine trusted methods ///////////////////////////////////////////////////////////////////////////// SearchEngineRules* SearchEngine::createRules() { return new SearchEngineTheoremProducer(d_core->getTM()); } SearchEngineTheoremProducer::SearchEngineTheoremProducer(TheoremManager* tm) : TheoremProducer(tm), d_commonRules(tm->getRules()) { } ///////////////////////////////////////////////////////////////////////////// // Proof rules ///////////////////////////////////////////////////////////////////////////// // Proof by contradiction: !A |- FALSE ==> |- A. "!A" doesn't // have to be present in the assumptions. Theorem SearchEngineTheoremProducer::proofByContradiction(const Expr& a, const Theorem& pfFalse) { if(CHECK_PROOFS) CHECK_SOUND(pfFalse.getExpr().isFalse(), "proofByContradiction: pfFalse = : " + pfFalse.toString()); Expr not_a(!a); Assumptions assump(pfFalse.getAssumptionsRef() - not_a); Proof pf; if(withProof()) { // TODO: optimize with 1 traversal? Theorem thm(pfFalse.getAssumptionsRef()[not_a]); Proof u; // proof label for !a if(!thm.isNull()) u = thm.getProof(); // Proof compaction: if u is Null, use "FALSE => A" rule if(u.isNull()) pf = newPf("false_implies_anything", a, pfFalse.getProof()); else pf = newPf("pf_by_contradiction", a, // LAMBDA-abstraction (LAMBDA (u: !a): pfFalse) newPf(u, not_a, pfFalse.getProof())); } return newTheorem(a, assump, pf); } // Similar rule, only negation introduction: // A |- FALSE ==> !A Theorem SearchEngineTheoremProducer::negIntro(const Expr& not_a, const Theorem& pfFalse) { if(CHECK_PROOFS) { CHECK_SOUND(pfFalse.getExpr().isFalse(), "negIntro: pfFalse = : " + pfFalse.toString()); CHECK_SOUND(not_a.isNot(), "negIntro: not_a = "+not_a.toString()); } Expr a(not_a[0]); Assumptions assump(pfFalse.getAssumptionsRef() - a); Proof pf; if(withProof()) { Theorem thm(pfFalse.getAssumptionsRef()[a]); Proof u; // proof label for 'a' if(!thm.isNull()) u = thm.getProof(); // Proof compaction: if u is Null, use "FALSE => !A" rule if(u.isNull()) pf = newPf("false_implies_anything", not_a, pfFalse.getProof()); else pf = newPf("neg_intro", not_a, // LAMBDA-abstraction (LAMBDA (u: a): pfFalse) newPf(u, a, pfFalse.getProof())); } return newTheorem(not_a, assump, pf); } // Case split: u1:A |- C, u2:!A |- C ==> |- C Theorem SearchEngineTheoremProducer::caseSplit(const Expr& a, const Theorem& a_proves_c, const Theorem& not_a_proves_c) { Expr c(a_proves_c.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(c == not_a_proves_c.getExpr(), "caseSplit: conclusions differ:\n positive case C = " + c.toString() + "\n negative case C = " + not_a_proves_c.getExpr().toString()); // The opposite assumption should not appear in the theorems // Actually, this doesn't violate soundness, no reason to check // CHECK_SOUND(a_proves_c.getAssumptions()[!a].isNull(), // "caseSplit: wrong assumption: " + (!a).toString() // +"\n in "+a_proves_c.toString()); // CHECK_SOUND(not_a_proves_c.getAssumptions()[a].isNull(), // "caseSplit: wrong assumption: " + a.toString() // +"\n in "+not_a_proves_c.toString()); } const Assumptions& a1(a_proves_c.getAssumptionsRef()); const Assumptions& a2(not_a_proves_c.getAssumptionsRef()); Assumptions a3 = a1 - a; Assumptions a4 = a2 - !a; // Proof compaction: if either theorem proves C without a or !a, // then just use that theorem. This only works if assumptions are // active. if (a1 == a3) return a_proves_c; if (a2 == a4) return not_a_proves_c; // No easy way out. Do the work. Proof pf; a3.add(a4); if(withProof()) { // Create lambda-abstractions vector pfs; pfs.push_back(newPf(a1[a].getProof(), a, a_proves_c.getProof())); pfs.push_back(newPf(a2[!a].getProof(), !a, not_a_proves_c.getProof())); pf = newPf("case_split", a, c, pfs); } return newTheorem(c, a3, pf); } // Conflict clause rule: // Gamma, A_1,...,A_n |- B ==> Gamma |- (OR B A_1 ... A_n) // The assumptions A_i are given by the vector 'lits'. // If B==FALSE, it is omitted from the result. // NOTE: here "!A_i" means an inverse of A_i, not just a negation. // That is, if A_i is NOT C, then !A_i is C. // verification function used by conflictClause void SearchEngineTheoremProducer::verifyConflict(const Theorem& thm, TheoremMap& m) { const Assumptions& a(thm.getAssumptionsRef()); const Assumptions::iterator iend = a.end(); for (Assumptions::iterator i = a.begin(); i != iend; ++i) { CHECK_SOUND(!i->isNull(), "SearchEngineTheoremProducer::conflictClause: " "Found null theorem"); if (!i->isRefl() && !i->isFlagged()) { i->setFlag(); if (m.count(*i) == 0) { CHECK_SOUND(!i->isAssump(), "SearchEngineTheoremProducer::conflictClause: " "literal and gamma sets do not form a complete " "cut of Theorem assumptions. Stray theorem: \n" +i->toString()); verifyConflict(*i, m); } else { m[*i] = true; } } } } Theorem SearchEngineTheoremProducer:: conflictClause(const Theorem& thm, const vector& lits, const vector& gamma) { // TRACE("search proofs", "conflictClause(", thm.getExpr(), ") {"); IF_DEBUG(if(debugger.trace("search proofs")) { ostream& os = debugger.getOS(); os << "lits = ["; for(vector::const_iterator i=lits.begin(), iend=lits.end(); i!=iend; ++i) os << i->getExpr() << ",\n"; os << "]\n\ngamma = ["; for(vector::const_iterator i=gamma.begin(), iend=gamma.end(); i!=iend; ++i) os << i->getExpr() << ",\n"; os << "]" << endl; }); bool checkProofs(CHECK_PROOFS); // This rule only makes sense when runnnig with assumptions if(checkProofs) { CHECK_SOUND(withAssumptions(), "conflictClause: called while running without assumptions"); } // Assumptions aOrig(thm.getAssumptions()); vector literals; vector u; // Vector of proof labels literals.reserve(lits.size() + 1); u.reserve(lits.size()); const vector::const_iterator iend = lits.end(); for(vector::const_iterator i=lits.begin(); i!=iend; ++i) { Expr neg(i->getExpr().negate()); literals.push_back(neg); if(withProof()) u.push_back(i->getProof()); } if(checkProofs) { TheoremMap m; // TRACE_MSG("search proofs", "adding gamma to m: {"); for(vector::const_iterator i = gamma.begin(); i != gamma.end(); ++i) { // TRACE("search proofs", "m[", *i, "]"); m[*i] = false; } // TRACE_MSG("search proofs", "}"); for(vector::const_iterator i = lits.begin(); i!=iend; ++i) { // TRACE("search proofs", "check lit: ", *i, ""); CHECK_SOUND(m.count(*i) == 0, "SearchEngineTheoremProducer::conflictClause: " "literal and gamma sets are not disjoint: lit = " +i->toString()); m[*i] = false; } thm.clearAllFlags(); verifyConflict(thm, m); TheoremMap::iterator t = m.begin(), tend = m.end(); for (; t != tend; ++t) { CHECK_SOUND(t->second == true, "SearchEngineTheoremProducer::conflictClause: " "literal or gamma set contains extra element : " + t->first.toString()); } } Assumptions a(gamma); if(!thm.getExpr().isFalse()) literals.push_back(thm.getExpr()); Proof pf; if(withProof()) { if(lits.size()>0) { vector assump; // If assumptions are not leaves, we need to create new // variables for them and substitute them for their proofs in // the proof term ExprHashMap subst; DebugAssert(u.size() == lits.size(), ""); for(size_t i=0, iend=lits.size(); i ", res.getExpr(), " }"); return res; } // Theorem // SearchEngineTheoremProducer:: // conflictClause(const Theorem& thm, const vector& lits) { // bool checkProofs(CHECK_PROOFS); // // This rule only makes sense when runnnig with assumptions // if(checkProofs) { // CHECK_SOUND(withAssumptions(), // "conflictClause: called while running without assumptions"); // } // Assumptions aOrig(thm.getAssumptions()); // vector literals; // vector negations; // vector u; // Vector of proof labels // literals.reserve(lits.size() + 1); // negations.reserve(lits.size()); // u.reserve(lits.size()); // for(vector::const_iterator i=lits.begin(), iend=lits.end(); // i!=iend; ++i) { // Expr neg(i->isNot()? (*i)[0] : !(*i)); // if(checkProofs) // CHECK_SOUND(!aOrig[neg].isNull(), // "SearchEngineTheoremProducer::conflictClause: " // "literal is not in the set of assumptions: neg = " // +neg.toString() + "\n Theorem = " + thm.toString()); // literals.push_back(*i); // negations.push_back(neg); // if(withProof()) u.push_back(aOrig[neg].getProof()); // } // Assumptions a = aOrig - negations; // if(!thm.getExpr().isFalse()) // literals.push_back(thm.getExpr()); // Proof pf; // if(withProof()) { // if(lits.size()>0) // pf = newPf("conflict_clause", newPf(u, literals, thm.getProof())); // else // pf = newPf("false_to_empty_or", thm.getProof()); // } // // Use ExprManager in newExpr, since literals may be empty // return newTheorem(Expr(d_em, OR, literals), a, pf); // } // "Cut" rule: { G_i |- A_i }; G', { A_i } |- B ==> union(G_i)+G' |- B. Theorem SearchEngineTheoremProducer:: cutRule(const vector& thmsA, const Theorem& as_prove_b) { if(CHECK_PROOFS) CHECK_SOUND(withAssumptions(), "cutRule called without assumptions activated"); // Optimization: use only those theorems that occur in B's assumptions. // *** No, take it back, it's a mis-optimization. Most of the time, // cutRule is applied when we *know* thmsA are present in the // assumptions of 'as_proof_b'. Proof pf; vector exprs; exprs.reserve(thmsA.size() + 1); const vector::const_iterator iend = thmsA.end(); for(vector::const_iterator i=thmsA.begin(); i!=iend; ++i) { exprs.push_back(i->getExpr()); } Assumptions a(thmsA); // add the As a.add(as_prove_b.getAssumptionsRef() - exprs); // Add G' if(withProof()) { vector pfs; pfs.reserve(thmsA.size() + 1); for(vector::const_iterator i = thmsA.begin(); i != iend; ++i) { pfs.push_back(i->getProof()); } exprs.push_back(as_prove_b.getExpr()); pfs.push_back(as_prove_b.getProof()); pf = newPf("cut_rule",exprs,pfs); } return newTheorem(as_prove_b.getExpr(), a, pf); } void SearchEngineTheoremProducer::checkSoundNoSkolems(const Expr& e, ExprMap& visited, const ExprMap& skolems) { if(visited.count(e)>0) return; else visited[e] = true; CHECK_SOUND(skolems.count(e) == 0, "skolem constant found in axioms of false theorem: " + e.toString()); for(Expr::iterator it = e.begin(), end = e.end(); it!= end; ++it) checkSoundNoSkolems(*it, visited, skolems); if(e.getKind() == FORALL || e.getKind() == EXISTS) checkSoundNoSkolems(e.getBody(), visited, skolems); } void SearchEngineTheoremProducer::checkSoundNoSkolems(const Theorem& t, ExprMap& visited, const ExprMap& skolems) { if(t.isRefl() || t.isFlagged()) return; t.setFlag(); if(t.isAssump()) checkSoundNoSkolems(t.getExpr(), visited, skolems); else { const Assumptions& a(t.getAssumptionsRef()); Assumptions::iterator it = a.begin(), end = a.end(); for(; it!=end; ++it) checkSoundNoSkolems(*it, visited, skolems); } } /*! Eliminate skolem axioms: * Gamma, Delta |- false => Gamma|- false * where Delta is a set of skolem axioms {|-Exists(x) phi (x) => phi(c)} * and gamma does not contain any of the skolem constants c. */ Theorem SearchEngineTheoremProducer::eliminateSkolemAxioms(const Theorem& tFalse, const std::vector& delta) { TRACE("skolem", "=>eliminateSkolemAxioms ", delta.size(), "{"); if(delta.empty()) { TRACE("skolem", "eliminateSkolemAxioms","" , "}"); return tFalse; } const Expr& falseExpr = tFalse.getExpr(); if(CHECK_PROOFS) { CHECK_SOUND(falseExpr.isFalse(), "eliminateSkolemAxiom called on non-false theorem"); ExprMap visited; ExprMap skolems; vector::const_iterator it = delta.begin(), end = delta.end(); for(; it!=end; ++it) { CHECK_SOUND(it->isRewrite(), "eliminateSkolemAxioms(): Skolem axiom is not " "an IFF: "+it->toString()); const Expr& ex = it->getLHS(); CHECK_SOUND(ex.isExists(), "Did not receive skolem axioms in Delta" " of eliminateSkolemAxioms" + it->toString()); // Collect the Skolem constants for further soundness checks for(unsigned int j=0; jnewLeafExpr(sk_var.mkOp()); } skolems[sk_var] = true; TRACE("skolem", ">> Eliminating variable: ", sk_var, "<<"); } } tFalse.clearAllFlags(); checkSoundNoSkolems(tFalse, visited, skolems); } Proof pf; if(!withProof()) return tFalse; else { Proof origFalse = tFalse.getProof(); std::vectorskolemizeLabels; std::vector exprs; for(unsigned int i=0; i& thms, const Theorem& clause, unsigned i) { Expr e(clause.getExpr()); if(CHECK_PROOFS) { // Soundness check: first, check the form of the 'clause' theorem CHECK_SOUND(e.isOr() && e.arity() > (int)i, "SearchEngineTheoremProducer::unitProp: bad theorem or i=" +int2string(i)+" > arity="+int2string(e.arity()) +" in clause = " + clause.toString()); // Now, check correspondence of thms to the disjunction CHECK_SOUND(((int)thms.size()) == e.arity() - 1, "SearchEngineTheoremProducer::unitProp: " "wrong number of theorems" "\n thms.size = " + int2string(thms.size()) +"\n clause.arity = " + int2string(e.arity())); for(unsigned j=0,k=0; j pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(b_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(b_th.getProof()); // can we note which branch to take? pf = newPf("prop_andr_af", exprs, pfs); } return newTheorem(andr_e[0].negate(), a, pf); } Theorem SearchEngineTheoremProducer::propAndrAT(const Theorem& andr_th, const Theorem& l_th, const Theorem& r_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && l_th.proves(andr_e[1]) && r_th.proves(andr_e[2]), "SearchEngineTheoremProducer::propAndrAT"); } Assumptions a(andr_th, l_th); a.add(r_th); Proof pf; if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(l_th.getExpr()); exprs.push_back(r_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(l_th.getProof()); pfs.push_back(r_th.getProof()); pf = newPf("prop_andr_at", exprs, pfs); } return newTheorem(andr_e[0], a, pf); } void SearchEngineTheoremProducer::propAndrLRT(const Theorem& andr_th, const Theorem& a_th, Theorem* l_th, Theorem* r_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && a_th.proves(andr_e[0]), "SearchEngineTheoremProducer::propAndrLRT"); } Assumptions a(andr_th, a_th); Proof pf; if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(a_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(a_th.getProof()); pf = newPf("prop_andr_lrt", exprs, pfs); } if (l_th) *l_th = newTheorem(andr_e[1], a, pf); if (r_th) *r_th = newTheorem(andr_e[2], a, pf); } Theorem SearchEngineTheoremProducer::propAndrLF(const Theorem& andr_th, const Theorem& a_th, const Theorem& r_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && a_th.refutes(andr_e[0]) && r_th.proves(andr_e[2]), "SearchEngineTheoremProducer::propAndrLF"); } Assumptions a(andr_th, a_th); a.add(r_th); Proof pf; if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(a_th.getExpr()); exprs.push_back(r_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(a_th.getProof()); pfs.push_back(r_th.getProof()); pf = newPf("prop_andr_lf", exprs, pfs); } return newTheorem(andr_e[1].negate(), a, pf); } Theorem SearchEngineTheoremProducer::propAndrRF(const Theorem& andr_th, const Theorem& a_th, const Theorem& l_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && a_th.refutes(andr_e[0]) && l_th.proves(andr_e[1]), "SearchEngineTheoremProducer::propAndrRF"); } Assumptions a(andr_th, a_th); a.add(l_th); Proof pf; if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(a_th.getExpr()); exprs.push_back(l_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(a_th.getProof()); pfs.push_back(l_th.getProof()); pf = newPf("prop_andr_rf", exprs, pfs); } return newTheorem(andr_e[2].negate(), a, pf); } Theorem SearchEngineTheoremProducer::confAndrAT(const Theorem& andr_th, const Theorem& a_th, bool left, const Theorem& b_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && a_th.proves(andr_e[0]) && ((left && b_th.refutes(andr_e[1])) || (!left && b_th.refutes(andr_e[2]))), "SearchEngineTheoremProducer::confAndrAT"); } Assumptions a(andr_th, a_th); a.add(b_th); Proof pf; if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(a_th.getExpr()); exprs.push_back(b_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(a_th.getProof()); pfs.push_back(b_th.getProof()); // can we note which branch to take? pf = newPf("conf_andr_at", exprs, pfs); } return newTheorem(d_em->falseExpr(), a, pf); } Theorem SearchEngineTheoremProducer::confAndrAF(const Theorem& andr_th, const Theorem& a_th, const Theorem& l_th, const Theorem& r_th) { const Expr& andr_e(andr_th.getExpr()); if(CHECK_PROOFS) { CHECK_SOUND(andr_e.getKind() == AND_R && a_th.refutes(andr_e[0]) && l_th.proves(andr_e[1]) && r_th.proves(andr_e[2]), "SearchEngineTheoremProducer::confAndrAF"); } Assumptions a; Proof pf; if(withAssumptions()) { a.add(andr_th); a.add(a_th); a.add(l_th); a.add(r_th); } if(withProof()) { vector pfs; vector exprs; exprs.push_back(andr_th.getExpr()); exprs.push_back(a_th.getExpr()); exprs.push_back(l_th.getExpr()); exprs.push_back(r_th.getExpr()); pfs.push_back(andr_th.getProof()); pfs.push_back(a_th.getProof()); pfs.push_back(l_th.getProof()); pfs.push_back(r_th.getProof()); pf = newPf("conf_andr_af", exprs, pfs); } return newTheorem(d_em->falseExpr(), a, pf); } Theorem SearchEngineTheoremProducer::propIffr(const Theorem& iffr_th, int p, const Theorem& a_th, const Theorem& b_th) { int a(-1), b(-1); if(CHECK_PROOFS) CHECK_SOUND(p == 0 || p == 1 || p == 2, "SearchEngineTheoremProducer::propIffr: p=" +int2string(p)); switch (p) { case 0: a = 1; b = 2; break; case 1: a = 0; b = 2; break; case 2: a = 0; b = 1; break; } const Expr& iffr_e(iffr_th.getExpr()); bool v0 = a_th.proves(iffr_e[a]); bool v1 = b_th.proves(iffr_e[b]); if (CHECK_PROOFS) { CHECK_SOUND(iffr_e.getKind() == IFF_R && (v0 || a_th.refutes(iffr_e[a])) && (v1 || b_th.refutes(iffr_e[b])), "SearchEngineTheoremProducer::propIffr"); } Assumptions aa; Proof pf; if (withAssumptions()) { aa.add(iffr_th); aa.add(a_th); aa.add(b_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iffr_th.getExpr()); exprs.push_back(a_th.getExpr()); exprs.push_back(b_th.getExpr()); pfs.push_back(iffr_th.getProof()); pfs.push_back(a_th.getProof()); pfs.push_back(b_th.getProof()); pf = newPf("prop_iffr", exprs, pfs); } return newTheorem(v0 == v1 ? iffr_e[p] : iffr_e[p].negate(), aa, pf); } Theorem SearchEngineTheoremProducer::confIffr(const Theorem& iffr_th, const Theorem& i_th, const Theorem& l_th, const Theorem& r_th) { const Expr& iffr_e(iffr_th.getExpr()); bool v0 = i_th.proves(iffr_e[0]); bool v1 = l_th.proves(iffr_e[1]); bool v2 = r_th.proves(iffr_e[2]); if (CHECK_PROOFS) { CHECK_SOUND(iffr_e.getKind() == IFF_R && (v0 || i_th.refutes(iffr_e[0])) && (v1 || l_th.refutes(iffr_e[1])) && (v2 || r_th.refutes(iffr_e[2])) && ((v0 && v1 != v2) || (!v0 && v1 == v2)), "SearchEngineTheoremProducer::confIffr"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iffr_th); a.add(i_th); a.add(l_th); a.add(r_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iffr_th.getExpr()); exprs.push_back(i_th.getExpr()); exprs.push_back(l_th.getExpr()); exprs.push_back(r_th.getExpr()); pfs.push_back(iffr_th.getProof()); pfs.push_back(i_th.getProof()); pfs.push_back(l_th.getProof()); pfs.push_back(r_th.getProof()); pf = newPf("conf_iffr", exprs, pfs); } return newTheorem(d_em->falseExpr(), a, pf); } Theorem SearchEngineTheoremProducer::propIterIte(const Theorem& iter_th, bool left, const Theorem& if_th, const Theorem& then_th) { const Expr& iter_e(iter_th.getExpr()); bool v0 = if_th.proves(iter_e[1]); bool v1 = then_th.proves(iter_e[left ? 2 : 3]); if (CHECK_PROOFS) { CHECK_SOUND(iter_e.getKind() == ITE_R && (v0 || if_th.refutes(iter_e[1])) && (v1 || then_th.refutes(iter_e[left ? 2 : 3])) && v0 == left, "SearchEngineTheoremProducer::propIterIte"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iter_th); a.add(if_th); a.add(then_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iter_th.getExpr()); exprs.push_back(if_th.getExpr()); exprs.push_back(then_th.getExpr()); pfs.push_back(iter_th.getProof()); pfs.push_back(if_th.getProof()); pfs.push_back(then_th.getProof()); pf = newPf("prop_iter_ite", exprs, pfs); } return newTheorem(v1 ? iter_e[0] : iter_e[0].negate(), a, pf); } void SearchEngineTheoremProducer::propIterIfThen(const Theorem& iter_th, bool left, const Theorem& ite_th, const Theorem& then_th, Theorem* if_th, Theorem* else_th) { const Expr& iter_e(iter_th.getExpr()); bool v0 = ite_th.proves(iter_e[0]); bool v1 = then_th.proves(iter_e[left ? 2 : 3]); if (CHECK_PROOFS) { CHECK_SOUND(iter_e.getKind() == ITE_R && (v0 || ite_th.refutes(iter_e[0])) && (v1 || then_th.refutes(iter_e[left ? 2 : 3])) && v0 != v1, "SearchEngineTheoremProducer::propIterIfThen"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iter_th); a.add(ite_th); a.add(then_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iter_th.getExpr()); exprs.push_back(ite_th.getExpr()); exprs.push_back(then_th.getExpr()); pfs.push_back(iter_th.getProof()); pfs.push_back(ite_th.getProof()); pfs.push_back(then_th.getExpr()); pf = newPf("prop_iter_if_then", exprs, pfs); } if (if_th) *if_th = newTheorem(left ? iter_e[1].negate() : iter_e[1], a, pf); if (else_th) *else_th = newTheorem(v0 ? iter_e[left ? 3 : 2] : iter_e[left ? 3 : 2].negate(), a, pf); } Theorem SearchEngineTheoremProducer::propIterThen(const Theorem& iter_th, const Theorem& ite_th, const Theorem& if_th) { const Expr& iter_e(iter_th.getExpr()); bool v0 = ite_th.proves(iter_e[0]); bool v1 = if_th.proves(iter_e[1]); if (CHECK_PROOFS) { CHECK_SOUND(iter_e.getKind() == ITE_R && (v0 || ite_th.refutes(iter_e[0])) && (v1 || if_th.refutes(iter_e[1])), "SearchEngineTheoremProducer::propIterThen"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iter_th); a.add(ite_th); a.add(if_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iter_th.getExpr()); exprs.push_back(ite_th.getExpr()); exprs.push_back(if_th.getExpr()); pfs.push_back(iter_th.getProof()); pfs.push_back(ite_th.getProof()); pfs.push_back(if_th.getExpr()); pf = newPf("prop_iter_then", exprs, pfs); } return newTheorem(v1 ? (v0 ? iter_e[2] : iter_e[2].negate()) : (v0 ? iter_e[3] : iter_e[3].negate()), a, pf); } Theorem SearchEngineTheoremProducer::confIterThenElse(const Theorem& iter_th, const Theorem& ite_th, const Theorem& then_th, const Theorem& else_th) { const Expr& iter_e(iter_th.getExpr()); bool v0 = ite_th.proves(iter_e[0]); bool v1 = then_th.proves(iter_e[2]); bool v2 = else_th.proves(iter_e[3]); if (CHECK_PROOFS) { CHECK_SOUND(iter_e.getKind() == ITE_R && (v0 || ite_th.refutes(iter_e[0])) && (v1 || then_th.refutes(iter_e[2])) && (v2 || else_th.refutes(iter_e[3])) && ((v0 && !v1 && !v2) || (!v0 && v1 && v2)), "SearchEngineTheoremProducer::confIterThenElse"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iter_th); a.add(ite_th); a.add(then_th); a.add(else_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iter_th.getExpr()); exprs.push_back(ite_th.getExpr()); exprs.push_back(then_th.getExpr()); exprs.push_back(else_th.getExpr()); pfs.push_back(iter_th.getProof()); pfs.push_back(ite_th.getProof()); pfs.push_back(then_th.getExpr()); pfs.push_back(else_th.getExpr()); pf = newPf("conf_iter_then_else", exprs, pfs); } return newTheorem(d_em->falseExpr(), a, pf); } Theorem SearchEngineTheoremProducer::confIterIfThen(const Theorem& iter_th, bool left, const Theorem& ite_th, const Theorem& if_th, const Theorem& then_th) { const Expr& iter_e(iter_th.getExpr()); bool v0 = ite_th.proves(iter_e[0]); bool v1 = if_th.proves(iter_e[1]); bool v2 = then_th.proves(iter_e[left ? 2 : 3]); if (CHECK_PROOFS) { CHECK_SOUND(iter_e.getKind() == ITE_R && (v0 || ite_th.refutes(iter_e[0])) && (v1 || if_th.refutes(iter_e[1])) && (v2 || then_th.refutes(iter_e[left ? 2 : 3])) && v1 == left && v0 != v2, "SearchEngineTheoremProducer::confIterThenElse"); } Assumptions a; Proof pf; if (withAssumptions()) { a.add(iter_th); a.add(ite_th); a.add(if_th); a.add(then_th); } if (withProof()) { vector pfs; vector exprs; exprs.push_back(iter_th.getExpr()); exprs.push_back(ite_th.getExpr()); exprs.push_back(if_th.getExpr()); exprs.push_back(then_th.getExpr()); pfs.push_back(iter_th.getProof()); pfs.push_back(ite_th.getProof()); pfs.push_back(if_th.getExpr()); pfs.push_back(then_th.getExpr()); pf = newPf("conf_iter_then_else", exprs, pfs); } return newTheorem(d_em->falseExpr(), a, pf); } // "Conflict" rule (all literals in a clause become FALSE) // { G_j |- !l_j, j in [1..n] } , G |- (OR l_1 ... l_n) ==> FALSE Theorem SearchEngineTheoremProducer::conflictRule(const std::vector& thms, const Theorem& clause) { Expr e(clause.getExpr()); if(CHECK_PROOFS) { // Soundness check: first, check the form of the 'clause' theorem CHECK_SOUND(e.isOr(), "SearchEngineTheoremProducer::unitProp: " "bad theorem in clause = "+clause.toString()); // Now, check correspondence of thms to the disjunction CHECK_SOUND(((int)thms.size()) == e.arity(), "SearchEngineTheoremProducer::conflictRule: " "wrong number of theorems" "\n thms.size = " + int2string(thms.size()) +"\n clause.arity = " + int2string(e.arity())); for(unsigned j=0; jfalseExpr(), a, pf); } /////////////////////////////////////////////////////////////////////// //// Conjunctive Normal Form (CNF) proof rules /////////////////////////////////////////////////////////////////////// Theorem SearchEngineTheoremProducer::andCNFRule(const Theorem& thm) { return opCNFRule(thm, AND, "and_cnf_rule"); } Theorem SearchEngineTheoremProducer::orCNFRule(const Theorem& thm) { return opCNFRule(thm, OR, "or_cnf_rule"); } Theorem SearchEngineTheoremProducer::impCNFRule(const Theorem& thm) { return opCNFRule(thm, IMPLIES, "implies_cnf_rule"); } Theorem SearchEngineTheoremProducer::iffCNFRule(const Theorem& thm) { return opCNFRule(thm, IFF, "iff_cnf_rule"); } Theorem SearchEngineTheoremProducer::iteCNFRule(const Theorem& thm) { return opCNFRule(thm, ITE, "ite_cnf_rule"); } Theorem SearchEngineTheoremProducer::iteToClauses(const Theorem& ite) { const Expr& iteExpr = ite.getExpr(); if(CHECK_PROOFS) { CHECK_SOUND(iteExpr.isITE() && iteExpr.getType().isBool(), "SearchEngineTheoremProducer::iteToClauses("+iteExpr.toString() +")\n Argument must be a Boolean ITE"); } const Expr& cond = iteExpr[0]; const Expr& t1 = iteExpr[1]; const Expr& t2 = iteExpr[2]; Proof pf; if(withProof()) pf = newPf("ite_to_clauses", iteExpr, ite.getProof()); return newTheorem((cond.negate() || t1) && (cond || t2), ite.getAssumptionsRef(), pf); } Theorem SearchEngineTheoremProducer::iffToClauses(const Theorem& iff) { if(CHECK_PROOFS) { CHECK_SOUND(iff.isRewrite() && iff.getLHS().getType().isBool(), "SearchEngineTheoremProducer::iffToClauses("+iff.getExpr().toString() +")\n Argument must be a Boolean IFF"); } const Expr& t1 = iff.getLHS(); const Expr& t2 = iff.getRHS(); Proof pf; if(withProof()) pf = newPf("iff_to_clauses", iff.getExpr(), iff.getProof()); return newTheorem((t1.negate() || t2) && (t1 || t2.negate()), iff.getAssumptionsRef(), pf); } ///////////////////////////////////////////////////////////////////////// //// helper functions for CNF (Conjunctive Normal Form) conversion ///////////////////////////////////////////////////////////////////////// Theorem SearchEngineTheoremProducer::opCNFRule(const Theorem& thm, int kind, const string& ruleName) { TRACE("mycnf", "opCNFRule["+d_em->getKindName(kind)+"](", thm.getExpr(), ") {"); ExprMap localCache; if(CHECK_PROOFS) { Expr phiIffVar = thm.getExpr(); CHECK_SOUND(phiIffVar.isIff(), "SearchEngineTheoremProducer::opCNFRule(" +d_em->getKindName(kind)+"): " "input must be an IFF: thm = " + phiIffVar.toString()); CHECK_SOUND(phiIffVar[0].getKind() == kind, "SearchEngineTheoremProducer::opCNFRule(" +d_em->getKindName(kind)+"): " "input phi has wrong kind: thm = " + phiIffVar.toString()); CHECK_SOUND(phiIffVar[0] != phiIffVar[1], "SearchEngineTheoremProducer::opCNFRule(" +d_em->getKindName(kind)+"): " "wrong input thm = " + phiIffVar.toString()); for(Expr::iterator it=phiIffVar[0].begin(), itend=phiIffVar[0].end(); it!=itend;++it){ CHECK_SOUND(phiIffVar[1] != *it, "SearchEngineTheoremProducer::opCNFRule(" +d_em->getKindName(kind)+"): " "wrong input thm = " + phiIffVar.toString()); } } const Expr& phi = thm.getExpr()[0]; const Expr& phiVar = thm.getExpr()[1]; std::vector boundVars; std::vector boundVarsAndLiterals; std::vector equivs; for(Expr::iterator i=phi.begin(), iend=phi.end(); i != iend; i++) { // First, strip the negation and check if the formula is atomic Expr tmp(*i); while(tmp.isNot()) tmp = tmp[0]; if(tmp.isPropAtom()) boundVarsAndLiterals.push_back(*i); else boundVarsAndLiterals.push_back(findInLocalCache(*i, localCache, boundVars)); } for(ExprMap::iterator it=localCache.begin(), itend=localCache.end(); it != itend; it++) { DebugAssert((*it).second.isIff(), "SearchEngineTheoremProducer::opCNFRule: " + (*it).second.toString()); DebugAssert(!(*it).second[0].isPropLiteral() && (*it).second[1].isAbsLiteral(), "SearchEngineTheoremProducer::opCNFRule: " + (*it).second.toString()); equivs.push_back((*it).second); } DebugAssert(boundVarsAndLiterals.size() == (unsigned)phi.arity(), "SearchEngineTheoremProducer::opCNFRule: " "wrong size of boundvars: phi = " + phi.toString()); DebugAssert(boundVars.size() == equivs.size(), "SearchEngineTheoremProducer::opCNFRule: " "wrong size of boundvars: phi = " + phi.toString()); Expr cnfInput = phi.arity() > 0 ? Expr(phi.getOp(), boundVarsAndLiterals) : phi; Expr result = convertToCNF(phiVar, cnfInput); if(boundVars.size() > 0) result = d_em->newClosureExpr(EXISTS, boundVars, result.andExpr(andExpr(equivs))); Proof pf; if(withProof()) pf = newPf(ruleName, thm.getExpr(), thm.getProof()); Theorem res(newTheorem(result, thm.getAssumptionsRef(), pf)); TRACE("mycnf", "opCNFRule["+d_em->getKindName(kind)+"] => ", res.getExpr(), " }"); return res; } //! produces the CNF for the formula v <==> phi Expr SearchEngineTheoremProducer::convertToCNF(const Expr& v, const Expr & phi) { //we assume that v \iff phi. v is the newVar corresponding to phi Expr::iterator i = phi.begin(), iend = phi.end(); std::vector clauses; std::vector lastClause; switch(phi.getKind()) { case AND: { const Expr& negV = v.negate(); lastClause.push_back(v); for(;i!=iend; ++i) { clauses.push_back(negV.orExpr(*i)); lastClause.push_back(i->negate()); } clauses.push_back(orExpr(lastClause)); } break; case OR:{ lastClause.push_back(v.negate()); for(;i!=iend; ++i) { clauses.push_back(v.orExpr(i->negate())); lastClause.push_back(*i); } clauses.push_back(orExpr(lastClause)); } break; case IFF: { const Expr& v1 = phi[0]; const Expr& v2 = phi[1]; Expr negV = v.negate(); Expr negv1 = v1.negate(); Expr negv2 = v2.negate(); clauses.push_back(Expr(OR, negV, negv1, v2)); clauses.push_back(Expr(OR, negV, v1, negv2)); clauses.push_back(Expr(OR, v, v1, v2)); clauses.push_back(Expr(OR, v, negv1, negv2)); } break; case IMPLIES:{ const Expr& v1 = phi[0]; const Expr& v2 = phi[1]; Expr negV = v.negate(); Expr negv1 = v1.negate(); Expr negv2 = v2.negate(); clauses.push_back(Expr(OR, negV, negv1, v2)); clauses.push_back(v.orExpr(v1)); clauses.push_back(v.orExpr(negv2)); } break; case ITE: { const Expr& v1 = phi[0]; const Expr& v2 = phi[1]; const Expr& v3 = phi[2]; const Expr& negV = v.negate(); const Expr& negv1 = v1.negate(); const Expr& negv2 = v2.negate(); const Expr& negv3 = v3.negate(); clauses.push_back(Expr(OR, negV, negv1, v2)); clauses.push_back(Expr(OR, negV, v1, v3)); clauses.push_back(Expr(OR, v, negv1, negv2)); clauses.push_back(Expr(OR, v, v1, negv3)); } break; default: DebugAssert(false, "SearchEngineTheoremProducer::convertToCNF: " "bad operator in phi = "+phi.toString()); break; } return andExpr(clauses); } /////////////////////////////////////////////////////////////////////// // helper functions for CNF converters /////////////////////////////////////////////////////////////////////// Expr SearchEngineTheoremProducer::findInLocalCache(const Expr& i, ExprMap& localCache, vector& boundVars) { TRACE("mycnf", "findInLocalCache(", i.toString(), ") { "); Expr boundVar; unsigned int negationDepth = 0; ExprMap::iterator it; Expr phi = i; while(phi.isNot()) { phi = phi[0]; negationDepth++; } it = localCache.find(phi); Expr v; if(it != localCache.end()) { v = ((*it).second)[1]; IF_DEBUG(debugger.counter("CNF Local Cache Hits")++); } else { v = d_em->newBoundVarExpr(i.getType()); boundVars.push_back(v); localCache[phi] = phi.iffExpr(v); } if(negationDepth % 2 != 0) v = !v; TRACE("mycnf", "findInLocalCache => ", v, " }"); return v; }