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),
89 d_state(c
, u
, valuation
),
90 d_im(*this, d_state
, pnm
),
91 d_literalsToPropagate(c
),
92 d_literalsToPropagateIndex(c
, 0),
94 d_mayEqualEqualityEngine(c
, name
+ "mayEqual", true),
97 d_infoMap(c
, &d_backtracker
, name
),
99 d_mergeInProgress(false),
101 d_RowAlreadyAdded(u
),
104 d_sharedTerms(c
, false),
107 d_constReadsContext(new context::Context()),
108 d_contextPopper(c
, d_constReadsContext
),
110 d_decisionRequests(c
),
112 d_modelConstraints(c
),
115 d_readTableContext(new context::Context()),
117 d_inCheckModel(false),
118 d_dstrat(new TheoryArraysDecisionStrategy(this)),
121 d_true
= NodeManager::currentNM()->mkConst
<bool>(true);
122 d_false
= NodeManager::currentNM()->mkConst
<bool>(false);
124 // The preprocessing congruence kinds
125 d_ppEqualityEngine
.addFunctionKind(kind::SELECT
);
126 d_ppEqualityEngine
.addFunctionKind(kind::STORE
);
128 // indicate we are using the default theory state object, and the arrays
130 d_theoryState
= &d_state
;
131 d_inferManager
= &d_im
;
134 TheoryArrays::~TheoryArrays() {
135 vector
<CTNodeList
*>::iterator it
= d_readBucketAllocations
.begin(), iend
= d_readBucketAllocations
.end();
136 for (; it
!= iend
; ++it
) {
139 delete d_readTableContext
;
140 CNodeNListMap::iterator it2
= d_constReads
.begin();
141 for( ; it2
!= d_constReads
.end(); ++it2
) {
142 it2
->second
->deleteSelf();
144 delete d_constReadsContext
;
147 TheoryRewriter
* TheoryArrays::getTheoryRewriter() { return &d_rewriter
; }
149 ProofRuleChecker
* TheoryArrays::getProofChecker() { return &d_checker
; }
151 bool TheoryArrays::needsEqualityEngine(EeSetupInfo
& esi
)
153 esi
.d_notify
= &d_notify
;
154 esi
.d_name
= d_instanceName
+ "ee";
155 esi
.d_notifyNewClass
= true;
156 esi
.d_notifyMerge
= true;
160 void TheoryArrays::finishInit()
162 Assert(d_equalityEngine
!= nullptr);
164 // The kinds we are treating as function application in congruence
165 d_equalityEngine
->addFunctionKind(kind::SELECT
);
168 d_equalityEngine
->addFunctionKind(kind::STORE
);
172 /////////////////////////////////////////////////////////////////////////////
174 /////////////////////////////////////////////////////////////////////////////
177 bool TheoryArrays::ppDisequal(TNode a
, TNode b
) {
178 bool termsExist
= d_ppEqualityEngine
.hasTerm(a
) && d_ppEqualityEngine
.hasTerm(b
);
179 Assert(!termsExist
|| !a
.isConst() || !b
.isConst() || a
== b
180 || d_ppEqualityEngine
.areDisequal(a
, b
, false));
181 return ((termsExist
&& d_ppEqualityEngine
.areDisequal(a
, b
, false)) ||
182 Rewriter::rewrite(a
.eqNode(b
)) == d_false
);
186 Node
TheoryArrays::solveWrite(TNode term
, bool solve1
, bool solve2
, bool ppCheck
)
191 if (term
[0].getKind() != kind::STORE
&&
192 term
[1].getKind() != kind::STORE
) {
195 TNode left
= term
[0];
196 TNode right
= term
[1];
197 int leftWrites
= 0, rightWrites
= 0;
199 // Count nested writes
201 while (e1
.getKind() == kind::STORE
) {
207 while (e2
.getKind() == kind::STORE
) {
212 if (rightWrites
> leftWrites
) {
216 int tmpWrites
= leftWrites
;
217 leftWrites
= rightWrites
;
218 rightWrites
= tmpWrites
;
221 NodeManager
* nm
= NodeManager::currentNM();
222 if (rightWrites
== 0) {
226 // write(store, index_0, v_0, index_1, v_1, ..., index_n, v_n) = store IFF
228 // read(store, index_n) = v_n &
229 // index_{n-1} != index_n -> read(store, index_{n-1}) = v_{n-1} &
230 // (index_{n-2} != index_{n-1} & index_{n-2} != index_n) -> read(store, index_{n-2}) = v_{n-2} &
232 // (index_1 != index_2 & ... & index_1 != index_n) -> read(store, index_1) = v_1
233 // (index_0 != index_1 & index_0 != index_2 & ... & index_0 != index_n) -> read(store, index_0) = v_0
234 TNode write_i
, write_j
, index_i
, index_j
;
236 NodeBuilder
result(kind::AND
);
239 for (i
= leftWrites
-1; i
>= 0; --i
) {
240 index_i
= write_i
[1];
242 // build: [index_i /= index_n && index_i /= index_(n-1) &&
243 // ... && index_i /= index_(i+1)] -> read(store, index_i) = v_i
246 NodeBuilder
hyp(kind::AND
);
247 for (j
= leftWrites
- 1; j
> i
; --j
) {
248 index_j
= write_j
[1];
249 if (!ppCheck
|| !ppDisequal(index_i
, index_j
)) {
250 Node
hyp2(index_i
.eqNode(index_j
));
251 hyp
<< hyp2
.notNode();
253 write_j
= write_j
[0];
256 Node r1
= nm
->mkNode(kind::SELECT
, e1
, index_i
);
257 conc
= r1
.eqNode(write_i
[2]);
258 if (hyp
.getNumChildren() != 0) {
259 if (hyp
.getNumChildren() == 1) {
260 conc
= hyp
.getChild(0).impNode(conc
);
264 conc
= r1
.impNode(conc
);
271 // Prepare for next iteration
272 write_i
= write_i
[0];
275 Assert(result
.getNumChildren() > 0);
276 if (result
.getNumChildren() == 1) {
277 return result
.getChild(0);
285 // store(...) = store(a,i,v) ==>
286 // store(store(...),i,select(a,i)) = a && select(store(...),i)=v
289 NodeBuilder
nb(kind::AND
);
290 while (right
.getKind() == kind::STORE
) {
291 tmp
= nm
->mkNode(kind::SELECT
, l
, right
[1]);
292 nb
<< tmp
.eqNode(right
[2]);
293 tmp
= nm
->mkNode(kind::SELECT
, right
[0], right
[1]);
294 l
= nm
->mkNode(kind::STORE
, l
, right
[1], tmp
);
297 nb
<< solveWrite(l
.eqNode(right
), solve1
, solve2
, ppCheck
);
304 TrustNode
TheoryArrays::ppRewrite(TNode term
, std::vector
<SkolemLemma
>& lems
)
306 // first, see if we need to expand definitions
307 TrustNode texp
= d_rewriter
.expandDefinition(term
);
314 return TrustNode::null();
316 d_ppEqualityEngine
.addTerm(term
);
317 NodeManager
* nm
= NodeManager::currentNM();
319 switch (term
.getKind()) {
321 // select(store(a,i,v),j) = select(a,j)
323 if (term
[0].getKind() == kind::STORE
&& ppDisequal(term
[0][1], term
[1])) {
324 ret
= nm
->mkNode(kind::SELECT
, term
[0][0], term
[1]);
329 // store(store(a,i,v),j,w) = store(store(a,j,w),i,v)
330 // IF i != j and j comes before i in the ordering
331 if (term
[0].getKind() == kind::STORE
&& (term
[1] < term
[0][1]) && ppDisequal(term
[1],term
[0][1])) {
332 Node inner
= nm
->mkNode(kind::STORE
, term
[0][0], term
[1], term
[2]);
333 Node outer
= nm
->mkNode(kind::STORE
, inner
, term
[0][1], term
[0][2]);
339 ret
= solveWrite(term
, d_solveWrite
, d_solveWrite2
, true);
345 if (!ret
.isNull() && ret
!= term
)
347 return TrustNode::mkTrustRewrite(term
, ret
, nullptr);
349 return TrustNode::null();
352 Theory::PPAssertStatus
TheoryArrays::ppAssert(
353 TrustNode tin
, TrustSubstitutionMap
& outSubstitutions
)
355 TNode in
= tin
.getNode();
356 switch(in
.getKind()) {
359 d_ppFacts
.push_back(in
);
360 d_ppEqualityEngine
.assertEquality(in
, true, in
);
361 if (in
[0].isVar() && isLegalElimination(in
[0], in
[1]))
363 outSubstitutions
.addSubstitutionSolved(in
[0], in
[1], tin
);
364 return PP_ASSERT_STATUS_SOLVED
;
366 if (in
[1].isVar() && isLegalElimination(in
[1], in
[0]))
368 outSubstitutions
.addSubstitutionSolved(in
[1], in
[0], tin
);
369 return PP_ASSERT_STATUS_SOLVED
;
375 d_ppFacts
.push_back(in
);
376 if (in
[0].getKind() == kind::EQUAL
) {
379 d_ppEqualityEngine
.assertEquality(in
[0], false, in
);
386 return PP_ASSERT_STATUS_UNSOLVED
;
390 /////////////////////////////////////////////////////////////////////////////
391 // T-PROPAGATION / REGISTRATION
392 /////////////////////////////////////////////////////////////////////////////
394 bool TheoryArrays::propagateLit(TNode literal
)
396 Debug("arrays") << spaces(getSatContext()->getLevel())
397 << "TheoryArrays::propagateLit(" << literal
<< ")"
400 // If already in conflict, no more propagation
401 if (d_state
.isInConflict())
403 Debug("arrays") << spaces(getSatContext()->getLevel())
404 << "TheoryArrays::propagateLit(" << literal
405 << "): already in conflict" << std::endl
;
410 if (d_inCheckModel
&& getSatContext()->getLevel() != d_topLevel
) {
413 bool ok
= d_out
->propagate(literal
);
415 d_state
.notifyInConflict();
418 }/* TheoryArrays::propagate(TNode) */
421 TNode
TheoryArrays::weakEquivGetRep(TNode node
) {
424 pointer
= d_infoMap
.getWeakEquivPointer(node
);
425 if (pointer
.isNull()) {
432 TNode
TheoryArrays::weakEquivGetRepIndex(TNode node
, TNode index
) {
433 Assert(!index
.isNull());
434 TNode pointer
, index2
;
436 pointer
= d_infoMap
.getWeakEquivPointer(node
);
437 if (pointer
.isNull()) {
440 index2
= d_infoMap
.getWeakEquivIndex(node
);
441 if (index2
.isNull() || !d_equalityEngine
->areEqual(index
, index2
))
446 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
447 if (secondary
.isNull()) {
455 void TheoryArrays::visitAllLeaves(TNode reason
, vector
<TNode
>& conjunctions
) {
456 switch (reason
.getKind()) {
458 Assert(reason
.getNumChildren() == 2);
459 visitAllLeaves(reason
[0], conjunctions
);
460 visitAllLeaves(reason
[1], conjunctions
);
463 conjunctions
.push_back(reason
);
466 d_equalityEngine
->explainEquality(
467 reason
[0], reason
[1], true, conjunctions
);
474 void TheoryArrays::weakEquivBuildCond(TNode node
, TNode index
, vector
<TNode
>& conjunctions
) {
475 Assert(!index
.isNull());
476 TNode pointer
, index2
;
478 pointer
= d_infoMap
.getWeakEquivPointer(node
);
479 if (pointer
.isNull()) {
482 index2
= d_infoMap
.getWeakEquivIndex(node
);
483 if (index2
.isNull()) {
484 // Null index means these two nodes became equal: explain the equality.
485 d_equalityEngine
->explainEquality(node
, pointer
, true, conjunctions
);
488 else if (!d_equalityEngine
->areEqual(index
, index2
))
490 // If indices are not equal in current context, need to add that to the lemma.
491 Node reason
= index
.eqNode(index2
).notNode();
492 d_permRef
.push_back(reason
);
493 conjunctions
.push_back(reason
);
497 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
498 if (secondary
.isNull()) {
501 TNode reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
502 Assert(!reason
.isNull());
503 visitAllLeaves(reason
, conjunctions
);
509 void TheoryArrays::weakEquivMakeRep(TNode node
) {
510 TNode pointer
= d_infoMap
.getWeakEquivPointer(node
);
511 if (pointer
.isNull()) {
514 weakEquivMakeRep(pointer
);
515 d_infoMap
.setWeakEquivPointer(pointer
, node
);
516 d_infoMap
.setWeakEquivIndex(pointer
, d_infoMap
.getWeakEquivIndex(node
));
517 d_infoMap
.setWeakEquivPointer(node
, TNode());
518 weakEquivMakeRepIndex(node
);
521 void TheoryArrays::weakEquivMakeRepIndex(TNode node
) {
522 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
523 if (secondary
.isNull()) {
526 TNode index
= d_infoMap
.getWeakEquivIndex(node
);
527 Assert(!index
.isNull());
528 TNode index2
= d_infoMap
.getWeakEquivIndex(secondary
);
531 while (index2
.isNull() || !d_equalityEngine
->areEqual(index
, index2
))
533 next
= d_infoMap
.getWeakEquivPointer(secondary
);
534 d_infoMap
.setWeakEquivSecondary(node
, next
);
535 reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
536 if (index2
.isNull()) {
537 reason
= reason
.andNode(secondary
.eqNode(next
));
540 reason
= reason
.andNode(index
.eqNode(index2
).notNode());
542 d_permRef
.push_back(reason
);
543 d_infoMap
.setWeakEquivSecondaryReason(node
, reason
);
548 index2
= d_infoMap
.getWeakEquivIndex(secondary
);
550 weakEquivMakeRepIndex(secondary
);
551 d_infoMap
.setWeakEquivSecondary(secondary
, node
);
552 d_infoMap
.setWeakEquivSecondaryReason(secondary
, d_infoMap
.getWeakEquivSecondaryReason(node
));
553 d_infoMap
.setWeakEquivSecondary(node
, TNode());
554 d_infoMap
.setWeakEquivSecondaryReason(node
, TNode());
557 void TheoryArrays::weakEquivAddSecondary(TNode index
, TNode arrayFrom
, TNode arrayTo
, TNode reason
) {
558 std::unordered_set
<TNode
> marked
;
559 vector
<TNode
> index_trail
;
560 vector
<TNode
>::iterator it
, iend
;
561 Node equivalence_trail
= reason
;
563 TNode pointer
, indexRep
;
564 if (!index
.isNull()) {
565 index_trail
.push_back(index
);
566 marked
.insert(d_equalityEngine
->getRepresentative(index
));
568 while (arrayFrom
!= arrayTo
) {
569 index
= d_infoMap
.getWeakEquivIndex(arrayFrom
);
570 pointer
= d_infoMap
.getWeakEquivPointer(arrayFrom
);
571 if (!index
.isNull()) {
572 indexRep
= d_equalityEngine
->getRepresentative(index
);
573 if (marked
.find(indexRep
) == marked
.end() && weakEquivGetRepIndex(arrayFrom
, index
) != arrayTo
) {
574 weakEquivMakeRepIndex(arrayFrom
);
575 d_infoMap
.setWeakEquivSecondary(arrayFrom
, arrayTo
);
576 current_reason
= equivalence_trail
;
577 for (it
= index_trail
.begin(), iend
= index_trail
.end(); it
!= iend
; ++it
) {
578 current_reason
= current_reason
.andNode(index
.eqNode(*it
).notNode());
580 d_permRef
.push_back(current_reason
);
581 d_infoMap
.setWeakEquivSecondaryReason(arrayFrom
, current_reason
);
583 marked
.insert(indexRep
);
586 equivalence_trail
= equivalence_trail
.andNode(arrayFrom
.eqNode(pointer
));
592 void TheoryArrays::checkWeakEquiv(bool arraysMerged
) {
593 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(&d_mayEqualEqualityEngine
);
594 for (; !eqcs_i
.isFinished(); ++eqcs_i
) {
595 Node eqc
= (*eqcs_i
);
596 if (!eqc
.getType().isArray()) {
599 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, &d_mayEqualEqualityEngine
);
600 TNode rep
= d_mayEqualEqualityEngine
.getRepresentative(*eqc_i
);
601 TNode weakEquivRep
= weakEquivGetRep(rep
);
602 for (; !eqc_i
.isFinished(); ++eqc_i
) {
604 Assert(!arraysMerged
|| weakEquivGetRep(n
) == weakEquivRep
);
605 TNode pointer
= d_infoMap
.getWeakEquivPointer(n
);
606 TNode index
= d_infoMap
.getWeakEquivIndex(n
);
607 TNode secondary
= d_infoMap
.getWeakEquivSecondary(n
);
608 Assert(pointer
.isNull() == (weakEquivGetRep(n
) == n
));
609 Assert(!pointer
.isNull() || secondary
.isNull());
610 Assert(!index
.isNull() || secondary
.isNull());
611 Assert(d_infoMap
.getWeakEquivSecondaryReason(n
).isNull()
612 || !secondary
.isNull());
613 if (!pointer
.isNull()) {
614 if (index
.isNull()) {
615 Assert(d_equalityEngine
->areEqual(n
, pointer
));
619 (n
.getKind() == kind::STORE
&& n
[0] == pointer
&& n
[1] == index
)
620 || (pointer
.getKind() == kind::STORE
&& pointer
[0] == n
621 && pointer
[1] == index
));
629 * Stores in d_infoMap the following information for each term a of type array:
631 * - all i, such that there exists a term a[i] or a = store(b i v)
632 * (i.e. all indices it is being read atl; store(b i v) is implicitly read at
633 * position i due to the implicit axiom store(b i v)[i] = v )
635 * - all the stores a is congruent to (this information is context dependent)
637 * - all store terms of the form store (a i v) (i.e. in which a appears
638 * directly; this is invariant because no new store terms are created)
640 * Note: completeness depends on having pre-register called on all the input
641 * terms before starting to instantiate lemmas.
643 void TheoryArrays::preRegisterTermInternal(TNode node
)
645 if (d_state
.isInConflict())
649 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node
<< ")" << std::endl
;
650 Kind nk
= node
.getKind();
651 if (nk
== kind::EQUAL
)
653 // Add the trigger for equality
654 // NOTE: note that if the equality is true or false already, it might not be added
655 d_equalityEngine
->addTriggerPredicate(node
);
658 else if (d_equalityEngine
->hasTerm(node
))
660 // Invariant: array terms should be preregistered before being added to the equality engine
661 Assert(nk
!= kind::SELECT
662 || d_isPreRegistered
.find(node
) != d_isPreRegistered
.end());
665 // add to equality engine and the may equality engine
666 TypeNode nodeType
= node
.getType();
667 if (nodeType
.isArray())
669 // index type should not be an array, otherwise we throw a logic exception
670 if (nodeType
.getArrayIndexType().isArray())
672 std::stringstream ss
;
673 ss
<< "Arrays cannot be indexed by array types, offending array type is "
675 throw LogicException(ss
.str());
677 d_mayEqualEqualityEngine
.addTerm(node
);
679 d_equalityEngine
->addTerm(node
);
681 switch (node
.getKind())
686 TNode store
= d_equalityEngine
->getRepresentative(node
[0]);
688 // The may equal needs the store
689 d_mayEqualEqualityEngine
.addTerm(store
);
691 Assert((d_isPreRegistered
.insert(node
), true));
693 Assert(d_equalityEngine
->getRepresentative(store
) == store
);
694 d_infoMap
.addIndex(store
, node
[1]);
696 // Synchronize d_constReadsContext with SAT context
697 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
698 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel())
700 d_constReadsContext
->push();
703 // Record read in sharing data structure
704 TNode index
= d_equalityEngine
->getRepresentative(node
[1]);
705 if (!options::arraysWeakEquivalence() && index
.isConst())
708 CNodeNListMap::iterator it
= d_constReads
.find(index
);
709 if (it
== d_constReads
.end())
711 temp
= new (true) CTNodeList(d_constReadsContext
);
712 d_constReads
[index
] = temp
;
718 temp
->push_back(node
);
719 d_constReadsList
.push_back(node
);
723 d_reads
.push_back(node
);
726 checkRowForIndex(node
[1], store
);
731 TNode a
= d_equalityEngine
->getRepresentative(node
[0]);
735 // Can't use d_mayEqualEqualityEngine to merge node with a because they
736 // are both constants, so just set the default value manually for node.
737 Assert(a
== node
[0]);
738 d_mayEqualEqualityEngine
.addTerm(node
);
739 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
740 Assert(d_mayEqualEqualityEngine
.getRepresentative(a
) == a
);
741 DefValMap::iterator it
= d_defValues
.find(a
);
742 Assert(it
!= d_defValues
.end());
743 d_defValues
[node
] = (*it
).second
;
747 d_mayEqualEqualityEngine
.assertEquality(node
.eqNode(a
), true, d_true
);
748 Assert(d_mayEqualEqualityEngine
.consistent());
753 NodeManager
* nm
= NodeManager::currentNM();
754 Node ni
= nm
->mkNode(kind::SELECT
, node
, i
);
755 if (!d_equalityEngine
->hasTerm(ni
))
757 preRegisterTermInternal(ni
);
759 // Apply RIntro1 Rule
760 d_im
.assertInference(ni
.eqNode(v
),
762 InferenceId::ARRAYS_READ_OVER_WRITE_1
,
764 PfRule::ARRAYS_READ_OVER_WRITE_1
);
766 d_infoMap
.addStore(node
, node
);
767 d_infoMap
.addInStore(a
, node
);
768 d_infoMap
.setModelRep(node
, node
);
770 // Add-Store for Weak Equivalence
771 if (options::arraysWeakEquivalence())
773 Assert(weakEquivGetRep(node
[0]) == weakEquivGetRep(a
));
774 Assert(weakEquivGetRep(node
) == node
);
775 d_infoMap
.setWeakEquivPointer(node
, node
[0]);
776 d_infoMap
.setWeakEquivIndex(node
, node
[1]);
777 #ifdef CVC5_ASSERTIONS
778 checkWeakEquiv(false);
785 case kind::STORE_ALL
: {
786 ArrayStoreAll storeAll
= node
.getConst
<ArrayStoreAll
>();
787 Node defaultValue
= storeAll
.getValue();
788 if (!defaultValue
.isConst()) {
789 throw LogicException("Array theory solver does not yet support non-constant default values for arrays");
791 d_infoMap
.setConstArr(node
, node
);
792 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
793 d_defValues
[node
] = defaultValue
;
797 // Variables etc, already processed above
800 // Invariant: preregistered terms are exactly the terms in the equality engine
801 // Disabled, see comment above for kind::EQUAL
802 // Assert(d_equalityEngine->hasTerm(node) ||
803 // !d_equalityEngine->consistent());
806 void TheoryArrays::preRegisterTerm(TNode node
)
808 preRegisterTermInternal(node
);
809 // If we have a select from an array of Bools, mark it as a term that can be propagated.
810 // Note: do this here instead of in preRegisterTermInternal to prevent internal select
811 // terms from being propagated out (as this results in an assertion failure).
812 if (node
.getKind() == kind::SELECT
&& node
.getType().isBoolean()) {
813 d_equalityEngine
->addTriggerPredicate(node
);
817 void TheoryArrays::explain(TNode literal
, Node
& explanation
)
820 Debug("arrays") << spaces(getSatContext()->getLevel())
821 << "TheoryArrays::explain(" << literal
<< ")" << std::endl
;
822 std::vector
<TNode
> assumptions
;
824 bool polarity
= literal
.getKind() != kind::NOT
;
825 TNode atom
= polarity
? literal
: literal
[0];
826 if (atom
.getKind() == kind::EQUAL
)
828 d_equalityEngine
->explainEquality(
829 atom
[0], atom
[1], polarity
, assumptions
, nullptr);
833 d_equalityEngine
->explainPredicate(atom
, polarity
, assumptions
, nullptr);
835 explanation
= mkAnd(assumptions
);
838 TrustNode
TheoryArrays::explain(TNode literal
)
840 return d_im
.explainLit(literal
);
843 /////////////////////////////////////////////////////////////////////////////
845 /////////////////////////////////////////////////////////////////////////////
847 void TheoryArrays::notifySharedTerm(TNode t
)
849 Debug("arrays::sharing") << spaces(getSatContext()->getLevel())
850 << "TheoryArrays::notifySharedTerm(" << t
<< ")"
852 if (t
.getType().isArray()) {
853 d_sharedArrays
.insert(t
);
856 #ifdef CVC5_ASSERTIONS
857 d_sharedOther
.insert(t
);
859 d_sharedTerms
= true;
863 void TheoryArrays::checkPair(TNode r1
, TNode r2
)
865 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1
<< " and " << r2
<< std::endl
;
869 Assert(d_equalityEngine
->isTriggerTerm(x
, THEORY_ARRAYS
));
871 if (d_equalityEngine
->hasTerm(x
) && d_equalityEngine
->hasTerm(y
)
872 && (d_equalityEngine
->areEqual(x
, y
)
873 || d_equalityEngine
->areDisequal(x
, y
, false)))
875 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl
;
879 // If the terms are already known to be equal, we are also in good shape
880 if (d_equalityEngine
->areEqual(r1
, r2
))
882 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl
;
886 if (r1
[0] != r2
[0]) {
887 // If arrays are known to be disequal, or cannot become equal, we can continue
888 Assert(d_mayEqualEqualityEngine
.hasTerm(r1
[0])
889 && d_mayEqualEqualityEngine
.hasTerm(r2
[0]));
890 if (r1
[0].getType() != r2
[0].getType()
891 || d_equalityEngine
->areDisequal(r1
[0], r2
[0], false))
893 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl
;
896 else if (!d_mayEqualEqualityEngine
.areEqual(r1
[0], r2
[0])) {
901 if (!d_equalityEngine
->isTriggerTerm(y
, THEORY_ARRAYS
))
903 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
907 // Get representative trigger terms
909 d_equalityEngine
->getTriggerTermRepresentative(x
, THEORY_ARRAYS
);
911 d_equalityEngine
->getTriggerTermRepresentative(y
, THEORY_ARRAYS
);
912 EqualityStatus eqStatusDomain
= d_valuation
.getEqualityStatus(x_shared
, y_shared
);
913 switch (eqStatusDomain
) {
914 case EQUALITY_TRUE_AND_PROPAGATED
:
915 // Should have been propagated to us
919 // Missed propagation - need to add the pair so that theory engine can force propagation
920 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): missed propagation" << std::endl
;
922 case EQUALITY_FALSE_AND_PROPAGATED
:
923 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair "
924 "called when false in model"
926 // Should have been propagated to us
929 case EQUALITY_FALSE
: CVC5_FALLTHROUGH
;
930 case EQUALITY_FALSE_IN_MODEL
:
931 // This is unlikely, but I think it could happen
932 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair called when false in model" << std::endl
;
935 // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
940 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl
;
941 addCarePair(x_shared
, y_shared
);
945 void TheoryArrays::computeCareGraph()
947 if (d_sharedArrays
.size() > 0) {
948 CDNodeSet::key_iterator it1
= d_sharedArrays
.key_begin(), it2
, iend
= d_sharedArrays
.key_end();
949 for (; it1
!= iend
; ++it1
) {
950 for (it2
= it1
, ++it2
; it2
!= iend
; ++it2
) {
951 if ((*it1
).getType() != (*it2
).getType()) {
954 EqualityStatus eqStatusArr
= getEqualityStatus((*it1
), (*it2
));
955 if (eqStatusArr
!= EQUALITY_UNKNOWN
) {
958 Assert(d_valuation
.getEqualityStatus((*it1
), (*it2
))
959 == EQUALITY_UNKNOWN
);
960 addCarePair((*it1
), (*it2
));
961 ++d_numSharedArrayVarSplits
;
967 // Synchronize d_constReadsContext with SAT context
968 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
969 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel()) {
970 d_constReadsContext
->push();
973 // Go through the read terms and see if there are any to split on
975 // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
976 // The context is popped at the end. If this loop is interrupted for some reason, we have to make sure the context still
977 // gets popped or the solver will be in an inconsistent state
978 d_constReadsContext
->push();
979 unsigned size
= d_reads
.size();
980 for (unsigned i
= 0; i
< size
; ++ i
) {
981 TNode r1
= d_reads
[i
];
983 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1
<< std::endl
;
984 Assert(d_equalityEngine
->hasTerm(r1
));
987 if (!d_equalityEngine
->isTriggerTerm(x
, THEORY_ARRAYS
))
989 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
993 d_equalityEngine
->getTriggerTermRepresentative(x
, THEORY_ARRAYS
);
995 // 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
996 // Also, insert this read in the list at the proper index
998 if (!x_shared
.isConst()) {
999 x_shared
= d_valuation
.getModelValue(x_shared
);
1001 if (!x_shared
.isNull()) {
1003 CNodeNListMap::iterator it
= d_constReads
.find(x_shared
);
1004 if (it
== d_constReads
.end()) {
1005 // This is the only x_shared with this model value - no need to create any splits
1006 temp
= new(true) CTNodeList(d_constReadsContext
);
1007 d_constReads
[x_shared
] = temp
;
1010 temp
= (*it
).second
;
1011 for (size_t j
= 0; j
< temp
->size(); ++j
) {
1012 checkPair(r1
, (*temp
)[j
]);
1015 temp
->push_back(r1
);
1018 // We don't know the model value for x. Just do brute force examination of all pairs of reads
1019 for (unsigned j
= 0; j
< size
; ++j
) {
1020 TNode r2
= d_reads
[j
];
1021 Assert(d_equalityEngine
->hasTerm(r2
));
1024 for (unsigned j
= 0; j
< d_constReadsList
.size(); ++j
) {
1025 TNode r2
= d_constReadsList
[j
];
1026 Assert(d_equalityEngine
->hasTerm(r2
));
1031 d_constReadsContext
->pop();
1036 /////////////////////////////////////////////////////////////////////////////
1038 /////////////////////////////////////////////////////////////////////////////
1040 bool TheoryArrays::collectModelValues(TheoryModel
* m
,
1041 const std::set
<Node
>& termSet
)
1043 // termSet contains terms appearing in assertions and shared terms, and also
1044 // includes additional reads due to the RIntro1 and RIntro2 rules.
1045 NodeManager
* nm
= NodeManager::currentNM();
1046 // Compute arrays that we need to produce representatives for
1047 std::vector
<Node
> arrays
;
1049 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
1050 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
1052 Node eqc
= (*eqcs_i
);
1053 if (!eqc
.getType().isArray())
1055 // not an array, skip
1058 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
1059 for (; !eqc_i
.isFinished(); ++eqc_i
)
1062 // If this EC is an array type and it contains something other than STORE
1063 // nodes, we have to compute a representative explicitly
1064 if (termSet
.find(n
) != termSet
.end())
1066 if (n
.getKind() != kind::STORE
)
1068 arrays
.push_back(n
);
1075 // Build a list of all the relevant reads, indexed by the store representative
1076 std::map
<Node
, std::vector
<Node
> > selects
;
1077 set
<Node
>::iterator set_it
= termSet
.begin(), set_it_end
= termSet
.end();
1078 for (; set_it
!= set_it_end
; ++set_it
)
1081 // If this term is a select, record that the EC rep of its store parameter
1082 // is being read from using this term
1083 if (n
.getKind() == kind::SELECT
)
1085 selects
[d_equalityEngine
->getRepresentative(n
[0])].push_back(n
);
1090 DefValMap::iterator it
;
1091 TypeSet defaultValuesSet
;
1093 // Compute all default values already in use
1095 for (size_t i
= 0; i
< arrays
.size(); ++i
)
1097 TNode nrep
= d_equalityEngine
->getRepresentative(arrays
[i
]);
1098 d_mayEqualEqualityEngine
.addTerm(
1099 nrep
); // add the term in case it isn't there already
1100 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1101 it
= d_defValues
.find(mayRep
);
1102 if (it
!= d_defValues
.end())
1104 defaultValuesSet
.add(nrep
.getType().getArrayConstituentType(),
1110 // Loop through all array equivalence classes that need a representative
1112 for (size_t i
= 0; i
< arrays
.size(); ++i
)
1114 TNode n
= arrays
[i
];
1115 TNode nrep
= d_equalityEngine
->getRepresentative(n
);
1118 // Compute default value for this array - there is one default value for
1119 // every mayEqual equivalence class
1120 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1121 it
= d_defValues
.find(mayRep
);
1122 // If this mayEqual EC doesn't have a default value associated, get the next
1123 // available default value for the associated array element type
1124 if (it
== d_defValues
.end())
1126 TypeNode valueType
= nrep
.getType().getArrayConstituentType();
1127 rep
= defaultValuesSet
.nextTypeEnum(valueType
);
1130 Assert(defaultValuesSet
.getSet(valueType
)->begin()
1131 != defaultValuesSet
.getSet(valueType
)->end());
1132 rep
= *(defaultValuesSet
.getSet(valueType
)->begin());
1134 Trace("arrays-models") << "New default value = " << rep
<< endl
;
1135 d_defValues
[mayRep
] = rep
;
1142 // Build the STORE_ALL term with the default value
1143 rep
= nm
->mkConst(ArrayStoreAll(nrep
.getType(), rep
));
1147 std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(n);
1148 if (it == d_skolemCache.end()) {
1149 rep = nm->mkSkolem("array_collect_model_var", n.getType(), "base model
1150 variable for array collectModelInfo"); d_skolemCache[n] = rep;
1158 // For each read, require that the rep stores the right value
1159 vector
<Node
>& reads
= selects
[nrep
];
1160 for (unsigned j
= 0; j
< reads
.size(); ++j
)
1162 rep
= nm
->mkNode(kind::STORE
, rep
, reads
[j
][1], reads
[j
]);
1164 if (!m
->assertEquality(n
, rep
, true))
1170 m
->assertSkeleton(rep
);
1176 /////////////////////////////////////////////////////////////////////////////
1178 /////////////////////////////////////////////////////////////////////////////
1181 void TheoryArrays::presolve()
1183 Trace("arrays")<<"Presolving \n";
1186 d_dstratInit
= true;
1187 // add the decision strategy, which is user-context-independent
1188 d_im
.getDecisionManager()->registerStrategy(
1189 DecisionManager::STRAT_ARRAYS
,
1191 DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT
);
1196 /////////////////////////////////////////////////////////////////////////////
1198 /////////////////////////////////////////////////////////////////////////////
1200 Node
TheoryArrays::getSkolem(TNode ref
)
1202 // the call to SkolemCache::getExtIndexSkolem should be deterministic, but use
1203 // cache anyways for now
1205 std::unordered_map
<Node
, Node
>::iterator it
= d_skolemCache
.find(ref
);
1206 if (it
== d_skolemCache
.end()) {
1207 Assert(ref
.getKind() == kind::NOT
&& ref
[0].getKind() == kind::EQUAL
);
1208 // make the skolem using the skolem cache utility
1209 skolem
= SkolemCache::getExtIndexSkolem(ref
);
1210 d_skolemCache
[ref
] = skolem
;
1213 skolem
= (*it
).second
;
1216 Debug("pf::array") << "Pregistering a Skolem" << std::endl
;
1217 preRegisterTermInternal(skolem
);
1218 Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl
;
1220 Debug("pf::array") << "getSkolem DONE" << std::endl
;
1224 void TheoryArrays::postCheck(Effort level
)
1226 if ((options::arraysEagerLemmas() || fullEffort(level
))
1227 && !d_state
.isInConflict() && options::arraysWeakEquivalence())
1229 // Replay all array merges to update weak equivalence data structures
1230 context::CDList
<Node
>::iterator it
= d_arrayMerges
.begin(), iend
= d_arrayMerges
.end();
1232 for (; it
!= iend
; ++it
) {
1236 weakEquivMakeRep(b
);
1237 if (weakEquivGetRep(a
) == b
) {
1238 weakEquivAddSecondary(TNode(), a
, b
, eq
);
1241 d_infoMap
.setWeakEquivPointer(b
, a
);
1242 d_infoMap
.setWeakEquivIndex(b
, TNode());
1244 #ifdef CVC5_ASSERTIONS
1245 checkWeakEquiv(false);
1248 #ifdef CVC5_ASSERTIONS
1249 checkWeakEquiv(true);
1252 d_readTableContext
->push();
1254 CTNodeList
* bucketList
= NULL
;
1255 CTNodeList::const_iterator i
= d_reads
.begin(), readsEnd
= d_reads
.end();
1256 for (; i
!= readsEnd
; ++i
) {
1257 const TNode
& r
= *i
;
1259 Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r
<< std::endl
;
1261 // Find the bucket for this read.
1262 mayRep
= d_mayEqualEqualityEngine
.getRepresentative(r
[0]);
1263 iRep
= d_equalityEngine
->getRepresentative(r
[1]);
1264 std::pair
<TNode
, TNode
> key(mayRep
, iRep
);
1265 ReadBucketMap::iterator rbm_it
= d_readBucketTable
.find(key
);
1266 if (rbm_it
== d_readBucketTable
.end())
1268 bucketList
= new(true) CTNodeList(d_readTableContext
);
1269 d_readBucketAllocations
.push_back(bucketList
);
1270 d_readBucketTable
[key
] = bucketList
;
1273 bucketList
= rbm_it
->second
;
1275 CTNodeList::const_iterator ctnl_it
= bucketList
->begin(),
1276 ctnl_iend
= bucketList
->end();
1277 for (; ctnl_it
!= ctnl_iend
; ++ctnl_it
)
1279 const TNode
& r2
= *ctnl_it
;
1280 Assert(r2
.getKind() == kind::SELECT
);
1281 Assert(mayRep
== d_mayEqualEqualityEngine
.getRepresentative(r2
[0]));
1282 Assert(iRep
== d_equalityEngine
->getRepresentative(r2
[1]));
1283 if (d_equalityEngine
->areEqual(r
, r2
))
1287 if (weakEquivGetRepIndex(r
[0], r
[1]) == weakEquivGetRepIndex(r2
[0], r
[1])) {
1288 // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1289 vector
<TNode
> conjunctions
;
1290 Assert(d_equalityEngine
->areEqual(r
, Rewriter::rewrite(r
)));
1291 Assert(d_equalityEngine
->areEqual(r2
, Rewriter::rewrite(r2
)));
1292 Node lemma
= Rewriter::rewrite(r
).eqNode(Rewriter::rewrite(r2
)).negate();
1293 d_permRef
.push_back(lemma
);
1294 conjunctions
.push_back(lemma
);
1295 if (r
[1] != r2
[1]) {
1296 d_equalityEngine
->explainEquality(r
[1], r2
[1], true, conjunctions
);
1298 // TODO: get smaller lemmas by eliminating shared parts of path
1299 weakEquivBuildCond(r
[0], r
[1], conjunctions
);
1300 weakEquivBuildCond(r2
[0], r
[1], conjunctions
);
1301 lemma
= mkAnd(conjunctions
, true);
1302 // LSH FIXME: which kind of arrays lemma is this
1304 << "Arrays::addExtLemma (weak-eq) " << lemma
<< "\n";
1305 d_out
->lemma(lemma
, LemmaProperty::SEND_ATOMS
);
1306 d_readTableContext
->pop();
1307 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl
;
1311 bucketList
->push_back(r
);
1313 d_readTableContext
->pop();
1316 if (!options::arraysEagerLemmas() && fullEffort(level
)
1317 && !d_state
.isInConflict() && !options::arraysWeakEquivalence())
1319 // generate the lemmas on the worklist
1320 Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue
.size() << "\n";
1321 while (d_RowQueue
.size() > 0 && !d_state
.isInConflict())
1323 if (dischargeLemmas()) {
1329 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl
;
1332 bool TheoryArrays::preNotifyFact(
1333 TNode atom
, bool pol
, TNode fact
, bool isPrereg
, bool isInternal
)
1335 if (!isInternal
&& !isPrereg
)
1337 if (atom
.getKind() == kind::EQUAL
)
1339 if (!d_equalityEngine
->hasTerm(atom
[0]))
1341 Assert(atom
[0].isConst());
1342 d_equalityEngine
->addTerm(atom
[0]);
1344 if (!d_equalityEngine
->hasTerm(atom
[1]))
1346 Assert(atom
[1].isConst());
1347 d_equalityEngine
->addTerm(atom
[1]);
1354 void TheoryArrays::notifyFact(TNode atom
, bool pol
, TNode fact
, bool isInternal
)
1357 if (atom
.getKind() == kind::EQUAL
&& !pol
&& !isInternal
)
1359 // Notice that this should be an external assertion, since we do not
1360 // internally infer disequalities.
1361 // Apply ArrDiseq Rule if diseq is between arrays
1362 if (fact
[0][0].getType().isArray() && !d_state
.isInConflict())
1364 NodeManager
* nm
= NodeManager::currentNM();
1367 // k is the skolem for this disequality.
1368 Debug("pf::array") << "Check: kind::NOT: array theory making a skolem"
1371 // If not in replay mode, generate a fresh skolem variable
1372 k
= getSkolem(fact
);
1374 Node ak
= nm
->mkNode(kind::SELECT
, fact
[0][0], k
);
1375 Node bk
= nm
->mkNode(kind::SELECT
, fact
[0][1], k
);
1376 Node eq
= ak
.eqNode(bk
);
1377 Node lemma
= fact
[0].orNode(eq
.notNode());
1379 if (options::arraysPropagate() > 0 && d_equalityEngine
->hasTerm(ak
)
1380 && d_equalityEngine
->hasTerm(bk
))
1382 // Propagate witness disequality - might produce a conflict
1383 Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1384 << "\teq = " << eq
<< std::endl
1385 << "\treason = " << fact
<< std::endl
;
1386 d_im
.assertInference(eq
, false, InferenceId::ARRAYS_EXT
, fact
, PfRule::ARRAYS_EXT
);
1390 // If this is the solution pass, generate the lemma. Otherwise, don't
1391 // generate it - as this is the lemma that we're reproving...
1392 Trace("arrays-lem") << "Arrays::addExtLemma " << lemma
<< "\n";
1393 d_im
.arrayLemma(eq
.notNode(), InferenceId::ARRAYS_EXT
, fact
, PfRule::ARRAYS_EXT
);
1398 Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem"
1400 d_modelConstraints
.push_back(fact
);
1405 Node
TheoryArrays::mkAnd(std::vector
<TNode
>& conjunctions
, bool invert
, unsigned startIndex
)
1407 if (conjunctions
.empty())
1409 return invert
? d_false
: d_true
;
1412 std::set
<TNode
> all
;
1414 unsigned i
= startIndex
;
1416 for (; i
< conjunctions
.size(); ++i
) {
1417 t
= conjunctions
[i
];
1421 else if (t
.getKind() == kind::AND
) {
1422 for(TNode::iterator child_it
= t
.begin(); child_it
!= t
.end(); ++child_it
) {
1423 if (*child_it
== d_true
) {
1426 all
.insert(*child_it
);
1434 if (all
.size() == 0) {
1435 return invert
? d_false
: d_true
;
1437 if (all
.size() == 1) {
1438 // All the same, or just one
1440 return (*(all
.begin())).negate();
1443 return *(all
.begin());
1447 NodeBuilder
conjunction(invert
? kind::OR
: kind::AND
);
1448 std::set
<TNode
>::const_iterator it
= all
.begin();
1449 std::set
<TNode
>::const_iterator it_end
= all
.end();
1450 while (it
!= it_end
) {
1452 conjunction
<< (*it
).negate();
1464 void TheoryArrays::setNonLinear(TNode a
)
1466 if (options::arraysWeakEquivalence()) return;
1467 if (d_infoMap
.isNonLinear(a
)) return;
1469 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a
<< ")\n";
1470 d_infoMap
.setNonLinear(a
);
1473 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1474 const CTNodeList
* st_a
= d_infoMap
.getStores(a
);
1475 const CTNodeList
* inst_a
= d_infoMap
.getInStores(a
);
1479 // Propagate non-linearity down chain of stores
1480 for( ; it
< st_a
->size(); ++it
) {
1481 TNode store
= (*st_a
)[it
];
1482 Assert(store
.getKind() == kind::STORE
);
1483 setNonLinear(store
[0]);
1486 // Instantiate ROW lemmas that were ignored before
1489 for(; it2
< i_a
->size(); ++it2
) {
1490 TNode i
= (*i_a
)[it2
];
1492 for ( ; it
< inst_a
->size(); ++it
) {
1493 TNode store
= (*inst_a
)[it
];
1494 Assert(store
.getKind() == kind::STORE
);
1497 lem
= std::make_tuple(store
, c
, j
, i
);
1498 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1505 void TheoryArrays::mergeArrays(TNode a
, TNode b
)
1507 // Note: a is the new representative
1508 Assert(a
.getType().isArray() && b
.getType().isArray());
1510 if (d_mergeInProgress
) {
1511 // Nested call to mergeArrays, just push on the queue and return
1512 d_mergeQueue
.push(a
.eqNode(b
));
1516 d_mergeInProgress
= true;
1520 // Normally, a is its own representative, but it's possible for a to have
1521 // been merged with another array after it got queued up by the equality engine,
1522 // so we take its representative to be safe.
1523 a
= d_equalityEngine
->getRepresentative(a
);
1524 Assert(d_equalityEngine
->getRepresentative(b
) == a
);
1525 Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: (" << a
<< ", " << b
<< ")\n";
1527 if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1528 bool aNL
= d_infoMap
.isNonLinear(a
);
1529 bool bNL
= d_infoMap
.isNonLinear(b
);
1532 // already both marked as non-linear - no need to do anything
1535 // Set b to be non-linear
1541 // Set a to be non-linear
1545 // Check for new non-linear arrays
1546 const CTNodeList
* astores
= d_infoMap
.getStores(a
);
1547 const CTNodeList
* bstores
= d_infoMap
.getStores(b
);
1548 Assert(astores
->size() <= 1 && bstores
->size() <= 1);
1549 if (astores
->size() > 0 && bstores
->size() > 0) {
1557 TNode constArrA
= d_infoMap
.getConstArr(a
);
1558 TNode constArrB
= d_infoMap
.getConstArr(b
);
1559 if (constArrA
.isNull()) {
1560 if (!constArrB
.isNull()) {
1561 d_infoMap
.setConstArr(a
,constArrB
);
1564 else if (!constArrB
.isNull()) {
1565 if (constArrA
!= constArrB
) {
1566 conflict(constArrA
,constArrB
);
1570 TNode mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1571 TNode mayRepB
= d_mayEqualEqualityEngine
.getRepresentative(b
);
1573 // If a and b have different default values associated with their mayequal equivalence classes,
1574 // things get complicated. Similarly, if two mayequal equivalence classes have different
1575 // constant representatives, it's not clear what to do. - disallow these cases for now. -Clark
1576 DefValMap::iterator it
= d_defValues
.find(mayRepA
);
1577 DefValMap::iterator it2
= d_defValues
.find(mayRepB
);
1580 if (it
!= d_defValues
.end()) {
1581 defValue
= (*it
).second
;
1582 if ((it2
!= d_defValues
.end() && (defValue
!= (*it2
).second
)) ||
1583 (mayRepA
.isConst() && mayRepB
.isConst() && mayRepA
!= mayRepB
)) {
1584 throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1587 else if (it2
!= d_defValues
.end()) {
1588 defValue
= (*it2
).second
;
1590 d_mayEqualEqualityEngine
.assertEquality(a
.eqNode(b
), true, d_true
);
1591 Assert(d_mayEqualEqualityEngine
.consistent());
1592 if (!defValue
.isNull()) {
1593 mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1594 d_defValues
[mayRepA
] = defValue
;
1597 checkRowLemmas(a
,b
);
1598 checkRowLemmas(b
,a
);
1600 // merge info adds the list of the 2nd argument to the first
1601 d_infoMap
.mergeInfo(a
, b
);
1603 if (options::arraysWeakEquivalence()) {
1604 d_arrayMerges
.push_back(a
.eqNode(b
));
1607 // If no more to do, break
1608 if (d_state
.isInConflict() || d_mergeQueue
.empty())
1613 // Otherwise, prepare for next iteration
1614 n
= d_mergeQueue
.front();
1619 d_mergeInProgress
= false;
1623 void TheoryArrays::checkStore(TNode a
) {
1624 if (options::arraysWeakEquivalence()) return;
1625 Trace("arrays-cri")<<"Arrays::checkStore "<<a
<<"\n";
1627 if(Trace
.isOn("arrays-cri")) {
1628 d_infoMap
.getInfo(a
)->print();
1630 Assert(a
.getType().isArray());
1631 Assert(a
.getKind() == kind::STORE
);
1635 TNode brep
= d_equalityEngine
->getRepresentative(b
);
1637 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(brep
)) {
1638 const CTNodeList
* js
= d_infoMap
.getIndices(brep
);
1641 for(; it
< js
->size(); ++it
) {
1642 TNode j
= (*js
)[it
];
1643 if (i
== j
) continue;
1644 lem
= std::make_tuple(a
, b
, i
, j
);
1645 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkStore ("<<a
<<", "<<b
<<", "<<i
<<", "<<j
<<")\n";
1652 void TheoryArrays::checkRowForIndex(TNode i
, TNode a
)
1654 if (options::arraysWeakEquivalence()) return;
1655 Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a
<<"\n";
1656 Trace("arrays-cri")<<" index "<<i
<<"\n";
1658 if(Trace
.isOn("arrays-cri")) {
1659 d_infoMap
.getInfo(a
)->print();
1661 Assert(a
.getType().isArray());
1662 Assert(d_equalityEngine
->getRepresentative(a
) == a
);
1664 TNode constArr
= d_infoMap
.getConstArr(a
);
1665 if (!constArr
.isNull()) {
1666 ArrayStoreAll storeAll
= constArr
.getConst
<ArrayStoreAll
>();
1667 Node defValue
= storeAll
.getValue();
1668 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1669 if (!d_equalityEngine
->hasTerm(selConst
))
1671 preRegisterTermInternal(selConst
);
1673 d_im
.assertInference(selConst
.eqNode(defValue
),
1675 InferenceId::UNKNOWN
,
1677 PfRule::ARRAYS_TRUST
);
1680 const CTNodeList
* stores
= d_infoMap
.getStores(a
);
1681 const CTNodeList
* instores
= d_infoMap
.getInStores(a
);
1685 for(; it
< stores
->size(); ++it
) {
1686 TNode store
= (*stores
)[it
];
1687 Assert(store
.getKind() == kind::STORE
);
1689 if (i
== j
) continue;
1690 lem
= std::make_tuple(store
, store
[0], j
, i
);
1691 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store
<<", "<<store
[0]<<", "<<j
<<", "<<i
<<")\n";
1695 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(a
)) {
1697 for(; it
< instores
->size(); ++it
) {
1698 TNode instore
= (*instores
)[it
];
1699 Assert(instore
.getKind() == kind::STORE
);
1700 TNode j
= instore
[1];
1701 if (i
== j
) continue;
1702 lem
= std::make_tuple(instore
, instore
[0], j
, i
);
1703 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore
<<", "<<instore
[0]<<", "<<j
<<", "<<i
<<")\n";
1710 // a just became equal to b
1711 // look for new ROW lemmas
1712 void TheoryArrays::checkRowLemmas(TNode a
, TNode b
)
1714 if (options::arraysWeakEquivalence()) return;
1715 Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a
<<"\n";
1716 if(Trace
.isOn("arrays-crl"))
1717 d_infoMap
.getInfo(a
)->print();
1718 Trace("arrays-crl")<<" ------------ and "<<b
<<"\n";
1719 if(Trace
.isOn("arrays-crl"))
1720 d_infoMap
.getInfo(b
)->print();
1722 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1724 TNode constArr
= d_infoMap
.getConstArr(b
);
1725 if (!constArr
.isNull()) {
1726 for( ; it
< i_a
->size(); ++it
) {
1727 TNode i
= (*i_a
)[it
];
1728 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1729 if (!d_equalityEngine
->hasTerm(selConst
))
1731 preRegisterTermInternal(selConst
);
1736 const CTNodeList
* st_b
= d_infoMap
.getStores(b
);
1737 const CTNodeList
* inst_b
= d_infoMap
.getInStores(b
);
1742 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1743 TNode i
= (*i_a
)[it
];
1745 for ( ; its
< st_b
->size(); ++its
) {
1746 TNode store
= (*st_b
)[its
];
1747 Assert(store
.getKind() == kind::STORE
);
1750 lem
= std::make_tuple(store
, c
, j
, i
);
1751 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1756 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(b
)) {
1757 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1758 TNode i
= (*i_a
)[it
];
1760 for ( ; its
< inst_b
->size(); ++its
) {
1761 TNode store
= (*inst_b
)[its
];
1762 Assert(store
.getKind() == kind::STORE
);
1765 lem
= std::make_tuple(store
, c
, j
, i
);
1766 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1771 Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1774 void TheoryArrays::propagateRowLemma(RowLemmaType lem
)
1776 Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1777 << options::arraysPropagate() << std::endl
;
1780 std::tie(a
, b
, i
, j
) = lem
;
1782 Assert(a
.getType().isArray() && b
.getType().isArray());
1783 if (d_equalityEngine
->areEqual(a
, b
) || d_equalityEngine
->areEqual(i
, j
))
1788 NodeManager
* nm
= NodeManager::currentNM();
1789 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1790 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1792 // Try to avoid introducing new read terms: track whether these already exist
1793 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1794 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1795 bool bothExist
= ajExists
&& bjExists
;
1797 // If propagating, check propagations
1798 int prop
= options::arraysPropagate();
1800 if (d_equalityEngine
->areDisequal(i
, j
, true) && (bothExist
|| prop
> 1))
1802 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj
<<", "<<bj
<<")\n";
1803 Node aj_eq_bj
= aj
.eqNode(bj
);
1805 (i
.isConst() && j
.isConst()) ? d_true
: i
.eqNode(j
).notNode();
1806 d_permRef
.push_back(reason
);
1808 preRegisterTermInternal(aj
);
1811 preRegisterTermInternal(bj
);
1813 d_im
.assertInference(
1814 aj_eq_bj
, true, InferenceId::ARRAYS_READ_OVER_WRITE
, reason
, PfRule::ARRAYS_READ_OVER_WRITE
);
1818 if (bothExist
&& d_equalityEngine
->areDisequal(aj
, bj
, true))
1820 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating i = j ("<<i
<<", "<<j
<<")\n";
1822 (aj
.isConst() && bj
.isConst()) ? d_true
: aj
.eqNode(bj
).notNode();
1823 Node j_eq_i
= j
.eqNode(i
);
1824 d_im
.assertInference(
1825 j_eq_i
, true, InferenceId::ARRAYS_READ_OVER_WRITE_CONTRA
, reason
, PfRule::ARRAYS_READ_OVER_WRITE_CONTRA
);
1832 void TheoryArrays::queueRowLemma(RowLemmaType lem
)
1834 Debug("pf::array") << "Array solver: queue row lemma called" << std::endl
;
1836 if (d_state
.isInConflict() || d_RowAlreadyAdded
.contains(lem
))
1841 std::tie(a
, b
, i
, j
) = lem
;
1843 Assert(a
.getType().isArray() && b
.getType().isArray());
1844 if (d_equalityEngine
->areEqual(a
, b
) || d_equalityEngine
->areEqual(i
, j
))
1849 NodeManager
* nm
= NodeManager::currentNM();
1850 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1851 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1853 // Try to avoid introducing new read terms: track whether these already exist
1854 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1855 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1856 bool bothExist
= ajExists
&& bjExists
;
1858 // If propagating, check propagations
1859 int prop
= options::arraysPropagate();
1861 propagateRowLemma(lem
);
1864 // Prefer equality between indexes so as not to introduce new read terms
1865 if (options::arraysEagerIndexSplitting() && !bothExist
1866 && !d_equalityEngine
->areDisequal(i
, j
, false))
1869 i_eq_j
= d_valuation
.ensureLiteral(i
.eqNode(j
)); // TODO: think about this
1871 i_eq_j
= i
.eqNode(j
);
1873 getOutputChannel().requirePhase(i_eq_j
, true);
1874 d_decisionRequests
.push(i_eq_j
);
1877 // TODO: maybe add triggers here
1879 if (options::arraysEagerLemmas() || bothExist
)
1881 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1882 Node aj2
= Rewriter::rewrite(aj
);
1885 preRegisterTermInternal(aj
);
1887 if (!d_equalityEngine
->hasTerm(aj2
))
1889 preRegisterTermInternal(aj2
);
1891 d_im
.assertInference(
1892 aj
.eqNode(aj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1894 Node bj2
= Rewriter::rewrite(bj
);
1897 preRegisterTermInternal(bj
);
1899 if (!d_equalityEngine
->hasTerm(bj2
))
1901 preRegisterTermInternal(bj2
);
1903 d_im
.assertInference(
1904 bj
.eqNode(bj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1911 Node eq1
= aj2
.eqNode(bj2
);
1912 Node eq1_r
= Rewriter::rewrite(eq1
);
1913 if (eq1_r
== d_true
) {
1914 if (!d_equalityEngine
->hasTerm(aj2
))
1916 preRegisterTermInternal(aj2
);
1918 if (!d_equalityEngine
->hasTerm(bj2
))
1920 preRegisterTermInternal(bj2
);
1922 d_im
.assertInference(eq1
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1926 Node eq2
= i
.eqNode(j
);
1927 Node eq2_r
= Rewriter::rewrite(eq2
);
1928 if (eq2_r
== d_true
) {
1929 d_im
.assertInference(eq2
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
1933 Node lemma
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
1935 Trace("arrays-lem") << "Arrays::addRowLemma (1) adding " << lemma
<< "\n";
1936 d_RowAlreadyAdded
.insert(lem
);
1937 // use non-rewritten nodes
1939 aj
.eqNode(bj
), InferenceId::ARRAYS_READ_OVER_WRITE
, eq2
.notNode(), PfRule::ARRAYS_READ_OVER_WRITE
);
1943 d_RowQueue
.push(lem
);
1947 Node
TheoryArrays::getNextDecisionRequest()
1949 if(! d_decisionRequests
.empty()) {
1950 Node n
= d_decisionRequests
.front();
1951 d_decisionRequests
.pop();
1954 return Node::null();
1958 bool TheoryArrays::dischargeLemmas()
1960 bool lemmasAdded
= false;
1961 size_t sz
= d_RowQueue
.size();
1962 for (unsigned count
= 0; count
< sz
; ++count
) {
1963 RowLemmaType l
= d_RowQueue
.front();
1965 if (d_RowAlreadyAdded
.contains(l
)) {
1970 std::tie(a
, b
, i
, j
) = l
;
1971 Assert(a
.getType().isArray() && b
.getType().isArray());
1973 NodeManager
* nm
= NodeManager::currentNM();
1974 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1975 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1976 bool ajExists
= d_equalityEngine
->hasTerm(aj
);
1977 bool bjExists
= d_equalityEngine
->hasTerm(bj
);
1979 // Check for redundant lemma
1980 // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
1981 if (!d_equalityEngine
->hasTerm(i
) || !d_equalityEngine
->hasTerm(j
)
1982 || d_equalityEngine
->areEqual(i
, j
) || !d_equalityEngine
->hasTerm(a
)
1983 || !d_equalityEngine
->hasTerm(b
) || d_equalityEngine
->areEqual(a
, b
)
1984 || (ajExists
&& bjExists
&& d_equalityEngine
->areEqual(aj
, bj
)))
1989 int prop
= options::arraysPropagate();
1991 propagateRowLemma(l
);
1992 if (d_state
.isInConflict())
1998 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1999 Node aj2
= Rewriter::rewrite(aj
);
2002 preRegisterTermInternal(aj
);
2004 if (!d_equalityEngine
->hasTerm(aj2
))
2006 preRegisterTermInternal(aj2
);
2008 d_im
.assertInference(
2009 aj
.eqNode(aj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2011 Node bj2
= Rewriter::rewrite(bj
);
2014 preRegisterTermInternal(bj
);
2016 if (!d_equalityEngine
->hasTerm(bj2
))
2018 preRegisterTermInternal(bj2
);
2020 d_im
.assertInference(
2021 bj
.eqNode(bj2
), true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2028 Node eq1
= aj2
.eqNode(bj2
);
2029 Node eq1_r
= Rewriter::rewrite(eq1
);
2030 if (eq1_r
== d_true
) {
2031 if (!d_equalityEngine
->hasTerm(aj2
))
2033 preRegisterTermInternal(aj2
);
2035 if (!d_equalityEngine
->hasTerm(bj2
))
2037 preRegisterTermInternal(bj2
);
2039 d_im
.assertInference(eq1
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2043 Node eq2
= i
.eqNode(j
);
2044 Node eq2_r
= Rewriter::rewrite(eq2
);
2045 if (eq2_r
== d_true
) {
2046 d_im
.assertInference(eq2
, true, InferenceId::UNKNOWN
, d_true
, PfRule::MACRO_SR_PRED_INTRO
);
2050 Node lem
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
2052 Trace("arrays-lem") << "Arrays::addRowLemma (2) adding " << lem
<< "\n";
2053 d_RowAlreadyAdded
.insert(l
);
2054 // use non-rewritten nodes, theory preprocessing will rewrite
2056 aj
.eqNode(bj
), InferenceId::ARRAYS_READ_OVER_WRITE
, eq2
.notNode(), PfRule::ARRAYS_READ_OVER_WRITE
);
2059 if (options::arraysReduceSharing()) {
2066 void TheoryArrays::conflict(TNode a
, TNode b
) {
2067 Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl
;
2070 // if in check model, don't send the conflict
2071 d_state
.notifyInConflict();
2074 d_im
.conflictEqConstantMerge(a
, b
);
2077 TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2082 void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
2083 Node
TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2085 return d_ta
->getNextDecisionRequest();
2087 std::string
TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2089 return std::string("th_arrays_dec");
2092 void TheoryArrays::computeRelevantTerms(std::set
<Node
>& termSet
)
2094 NodeManager
* nm
= NodeManager::currentNM();
2095 // make sure RIntro1 reads are included in the relevant set of reads
2096 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
2097 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
2099 Node eqc
= (*eqcs_i
);
2100 if (!eqc
.getType().isArray())
2102 // not an array, skip
2105 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
2106 for (; !eqc_i
.isFinished(); ++eqc_i
)
2109 if (termSet
.find(n
) != termSet
.end())
2111 if (n
.getKind() == kind::STORE
)
2113 // Make sure RIntro1 reads are included
2114 Node r
= nm
->mkNode(kind::SELECT
, n
, n
[1]);
2115 Trace("arrays::collectModelInfo")
2116 << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r
2124 // Now do a fixed-point iteration to get all reads that need to be included
2125 // because of RIntro2 rule
2130 eqcs_i
= eq::EqClassesIterator(d_equalityEngine
);
2131 for (; !eqcs_i
.isFinished(); ++eqcs_i
)
2133 Node eqc
= (*eqcs_i
);
2134 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, d_equalityEngine
);
2135 for (; !eqc_i
.isFinished(); ++eqc_i
)
2138 if (n
.getKind() == kind::SELECT
&& termSet
.find(n
) != termSet
.end())
2140 // Find all terms equivalent to n[0] and get corresponding read terms
2141 Node array_eqc
= d_equalityEngine
->getRepresentative(n
[0]);
2142 eq::EqClassIterator array_eqc_i
=
2143 eq::EqClassIterator(array_eqc
, d_equalityEngine
);
2144 for (; !array_eqc_i
.isFinished(); ++array_eqc_i
)
2146 Node arr
= *array_eqc_i
;
2147 if (arr
.getKind() == kind::STORE
2148 && termSet
.find(arr
) != termSet
.end()
2149 && !d_equalityEngine
->areEqual(arr
[1], n
[1]))
2151 Node r
= nm
->mkNode(kind::SELECT
, arr
, n
[1]);
2152 if (termSet
.find(r
) == termSet
.end()
2153 && d_equalityEngine
->hasTerm(r
))
2155 Trace("arrays::collectModelInfo")
2156 << "TheoryArrays::collectModelInfo, adding RIntro2(a) "
2162 r
= nm
->mkNode(kind::SELECT
, arr
[0], n
[1]);
2163 if (termSet
.find(r
) == termSet
.end()
2164 && d_equalityEngine
->hasTerm(r
))
2166 Trace("arrays::collectModelInfo")
2167 << "TheoryArrays::collectModelInfo, adding RIntro2(b) "
2176 // Find all stores in which n[0] appears and get corresponding read
2178 const CTNodeList
* instores
= d_infoMap
.getInStores(array_eqc
);
2180 for (; it
< instores
->size(); ++it
)
2182 TNode instore
= (*instores
)[it
];
2183 Assert(instore
.getKind() == kind::STORE
);
2184 if (termSet
.find(instore
) != termSet
.end()
2185 && !d_equalityEngine
->areEqual(instore
[1], n
[1]))
2187 Node r
= nm
->mkNode(kind::SELECT
, instore
, n
[1]);
2188 if (termSet
.find(r
) == termSet
.end()
2189 && d_equalityEngine
->hasTerm(r
))
2191 Trace("arrays::collectModelInfo")
2192 << "TheoryArrays::collectModelInfo, adding RIntro2(c) "
2198 r
= nm
->mkNode(kind::SELECT
, instore
[0], n
[1]);
2199 if (termSet
.find(r
) == termSet
.end()
2200 && d_equalityEngine
->hasTerm(r
))
2202 Trace("arrays::collectModelInfo")
2203 << "TheoryArrays::collectModelInfo, adding RIntro2(d) "
2217 } // namespace arrays
2218 } // namespace theory