50a5c4493dbdea231b05bfbec777611cae5699fd
[cvc5.git] / src / theory / theory.cpp
1 /********************* */
2 /*! \file theory.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Tim King, Mathias Preiner, Dejan Jovanovic
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 Base for theory interface.
13 **
14 ** Base for theory interface.
15 **/
16
17 #include "theory/theory.h"
18
19 #include <iostream>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23
24 #include "base/check.h"
25 #include "expr/node_algorithm.h"
26 #include "options/theory_options.h"
27 #include "smt/smt_statistics_registry.h"
28 #include "theory/ext_theory.h"
29 #include "theory/quantifiers_engine.h"
30 #include "theory/substitutions.h"
31 #include "theory/theory_rewriter.h"
32
33 using namespace std;
34
35 namespace CVC4 {
36 namespace theory {
37
38 /** Default value for the uninterpreted sorts is the UF theory */
39 TheoryId Theory::s_uninterpretedSortOwner = THEORY_UF;
40
41 std::ostream& operator<<(std::ostream& os, Theory::Effort level){
42 switch(level){
43 case Theory::EFFORT_STANDARD:
44 os << "EFFORT_STANDARD"; break;
45 case Theory::EFFORT_FULL:
46 os << "EFFORT_FULL"; break;
47 case Theory::EFFORT_LAST_CALL:
48 os << "EFFORT_LAST_CALL"; break;
49 default:
50 Unreachable();
51 }
52 return os;
53 }/* ostream& operator<<(ostream&, Theory::Effort) */
54
55 Theory::Theory(TheoryId id,
56 context::Context* satContext,
57 context::UserContext* userContext,
58 OutputChannel& out,
59 Valuation valuation,
60 const LogicInfo& logicInfo,
61 ProofNodeManager* pnm,
62 std::string name)
63 : d_id(id),
64 d_satContext(satContext),
65 d_userContext(userContext),
66 d_logicInfo(logicInfo),
67 d_pnm(pnm),
68 d_facts(satContext),
69 d_factsHead(satContext, 0),
70 d_sharedTermsIndex(satContext, 0),
71 d_careGraph(NULL),
72 d_quantEngine(NULL),
73 d_decManager(nullptr),
74 d_instanceName(name),
75 d_checkTime(getStatsPrefix(id) + name + "::checkTime"),
76 d_computeCareGraphTime(getStatsPrefix(id) + name
77 + "::computeCareGraphTime"),
78 d_sharedTerms(satContext),
79 d_out(&out),
80 d_valuation(valuation),
81 d_equalityEngine(nullptr),
82 d_allocEqualityEngine(nullptr),
83 d_theoryState(nullptr),
84 d_proofsEnabled(false)
85 {
86 smtStatisticsRegistry()->registerStat(&d_checkTime);
87 smtStatisticsRegistry()->registerStat(&d_computeCareGraphTime);
88 }
89
90 Theory::~Theory() {
91 smtStatisticsRegistry()->unregisterStat(&d_checkTime);
92 smtStatisticsRegistry()->unregisterStat(&d_computeCareGraphTime);
93 }
94
95 bool Theory::needsEqualityEngine(EeSetupInfo& esi)
96 {
97 // by default, this theory does not use an (official) equality engine
98 return false;
99 }
100
101 void Theory::setEqualityEngine(eq::EqualityEngine* ee)
102 {
103 // set the equality engine pointer
104 d_equalityEngine = ee;
105 if (d_theoryState != nullptr)
106 {
107 d_theoryState->setEqualityEngine(ee);
108 }
109 }
110
111 void Theory::setQuantifiersEngine(QuantifiersEngine* qe)
112 {
113 Assert(d_quantEngine == nullptr);
114 // quantifiers engine may be null if not in quantified logic
115 d_quantEngine = qe;
116 }
117
118 void Theory::setDecisionManager(DecisionManager* dm)
119 {
120 Assert(d_decManager == nullptr);
121 Assert(dm != nullptr);
122 d_decManager = dm;
123 }
124
125 void Theory::finishInitStandalone()
126 {
127 EeSetupInfo esi;
128 if (needsEqualityEngine(esi))
129 {
130 // always associated with the same SAT context as the theory (d_satContext)
131 d_allocEqualityEngine.reset(new eq::EqualityEngine(
132 *esi.d_notify, d_satContext, esi.d_name, esi.d_constantsAreTriggers));
133 // use it as the official equality engine
134 setEqualityEngine(d_allocEqualityEngine.get());
135 }
136 finishInit();
137 }
138
139 TheoryId Theory::theoryOf(options::TheoryOfMode mode, TNode node)
140 {
141 TheoryId tid = THEORY_BUILTIN;
142 switch(mode) {
143 case options::TheoryOfMode::THEORY_OF_TYPE_BASED:
144 // Constants, variables, 0-ary constructors
145 if (node.isVar())
146 {
147 if (node.getKind() == kind::BOOLEAN_TERM_VARIABLE)
148 {
149 tid = THEORY_UF;
150 }
151 else
152 {
153 tid = Theory::theoryOf(node.getType());
154 }
155 }
156 else if (node.isConst())
157 {
158 tid = Theory::theoryOf(node.getType());
159 }
160 else if (node.getKind() == kind::EQUAL)
161 {
162 // Equality is owned by the theory that owns the domain
163 tid = Theory::theoryOf(node[0].getType());
164 }
165 else
166 {
167 // Regular nodes are owned by the kind
168 tid = kindToTheoryId(node.getKind());
169 }
170 break;
171 case options::TheoryOfMode::THEORY_OF_TERM_BASED:
172 // Variables
173 if (node.isVar())
174 {
175 if (Theory::theoryOf(node.getType()) != theory::THEORY_BOOL)
176 {
177 // We treat the variables as uninterpreted
178 tid = s_uninterpretedSortOwner;
179 }
180 else
181 {
182 if (node.getKind() == kind::BOOLEAN_TERM_VARIABLE)
183 {
184 // Boolean vars go to UF
185 tid = THEORY_UF;
186 }
187 else
188 {
189 // Except for the Boolean ones
190 tid = THEORY_BOOL;
191 }
192 }
193 }
194 else if (node.isConst())
195 {
196 // Constants go to the theory of the type
197 tid = Theory::theoryOf(node.getType());
198 }
199 else if (node.getKind() == kind::EQUAL)
200 { // Equality
201 // If one of them is an ITE, it's irelevant, since they will get
202 // replaced out anyhow
203 if (node[0].getKind() == kind::ITE)
204 {
205 tid = Theory::theoryOf(node[0].getType());
206 }
207 else if (node[1].getKind() == kind::ITE)
208 {
209 tid = Theory::theoryOf(node[1].getType());
210 }
211 else
212 {
213 TNode l = node[0];
214 TNode r = node[1];
215 TypeNode ltype = l.getType();
216 TypeNode rtype = r.getType();
217 if (ltype != rtype)
218 {
219 tid = Theory::theoryOf(l.getType());
220 }
221 else
222 {
223 // If both sides belong to the same theory the choice is easy
224 TheoryId T1 = Theory::theoryOf(l);
225 TheoryId T2 = Theory::theoryOf(r);
226 if (T1 == T2)
227 {
228 tid = T1;
229 }
230 else
231 {
232 TheoryId T3 = Theory::theoryOf(ltype);
233 // This is a case of
234 // * x*y = f(z) -> UF
235 // * x = c -> UF
236 // * f(x) = read(a, y) -> either UF or ARRAY
237 // at least one of the theories has to be parametric, i.e. theory
238 // of the type is different from the theory of the term
239 if (T1 == T3)
240 {
241 tid = T2;
242 }
243 else if (T2 == T3)
244 {
245 tid = T1;
246 }
247 else
248 {
249 // If both are parametric, we take the smaller one (arbitrary)
250 tid = T1 < T2 ? T1 : T2;
251 }
252 }
253 }
254 }
255 }
256 else
257 {
258 // Regular nodes are owned by the kind
259 tid = kindToTheoryId(node.getKind());
260 }
261 break;
262 default:
263 Unreachable();
264 }
265 Trace("theory::internal") << "theoryOf(" << mode << ", " << node << ") -> " << tid << std::endl;
266 return tid;
267 }
268
269 void Theory::notifySharedTerm(TNode n)
270 {
271 // TODO (project #39): this will move to addSharedTerm, as every theory with
272 // an equality does this in their notifySharedTerm method.
273 // if we have an equality engine, add the trigger term
274 if (d_equalityEngine != nullptr)
275 {
276 d_equalityEngine->addTriggerTerm(n, d_id);
277 }
278 }
279
280 void Theory::computeCareGraph() {
281 Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << endl;
282 for (unsigned i = 0; i < d_sharedTerms.size(); ++ i) {
283 TNode a = d_sharedTerms[i];
284 TypeNode aType = a.getType();
285 for (unsigned j = i + 1; j < d_sharedTerms.size(); ++ j) {
286 TNode b = d_sharedTerms[j];
287 if (b.getType() != aType) {
288 // We don't care about the terms of different types
289 continue;
290 }
291 switch (d_valuation.getEqualityStatus(a, b)) {
292 case EQUALITY_TRUE_AND_PROPAGATED:
293 case EQUALITY_FALSE_AND_PROPAGATED:
294 // If we know about it, we should have propagated it, so we can skip
295 break;
296 default:
297 // Let's split on it
298 addCarePair(a, b);
299 break;
300 }
301 }
302 }
303 }
304
305 void Theory::printFacts(std::ostream& os) const {
306 unsigned i, n = d_facts.size();
307 for(i = 0; i < n; i++){
308 const Assertion& a_i = d_facts[i];
309 Node assertion = a_i;
310 os << d_id << '[' << i << ']' << " " << assertion << endl;
311 }
312 }
313
314 void Theory::debugPrintFacts() const{
315 DebugChannel.getStream() << "Theory::debugPrintFacts()" << endl;
316 printFacts(DebugChannel.getStream());
317 }
318
319 bool Theory::isLegalElimination(TNode x, TNode val)
320 {
321 Assert(x.isVar());
322 if (x.getKind() == kind::BOOLEAN_TERM_VARIABLE
323 || val.getKind() == kind::BOOLEAN_TERM_VARIABLE)
324 {
325 return false;
326 }
327 if (expr::hasSubterm(val, x))
328 {
329 return false;
330 }
331 if (!val.getType().isSubtypeOf(x.getType()))
332 {
333 return false;
334 }
335 if (!options::produceModels())
336 {
337 // don't care about the model, we are fine
338 return true;
339 }
340 // if there is a model object
341 TheoryModel* tm = d_valuation.getModel();
342 Assert(tm != nullptr);
343 return tm->isLegalElimination(x, val);
344 }
345
346 std::unordered_set<TNode, TNodeHashFunction> Theory::currentlySharedTerms() const{
347 std::unordered_set<TNode, TNodeHashFunction> currentlyShared;
348 for (shared_terms_iterator i = shared_terms_begin(),
349 i_end = shared_terms_end(); i != i_end; ++i) {
350 currentlyShared.insert (*i);
351 }
352 return currentlyShared;
353 }
354
355 bool Theory::collectModelInfo(TheoryModel* m)
356 {
357 std::set<Node> termSet;
358 // Compute terms appearing in assertions and shared terms
359 computeRelevantTerms(termSet);
360 // if we are using an equality engine, assert it to the model
361 if (d_equalityEngine != nullptr)
362 {
363 if (!m->assertEqualityEngine(d_equalityEngine, &termSet))
364 {
365 return false;
366 }
367 }
368 // now, collect theory-specific value assigments
369 return collectModelValues(m, termSet);
370 }
371
372 void Theory::collectTerms(TNode n,
373 set<Kind>& irrKinds,
374 set<Node>& termSet) const
375 {
376 if (termSet.find(n) != termSet.end()) {
377 return;
378 }
379 Kind nk = n.getKind();
380 if (irrKinds.find(nk) == irrKinds.end())
381 {
382 Trace("theory::collectTerms")
383 << "Theory::collectTerms: adding " << n << endl;
384 termSet.insert(n);
385 }
386 if (nk == kind::NOT || nk == kind::EQUAL || !isLeaf(n))
387 {
388 for(TNode::iterator child_it = n.begin(); child_it != n.end(); ++child_it) {
389 collectTerms(*child_it, irrKinds, termSet);
390 }
391 }
392 }
393
394 void Theory::computeRelevantTermsInternal(std::set<Node>& termSet,
395 std::set<Kind>& irrKinds,
396 bool includeShared) const
397 {
398 // Collect all terms appearing in assertions
399 irrKinds.insert(kind::EQUAL);
400 irrKinds.insert(kind::NOT);
401 context::CDList<Assertion>::const_iterator assert_it = facts_begin(),
402 assert_it_end = facts_end();
403 for (; assert_it != assert_it_end; ++assert_it)
404 {
405 collectTerms(*assert_it, irrKinds, termSet);
406 }
407
408 if (!includeShared)
409 {
410 return;
411 }
412 // Add terms that are shared terms
413 std::set<Kind> kempty;
414 context::CDList<TNode>::const_iterator shared_it = shared_terms_begin(),
415 shared_it_end = shared_terms_end();
416 for (; shared_it != shared_it_end; ++shared_it)
417 {
418 collectTerms(*shared_it, kempty, termSet);
419 }
420 }
421
422 void Theory::computeRelevantTerms(std::set<Node>& termSet, bool includeShared)
423 {
424 std::set<Kind> irrKinds;
425 computeRelevantTermsInternal(termSet, irrKinds, includeShared);
426 }
427
428 bool Theory::collectModelValues(TheoryModel* m, const std::set<Node>& termSet)
429 {
430 return true;
431 }
432
433 Theory::PPAssertStatus Theory::ppAssert(TNode in,
434 SubstitutionMap& outSubstitutions)
435 {
436 if (in.getKind() == kind::EQUAL)
437 {
438 // (and (= x t) phi) can be replaced by phi[x/t] if
439 // 1) x is a variable
440 // 2) x is not in the term t
441 // 3) x : T and t : S, then S <: T
442 if (in[0].isVar() && isLegalElimination(in[0], in[1])
443 && in[0].getKind() != kind::BOOLEAN_TERM_VARIABLE)
444 {
445 outSubstitutions.addSubstitution(in[0], in[1]);
446 return PP_ASSERT_STATUS_SOLVED;
447 }
448 if (in[1].isVar() && isLegalElimination(in[1], in[0])
449 && in[1].getKind() != kind::BOOLEAN_TERM_VARIABLE)
450 {
451 outSubstitutions.addSubstitution(in[1], in[0]);
452 return PP_ASSERT_STATUS_SOLVED;
453 }
454 if (in[0].isConst() && in[1].isConst())
455 {
456 if (in[0] != in[1])
457 {
458 return PP_ASSERT_STATUS_CONFLICT;
459 }
460 }
461 }
462
463 return PP_ASSERT_STATUS_UNSOLVED;
464 }
465
466 std::pair<bool, Node> Theory::entailmentCheck(TNode lit)
467 {
468 return make_pair(false, Node::null());
469 }
470
471 void Theory::addCarePair(TNode t1, TNode t2) {
472 if (d_careGraph) {
473 d_careGraph->insert(CarePair(t1, t2, d_id));
474 }
475 }
476
477 void Theory::getCareGraph(CareGraph* careGraph) {
478 Assert(careGraph != NULL);
479
480 Trace("sharing") << "Theory<" << getId() << ">::getCareGraph()" << std::endl;
481 TimerStat::CodeTimer computeCareGraphTime(d_computeCareGraphTime);
482 d_careGraph = careGraph;
483 computeCareGraph();
484 d_careGraph = NULL;
485 }
486
487 EqualityStatus Theory::getEqualityStatus(TNode a, TNode b)
488 {
489 // if not using an equality engine, then by default we don't know the status
490 if (d_equalityEngine == nullptr)
491 {
492 return EQUALITY_UNKNOWN;
493 }
494 Assert(d_equalityEngine->hasTerm(a) && d_equalityEngine->hasTerm(b));
495
496 // Check for equality (simplest)
497 if (d_equalityEngine->areEqual(a, b))
498 {
499 // The terms are implied to be equal
500 return EQUALITY_TRUE;
501 }
502
503 // Check for disequality
504 if (d_equalityEngine->areDisequal(a, b, false))
505 {
506 // The terms are implied to be dis-equal
507 return EQUALITY_FALSE;
508 }
509
510 // All other terms are unknown, which is conservative. A theory may override
511 // this method if it knows more information.
512 return EQUALITY_UNKNOWN;
513 }
514
515 void Theory::check(Effort level)
516 {
517 // see if we are already done (as an optimization)
518 if (done() && level < EFFORT_FULL)
519 {
520 return;
521 }
522 Assert(d_theoryState!=nullptr);
523 // standard calls for resource, stats
524 d_out->spendResource(ResourceManager::Resource::TheoryCheckStep);
525 TimerStat::CodeTimer checkTimer(d_checkTime);
526 // pre-check at level
527 if (preCheck(level))
528 {
529 // check aborted for a theory-specific reason
530 return;
531 }
532 // process the pending fact queue
533 while (!done() && !d_theoryState->isInConflict())
534 {
535 // Get the next assertion from the fact queue
536 Assertion assertion = get();
537 TNode fact = assertion.d_assertion;
538 bool polarity = fact.getKind() != kind::NOT;
539 TNode atom = polarity ? fact : fact[0];
540 // call the pre-notify method
541 if (preNotifyFact(atom, polarity, fact, assertion.d_isPreregistered, false))
542 {
543 // handled in theory-specific way that doesn't involve equality engine
544 continue;
545 }
546 // Theories that don't have an equality engine should always return true
547 // for preNotifyFact
548 Assert(d_equalityEngine != nullptr);
549 // assert to the equality engine
550 if (atom.getKind() == kind::EQUAL)
551 {
552 d_equalityEngine->assertEquality(atom, polarity, fact);
553 }
554 else
555 {
556 d_equalityEngine->assertPredicate(atom, polarity, fact);
557 }
558 // notify the theory of the new fact, which is not internal
559 notifyFact(atom, polarity, fact, false);
560 }
561 // post-check at level
562 postCheck(level);
563 }
564
565 bool Theory::preCheck(Effort level) { return false; }
566
567 void Theory::postCheck(Effort level) {}
568
569 bool Theory::preNotifyFact(
570 TNode atom, bool polarity, TNode fact, bool isPrereg, bool isInternal)
571 {
572 return false;
573 }
574
575 void Theory::notifyFact(TNode atom, bool polarity, TNode fact, bool isInternal)
576 {
577 }
578
579 void Theory::preRegisterTerm(TNode node) {}
580
581 void Theory::addSharedTerm(TNode n)
582 {
583 Debug("sharing") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")"
584 << std::endl;
585 Debug("theory::assertions")
586 << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << std::endl;
587 d_sharedTerms.push_back(n);
588 // now call theory-specific method notifySharedTerm
589 notifySharedTerm(n);
590 }
591
592 eq::EqualityEngine* Theory::getEqualityEngine()
593 {
594 // get the assigned equality engine, which is a pointer stored in this class
595 return d_equalityEngine;
596 }
597
598 }/* CVC4::theory namespace */
599 }/* CVC4 namespace */