1 /*********************                                                        */
2 /*! \file theory_proof.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Guy Katz, Liana Hadarean, Yoni Zohar
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8  ** in the top-level source directory) and their institutional affiliations.
9  ** All rights reserved.  See the file COPYING in the top-level source
10  ** directory for licensing information.\endverbatim
11  **
12  ** \brief [[ Add one-line brief description here ]]
13  **
14  ** [[ Add lengthier description here ]]
15  ** \todo document this file
16  **/
17 #include "proof/theory_proof.h"
18 
19 #include "base/cvc4_assert.h"
20 #include "context/context.h"
21 #include "options/bv_options.h"
22 #include "options/proof_options.h"
23 #include "proof/arith_proof.h"
24 #include "proof/array_proof.h"
25 #include "proof/clausal_bitvector_proof.h"
26 #include "proof/clause_id.h"
27 #include "proof/cnf_proof.h"
28 #include "proof/proof_manager.h"
29 #include "proof/proof_output_channel.h"
30 #include "proof/proof_utils.h"
31 #include "proof/resolution_bitvector_proof.h"
32 #include "proof/sat_proof.h"
33 #include "proof/simplify_boolean_node.h"
34 #include "proof/uf_proof.h"
35 #include "prop/sat_solver_types.h"
36 #include "smt/smt_engine.h"
37 #include "smt/smt_engine_scope.h"
38 #include "smt_util/node_visitor.h"
39 #include "theory/arrays/theory_arrays.h"
40 #include "theory/bv/theory_bv.h"
41 #include "theory/output_channel.h"
42 #include "theory/term_registration_visitor.h"
43 #include "theory/uf/theory_uf.h"
44 #include "theory/valuation.h"
45 #include "util/hash.h"
46 #include "util/proof.h"
47 
48 namespace CVC4 {
49 
50 using proof::LfscResolutionBitVectorProof;
51 using proof::ResolutionBitVectorProof;
52 
53 unsigned CVC4::ProofLetCount::counter = 0;
54 static unsigned LET_COUNT = 1;
55 
TheoryProofEngine()56 TheoryProofEngine::TheoryProofEngine()
57   : d_registrationCache()
58   , d_theoryProofTable()
59 {
60   d_theoryProofTable[theory::THEORY_BOOL] = new LFSCBooleanProof(this);
61 }
62 
~TheoryProofEngine()63 TheoryProofEngine::~TheoryProofEngine() {
64   TheoryProofTable::iterator it = d_theoryProofTable.begin();
65   TheoryProofTable::iterator end = d_theoryProofTable.end();
66   for (; it != end; ++it) {
67     delete it->second;
68   }
69 }
70 
registerTheory(theory::Theory * th)71 void TheoryProofEngine::registerTheory(theory::Theory* th) {
72   if (th) {
73     theory::TheoryId id = th->getId();
74     if(d_theoryProofTable.find(id) == d_theoryProofTable.end()) {
75 
76       Trace("pf::tp") << "TheoryProofEngine::registerTheory: " << id << std::endl;
77 
78       if (id == theory::THEORY_UF) {
79         d_theoryProofTable[id] = new LFSCUFProof((theory::uf::TheoryUF*)th, this);
80         return;
81       }
82 
83       if (id == theory::THEORY_BV) {
84         auto thBv = static_cast<theory::bv::TheoryBV*>(th);
85         if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER
86             && options::bvSatSolver() == theory::bv::SAT_SOLVER_CRYPTOMINISAT)
87         {
88           proof::BitVectorProof* bvp = nullptr;
89           switch (options::bvProofFormat())
90           {
91             case theory::bv::BvProofFormat::BITVECTOR_PROOF_DRAT:
92             {
93               bvp = new proof::LfscDratBitVectorProof(thBv, this);
94               break;
95             }
96             case theory::bv::BvProofFormat::BITVECTOR_PROOF_LRAT:
97             {
98               bvp = new proof::LfscLratBitVectorProof(thBv, this);
99               break;
100             }
101             case theory::bv::BvProofFormat::BITVECTOR_PROOF_ER:
102             {
103               bvp = new proof::LfscErBitVectorProof(thBv, this);
104               break;
105             }
106             default: { Unreachable("Invalid BvProofFormat");
107             }
108           };
109           d_theoryProofTable[id] = bvp;
110         }
111         else
112         {
113           proof::BitVectorProof* bvp =
114               new proof::LfscResolutionBitVectorProof(thBv, this);
115           d_theoryProofTable[id] = bvp;
116         }
117         return;
118       }
119 
120       if (id == theory::THEORY_ARRAYS) {
121         d_theoryProofTable[id] = new LFSCArrayProof((theory::arrays::TheoryArrays*)th, this);
122         return;
123       }
124 
125       if (id == theory::THEORY_ARITH) {
126         d_theoryProofTable[id] = new LFSCArithProof((theory::arith::TheoryArith*)th, this);
127         return;
128       }
129 
130       // TODO other theories
131     }
132   }
133 }
134 
finishRegisterTheory(theory::Theory * th)135 void TheoryProofEngine::finishRegisterTheory(theory::Theory* th) {
136   if (th) {
137     theory::TheoryId id = th->getId();
138     if (id == theory::THEORY_BV) {
139       theory::bv::TheoryBV* bv_th = static_cast<theory::bv::TheoryBV*>(th);
140       Assert(d_theoryProofTable.find(id) != d_theoryProofTable.end());
141       proof::BitVectorProof* bvp =
142           static_cast<proof::BitVectorProof*>(d_theoryProofTable[id]);
143       bv_th->setProofLog(bvp);
144       return;
145     }
146   }
147 }
148 
getTheoryProof(theory::TheoryId id)149 TheoryProof* TheoryProofEngine::getTheoryProof(theory::TheoryId id) {
150   // The UF theory handles queries for the Builtin theory.
151   if (id == theory::THEORY_BUILTIN) {
152     Debug("pf::tp") << "TheoryProofEngine::getTheoryProof: BUILTIN --> UF" << std::endl;
153     id = theory::THEORY_UF;
154   }
155 
156   if (d_theoryProofTable.find(id) == d_theoryProofTable.end()) {
157     std::stringstream ss;
158     ss << "Error! Proofs not yet supported for the following theory: " << id << std::endl;
159     InternalError(ss.str().c_str());
160   }
161 
162   return d_theoryProofTable[id];
163 }
164 
markTermForFutureRegistration(Expr term,theory::TheoryId id)165 void TheoryProofEngine::markTermForFutureRegistration(Expr term, theory::TheoryId id) {
166   d_exprToTheoryIds[term].insert(id);
167 }
168 
printConstantDisequalityProof(std::ostream & os,Expr c1,Expr c2,const ProofLetMap & globalLetMap)169 void TheoryProofEngine::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) {
170   Assert(c1.isConst());
171   Assert(c2.isConst());
172 
173   Assert(theory::Theory::theoryOf(c1) == theory::Theory::theoryOf(c2));
174   getTheoryProof(theory::Theory::theoryOf(c1))->printConstantDisequalityProof(os, c1, c2, globalLetMap);
175 }
176 
registerTerm(Expr term)177 void TheoryProofEngine::registerTerm(Expr term) {
178   Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering term: " << term << std::endl;
179 
180   if (d_registrationCache.count(term)) {
181     return;
182   }
183 
184   Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering NEW term: " << term << std::endl;
185 
186   theory::TheoryId theory_id = theory::Theory::theoryOf(term);
187 
188   Debug("pf::tp::register") << "Term's theory( " << term << " ) = " << theory_id << std::endl;
189 
190   // don't need to register boolean terms
191   if (theory_id == theory::THEORY_BUILTIN ||
192       term.getKind() == kind::ITE) {
193     for (unsigned i = 0; i < term.getNumChildren(); ++i) {
194       registerTerm(term[i]);
195     }
196     d_registrationCache.insert(term);
197     return;
198   }
199 
200   if (!supportedTheory(theory_id)) return;
201 
202   // Register the term with its owner theory
203   getTheoryProof(theory_id)->registerTerm(term);
204 
205   // A special case: the array theory needs to know of every skolem, even if
206   // it belongs to another theory (e.g., a BV skolem)
207   if (ProofManager::getSkolemizationManager()->isSkolem(term) && theory_id != theory::THEORY_ARRAYS) {
208     Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering a non-array skolem: " << term << std::endl;
209     getTheoryProof(theory::THEORY_ARRAYS)->registerTerm(term);
210   }
211 
212   d_registrationCache.insert(term);
213 }
214 
getTheoryForLemma(const prop::SatClause * clause)215 theory::TheoryId TheoryProofEngine::getTheoryForLemma(const prop::SatClause* clause) {
216   ProofManager* pm = ProofManager::currentPM();
217 
218   std::set<Node> nodes;
219   for(unsigned i = 0; i < clause->size(); ++i) {
220     prop::SatLiteral lit = (*clause)[i];
221     Node node = pm->getCnfProof()->getAtom(lit.getSatVariable());
222     Expr atom = node.toExpr();
223     if (atom.isConst()) {
224       Assert (atom == utils::mkTrue());
225       continue;
226     }
227 
228     nodes.insert(lit.isNegated() ? node.notNode() : node);
229   }
230 
231   // Ensure that the lemma is in the database.
232   Assert (pm->getCnfProof()->haveProofRecipe(nodes));
233   return pm->getCnfProof()->getProofRecipe(nodes).getTheory();
234 }
235 
bind(Expr term,ProofLetMap & map,Bindings & let_order)236 void LFSCTheoryProofEngine::bind(Expr term, ProofLetMap& map, Bindings& let_order) {
237   ProofLetMap::iterator it = map.find(term);
238   if (it != map.end()) {
239     ProofLetCount& count = it->second;
240     count.increment();
241     return;
242   }
243   for (unsigned i = 0; i < term.getNumChildren(); ++i) {
244     bind(term[i], map, let_order);
245   }
246   unsigned new_id = ProofLetCount::newId();
247   map[term] = ProofLetCount(new_id);
248   let_order.push_back(LetOrderElement(term, new_id));
249 }
250 
printLetTerm(Expr term,std::ostream & os)251 void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) {
252   ProofLetMap map;
253   Bindings let_order;
254   bind(term, map, let_order);
255   std::ostringstream paren;
256   for (unsigned i = 0; i < let_order.size(); ++i) {
257     Expr current_expr = let_order[i].expr;
258     unsigned let_id = let_order[i].id;
259     ProofLetMap::const_iterator it = map.find(current_expr);
260     Assert (it != map.end());
261     unsigned let_count = it->second.count;
262     Assert(let_count);
263     // skip terms that only appear once
264     if (let_count <= LET_COUNT) {
265       continue;
266     }
267 
268     os << "(@ let" <<let_id << " ";
269     printTheoryTerm(current_expr, os, map);
270     paren <<")";
271   }
272   unsigned last_let_id = let_order.back().id;
273   Expr last = let_order.back().expr;
274   unsigned last_count = map.find(last)->second.count;
275   if (last_count <= LET_COUNT) {
276     printTheoryTerm(last, os, map);
277   }
278   else {
279     os << " let" << last_let_id;
280   }
281   os << paren.str();
282 }
283 
printTheoryTerm(Expr term,std::ostream & os,const ProofLetMap & map)284 void LFSCTheoryProofEngine::printTheoryTerm(Expr term, std::ostream& os, const ProofLetMap& map) {
285   theory::TheoryId theory_id = theory::Theory::theoryOf(term);
286 
287   // boolean terms and ITEs are special because they
288   // are common to all theories
289   if (theory_id == theory::THEORY_BUILTIN ||
290       term.getKind() == kind::ITE ||
291       term.getKind() == kind::EQUAL) {
292     printCoreTerm(term, os, map);
293     return;
294   }
295   // dispatch to proper theory
296   getTheoryProof(theory_id)->printOwnedTerm(term, os, map);
297 }
298 
printSort(Type type,std::ostream & os)299 void LFSCTheoryProofEngine::printSort(Type type, std::ostream& os) {
300   if (type.isSort()) {
301     getTheoryProof(theory::THEORY_UF)->printOwnedSort(type, os);
302     return;
303   }
304   if (type.isBitVector()) {
305     getTheoryProof(theory::THEORY_BV)->printOwnedSort(type, os);
306     return;
307   }
308 
309   if (type.isArray()) {
310     getTheoryProof(theory::THEORY_ARRAYS)->printOwnedSort(type, os);
311     return;
312   }
313 
314   if (type.isInteger() || type.isReal()) {
315     getTheoryProof(theory::THEORY_ARITH)->printOwnedSort(type, os);
316     return;
317   }
318 
319   if (type.isBoolean()) {
320     getTheoryProof(theory::THEORY_BOOL)->printOwnedSort(type, os);
321     return;
322   }
323 
324   Unreachable();
325 }
326 
performExtraRegistrations()327 void LFSCTheoryProofEngine::performExtraRegistrations() {
328   ExprToTheoryIds::const_iterator it;
329   for (it = d_exprToTheoryIds.begin(); it != d_exprToTheoryIds.end(); ++it) {
330     if (d_registrationCache.count(it->first)) { // Only register if the term appeared
331       TheoryIdSet::const_iterator theoryIt;
332       for (theoryIt = it->second.begin(); theoryIt != it->second.end(); ++theoryIt) {
333         Debug("pf::tp") << "\tExtra registration of term " << it->first
334                         << " with theory: " << *theoryIt << std::endl;
335         Assert(supportedTheory(*theoryIt));
336         getTheoryProof(*theoryIt)->registerTerm(it->first);
337       }
338     }
339   }
340 }
341 
registerTermsFromAssertions()342 void LFSCTheoryProofEngine::registerTermsFromAssertions() {
343   ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions();
344   ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions();
345 
346   for(; it != end; ++it) {
347     registerTerm(*it);
348   }
349 
350   performExtraRegistrations();
351 }
352 
printAssertions(std::ostream & os,std::ostream & paren)353 void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) {
354   Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions called" << std::endl << std::endl;
355 
356   ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions();
357   ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions();
358 
359   for (; it != end; ++it) {
360     Debug("pf::tp") << "printAssertions: assertion is: " << *it << std::endl;
361     os << "(% " << ProofManager::currentPM()->getInputFormulaName(*it) << " (th_holds ";
362 
363     // Assertions appear before the global let map, so we use a dummpMap to avoid letification here.
364     ProofLetMap dummyMap;
365 
366     bool convertFromBool = (it->getType().isBoolean() && printsAsBool(*it));
367     if (convertFromBool) os << "(p_app ";
368     printBoundTerm(*it, os, dummyMap);
369     if (convertFromBool) os << ")";
370 
371     os << ")\n";
372     paren << ")";
373   }
374 
375   Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions done" << std::endl << std::endl;
376 }
377 
printLemmaRewrites(NodePairSet & rewrites,std::ostream & os,std::ostream & paren)378 void LFSCTheoryProofEngine::printLemmaRewrites(NodePairSet& rewrites,
379                                                std::ostream& os,
380                                                std::ostream& paren) {
381   Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites called" << std::endl << std::endl;
382 
383   NodePairSet::const_iterator it;
384 
385   for (it = rewrites.begin(); it != rewrites.end(); ++it) {
386     Debug("pf::tp") << "printLemmaRewrites: " << it->first << " --> " << it->second << std::endl;
387 
388     Node n1 = it->first;
389     Node n2 = it->second;
390     Assert(n1.toExpr() == utils::mkFalse() ||
391            theory::Theory::theoryOf(n1) == theory::Theory::theoryOf(n2));
392 
393     std::ostringstream rewriteRule;
394     rewriteRule << ".lrr" << d_assertionToRewrite.size();
395 
396     os << "(th_let_pf _ ";
397     getTheoryProof(theory::Theory::theoryOf(n1))->printRewriteProof(os, n1, n2);
398     os << "(\\ " << rewriteRule.str() << "\n";
399 
400     d_assertionToRewrite[it->first] = rewriteRule.str();
401     Debug("pf::tp") << "d_assertionToRewrite[" << it->first << "] = " << rewriteRule.str() << std::endl;
402     paren << "))";
403   }
404 
405   Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites done" << std::endl << std::endl;
406 }
407 
printSortDeclarations(std::ostream & os,std::ostream & paren)408 void LFSCTheoryProofEngine::printSortDeclarations(std::ostream& os, std::ostream& paren) {
409   Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations called" << std::endl << std::endl;
410 
411   TheoryProofTable::const_iterator it = d_theoryProofTable.begin();
412   TheoryProofTable::const_iterator end = d_theoryProofTable.end();
413   for (; it != end; ++it) {
414     it->second->printSortDeclarations(os, paren);
415   }
416 
417   Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations done" << std::endl << std::endl;
418 }
419 
printTermDeclarations(std::ostream & os,std::ostream & paren)420 void LFSCTheoryProofEngine::printTermDeclarations(std::ostream& os, std::ostream& paren) {
421   Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations called" << std::endl << std::endl;
422 
423   TheoryProofTable::const_iterator it = d_theoryProofTable.begin();
424   TheoryProofTable::const_iterator end = d_theoryProofTable.end();
425   for (; it != end; ++it) {
426     it->second->printTermDeclarations(os, paren);
427   }
428 
429   Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations done" << std::endl << std::endl;
430 }
431 
printDeferredDeclarations(std::ostream & os,std::ostream & paren)432 void LFSCTheoryProofEngine::printDeferredDeclarations(std::ostream& os, std::ostream& paren) {
433   Debug("pf::tp") << "LFSCTheoryProofEngine::printDeferredDeclarations called" << std::endl;
434 
435   TheoryProofTable::const_iterator it = d_theoryProofTable.begin();
436   TheoryProofTable::const_iterator end = d_theoryProofTable.end();
437   for (; it != end; ++it) {
438     it->second->printDeferredDeclarations(os, paren);
439   }
440 }
441 
printAliasingDeclarations(std::ostream & os,std::ostream & paren,const ProofLetMap & globalLetMap)442 void LFSCTheoryProofEngine::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) {
443   Debug("pf::tp") << "LFSCTheoryProofEngine::printAliasingDeclarations called" << std::endl;
444 
445   TheoryProofTable::const_iterator it = d_theoryProofTable.begin();
446   TheoryProofTable::const_iterator end = d_theoryProofTable.end();
447   for (; it != end; ++it) {
448     it->second->printAliasingDeclarations(os, paren, globalLetMap);
449   }
450 }
451 
dumpTheoryLemmas(const IdToSatClause & lemmas)452 void LFSCTheoryProofEngine::dumpTheoryLemmas(const IdToSatClause& lemmas) {
453   Debug("pf::dumpLemmas") << "Dumping ALL theory lemmas" << std::endl << std::endl;
454 
455   ProofManager* pm = ProofManager::currentPM();
456   for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) {
457     ClauseId id = it->first;
458     Debug("pf::dumpLemmas") << "**** \tLemma ID = " << id << std::endl;
459     const prop::SatClause* clause = it->second;
460     std::set<Node> nodes;
461     for(unsigned i = 0; i < clause->size(); ++i) {
462       prop::SatLiteral lit = (*clause)[i];
463       Node node = pm->getCnfProof()->getAtom(lit.getSatVariable());
464       if (node.isConst()) {
465         Assert (node.toExpr() == utils::mkTrue());
466         continue;
467       }
468       nodes.insert(lit.isNegated() ? node.notNode() : node);
469     }
470 
471     LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(nodes);
472     recipe.dump("pf::dumpLemmas");
473   }
474 
475   Debug("pf::dumpLemmas") << "Theory lemma printing DONE" << std::endl << std::endl;
476 }
477 
478 // TODO: this function should be moved into the BV prover.
finalizeBvConflicts(const IdToSatClause & lemmas,std::ostream & os)479 void LFSCTheoryProofEngine::finalizeBvConflicts(const IdToSatClause& lemmas, std::ostream& os) {
480   // BitVector theory is special case: must know all conflicts needed
481   // ahead of time for resolution proof lemmas
482   std::vector<Expr> bv_lemmas;
483 
484   for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) {
485     const prop::SatClause* clause = it->second;
486 
487     std::vector<Expr> conflict;
488     std::set<Node> conflictNodes;
489     for(unsigned i = 0; i < clause->size(); ++i) {
490       prop::SatLiteral lit = (*clause)[i];
491       Node node = ProofManager::currentPM()->getCnfProof()->getAtom(lit.getSatVariable());
492       Expr atom = node.toExpr();
493 
494       // The literals (true) and (not false) are omitted from conflicts
495       if (atom.isConst()) {
496         Assert (atom == utils::mkTrue() ||
497                 (atom == utils::mkFalse() && lit.isNegated()));
498         continue;
499       }
500 
501       Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom;
502       conflict.push_back(expr_lit);
503       conflictNodes.insert(lit.isNegated() ? node.notNode() : node);
504     }
505 
506     LemmaProofRecipe recipe = ProofManager::currentPM()->getCnfProof()->getProofRecipe(conflictNodes);
507 
508     unsigned numberOfSteps = recipe.getNumSteps();
509 
510     prop::SatClause currentClause = *clause;
511     std::vector<Expr> currentClauseExpr = conflict;
512 
513     for (unsigned i = 0; i < numberOfSteps; ++i) {
514       const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i);
515 
516       if (currentStep->getTheory() != theory::THEORY_BV) {
517         continue;
518       }
519 
520       // If any rewrites took place, we need to update the conflict clause accordingly
521       std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i);
522       std::map<Node, Node> explanationToMissingAssertion;
523       std::set<Node>::iterator assertionIt;
524       for (assertionIt = missingAssertions.begin();
525            assertionIt != missingAssertions.end();
526            ++assertionIt) {
527         Node negated = (*assertionIt).negate();
528         explanationToMissingAssertion[recipe.getExplanation(negated)] = negated;
529       }
530 
531       currentClause = *clause;
532       currentClauseExpr = conflict;
533 
534       for (unsigned j = 0; j < i; ++j) {
535         // Literals already used in previous steps need to be negated
536         Node previousLiteralNode = recipe.getStep(j)->getLiteral();
537 
538         // If this literal is the result of a rewrite, we need to translate it
539         if (explanationToMissingAssertion.find(previousLiteralNode) !=
540             explanationToMissingAssertion.end()) {
541           previousLiteralNode = explanationToMissingAssertion[previousLiteralNode];
542         }
543 
544         Node previousLiteralNodeNegated = previousLiteralNode.negate();
545         prop::SatLiteral previousLiteralNegated =
546           ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated);
547 
548         currentClause.push_back(previousLiteralNegated);
549         currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr());
550       }
551 
552       // If we're in the final step, the last literal is Null and should not be added.
553       // Otherwise, the current literal does NOT need to be negated
554       Node currentLiteralNode = currentStep->getLiteral();
555 
556       if (currentLiteralNode != Node()) {
557         prop::SatLiteral currentLiteral =
558           ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode);
559 
560         currentClause.push_back(currentLiteral);
561         currentClauseExpr.push_back(currentLiteralNode.toExpr());
562       }
563 
564       bv_lemmas.push_back(utils::mkSortedExpr(kind::OR, currentClauseExpr));
565     }
566   }
567 
568   proof::BitVectorProof* bv = ProofManager::getBitVectorProof();
569   bv->finalizeConflicts(bv_lemmas);
570 }
571 
printTheoryLemmas(const IdToSatClause & lemmas,std::ostream & os,std::ostream & paren,ProofLetMap & map)572 void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas,
573                                               std::ostream& os,
574                                               std::ostream& paren,
575                                               ProofLetMap& map) {
576   os << " ;; Theory Lemmas \n";
577   Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: starting" << std::endl;
578 
579   if (Debug.isOn("pf::dumpLemmas")) {
580     dumpTheoryLemmas(lemmas);
581   }
582 
583   //  finalizeBvConflicts(lemmas, os, paren, map);
584   ProofManager::getBitVectorProof()->printBBDeclarationAndCnf(os, paren, map);
585 
586   if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) {
587     Assert (lemmas.size() == 1);
588     // nothing more to do (no combination with eager so far)
589     return;
590   }
591 
592   ProofManager* pm = ProofManager::currentPM();
593   Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemmas..." << std::endl;
594 
595   for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) {
596     ClauseId id = it->first;
597     const prop::SatClause* clause = it->second;
598 
599     Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemma. ID = "
600                     << id << std::endl;
601 
602     std::vector<Expr> clause_expr;
603     std::set<Node> clause_expr_nodes;
604     for(unsigned i = 0; i < clause->size(); ++i) {
605       prop::SatLiteral lit = (*clause)[i];
606       Node node = pm->getCnfProof()->getAtom(lit.getSatVariable());
607       Expr atom = node.toExpr();
608       if (atom.isConst()) {
609         Assert (atom == utils::mkTrue());
610         continue;
611       }
612       Expr expr_lit = lit.isNegated() ? atom.notExpr(): atom;
613       clause_expr.push_back(expr_lit);
614       clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node);
615     }
616 
617     LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(clause_expr_nodes);
618 
619     if (recipe.simpleLemma()) {
620       // In a simple lemma, there will be no propositional resolution in the end
621 
622       Debug("pf::tp") << "Simple lemma" << std::endl;
623       // Printing the clause as it appears in resolution proof
624       os << "(satlem _ _ ";
625       std::ostringstream clause_paren;
626       pm->getCnfProof()->printClause(*clause, os, clause_paren);
627 
628       // Find and handle missing assertions, due to rewrites
629       std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(0);
630       if (!missingAssertions.empty()) {
631         Debug("pf::tp") << "Have missing assertions for this simple lemma!" << std::endl;
632       }
633 
634       std::set<Node>::const_iterator missingAssertion;
635       for (missingAssertion = missingAssertions.begin();
636            missingAssertion != missingAssertions.end();
637            ++missingAssertion) {
638 
639         Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl;
640         Assert(recipe.wasRewritten(missingAssertion->negate()));
641         Node explanation = recipe.getExplanation(missingAssertion->negate()).negate();
642         Debug("pf::tp") << "Found explanation: " << explanation << std::endl;
643 
644         // We have a missing assertion.
645         //     rewriteIt->first is the assertion after the rewrite (the explanation),
646         //     rewriteIt->second is the original assertion that needs to be fed into the theory.
647 
648         bool found = false;
649         unsigned k;
650         for (k = 0; k < clause_expr.size(); ++k) {
651           if (clause_expr[k] == explanation.toExpr()) {
652             found = true;
653             break;
654           }
655         }
656 
657         AlwaysAssert(found);
658         Debug("pf::tp") << "Replacing theory assertion "
659                         << clause_expr[k]
660                         << " with "
661                         << *missingAssertion
662                         << std::endl;
663 
664         clause_expr[k] = missingAssertion->toExpr();
665 
666         std::ostringstream rewritten;
667 
668         if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) {
669           rewritten << "(or_elim_2 _ _ ";
670           rewritten << "(not_not_intro _ ";
671           rewritten << pm->getLitName(explanation);
672           rewritten << ") (iff_elim_2 _ _ ";
673           rewritten << d_assertionToRewrite[missingAssertion->negate()];
674           rewritten << "))";
675         }
676         else {
677           rewritten << "(or_elim_1 _ _ ";
678           rewritten << "(not_not_intro _ ";
679           rewritten << pm->getLitName(explanation);
680           rewritten << ") (iff_elim_1 _ _ ";
681           rewritten << d_assertionToRewrite[missingAssertion->negate()];
682           rewritten << "))";
683         }
684 
685         Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl
686                         << pm->getLitName(*missingAssertion) << " --> " << rewritten.str()
687                         << ", explanation = " << explanation
688                         << std::endl << std::endl;
689 
690         pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str());
691       }
692 
693       // Query the appropriate theory for a proof of this clause
694       theory::TheoryId theory_id = getTheoryForLemma(clause);
695       Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl;
696       getTheoryProof(theory_id)->printTheoryLemmaProof(clause_expr, os, paren, map);
697 
698       // Turn rewrite filter OFF
699       pm->clearRewriteFilters();
700 
701       Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl;
702       os << clause_paren.str();
703       os << "( \\ " << pm->getLemmaClauseName(id) <<"\n";
704       paren << "))";
705     } else { // This is a composite lemma
706 
707       unsigned numberOfSteps = recipe.getNumSteps();
708       prop::SatClause currentClause = *clause;
709       std::vector<Expr> currentClauseExpr = clause_expr;
710 
711       for (unsigned i = 0; i < numberOfSteps; ++i) {
712         const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i);
713 
714         currentClause = *clause;
715         currentClauseExpr = clause_expr;
716 
717         for (unsigned j = 0; j < i; ++j) {
718           // Literals already used in previous steps need to be negated
719           Node previousLiteralNode = recipe.getStep(j)->getLiteral();
720           Node previousLiteralNodeNegated = previousLiteralNode.negate();
721           prop::SatLiteral previousLiteralNegated =
722             ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated);
723           currentClause.push_back(previousLiteralNegated);
724           currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr());
725         }
726 
727         // If the current literal is NULL, can ignore (final step)
728         // Otherwise, the current literal does NOT need to be negated
729         Node currentLiteralNode = currentStep->getLiteral();
730         if (currentLiteralNode != Node()) {
731           prop::SatLiteral currentLiteral =
732             ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode);
733 
734           currentClause.push_back(currentLiteral);
735           currentClauseExpr.push_back(currentLiteralNode.toExpr());
736         }
737 
738         os << "(satlem _ _ ";
739         std::ostringstream clause_paren;
740 
741         pm->getCnfProof()->printClause(currentClause, os, clause_paren);
742 
743         // query appropriate theory for proof of clause
744         theory::TheoryId theory_id = currentStep->getTheory();
745         Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl;
746 
747         std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i);
748         if (!missingAssertions.empty()) {
749           Debug("pf::tp") << "Have missing assertions for this step!" << std::endl;
750         }
751 
752         // Turn rewrite filter ON
753         std::set<Node>::const_iterator missingAssertion;
754         for (missingAssertion = missingAssertions.begin();
755              missingAssertion != missingAssertions.end();
756              ++missingAssertion) {
757 
758           Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl;
759 
760           Assert(recipe.wasRewritten(missingAssertion->negate()));
761           Node explanation = recipe.getExplanation(missingAssertion->negate()).negate();
762 
763           Debug("pf::tp") << "Found explanation: " << explanation << std::endl;
764 
765           // We have a missing assertion.
766           //     rewriteIt->first is the assertion after the rewrite (the explanation),
767           //     rewriteIt->second is the original assertion that needs to be fed into the theory.
768 
769           bool found = false;
770           unsigned k;
771           for (k = 0; k < currentClauseExpr.size(); ++k) {
772             if (currentClauseExpr[k] == explanation.toExpr()) {
773               found = true;
774               break;
775             }
776           }
777 
778           AlwaysAssert(found);
779 
780           Debug("pf::tp") << "Replacing theory assertion "
781                           << currentClauseExpr[k]
782                           << " with "
783                           << *missingAssertion
784                           << std::endl;
785 
786           currentClauseExpr[k] = missingAssertion->toExpr();
787 
788           std::ostringstream rewritten;
789 
790           if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) {
791             rewritten << "(or_elim_2 _ _ ";
792             rewritten << "(not_not_intro _ ";
793             rewritten << pm->getLitName(explanation);
794             rewritten << ") (iff_elim_2 _ _ ";
795             rewritten << d_assertionToRewrite[missingAssertion->negate()];
796             rewritten << "))";
797           }
798           else {
799             rewritten << "(or_elim_1 _ _ ";
800             rewritten << "(not_not_intro _ ";
801             rewritten << pm->getLitName(explanation);
802             rewritten << ") (iff_elim_1 _ _ ";
803             rewritten << d_assertionToRewrite[missingAssertion->negate()];
804             rewritten << "))";
805           }
806 
807           Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl
808                           << pm->getLitName(*missingAssertion) << " --> " << rewritten.str()
809                           << "explanation = " << explanation
810                           << std::endl << std::endl;
811 
812           pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str());
813         }
814 
815         getTheoryProof(theory_id)->printTheoryLemmaProof(currentClauseExpr, os, paren, map);
816 
817         // Turn rewrite filter OFF
818         pm->clearRewriteFilters();
819 
820         Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl;
821         os << clause_paren.str();
822         os << "( \\ " << pm->getLemmaClauseName(id) << "s" << i <<"\n";
823         paren << "))";
824       }
825 
826       Assert(numberOfSteps >= 2);
827 
828       os << "(satlem_simplify _ _ _ ";
829       for (unsigned i = 0; i < numberOfSteps - 1; ++i) {
830         // Resolve step i with step i + 1
831         if (recipe.getStep(i)->getLiteral().getKind() == kind::NOT) {
832           os << "(Q _ _ ";
833         } else {
834           os << "(R _ _ ";
835         }
836 
837         os << pm->getLemmaClauseName(id) << "s" << i;
838         os << " ";
839       }
840 
841       os << pm->getLemmaClauseName(id) << "s" << numberOfSteps - 1 << " ";
842 
843       prop::SatLiteral v;
844       for (int i = numberOfSteps - 2; i >= 0; --i) {
845         v = ProofManager::currentPM()->getCnfProof()->getLiteral(recipe.getStep(i)->getLiteral());
846         os << ProofManager::getVarName(v.getSatVariable(), "") << ") ";
847       }
848 
849       os << "( \\ " << pm->getLemmaClauseName(id) << "\n";
850       paren << "))";
851     }
852   }
853 }
854 
printBoundTerm(Expr term,std::ostream & os,const ProofLetMap & map)855 void LFSCTheoryProofEngine::printBoundTerm(Expr term, std::ostream& os, const ProofLetMap& map) {
856   Debug("pf::tp") << "LFSCTheoryProofEngine::printBoundTerm( " << term << " ) " << std::endl;
857 
858   ProofLetMap::const_iterator it = map.find(term);
859   if (it != map.end()) {
860     unsigned id = it->second.id;
861     unsigned count = it->second.count;
862 
863     if (count > LET_COUNT) {
864       os << "let" << id;
865       return;
866     }
867   }
868 
869   printTheoryTerm(term, os, map);
870 }
871 
printCoreTerm(Expr term,std::ostream & os,const ProofLetMap & map)872 void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const ProofLetMap& map) {
873   if (term.isVariable()) {
874     os << ProofManager::sanitize(term);
875     return;
876   }
877 
878   Kind k = term.getKind();
879 
880   switch(k) {
881   case kind::ITE: {
882     os << (term.getType().isBoolean() ? "(ifte ": "(ite _ ");
883 
884     bool booleanCase = term[0].getType().isBoolean();
885     if (booleanCase && printsAsBool(term[0])) os << "(p_app ";
886     printBoundTerm(term[0], os, map);
887     if (booleanCase && printsAsBool(term[0])) os << ")";
888 
889     os << " ";
890     printBoundTerm(term[1], os, map);
891     os << " ";
892     printBoundTerm(term[2], os, map);
893     os << ")";
894     return;
895   }
896 
897   case kind::EQUAL: {
898     bool booleanCase = term[0].getType().isBoolean();
899 
900     os << "(";
901     if (booleanCase) {
902       os << "iff ";
903     } else {
904       os << "= ";
905       printSort(term[0].getType(), os);
906       os << " ";
907     }
908 
909     if (booleanCase && printsAsBool(term[0])) os << "(p_app ";
910     printBoundTerm(term[0], os, map);
911     if (booleanCase && printsAsBool(term[0])) os << ")";
912 
913     os << " ";
914 
915     if (booleanCase && printsAsBool(term[1])) os << "(p_app ";
916     printBoundTerm(term[1], os, map);
917     if (booleanCase && printsAsBool(term[1])) os << ") ";
918     os << ")";
919 
920     return;
921   }
922 
923   case kind::DISTINCT:
924     // Distinct nodes can have any number of chidlren.
925     Assert (term.getNumChildren() >= 2);
926 
927     if (term.getNumChildren() == 2) {
928       os << "(not (= ";
929       printSort(term[0].getType(), os);
930       os << " ";
931       printBoundTerm(term[0], os, map);
932       os << " ";
933       printBoundTerm(term[1], os, map);
934       os << "))";
935     } else {
936       unsigned numOfPairs = term.getNumChildren() * (term.getNumChildren() - 1) / 2;
937       for (unsigned i = 1; i < numOfPairs; ++i) {
938         os << "(and ";
939       }
940 
941       for (unsigned i = 0; i < term.getNumChildren(); ++i) {
942         for (unsigned j = i + 1; j < term.getNumChildren(); ++j) {
943           if ((i != 0) || (j != 1)) {
944             os << "(not (= ";
945             printSort(term[0].getType(), os);
946             os << " ";
947             printBoundTerm(term[i], os, map);
948             os << " ";
949             printBoundTerm(term[j], os, map);
950             os << ")))";
951           } else {
952             os << "(not (= ";
953             printSort(term[0].getType(), os);
954             os << " ";
955             printBoundTerm(term[0], os, map);
956             os << " ";
957             printBoundTerm(term[1], os, map);
958             os << "))";
959           }
960         }
961       }
962     }
963 
964     return;
965 
966   case kind::CHAIN: {
967     // LFSC doesn't allow declarations with variable numbers of
968     // arguments, so we have to flatten chained operators, like =.
969     Kind op = term.getOperator().getConst<Chain>().getOperator();
970     std::string op_str;
971     bool booleanCase = false;
972     if (op==kind::EQUAL && term[0].getType().isBoolean()) {
973       booleanCase = term[0].getType().isBoolean();
974       op_str = "iff";
975     } else {
976       op_str = utils::toLFSCKind(op);
977     }
978     size_t n = term.getNumChildren();
979     std::ostringstream paren;
980     for(size_t i = 1; i < n; ++i) {
981       if(i + 1 < n) {
982         os << "(" << utils::toLFSCKind(kind::AND) << " ";
983         paren << ")";
984       }
985       os << "(" << op_str << " ";
986       if (booleanCase && printsAsBool(term[i - 1])) os << "(p_app ";
987       printBoundTerm(term[i - 1], os, map);
988       if (booleanCase && printsAsBool(term[i - 1])) os << ")";
989       os << " ";
990       if (booleanCase && printsAsBool(term[i])) os << "(p_app ";
991       printBoundTerm(term[i], os, map);
992       if (booleanCase && printsAsBool(term[i])) os << ")";
993       os << ")";
994       if(i + 1 < n) {
995         os << " ";
996       }
997     }
998     os << paren.str();
999     return;
1000   }
1001 
1002   default:
1003     Unhandled(k);
1004   }
1005 
1006 }
1007 
printTheoryLemmaProof(std::vector<Expr> & lemma,std::ostream & os,std::ostream & paren,const ProofLetMap & map)1008 void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma,
1009                                         std::ostream& os,
1010                                         std::ostream& paren,
1011                                         const ProofLetMap& map) {
1012   // Default method for replaying proofs: assert (negated) literals back to a fresh copy of the theory
1013   Assert(d_theory!=NULL);
1014 
1015   context::UserContext fakeContext;
1016   ProofOutputChannel oc;
1017   theory::Valuation v(NULL);
1018   //make new copy of theory
1019   theory::Theory* th;
1020   Trace("pf::tp") << ";; Print theory lemma proof, theory id = " << d_theory->getId() << std::endl;
1021 
1022   if (d_theory->getId()==theory::THEORY_UF) {
1023     th = new theory::uf::TheoryUF(&fakeContext, &fakeContext, oc, v,
1024                                   ProofManager::currentPM()->getLogicInfo(),
1025                                   "replay::");
1026   } else if (d_theory->getId()==theory::THEORY_ARRAYS) {
1027     th = new theory::arrays::TheoryArrays(&fakeContext, &fakeContext, oc, v,
1028                                           ProofManager::currentPM()->getLogicInfo(),
1029                                           "replay::");
1030   } else if (d_theory->getId() == theory::THEORY_ARITH) {
1031     Trace("theory-proof-debug") << "Arith proofs currently not supported. Use 'trust'" << std::endl;
1032     os << " (clausify_false trust)";
1033     return;
1034   } else {
1035     InternalError(std::string("can't generate theory-proof for ") + ProofManager::currentPM()->getLogic());
1036   }
1037 
1038   Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->ProduceProofs()" << std::endl;
1039   th->produceProofs();
1040   Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->ProduceProofs() DONE" << std::endl;
1041 
1042   MyPreRegisterVisitor preRegVisitor(th);
1043   for (unsigned i=0; i<lemma.size(); i++) {
1044     Node strippedLit = (lemma[i].getKind() == kind::NOT) ? lemma[i][0] : lemma[i];
1045     if (strippedLit.getKind() == kind::EQUAL ||
1046         d_theory->getId() == theory::Theory::theoryOf(strippedLit)) {
1047       Node lit = Node::fromExpr( lemma[i] ).negate();
1048       Trace("pf::tp") << "; preregistering and asserting " << lit << std::endl;
1049       NodeVisitor<MyPreRegisterVisitor>::run(preRegVisitor, lit);
1050       th->assertFact(lit, false);
1051     }
1052   }
1053 
1054   Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->check()" << std::endl;
1055   th->check(theory::Theory::EFFORT_FULL);
1056   Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->check() DONE" << std::endl;
1057 
1058   if(!oc.hasConflict()) {
1059     Trace("pf::tp") << "; conflict is null" << std::endl;
1060     Node lastLemma  = oc.getLastLemma();
1061     Assert(!lastLemma.isNull());
1062     Trace("pf::tp") << "; ++ but got lemma: " << lastLemma << std::endl;
1063 
1064     if (lastLemma.getKind() == kind::OR) {
1065       Debug("pf::tp") << "OR lemma. Negating each child separately" << std::endl;
1066       for (unsigned i = 0; i < lastLemma.getNumChildren(); ++i) {
1067         if (lastLemma[i].getKind() == kind::NOT) {
1068           Trace("pf::tp") << ";     asserting fact: " << lastLemma[i][0] << std::endl;
1069           th->assertFact(lastLemma[i][0], false);
1070         }
1071         else {
1072           Trace("pf::tp") << ";     asserting fact: " << lastLemma[i].notNode() << std::endl;
1073           th->assertFact(lastLemma[i].notNode(), false);
1074         }
1075       }
1076     } else {
1077       Unreachable();
1078 
1079       Assert(oc.getLastLemma().getKind() == kind::NOT);
1080       Debug("pf::tp") << "NOT lemma" << std::endl;
1081       Trace("pf::tp") << ";     asserting fact: " << oc.getLastLemma()[0]
1082                       << std::endl;
1083       th->assertFact(oc.getLastLemma()[0], false);
1084     }
1085 
1086     // Trace("pf::tp") << "; ++ but got lemma: " << oc.d_lemma << std::endl;
1087     // Trace("pf::tp") << "; asserting " << oc.d_lemma[1].negate() << std::endl;
1088     // th->assertFact(oc.d_lemma[1].negate(), false);
1089 
1090     //
1091     th->check(theory::Theory::EFFORT_FULL);
1092   } else {
1093     Debug("pf::tp") << "Calling   oc.d_proof->toStream(os)" << std::endl;
1094     oc.getConflictProof().toStream(os, map);
1095     Debug("pf::tp") << "Calling   oc.d_proof->toStream(os) -- DONE!" << std::endl;
1096   }
1097 
1098   Debug("pf::tp") << "About to delete the theory solver used for proving the lemma... " << std::endl;
1099   delete th;
1100   Debug("pf::tp") << "About to delete the theory solver used for proving the lemma: DONE! " << std::endl;
1101 }
1102 
supportedTheory(theory::TheoryId id)1103 bool TheoryProofEngine::supportedTheory(theory::TheoryId id) {
1104   return (id == theory::THEORY_ARRAYS ||
1105           id == theory::THEORY_ARITH ||
1106           id == theory::THEORY_BV ||
1107           id == theory::THEORY_UF ||
1108           id == theory::THEORY_BOOL);
1109 }
1110 
printsAsBool(const Node & n)1111 bool TheoryProofEngine::printsAsBool(const Node &n) {
1112   if (!n.getType().isBoolean()) {
1113     return false;
1114   }
1115 
1116   theory::TheoryId theory_id = theory::Theory::theoryOf(n);
1117   return getTheoryProof(theory_id)->printsAsBool(n);
1118 }
1119 
BooleanProof(TheoryProofEngine * proofEngine)1120 BooleanProof::BooleanProof(TheoryProofEngine* proofEngine)
1121   : TheoryProof(NULL, proofEngine)
1122 {}
1123 
registerTerm(Expr term)1124 void BooleanProof::registerTerm(Expr term) {
1125   Assert (term.getType().isBoolean());
1126 
1127   if (term.isVariable() && d_declarations.find(term) == d_declarations.end()) {
1128     d_declarations.insert(term);
1129     return;
1130   }
1131   for (unsigned i = 0; i < term.getNumChildren(); ++i) {
1132     d_proofEngine->registerTerm(term[i]);
1133   }
1134 }
1135 
getTheoryId()1136 theory::TheoryId BooleanProof::getTheoryId() { return theory::THEORY_BOOL; }
printConstantDisequalityProof(std::ostream & os,Expr c1,Expr c2,const ProofLetMap & globalLetMap)1137 void LFSCBooleanProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) {
1138   Node falseNode = NodeManager::currentNM()->mkConst(false);
1139   Node trueNode = NodeManager::currentNM()->mkConst(true);
1140 
1141   Assert(c1 == falseNode.toExpr() || c1 == trueNode.toExpr());
1142   Assert(c2 == falseNode.toExpr() || c2 == trueNode.toExpr());
1143   Assert(c1 != c2);
1144 
1145   if (c1 == trueNode.toExpr())
1146     os << "t_t_neq_f";
1147   else
1148     os << "(negsymm _ _ _ t_t_neq_f)";
1149 }
1150 
printOwnedTerm(Expr term,std::ostream & os,const ProofLetMap & map)1151 void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) {
1152   Assert (term.getType().isBoolean());
1153   if (term.isVariable()) {
1154     os << ProofManager::sanitize(term);
1155     return;
1156   }
1157 
1158   Kind k = term.getKind();
1159   switch(k) {
1160   case kind::OR:
1161   case kind::AND:
1162     if (options::lfscLetification() && term.getNumChildren() > 2) {
1163       // If letification is on, the entire term is probably a let expression.
1164       // However, we need to transform it from (and a b c) into (and a (and b c)) form first.
1165       Node currentExpression = term[term.getNumChildren() - 1];
1166       for (int i = term.getNumChildren() - 2; i >= 0; --i) {
1167         NodeBuilder<> builder(k);
1168         builder << term[i];
1169         builder << currentExpression.toExpr();
1170         currentExpression = builder;
1171       }
1172 
1173       // The let map should already have the current expression.
1174       ProofLetMap::const_iterator it = map.find(currentExpression.toExpr());
1175       if (it != map.end()) {
1176         unsigned id = it->second.id;
1177         unsigned count = it->second.count;
1178 
1179         if (count > LET_COUNT) {
1180           os << "let" << id;
1181           break;
1182         }
1183       }
1184     }
1185 
1186     // If letification is off or there were 2 children, same treatment as the other cases.
1187     // (No break is intentional).
1188   case kind::XOR:
1189   case kind::IMPLIES:
1190   case kind::NOT:
1191     // print the Boolean operators
1192     os << "(" << utils::toLFSCKind(k);
1193     if(term.getNumChildren() > 2) {
1194       // LFSC doesn't allow declarations with variable numbers of
1195       // arguments, so we have to flatten these N-ary versions.
1196       std::ostringstream paren;
1197       os << " ";
1198       for (unsigned i = 0; i < term.getNumChildren(); ++i) {
1199 
1200         if (printsAsBool(term[i])) os << "(p_app ";
1201         d_proofEngine->printBoundTerm(term[i], os, map);
1202         if (printsAsBool(term[i])) os << ")";
1203 
1204         os << " ";
1205         if(i < term.getNumChildren() - 2) {
1206           os << "(" << utils::toLFSCKind(k) << " ";
1207           paren << ")";
1208         }
1209       }
1210       os << paren.str() << ")";
1211     } else {
1212       // this is for binary and unary operators
1213       for (unsigned i = 0; i < term.getNumChildren(); ++i) {
1214         os << " ";
1215         if (printsAsBool(term[i])) os << "(p_app ";
1216         d_proofEngine->printBoundTerm(term[i], os, map);
1217         if (printsAsBool(term[i])) os << ")";
1218       }
1219       os << ")";
1220     }
1221     return;
1222 
1223   case kind::CONST_BOOLEAN:
1224     os << (term.getConst<bool>() ? "true" : "false");
1225     return;
1226 
1227   default:
1228     Unhandled(k);
1229   }
1230 
1231 }
1232 
printOwnedSort(Type type,std::ostream & os)1233 void LFSCBooleanProof::printOwnedSort(Type type, std::ostream& os) {
1234   Assert (type.isBoolean());
1235   os << "Bool";
1236 }
1237 
printSortDeclarations(std::ostream & os,std::ostream & paren)1238 void LFSCBooleanProof::printSortDeclarations(std::ostream& os, std::ostream& paren) {
1239   // Nothing to do here at this point.
1240 }
1241 
printTermDeclarations(std::ostream & os,std::ostream & paren)1242 void LFSCBooleanProof::printTermDeclarations(std::ostream& os, std::ostream& paren) {
1243   for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) {
1244     Expr term = *it;
1245 
1246     os << "(% " << ProofManager::sanitize(term) << " (term ";
1247     printSort(term.getType(), os);
1248     os <<")\n";
1249     paren <<")";
1250   }
1251 }
1252 
printDeferredDeclarations(std::ostream & os,std::ostream & paren)1253 void LFSCBooleanProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) {
1254   // Nothing to do here at this point.
1255 }
1256 
printAliasingDeclarations(std::ostream & os,std::ostream & paren,const ProofLetMap & globalLetMap)1257 void LFSCBooleanProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) {
1258   // Nothing to do here at this point.
1259 }
1260 
printTheoryLemmaProof(std::vector<Expr> & lemma,std::ostream & os,std::ostream & paren,const ProofLetMap & map)1261 void LFSCBooleanProof::printTheoryLemmaProof(std::vector<Expr>& lemma,
1262                                              std::ostream& os,
1263                                              std::ostream& paren,
1264                                              const ProofLetMap& map) {
1265   Unreachable("No boolean lemmas yet!");
1266 }
1267 
printsAsBool(const Node & n)1268 bool LFSCBooleanProof::printsAsBool(const Node &n)
1269 {
1270   Kind k = n.getKind();
1271   switch (k) {
1272   case kind::BOOLEAN_TERM_VARIABLE:
1273   case kind::VARIABLE:
1274     return true;
1275 
1276   default:
1277     return false;
1278   }
1279 }
1280 
printConstantDisequalityProof(std::ostream & os,Expr c1,Expr c2,const ProofLetMap & globalLetMap)1281 void TheoryProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) {
1282   // By default, we just print a trust statement. Specific theories can implement
1283   // better proofs.
1284 
1285   os << "(trust_f (not (= _ ";
1286   d_proofEngine->printBoundTerm(c1, os, globalLetMap);
1287   os << " ";
1288   d_proofEngine->printBoundTerm(c2, os, globalLetMap);
1289   os << ")))";
1290 }
1291 
printRewriteProof(std::ostream & os,const Node & n1,const Node & n2)1292 void TheoryProof::printRewriteProof(std::ostream& os, const Node &n1, const Node &n2) {
1293   // This is the default for a rewrite proof: just a trust statement.
1294   ProofLetMap emptyMap;
1295   os << "(trust_f (iff ";
1296   d_proofEngine->printBoundTerm(n1.toExpr(), os, emptyMap);
1297   os << " ";
1298   d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap);
1299   os << "))";
1300 }
1301 
match(TNode n1,TNode n2)1302 bool TheoryProof::match(TNode n1, TNode n2)
1303 {
1304   theory::TheoryId theoryId = this->getTheoryId();
1305   ProofManager* pm = ProofManager::currentPM();
1306   bool ufProof = (theoryId == theory::THEORY_UF);
1307   Debug(ufProof ? "pf::uf" : "mgd") << "match " << n1 << " " << n2 << std::endl;
1308   if (pm->hasOp(n1))
1309   {
1310     n1 = pm->lookupOp(n1);
1311   }
1312   if (pm->hasOp(n2))
1313   {
1314     n2 = pm->lookupOp(n2);
1315   }
1316   Debug(ufProof ? "pf::uf" : "mgd") << "+ match " << n1 << " " << n2
1317                                     << std::endl;
1318   if (!ufProof)
1319   {
1320     Debug("pf::array") << "+ match: step 1" << std::endl;
1321   }
1322   if (n1 == n2)
1323   {
1324     return true;
1325   }
1326 
1327   if (n1.getType().isFunction() && n2.hasOperator())
1328   {
1329     if (pm->hasOp(n2.getOperator()))
1330     {
1331       return n1 == pm->lookupOp(n2.getOperator());
1332     }
1333     else
1334     {
1335       return n1 == n2.getOperator();
1336     }
1337   }
1338 
1339   if (n2.getType().isFunction() && n1.hasOperator())
1340   {
1341     if (pm->hasOp(n1.getOperator()))
1342     {
1343       return n2 == pm->lookupOp(n1.getOperator());
1344     }
1345     else
1346     {
1347       return n2 == n1.getOperator();
1348     }
1349   }
1350 
1351   if (n1.hasOperator() && n2.hasOperator()
1352       && n1.getOperator() != n2.getOperator())
1353   {
1354     if (ufProof
1355         || !((n1.getKind() == kind::SELECT
1356               && n2.getKind() == kind::PARTIAL_SELECT_0)
1357              || (n1.getKind() == kind::SELECT
1358                  && n2.getKind() == kind::PARTIAL_SELECT_1)
1359              || (n1.getKind() == kind::PARTIAL_SELECT_1
1360                  && n2.getKind() == kind::SELECT)
1361              || (n1.getKind() == kind::PARTIAL_SELECT_1
1362                  && n2.getKind() == kind::PARTIAL_SELECT_0)
1363              || (n1.getKind() == kind::PARTIAL_SELECT_0
1364                  && n2.getKind() == kind::SELECT)
1365              || (n1.getKind() == kind::PARTIAL_SELECT_0
1366                  && n2.getKind() == kind::PARTIAL_SELECT_1)))
1367     {
1368       return false;
1369     }
1370   }
1371 
1372   for (size_t i = 0; i < n1.getNumChildren() && i < n2.getNumChildren(); ++i)
1373   {
1374     if (n1[i] != n2[i])
1375     {
1376       return false;
1377     }
1378   }
1379 
1380   return true;
1381 }
1382 
assertAndPrint(const theory::eq::EqProof & pf,const ProofLetMap & map,std::shared_ptr<theory::eq::EqProof> subTrans,theory::eq::EqProof::PrettyPrinter * pPrettyPrinter)1383 int TheoryProof::assertAndPrint(
1384     const theory::eq::EqProof& pf,
1385     const ProofLetMap& map,
1386     std::shared_ptr<theory::eq::EqProof> subTrans,
1387     theory::eq::EqProof::PrettyPrinter* pPrettyPrinter)
1388 {
1389   theory::TheoryId theoryId = getTheoryId();
1390   int neg = -1;
1391   Assert(theoryId == theory::THEORY_UF || theoryId == theory::THEORY_ARRAYS);
1392   bool ufProof = (theoryId == theory::THEORY_UF);
1393   std::string theoryName = theory::getStatsPrefix(theoryId);
1394   pf.debug_print(("pf::" + theoryName).c_str(), 0, pPrettyPrinter);
1395   Debug("pf::" + theoryName) << std::endl;
1396 
1397   Assert(pf.d_id == theory::eq::MERGED_THROUGH_TRANS);
1398   Assert(!pf.d_node.isNull());
1399   Assert(pf.d_children.size() >= 2);
1400 
1401   subTrans->d_id = theory::eq::MERGED_THROUGH_TRANS;
1402   subTrans->d_node = pf.d_node;
1403 
1404   size_t i = 0;
1405   while (i < pf.d_children.size())
1406   {
1407     // special treatment for uf and not for array
1408     if (ufProof
1409         || pf.d_children[i]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE)
1410     {
1411       pf.d_children[i]->d_node = simplifyBooleanNode(pf.d_children[i]->d_node);
1412     }
1413 
1414     // Look for the negative clause, with which we will form a contradiction.
1415     if (!pf.d_children[i]->d_node.isNull()
1416         && pf.d_children[i]->d_node.getKind() == kind::NOT)
1417     {
1418       Assert(neg < 0);
1419       (neg) = i;
1420       ++i;
1421     }
1422 
1423     // Handle congruence closures over equalities.
1424     else if (pf.d_children[i]->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE
1425              && pf.d_children[i]->d_node.isNull())
1426     {
1427       Debug("pf::" + theoryName) << "Handling congruence over equalities"
1428                                  << std::endl;
1429 
1430       // Gather the sequence of consecutive congruence closures.
1431       std::vector<std::shared_ptr<const theory::eq::EqProof>>
1432           congruenceClosures;
1433       unsigned count;
1434       Debug("pf::" + theoryName) << "Collecting congruence sequence"
1435                                  << std::endl;
1436       for (count = 0; i + count < pf.d_children.size()
1437                       && pf.d_children[i + count]->d_id
1438                              == theory::eq::MERGED_THROUGH_CONGRUENCE
1439                       && pf.d_children[i + count]->d_node.isNull();
1440            ++count)
1441       {
1442         Debug("pf::" + theoryName) << "Found a congruence: " << std::endl;
1443         pf.d_children[i + count]->debug_print(
1444             ("pf::" + theoryName).c_str(), 0, pPrettyPrinter);
1445         congruenceClosures.push_back(pf.d_children[i + count]);
1446       }
1447 
1448       Debug("pf::" + theoryName)
1449           << "Total number of congruences found: " << congruenceClosures.size()
1450           << std::endl;
1451 
1452       // Determine if the "target" of the congruence sequence appears right
1453       // before or right after the sequence.
1454       bool targetAppearsBefore = true;
1455       bool targetAppearsAfter = true;
1456 
1457       if ((i == 0) || (i == 1 && neg == 0))
1458       {
1459         Debug("pf::" + theoryName) << "Target does not appear before"
1460                                    << std::endl;
1461         targetAppearsBefore = false;
1462       }
1463 
1464       if ((i + count >= pf.d_children.size())
1465           || (!pf.d_children[i + count]->d_node.isNull()
1466               && pf.d_children[i + count]->d_node.getKind() == kind::NOT))
1467       {
1468         Debug("pf::" + theoryName) << "Target does not appear after"
1469                                    << std::endl;
1470         targetAppearsAfter = false;
1471       }
1472 
1473       // Flow changes between uf and array
1474       if (ufProof)
1475       {
1476         // Assert that we have precisely at least one possible clause.
1477         Assert(targetAppearsBefore || targetAppearsAfter);
1478 
1479         // If both are valid, assume the one after the sequence is correct
1480         if (targetAppearsAfter && targetAppearsBefore)
1481         {
1482           targetAppearsBefore = false;
1483         }
1484       }
1485       else
1486       {  // not a uf proof
1487         // Assert that we have precisely one target clause.
1488         Assert(targetAppearsBefore != targetAppearsAfter);
1489       }
1490 
1491       // Begin breaking up the congruences and ordering the equalities
1492       // correctly.
1493       std::vector<std::shared_ptr<theory::eq::EqProof>> orderedEqualities;
1494 
1495       // Insert target clause first.
1496       if (targetAppearsBefore)
1497       {
1498         orderedEqualities.push_back(pf.d_children[i - 1]);
1499         // The target has already been added to subTrans; remove it.
1500         subTrans->d_children.pop_back();
1501       }
1502       else
1503       {
1504         orderedEqualities.push_back(pf.d_children[i + count]);
1505       }
1506 
1507       // Start with the congruence closure closest to the target clause, and
1508       // work our way back/forward.
1509       if (targetAppearsBefore)
1510       {
1511         for (unsigned j = 0; j < count; ++j)
1512         {
1513           if (pf.d_children[i + j]->d_children[0]->d_id
1514               != theory::eq::MERGED_THROUGH_REFLEXIVITY)
1515             orderedEqualities.insert(orderedEqualities.begin(),
1516                                      pf.d_children[i + j]->d_children[0]);
1517           if (pf.d_children[i + j]->d_children[1]->d_id
1518               != theory::eq::MERGED_THROUGH_REFLEXIVITY)
1519             orderedEqualities.insert(orderedEqualities.end(),
1520                                      pf.d_children[i + j]->d_children[1]);
1521         }
1522       }
1523       else
1524       {
1525         for (unsigned j = 0; j < count; ++j)
1526         {
1527           if (pf.d_children[i + count - 1 - j]->d_children[0]->d_id
1528               != theory::eq::MERGED_THROUGH_REFLEXIVITY)
1529             orderedEqualities.insert(
1530                 orderedEqualities.begin(),
1531                 pf.d_children[i + count - 1 - j]->d_children[0]);
1532           if (pf.d_children[i + count - 1 - j]->d_children[1]->d_id
1533               != theory::eq::MERGED_THROUGH_REFLEXIVITY)
1534             orderedEqualities.insert(
1535                 orderedEqualities.end(),
1536                 pf.d_children[i + count - 1 - j]->d_children[1]);
1537         }
1538       }
1539 
1540       // Copy the result into the main transitivity proof.
1541       subTrans->d_children.insert(subTrans->d_children.end(),
1542                                   orderedEqualities.begin(),
1543                                   orderedEqualities.end());
1544 
1545       // Increase i to skip over the children that have been processed.
1546       i += count;
1547       if (targetAppearsAfter)
1548       {
1549         ++i;
1550       }
1551     }
1552 
1553     // Else, just copy the child proof as is
1554     else
1555     {
1556       subTrans->d_children.push_back(pf.d_children[i]);
1557       ++i;
1558     }
1559   }
1560 
1561   bool disequalityFound = (neg >= 0);
1562   if (!disequalityFound)
1563   {
1564     Debug("pf::" + theoryName)
1565         << "A disequality was NOT found. UNSAT due to merged constants"
1566         << std::endl;
1567     Debug("pf::" + theoryName) << "Proof for: " << pf.d_node << std::endl;
1568     Assert(pf.d_node.getKind() == kind::EQUAL);
1569     Assert(pf.d_node.getNumChildren() == 2);
1570     Assert(pf.d_node[0].isConst() && pf.d_node[1].isConst());
1571   }
1572   return neg;
1573 }
1574 
identicalEqualitiesPrinterHelper(bool evenLengthSequence,bool sequenceOver,const theory::eq::EqProof & pf,const ProofLetMap & map,const std::string subproofStr,std::stringstream * outStream,Node n,Node nodeAfterEqualitySequence)1575 std::pair<Node, Node> TheoryProof::identicalEqualitiesPrinterHelper(
1576     bool evenLengthSequence,
1577     bool sequenceOver,
1578     const theory::eq::EqProof& pf,
1579     const ProofLetMap& map,
1580     const std::string subproofStr,
1581     std::stringstream* outStream,
1582     Node n,
1583     Node nodeAfterEqualitySequence)
1584 {
1585   theory::TheoryId theoryId = getTheoryId();
1586   Assert(theoryId == theory::THEORY_UF || theoryId == theory::THEORY_ARRAYS);
1587   bool ufProof = (theoryId == theory::THEORY_UF);
1588   std::string theoryName = theory::getStatsPrefix(theoryId);
1589   if (evenLengthSequence)
1590   {
1591     // If the length is even, we need to apply transitivity for the "correct"
1592     // hand of the equality.
1593 
1594     Debug("pf::" + theoryName) << "Equality sequence of even length"
1595                                << std::endl;
1596     Debug("pf::" + theoryName) << "n1 is: " << n << std::endl;
1597     Debug("pf::" + theoryName) << "pf-d_node is: " << pf.d_node << std::endl;
1598     Debug("pf::" + theoryName) << "Next node is: " << nodeAfterEqualitySequence
1599                                << std::endl;
1600 
1601     (*outStream) << "(trans _ _ _ _ ";
1602 
1603     // If the sequence is at the very end of the transitivity proof, use
1604     // pf.d_node to guide us.
1605     if (!sequenceOver)
1606     {
1607       if (match(n[0], pf.d_node[0]))
1608       {
1609         n = n[0].eqNode(n[0]);
1610         (*outStream) << subproofStr << " (symm _ _ _ " << subproofStr << ")";
1611       }
1612       else if (match(n[1], pf.d_node[1]))
1613       {
1614         n = n[1].eqNode(n[1]);
1615         (*outStream) << " (symm _ _ _ " << subproofStr << ")" << subproofStr;
1616       }
1617       else
1618       {
1619         Debug("pf::" + theoryName) << "Error: identical equalities over, but "
1620                                       "hands don't match what we're proving."
1621                                    << std::endl;
1622         Assert(false);
1623       }
1624     }
1625     else
1626     {
1627       // We have a "next node". Use it to guide us.
1628       if (!ufProof && nodeAfterEqualitySequence.getKind() == kind::NOT)
1629       {
1630         nodeAfterEqualitySequence = nodeAfterEqualitySequence[0];
1631       }
1632 
1633       Assert(nodeAfterEqualitySequence.getKind() == kind::EQUAL);
1634 
1635       if ((n[0] == nodeAfterEqualitySequence[0])
1636           || (n[0] == nodeAfterEqualitySequence[1]))
1637       {
1638         // Eliminate n[1]
1639         (*outStream) << subproofStr << " (symm _ _ _ " << subproofStr << ")";
1640         n = n[0].eqNode(n[0]);
1641       }
1642       else if ((n[1] == nodeAfterEqualitySequence[0])
1643                || (n[1] == nodeAfterEqualitySequence[1]))
1644       {
1645         // Eliminate n[0]
1646         (*outStream) << " (symm _ _ _ " << subproofStr << ")" << subproofStr;
1647         n = n[1].eqNode(n[1]);
1648       }
1649       else
1650       {
1651         Debug("pf::" + theoryName) << "Error: even length sequence, but I "
1652                                       "don't know which hand to keep!"
1653                                    << std::endl;
1654         Assert(false);
1655       }
1656     }
1657 
1658     (*outStream) << ")";
1659   }
1660   else
1661   {
1662     Debug("pf::" + theoryName) << "Equality sequence length is odd!"
1663                                << std::endl;
1664     (*outStream).str(subproofStr);
1665   }
1666 
1667   Debug("pf::" + theoryName) << "Have proven: " << n << std::endl;
1668   return std::make_pair<Node&, Node&>(n, nodeAfterEqualitySequence);
1669 }
1670 
1671 } /* namespace CVC4 */
1672