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