Connect the relevance manager to TheoryEngine and use it in non-linear arithmetic...
[cvc5.git] / src / theory / theory_engine.cpp
1 /********************* */
2 /*! \file theory_engine.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Dejan Jovanovic, Andrew Reynolds, Morgan Deters
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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 The theory engine
13 **
14 ** The theory engine.
15 **/
16
17 #include "theory/theory_engine.h"
18
19 #include <list>
20 #include <vector>
21
22 #include "base/map_util.h"
23 #include "decision/decision_engine.h"
24 #include "expr/attribute.h"
25 #include "expr/node.h"
26 #include "expr/node_algorithm.h"
27 #include "expr/node_builder.h"
28 #include "expr/node_visitor.h"
29 #include "options/bv_options.h"
30 #include "options/options.h"
31 #include "options/proof_options.h"
32 #include "options/quantifiers_options.h"
33 #include "options/theory_options.h"
34 #include "preprocessing/assertion_pipeline.h"
35 #include "proof/cnf_proof.h"
36 #include "proof/lemma_proof.h"
37 #include "proof/proof_manager.h"
38 #include "proof/theory_proof.h"
39 #include "smt/logic_exception.h"
40 #include "smt/term_formula_removal.h"
41 #include "theory/arith/arith_ite_utils.h"
42 #include "theory/bv/theory_bv_utils.h"
43 #include "theory/care_graph.h"
44 #include "theory/decision_manager.h"
45 #include "theory/ee_manager_distributed.h"
46 #include "theory/quantifiers/first_order_model.h"
47 #include "theory/quantifiers/fmf/model_engine.h"
48 #include "theory/quantifiers/theory_quantifiers.h"
49 #include "theory/quantifiers_engine.h"
50 #include "theory/relevance_manager.h"
51 #include "theory/rewriter.h"
52 #include "theory/theory.h"
53 #include "theory/theory_model.h"
54 #include "theory/theory_traits.h"
55 #include "theory/uf/equality_engine.h"
56 #include "util/resource_manager.h"
57
58 using namespace std;
59
60 using namespace CVC4::preprocessing;
61 using namespace CVC4::theory;
62
63 namespace CVC4 {
64
65 /* -------------------------------------------------------------------------- */
66
67 namespace theory {
68
69 /**
70 * IMPORTANT: The order of the theories is important. For example, strings
71 * depends on arith, quantifiers needs to come as the very last.
72 * Do not change this order.
73 */
74
75 #define CVC4_FOR_EACH_THEORY \
76 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_BUILTIN) \
77 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_BOOL) \
78 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_UF) \
79 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_ARITH) \
80 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_BV) \
81 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_FP) \
82 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_ARRAYS) \
83 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_DATATYPES) \
84 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_SEP) \
85 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_SETS) \
86 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_STRINGS) \
87 CVC4_FOR_EACH_THEORY_STATEMENT(CVC4::theory::THEORY_QUANTIFIERS)
88
89 } // namespace theory
90
91 /* -------------------------------------------------------------------------- */
92
93 inline void flattenAnd(Node n, std::vector<TNode>& out){
94 Assert(n.getKind() == kind::AND);
95 for(Node::iterator i=n.begin(), i_end=n.end(); i != i_end; ++i){
96 Node curr = *i;
97 if(curr.getKind() == kind::AND){
98 flattenAnd(curr, out);
99 }else{
100 out.push_back(curr);
101 }
102 }
103 }
104
105 inline Node flattenAnd(Node n){
106 std::vector<TNode> out;
107 flattenAnd(n, out);
108 return NodeManager::currentNM()->mkNode(kind::AND, out);
109 }
110
111 /**
112 * Compute the string for a given theory id. In this module, we use
113 * THEORY_SAT_SOLVER as an id, which is not a normal id but maps to
114 * THEORY_LAST. Thus, we need our own string conversion here.
115 *
116 * @param id The theory id
117 * @return The string corresponding to the theory id
118 */
119 std::string getTheoryString(theory::TheoryId id)
120 {
121 if (id == theory::THEORY_SAT_SOLVER)
122 {
123 return "THEORY_SAT_SOLVER";
124 }
125 else
126 {
127 std::stringstream ss;
128 ss << id;
129 return ss.str();
130 }
131 }
132
133 void TheoryEngine::finishInit() {
134 // initialize the quantifiers engine
135 if (d_logicInfo.isQuantified())
136 {
137 // initialize the quantifiers engine
138 d_quantEngine = new QuantifiersEngine(d_context, d_userContext, this);
139 }
140
141 // Initialize the equality engine architecture for all theories, which
142 // includes the master equality engine.
143 d_eeDistributed.reset(new EqEngineManagerDistributed(*this));
144 d_eeDistributed->finishInit();
145
146 // Initialize the model and model builder.
147 if (d_logicInfo.isQuantified())
148 {
149 d_curr_model_builder = d_quantEngine->getModelBuilder();
150 d_curr_model = d_quantEngine->getModel();
151 } else {
152 d_curr_model = new theory::TheoryModel(
153 d_userContext, "DefaultModel", options::assignFunctionValues());
154 d_aloc_curr_model = true;
155 }
156 // create the relevance filter if any option requires it
157 if (options::relevanceFilter())
158 {
159 d_relManager.reset(
160 new RelevanceManager(d_userContext, theory::Valuation(this)));
161 }
162
163 //make the default builder, e.g. in the case that the quantifiers engine does not have a model builder
164 if( d_curr_model_builder==NULL ){
165 d_curr_model_builder = new theory::TheoryEngineModelBuilder(this);
166 d_aloc_curr_model_builder = true;
167 }
168
169 // set the core equality engine on quantifiers engine
170 if (d_logicInfo.isQuantified())
171 {
172 d_quantEngine->setMasterEqualityEngine(
173 d_eeDistributed->getMasterEqualityEngine());
174 }
175
176 // finish initializing the theories
177 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId != theory::THEORY_LAST; ++ theoryId) {
178 Theory* t = d_theoryTable[theoryId];
179 if (t == nullptr)
180 {
181 continue;
182 }
183 // setup the pointers to the utilities
184 const EeTheoryInfo* eeti = d_eeDistributed->getEeTheoryInfo(theoryId);
185 Assert(eeti != nullptr);
186 // the theory's official equality engine is the one specified by the
187 // equality engine manager
188 t->setEqualityEngine(eeti->d_usedEe);
189 // set the quantifiers engine
190 t->setQuantifiersEngine(d_quantEngine);
191 // set the decision manager for the theory
192 t->setDecisionManager(d_decManager.get());
193 // finish initializing the theory
194 t->finishInit();
195 }
196 }
197
198 TheoryEngine::TheoryEngine(context::Context* context,
199 context::UserContext* userContext,
200 ResourceManager* rm,
201 RemoveTermFormulas& iteRemover,
202 const LogicInfo& logicInfo)
203 : d_propEngine(nullptr),
204 d_context(context),
205 d_userContext(userContext),
206 d_logicInfo(logicInfo),
207 d_sharedTerms(this, context),
208 d_eeDistributed(nullptr),
209 d_quantEngine(nullptr),
210 d_decManager(new DecisionManager(userContext)),
211 d_relManager(nullptr),
212 d_curr_model(nullptr),
213 d_aloc_curr_model(false),
214 d_curr_model_builder(nullptr),
215 d_aloc_curr_model_builder(false),
216 d_eager_model_building(false),
217 d_possiblePropagations(context),
218 d_hasPropagated(context),
219 d_inConflict(context, false),
220 d_inSatMode(false),
221 d_hasShutDown(false),
222 d_incomplete(context, false),
223 d_propagationMap(context),
224 d_propagationMapTimestamp(context, 0),
225 d_propagatedLiterals(context),
226 d_propagatedLiteralsIndex(context, 0),
227 d_atomRequests(context),
228 d_tpp(*this, iteRemover),
229 d_combineTheoriesTime("TheoryEngine::combineTheoriesTime"),
230 d_true(),
231 d_false(),
232 d_interrupted(false),
233 d_resourceManager(rm),
234 d_inPreregister(false),
235 d_factsAsserted(context, false),
236 d_preRegistrationVisitor(this, context),
237 d_sharedTermsVisitor(d_sharedTerms),
238 d_attr_handle(),
239 d_arithSubstitutionsAdded("theory::arith::zzz::arith::substitutions", 0)
240 {
241 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId != theory::THEORY_LAST;
242 ++ theoryId)
243 {
244 d_theoryTable[theoryId] = NULL;
245 d_theoryOut[theoryId] = NULL;
246 }
247
248 smtStatisticsRegistry()->registerStat(&d_combineTheoriesTime);
249 d_true = NodeManager::currentNM()->mkConst<bool>(true);
250 d_false = NodeManager::currentNM()->mkConst<bool>(false);
251
252 #ifdef CVC4_PROOF
253 ProofManager::currentPM()->initTheoryProofEngine();
254 #endif
255
256 smtStatisticsRegistry()->registerStat(&d_arithSubstitutionsAdded);
257 }
258
259 TheoryEngine::~TheoryEngine() {
260 Assert(d_hasShutDown);
261
262 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId != theory::THEORY_LAST; ++ theoryId) {
263 if(d_theoryTable[theoryId] != NULL) {
264 delete d_theoryTable[theoryId];
265 delete d_theoryOut[theoryId];
266 }
267 }
268
269 if( d_aloc_curr_model_builder ){
270 delete d_curr_model_builder;
271 }
272 if( d_aloc_curr_model ){
273 delete d_curr_model;
274 }
275
276 delete d_quantEngine;
277
278 smtStatisticsRegistry()->unregisterStat(&d_combineTheoriesTime);
279 smtStatisticsRegistry()->unregisterStat(&d_arithSubstitutionsAdded);
280 }
281
282 void TheoryEngine::interrupt() { d_interrupted = true; }
283 void TheoryEngine::preRegister(TNode preprocessed) {
284
285 Debug("theory") << "TheoryEngine::preRegister( " << preprocessed << ")" << std::endl;
286 if(Dump.isOn("missed-t-propagations")) {
287 d_possiblePropagations.push_back(preprocessed);
288 }
289 d_preregisterQueue.push(preprocessed);
290
291 if (!d_inPreregister) {
292 // We're in pre-register
293 d_inPreregister = true;
294
295 // Process the pre-registration queue
296 while (!d_preregisterQueue.empty()) {
297 // Get the next atom to pre-register
298 preprocessed = d_preregisterQueue.front();
299 d_preregisterQueue.pop();
300
301 if (d_logicInfo.isSharingEnabled() && preprocessed.getKind() == kind::EQUAL) {
302 // When sharing is enabled, we propagate from the shared terms manager also
303 d_sharedTerms.addEqualityToPropagate(preprocessed);
304 }
305
306 // the atom should not have free variables
307 Debug("theory") << "TheoryEngine::preRegister: " << preprocessed
308 << std::endl;
309 Assert(!expr::hasFreeVar(preprocessed));
310 // Pre-register the terms in the atom
311 Theory::Set theories = NodeVisitor<PreRegisterVisitor>::run(d_preRegistrationVisitor, preprocessed);
312 theories = Theory::setRemove(THEORY_BOOL, theories);
313 // Remove the top theory, if any more that means multiple theories were involved
314 bool multipleTheories = Theory::setRemove(Theory::theoryOf(preprocessed), theories);
315 TheoryId i;
316 // These checks don't work with finite model finding, because it
317 // uses Rational constants to represent cardinality constraints,
318 // even though arithmetic isn't actually involved.
319 if(!options::finiteModelFind()) {
320 while((i = Theory::setPop(theories)) != THEORY_LAST) {
321 if(!d_logicInfo.isTheoryEnabled(i)) {
322 LogicInfo newLogicInfo = d_logicInfo.getUnlockedCopy();
323 newLogicInfo.enableTheory(i);
324 newLogicInfo.lock();
325 stringstream ss;
326 ss << "The logic was specified as " << d_logicInfo.getLogicString()
327 << ", which doesn't include " << i
328 << ", but found a term in that theory." << endl
329 << "You might want to extend your logic to "
330 << newLogicInfo.getLogicString() << endl;
331 throw LogicException(ss.str());
332 }
333 }
334 }
335 if (multipleTheories) {
336 // Collect the shared terms if there are multiple theories
337 NodeVisitor<SharedTermsVisitor>::run(d_sharedTermsVisitor, preprocessed);
338 }
339 }
340
341 // Leaving pre-register
342 d_inPreregister = false;
343 }
344 }
345
346 void TheoryEngine::printAssertions(const char* tag) {
347 if (Trace.isOn(tag)) {
348
349 for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) {
350 Theory* theory = d_theoryTable[theoryId];
351 if (theory && d_logicInfo.isTheoryEnabled(theoryId)) {
352 Trace(tag) << "--------------------------------------------" << endl;
353 Trace(tag) << "Assertions of " << theory->getId() << ": " << endl;
354 {
355 context::CDList<Assertion>::const_iterator it = theory->facts_begin(),
356 it_end =
357 theory->facts_end();
358 for (unsigned i = 0; it != it_end; ++it, ++i)
359 {
360 if ((*it).d_isPreregistered)
361 {
362 Trace(tag) << "[" << i << "]: ";
363 }
364 else
365 {
366 Trace(tag) << "(" << i << "): ";
367 }
368 Trace(tag) << (*it).d_assertion << endl;
369 }
370 }
371
372 if (d_logicInfo.isSharingEnabled()) {
373 Trace(tag) << "Shared terms of " << theory->getId() << ": " << endl;
374 context::CDList<TNode>::const_iterator it = theory->shared_terms_begin(), it_end = theory->shared_terms_end();
375 for (unsigned i = 0; it != it_end; ++ it, ++i) {
376 Trace(tag) << "[" << i << "]: " << (*it) << endl;
377 }
378 }
379 }
380 }
381 }
382 }
383
384 void TheoryEngine::dumpAssertions(const char* tag) {
385 if (Dump.isOn(tag)) {
386 Dump(tag) << CommentCommand("Starting completeness check");
387 for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) {
388 Theory* theory = d_theoryTable[theoryId];
389 if (theory && d_logicInfo.isTheoryEnabled(theoryId)) {
390 Dump(tag) << CommentCommand("Completeness check");
391 Dump(tag) << PushCommand();
392
393 // Dump the shared terms
394 if (d_logicInfo.isSharingEnabled()) {
395 Dump(tag) << CommentCommand("Shared terms");
396 context::CDList<TNode>::const_iterator it = theory->shared_terms_begin(), it_end = theory->shared_terms_end();
397 for (unsigned i = 0; it != it_end; ++ it, ++i) {
398 stringstream ss;
399 ss << (*it);
400 Dump(tag) << CommentCommand(ss.str());
401 }
402 }
403
404 // Dump the assertions
405 Dump(tag) << CommentCommand("Assertions");
406 context::CDList<Assertion>::const_iterator it = theory->facts_begin(), it_end = theory->facts_end();
407 for (; it != it_end; ++ it) {
408 // Get the assertion
409 Node assertionNode = (*it).d_assertion;
410 // Purify all the terms
411
412 if ((*it).d_isPreregistered)
413 {
414 Dump(tag) << CommentCommand("Preregistered");
415 }
416 else
417 {
418 Dump(tag) << CommentCommand("Shared assertion");
419 }
420 Dump(tag) << AssertCommand(assertionNode.toExpr());
421 }
422 Dump(tag) << CheckSatCommand();
423
424 Dump(tag) << PopCommand();
425 }
426 }
427 }
428 }
429
430 /**
431 * Check all (currently-active) theories for conflicts.
432 * @param effort the effort level to use
433 */
434 void TheoryEngine::check(Theory::Effort effort) {
435 // spendResource();
436
437 // Reset the interrupt flag
438 d_interrupted = false;
439
440 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
441 #undef CVC4_FOR_EACH_THEORY_STATEMENT
442 #endif
443 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
444 if (theory::TheoryTraits<THEORY>::hasCheck && d_logicInfo.isTheoryEnabled(THEORY)) { \
445 theoryOf(THEORY)->check(effort); \
446 if (d_inConflict) { \
447 Debug("conflict") << THEORY << " in conflict. " << std::endl; \
448 break; \
449 } \
450 }
451
452 // Do the checking
453 try {
454
455 // Mark the output channel unused (if this is FULL_EFFORT, and nothing
456 // is done by the theories, no additional check will be needed)
457 d_outputChannelUsed = false;
458
459 // Mark the lemmas flag (no lemmas added)
460 d_lemmasAdded = false;
461
462 Debug("theory") << "TheoryEngine::check(" << effort << "): d_factsAsserted = " << (d_factsAsserted ? "true" : "false") << endl;
463
464 // If in full effort, we have a fake new assertion just to jumpstart the checking
465 if (Theory::fullEffort(effort)) {
466 d_factsAsserted = true;
467 // Reset round for the relevance manager, which notice only sets a flag
468 // to indicate that its information must be recomputed.
469 if (d_relManager != nullptr)
470 {
471 d_relManager->resetRound();
472 }
473 }
474
475 // Check until done
476 while (d_factsAsserted && !d_inConflict && !d_lemmasAdded) {
477
478 Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << endl;
479
480 Trace("theory::assertions") << endl;
481 if (Trace.isOn("theory::assertions")) {
482 printAssertions("theory::assertions");
483 }
484
485 if(Theory::fullEffort(effort)) {
486 Trace("theory::assertions::fulleffort") << endl;
487 if (Trace.isOn("theory::assertions::fulleffort")) {
488 printAssertions("theory::assertions::fulleffort");
489 }
490 }
491
492 // Note that we've discharged all the facts
493 d_factsAsserted = false;
494
495 // Do the checking
496 CVC4_FOR_EACH_THEORY;
497
498 if(Dump.isOn("missed-t-conflicts")) {
499 Dump("missed-t-conflicts")
500 << CommentCommand("Completeness check for T-conflicts; expect sat")
501 << CheckSatCommand();
502 }
503
504 Debug("theory") << "TheoryEngine::check(" << effort << "): running propagation after the initial check" << endl;
505
506 // We are still satisfiable, propagate as much as possible
507 propagate(effort);
508
509 // We do combination if all has been processed and we are in fullcheck
510 if (Theory::fullEffort(effort) && d_logicInfo.isSharingEnabled() && !d_factsAsserted && !d_lemmasAdded && !d_inConflict) {
511 // Do the combination
512 Debug("theory") << "TheoryEngine::check(" << effort << "): running combination" << endl;
513 combineTheories();
514 if(d_logicInfo.isQuantified()){
515 d_quantEngine->notifyCombineTheories();
516 }
517 }
518 }
519
520 // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma
521 if( Theory::fullEffort(effort) && ! d_inConflict && ! needCheck() ) {
522 Trace("theory::assertions-model") << endl;
523 if (Trace.isOn("theory::assertions-model")) {
524 printAssertions("theory::assertions-model");
525 }
526 //checks for theories requiring the model go at last call
527 d_curr_model->reset();
528 for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) {
529 if( theoryId!=THEORY_QUANTIFIERS ){
530 Theory* theory = d_theoryTable[theoryId];
531 if (theory && d_logicInfo.isTheoryEnabled(theoryId)) {
532 if( theory->needsCheckLastEffort() ){
533 if( !d_curr_model->isBuilt() ){
534 if( !d_curr_model_builder->buildModel(d_curr_model) ){
535 break;
536 }
537 }
538 theory->check(Theory::EFFORT_LAST_CALL);
539 }
540 }
541 }
542 }
543 if (!d_inConflict)
544 {
545 if(d_logicInfo.isQuantified()) {
546 // quantifiers engine must check at last call effort
547 d_quantEngine->check(Theory::EFFORT_LAST_CALL);
548 }
549 }
550 if (!d_inConflict && !needCheck())
551 {
552 // If d_eager_model_building is false, then we only mark that we
553 // are in "SAT mode". We build the model later only if the user asks
554 // for it via getBuiltModel.
555 d_inSatMode = true;
556 if (d_eager_model_building && !d_curr_model->isBuilt())
557 {
558 d_curr_model_builder->buildModel(d_curr_model);
559 }
560 }
561 }
562
563 Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas");
564 Debug("theory") << ", need check = " << (needCheck() ? "YES" : "NO") << endl;
565
566 if( Theory::fullEffort(effort) && !d_inConflict && !needCheck()) {
567 // case where we are about to answer SAT, the master equality engine,
568 // if it exists, must be consistent.
569 eq::EqualityEngine* mee = d_eeDistributed->getMasterEqualityEngine();
570 if (mee != NULL)
571 {
572 AlwaysAssert(mee->consistent());
573 }
574 if (d_curr_model->isBuilt())
575 {
576 // model construction should always succeed unless lemmas were added
577 AlwaysAssert(d_curr_model->isBuiltSuccess());
578 if (options::produceModels())
579 {
580 // Do post-processing of model from the theories (used for THEORY_SEP
581 // to construct heap model)
582 postProcessModel(d_curr_model);
583 // also call the model builder's post-process model
584 d_curr_model_builder->postProcessModel(d_incomplete.get(),
585 d_curr_model);
586 }
587 }
588 }
589 } catch(const theory::Interrupted&) {
590 Trace("theory") << "TheoryEngine::check() => interrupted" << endl;
591 }
592 // If fulleffort, check all theories
593 if(Dump.isOn("theory::fullcheck") && Theory::fullEffort(effort)) {
594 if (!d_inConflict && !needCheck()) {
595 dumpAssertions("theory::fullcheck");
596 }
597 }
598 }
599
600 void TheoryEngine::combineTheories() {
601
602 Trace("combineTheories") << "TheoryEngine::combineTheories()" << endl;
603
604 TimerStat::CodeTimer combineTheoriesTimer(d_combineTheoriesTime);
605
606 // Care graph we'll be building
607 CareGraph careGraph;
608
609 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
610 #undef CVC4_FOR_EACH_THEORY_STATEMENT
611 #endif
612 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
613 if (theory::TheoryTraits<THEORY>::isParametric && d_logicInfo.isTheoryEnabled(THEORY)) { \
614 theoryOf(THEORY)->getCareGraph(&careGraph); \
615 }
616
617 // Call on each parametric theory to give us its care graph
618 CVC4_FOR_EACH_THEORY;
619
620 Trace("combineTheories") << "TheoryEngine::combineTheories(): care graph size = " << careGraph.size() << endl;
621
622 // Now add splitters for the ones we are interested in
623 CareGraph::const_iterator care_it = careGraph.begin();
624 CareGraph::const_iterator care_it_end = careGraph.end();
625
626 for (; care_it != care_it_end; ++ care_it) {
627 const CarePair& carePair = *care_it;
628
629 Debug("combineTheories")
630 << "TheoryEngine::combineTheories(): checking " << carePair.d_a << " = "
631 << carePair.d_b << " from " << carePair.d_theory << endl;
632
633 Assert(d_sharedTerms.isShared(carePair.d_a) || carePair.d_a.isConst());
634 Assert(d_sharedTerms.isShared(carePair.d_b) || carePair.d_b.isConst());
635
636 // The equality in question (order for no repetition)
637 Node equality = carePair.d_a.eqNode(carePair.d_b);
638 // EqualityStatus es = getEqualityStatus(carePair.d_a, carePair.d_b);
639 // Debug("combineTheories") << "TheoryEngine::combineTheories(): " <<
640 // (es == EQUALITY_TRUE_AND_PROPAGATED ? "EQUALITY_TRUE_AND_PROPAGATED" :
641 // es == EQUALITY_FALSE_AND_PROPAGATED ? "EQUALITY_FALSE_AND_PROPAGATED" :
642 // es == EQUALITY_TRUE ? "EQUALITY_TRUE" :
643 // es == EQUALITY_FALSE ? "EQUALITY_FALSE" :
644 // es == EQUALITY_TRUE_IN_MODEL ? "EQUALITY_TRUE_IN_MODEL" :
645 // es == EQUALITY_FALSE_IN_MODEL ? "EQUALITY_FALSE_IN_MODEL" :
646 // es == EQUALITY_UNKNOWN ? "EQUALITY_UNKNOWN" :
647 // "Unexpected case") << endl;
648
649 // We need to split on it
650 Debug("combineTheories") << "TheoryEngine::combineTheories(): requesting a split " << endl;
651
652 lemma(equality.orNode(equality.notNode()),
653 RULE_INVALID,
654 false,
655 LemmaProperty::NONE,
656 carePair.d_theory);
657
658 // This code is supposed to force preference to follow what the theory models already have
659 // but it doesn't seem to make a big difference - need to explore more -Clark
660 // if (true) {
661 // if (es == EQUALITY_TRUE || es == EQUALITY_TRUE_IN_MODEL) {
662 Node e = ensureLiteral(equality);
663 d_propEngine->requirePhase(e, true);
664 // }
665 // else if (es == EQUALITY_FALSE_IN_MODEL) {
666 // Node e = ensureLiteral(equality);
667 // d_propEngine->requirePhase(e, false);
668 // }
669 // }
670 }
671 }
672
673 void TheoryEngine::propagate(Theory::Effort effort) {
674 // Reset the interrupt flag
675 d_interrupted = false;
676
677 // Definition of the statement that is to be run by every theory
678 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
679 #undef CVC4_FOR_EACH_THEORY_STATEMENT
680 #endif
681 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
682 if (theory::TheoryTraits<THEORY>::hasPropagate && d_logicInfo.isTheoryEnabled(THEORY)) { \
683 theoryOf(THEORY)->propagate(effort); \
684 }
685
686 // Reset the interrupt flag
687 d_interrupted = false;
688
689 // Propagate for each theory using the statement above
690 CVC4_FOR_EACH_THEORY;
691
692 if(Dump.isOn("missed-t-propagations")) {
693 for(unsigned i = 0; i < d_possiblePropagations.size(); ++i) {
694 Node atom = d_possiblePropagations[i];
695 bool value;
696 if(d_propEngine->hasValue(atom, value)) {
697 continue;
698 }
699 // Doesn't have a value, check it (and the negation)
700 if(d_hasPropagated.find(atom) == d_hasPropagated.end()) {
701 Dump("missed-t-propagations")
702 << CommentCommand("Completeness check for T-propagations; expect invalid")
703 << EchoCommand(atom.toString())
704 << QueryCommand(atom.toExpr())
705 << EchoCommand(atom.notNode().toString())
706 << QueryCommand(atom.notNode().toExpr());
707 }
708 }
709 }
710 }
711
712 Node TheoryEngine::getNextDecisionRequest()
713 {
714 return d_decManager->getNextDecisionRequest();
715 }
716
717 bool TheoryEngine::properConflict(TNode conflict) const {
718 bool value;
719 if (conflict.getKind() == kind::AND) {
720 for (unsigned i = 0; i < conflict.getNumChildren(); ++ i) {
721 if (! getPropEngine()->hasValue(conflict[i], value)) {
722 Debug("properConflict") << "Bad conflict is due to unassigned atom: "
723 << conflict[i] << endl;
724 return false;
725 }
726 if (! value) {
727 Debug("properConflict") << "Bad conflict is due to false atom: "
728 << conflict[i] << endl;
729 return false;
730 }
731 if (conflict[i] != Rewriter::rewrite(conflict[i])) {
732 Debug("properConflict") << "Bad conflict is due to atom not in normal form: "
733 << conflict[i] << " vs " << Rewriter::rewrite(conflict[i]) << endl;
734 return false;
735 }
736 }
737 } else {
738 if (! getPropEngine()->hasValue(conflict, value)) {
739 Debug("properConflict") << "Bad conflict is due to unassigned atom: "
740 << conflict << endl;
741 return false;
742 }
743 if(! value) {
744 Debug("properConflict") << "Bad conflict is due to false atom: "
745 << conflict << endl;
746 return false;
747 }
748 if (conflict != Rewriter::rewrite(conflict)) {
749 Debug("properConflict") << "Bad conflict is due to atom not in normal form: "
750 << conflict << " vs " << Rewriter::rewrite(conflict) << endl;
751 return false;
752 }
753 }
754 return true;
755 }
756
757 bool TheoryEngine::properPropagation(TNode lit) const {
758 if(!getPropEngine()->isSatLiteral(lit)) {
759 return false;
760 }
761 bool b;
762 return !getPropEngine()->hasValue(lit, b);
763 }
764
765 bool TheoryEngine::properExplanation(TNode node, TNode expl) const {
766 // Explanation must be either a conjunction of true literals that have true SAT values already
767 // or a singled literal that has a true SAT value already.
768 if (expl.getKind() == kind::AND) {
769 for (unsigned i = 0; i < expl.getNumChildren(); ++ i) {
770 bool value;
771 if (!d_propEngine->hasValue(expl[i], value) || !value) {
772 return false;
773 }
774 }
775 } else {
776 bool value;
777 return d_propEngine->hasValue(expl, value) && value;
778 }
779 return true;
780 }
781
782 bool TheoryEngine::collectModelInfo(theory::TheoryModel* m)
783 {
784 //have shared term engine collectModelInfo
785 // d_sharedTerms.collectModelInfo( m );
786 // Consult each active theory to get all relevant information
787 // concerning the model.
788 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) {
789 if(d_logicInfo.isTheoryEnabled(theoryId)) {
790 Trace("model-builder") << " CollectModelInfo on theory: " << theoryId << endl;
791 if (!d_theoryTable[theoryId]->collectModelInfo(m))
792 {
793 return false;
794 }
795 }
796 }
797 Trace("model-builder") << " CollectModelInfo boolean variables" << std::endl;
798 // Get the Boolean variables
799 vector<TNode> boolVars;
800 d_propEngine->getBooleanVariables(boolVars);
801 vector<TNode>::iterator it, iend = boolVars.end();
802 bool hasValue, value;
803 for (it = boolVars.begin(); it != iend; ++it) {
804 TNode var = *it;
805 hasValue = d_propEngine->hasValue(var, value);
806 // TODO: Assert that hasValue is true?
807 if (!hasValue) {
808 Trace("model-builder-assertions")
809 << " has no value : " << var << std::endl;
810 value = false;
811 }
812 Trace("model-builder-assertions") << "(assert" << (value ? " " : " (not ") << var << (value ? ");" : "));") << endl;
813 if (!m->assertPredicate(var, value))
814 {
815 return false;
816 }
817 }
818 return true;
819 }
820
821 void TheoryEngine::postProcessModel( theory::TheoryModel* m ){
822 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) {
823 if(d_logicInfo.isTheoryEnabled(theoryId)) {
824 Trace("model-builder-debug") << " PostProcessModel on theory: " << theoryId << endl;
825 d_theoryTable[theoryId]->postProcessModel( m );
826 }
827 }
828 }
829
830 TheoryModel* TheoryEngine::getModel() {
831 return d_curr_model;
832 }
833
834 TheoryModel* TheoryEngine::getBuiltModel()
835 {
836 if (!d_curr_model->isBuilt())
837 {
838 // If this method was called, we should be in SAT mode, and produceModels
839 // should be true.
840 AlwaysAssert(options::produceModels());
841 if (!d_inSatMode)
842 {
843 // not available, perhaps due to interuption.
844 return nullptr;
845 }
846 // must build model at this point
847 d_curr_model_builder->buildModel(d_curr_model);
848 }
849 return d_curr_model;
850 }
851
852 bool TheoryEngine::getSynthSolutions(
853 std::map<Node, std::map<Node, Node>>& sol_map)
854 {
855 if (d_quantEngine)
856 {
857 return d_quantEngine->getSynthSolutions(sol_map);
858 }
859 // we are not in a quantified logic, there is no synthesis solution
860 return false;
861 }
862
863 bool TheoryEngine::presolve() {
864 // Reset the interrupt flag
865 d_interrupted = false;
866
867 // Reset the decision manager. This clears its decision strategies that are
868 // no longer valid in this user context.
869 d_decManager->presolve();
870
871 try {
872 // Definition of the statement that is to be run by every theory
873 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
874 #undef CVC4_FOR_EACH_THEORY_STATEMENT
875 #endif
876 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
877 if (theory::TheoryTraits<THEORY>::hasPresolve) { \
878 theoryOf(THEORY)->presolve(); \
879 if(d_inConflict) { \
880 return true; \
881 } \
882 }
883
884 // Presolve for each theory using the statement above
885 CVC4_FOR_EACH_THEORY;
886 } catch(const theory::Interrupted&) {
887 Trace("theory") << "TheoryEngine::presolve() => interrupted" << endl;
888 }
889 // return whether we have a conflict
890 return false;
891 }/* TheoryEngine::presolve() */
892
893 void TheoryEngine::postsolve() {
894 // no longer in SAT mode
895 d_inSatMode = false;
896 // Reset the interrupt flag
897 d_interrupted = false;
898 bool CVC4_UNUSED wasInConflict = d_inConflict;
899
900 try {
901 // Definition of the statement that is to be run by every theory
902 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
903 #undef CVC4_FOR_EACH_THEORY_STATEMENT
904 #endif
905 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
906 if (theory::TheoryTraits<THEORY>::hasPostsolve) \
907 { \
908 theoryOf(THEORY)->postsolve(); \
909 Assert(!d_inConflict || wasInConflict) \
910 << "conflict raised during postsolve()"; \
911 }
912
913 // Postsolve for each theory using the statement above
914 CVC4_FOR_EACH_THEORY;
915 } catch(const theory::Interrupted&) {
916 Trace("theory") << "TheoryEngine::postsolve() => interrupted" << endl;
917 }
918 }/* TheoryEngine::postsolve() */
919
920
921 void TheoryEngine::notifyRestart() {
922 // Reset the interrupt flag
923 d_interrupted = false;
924
925 // Definition of the statement that is to be run by every theory
926 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
927 #undef CVC4_FOR_EACH_THEORY_STATEMENT
928 #endif
929 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
930 if (theory::TheoryTraits<THEORY>::hasNotifyRestart && d_logicInfo.isTheoryEnabled(THEORY)) { \
931 theoryOf(THEORY)->notifyRestart(); \
932 }
933
934 // notify each theory using the statement above
935 CVC4_FOR_EACH_THEORY;
936 }
937
938 void TheoryEngine::ppStaticLearn(TNode in, NodeBuilder<>& learned) {
939 // Reset the interrupt flag
940 d_interrupted = false;
941
942 // Definition of the statement that is to be run by every theory
943 #ifdef CVC4_FOR_EACH_THEORY_STATEMENT
944 #undef CVC4_FOR_EACH_THEORY_STATEMENT
945 #endif
946 #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
947 if (theory::TheoryTraits<THEORY>::hasPpStaticLearn) { \
948 theoryOf(THEORY)->ppStaticLearn(in, learned); \
949 }
950
951 // static learning for each theory using the statement above
952 CVC4_FOR_EACH_THEORY;
953 }
954
955 bool TheoryEngine::isRelevant(Node lit) const
956 {
957 if (d_relManager != nullptr)
958 {
959 return d_relManager->isRelevant(lit);
960 }
961 // otherwise must assume its relevant
962 return true;
963 }
964
965 void TheoryEngine::shutdown() {
966 // Set this first; if a Theory shutdown() throws an exception,
967 // at least the destruction of the TheoryEngine won't confound
968 // matters.
969 d_hasShutDown = true;
970
971 // Shutdown all the theories
972 for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) {
973 if(d_theoryTable[theoryId]) {
974 theoryOf(theoryId)->shutdown();
975 }
976 }
977
978 d_tpp.clearCache();
979 }
980
981 theory::Theory::PPAssertStatus TheoryEngine::solve(TNode literal, SubstitutionMap& substitutionOut) {
982 // Reset the interrupt flag
983 d_interrupted = false;
984
985 TNode atom = literal.getKind() == kind::NOT ? literal[0] : literal;
986 Trace("theory::solve") << "TheoryEngine::solve(" << literal << "): solving with " << theoryOf(atom)->getId() << endl;
987
988 if(! d_logicInfo.isTheoryEnabled(Theory::theoryOf(atom)) &&
989 Theory::theoryOf(atom) != THEORY_SAT_SOLVER) {
990 stringstream ss;
991 ss << "The logic was specified as " << d_logicInfo.getLogicString()
992 << ", which doesn't include " << Theory::theoryOf(atom)
993 << ", but got a preprocessing-time fact for that theory." << endl
994 << "The fact:" << endl
995 << literal;
996 throw LogicException(ss.str());
997 }
998
999 Theory::PPAssertStatus solveStatus = theoryOf(atom)->ppAssert(literal, substitutionOut);
1000 Trace("theory::solve") << "TheoryEngine::solve(" << literal << ") => " << solveStatus << endl;
1001 return solveStatus;
1002 }
1003
1004 void TheoryEngine::preprocessStart() { d_tpp.clearCache(); }
1005
1006 Node TheoryEngine::preprocess(TNode assertion) {
1007 TrustNode trn = d_tpp.theoryPreprocess(assertion);
1008 if (trn.isNull())
1009 {
1010 // no change
1011 return assertion;
1012 }
1013 return trn.getNode();
1014 }
1015
1016 void TheoryEngine::notifyPreprocessedAssertions(
1017 const std::vector<Node>& assertions) {
1018 // call all the theories
1019 for (TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST;
1020 ++theoryId) {
1021 if (d_theoryTable[theoryId]) {
1022 theoryOf(theoryId)->ppNotifyAssertions(assertions);
1023 }
1024 }
1025 if (d_relManager != nullptr)
1026 {
1027 d_relManager->notifyPreprocessedAssertions(assertions);
1028 }
1029 }
1030
1031 bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) {
1032
1033 // What and where we are asserting
1034 NodeTheoryPair toAssert(assertion, toTheoryId, d_propagationMapTimestamp);
1035 // What and where it came from
1036 NodeTheoryPair toExplain(originalAssertion, fromTheoryId, d_propagationMapTimestamp);
1037
1038 // See if the theory already got this literal
1039 PropagationMap::const_iterator find = d_propagationMap.find(toAssert);
1040 if (find != d_propagationMap.end()) {
1041 // The theory already knows this
1042 Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): already there" << endl;
1043 return false;
1044 }
1045
1046 Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): marking [" << d_propagationMapTimestamp << "] " << assertion << ", " << toTheoryId << " from " << originalAssertion << ", " << fromTheoryId << endl;
1047
1048 // Mark the propagation
1049 d_propagationMap[toAssert] = toExplain;
1050 d_propagationMapTimestamp = d_propagationMapTimestamp + 1;
1051
1052 return true;
1053 }
1054
1055
1056 void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) {
1057
1058 Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << originalAssertion << "," << toTheoryId << ", " << fromTheoryId << ")" << endl;
1059
1060 Assert(toTheoryId != fromTheoryId);
1061 if(toTheoryId != THEORY_SAT_SOLVER &&
1062 ! d_logicInfo.isTheoryEnabled(toTheoryId)) {
1063 stringstream ss;
1064 ss << "The logic was specified as " << d_logicInfo.getLogicString()
1065 << ", which doesn't include " << toTheoryId
1066 << ", but got an asserted fact to that theory." << endl
1067 << "The fact:" << endl
1068 << assertion;
1069 throw LogicException(ss.str());
1070 }
1071
1072 if (d_inConflict) {
1073 return;
1074 }
1075
1076 // If sharing is disabled, things are easy
1077 if (!d_logicInfo.isSharingEnabled()) {
1078 Assert(assertion == originalAssertion);
1079 if (fromTheoryId == THEORY_SAT_SOLVER) {
1080 // Send to the apropriate theory
1081 theory::Theory* toTheory = theoryOf(toTheoryId);
1082 // We assert it, and we know it's preregistereed
1083 toTheory->assertFact(assertion, true);
1084 // Mark that we have more information
1085 d_factsAsserted = true;
1086 } else {
1087 Assert(toTheoryId == THEORY_SAT_SOLVER);
1088 // Check for propositional conflict
1089 bool value;
1090 if (d_propEngine->hasValue(assertion, value)) {
1091 if (!value) {
1092 Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict (no sharing)" << endl;
1093 Trace("dtview::conflict")
1094 << ":THEORY-CONFLICT: " << assertion << std::endl;
1095 d_inConflict = true;
1096 } else {
1097 return;
1098 }
1099 }
1100 d_propagatedLiterals.push_back(assertion);
1101 }
1102 return;
1103 }
1104
1105 // Polarity of the assertion
1106 bool polarity = assertion.getKind() != kind::NOT;
1107
1108 // Atom of the assertion
1109 TNode atom = polarity ? assertion : assertion[0];
1110
1111 // If sending to the shared terms database, it's also simple
1112 if (toTheoryId == THEORY_BUILTIN) {
1113 Assert(atom.getKind() == kind::EQUAL)
1114 << "atom should be an EQUALity, not `" << atom << "'";
1115 if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
1116 d_sharedTerms.assertEquality(atom, polarity, assertion);
1117 }
1118 return;
1119 }
1120
1121 // Things from the SAT solver are already normalized, so they go
1122 // directly to the apropriate theory
1123 if (fromTheoryId == THEORY_SAT_SOLVER) {
1124 // We know that this is normalized, so just send it off to the theory
1125 if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
1126 // Is it preregistered
1127 bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId;
1128 // We assert it
1129 theoryOf(toTheoryId)->assertFact(assertion, preregistered);
1130 // Mark that we have more information
1131 d_factsAsserted = true;
1132 }
1133 return;
1134 }
1135
1136 // Propagations to the SAT solver are just enqueued for pickup by
1137 // the SAT solver later
1138 if (toTheoryId == THEORY_SAT_SOLVER) {
1139 if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
1140 // Enqueue for propagation to the SAT solver
1141 d_propagatedLiterals.push_back(assertion);
1142 // Check for propositional conflicts
1143 bool value;
1144 if (d_propEngine->hasValue(assertion, value) && !value) {
1145 Trace("theory::propagate")
1146 << "TheoryEngine::assertToTheory(" << assertion << ", "
1147 << toTheoryId << ", " << fromTheoryId << "): conflict (sharing)"
1148 << endl;
1149 Trace("dtview::conflict")
1150 << ":THEORY-CONFLICT: " << assertion << std::endl;
1151 d_inConflict = true;
1152 }
1153 }
1154 return;
1155 }
1156
1157 Assert(atom.getKind() == kind::EQUAL);
1158
1159 // Normalize
1160 Node normalizedLiteral = Rewriter::rewrite(assertion);
1161
1162 // See if it rewrites false directly -> conflict
1163 if (normalizedLiteral.isConst()) {
1164 if (!normalizedLiteral.getConst<bool>()) {
1165 // Mark the propagation for explanations
1166 if (markPropagation(normalizedLiteral, originalAssertion, toTheoryId, fromTheoryId)) {
1167 // Get the explanation (conflict will figure out where it came from)
1168 conflict(normalizedLiteral, toTheoryId);
1169 } else {
1170 Unreachable();
1171 }
1172 return;
1173 }
1174 }
1175
1176 // Try and assert (note that we assert the non-normalized one)
1177 if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
1178 // Check if has been pre-registered with the theory
1179 bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId;
1180 // Assert away
1181 theoryOf(toTheoryId)->assertFact(assertion, preregistered);
1182 d_factsAsserted = true;
1183 }
1184
1185 return;
1186 }
1187
1188 void TheoryEngine::assertFact(TNode literal)
1189 {
1190 Trace("theory") << "TheoryEngine::assertFact(" << literal << ")" << endl;
1191
1192 // spendResource();
1193
1194 // If we're in conflict, nothing to do
1195 if (d_inConflict) {
1196 return;
1197 }
1198
1199 // Get the atom
1200 bool polarity = literal.getKind() != kind::NOT;
1201 TNode atom = polarity ? literal : literal[0];
1202
1203 if (d_logicInfo.isSharingEnabled()) {
1204
1205 // If any shared terms, it's time to do sharing work
1206 if (d_sharedTerms.hasSharedTerms(atom)) {
1207 // Notify the theories the shared terms
1208 SharedTermsDatabase::shared_terms_iterator it = d_sharedTerms.begin(atom);
1209 SharedTermsDatabase::shared_terms_iterator it_end = d_sharedTerms.end(atom);
1210 for (; it != it_end; ++ it) {
1211 TNode term = *it;
1212 Theory::Set theories = d_sharedTerms.getTheoriesToNotify(atom, term);
1213 for (TheoryId id = THEORY_FIRST; id != THEORY_LAST; ++ id) {
1214 if (Theory::setContains(id, theories)) {
1215 theoryOf(id)->addSharedTermInternal(term);
1216 }
1217 }
1218 d_sharedTerms.markNotified(term, theories);
1219 }
1220 }
1221
1222 // If it's an equality, assert it to the shared term manager, even though the terms are not
1223 // yet shared. As the terms become shared later, the shared terms manager will then add them
1224 // to the assert the equality to the interested theories
1225 if (atom.getKind() == kind::EQUAL) {
1226 // Assert it to the the owning theory
1227 assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
1228 // Shared terms manager will assert to interested theories directly, as the terms become shared
1229 assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ THEORY_SAT_SOLVER);
1230
1231 // Now, let's check for any atom triggers from lemmas
1232 AtomRequests::atom_iterator it = d_atomRequests.getAtomIterator(atom);
1233 while (!it.done()) {
1234 const AtomRequests::Request& request = it.get();
1235 Node toAssert =
1236 polarity ? (Node)request.d_atom : request.d_atom.notNode();
1237 Debug("theory::atoms") << "TheoryEngine::assertFact(" << literal << "): sending requested " << toAssert << endl;
1238 assertToTheory(
1239 toAssert, literal, request.d_toTheory, THEORY_SAT_SOLVER);
1240 it.next();
1241 }
1242
1243 } else {
1244 // Not an equality, just assert to the appropriate theory
1245 assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
1246 }
1247 } else {
1248 // Assert the fact to the appropriate theory directly
1249 assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
1250 }
1251 }
1252
1253 bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) {
1254
1255 Debug("theory::propagate") << "TheoryEngine::propagate(" << literal << ", " << theory << ")" << endl;
1256
1257 Trace("dtview::prop") << std::string(d_context->getLevel(), ' ')
1258 << ":THEORY-PROP: " << literal << endl;
1259
1260 // spendResource();
1261
1262 if(Dump.isOn("t-propagations")) {
1263 Dump("t-propagations") << CommentCommand("negation of theory propagation: expect valid")
1264 << QueryCommand(literal.toExpr());
1265 }
1266 if(Dump.isOn("missed-t-propagations")) {
1267 d_hasPropagated.insert(literal);
1268 }
1269
1270 // Get the atom
1271 bool polarity = literal.getKind() != kind::NOT;
1272 TNode atom = polarity ? literal : literal[0];
1273
1274 if (d_logicInfo.isSharingEnabled() && atom.getKind() == kind::EQUAL) {
1275 if (d_propEngine->isSatLiteral(literal)) {
1276 // We propagate SAT literals to SAT
1277 assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
1278 }
1279 if (theory != THEORY_BUILTIN) {
1280 // Assert to the shared terms database
1281 assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory);
1282 }
1283 } else {
1284 // We could be propagating a unit-clause lemma. In this case, we need to provide a
1285 // recipe.
1286 // TODO: Consider putting this someplace else? This is the only refence to the proof
1287 // manager in this class.
1288
1289 PROOF({
1290 LemmaProofRecipe proofRecipe;
1291 proofRecipe.addBaseAssertion(literal);
1292
1293 Node emptyNode;
1294 LemmaProofRecipe::ProofStep proofStep(theory, emptyNode);
1295 proofStep.addAssertion(literal);
1296 proofRecipe.addStep(proofStep);
1297
1298 ProofManager::getCnfProof()->setProofRecipe(&proofRecipe);
1299 });
1300
1301 // Just send off to the SAT solver
1302 Assert(d_propEngine->isSatLiteral(literal));
1303 assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
1304 }
1305
1306 return !d_inConflict;
1307 }
1308
1309 const LogicInfo& TheoryEngine::getLogicInfo() const { return d_logicInfo; }
1310
1311 theory::EqualityStatus TheoryEngine::getEqualityStatus(TNode a, TNode b) {
1312 Assert(a.getType().isComparableTo(b.getType()));
1313 if (d_sharedTerms.isShared(a) && d_sharedTerms.isShared(b)) {
1314 if (d_sharedTerms.areEqual(a,b)) {
1315 return EQUALITY_TRUE_AND_PROPAGATED;
1316 }
1317 else if (d_sharedTerms.areDisequal(a,b)) {
1318 return EQUALITY_FALSE_AND_PROPAGATED;
1319 }
1320 }
1321 return theoryOf(Theory::theoryOf(a.getType()))->getEqualityStatus(a, b);
1322 }
1323
1324 Node TheoryEngine::getModelValue(TNode var) {
1325 if (var.isConst())
1326 {
1327 // the model value of a constant must be itself
1328 return var;
1329 }
1330 Assert(d_sharedTerms.isShared(var));
1331 return theoryOf(Theory::theoryOf(var.getType()))->getModelValue(var);
1332 }
1333
1334
1335 Node TheoryEngine::ensureLiteral(TNode n) {
1336 Debug("ensureLiteral") << "rewriting: " << n << std::endl;
1337 Node rewritten = Rewriter::rewrite(n);
1338 Debug("ensureLiteral") << " got: " << rewritten << std::endl;
1339 Node preprocessed = preprocess(rewritten);
1340 Debug("ensureLiteral") << "preprocessed: " << preprocessed << std::endl;
1341 d_propEngine->ensureLiteral(preprocessed);
1342 return preprocessed;
1343 }
1344
1345
1346 void TheoryEngine::printInstantiations( std::ostream& out ) {
1347 if( d_quantEngine ){
1348 d_quantEngine->printInstantiations( out );
1349 }else{
1350 out << "Internal error : instantiations not available when quantifiers are not present." << std::endl;
1351 Assert(false);
1352 }
1353 }
1354
1355 void TheoryEngine::printSynthSolution( std::ostream& out ) {
1356 if( d_quantEngine ){
1357 d_quantEngine->printSynthSolution( out );
1358 }else{
1359 out << "Internal error : synth solution not available when quantifiers are not present." << std::endl;
1360 Assert(false);
1361 }
1362 }
1363
1364 void TheoryEngine::getInstantiatedQuantifiedFormulas( std::vector< Node >& qs ) {
1365 if( d_quantEngine ){
1366 d_quantEngine->getInstantiatedQuantifiedFormulas( qs );
1367 }else{
1368 Assert(false);
1369 }
1370 }
1371
1372 void TheoryEngine::getInstantiations( Node q, std::vector< Node >& insts ) {
1373 if( d_quantEngine ){
1374 d_quantEngine->getInstantiations( q, insts );
1375 }else{
1376 Assert(false);
1377 }
1378 }
1379
1380 void TheoryEngine::getInstantiationTermVectors( Node q, std::vector< std::vector< Node > >& tvecs ) {
1381 if( d_quantEngine ){
1382 d_quantEngine->getInstantiationTermVectors( q, tvecs );
1383 }else{
1384 Assert(false);
1385 }
1386 }
1387
1388 void TheoryEngine::getInstantiations( std::map< Node, std::vector< Node > >& insts ) {
1389 if( d_quantEngine ){
1390 d_quantEngine->getInstantiations( insts );
1391 }else{
1392 Assert(false);
1393 }
1394 }
1395
1396 void TheoryEngine::getInstantiationTermVectors( std::map< Node, std::vector< std::vector< Node > > >& insts ) {
1397 if( d_quantEngine ){
1398 d_quantEngine->getInstantiationTermVectors( insts );
1399 }else{
1400 Assert(false);
1401 }
1402 }
1403
1404 Node TheoryEngine::getInstantiatedConjunction( Node q ) {
1405 if( d_quantEngine ){
1406 return d_quantEngine->getInstantiatedConjunction( q );
1407 }else{
1408 Assert(false);
1409 return Node::null();
1410 }
1411 }
1412
1413
1414 static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) {
1415
1416 std::set<TNode> all;
1417 for (unsigned i = 0; i < explanation.size(); ++ i) {
1418 Assert(explanation[i].d_theory == THEORY_SAT_SOLVER);
1419 all.insert(explanation[i].d_node);
1420 }
1421
1422 if (all.size() == 0) {
1423 // Normalize to true
1424 return NodeManager::currentNM()->mkConst<bool>(true);
1425 }
1426
1427 if (all.size() == 1) {
1428 // All the same, or just one
1429 return explanation[0].d_node;
1430 }
1431
1432 NodeBuilder<> conjunction(kind::AND);
1433 std::set<TNode>::const_iterator it = all.begin();
1434 std::set<TNode>::const_iterator it_end = all.end();
1435 while (it != it_end) {
1436 conjunction << *it;
1437 ++ it;
1438 }
1439
1440 return conjunction;
1441 }
1442
1443 Node TheoryEngine::getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe) {
1444 Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl;
1445
1446 bool polarity = node.getKind() != kind::NOT;
1447 TNode atom = polarity ? node : node[0];
1448
1449 // If we're not in shared mode, explanations are simple
1450 if (!d_logicInfo.isSharingEnabled()) {
1451 Debug("theory::explain") << "TheoryEngine::getExplanation: sharing is NOT enabled. "
1452 << " Responsible theory is: "
1453 << theoryOf(atom)->getId() << std::endl;
1454
1455 TrustNode texplanation = theoryOf(atom)->explain(node);
1456 Node explanation = texplanation.getNode();
1457 Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl;
1458 PROOF({
1459 if(proofRecipe) {
1460 Node emptyNode;
1461 LemmaProofRecipe::ProofStep proofStep(theoryOf(atom)->getId(), emptyNode);
1462 proofStep.addAssertion(node);
1463 proofRecipe->addBaseAssertion(node);
1464
1465 if (explanation.getKind() == kind::AND) {
1466 // If the explanation is a conjunction, the recipe for the corresponding lemma is
1467 // the negation of its conjuncts.
1468 Node flat = flattenAnd(explanation);
1469 for (unsigned i = 0; i < flat.getNumChildren(); ++i) {
1470 if (flat[i].isConst() && flat[i].getConst<bool>()) {
1471 ++ i;
1472 continue;
1473 }
1474 if (flat[i].getKind() == kind::NOT &&
1475 flat[i][0].isConst() && !flat[i][0].getConst<bool>()) {
1476 ++ i;
1477 continue;
1478 }
1479 Debug("theory::explain") << "TheoryEngine::getExplanationAndRecipe: adding recipe assertion: "
1480 << flat[i].negate() << std::endl;
1481 proofStep.addAssertion(flat[i].negate());
1482 proofRecipe->addBaseAssertion(flat[i].negate());
1483 }
1484 } else {
1485 // The recipe for proving it is by negating it. "True" is not an acceptable reason.
1486 if (!((explanation.isConst() && explanation.getConst<bool>()) ||
1487 (explanation.getKind() == kind::NOT &&
1488 explanation[0].isConst() && !explanation[0].getConst<bool>()))) {
1489 proofStep.addAssertion(explanation.negate());
1490 proofRecipe->addBaseAssertion(explanation.negate());
1491 }
1492 }
1493
1494 proofRecipe->addStep(proofStep);
1495 }
1496 });
1497
1498 return explanation;
1499 }
1500
1501 Debug("theory::explain") << "TheoryEngine::getExplanation: sharing IS enabled" << std::endl;
1502
1503 // Initial thing to explain
1504 NodeTheoryPair toExplain(node, THEORY_SAT_SOLVER, d_propagationMapTimestamp);
1505 Assert(d_propagationMap.find(toExplain) != d_propagationMap.end());
1506
1507 NodeTheoryPair nodeExplainerPair = d_propagationMap[toExplain];
1508 Debug("theory::explain")
1509 << "TheoryEngine::getExplanation: explainer for node "
1510 << nodeExplainerPair.d_node
1511 << " is theory: " << nodeExplainerPair.d_theory << std::endl;
1512 TheoryId explainer = nodeExplainerPair.d_theory;
1513
1514 // Create the workplace for explanations
1515 std::vector<NodeTheoryPair> explanationVector;
1516 explanationVector.push_back(d_propagationMap[toExplain]);
1517 // Process the explanation
1518 if (proofRecipe) {
1519 Node emptyNode;
1520 LemmaProofRecipe::ProofStep proofStep(explainer, emptyNode);
1521 proofStep.addAssertion(node);
1522 proofRecipe->addStep(proofStep);
1523 proofRecipe->addBaseAssertion(node);
1524 }
1525
1526 getExplanation(explanationVector, proofRecipe);
1527 Node explanation = mkExplanation(explanationVector);
1528
1529 Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl;
1530
1531 return explanation;
1532 }
1533
1534 Node TheoryEngine::getExplanation(TNode node) {
1535 LemmaProofRecipe *dontCareRecipe = NULL;
1536 return getExplanationAndRecipe(node, dontCareRecipe);
1537 }
1538
1539 struct AtomsCollect {
1540
1541 std::vector<TNode> d_atoms;
1542 std::unordered_set<TNode, TNodeHashFunction> d_visited;
1543
1544 public:
1545
1546 typedef void return_type;
1547
1548 bool alreadyVisited(TNode current, TNode parent) {
1549 // Check if already visited
1550 if (d_visited.find(current) != d_visited.end()) return true;
1551 // Don't visit non-boolean
1552 if (!current.getType().isBoolean()) return true;
1553 // New node
1554 return false;
1555 }
1556
1557 void visit(TNode current, TNode parent) {
1558 if (Theory::theoryOf(current) != theory::THEORY_BOOL) {
1559 d_atoms.push_back(current);
1560 }
1561 d_visited.insert(current);
1562 }
1563
1564 void start(TNode node) {}
1565 void done(TNode node) {}
1566
1567 std::vector<TNode> getAtoms() const {
1568 return d_atoms;
1569 }
1570 };
1571
1572 void TheoryEngine::ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId atomsTo) {
1573 for (unsigned i = 0; i < atoms.size(); ++ i) {
1574
1575 // Non-equality atoms are either owned by theory or they don't make sense
1576 if (atoms[i].getKind() != kind::EQUAL) {
1577 continue;
1578 }
1579
1580 // The equality
1581 Node eq = atoms[i];
1582 // Simple normalization to not repeat stuff
1583 if (eq[0] > eq[1]) {
1584 eq = eq[1].eqNode(eq[0]);
1585 }
1586
1587 // Rewrite the equality
1588 Node eqNormalized = Rewriter::rewrite(atoms[i]);
1589
1590 Debug("theory::atoms") << "TheoryEngine::ensureLemmaAtoms(): " << eq << " with nf " << eqNormalized << endl;
1591
1592 // If the equality is a boolean constant, we send immediately
1593 if (eqNormalized.isConst()) {
1594 if (eqNormalized.getConst<bool>()) {
1595 assertToTheory(eq, eqNormalized, /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER);
1596 } else {
1597 assertToTheory(eq.notNode(), eqNormalized.notNode(), /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER);
1598 }
1599 continue;
1600 }else if( eqNormalized.getKind() != kind::EQUAL){
1601 Assert(eqNormalized.getKind() == kind::BOOLEAN_TERM_VARIABLE
1602 || (eqNormalized.getKind() == kind::NOT
1603 && eqNormalized[0].getKind() == kind::BOOLEAN_TERM_VARIABLE));
1604 // this happens for Boolean term equalities V = true that are rewritten to V, we should skip
1605 // TODO : revisit this
1606 continue;
1607 }
1608
1609 // If the normalization did the just flips, keep the flip
1610 if (eqNormalized[0] == eq[1] && eqNormalized[1] == eq[0]) {
1611 eq = eqNormalized;
1612 }
1613
1614 // Check if the equality is already known by the sat solver
1615 if (d_propEngine->isSatLiteral(eqNormalized)) {
1616 bool value;
1617 if (d_propEngine->hasValue(eqNormalized, value)) {
1618 if (value) {
1619 assertToTheory(eq, eqNormalized, atomsTo, theory::THEORY_SAT_SOLVER);
1620 continue;
1621 } else {
1622 assertToTheory(eq.notNode(), eqNormalized.notNode(), atomsTo, theory::THEORY_SAT_SOLVER);
1623 continue;
1624 }
1625 }
1626 }
1627
1628 // If the theory is asking about a different form, or the form is ok but if will go to a different theory
1629 // then we must figure it out
1630 if (eqNormalized != eq || Theory::theoryOf(eq) != atomsTo) {
1631 // If you get eqNormalized, send atoms[i] to atomsTo
1632 d_atomRequests.add(eqNormalized, eq, atomsTo);
1633 }
1634 }
1635 }
1636
1637 theory::LemmaStatus TheoryEngine::lemma(TNode node,
1638 ProofRule rule,
1639 bool negated,
1640 theory::LemmaProperty p,
1641 theory::TheoryId atomsTo)
1642 {
1643 // For resource-limiting (also does a time check).
1644 // spendResource();
1645
1646 // Do we need to check atoms
1647 if (atomsTo != theory::THEORY_LAST) {
1648 Debug("theory::atoms") << "TheoryEngine::lemma(" << node << ", " << atomsTo << ")" << endl;
1649 AtomsCollect collectAtoms;
1650 NodeVisitor<AtomsCollect>::run(collectAtoms, node);
1651 ensureLemmaAtoms(collectAtoms.getAtoms(), atomsTo);
1652 }
1653
1654 if(Dump.isOn("t-lemmas")) {
1655 Node n = node;
1656 if (!negated) {
1657 n = node.negate();
1658 }
1659 Dump("t-lemmas") << CommentCommand("theory lemma: expect valid")
1660 << CheckSatCommand(n.toExpr());
1661 }
1662 bool removable = isLemmaPropertyRemovable(p);
1663 bool preprocess = isLemmaPropertyPreprocess(p);
1664
1665 // call preprocessor
1666 std::vector<TrustNode> newLemmas;
1667 std::vector<Node> newSkolems;
1668 TrustNode tlemma = d_tpp.preprocess(node, newLemmas, newSkolems, preprocess);
1669
1670 // !!!!!!! temporary, until this method is fully updated from proof-new
1671 if (tlemma.isNull())
1672 {
1673 tlemma = TrustNode::mkTrustLemma(node);
1674 }
1675
1676 // must use an assertion pipeline due to decision engine below
1677 AssertionPipeline lemmas;
1678 // make the assertion pipeline
1679 lemmas.push_back(tlemma.getNode());
1680 lemmas.updateRealAssertionsEnd();
1681 Assert(newSkolems.size() == newLemmas.size());
1682 for (size_t i = 0, nsize = newLemmas.size(); i < nsize; i++)
1683 {
1684 // store skolem mapping here
1685 IteSkolemMap& imap = lemmas.getIteSkolemMap();
1686 imap[newSkolems[i]] = lemmas.size();
1687 lemmas.push_back(newLemmas[i].getNode());
1688 }
1689
1690 // If specified, we must add this lemma to the set of those that need to be
1691 // justified, where note we pass all auxiliary lemmas in lemmas, since these
1692 // by extension must be justified as well.
1693 if (d_relManager != nullptr && isLemmaPropertyNeedsJustify(p))
1694 {
1695 d_relManager->notifyPreprocessedAssertions(lemmas.ref());
1696 }
1697
1698 // assert lemmas to prop engine
1699 for (size_t i = 0, lsize = lemmas.size(); i < lsize; ++i)
1700 {
1701 d_propEngine->assertLemma(
1702 lemmas[i], i == 0 && negated, removable, rule, node);
1703 }
1704
1705 // WARNING: Below this point don't assume lemmas[0] to be not negated.
1706 if(negated) {
1707 lemmas.replace(0, lemmas[0].notNode());
1708 negated = false;
1709 }
1710
1711 // assert to decision engine
1712 if (!removable)
1713 {
1714 d_propEngine->addAssertionsToDecisionEngine(lemmas);
1715 }
1716
1717 // Mark that we added some lemmas
1718 d_lemmasAdded = true;
1719
1720 // Lemma analysis isn't online yet; this lemma may only live for this
1721 // user level.
1722 Node retLemma = lemmas[0];
1723 if (lemmas.size() > 1)
1724 {
1725 // the returned lemma is the conjunction of all additional lemmas.
1726 retLemma = NodeManager::currentNM()->mkNode(kind::AND, lemmas.ref());
1727 }
1728 return theory::LemmaStatus(retLemma, d_userContext->getLevel());
1729 }
1730
1731 void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) {
1732
1733 Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << ")" << endl;
1734
1735 Trace("dtview::conflict") << ":THEORY-CONFLICT: " << conflict << std::endl;
1736
1737 // Mark that we are in conflict
1738 d_inConflict = true;
1739
1740 if(Dump.isOn("t-conflicts")) {
1741 Dump("t-conflicts") << CommentCommand("theory conflict: expect unsat")
1742 << CheckSatCommand(conflict.toExpr());
1743 }
1744
1745 LemmaProofRecipe* proofRecipe = NULL;
1746 PROOF({
1747 proofRecipe = new LemmaProofRecipe;
1748 Node emptyNode;
1749 LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode);
1750
1751 if (conflict.getKind() == kind::AND) {
1752 for (unsigned i = 0; i < conflict.getNumChildren(); ++i) {
1753 proofStep.addAssertion(conflict[i].negate());
1754 }
1755 } else {
1756 proofStep.addAssertion(conflict.negate());
1757 }
1758
1759 proofRecipe->addStep(proofStep);
1760 });
1761
1762 // In the multiple-theories case, we need to reconstruct the conflict
1763 if (d_logicInfo.isSharingEnabled()) {
1764 // Create the workplace for explanations
1765 std::vector<NodeTheoryPair> explanationVector;
1766 explanationVector.push_back(NodeTheoryPair(conflict, theoryId, d_propagationMapTimestamp));
1767
1768 // Process the explanation
1769 getExplanation(explanationVector, proofRecipe);
1770 PROOF(ProofManager::getCnfProof()->setProofRecipe(proofRecipe));
1771 Node fullConflict = mkExplanation(explanationVector);
1772 Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl;
1773 Assert(properConflict(fullConflict));
1774 lemma(fullConflict,
1775 RULE_CONFLICT,
1776 true,
1777 LemmaProperty::REMOVABLE,
1778 THEORY_LAST);
1779 } else {
1780 // When only one theory, the conflict should need no processing
1781 Assert(properConflict(conflict));
1782 PROOF({
1783 if (conflict.getKind() == kind::AND) {
1784 // If the conflict is a conjunction, the corresponding lemma is derived by negating
1785 // its conjuncts.
1786 for (unsigned i = 0; i < conflict.getNumChildren(); ++i) {
1787 if (conflict[i].isConst() && conflict[i].getConst<bool>()) {
1788 ++ i;
1789 continue;
1790 }
1791 if (conflict[i].getKind() == kind::NOT &&
1792 conflict[i][0].isConst() && !conflict[i][0].getConst<bool>()) {
1793 ++ i;
1794 continue;
1795 }
1796 proofRecipe->getStep(0)->addAssertion(conflict[i].negate());
1797 proofRecipe->addBaseAssertion(conflict[i].negate());
1798 }
1799 } else {
1800 proofRecipe->getStep(0)->addAssertion(conflict.negate());
1801 proofRecipe->addBaseAssertion(conflict.negate());
1802 }
1803
1804 ProofManager::getCnfProof()->setProofRecipe(proofRecipe);
1805 });
1806
1807 lemma(conflict, RULE_CONFLICT, true, LemmaProperty::REMOVABLE, THEORY_LAST);
1808 }
1809
1810 PROOF({
1811 delete proofRecipe;
1812 proofRecipe = NULL;
1813 });
1814 }
1815
1816 SharedTermsDatabase* TheoryEngine::getSharedTermsDatabase()
1817 {
1818 return &d_sharedTerms;
1819 }
1820
1821 void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* proofRecipe) {
1822 Assert(explanationVector.size() > 0);
1823
1824 unsigned i = 0; // Index of the current literal we are processing
1825 unsigned j = 0; // Index of the last literal we are keeping
1826
1827 std::unique_ptr<std::set<Node>> inputAssertions = nullptr;
1828 PROOF({
1829 if (proofRecipe)
1830 {
1831 inputAssertions.reset(
1832 new std::set<Node>(proofRecipe->getStep(0)->getAssertions()));
1833 }
1834 });
1835 // cache of nodes we have already explained by some theory
1836 std::unordered_map<Node, size_t, NodeHashFunction> cache;
1837
1838 while (i < explanationVector.size()) {
1839 // Get the current literal to explain
1840 NodeTheoryPair toExplain = explanationVector[i];
1841
1842 Debug("theory::explain")
1843 << "[i=" << i << "] TheoryEngine::explain(): processing ["
1844 << toExplain.d_timestamp << "] " << toExplain.d_node << " sent from "
1845 << toExplain.d_theory << endl;
1846
1847 if (cache.find(toExplain.d_node) != cache.end()
1848 && cache[toExplain.d_node] < toExplain.d_timestamp)
1849 {
1850 ++i;
1851 continue;
1852 }
1853 cache[toExplain.d_node] = toExplain.d_timestamp;
1854
1855 // If a true constant or a negation of a false constant we can ignore it
1856 if (toExplain.d_node.isConst() && toExplain.d_node.getConst<bool>())
1857 {
1858 ++ i;
1859 continue;
1860 }
1861 if (toExplain.d_node.getKind() == kind::NOT && toExplain.d_node[0].isConst()
1862 && !toExplain.d_node[0].getConst<bool>())
1863 {
1864 ++ i;
1865 continue;
1866 }
1867
1868 // If from the SAT solver, keep it
1869 if (toExplain.d_theory == THEORY_SAT_SOLVER)
1870 {
1871 Debug("theory::explain") << "\tLiteral came from THEORY_SAT_SOLVER. Kepping it." << endl;
1872 explanationVector[j++] = explanationVector[i++];
1873 continue;
1874 }
1875
1876 // If an and, expand it
1877 if (toExplain.d_node.getKind() == kind::AND)
1878 {
1879 Debug("theory::explain")
1880 << "TheoryEngine::explain(): expanding " << toExplain.d_node
1881 << " got from " << toExplain.d_theory << endl;
1882 for (unsigned k = 0; k < toExplain.d_node.getNumChildren(); ++k)
1883 {
1884 NodeTheoryPair newExplain(
1885 toExplain.d_node[k], toExplain.d_theory, toExplain.d_timestamp);
1886 explanationVector.push_back(newExplain);
1887 }
1888 ++ i;
1889 continue;
1890 }
1891
1892 // See if it was sent to the theory by another theory
1893 PropagationMap::const_iterator find = d_propagationMap.find(toExplain);
1894 if (find != d_propagationMap.end()) {
1895 Debug("theory::explain")
1896 << "\tTerm was propagated by another theory (theory = "
1897 << getTheoryString((*find).second.d_theory) << ")" << std::endl;
1898 // There is some propagation, check if its a timely one
1899 if ((*find).second.d_timestamp < toExplain.d_timestamp)
1900 {
1901 Debug("theory::explain")
1902 << "\tRelevant timetsamp, pushing " << (*find).second.d_node
1903 << "to index = " << explanationVector.size() << std::endl;
1904 explanationVector.push_back((*find).second);
1905 ++i;
1906
1907 PROOF({
1908 if (toExplain.d_node != (*find).second.d_node)
1909 {
1910 Debug("pf::explain")
1911 << "TheoryEngine::getExplanation: Rewrite alert! toAssert = "
1912 << toExplain.d_node << ", toExplain = " << (*find).second.d_node
1913 << std::endl;
1914
1915 if (proofRecipe)
1916 {
1917 proofRecipe->addRewriteRule(toExplain.d_node,
1918 (*find).second.d_node);
1919 }
1920 }
1921 })
1922
1923 continue;
1924 }
1925 }
1926
1927 Node explanation;
1928 if (toExplain.d_theory == THEORY_BUILTIN)
1929 {
1930 explanation = d_sharedTerms.explain(toExplain.d_node);
1931 Debug("theory::explain") << "\tTerm was propagated by THEORY_BUILTIN. Explanation: " << explanation << std::endl;
1932 }
1933 else
1934 {
1935 TrustNode texp = theoryOf(toExplain.d_theory)->explain(toExplain.d_node);
1936 explanation = texp.getNode();
1937 Debug("theory::explain") << "\tTerm was propagated by owner theory: "
1938 << theoryOf(toExplain.d_theory)->getId()
1939 << ". Explanation: " << explanation << std::endl;
1940 }
1941
1942 Debug("theory::explain")
1943 << "TheoryEngine::explain(): got explanation " << explanation
1944 << " got from " << toExplain.d_theory << endl;
1945 Assert(explanation != toExplain.d_node)
1946 << "wasn't sent to you, so why are you explaining it trivially";
1947 // Mark the explanation
1948 NodeTheoryPair newExplain(
1949 explanation, toExplain.d_theory, toExplain.d_timestamp);
1950 explanationVector.push_back(newExplain);
1951
1952 ++ i;
1953
1954 PROOF({
1955 if (proofRecipe && inputAssertions)
1956 {
1957 // If we're expanding the target node of the explanation (this is the
1958 // first expansion...), we don't want to add it as a separate proof
1959 // step. It is already part of the assertions.
1960 if (!ContainsKey(*inputAssertions, toExplain.d_node))
1961 {
1962 LemmaProofRecipe::ProofStep proofStep(toExplain.d_theory,
1963 toExplain.d_node);
1964 if (explanation.getKind() == kind::AND)
1965 {
1966 Node flat = flattenAnd(explanation);
1967 for (unsigned k = 0; k < flat.getNumChildren(); ++k)
1968 {
1969 // If a true constant or a negation of a false constant we can
1970 // ignore it
1971 if (!((flat[k].isConst() && flat[k].getConst<bool>())
1972 || (flat[k].getKind() == kind::NOT && flat[k][0].isConst()
1973 && !flat[k][0].getConst<bool>())))
1974 {
1975 proofStep.addAssertion(flat[k].negate());
1976 }
1977 }
1978 }
1979 else
1980 {
1981 if (!((explanation.isConst() && explanation.getConst<bool>())
1982 || (explanation.getKind() == kind::NOT
1983 && explanation[0].isConst()
1984 && !explanation[0].getConst<bool>())))
1985 {
1986 proofStep.addAssertion(explanation.negate());
1987 }
1988 }
1989 proofRecipe->addStep(proofStep);
1990 }
1991 }
1992 });
1993 }
1994
1995 // Keep only the relevant literals
1996 explanationVector.resize(j);
1997
1998 PROOF({
1999 if (proofRecipe) {
2000 // The remaining literals are the base of the proof
2001 for (unsigned k = 0; k < explanationVector.size(); ++k) {
2002 proofRecipe->addBaseAssertion(explanationVector[k].d_node.negate());
2003 }
2004 }
2005 });
2006 }
2007
2008 void TheoryEngine::setUserAttribute(const std::string& attr,
2009 Node n,
2010 const std::vector<Node>& node_values,
2011 const std::string& str_value)
2012 {
2013 Trace("te-attr") << "set user attribute " << attr << " " << n << endl;
2014 if( d_attr_handle.find( attr )!=d_attr_handle.end() ){
2015 for( size_t i=0; i<d_attr_handle[attr].size(); i++ ){
2016 d_attr_handle[attr][i]->setUserAttribute(attr, n, node_values, str_value);
2017 }
2018 } else {
2019 //unhandled exception?
2020 }
2021 }
2022
2023 void TheoryEngine::handleUserAttribute(const char* attr, Theory* t) {
2024 Trace("te-attr") << "Handle user attribute " << attr << " " << t << endl;
2025 std::string str( attr );
2026 d_attr_handle[ str ].push_back( t );
2027 }
2028
2029 void TheoryEngine::checkTheoryAssertionsWithModel(bool hardFailure) {
2030 for(TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) {
2031 Theory* theory = d_theoryTable[theoryId];
2032 if(theory && d_logicInfo.isTheoryEnabled(theoryId)) {
2033 for(context::CDList<Assertion>::const_iterator it = theory->facts_begin(),
2034 it_end = theory->facts_end();
2035 it != it_end;
2036 ++it) {
2037 Node assertion = (*it).d_assertion;
2038 if (!isRelevant(assertion))
2039 {
2040 // not relevant, skip
2041 continue;
2042 }
2043 Node val = getModel()->getValue(assertion);
2044 if (val != d_true)
2045 {
2046 std::stringstream ss;
2047 ss << " " << theoryId
2048 << " has an asserted fact that the model doesn't satisfy." << endl
2049 << "The fact: " << assertion << endl
2050 << "Model value: " << val << endl;
2051 if (hardFailure)
2052 {
2053 if (val == d_false)
2054 {
2055 // Always an error if it is false
2056 InternalError() << ss.str();
2057 }
2058 else
2059 {
2060 // Otherwise just a warning. Notice this case may happen for
2061 // assertions with unevaluable operators, e.g. transcendental
2062 // functions. It also may happen for separation logic, where
2063 // check-model support is limited.
2064 Warning() << ss.str();
2065 }
2066 }
2067 }
2068 }
2069 }
2070 }
2071 }
2072
2073 std::pair<bool, Node> TheoryEngine::entailmentCheck(options::TheoryOfMode mode,
2074 TNode lit)
2075 {
2076 TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit;
2077 if( atom.getKind()==kind::AND || atom.getKind()==kind::OR || atom.getKind()==kind::IMPLIES ){
2078 //Boolean connective, recurse
2079 std::vector< Node > children;
2080 bool pol = (lit.getKind()!=kind::NOT);
2081 bool is_conjunction = pol==(lit.getKind()==kind::AND);
2082 for( unsigned i=0; i<atom.getNumChildren(); i++ ){
2083 Node ch = atom[i];
2084 if( pol==( lit.getKind()==kind::IMPLIES && i==0 ) ){
2085 ch = atom[i].negate();
2086 }
2087 std::pair<bool, Node> chres = entailmentCheck(mode, ch);
2088 if( chres.first ){
2089 if( !is_conjunction ){
2090 return chres;
2091 }else{
2092 children.push_back( chres.second );
2093 }
2094 }else if( !chres.first && is_conjunction ){
2095 return std::pair<bool, Node>(false, Node::null());
2096 }
2097 }
2098 if( is_conjunction ){
2099 return std::pair<bool, Node>(true, NodeManager::currentNM()->mkNode(kind::AND, children));
2100 }else{
2101 return std::pair<bool, Node>(false, Node::null());
2102 }
2103 }else if( atom.getKind()==kind::ITE || ( atom.getKind()==kind::EQUAL && atom[0].getType().isBoolean() ) ){
2104 bool pol = (lit.getKind()!=kind::NOT);
2105 for( unsigned r=0; r<2; r++ ){
2106 Node ch = atom[0];
2107 if( r==1 ){
2108 ch = ch.negate();
2109 }
2110 std::pair<bool, Node> chres = entailmentCheck(mode, ch);
2111 if( chres.first ){
2112 Node ch2 = atom[ atom.getKind()==kind::ITE ? r+1 : 1 ];
2113 if( pol==( atom.getKind()==kind::ITE ? true : r==1 ) ){
2114 ch2 = ch2.negate();
2115 }
2116 std::pair<bool, Node> chres2 = entailmentCheck(mode, ch2);
2117 if( chres2.first ){
2118 return std::pair<bool, Node>(true, NodeManager::currentNM()->mkNode(kind::AND, chres.second, chres2.second));
2119 }else{
2120 break;
2121 }
2122 }
2123 }
2124 return std::pair<bool, Node>(false, Node::null());
2125 }else{
2126 //it is a theory atom
2127 theory::TheoryId tid = theory::Theory::theoryOf(mode, atom);
2128 theory::Theory* th = theoryOf(tid);
2129
2130 Assert(th != NULL);
2131 Trace("theory-engine-entc") << "Entailment check : " << lit << std::endl;
2132
2133 std::pair<bool, Node> chres = th->entailmentCheck(lit);
2134 return chres;
2135 }
2136 }
2137
2138 void TheoryEngine::spendResource(ResourceManager::Resource r)
2139 {
2140 d_resourceManager->spendResource(r);
2141 }
2142
2143 }/* CVC4 namespace */