1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Morgan Deters, Andrew Reynolds, Clark Barrett |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 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. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Theory of arrays. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H |
19 |
|
#define CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H |
20 |
|
|
21 |
|
#include <tuple> |
22 |
|
#include <unordered_map> |
23 |
|
|
24 |
|
#include "context/cdhashmap.h" |
25 |
|
#include "context/cdhashset.h" |
26 |
|
#include "context/cdqueue.h" |
27 |
|
#include "theory/arrays/array_info.h" |
28 |
|
#include "theory/arrays/inference_manager.h" |
29 |
|
#include "theory/arrays/proof_checker.h" |
30 |
|
#include "theory/arrays/theory_arrays_rewriter.h" |
31 |
|
#include "theory/decision_strategy.h" |
32 |
|
#include "theory/theory.h" |
33 |
|
#include "theory/theory_state.h" |
34 |
|
#include "theory/uf/equality_engine.h" |
35 |
|
#include "util/statistics_stats.h" |
36 |
|
|
37 |
|
namespace cvc5 { |
38 |
|
namespace theory { |
39 |
|
namespace arrays { |
40 |
|
|
41 |
|
/** |
42 |
|
* Decision procedure for arrays. |
43 |
|
* |
44 |
|
* Overview of decision procedure: |
45 |
|
* |
46 |
|
* Preliminary notation: |
47 |
|
* Stores(a) = {t | a ~ t and t = store( _ _ _ )} |
48 |
|
* InStores(a) = {t | t = store (b _ _) and a ~ b } |
49 |
|
* Indices(a) = {i | there exists a term b[i] such that a ~ b or store(b i v)} |
50 |
|
* ~ represents the equivalence relation based on the asserted equalities in the |
51 |
|
* current context. |
52 |
|
* |
53 |
|
* The rules implemented are the following: |
54 |
|
* store(b i v) |
55 |
|
* Row1 ------------------- |
56 |
|
* store(b i v)[i] = v |
57 |
|
* |
58 |
|
* store(b i v) a'[j] |
59 |
|
* Row ---------------------- [ a' ~ store(b i v) or a' ~ b ] |
60 |
|
* i = j OR a[j] = b[j] |
61 |
|
* |
62 |
|
* a b same kind arrays |
63 |
|
* Ext ------------------------ [ a!= b in current context, k new var] |
64 |
|
* a = b OR a[k] != b[k]p |
65 |
|
* |
66 |
|
* |
67 |
|
* The Row1 one rule is implemented implicitly as follows: |
68 |
|
* - for each store(b i v) term add the following equality to the congruence |
69 |
|
* closure store(b i v)[i] = v |
70 |
|
* - if one of the literals in a conflict is of the form store(b i v)[i] = v |
71 |
|
* remove it from the conflict |
72 |
|
* |
73 |
|
* Because new store terms are not created, we need to check if we need to |
74 |
|
* instantiate a new Row axiom in the following cases: |
75 |
|
* 1. the congruence relation changes (i.e. two terms get merged) |
76 |
|
* - when a new equality between array terms a = b is asserted we check if |
77 |
|
* we can instantiate a Row lemma for all pairs of indices i where a is |
78 |
|
* being read and stores |
79 |
|
* - this is only done during full effort check |
80 |
|
* 2. a new read term is created either as a consequences of an Ext lemma or a |
81 |
|
* Row lemma |
82 |
|
* - this is implemented in the checkRowForIndex method which is called |
83 |
|
* when preregistering a term of the form a[i]. |
84 |
|
* - as a consequence lemmas are instantiated even before full effort check |
85 |
|
* |
86 |
|
* The Ext axiom is instantiated when a disequality is asserted during full effort |
87 |
|
* check. Ext lemmas are stored in a cache to prevent instantiating essentially |
88 |
|
* the same lemma multiple times. |
89 |
|
*/ |
90 |
|
|
91 |
779696 |
static inline std::string spaces(int level) |
92 |
|
{ |
93 |
779696 |
std::string indentStr(level, ' '); |
94 |
779696 |
return indentStr; |
95 |
|
} |
96 |
|
|
97 |
|
class TheoryArrays : public Theory { |
98 |
|
|
99 |
|
///////////////////////////////////////////////////////////////////////////// |
100 |
|
// MISC |
101 |
|
///////////////////////////////////////////////////////////////////////////// |
102 |
|
|
103 |
|
private: |
104 |
|
|
105 |
|
/** True node for predicates = true */ |
106 |
|
Node d_true; |
107 |
|
|
108 |
|
/** True node for predicates = false */ |
109 |
|
Node d_false; |
110 |
|
|
111 |
|
// Statistics |
112 |
|
|
113 |
|
/** number of Row lemmas */ |
114 |
|
IntStat d_numRow; |
115 |
|
/** number of Ext lemmas */ |
116 |
|
IntStat d_numExt; |
117 |
|
/** number of propagations */ |
118 |
|
IntStat d_numProp; |
119 |
|
/** number of explanations */ |
120 |
|
IntStat d_numExplain; |
121 |
|
/** calls to non-linear */ |
122 |
|
IntStat d_numNonLinear; |
123 |
|
/** splits on array variables */ |
124 |
|
IntStat d_numSharedArrayVarSplits; |
125 |
|
/** splits in getModelVal */ |
126 |
|
IntStat d_numGetModelValSplits; |
127 |
|
/** conflicts in getModelVal */ |
128 |
|
IntStat d_numGetModelValConflicts; |
129 |
|
/** splits in setModelVal */ |
130 |
|
IntStat d_numSetModelValSplits; |
131 |
|
/** conflicts in setModelVal */ |
132 |
|
IntStat d_numSetModelValConflicts; |
133 |
|
|
134 |
|
public: |
135 |
|
TheoryArrays(context::Context* c, |
136 |
|
context::UserContext* u, |
137 |
|
OutputChannel& out, |
138 |
|
Valuation valuation, |
139 |
|
const LogicInfo& logicInfo, |
140 |
|
ProofNodeManager* pnm = nullptr, |
141 |
|
std::string name = "theory::arrays::"); |
142 |
|
~TheoryArrays(); |
143 |
|
|
144 |
|
//--------------------------------- initialization |
145 |
|
/** get the official theory rewriter of this theory */ |
146 |
|
TheoryRewriter* getTheoryRewriter() override; |
147 |
|
/** get the proof checker of this theory */ |
148 |
|
ProofRuleChecker* getProofChecker() override; |
149 |
|
/** |
150 |
|
* Returns true if we need an equality engine. If so, we initialize the |
151 |
|
* information regarding how it should be setup. For details, see the |
152 |
|
* documentation in Theory::needsEqualityEngine. |
153 |
|
*/ |
154 |
|
bool needsEqualityEngine(EeSetupInfo& esi) override; |
155 |
|
/** finish initialization */ |
156 |
|
void finishInit() override; |
157 |
|
//--------------------------------- end initialization |
158 |
|
|
159 |
|
std::string identify() const override { return std::string("TheoryArrays"); } |
160 |
|
|
161 |
|
///////////////////////////////////////////////////////////////////////////// |
162 |
|
// PREPROCESSING |
163 |
|
///////////////////////////////////////////////////////////////////////////// |
164 |
|
|
165 |
|
private: |
166 |
|
|
167 |
|
// PPNotifyClass: dummy template class for d_ppEqualityEngine - notifications not used |
168 |
|
class PPNotifyClass { |
169 |
|
public: |
170 |
|
bool notify(TNode propagation) { return true; } |
171 |
|
void notify(TNode t1, TNode t2) { } |
172 |
|
}; |
173 |
|
|
174 |
|
/** The notify class for d_ppEqualityEngine */ |
175 |
|
PPNotifyClass d_ppNotify; |
176 |
|
|
177 |
|
/** Equaltity engine */ |
178 |
|
eq::EqualityEngine d_ppEqualityEngine; |
179 |
|
|
180 |
|
// List of facts learned by preprocessor - needed for permanent ref for benefit of d_ppEqualityEngine |
181 |
|
context::CDList<Node> d_ppFacts; |
182 |
|
|
183 |
|
Node preprocessTerm(TNode term); |
184 |
|
Node recursivePreprocessTerm(TNode term); |
185 |
|
bool ppDisequal(TNode a, TNode b); |
186 |
|
Node solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck); |
187 |
|
|
188 |
|
/** The theory rewriter for this theory. */ |
189 |
|
TheoryArraysRewriter d_rewriter; |
190 |
|
/** A (default) theory state object */ |
191 |
|
TheoryState d_state; |
192 |
|
/** The arrays inference manager */ |
193 |
|
InferenceManager d_im; |
194 |
|
|
195 |
|
public: |
196 |
|
PPAssertStatus ppAssert(TrustNode tin, |
197 |
|
TrustSubstitutionMap& outSubstitutions) override; |
198 |
|
TrustNode ppRewrite(TNode atom, std::vector<SkolemLemma>& lems) override; |
199 |
|
|
200 |
|
///////////////////////////////////////////////////////////////////////////// |
201 |
|
// T-PROPAGATION / REGISTRATION |
202 |
|
///////////////////////////////////////////////////////////////////////////// |
203 |
|
|
204 |
|
private: |
205 |
|
/** Literals to propagate */ |
206 |
|
context::CDList<Node> d_literalsToPropagate; |
207 |
|
|
208 |
|
/** Index of the next literal to propagate */ |
209 |
|
context::CDO<unsigned> d_literalsToPropagateIndex; |
210 |
|
|
211 |
|
/** Should be called to propagate the literal. */ |
212 |
|
bool propagateLit(TNode literal); |
213 |
|
|
214 |
|
/** Explain why this literal is true by building an explanation */ |
215 |
|
void explain(TNode literal, Node& exp); |
216 |
|
|
217 |
|
/** For debugging only- checks invariants about when things are preregistered*/ |
218 |
|
context::CDHashSet<Node> d_isPreRegistered; |
219 |
|
|
220 |
|
/** Helper for preRegisterTerm, also used internally */ |
221 |
|
void preRegisterTermInternal(TNode n); |
222 |
|
|
223 |
|
public: |
224 |
|
void preRegisterTerm(TNode n) override; |
225 |
|
TrustNode explain(TNode n) override; |
226 |
|
|
227 |
|
///////////////////////////////////////////////////////////////////////////// |
228 |
|
// SHARING |
229 |
|
///////////////////////////////////////////////////////////////////////////// |
230 |
|
|
231 |
|
private: |
232 |
|
class MayEqualNotifyClass { |
233 |
|
public: |
234 |
|
bool notify(TNode propagation) { return true; } |
235 |
|
void notify(TNode t1, TNode t2) { } |
236 |
|
}; |
237 |
|
|
238 |
|
/** The notify class for d_mayEqualEqualityEngine */ |
239 |
|
MayEqualNotifyClass d_mayEqualNotify; |
240 |
|
|
241 |
|
/** Equaltity engine for determining if two arrays might be equal */ |
242 |
|
eq::EqualityEngine d_mayEqualEqualityEngine; |
243 |
|
|
244 |
|
// Helper for computeCareGraph |
245 |
|
void checkPair(TNode r1, TNode r2); |
246 |
|
|
247 |
|
public: |
248 |
|
void notifySharedTerm(TNode t) override; |
249 |
|
void computeCareGraph() override; |
250 |
|
bool isShared(TNode t) |
251 |
|
{ |
252 |
|
return (d_sharedArrays.find(t) != d_sharedArrays.end()); |
253 |
|
} |
254 |
|
|
255 |
|
///////////////////////////////////////////////////////////////////////////// |
256 |
|
// MODEL GENERATION |
257 |
|
///////////////////////////////////////////////////////////////////////////// |
258 |
|
|
259 |
|
public: |
260 |
|
/** Collect model values in m based on the relevant terms given by termSet */ |
261 |
|
bool collectModelValues(TheoryModel* m, |
262 |
|
const std::set<Node>& termSet) override; |
263 |
|
|
264 |
|
///////////////////////////////////////////////////////////////////////////// |
265 |
|
// NOTIFICATIONS |
266 |
|
///////////////////////////////////////////////////////////////////////////// |
267 |
|
|
268 |
|
|
269 |
|
void presolve() override; |
270 |
9835 |
void shutdown() override {} |
271 |
|
|
272 |
|
///////////////////////////////////////////////////////////////////////////// |
273 |
|
// MAIN SOLVER |
274 |
|
///////////////////////////////////////////////////////////////////////////// |
275 |
|
|
276 |
|
//--------------------------------- standard check |
277 |
|
/** Post-check, called after the fact queue of the theory is processed. */ |
278 |
|
void postCheck(Effort level) override; |
279 |
|
/** Pre-notify fact, return true if processed. */ |
280 |
|
bool preNotifyFact(TNode atom, |
281 |
|
bool pol, |
282 |
|
TNode fact, |
283 |
|
bool isPrereg, |
284 |
|
bool isInternal) override; |
285 |
|
/** Notify fact */ |
286 |
|
void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override; |
287 |
|
//--------------------------------- end standard check |
288 |
|
|
289 |
|
private: |
290 |
|
TNode weakEquivGetRep(TNode node); |
291 |
|
TNode weakEquivGetRepIndex(TNode node, TNode index); |
292 |
|
void visitAllLeaves(TNode reason, std::vector<TNode>& conjunctions); |
293 |
|
void weakEquivBuildCond(TNode node, TNode index, std::vector<TNode>& conjunctions); |
294 |
|
void weakEquivMakeRep(TNode node); |
295 |
|
void weakEquivMakeRepIndex(TNode node); |
296 |
|
void weakEquivAddSecondary(TNode index, TNode arrayFrom, TNode arrayTo, TNode reason); |
297 |
|
void checkWeakEquiv(bool arraysMerged); |
298 |
|
|
299 |
|
// NotifyClass: template helper class for d_equalityEngine - handles call-back from congruence closure module |
300 |
9841 |
class NotifyClass : public eq::EqualityEngineNotify { |
301 |
|
TheoryArrays& d_arrays; |
302 |
|
public: |
303 |
9841 |
NotifyClass(TheoryArrays& arrays): d_arrays(arrays) {} |
304 |
|
|
305 |
54157 |
bool eqNotifyTriggerPredicate(TNode predicate, bool value) override |
306 |
|
{ |
307 |
108314 |
Debug("arrays::propagate") |
308 |
108314 |
<< spaces(d_arrays.getSatContext()->getLevel()) |
309 |
54157 |
<< "NotifyClass::eqNotifyTriggerPredicate(" << predicate << ", " |
310 |
54157 |
<< (value ? "true" : "false") << ")" << std::endl; |
311 |
|
// Just forward to arrays |
312 |
54157 |
if (value) { |
313 |
36842 |
return d_arrays.propagateLit(predicate); |
314 |
|
} |
315 |
17315 |
return d_arrays.propagateLit(predicate.notNode()); |
316 |
|
} |
317 |
|
|
318 |
155345 |
bool eqNotifyTriggerTermEquality(TheoryId tag, |
319 |
|
TNode t1, |
320 |
|
TNode t2, |
321 |
|
bool value) override |
322 |
|
{ |
323 |
155345 |
Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; |
324 |
155345 |
if (value) { |
325 |
|
// Propagate equality between shared terms |
326 |
128547 |
return d_arrays.propagateLit(t1.eqNode(t2)); |
327 |
|
} |
328 |
26798 |
return d_arrays.propagateLit(t1.eqNode(t2).notNode()); |
329 |
|
} |
330 |
|
|
331 |
69 |
void eqNotifyConstantTermMerge(TNode t1, TNode t2) override |
332 |
|
{ |
333 |
69 |
Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; |
334 |
69 |
d_arrays.conflict(t1, t2); |
335 |
69 |
} |
336 |
|
|
337 |
45277 |
void eqNotifyNewClass(TNode t) override |
338 |
|
{ |
339 |
45277 |
d_arrays.preRegisterTermInternal(t); |
340 |
45277 |
} |
341 |
291718 |
void eqNotifyMerge(TNode t1, TNode t2) override |
342 |
|
{ |
343 |
291718 |
if (t1.getType().isArray()) { |
344 |
5810 |
d_arrays.mergeArrays(t1, t2); |
345 |
|
} |
346 |
291715 |
} |
347 |
31463 |
void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override {} |
348 |
|
}; |
349 |
|
|
350 |
|
/** The notify class for d_equalityEngine */ |
351 |
|
NotifyClass d_notify; |
352 |
|
|
353 |
|
/** The proof checker */ |
354 |
|
ArraysProofRuleChecker d_checker; |
355 |
|
|
356 |
|
/** Conflict when merging constants */ |
357 |
|
void conflict(TNode a, TNode b); |
358 |
|
|
359 |
|
/** The conflict node */ |
360 |
|
Node d_conflictNode; |
361 |
|
|
362 |
|
/** |
363 |
|
* Context dependent map from a congruence class canonical representative of |
364 |
|
* type array to an Info pointer that keeps track of information useful to axiom |
365 |
|
* instantiation |
366 |
|
*/ |
367 |
|
|
368 |
|
Backtracker<TNode> d_backtracker; |
369 |
|
ArrayInfo d_infoMap; |
370 |
|
|
371 |
|
context::CDQueue<Node> d_mergeQueue; |
372 |
|
|
373 |
|
bool d_mergeInProgress; |
374 |
|
|
375 |
|
using RowLemmaType = std::tuple<TNode, TNode, TNode, TNode>; |
376 |
|
|
377 |
|
context::CDQueue<RowLemmaType> d_RowQueue; |
378 |
|
context::CDHashSet<RowLemmaType, RowLemmaTypeHashFunction > d_RowAlreadyAdded; |
379 |
|
|
380 |
|
typedef context::CDHashSet<Node> CDNodeSet; |
381 |
|
|
382 |
|
CDNodeSet d_sharedArrays; |
383 |
|
CDNodeSet d_sharedOther; |
384 |
|
context::CDO<bool> d_sharedTerms; |
385 |
|
|
386 |
|
// Map from constant values to read terms that read from that values equal to that constant value in the current model |
387 |
|
// When a new read term is created, we check the index to see if we know the model value. If so, we add it to d_constReads (and d_constReadsList) |
388 |
|
// If not, we push it onto d_reads and figure out where it goes at computeCareGraph time. |
389 |
|
// d_constReadsList is used as a backup in case we can't compute the model at computeCareGraph time. |
390 |
|
typedef std::unordered_map<Node, CTNodeList*> CNodeNListMap; |
391 |
|
CNodeNListMap d_constReads; |
392 |
|
context::CDList<TNode> d_reads; |
393 |
|
context::CDList<TNode> d_constReadsList; |
394 |
|
context::Context* d_constReadsContext; |
395 |
|
/** Helper class to keep d_constReadsContext in sync with satContext */ |
396 |
9841 |
class ContextPopper : public context::ContextNotifyObj { |
397 |
|
context::Context* d_satContext; |
398 |
|
context::Context* d_contextToPop; |
399 |
|
protected: |
400 |
2972226 |
void contextNotifyPop() override |
401 |
|
{ |
402 |
2972226 |
if (d_contextToPop->getLevel() > d_satContext->getLevel()) |
403 |
|
{ |
404 |
23180 |
d_contextToPop->pop(); |
405 |
|
} |
406 |
2972226 |
} |
407 |
|
public: |
408 |
9841 |
ContextPopper(context::Context* context, context::Context* contextToPop) |
409 |
9841 |
:context::ContextNotifyObj(context), d_satContext(context), |
410 |
9841 |
d_contextToPop(contextToPop) |
411 |
9841 |
{} |
412 |
|
|
413 |
|
};/* class ContextPopper */ |
414 |
|
ContextPopper d_contextPopper; |
415 |
|
|
416 |
|
std::unordered_map<Node, Node> d_skolemCache; |
417 |
|
context::CDO<unsigned> d_skolemIndex; |
418 |
|
std::vector<Node> d_skolemAssertions; |
419 |
|
|
420 |
|
// The decision requests we have for the core |
421 |
|
context::CDQueue<Node> d_decisionRequests; |
422 |
|
|
423 |
|
// List of nodes that need permanent references in this context |
424 |
|
context::CDList<Node> d_permRef; |
425 |
|
context::CDList<Node> d_modelConstraints; |
426 |
|
context::CDHashSet<Node> d_lemmasSaved; |
427 |
|
std::vector<Node> d_lemmas; |
428 |
|
|
429 |
|
// Default values for each mayEqual equivalence class |
430 |
|
typedef context::CDHashMap<Node, Node> DefValMap; |
431 |
|
DefValMap d_defValues; |
432 |
|
|
433 |
|
typedef std::unordered_map<std::pair<TNode, TNode>, CTNodeList*, TNodePairHashFunction> ReadBucketMap; |
434 |
|
ReadBucketMap d_readBucketTable; |
435 |
|
context::Context* d_readTableContext; |
436 |
|
context::CDList<Node> d_arrayMerges; |
437 |
|
std::vector<CTNodeList*> d_readBucketAllocations; |
438 |
|
|
439 |
|
Node getSkolem(TNode ref); |
440 |
|
Node mkAnd(std::vector<TNode>& conjunctions, bool invert = false, unsigned startIndex = 0); |
441 |
|
void setNonLinear(TNode a); |
442 |
|
Node removeRepLoops(TNode a, TNode rep); |
443 |
|
Node expandStores(TNode s, std::vector<TNode>& assumptions, bool checkLoop = false, TNode a = TNode(), TNode b = TNode()); |
444 |
|
void mergeArrays(TNode a, TNode b); |
445 |
|
void checkStore(TNode a); |
446 |
|
void checkRowForIndex(TNode i, TNode a); |
447 |
|
void checkRowLemmas(TNode a, TNode b); |
448 |
|
void propagateRowLemma(RowLemmaType lem); |
449 |
|
void queueRowLemma(RowLemmaType lem); |
450 |
|
bool dischargeLemmas(); |
451 |
|
|
452 |
|
std::vector<Node> d_decisions; |
453 |
|
bool d_inCheckModel; |
454 |
|
int d_topLevel; |
455 |
|
|
456 |
|
/** |
457 |
|
* The decision strategy for the theory of arrays, which calls the |
458 |
|
* getNextDecisionEngineRequest function below. |
459 |
|
*/ |
460 |
19682 |
class TheoryArraysDecisionStrategy : public DecisionStrategy |
461 |
|
{ |
462 |
|
public: |
463 |
|
TheoryArraysDecisionStrategy(TheoryArrays* ta); |
464 |
|
/** initialize */ |
465 |
|
void initialize() override; |
466 |
|
/** get next decision request */ |
467 |
|
Node getNextDecisionRequest() override; |
468 |
|
/** identify */ |
469 |
|
std::string identify() const override; |
470 |
|
|
471 |
|
private: |
472 |
|
/** pointer to the theory of arrays */ |
473 |
|
TheoryArrays* d_ta; |
474 |
|
}; |
475 |
|
/** an instance of the above decision strategy */ |
476 |
|
std::unique_ptr<TheoryArraysDecisionStrategy> d_dstrat; |
477 |
|
/** Have we registered the above strategy? (context-independent) */ |
478 |
|
bool d_dstratInit; |
479 |
|
/** get the next decision request |
480 |
|
* |
481 |
|
* If the "arrays-eager-index" option is enabled, then whenever a |
482 |
|
* read-over-write lemma is generated, a decision request is also generated |
483 |
|
* for the comparison between the indexes that appears in the lemma. |
484 |
|
*/ |
485 |
|
Node getNextDecisionRequest(); |
486 |
|
/** |
487 |
|
* Compute relevant terms. This includes select nodes for the |
488 |
|
* RIntro1 and RIntro2 rules. |
489 |
|
*/ |
490 |
|
void computeRelevantTerms(std::set<Node>& termSet) override; |
491 |
|
};/* class TheoryArrays */ |
492 |
|
|
493 |
|
} // namespace arrays |
494 |
|
} // namespace theory |
495 |
|
} // namespace cvc5 |
496 |
|
|
497 |
|
#endif /* CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H */ |