1 /******************************************************************************
2 * Top contributors (to current version):
3 * Clark Barrett, Andrew Reynolds, Morgan Deters
5 * This file is part of the cvc5 project.
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 * ****************************************************************************
13 * Implementation of the theory of arrays.
16 #include "theory/arrays/theory_arrays.h"
21 #include "expr/array_store_all.h"
22 #include "expr/kind.h"
23 #include "expr/node_algorithm.h"
24 #include "options/arrays_options.h"
25 #include "options/smt_options.h"
26 #include "proof/proof_checker.h"
27 #include "smt/logic_exception.h"
28 #include "smt/smt_statistics_registry.h"
29 #include "theory/arrays/skolem_cache.h"
30 #include "theory/arrays/theory_arrays_rewriter.h"
31 #include "theory/decision_manager.h"
32 #include "theory/rewriter.h"
33 #include "theory/theory_model.h"
34 #include "theory/trust_substitutions.h"
35 #include "theory/valuation.h"
43 // These are the options that produce the best empirical results on QF_AX benchmarks.
45 // eagerIndexSplitting = false
47 // Use static configuration of options for now
48 const bool d_ccStore
= false;
49 //const bool d_eagerLemmas = false;
50 const bool d_preprocess
= true;
51 const bool d_solveWrite
= true;
52 const bool d_solveWrite2
= false;
53 // These are now options
54 //const bool d_propagateLemmas = true;
55 //bool d_useNonLinearOpt = true;
56 //bool d_eagerIndexSplitting = false;
58 TheoryArrays::TheoryArrays(context::Context
* c
,
59 context::UserContext
* u
,
62 const LogicInfo
& logicInfo
,
63 ProofNodeManager
* pnm
,
65 : Theory(THEORY_ARRAYS
, c
, u
, out
, valuation
, logicInfo
, pnm
, name
),
67 smtStatisticsRegistry().registerInt(name
+ "number of Row lemmas")),
69 smtStatisticsRegistry().registerInt(name
+ "number of Ext lemmas")),
71 smtStatisticsRegistry().registerInt(name
+ "number of propagations")),
73 smtStatisticsRegistry().registerInt(name
+ "number of explanations")),
74 d_numNonLinear(smtStatisticsRegistry().registerInt(
75 name
+ "number of calls to setNonLinear")),
76 d_numSharedArrayVarSplits(smtStatisticsRegistry().registerInt(
77 name
+ "number of shared array var splits")),
78 d_numGetModelValSplits(smtStatisticsRegistry().registerInt(
79 name
+ "number of getModelVal splits")),
80 d_numGetModelValConflicts(smtStatisticsRegistry().registerInt(
81 name
+ "number of getModelVal conflicts")),
82 d_numSetModelValSplits(smtStatisticsRegistry().registerInt(
83 name
+ "number of setModelVal splits")),
84 d_numSetModelValConflicts(smtStatisticsRegistry().registerInt(
85 name
+ "number of setModelVal conflicts")),
86 d_ppEqualityEngine(u
, name
+ "pp", true),
88 d_state(c
, u
, valuation
),
89 d_im(*this, d_state
, pnm
),
90 d_literalsToPropagate(c
),
91 d_literalsToPropagateIndex(c
, 0),
93 d_mayEqualEqualityEngine(c
, name
+ "mayEqual", true),
96 d_infoMap(c
, &d_backtracker
, name
),
98 d_mergeInProgress(false),
100 d_RowAlreadyAdded(u
),
103 d_sharedTerms(c
, false),
106 d_constReadsContext(new context::Context()),
107 d_contextPopper(c
, d_constReadsContext
),
109 d_decisionRequests(c
),
111 d_modelConstraints(c
),
114 d_readTableContext(new context::Context()),
116 d_inCheckModel(false),
117 d_dstrat(new TheoryArraysDecisionStrategy(this)),
120 d_true
= NodeManager::currentNM()->mkConst
<bool>(true);
121 d_false
= NodeManager::currentNM()->mkConst
<bool>(false);
123 // The preprocessing congruence kinds
124 d_ppEqualityEngine
.addFunctionKind(kind::SELECT
);
125 d_ppEqualityEngine
.addFunctionKind(kind::STORE
);
127 // indicate we are using the default theory state object, and the arrays
129 d_theoryState
= &d_state
;
130 d_inferManager
= &d_im
;
133 TheoryArrays::~TheoryArrays() {
134 vector
<CTNodeList
*>::iterator it
= d_readBucketAllocations
.begin(), iend
= d_readBucketAllocations
.end();
135 for (; it
!= iend
; ++it
) {
138 delete d_readTableContext
;
139 CNodeNListMap::iterator it2
= d_constReads
.begin();
140 for( ; it2
!= d_constReads
.end(); ++it2
) {
141 it2
->second
->deleteSelf();
143 delete d_constReadsContext
;
146 TheoryRewriter
* TheoryArrays::getTheoryRewriter() { return &d_rewriter
; }
148 ProofRuleChecker
* TheoryArrays::getProofChecker() { return &d_checker
; }
150 bool TheoryArrays::needsEqualityEngine(EeSetupInfo
& esi
)
152 esi
.d_notify
= &d_notify
;
153 esi
.d_name
= d_instanceName
+ "ee";
154 esi
.d_notifyNewClass
= true;
155 esi
.d_notifyMerge
= true;
159 void TheoryArrays::finishInit()
161 Assert(d_equalityEngine
!= nullptr);
163 // The kinds we are treating as function application in congruence
164 d_equalityEngine
->addFunctionKind(kind::SELECT
);
167 d_equalityEngine
->addFunctionKind(kind::STORE
);
171 /////////////////////////////////////////////////////////////////////////////
173 /////////////////////////////////////////////////////////////////////////////
176 bool TheoryArrays::ppDisequal(TNode a
, TNode b
) {
177 bool termsExist
= d_ppEqualityEngine
.hasTerm(a
) && d_ppEqualityEngine
.hasTerm(b
);
178 Assert(!termsExist
|| !a
.isConst() || !b
.isConst() || a
== b
179 || d_ppEqualityEngine
.areDisequal(a
, b
, false));
180 return ((termsExist
&& d_ppEqualityEngine
.areDisequal(a
, b
, false)) ||
181 Rewriter::rewrite(a
.eqNode(b
)) == d_false
);
185 Node
TheoryArrays::solveWrite(TNode term
, bool solve1
, bool solve2
, bool ppCheck
)
190 if (term
[0].getKind() != kind::STORE
&&
191 term
[1].getKind() != kind::STORE
) {
194 TNode left
= term
[0];
195 TNode right
= term
[1];
196 int leftWrites
= 0, rightWrites
= 0;
198 // Count nested writes
200 while (e1
.getKind() == kind::STORE
) {
206 while (e2
.getKind() == kind::STORE
) {
211 if (rightWrites
> leftWrites
) {
215 int tmpWrites
= leftWrites
;
216 leftWrites
= rightWrites
;
217 rightWrites
= tmpWrites
;
220 NodeManager
* nm
= NodeManager::currentNM();
221 if (rightWrites
== 0) {
225 // write(store, index_0, v_0, index_1, v_1, ..., index_n, v_n) = store IFF
227 // read(store, index_n) = v_n &
228 // index_{n-1} != index_n -> read(store, index_{n-1}) = v_{n-1} &
229 // (index_{n-2} != index_{n-1} & index_{n-2} != index_n) -> read(store, index_{n-2}) = v_{n-2} &
231 // (index_1 != index_2 & ... & index_1 != index_n) -> read(store, index_1) = v_1
232 // (index_0 != index_1 & index_0 != index_2 & ... & index_0 != index_n) -> read(store, index_0) = v_0
233 TNode write_i
, write_j
, index_i
, index_j
;
235 NodeBuilder
result(kind::AND
);
238 for (i
= leftWrites
-1; i
>= 0; --i
) {
239 index_i
= write_i
[1];
241 // build: [index_i /= index_n && index_i /= index_(n-1) &&
242 // ... && index_i /= index_(i+1)] -> read(store, index_i) = v_i
245 NodeBuilder
hyp(kind::AND
);
246 for (j
= leftWrites
- 1; j
> i
; --j
) {
247 index_j
= write_j
[1];
248 if (!ppCheck
|| !ppDisequal(index_i
, index_j
)) {
249 Node
hyp2(index_i
.eqNode(index_j
));
250 hyp
<< hyp2
.notNode();
252 write_j
= write_j
[0];
255 Node r1
= nm
->mkNode(kind::SELECT
, e1
, index_i
);
256 conc
= r1
.eqNode(write_i
[2]);
257 if (hyp
.getNumChildren() != 0) {
258 if (hyp
.getNumChildren() == 1) {
259 conc
= hyp
.getChild(0).impNode(conc
);
263 conc
= r1
.impNode(conc
);
270 // Prepare for next iteration
271 write_i
= write_i
[0];
274 Assert(result
.getNumChildren() > 0);
275 if (result
.getNumChildren() == 1) {
276 return result
.getChild(0);
284 // store(...) = store(a,i,v) ==>
285 // store(store(...),i,select(a,i)) = a && select(store(...),i)=v
288 NodeBuilder
nb(kind::AND
);
289 while (right
.getKind() == kind::STORE
) {
290 tmp
= nm
->mkNode(kind::SELECT
, l
, right
[1]);
291 nb
<< tmp
.eqNode(right
[2]);
292 tmp
= nm
->mkNode(kind::SELECT
, right
[0], right
[1]);
293 l
= nm
->mkNode(kind::STORE
, l
, right
[1], tmp
);
296 nb
<< solveWrite(l
.eqNode(right
), solve1
, solve2
, ppCheck
);
303 TrustNode
TheoryArrays::ppRewrite(TNode term
, std::vector
<SkolemLemma
>& lems
)
305 // first, see if we need to expand definitions
306 TrustNode texp
= d_rewriter
.expandDefinition(term
);
313 return TrustNode::null();
315 d_ppEqualityEngine
.addTerm(term
);
316 NodeManager
* nm
= NodeManager::currentNM();
318 switch (term
.getKind()) {
320 // select(store(a,i,v),j) = select(a,j)
322 if (term
[0].getKind() == kind::STORE
&& ppDisequal(term
[0][1], term
[1])) {
323 ret
= nm
->mkNode(kind::SELECT
, term
[0][0], term
[1]);
328 // store(store(a,i,v),j,w) = store(store(a,j,w),i,v)
329 // IF i != j and j comes before i in the ordering
330 if (term
[0].getKind() == kind::STORE
&& (term
[1] < term
[0][1]) && ppDisequal(term
[1],term
[0][1])) {
331 Node inner
= nm
->mkNode(kind::STORE
, term
[0][0], term
[1], term
[2]);
332 Node outer
= nm
->mkNode(kind::STORE
, inner
, term
[0][1], term
[0][2]);
338 ret
= solveWrite(term
, d_solveWrite
, d_solveWrite2
, true);
344 if (!ret
.isNull() && ret
!= term
)
346 return TrustNode::mkTrustRewrite(term
, ret
, nullptr);
348 return TrustNode::null();
351 Theory::PPAssertStatus
TheoryArrays::ppAssert(
352 TrustNode tin
, TrustSubstitutionMap
& outSubstitutions
)
354 TNode in
= tin
.getNode();
355 switch(in
.getKind()) {
358 d_ppFacts
.push_back(in
);
359 d_ppEqualityEngine
.assertEquality(in
, true, in
);
360 if (in
[0].isVar() && isLegalElimination(in
[0], in
[1]))
362 outSubstitutions
.addSubstitutionSolved(in
[0], in
[1], tin
);
363 return PP_ASSERT_STATUS_SOLVED
;
365 if (in
[1].isVar() && isLegalElimination(in
[1], in
[0]))
367 outSubstitutions
.addSubstitutionSolved(in
[1], in
[0], tin
);
368 return PP_ASSERT_STATUS_SOLVED
;
374 d_ppFacts
.push_back(in
);
375 if (in
[0].getKind() == kind::EQUAL
) {
378 d_ppEqualityEngine
.assertEquality(in
[0], false, in
);
385 return PP_ASSERT_STATUS_UNSOLVED
;
389 /////////////////////////////////////////////////////////////////////////////
390 // T-PROPAGATION / REGISTRATION
391 /////////////////////////////////////////////////////////////////////////////
393 bool TheoryArrays::propagateLit(TNode literal
)
395 Debug("arrays") << spaces(getSatContext()->getLevel())
396 << "TheoryArrays::propagateLit(" << literal
<< ")"
399 // If already in conflict, no more propagation
400 if (d_state
.isInConflict())
402 Debug("arrays") << spaces(getSatContext()->getLevel())
403 << "TheoryArrays::propagateLit(" << literal
404 << "): already in conflict" << std::endl
;
409 if (d_inCheckModel
&& getSatContext()->getLevel() != d_topLevel
) {
412 bool ok
= d_out
->propagate(literal
);
414 d_state
.notifyInConflict();
417 }/* TheoryArrays::propagate(TNode) */
420 TNode
TheoryArrays::weakEquivGetRep(TNode node
) {
423 pointer
= d_infoMap
.getWeakEquivPointer(node
);
424 if (pointer
.isNull()) {
431 TNode
TheoryArrays::weakEquivGetRepIndex(TNode node
, TNode index
) {
432 Assert(!index
.isNull());
433 TNode pointer
, index2
;
435 pointer
= d_infoMap
.getWeakEquivPointer(node
);
436 if (pointer
.isNull()) {
439 index2
= d_infoMap
.getWeakEquivIndex(node
);
440 if (index2
.isNull() || !d_equalityEngine
->areEqual(index
, index2
))
445 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
446 if (secondary
.isNull()) {
454 void TheoryArrays::visitAllLeaves(TNode reason
, vector
<TNode
>& conjunctions
) {
455 switch (reason
.getKind()) {
457 Assert(reason
.getNumChildren() == 2);
458 visitAllLeaves(reason
[0], conjunctions
);
459 visitAllLeaves(reason
[1], conjunctions
);
462 conjunctions
.push_back(reason
);
465 d_equalityEngine
->explainEquality(
466 reason
[0], reason
[1], true, conjunctions
);
473 void TheoryArrays::weakEquivBuildCond(TNode node
, TNode index
, vector
<TNode
>& conjunctions
) {
474 Assert(!index
.isNull());
475 TNode pointer
, index2
;
477 pointer
= d_infoMap
.getWeakEquivPointer(node
);
478 if (pointer
.isNull()) {
481 index2
= d_infoMap
.getWeakEquivIndex(node
);
482 if (index2
.isNull()) {
483 // Null index means these two nodes became equal: explain the equality.
484 d_equalityEngine
->explainEquality(node
, pointer
, true, conjunctions
);
487 else if (!d_equalityEngine
->areEqual(index
, index2
))
489 // If indices are not equal in current context, need to add that to the lemma.
490 Node reason
= index
.eqNode(index2
).notNode();
491 d_permRef
.push_back(reason
);
492 conjunctions
.push_back(reason
);
496 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
497 if (secondary
.isNull()) {
500 TNode reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
501 Assert(!reason
.isNull());
502 visitAllLeaves(reason
, conjunctions
);
508 void TheoryArrays::weakEquivMakeRep(TNode node
) {
509 TNode pointer
= d_infoMap
.getWeakEquivPointer(node
);
510 if (pointer
.isNull()) {
513 weakEquivMakeRep(pointer
);
514 d_infoMap
.setWeakEquivPointer(pointer
, node
);
515 d_infoMap
.setWeakEquivIndex(pointer
, d_infoMap
.getWeakEquivIndex(node
));
516 d_infoMap
.setWeakEquivPointer(node
, TNode());
517 weakEquivMakeRepIndex(node
);
520 void TheoryArrays::weakEquivMakeRepIndex(TNode node
) {
521 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
522 if (secondary
.isNull()) {
525 TNode index
= d_infoMap
.getWeakEquivIndex(node
);
526 Assert(!index
.isNull());
527 TNode index2
= d_infoMap
.getWeakEquivIndex(secondary
);
530 while (index2
.isNull() || !d_equalityEngine
->areEqual(index
, index2
))
532 next
= d_infoMap
.getWeakEquivPointer(secondary
);
533 d_infoMap
.setWeakEquivSecondary(node
, next
);
534 reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
535 if (index2
.isNull()) {
536 reason
= reason
.andNode(secondary
.eqNode(next
));
539 reason
= reason
.andNode(index
.eqNode(index2
).notNode());
541 d_permRef
.push_back(reason
);
542 d_infoMap
.setWeakEquivSecondaryReason(node
, reason
);
547 index2
= d_infoMap
.getWeakEquivIndex(secondary
);
549 weakEquivMakeRepIndex(secondary
);
550 d_infoMap
.setWeakEquivSecondary(secondary
, node
);
551 d_infoMap
.setWeakEquivSecondaryReason(secondary
, d_infoMap
.getWeakEquivSecondaryReason(node
));
552 d_infoMap
.setWeakEquivSecondary(node
, TNode());
553 d_infoMap
.setWeakEquivSecondaryReason(node
, TNode());
556 void TheoryArrays::weakEquivAddSecondary(TNode index
, TNode arrayFrom
, TNode arrayTo
, TNode reason
) {
557 std::unordered_set
<TNode
> marked
;
558 vector
<TNode
> index_trail
;
559 vector
<TNode
>::iterator it
, iend
;
560 Node equivalence_trail
= reason
;
562 TNode pointer
, indexRep
;
563 if (!index
.isNull()) {
564 index_trail
.push_back(index
);
565 marked
.insert(d_equalityEngine
->getRepresentative(index
));
567 while (arrayFrom
!= arrayTo
) {
568 index
= d_infoMap
.getWeakEquivIndex(arrayFrom
);
569 pointer
= d_infoMap
.getWeakEquivPointer(arrayFrom
);
570 if (!index
.isNull()) {
571 indexRep
= d_equalityEngine
->getRepresentative(index
);
572 if (marked
.find(indexRep
) == marked
.end() && weakEquivGetRepIndex(arrayFrom
, index
) != arrayTo
) {
573 weakEquivMakeRepIndex(arrayFrom
);
574 d_infoMap
.setWeakEquivSecondary(arrayFrom
, arrayTo
);
575 current_reason
= equivalence_trail
;
576 for (it
= index_trail
.begin(), iend
= index_trail
.end(); it
!= iend
; ++it
) {
577 current_reason
= current_reason
.andNode(index
.eqNode(*it
).notNode());
579 d_permRef
.push_back(current_reason
);
580 d_infoMap
.setWeakEquivSecondaryReason(arrayFrom
, current_reason
);
582 marked
.insert(indexRep
);
585 equivalence_trail
= equivalence_trail
.andNode(arrayFrom
.eqNode(pointer
));
591 void TheoryArrays::checkWeakEquiv(bool arraysMerged
) {
592 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(&d_mayEqualEqualityEngine
);
593 for (; !eqcs_i
.isFinished(); ++eqcs_i
) {
594 Node eqc
= (*eqcs_i
);
595 if (!eqc
.getType().isArray()) {
598 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, &d_mayEqualEqualityEngine
);
599 TNode rep
= d_mayEqualEqualityEngine
.getRepresentative(*eqc_i
);
600 TNode weakEquivRep
= weakEquivGetRep(rep
);
601 for (; !eqc_i
.isFinished(); ++eqc_i
) {
603 Assert(!arraysMerged
|| weakEquivGetRep(n
) == weakEquivRep
);
604 TNode pointer
= d_infoMap
.getWeakEquivPointer(n
);
605 TNode index
= d_infoMap
.getWeakEquivIndex(n
);
606 TNode secondary
= d_infoMap
.getWeakEquivSecondary(n
);
607 Assert(pointer
.isNull() == (weakEquivGetRep(n
) == n
));
608 Assert(!pointer
.isNull() || secondary
.isNull());
609 Assert(!index
.isNull() || secondary
.isNull());
610 Assert(d_infoMap
.getWeakEquivSecondaryReason(n
).isNull()
611 || !secondary
.isNull());
612 if (!pointer
.isNull()) {
613 if (index
.isNull()) {
614 Assert(d_equalityEngine
->areEqual(n
, pointer
));
618 (n
.getKind() == kind::STORE
&& n
[0] == pointer
&& n
[1] == index
)
619 || (pointer
.getKind() == kind::STORE
&& pointer
[0] == n
620 && pointer
[1] == index
));
628 * Stores in d_infoMap the following information for each term a of type array:
630 * - all i, such that there exists a term a[i] or a = store(b i v)
631 * (i.e. all indices it is being read atl; store(b i v) is implicitly read at
632 * position i due to the implicit axiom store(b i v)[i] = v )
634 * - all the stores a is congruent to (this information is context dependent)
636 * - all store terms of the form store (a i v) (i.e. in which a appears
637 * directly; this is invariant because no new store terms are created)
639 * Note: completeness depends on having pre-register called on all the input
640 * terms before starting to instantiate lemmas.
642 void TheoryArrays::preRegisterTermInternal(TNode node
)
644 if (d_state
.isInConflict())
648 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node
<< ")" << std::endl
;
649 Kind nk
= node
.getKind();
650 if (nk
== kind::EQUAL
)
652 // Add the trigger for equality
653 // NOTE: note that if the equality is true or false already, it might not be added
654 d_equalityEngine
->addTriggerPredicate(node
);
657 else if (d_equalityEngine
->hasTerm(node
))
659 // Invariant: array terms should be preregistered before being added to the equality engine
660 Assert(nk
!= kind::SELECT
661 || d_isPreRegistered
.find(node
) != d_isPreRegistered
.end());
664 // add to equality engine and the may equality engine
665 TypeNode nodeType
= node
.getType();
666 if (nodeType
.isArray())
668 // index type should not be an array, otherwise we throw a logic exception
669 if (nodeType
.getArrayIndexType().isArray())
671 std::stringstream ss
;
672 ss
<< "Arrays cannot be indexed by array types, offending array type is "
674 throw LogicException(ss
.str());
676 d_mayEqualEqualityEngine
.addTerm(node
);
678 d_equalityEngine
->addTerm(node
);
680 switch (node
.getKind())
685 TNode store
= d_equalityEngine
->getRepresentative(node
[0]);
687 // The may equal needs the store
688 d_mayEqualEqualityEngine
.addTerm(store
);
690 Assert((d_isPreRegistered
.insert(node
), true));
692 Assert(d_equalityEngine
->getRepresentative(store
) == store
);
693 d_infoMap
.addIndex(store
, node
[1]);
695 // Synchronize d_constReadsContext with SAT context
696 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
697 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel())
699 d_constReadsContext
->push();
702 // Record read in sharing data structure
703 TNode index
= d_equalityEngine
->getRepresentative(node
[1]);
704 if (!options::arraysWeakEquivalence() && index
.isConst())
707 CNodeNListMap::iterator it
= d_constReads
.find(index
);
708 if (it
== d_constReads
.end())
710 temp
= new (true) CTNodeList(d_constReadsContext
);
711 d_constReads
[index
] = temp
;
717 temp
->push_back(node
);
718 d_constReadsList
.push_back(node
);
722 d_reads
.push_back(node
);
725 checkRowForIndex(node
[1], store
);
730 TNode a
= d_equalityEngine
->getRepresentative(node
[0]);
734 // Can't use d_mayEqualEqualityEngine to merge node with a because they
735 // are both constants, so just set the default value manually for node.
736 Assert(a
== node
[0]);
737 d_mayEqualEqualityEngine
.addTerm(node
);
738 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
739 Assert(d_mayEqualEqualityEngine
.getRepresentative(a
) == a
);
740 DefValMap::iterator it
= d_defValues
.find(a
);
741 Assert(it
!= d_defValues
.end());
742 d_defValues
[node
] = (*it
).second
;
746 d_mayEqualEqualityEngine
.assertEquality(node
.eqNode(a
), true, d_true
);
747 Assert(d_mayEqualEqualityEngine
.consistent());
752 NodeManager
* nm
= NodeManager::currentNM();
753 Node ni
= nm
->mkNode(kind::SELECT
, node
, i
);
754 if (!d_equalityEngine
->hasTerm(ni
))
756 preRegisterTermInternal(ni
);
758 // Apply RIntro1 Rule
759 d_im
.assertInference(ni
.eqNode(v
),
761 InferenceId::ARRAYS_READ_OVER_WRITE_1
,
763 PfRule::ARRAYS_READ_OVER_WRITE_1
);
765 d_infoMap
.addStore(node
, node
);
766 d_infoMap
.addInStore(a
, node
);
767 d_infoMap
.setModelRep(node
, node
);
769 // Add-Store for Weak Equivalence
770 if (options::arraysWeakEquivalence())
772 Assert(weakEquivGetRep(node
[0]) == weakEquivGetRep(a
));
773 Assert(weakEquivGetRep(node
) == node
);
774 d_infoMap
.setWeakEquivPointer(node
, node
[0]);
775 d_infoMap
.setWeakEquivIndex(node
, node
[1]);
776 #ifdef CVC5_ASSERTIONS
777 checkWeakEquiv(false);
784 case kind::STORE_ALL
: {
785 ArrayStoreAll storeAll
= node
.getConst
<ArrayStoreAll
>();
786 Node defaultValue
= storeAll
.getValue();
787 if (!defaultValue
.isConst()) {
788 throw LogicException("Array theory solver does not yet support non-constant default values for arrays");
790 d_infoMap
.setConstArr(node
, node
);
791 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
792 d_defValues
[node
] = defaultValue
;
796 // Variables etc, already processed above
799 // Invariant: preregistered terms are exactly the terms in the equality engine
800 // Disabled, see comment above for kind::EQUAL
801 // Assert(d_equalityEngine->hasTerm(node) ||
802 // !d_equalityEngine->consistent());
805 void TheoryArrays::preRegisterTerm(TNode node
)
807 preRegisterTermInternal(node
);
808 // If we have a select from an array of Bools, mark it as a term that can be propagated.
809 // Note: do this here instead of in preRegisterTermInternal to prevent internal select
810 // terms from being propagated out (as this results in an assertion failure).
811 if (node
.getKind() == kind::SELECT
&& node
.getType().isBoolean()) {
812 d_equalityEngine
->addTriggerPredicate(node
);
816 void TheoryArrays::explain(TNode literal
, Node
& explanation
)
819 Debug("arrays") << spaces(getSatContext()->getLevel())
820 << "TheoryArrays::explain(" << literal
<< ")" << std::endl
;
821 std::vector
<TNode
> assumptions
;
823 bool polarity
= literal
.getKind() != kind::NOT
;
824 TNode atom
= polarity
? literal
: literal
[0];
825 if (atom
.getKind() == kind::EQUAL
)
827 d_equalityEngine
->explainEquality(
828 atom
[0], atom
[1], polarity
, assumptions
, nullptr);
832 d_equalityEngine
->explainPredicate(atom
, polarity
, assumptions
, nullptr);
834 explanation
= mkAnd(assumptions
);
837 TrustNode
TheoryArrays::explain(TNode literal
)
839 return d_im
.explainLit(literal
);
842 /////////////////////////////////////////////////////////////////////////////
844 /////////////////////////////////////////////////////////////////////////////
846 void TheoryArrays::notifySharedTerm(TNode t
)
848 Debug("arrays::sharing") << spaces(getSatContext()->getLevel())
849 << "TheoryArrays::notifySharedTerm(" << t
<< ")"
851 if (t
.getType().isArray()) {
852 d_sharedArrays
.insert(t
);
855 #ifdef CVC5_ASSERTIONS
856 d_sharedOther
.insert(t
);
858 d_sharedTerms
= true;
862 void TheoryArrays::checkPair(TNode r1
, TNode r2
)
864 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1
<< " and " << r2
<< std::endl
;
868 Assert(d_equalityEngine
->isTriggerTerm(x
, THEORY_ARRAYS
));
870 if (d_equalityEngine
->hasTerm(x
) && d_equalityEngine
->hasTerm(y
)
871 && (d_equalityEngine
->areEqual(x
, y
)
872 || d_equalityEngine
->areDisequal(x
, y
, false)))
874 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl
;
878 // If the terms are already known to be equal, we are also in good shape
879 if (d_equalityEngine
->areEqual(r1
, r2
))
881 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl
;
885 if (r1
[0] != r2
[0]) {
886 // If arrays are known to be disequal, or cannot become equal, we can continue
887 Assert(d_mayEqualEqualityEngine
.hasTerm(r1
[0])
888 && d_mayEqualEqualityEngine
.hasTerm(r2
[0]));
889 if (r1
[0].getType() != r2
[0].getType()
890 || d_equalityEngine
->areDisequal(r1
[0], r2
[0], false))
892 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl
;
895 else if (!d_mayEqualEqualityEngine
.areEqual(r1
[0], r2
[0])) {
900 if (!d_equalityEngine
->isTriggerTerm(y
, THEORY_ARRAYS
))
902 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
906 // Get representative trigger terms
908 d_equalityEngine
->getTriggerTermRepresentative(x
, THEORY_ARRAYS
);
910 d_equalityEngine
->getTriggerTermRepresentative(y
, THEORY_ARRAYS
);
911 EqualityStatus eqStatusDomain
= d_valuation
.getEqualityStatus(x_shared
, y_shared
);
912 switch (eqStatusDomain
) {
913 case EQUALITY_TRUE_AND_PROPAGATED
:
914 // Should have been propagated to us
918 // Missed propagation - need to add the pair so that theory engine can force propagation
919 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): missed propagation" << std::endl
;
921 case EQUALITY_FALSE_AND_PROPAGATED
:
922 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair "
923 "called when false in model"
925 // Should have been propagated to us
928 case EQUALITY_FALSE
: CVC5_FALLTHROUGH
;
929 case EQUALITY_FALSE_IN_MODEL
:
930 // This is unlikely, but I think it could happen
931 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair called when false in model" << std::endl
;
934 // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
939 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl
;
940 addCarePair(x_shared
, y_shared
);
944 void TheoryArrays::computeCareGraph()
946 if (d_sharedArrays
.size() > 0) {
947 CDNodeSet::key_iterator it1
= d_sharedArrays
.key_begin(), it2
, iend
= d_sharedArrays
.key_end();
948 for (; it1
!= iend
; ++it1
) {
949 for (it2
= it1
, ++it2
; it2
!= iend
; ++it2
) {
950 if ((*it1
).getType() != (*it2
).getType()) {
953 EqualityStatus eqStatusArr
= getEqualityStatus((*it1
), (*it2
));
954 if (eqStatusArr
!= EQUALITY_UNKNOWN
) {
957 Assert(d_valuation
.getEqualityStatus((*it1
), (*it2
))
958 == EQUALITY_UNKNOWN
);
959 addCarePair((*it1
), (*it2
));
960 ++d_numSharedArrayVarSplits
;
966 // Synchronize d_constReadsContext with SAT context
967 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
968 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel()) {
969 d_constReadsContext
->push();
972 // Go through the read terms and see if there are any to split on
974 // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
975 // The context is popped at the end. If this loop is interrupted for some reason, we have to make sure the context still
976 // gets popped or the solver will be in an inconsistent state
977 d_constReadsContext
->push();
978 unsigned size
= d_reads
.size();
979 for (unsigned i
= 0; i
< size
; ++ i
) {
980 TNode r1
= d_reads
[i
];
982 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1
<< std::endl
;
983 Assert(d_equalityEngine
->hasTerm(r1
));
986 if (!d_equalityEngine
->isTriggerTerm(x
, THEORY_ARRAYS
))
988 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
992 d_equalityEngine
->getTriggerTermRepresentative(x
, THEORY_ARRAYS
);
994 // Get the model value of index and find all reads that read from that same model value: these are the pairs we have to check
995 // Also, insert this read in the list at the proper index
997 if (!x_shared
.isConst()) {
998 x_shared
= d_valuation
.getModelValue(x_shared
);
1000 if (!x_shared
.isNull()) {
1002 CNodeNListMap::iterator it
= d_constReads
.find(x_shared
);
1003 if (it
== d_constReads
.end()) {
1004 // This is the only x_shared with this model value - no need to create any splits
1005 temp
= new(true) CTNodeList(d_constReadsContext
);
1006 d_constReads
[x_shared
] = temp
;
1009 temp
= (*it
).second
;
1010 for (size_t j
= 0; j
< temp
->size(); ++j
) {
1011 checkPair(r1
, (*temp
)[j
]);
1014 temp
->push_back(r1
);
1017 // We don't know the model value for x. Just do brute force examination of all pairs of reads
1018 for (unsigned j
= 0; j
< size
; ++j
) {
1019 TNode r2
= d_reads
[j
];
1020 Assert(d_equalityEngine
->hasTerm(r2
));
1023 for (unsigned j
= 0; j
< d_constReadsList
.size(); ++j
) {
1024 TNode r2
= d_constReadsList
[j
];
1025 Assert(d_equalityEngine
->hasTerm(r2
));
1030 d_constReadsContext
->pop();
1035 /////////////////////////////////////////////////////////////////////////////
1037 /////////////////////////////////////////////////////////////////////////////
1039 bool TheoryArrays::collectModelValues(TheoryModel
* m
,
1040 const std::set
<Node
>& termSet
)
1042 // termSet contains terms appearing in assertions and shared terms, and also
1043 // includes additional reads due to the RIntro1 and RIntro2 rules.
1044 NodeManager
* nm
= NodeManager::currentNM();
1045 // Compute arrays that we need to produce representatives for
1046 std::vector
<Node
> arrays
;
1048 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
1049 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
1051 Node eqc
= (*eqcs_i
);
1052 if (!eqc
.getType().isArray())
1054 // not an array, skip
1057 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
1058 for (; !eqc_i
.isFinished(); ++eqc_i
)
1061 // If this EC is an array type and it contains something other than STORE
1062 // nodes, we have to compute a representative explicitly
1063 if (termSet
.find(n
) != termSet
.end())
1065 if (n
.getKind() != kind::STORE
)
1067 arrays
.push_back(n
);
1074 // Build a list of all the relevant reads, indexed by the store representative
1075 std::map
<Node
, std::vector
<Node
> > selects
;
1076 set
<Node
>::iterator set_it
= termSet
.begin(), set_it_end
= termSet
.end();
1077 for (; set_it
!= set_it_end
; ++set_it
)
1080 // If this term is a select, record that the EC rep of its store parameter
1081 // is being read from using this term
1082 if (n
.getKind() == kind::SELECT
)
1084 selects
[d_equalityEngine
->getRepresentative(n
[0])].push_back(n
);
1089 DefValMap::iterator it
;
1090 TypeSet defaultValuesSet
;
1092 // Compute all default values already in use
1094 for (size_t i
= 0; i
< arrays
.size(); ++i
)
1096 TNode nrep
= d_equalityEngine
->getRepresentative(arrays
[i
]);
1097 d_mayEqualEqualityEngine
.addTerm(
1098 nrep
); // add the term in case it isn't there already
1099 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1100 it
= d_defValues
.find(mayRep
);
1101 if (it
!= d_defValues
.end())
1103 defaultValuesSet
.add(nrep
.getType().getArrayConstituentType(),
1109 // Loop through all array equivalence classes that need a representative
1111 for (size_t i
= 0; i
< arrays
.size(); ++i
)
1113 TNode n
= arrays
[i
];
1114 TNode nrep
= d_equalityEngine
->getRepresentative(n
);
1117 // Compute default value for this array - there is one default value for
1118 // every mayEqual equivalence class
1119 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1120 it
= d_defValues
.find(mayRep
);
1121 // If this mayEqual EC doesn't have a default value associated, get the next
1122 // available default value for the associated array element type
1123 if (it
== d_defValues
.end())
1125 TypeNode valueType
= nrep
.getType().getArrayConstituentType();
1126 rep
= defaultValuesSet
.nextTypeEnum(valueType
);
1129 Assert(defaultValuesSet
.getSet(valueType
)->begin()
1130 != defaultValuesSet
.getSet(valueType
)->end());
1131 rep
= *(defaultValuesSet
.getSet(valueType
)->begin());
1133 Trace("arrays-models") << "New default value = " << rep
<< endl
;
1134 d_defValues
[mayRep
] = rep
;
1141 // Build the STORE_ALL term with the default value
1142 rep
= nm
->mkConst(ArrayStoreAll(nrep
.getType(), rep
));
1146 std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(n);
1147 if (it == d_skolemCache.end()) {
1148 rep = nm->mkSkolem("array_collect_model_var", n.getType(), "base model
1149 variable for array collectModelInfo"); d_skolemCache[n] = rep;
1157 // For each read, require that the rep stores the right value
1158 vector
<Node
>& reads
= selects
[nrep
];
1159 for (unsigned j
= 0; j
< reads
.size(); ++j
)
1161 rep
= nm
->mkNode(kind::STORE
, rep
, reads
[j
][1], reads
[j
]);
1163 if (!m
->assertEquality(n
, rep
, true))
1169 m
->assertSkeleton(rep
);
1175 /////////////////////////////////////////////////////////////////////////////
1177 /////////////////////////////////////////////////////////////////////////////
1180 void TheoryArrays::presolve()
1182 Trace("arrays")<<"Presolving \n";
1185 d_dstratInit
= true;
1186 // add the decision strategy, which is user-context-independent
1187 d_im
.getDecisionManager()->registerStrategy(
1188 DecisionManager::STRAT_ARRAYS
,
1190 DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT
);
1195 /////////////////////////////////////////////////////////////////////////////
1197 /////////////////////////////////////////////////////////////////////////////
1199 Node
TheoryArrays::getSkolem(TNode ref
)
1201 // the call to SkolemCache::getExtIndexSkolem should be deterministic, but use
1202 // cache anyways for now
1204 std::unordered_map
<Node
, Node
>::iterator it
= d_skolemCache
.find(ref
);
1205 if (it
== d_skolemCache
.end()) {
1206 Assert(ref
.getKind() == kind::NOT
&& ref
[0].getKind() == kind::EQUAL
);
1207 // make the skolem using the skolem cache utility
1208 skolem
= SkolemCache::getExtIndexSkolem(ref
);
1209 d_skolemCache
[ref
] = skolem
;
1212 skolem
= (*it
).second
;
1215 Debug("pf::array") << "Pregistering a Skolem" << std::endl
;
1216 preRegisterTermInternal(skolem
);
1217 Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl
;
1219 Debug("pf::array") << "getSkolem DONE" << std::endl
;
1223 void TheoryArrays::postCheck(Effort level
)
1225 if ((options::arraysEagerLemmas() || fullEffort(level
))
1226 && !d_state
.isInConflict() && options::arraysWeakEquivalence())
1228 // Replay all array merges to update weak equivalence data structures
1229 context::CDList
<Node
>::iterator it
= d_arrayMerges
.begin(), iend
= d_arrayMerges
.end();
1231 for (; it
!= iend
; ++it
) {
1235 weakEquivMakeRep(b
);
1236 if (weakEquivGetRep(a
) == b
) {
1237 weakEquivAddSecondary(TNode(), a
, b
, eq
);
1240 d_infoMap
.setWeakEquivPointer(b
, a
);
1241 d_infoMap
.setWeakEquivIndex(b
, TNode());
1243 #ifdef CVC5_ASSERTIONS
1244 checkWeakEquiv(false);
1247 #ifdef CVC5_ASSERTIONS
1248 checkWeakEquiv(true);
1251 d_readTableContext
->push();
1253 CTNodeList
* bucketList
= NULL
;
1254 CTNodeList::const_iterator i
= d_reads
.begin(), readsEnd
= d_reads
.end();
1255 for (; i
!= readsEnd
; ++i
) {
1256 const TNode
& r
= *i
;
1258 Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r
<< std::endl
;
1260 // Find the bucket for this read.
1261 mayRep
= d_mayEqualEqualityEngine
.getRepresentative(r
[0]);
1262 iRep
= d_equalityEngine
->getRepresentative(r
[1]);
1263 std::pair
<TNode
, TNode
> key(mayRep
, iRep
);
1264 ReadBucketMap::iterator rbm_it
= d_readBucketTable
.find(key
);
1265 if (rbm_it
== d_readBucketTable
.end())
1267 bucketList
= new(true) CTNodeList(d_readTableContext
);
1268 d_readBucketAllocations
.push_back(bucketList
);
1269 d_readBucketTable
[key
] = bucketList
;
1272 bucketList
= rbm_it
->second
;
1274 CTNodeList::const_iterator ctnl_it
= bucketList
->begin(),
1275 ctnl_iend
= bucketList
->end();
1276 for (; ctnl_it
!= ctnl_iend
; ++ctnl_it
)
1278 const TNode
& r2
= *ctnl_it
;
1279 Assert(r2
.getKind() == kind::SELECT
);
1280 Assert(mayRep
== d_mayEqualEqualityEngine
.getRepresentative(r2
[0]));
1281 Assert(iRep
== d_equalityEngine
->getRepresentative(r2
[1]));
1282 if (d_equalityEngine
->areEqual(r
, r2
))
1286 if (weakEquivGetRepIndex(r
[0], r
[1]) == weakEquivGetRepIndex(r2
[0], r
[1])) {
1287 // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1288 vector
<TNode
> conjunctions
;
1289 Assert(d_equalityEngine
->areEqual(r
, Rewriter::rewrite(r
)));
1290 Assert(d_equalityEngine
->areEqual(r2
, Rewriter::rewrite(r2
)));
1291 Node lemma
= Rewriter::rewrite(r
).eqNode(Rewriter::rewrite(r2
)).negate();
1292 d_permRef
.push_back(lemma
);
1293 conjunctions
.push_back(lemma
);
1294 if (r
[1] != r2
[1]) {
1295 d_equalityEngine
->explainEquality(r
[1], r2
[1], true, conjunctions
);
1297 // TODO: get smaller lemmas by eliminating shared parts of path
1298 weakEquivBuildCond(r
[0], r
[1], conjunctions
);
1299 weakEquivBuildCond(r2
[0], r
[1], conjunctions
);
1300 lemma
= mkAnd(conjunctions
, true);
1301 // LSH FIXME: which kind of arrays lemma is this
1303 << "Arrays::addExtLemma (weak-eq) " << lemma
<< "\n";
1304 d_out
->lemma(lemma
, LemmaProperty::SEND_ATOMS
);
1305 d_readTableContext
->pop();
1306 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl
;
1310 bucketList
->push_back(r
);
1312 d_readTableContext
->pop();
1315 if (!options::arraysEagerLemmas() && fullEffort(level
)
1316 && !d_state
.isInConflict() && !options::arraysWeakEquivalence())
1318 // generate the lemmas on the worklist
1319 Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue
.size() << "\n";
1320 while (d_RowQueue
.size() > 0 && !d_state
.isInConflict())
1322 if (dischargeLemmas()) {
1328 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl
;
1331 bool TheoryArrays::preNotifyFact(
1332 TNode atom
, bool pol
, TNode fact
, bool isPrereg
, bool isInternal
)
1334 if (!isInternal
&& !isPrereg
)
1336 if (atom
.getKind() == kind::EQUAL
)
1338 if (!d_equalityEngine
->hasTerm(atom
[0]))
1340 Assert(atom
[0].isConst());
1341 d_equalityEngine
->addTerm(atom
[0]);
1343 if (!d_equalityEngine
->hasTerm(atom
[1]))
1345 Assert(atom
[1].isConst());
1346 d_equalityEngine
->addTerm(atom
[1]);
1353 void TheoryArrays::notifyFact(TNode atom
, bool pol
, TNode fact
, bool isInternal
)
1356 if (atom
.getKind() == kind::EQUAL
&& !pol
&& !isInternal
)
1358 // Notice that this should be an external assertion, since we do not
1359 // internally infer disequalities.
1360 // Apply ArrDiseq Rule if diseq is between arrays
1361 if (fact
[0][0].getType().isArray() && !d_state
.isInConflict())
1363 NodeManager
* nm
= NodeManager::currentNM();
1366 // k is the skolem for this disequality.
1367 Debug("pf::array") << "Check: kind::NOT: array theory making a skolem"
1370 // If not in replay mode, generate a fresh skolem variable
1371 k
= getSkolem(fact
);
1373 Node ak
= nm
->mkNode(kind::SELECT
, fact
[0][0], k
);
1374 Node bk
= nm
->mkNode(kind::SELECT
, fact
[0][1], k
);
1375 Node eq
= ak
.eqNode(bk
);
1376 Node lemma
= fact
[0].orNode(eq
.notNode());
1378 if (options::arraysPropagate() > 0 && d_equalityEngine
->hasTerm(ak
)
1379 && d_equalityEngine
->hasTerm(bk
))
1381 // Propagate witness disequality - might produce a conflict
1382 Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1383 << "\teq = " << eq
<< std::endl
1384 << "\treason = " << fact
<< std::endl
;
1385 d_im
.assertInference(eq
, false, InferenceId::ARRAYS_EXT
, fact
, PfRule::ARRAYS_EXT
);
1389 // If this is the solution pass, generate the lemma. Otherwise, don't
1390 // generate it - as this is the lemma that we're reproving...
1391 Trace("arrays-lem") << "Arrays::addExtLemma " << lemma
<< "\n";
1392 d_im
.arrayLemma(eq
.notNode(), InferenceId::ARRAYS_EXT
, fact
, PfRule::ARRAYS_EXT
);
1397 Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem"
1399 d_modelConstraints
.push_back(fact
);
1404 Node
TheoryArrays::mkAnd(std::vector
<TNode
>& conjunctions
, bool invert
, unsigned startIndex
)
1406 if (conjunctions
.empty())
1408 return invert
? d_false
: d_true
;
1411 std::set
<TNode
> all
;
1413 unsigned i
= startIndex
;
1415 for (; i
< conjunctions
.size(); ++i
) {
1416 t
= conjunctions
[i
];
1420 else if (t
.getKind() == kind::AND
) {
1421 for(TNode::iterator child_it
= t
.begin(); child_it
!= t
.end(); ++child_it
) {
1422 if (*child_it
== d_true
) {
1425 all
.insert(*child_it
);
1433 if (all
.size() == 0) {
1434 return invert
? d_false
: d_true
;
1436 if (all
.size() == 1) {
1437 // All the same, or just one
1439 return (*(all
.begin())).negate();
1442 return *(all
.begin());
1446 NodeBuilder
conjunction(invert
? kind::OR
: kind::AND
);
1447 std::set
<TNode
>::const_iterator it
= all
.begin();
1448 std::set
<TNode
>::const_iterator it_end
= all
.end();
1449 while (it
!= it_end
) {
1451 conjunction
<< (*it
).negate();
1463 void TheoryArrays::setNonLinear(TNode a
)
1465 if (options::arraysWeakEquivalence()) return;
1466 if (d_infoMap
.isNonLinear(a
)) return;
1468 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a
<< ")\n";
1469 d_infoMap
.setNonLinear(a
);
1472 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1473 const CTNodeList
* st_a
= d_infoMap
.getStores(a
);
1474 const CTNodeList
* inst_a
= d_infoMap
.getInStores(a
);
1478 // Propagate non-linearity down chain of stores
1479 for( ; it
< st_a
->size(); ++it
) {
1480 TNode store
= (*st_a
)[it
];
1481 Assert(store
.getKind() == kind::STORE
);
1482 setNonLinear(store
[0]);
1485 // Instantiate ROW lemmas that were ignored before
1488 for(; it2
< i_a
->size(); ++it2
) {
1489 TNode i
= (*i_a
)[it2
];
1491 for ( ; it
< inst_a
->size(); ++it
) {
1492 TNode store
= (*inst_a
)[it
];
1493 Assert(store
.getKind() == kind::STORE
);
1496 lem
= std::make_tuple(store
, c
, j
, i
);
1497 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1504 void TheoryArrays::mergeArrays(TNode a
, TNode b
)
1506 // Note: a is the new representative
1507 Assert(a
.getType().isArray() && b
.getType().isArray());
1509 if (d_mergeInProgress
) {
1510 // Nested call to mergeArrays, just push on the queue and return
1511 d_mergeQueue
.push(a
.eqNode(b
));
1515 d_mergeInProgress
= true;
1519 // Normally, a is its own representative, but it's possible for a to have
1520 // been merged with another array after it got queued up by the equality engine,
1521 // so we take its representative to be safe.
1522 a
= d_equalityEngine
->getRepresentative(a
);
1523 Assert(d_equalityEngine
->getRepresentative(b
) == a
);
1524 Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: (" << a
<< ", " << b
<< ")\n";
1526 if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1527 bool aNL
= d_infoMap
.isNonLinear(a
);
1528 bool bNL
= d_infoMap
.isNonLinear(b
);
1531 // already both marked as non-linear - no need to do anything
1534 // Set b to be non-linear
1540 // Set a to be non-linear
1544 // Check for new non-linear arrays
1545 const CTNodeList
* astores
= d_infoMap
.getStores(a
);
1546 const CTNodeList
* bstores
= d_infoMap
.getStores(b
);
1547 Assert(astores
->size() <= 1 && bstores
->size() <= 1);
1548 if (astores
->size() > 0 && bstores
->size() > 0) {
1556 TNode constArrA
= d_infoMap
.getConstArr(a
);
1557 TNode constArrB
= d_infoMap
.getConstArr(b
);
1558 if (constArrA
.isNull()) {
1559 if (!constArrB
.isNull()) {
1560 d_infoMap
.setConstArr(a
,constArrB
);
1563 else if (!constArrB
.isNull()) {
1564 if (constArrA
!= constArrB
) {
1565 conflict(constArrA
,constArrB
);
1569 TNode mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1570 TNode mayRepB
= d_mayEqualEqualityEngine
.getRepresentative(b
);
1572 // If a and b have different default values associated with their mayequal equivalence classes,
1573 // things get complicated. Similarly, if two mayequal equivalence classes have different
1574 // constant representatives, it's not clear what to do. - disallow these cases for now. -Clark
1575 DefValMap::iterator it
= d_defValues
.find(mayRepA
);
1576 DefValMap::iterator it2
= d_defValues
.find(mayRepB
);
1579 if (it
!= d_defValues
.end()) {
1580 defValue
= (*it
).second
;
1581 if ((it2
!= d_defValues
.end() && (defValue
!= (*it2
).second
)) ||
1582 (mayRepA
.isConst() && mayRepB
.isConst() && mayRepA
!= mayRepB
)) {
1583 throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1586 else if (it2
!= d_defValues
.end()) {
1587 defValue
= (*it2
).second
;
1589 d_mayEqualEqualityEngine
.assertEquality(a
.eqNode(b
), true, d_true
);
1590 Assert(d_mayEqualEqualityEngine
.consistent());
1591 if (!defValue
.isNull()) {
1592 mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1593 d_defValues
[mayRepA
] = defValue
;
1596 checkRowLemmas(a
,b
);
1597 checkRowLemmas(b
,a
);
1599 // merge info adds the list of the 2nd argument to the first
1600 d_infoMap
.mergeInfo(a
, b
);
1602 if (options::arraysWeakEquivalence()) {
1603 d_arrayMerges
.push_back(a
.eqNode(b
));
1606 // If no more to do, break
1607 if (d_state
.isInConflict() || d_mergeQueue
.empty())
1612 // Otherwise, prepare for next iteration
1613 n
= d_mergeQueue
.front();
1618 d_mergeInProgress
= false;
1622 void TheoryArrays::checkStore(TNode a
) {
1623 if (options::arraysWeakEquivalence()) return;
1624 Trace("arrays-cri")<<"Arrays::checkStore "<<a
<<"\n";
1626 if(Trace
.isOn("arrays-cri")) {
1627 d_infoMap
.getInfo(a
)->print();
1629 Assert(a
.getType().isArray());
1630 Assert(a
.getKind() == kind::STORE
);
1634 TNode brep
= d_equalityEngine
->getRepresentative(b
);
1636 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(brep
)) {
1637 const CTNodeList
* js
= d_infoMap
.getIndices(brep
);
1640 for(; it
< js
->size(); ++it
) {
1641 TNode j
= (*js
)[it
];
1642 if (i
== j
) continue;
1643 lem
= std::make_tuple(a
, b
, i
, j
);
1644 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkStore ("<<a
<<", "<<b
<<", "<<i
<<", "<<j
<<")\n";
1651 void TheoryArrays::checkRowForIndex(TNode i
, TNode a
)
1653 if (options::arraysWeakEquivalence()) return;
1654 Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a
<<"\n";
1655 Trace("arrays-cri")<<" index "<<i
<<"\n";
1657 if(Trace
.isOn("arrays-cri")) {
1658 d_infoMap
.getInfo(a
)->print();
1660 Assert(a
.getType().isArray());
1661 Assert(d_equalityEngine
->getRepresentative(a
) == a
);
1663 TNode constArr
= d_infoMap
.getConstArr(a
);
1664 if (!constArr
.isNull()) {
1665 ArrayStoreAll storeAll
= constArr
.getConst
<ArrayStoreAll
>();
1666 Node defValue
= storeAll
.getValue();
1667 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1668 if (!d_equalityEngine
->hasTerm(selConst
))
1670 preRegisterTermInternal(selConst
);
1672 d_im
.assertInference(selConst
.eqNode(defValue
),
1674 InferenceId::UNKNOWN
,
1676 PfRule::ARRAYS_TRUST
);
1679 const CTNodeList
* stores
= d_infoMap
.getStores(a
);
1680 const CTNodeList
* instores
= d_infoMap
.getInStores(a
);
1684 for(; it
< stores
->size(); ++it
) {
1685 TNode store
= (*stores
)[it
];
1686 Assert(store
.getKind() == kind::STORE
);
1688 if (i
== j
) continue;
1689 lem
= std::make_tuple(store
, store
[0], j
, i
);
1690 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store
<<", "<<store
[0]<<", "<<j
<<", "<<i
<<")\n";
1694 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(a
)) {
1696 for(; it
< instores
->size(); ++it
) {
1697 TNode instore
= (*instores
)[it
];
1698 Assert(instore
.getKind() == kind::STORE
);
1699 TNode j
= instore
[1];
1700 if (i
== j
) continue;
1701 lem
= std::make_tuple(instore
, instore
[0], j
, i
);
1702 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore
<<", "<<instore
[0]<<", "<<j
<<", "<<i
<<")\n";
1709 // a just became equal to b
1710 // look for new ROW lemmas
1711 void TheoryArrays::checkRowLemmas(TNode a
, TNode b
)
1713 if (options::arraysWeakEquivalence()) return;
1714 Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a
<<"\n";
1715 if(Trace
.isOn("arrays-crl"))
1716 d_infoMap
.getInfo(a
)->print();
1717 Trace("arrays-crl")<<" ------------ and "<<b
<<"\n";
1718 if(Trace
.isOn("arrays-crl"))
1719 d_infoMap
.getInfo(b
)->print();
1721 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1723 TNode constArr
= d_infoMap
.getConstArr(b
);
1724 if (!constArr
.isNull()) {
1725 for( ; it
< i_a
->size(); ++it
) {
1726 TNode i
= (*i_a
)[it
];
1727 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1728 if (!d_equalityEngine
->hasTerm(selConst
))
1730 preRegisterTermInternal(selConst
);
1735 const CTNodeList
* st_b
= d_infoMap
.getStores(b
);
1736 const CTNodeList
* inst_b
= d_infoMap
.getInStores(b
);
1741 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1742 TNode i
= (*i_a
)[it
];
1744 for ( ; its
< st_b
->size(); ++its
) {
1745 TNode store
= (*st_b
)[its
];
1746 Assert(store
.getKind() == kind::STORE
);
1749 lem
= std::make_tuple(store
, c
, j
, i
);
1750 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1755 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(b
)) {
1756 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1757 TNode i
= (*i_a
)[it
];
1759 for ( ; its
< inst_b
->size(); ++its
) {
1760 TNode store
= (*inst_b
)[its
];
1761 Assert(store
.getKind() == kind::STORE
);
1764 lem
= std::make_tuple(store
, c
, j
, i
);
1765 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1770 Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1773 void TheoryArrays::propagateRowLemma(RowLemmaType lem
)
1775 Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1776 << options::arraysPropagate() << std::endl
;
1779 std::tie(a
, b
, i
, j
) = lem
;
1781 Assert(a
.getType().isArray() && b
.getType().isArray());
1782 if (d_equalityEngine
->areEqual(a
, b
) || d_equalityEngine
->areEqual(i
, j
))
1787 NodeManager
* nm
= NodeManager::currentNM();
1788 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1789 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1791 // Try to avoid introducing new read terms: track whether these already exist
1792 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1793 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1794 bool bothExist
= ajExists
&& bjExists
;
1796 // If propagating, check propagations
1797 int prop
= options::arraysPropagate();
1799 if (d_equalityEngine
->areDisequal(i
, j
, true) && (bothExist
|| prop
> 1))
1801 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj
<<", "<<bj
<<")\n";
1802 Node aj_eq_bj
= aj
.eqNode(bj
);
1804 (i
.isConst() && j
.isConst()) ? d_true
: i
.eqNode(j
).notNode();
1805 d_permRef
.push_back(reason
);
1807 preRegisterTermInternal(aj
);
1810 preRegisterTermInternal(bj
);
1812 d_im
.assertInference(
1813 aj_eq_bj
, true, InferenceId::ARRAYS_READ_OVER_WRITE
, reason
, PfRule::ARRAYS_READ_OVER_WRITE
);
1817 if (bothExist
&& d_equalityEngine
->areDisequal(aj
, bj
, true))
1819 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating i = j ("<<i
<<", "<<j
<<")\n";
1821 (aj
.isConst() && bj
.isConst()) ? d_true
: aj
.eqNode(bj
).notNode();
1822 Node j_eq_i
= j
.eqNode(i
);
1823 d_im
.assertInference(
1824 j_eq_i
, true, InferenceId::ARRAYS_READ_OVER_WRITE_CONTRA
, reason
, PfRule::ARRAYS_READ_OVER_WRITE_CONTRA
);
1831 void TheoryArrays::queueRowLemma(RowLemmaType lem
)
1833 Debug("pf::array") << "Array solver: queue row lemma called" << std::endl
;
1835 if (d_state
.isInConflict() || d_RowAlreadyAdded
.contains(lem
))
1840 std::tie(a
, b
, i
, j
) = lem
;
1842 Assert(a
.getType().isArray() && b
.getType().isArray());
1843 if (d_equalityEngine
->areEqual(a
, b
) || d_equalityEngine
->areEqual(i
, j
))
1848 NodeManager
* nm
= NodeManager::currentNM();
1849 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1850 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1852 // Try to avoid introducing new read terms: track whether these already exist
1853 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1854 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1855 bool bothExist
= ajExists
&& bjExists
;
1857 // If propagating, check propagations
1858 int prop
= options::arraysPropagate();
1860 propagateRowLemma(lem
);
1863 // Prefer equality between indexes so as not to introduce new read terms
1864 if (options::arraysEagerIndexSplitting() && !bothExist
1865 && !d_equalityEngine
->areDisequal(i
, j
, false))
1868 i_eq_j
= d_valuation
.ensureLiteral(i
.eqNode(j
)); // TODO: think about this
1870 i_eq_j
= i
.eqNode(j
);
1872 getOutputChannel().requirePhase(i_eq_j
, true);
1873 d_decisionRequests
.push(i_eq_j
);
1876 // TODO: maybe add triggers here
1878 if (options::arraysEagerLemmas() || bothExist
)
1880 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1881 Node aj2
= Rewriter::rewrite(aj
);
1884 preRegisterTermInternal(aj
);
1886 if (!d_equalityEngine
->hasTerm(aj2
))
1888 preRegisterTermInternal(aj2
);
1890 d_im
.assertInference(
1891 aj
.eqNode(aj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1893 Node bj2
= Rewriter::rewrite(bj
);
1896 preRegisterTermInternal(bj
);
1898 if (!d_equalityEngine
->hasTerm(bj2
))
1900 preRegisterTermInternal(bj2
);
1902 d_im
.assertInference(
1903 bj
.eqNode(bj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1910 Node eq1
= aj2
.eqNode(bj2
);
1911 Node eq1_r
= Rewriter::rewrite(eq1
);
1912 if (eq1_r
== d_true
) {
1913 if (!d_equalityEngine
->hasTerm(aj2
))
1915 preRegisterTermInternal(aj2
);
1917 if (!d_equalityEngine
->hasTerm(bj2
))
1919 preRegisterTermInternal(bj2
);
1921 d_im
.assertInference(eq1
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1925 Node eq2
= i
.eqNode(j
);
1926 Node eq2_r
= Rewriter::rewrite(eq2
);
1927 if (eq2_r
== d_true
) {
1928 d_im
.assertInference(eq2
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1932 Node lemma
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
1934 Trace("arrays-lem") << "Arrays::addRowLemma (1) adding " << lemma
<< "\n";
1935 d_RowAlreadyAdded
.insert(lem
);
1936 // use non-rewritten nodes
1938 aj
.eqNode(bj
), InferenceId::ARRAYS_READ_OVER_WRITE
, eq2
.notNode(), PfRule::ARRAYS_READ_OVER_WRITE
);
1942 d_RowQueue
.push(lem
);
1946 Node
TheoryArrays::getNextDecisionRequest()
1948 if(! d_decisionRequests
.empty()) {
1949 Node n
= d_decisionRequests
.front();
1950 d_decisionRequests
.pop();
1953 return Node::null();
1957 bool TheoryArrays::dischargeLemmas()
1959 bool lemmasAdded
= false;
1960 size_t sz
= d_RowQueue
.size();
1961 for (unsigned count
= 0; count
< sz
; ++count
) {
1962 RowLemmaType l
= d_RowQueue
.front();
1964 if (d_RowAlreadyAdded
.contains(l
)) {
1969 std::tie(a
, b
, i
, j
) = l
;
1970 Assert(a
.getType().isArray() && b
.getType().isArray());
1972 NodeManager
* nm
= NodeManager::currentNM();
1973 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1974 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1975 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1976 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1978 // Check for redundant lemma
1979 // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
1980 if (!d_equalityEngine
->hasTerm(i
) || !d_equalityEngine
->hasTerm(j
)
1981 || d_equalityEngine
->areEqual(i
, j
) || !d_equalityEngine
->hasTerm(a
)
1982 || !d_equalityEngine
->hasTerm(b
) || d_equalityEngine
->areEqual(a
, b
)
1983 || (ajExists
&& bjExists
&& d_equalityEngine
->areEqual(aj
, bj
)))
1988 int prop
= options::arraysPropagate();
1990 propagateRowLemma(l
);
1991 if (d_state
.isInConflict())
1997 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1998 Node aj2
= Rewriter::rewrite(aj
);
2001 preRegisterTermInternal(aj
);
2003 if (!d_equalityEngine
->hasTerm(aj2
))
2005 preRegisterTermInternal(aj2
);
2007 d_im
.assertInference(
2008 aj
.eqNode(aj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2010 Node bj2
= Rewriter::rewrite(bj
);
2013 preRegisterTermInternal(bj
);
2015 if (!d_equalityEngine
->hasTerm(bj2
))
2017 preRegisterTermInternal(bj2
);
2019 d_im
.assertInference(
2020 bj
.eqNode(bj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2027 Node eq1
= aj2
.eqNode(bj2
);
2028 Node eq1_r
= Rewriter::rewrite(eq1
);
2029 if (eq1_r
== d_true
) {
2030 if (!d_equalityEngine
->hasTerm(aj2
))
2032 preRegisterTermInternal(aj2
);
2034 if (!d_equalityEngine
->hasTerm(bj2
))
2036 preRegisterTermInternal(bj2
);
2038 d_im
.assertInference(eq1
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2042 Node eq2
= i
.eqNode(j
);
2043 Node eq2_r
= Rewriter::rewrite(eq2
);
2044 if (eq2_r
== d_true
) {
2045 d_im
.assertInference(eq2
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2049 Node lem
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
2051 Trace("arrays-lem") << "Arrays::addRowLemma (2) adding " << lem
<< "\n";
2052 d_RowAlreadyAdded
.insert(l
);
2053 // use non-rewritten nodes, theory preprocessing will rewrite
2055 aj
.eqNode(bj
), InferenceId::ARRAYS_READ_OVER_WRITE
, eq2
.notNode(), PfRule::ARRAYS_READ_OVER_WRITE
);
2058 if (options::arraysReduceSharing()) {
2065 void TheoryArrays::conflict(TNode a
, TNode b
) {
2066 Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl
;
2069 // if in check model, don't send the conflict
2070 d_state
.notifyInConflict();
2073 d_im
.conflictEqConstantMerge(a
, b
);
2076 TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2081 void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
2082 Node
TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2084 return d_ta
->getNextDecisionRequest();
2086 std::string
TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2088 return std::string("th_arrays_dec");
2091 void TheoryArrays::computeRelevantTerms(std::set
<Node
>& termSet
)
2093 NodeManager
* nm
= NodeManager::currentNM();
2094 // make sure RIntro1 reads are included in the relevant set of reads
2095 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
2096 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
2098 Node eqc
= (*eqcs_i
);
2099 if (!eqc
.getType().isArray())
2101 // not an array, skip
2104 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
2105 for (; !eqc_i
.isFinished(); ++eqc_i
)
2108 if (termSet
.find(n
) != termSet
.end())
2110 if (n
.getKind() == kind::STORE
)
2112 // Make sure RIntro1 reads are included
2113 Node r
= nm
->mkNode(kind::SELECT
, n
, n
[1]);
2114 Trace("arrays::collectModelInfo")
2115 << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r
2123 // Now do a fixed-point iteration to get all reads that need to be included
2124 // because of RIntro2 rule
2129 eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
2130 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
2132 Node eqc
= (*eqcs_i
);
2133 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
2134 for (; !eqc_i
.isFinished(); ++eqc_i
)
2137 if (n
.getKind() == kind::SELECT
&& termSet
.find(n
) != termSet
.end())
2139 // Find all terms equivalent to n[0] and get corresponding read terms
2140 Node array_eqc
= d_equalityEngine
->getRepresentative(n
[0]);
2141 eq::EqClassIterator array_eqc_i
=
2142 eq::EqClassIterator(array_eqc
, d_equalityEngine
);
2143 for (; !array_eqc_i
.isFinished(); ++array_eqc_i
)
2145 Node arr
= *array_eqc_i
;
2146 if (arr
.getKind() == kind::STORE
2147 && termSet
.find(arr
) != termSet
.end()
2148 && !d_equalityEngine
->areEqual(arr
[1], n
[1]))
2150 Node r
= nm
->mkNode(kind::SELECT
, arr
, n
[1]);
2151 if (termSet
.find(r
) == termSet
.end()
2152 && d_equalityEngine
->hasTerm(r
))
2154 Trace("arrays::collectModelInfo")
2155 << "TheoryArrays::collectModelInfo, adding RIntro2(a) "
2161 r
= nm
->mkNode(kind::SELECT
, arr
[0], n
[1]);
2162 if (termSet
.find(r
) == termSet
.end()
2163 && d_equalityEngine
->hasTerm(r
))
2165 Trace("arrays::collectModelInfo")
2166 << "TheoryArrays::collectModelInfo, adding RIntro2(b) "
2175 // Find all stores in which n[0] appears and get corresponding read
2177 const CTNodeList
* instores
= d_infoMap
.getInStores(array_eqc
);
2179 for (; it
< instores
->size(); ++it
)
2181 TNode instore
= (*instores
)[it
];
2182 Assert(instore
.getKind() == kind::STORE
);
2183 if (termSet
.find(instore
) != termSet
.end()
2184 && !d_equalityEngine
->areEqual(instore
[1], n
[1]))
2186 Node r
= nm
->mkNode(kind::SELECT
, instore
, n
[1]);
2187 if (termSet
.find(r
) == termSet
.end()
2188 && d_equalityEngine
->hasTerm(r
))
2190 Trace("arrays::collectModelInfo")
2191 << "TheoryArrays::collectModelInfo, adding RIntro2(c) "
2197 r
= nm
->mkNode(kind::SELECT
, instore
[0], n
[1]);
2198 if (termSet
.find(r
) == termSet
.end()
2199 && d_equalityEngine
->hasTerm(r
))
2201 Trace("arrays::collectModelInfo")
2202 << "TheoryArrays::collectModelInfo, adding RIntro2(d) "
2216 } // namespace arrays
2217 } // namespace theory