1 /********************* */
2 /*! \file theory_arrays.cpp
4 ** Original author: Clark Barrett
5 ** Major contributors: Morgan Deters
6 ** Minor contributors (to current version): Tim King, Kshitij Bansal, Andrew Reynolds, Dejan Jovanovic
7 ** This file is part of the CVC4 project.
8 ** Copyright (c) 2009-2014 New York University and The University of Iowa
9 ** See the file COPYING in the top-level source directory for licensing
10 ** information.\endverbatim
12 ** \brief Implementation of the theory of arrays.
14 ** Implementation of the theory of arrays.
17 #include "theory/arrays/theory_arrays.h"
21 #include "expr/kind.h"
22 #include "options/arrays_options.h"
23 #include "options/smt_options.h"
24 #include "smt/logic_exception.h"
25 #include "smt/smt_statistics_registry.h"
26 #include "smt_util/command.h"
27 #include "theory/rewriter.h"
28 #include "theory/theory_model.h"
29 #include "proof/theory_proof.h"
30 #include "proof/proof_manager.h"
31 #include "theory/valuation.h"
39 // These are the options that produce the best empirical results on QF_AX benchmarks.
41 // eagerIndexSplitting = false
43 // Use static configuration of options for now
44 const bool d_ccStore
= false;
45 const bool d_useArrTable
= false;
46 //const bool d_eagerLemmas = false;
47 const bool d_preprocess
= true;
48 const bool d_solveWrite
= true;
49 const bool d_solveWrite2
= false;
50 // These are now options
51 //const bool d_propagateLemmas = true;
52 //bool d_useNonLinearOpt = true;
53 //bool d_lazyRIntro1 = true;
54 //bool d_eagerIndexSplitting = false;
56 TheoryArrays::TheoryArrays(context::Context
* c
, context::UserContext
* u
,
57 OutputChannel
& out
, Valuation valuation
,
58 const LogicInfo
& logicInfo
, std::string name
)
59 : Theory(THEORY_ARRAY
, c
, u
, out
, valuation
, logicInfo
, name
),
60 d_numRow(name
+ "theory::arrays::number of Row lemmas", 0),
61 d_numExt(name
+ "theory::arrays::number of Ext lemmas", 0),
62 d_numProp(name
+ "theory::arrays::number of propagations", 0),
63 d_numExplain(name
+ "theory::arrays::number of explanations", 0),
64 d_numNonLinear(name
+ "theory::arrays::number of calls to setNonLinear", 0),
65 d_numSharedArrayVarSplits(name
+ "theory::arrays::number of shared array var splits", 0),
66 d_numGetModelValSplits(name
+ "theory::arrays::number of getModelVal splits", 0),
67 d_numGetModelValConflicts(name
+ "theory::arrays::number of getModelVal conflicts", 0),
68 d_numSetModelValSplits(name
+ "theory::arrays::number of setModelVal splits", 0),
69 d_numSetModelValConflicts(name
+ "theory::arrays::number of setModelVal conflicts", 0),
70 d_ppEqualityEngine(u
, name
+ "theory::arrays::TheoryArraysPP" , true),
73 d_literalsToPropagate(c
),
74 d_literalsToPropagateIndex(c
, 0),
76 d_mayEqualEqualityEngine(c
, name
+ "theory::arrays::TheoryArraysMayEqual", true),
78 d_equalityEngine(d_notify
, c
, name
+ "theory::arrays::TheoryArrays", true),
81 d_infoMap(c
, &d_backtracker
),
83 d_mergeInProgress(false),
88 d_sharedTerms(c
, false),
91 d_constReadsContext(new context::Context()),
92 d_contextPopper(c
, d_constReadsContext
),
94 d_decisionRequests(c
),
96 d_modelConstraints(c
),
99 d_readTableContext(new context::Context()),
101 d_inCheckModel(false)
103 smtStatisticsRegistry()->registerStat(&d_numRow
);
104 smtStatisticsRegistry()->registerStat(&d_numExt
);
105 smtStatisticsRegistry()->registerStat(&d_numProp
);
106 smtStatisticsRegistry()->registerStat(&d_numExplain
);
107 smtStatisticsRegistry()->registerStat(&d_numNonLinear
);
108 smtStatisticsRegistry()->registerStat(&d_numSharedArrayVarSplits
);
109 smtStatisticsRegistry()->registerStat(&d_numGetModelValSplits
);
110 smtStatisticsRegistry()->registerStat(&d_numGetModelValConflicts
);
111 smtStatisticsRegistry()->registerStat(&d_numSetModelValSplits
);
112 smtStatisticsRegistry()->registerStat(&d_numSetModelValConflicts
);
114 d_true
= NodeManager::currentNM()->mkConst
<bool>(true);
115 d_false
= NodeManager::currentNM()->mkConst
<bool>(false);
117 // The preprocessing congruence kinds
118 d_ppEqualityEngine
.addFunctionKind(kind::SELECT
);
119 d_ppEqualityEngine
.addFunctionKind(kind::STORE
);
121 // The kinds we are treating as function application in congruence
122 d_equalityEngine
.addFunctionKind(kind::SELECT
);
124 d_equalityEngine
.addFunctionKind(kind::STORE
);
127 d_equalityEngine
.addFunctionKind(kind::ARR_TABLE_FUN
);
131 TheoryArrays::~TheoryArrays() {
132 vector
<CTNodeList
*>::iterator it
= d_readBucketAllocations
.begin(), iend
= d_readBucketAllocations
.end();
133 for (; it
!= iend
; ++it
) {
136 delete d_readTableContext
;
137 CNodeNListMap::iterator it2
= d_constReads
.begin();
138 for( ; it2
!= d_constReads
.end(); ++it2
) {
139 it2
->second
->deleteSelf();
141 delete d_constReadsContext
;
142 smtStatisticsRegistry()->unregisterStat(&d_numRow
);
143 smtStatisticsRegistry()->unregisterStat(&d_numExt
);
144 smtStatisticsRegistry()->unregisterStat(&d_numProp
);
145 smtStatisticsRegistry()->unregisterStat(&d_numExplain
);
146 smtStatisticsRegistry()->unregisterStat(&d_numNonLinear
);
147 smtStatisticsRegistry()->unregisterStat(&d_numSharedArrayVarSplits
);
148 smtStatisticsRegistry()->unregisterStat(&d_numGetModelValSplits
);
149 smtStatisticsRegistry()->unregisterStat(&d_numGetModelValConflicts
);
150 smtStatisticsRegistry()->unregisterStat(&d_numSetModelValSplits
);
151 smtStatisticsRegistry()->unregisterStat(&d_numSetModelValConflicts
);
154 void TheoryArrays::setMasterEqualityEngine(eq::EqualityEngine
* eq
) {
155 d_equalityEngine
.setMasterEqualityEngine(eq
);
159 /////////////////////////////////////////////////////////////////////////////
161 /////////////////////////////////////////////////////////////////////////////
164 bool TheoryArrays::ppDisequal(TNode a
, TNode b
) {
165 bool termsExist
= d_ppEqualityEngine
.hasTerm(a
) && d_ppEqualityEngine
.hasTerm(b
);
166 Assert(!termsExist
|| !a
.isConst() || !b
.isConst() || a
== b
|| d_ppEqualityEngine
.areDisequal(a
, b
, false));
167 return ((termsExist
&& d_ppEqualityEngine
.areDisequal(a
, b
, false)) ||
168 Rewriter::rewrite(a
.eqNode(b
)) == d_false
);
172 Node
TheoryArrays::solveWrite(TNode term
, bool solve1
, bool solve2
, bool ppCheck
)
177 if (term
[0].getKind() != kind::STORE
&&
178 term
[1].getKind() != kind::STORE
) {
181 TNode left
= term
[0];
182 TNode right
= term
[1];
183 int leftWrites
= 0, rightWrites
= 0;
185 // Count nested writes
187 while (e1
.getKind() == kind::STORE
) {
193 while (e2
.getKind() == kind::STORE
) {
198 if (rightWrites
> leftWrites
) {
202 int tmpWrites
= leftWrites
;
203 leftWrites
= rightWrites
;
204 rightWrites
= tmpWrites
;
207 NodeManager
* nm
= NodeManager::currentNM();
208 if (rightWrites
== 0) {
212 // write(store, index_0, v_0, index_1, v_1, ..., index_n, v_n) = store IFF
214 // read(store, index_n) = v_n &
215 // index_{n-1} != index_n -> read(store, index_{n-1}) = v_{n-1} &
216 // (index_{n-2} != index_{n-1} & index_{n-2} != index_n) -> read(store, index_{n-2}) = v_{n-2} &
218 // (index_1 != index_2 & ... & index_1 != index_n) -> read(store, index_1) = v_1
219 // (index_0 != index_1 & index_0 != index_2 & ... & index_0 != index_n) -> read(store, index_0) = v_0
220 TNode write_i
, write_j
, index_i
, index_j
;
222 NodeBuilder
<> result(kind::AND
);
225 for (i
= leftWrites
-1; i
>= 0; --i
) {
226 index_i
= write_i
[1];
228 // build: [index_i /= index_n && index_i /= index_(n-1) &&
229 // ... && index_i /= index_(i+1)] -> read(store, index_i) = v_i
232 NodeBuilder
<> hyp(kind::AND
);
233 for (j
= leftWrites
- 1; j
> i
; --j
) {
234 index_j
= write_j
[1];
235 if (!ppCheck
|| !ppDisequal(index_i
, index_j
)) {
236 Node
hyp2(index_i
.getType() == nm
->booleanType()?
237 index_i
.iffNode(index_j
) : index_i
.eqNode(index_j
));
238 hyp
<< hyp2
.notNode();
240 write_j
= write_j
[0];
243 Node r1
= nm
->mkNode(kind::SELECT
, e1
, index_i
);
244 conc
= (r1
.getType() == nm
->booleanType())?
245 r1
.iffNode(write_i
[2]) : r1
.eqNode(write_i
[2]);
246 if (hyp
.getNumChildren() != 0) {
247 if (hyp
.getNumChildren() == 1) {
248 conc
= hyp
.getChild(0).impNode(conc
);
252 conc
= r1
.impNode(conc
);
259 // Prepare for next iteration
260 write_i
= write_i
[0];
263 Assert(result
.getNumChildren() > 0);
264 if (result
.getNumChildren() == 1) {
265 return result
.getChild(0);
273 // store(...) = store(a,i,v) ==>
274 // store(store(...),i,select(a,i)) = a && select(store(...),i)=v
277 NodeBuilder
<> nb(kind::AND
);
278 while (right
.getKind() == kind::STORE
) {
279 tmp
= nm
->mkNode(kind::SELECT
, l
, right
[1]);
280 nb
<< tmp
.eqNode(right
[2]);
281 tmp
= nm
->mkNode(kind::SELECT
, right
[0], right
[1]);
282 l
= nm
->mkNode(kind::STORE
, l
, right
[1], tmp
);
285 nb
<< solveWrite(l
.eqNode(right
), solve1
, solve2
, ppCheck
);
293 Node
TheoryArrays::ppRewrite(TNode term
) {
294 if (!d_preprocess
) return term
;
295 d_ppEqualityEngine
.addTerm(term
);
296 switch (term
.getKind()) {
298 // select(store(a,i,v),j) = select(a,j)
300 if (term
[0].getKind() == kind::STORE
&& ppDisequal(term
[0][1], term
[1])) {
301 return NodeBuilder
<2>(kind::SELECT
) << term
[0][0] << term
[1];
306 // store(store(a,i,v),j,w) = store(store(a,j,w),i,v)
307 // IF i != j and j comes before i in the ordering
308 if (term
[0].getKind() == kind::STORE
&& (term
[1] < term
[0][1]) && ppDisequal(term
[1],term
[0][1])) {
309 Node inner
= NodeBuilder
<3>(kind::STORE
) << term
[0][0] << term
[1] << term
[2];
310 Node outer
= NodeBuilder
<3>(kind::STORE
) << inner
<< term
[0][1] << term
[0][2];
316 return solveWrite(term
, d_solveWrite
, d_solveWrite2
, true);
326 Theory::PPAssertStatus
TheoryArrays::ppAssert(TNode in
, SubstitutionMap
& outSubstitutions
) {
327 switch(in
.getKind()) {
330 d_ppFacts
.push_back(in
);
331 d_ppEqualityEngine
.assertEquality(in
, true, in
);
332 if (in
[0].isVar() && !in
[1].hasSubterm(in
[0]) && (in
[1].getType()).isSubtypeOf(in
[0].getType()) ){
333 outSubstitutions
.addSubstitution(in
[0], in
[1]);
334 return PP_ASSERT_STATUS_SOLVED
;
336 if (in
[1].isVar() && !in
[0].hasSubterm(in
[1]) && (in
[0].getType()).isSubtypeOf(in
[1].getType())){
337 outSubstitutions
.addSubstitution(in
[1], in
[0]);
338 return PP_ASSERT_STATUS_SOLVED
;
344 d_ppFacts
.push_back(in
);
345 Assert(in
[0].getKind() == kind::EQUAL
||
346 in
[0].getKind() == kind::IFF
);
349 d_ppEqualityEngine
.assertEquality(in
[0], false, in
);
355 return PP_ASSERT_STATUS_UNSOLVED
;
359 /////////////////////////////////////////////////////////////////////////////
360 // T-PROPAGATION / REGISTRATION
361 /////////////////////////////////////////////////////////////////////////////
364 bool TheoryArrays::propagate(TNode literal
)
366 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::propagate(" << literal
<< ")" << std::endl
;
368 // If already in conflict, no more propagation
370 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::propagate(" << literal
<< "): already in conflict" << std::endl
;
375 if (d_inCheckModel
&& getSatContext()->getLevel() != d_topLevel
) {
378 bool ok
= d_out
->propagate(literal
);
383 }/* TheoryArrays::propagate(TNode) */
386 void TheoryArrays::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
388 bool polarity
= literal
.getKind() != kind::NOT
;
389 TNode atom
= polarity
? literal
: literal
[0];
390 //eq::EqProof * eqp = new eq::EqProof;
391 eq::EqProof
* eqp
= NULL
;
392 if (atom
.getKind() == kind::EQUAL
|| atom
.getKind() == kind::IFF
) {
393 d_equalityEngine
.explainEquality(atom
[0], atom
[1], polarity
, assumptions
, eqp
);
395 d_equalityEngine
.explainPredicate(atom
, polarity
, assumptions
);
398 Debug("array-pf") << " Proof is : " << std::endl
;
399 eqp
->debug_print("array-pf");
403 TNode
TheoryArrays::weakEquivGetRep(TNode node
) {
406 pointer
= d_infoMap
.getWeakEquivPointer(node
);
407 if (pointer
.isNull()) {
414 TNode
TheoryArrays::weakEquivGetRepIndex(TNode node
, TNode index
) {
415 Assert(!index
.isNull());
416 TNode pointer
, index2
;
418 pointer
= d_infoMap
.getWeakEquivPointer(node
);
419 if (pointer
.isNull()) {
422 index2
= d_infoMap
.getWeakEquivIndex(node
);
423 if (index2
.isNull() || !d_equalityEngine
.areEqual(index
, index2
)) {
427 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
428 if (secondary
.isNull()) {
436 void TheoryArrays::visitAllLeaves(TNode reason
, vector
<TNode
>& conjunctions
) {
437 switch (reason
.getKind()) {
439 Assert(reason
.getNumChildren() == 2);
440 visitAllLeaves(reason
[0], conjunctions
);
441 visitAllLeaves(reason
[1], conjunctions
);
444 conjunctions
.push_back(reason
);
447 d_equalityEngine
.explainEquality(reason
[0], reason
[1], true, conjunctions
);
454 void TheoryArrays::weakEquivBuildCond(TNode node
, TNode index
, vector
<TNode
>& conjunctions
) {
455 Assert(!index
.isNull());
456 TNode pointer
, index2
;
458 pointer
= d_infoMap
.getWeakEquivPointer(node
);
459 if (pointer
.isNull()) {
462 index2
= d_infoMap
.getWeakEquivIndex(node
);
463 if (index2
.isNull()) {
464 // Null index means these two nodes became equal: explain the equality.
465 d_equalityEngine
.explainEquality(node
, pointer
, true, conjunctions
);
468 else if (!d_equalityEngine
.areEqual(index
, index2
)) {
469 // If indices are not equal in current context, need to add that to the lemma.
470 Node reason
= index
.eqNode(index2
).notNode();
471 d_permRef
.push_back(reason
);
472 conjunctions
.push_back(reason
);
476 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
477 if (secondary
.isNull()) {
480 TNode reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
481 Assert(!reason
.isNull());
482 visitAllLeaves(reason
, conjunctions
);
488 void TheoryArrays::weakEquivMakeRep(TNode node
) {
489 TNode pointer
= d_infoMap
.getWeakEquivPointer(node
);
490 if (pointer
.isNull()) {
493 weakEquivMakeRep(pointer
);
494 d_infoMap
.setWeakEquivPointer(pointer
, node
);
495 d_infoMap
.setWeakEquivIndex(pointer
, d_infoMap
.getWeakEquivIndex(node
));
496 d_infoMap
.setWeakEquivPointer(node
, TNode());
497 weakEquivMakeRepIndex(node
);
500 void TheoryArrays::weakEquivMakeRepIndex(TNode node
) {
501 TNode secondary
= d_infoMap
.getWeakEquivSecondary(node
);
502 if (secondary
.isNull()) {
505 TNode index
= d_infoMap
.getWeakEquivIndex(node
);
506 Assert(!index
.isNull());
507 TNode index2
= d_infoMap
.getWeakEquivIndex(secondary
);
510 while (index2
.isNull() || !d_equalityEngine
.areEqual(index
, index2
)) {
511 next
= d_infoMap
.getWeakEquivPointer(secondary
);
512 d_infoMap
.setWeakEquivSecondary(node
, next
);
513 reason
= d_infoMap
.getWeakEquivSecondaryReason(node
);
514 if (index2
.isNull()) {
515 reason
= reason
.andNode(secondary
.eqNode(next
));
518 reason
= reason
.andNode(index
.eqNode(index2
).notNode());
520 d_permRef
.push_back(reason
);
521 d_infoMap
.setWeakEquivSecondaryReason(node
, reason
);
526 index2
= d_infoMap
.getWeakEquivIndex(secondary
);
528 weakEquivMakeRepIndex(secondary
);
529 d_infoMap
.setWeakEquivSecondary(secondary
, node
);
530 d_infoMap
.setWeakEquivSecondaryReason(secondary
, d_infoMap
.getWeakEquivSecondaryReason(node
));
531 d_infoMap
.setWeakEquivSecondary(node
, TNode());
532 d_infoMap
.setWeakEquivSecondaryReason(node
, TNode());
535 void TheoryArrays::weakEquivAddSecondary(TNode index
, TNode arrayFrom
, TNode arrayTo
, TNode reason
) {
536 std::hash_set
<TNode
, TNodeHashFunction
> marked
;
537 vector
<TNode
> index_trail
;
538 vector
<TNode
>::iterator it
, iend
;
539 Node equivalence_trail
= reason
;
541 TNode pointer
, indexRep
;
542 if (!index
.isNull()) {
543 index_trail
.push_back(index
);
544 marked
.insert(d_equalityEngine
.getRepresentative(index
));
546 while (arrayFrom
!= arrayTo
) {
547 index
= d_infoMap
.getWeakEquivIndex(arrayFrom
);
548 pointer
= d_infoMap
.getWeakEquivPointer(arrayFrom
);
549 if (!index
.isNull()) {
550 indexRep
= d_equalityEngine
.getRepresentative(index
);
551 if (marked
.find(indexRep
) == marked
.end() && weakEquivGetRepIndex(arrayFrom
, index
) != arrayTo
) {
552 weakEquivMakeRepIndex(arrayFrom
);
553 d_infoMap
.setWeakEquivSecondary(arrayFrom
, arrayTo
);
554 current_reason
= equivalence_trail
;
555 for (it
= index_trail
.begin(), iend
= index_trail
.end(); it
!= iend
; ++it
) {
556 current_reason
= current_reason
.andNode(index
.eqNode(*it
).notNode());
558 d_permRef
.push_back(current_reason
);
559 d_infoMap
.setWeakEquivSecondaryReason(arrayFrom
, current_reason
);
561 marked
.insert(indexRep
);
564 equivalence_trail
= equivalence_trail
.andNode(arrayFrom
.eqNode(pointer
));
570 void TheoryArrays::checkWeakEquiv(bool arraysMerged
) {
571 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(&d_mayEqualEqualityEngine
);
572 for (; !eqcs_i
.isFinished(); ++eqcs_i
) {
573 Node eqc
= (*eqcs_i
);
574 if (!eqc
.getType().isArray()) {
577 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, &d_mayEqualEqualityEngine
);
578 TNode rep
= d_mayEqualEqualityEngine
.getRepresentative(*eqc_i
);
579 TNode weakEquivRep
= weakEquivGetRep(rep
);
580 for (; !eqc_i
.isFinished(); ++eqc_i
) {
582 Assert(!arraysMerged
|| weakEquivGetRep(n
) == weakEquivRep
);
583 TNode pointer
= d_infoMap
.getWeakEquivPointer(n
);
584 TNode index
= d_infoMap
.getWeakEquivIndex(n
);
585 TNode secondary
= d_infoMap
.getWeakEquivSecondary(n
);
586 Assert(pointer
.isNull() == (weakEquivGetRep(n
) == n
));
587 Assert(!pointer
.isNull() || secondary
.isNull());
588 Assert(!index
.isNull() || secondary
.isNull());
589 Assert(d_infoMap
.getWeakEquivSecondaryReason(n
).isNull() || !secondary
.isNull());
590 if (!pointer
.isNull()) {
591 if (index
.isNull()) {
592 Assert(d_equalityEngine
.areEqual(n
, pointer
));
595 Assert((n
.getKind() == kind::STORE
&& n
[0] == pointer
&& n
[1] == index
) ||
596 (pointer
.getKind() == kind::STORE
&& pointer
[0] == n
&& pointer
[1] == index
));
604 * Stores in d_infoMap the following information for each term a of type array:
606 * - all i, such that there exists a term a[i] or a = store(b i v)
607 * (i.e. all indices it is being read atl; store(b i v) is implicitly read at
608 * position i due to the implicit axiom store(b i v)[i] = v )
610 * - all the stores a is congruent to (this information is context dependent)
612 * - all store terms of the form store (a i v) (i.e. in which a appears
613 * directly; this is invariant because no new store terms are created)
615 * Note: completeness depends on having pre-register called on all the input
616 * terms before starting to instantiate lemmas.
618 void TheoryArrays::preRegisterTermInternal(TNode node
)
623 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node
<< ")" << std::endl
;
624 switch (node
.getKind()) {
626 // Add the trigger for equality
627 // NOTE: note that if the equality is true or false already, it might not be added
628 d_equalityEngine
.addTriggerEquality(node
);
631 // Invariant: array terms should be preregistered before being added to the equality engine
632 if (d_equalityEngine
.hasTerm(node
)) {
633 Assert(d_isPreRegistered
.find(node
) != d_isPreRegistered
.end());
637 TNode store
= d_equalityEngine
.getRepresentative(node
[0]);
639 // The may equal needs the store
640 d_mayEqualEqualityEngine
.addTerm(store
);
642 if (options::arraysLazyRIntro1() && !options::arraysWeakEquivalence()) {
643 // Apply RIntro1 rule to any stores equal to store if not done already
644 const CTNodeList
* stores
= d_infoMap
.getStores(store
);
645 CTNodeList::const_iterator it
= stores
->begin();
646 if (it
!= stores
->end()) {
647 NodeManager
* nm
= NodeManager::currentNM();
649 if (!d_infoMap
.rIntro1Applied(s
)) {
650 d_infoMap
.setRIntro1Applied(s
);
651 Assert(s
.getKind()==kind::STORE
);
652 Node ni
= nm
->mkNode(kind::SELECT
, s
, s
[1]);
654 preRegisterTermInternal(ni
);
656 d_equalityEngine
.assertEquality(ni
.eqNode(s
[2]), true, d_true
, eq::MERGED_ARRAYS_ROW1
);
657 Assert(++it
== stores
->end());
662 if (node
.getType().isArray()) {
663 d_mayEqualEqualityEngine
.addTerm(node
);
664 d_equalityEngine
.addTriggerTerm(node
, THEORY_ARRAY
);
667 d_equalityEngine
.addTerm(node
);
669 // Maybe it's a predicate
670 // TODO: remove this or keep it if we allow Boolean elements in arrays.
671 if (node
.getType().isBoolean()) {
672 // Get triggered for both equal and dis-equal
673 d_equalityEngine
.addTriggerPredicate(node
);
676 Assert(d_equalityEngine
.getRepresentative(store
) == store
);
677 d_infoMap
.addIndex(store
, node
[1]);
679 // Synchronize d_constReadsContext with SAT context
680 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
681 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel()) {
682 d_constReadsContext
->push();
685 // Record read in sharing data structure
686 TNode index
= d_equalityEngine
.getRepresentative(node
[1]);
687 if (!options::arraysWeakEquivalence() && index
.isConst()) {
689 CNodeNListMap::iterator it
= d_constReads
.find(index
);
690 if (it
== d_constReads
.end()) {
691 temp
= new(true) CTNodeList(d_constReadsContext
);
692 d_constReads
[index
] = temp
;
697 temp
->push_back(node
);
698 d_constReadsList
.push_back(node
);
701 d_reads
.push_back(node
);
704 Assert((d_isPreRegistered
.insert(node
), true));
705 checkRowForIndex(node
[1], store
);
709 if (d_equalityEngine
.hasTerm(node
)) {
712 d_equalityEngine
.addTriggerTerm(node
, THEORY_ARRAY
);
714 TNode a
= d_equalityEngine
.getRepresentative(node
[0]);
716 if (node
.isConst()) {
717 // Can't use d_mayEqualEqualityEngine to merge node with a because they are both constants,
718 // so just set the default value manually for node.
719 Assert(a
== node
[0]);
720 d_mayEqualEqualityEngine
.addTerm(node
);
721 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
722 Assert(d_mayEqualEqualityEngine
.getRepresentative(a
) == a
);
723 DefValMap::iterator it
= d_defValues
.find(a
);
724 Assert(it
!= d_defValues
.end());
725 d_defValues
[node
] = (*it
).second
;
728 d_mayEqualEqualityEngine
.assertEquality(node
.eqNode(a
), true, d_true
);
729 Assert(d_mayEqualEqualityEngine
.consistent());
732 if (!options::arraysLazyRIntro1() || options::arraysWeakEquivalence()) {
735 NodeManager
* nm
= NodeManager::currentNM();
736 Node ni
= nm
->mkNode(kind::SELECT
, node
, i
);
737 if (!d_equalityEngine
.hasTerm(ni
)) {
738 preRegisterTermInternal(ni
);
741 // Apply RIntro1 Rule
742 d_equalityEngine
.assertEquality(ni
.eqNode(v
), true, d_true
, eq::MERGED_ARRAYS_ROW1
);
745 d_infoMap
.addStore(node
, node
);
746 d_infoMap
.addInStore(a
, node
);
747 d_infoMap
.setModelRep(node
, node
);
749 //Add-Store for Weak Equivalence
750 if (options::arraysWeakEquivalence()) {
751 Assert(weakEquivGetRep(node
[0]) == weakEquivGetRep(a
));
752 Assert(weakEquivGetRep(node
) == node
);
753 d_infoMap
.setWeakEquivPointer(node
, node
[0]);
754 d_infoMap
.setWeakEquivIndex(node
, node
[1]);
755 #ifdef CVC4_ASSERTIONS
756 checkWeakEquiv(false);
763 case kind::STORE_ALL
: {
764 if (d_equalityEngine
.hasTerm(node
)) {
767 ArrayStoreAll storeAll
= node
.getConst
<ArrayStoreAll
>();
768 Node defaultValue
= Node::fromExpr(storeAll
.getExpr());
769 if (!defaultValue
.isConst()) {
770 throw LogicException("Array theory solver does not yet support non-constant default values for arrays");
772 d_infoMap
.setConstArr(node
, node
);
773 d_mayEqualEqualityEngine
.addTerm(node
);
774 Assert(d_mayEqualEqualityEngine
.getRepresentative(node
) == node
);
775 d_equalityEngine
.addTriggerTerm(node
, THEORY_ARRAY
);
776 d_defValues
[node
] = defaultValue
;
781 if (node
.getType().isArray()) {
782 // The may equal needs the node
783 d_mayEqualEqualityEngine
.addTerm(node
);
784 d_equalityEngine
.addTriggerTerm(node
, THEORY_ARRAY
);
785 Assert(d_equalityEngine
.getSize(node
) == 1);
788 d_equalityEngine
.addTerm(node
);
792 // Invariant: preregistered terms are exactly the terms in the equality engine
793 // Disabled, see comment above for kind::EQUAL
794 // Assert(d_equalityEngine.hasTerm(node) || !d_equalityEngine.consistent());
798 void TheoryArrays::preRegisterTerm(TNode node
)
800 preRegisterTermInternal(node
);
804 void TheoryArrays::propagate(Effort e
)
806 // direct propagation now
810 Node
TheoryArrays::explain(TNode literal
)
813 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::explain(" << literal
<< ")" << std::endl
;
814 std::vector
<TNode
> assumptions
;
815 explain(literal
, assumptions
);
816 return mkAnd(assumptions
);
820 /////////////////////////////////////////////////////////////////////////////
822 /////////////////////////////////////////////////////////////////////////////
825 void TheoryArrays::addSharedTerm(TNode t
) {
826 Debug("arrays::sharing") << spaces(getSatContext()->getLevel()) << "TheoryArrays::addSharedTerm(" << t
<< ")" << std::endl
;
827 d_equalityEngine
.addTriggerTerm(t
, THEORY_ARRAY
);
828 if (t
.getType().isArray()) {
829 d_sharedArrays
.insert(t
);
832 #ifdef CVC4_ASSERTIONS
833 d_sharedOther
.insert(t
);
835 d_sharedTerms
= true;
840 EqualityStatus
TheoryArrays::getEqualityStatus(TNode a
, TNode b
) {
841 Assert(d_equalityEngine
.hasTerm(a
) && d_equalityEngine
.hasTerm(b
));
842 if (d_equalityEngine
.areEqual(a
, b
)) {
843 // The terms are implied to be equal
844 return EQUALITY_TRUE
;
846 else if (d_equalityEngine
.areDisequal(a
, b
, false)) {
847 // The terms are implied to be dis-equal
848 return EQUALITY_FALSE
;
850 return EQUALITY_UNKNOWN
;//FALSE_IN_MODEL;
854 void TheoryArrays::checkPair(TNode r1
, TNode r2
)
856 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1
<< " and " << r2
<< std::endl
;
860 Assert(d_equalityEngine
.isTriggerTerm(x
, THEORY_ARRAY
));
862 if (d_equalityEngine
.hasTerm(x
) && d_equalityEngine
.hasTerm(y
) &&
863 (d_equalityEngine
.areEqual(x
,y
) || d_equalityEngine
.areDisequal(x
,y
,false))) {
864 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl
;
868 // If the terms are already known to be equal, we are also in good shape
869 if (d_equalityEngine
.areEqual(r1
, r2
)) {
870 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl
;
874 if (r1
[0] != r2
[0]) {
875 // If arrays are known to be disequal, or cannot become equal, we can continue
876 Assert(d_mayEqualEqualityEngine
.hasTerm(r1
[0]) && d_mayEqualEqualityEngine
.hasTerm(r2
[0]));
877 if (r1
[0].getType() != r2
[0].getType() ||
878 d_equalityEngine
.areDisequal(r1
[0], r2
[0], false)) {
879 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl
;
882 else if (!d_mayEqualEqualityEngine
.areEqual(r1
[0], r2
[0])) {
887 if (!d_equalityEngine
.isTriggerTerm(y
, THEORY_ARRAY
)) {
888 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
892 // Get representative trigger terms
893 TNode x_shared
= d_equalityEngine
.getTriggerTermRepresentative(x
, THEORY_ARRAY
);
894 TNode y_shared
= d_equalityEngine
.getTriggerTermRepresentative(y
, THEORY_ARRAY
);
895 EqualityStatus eqStatusDomain
= d_valuation
.getEqualityStatus(x_shared
, y_shared
);
896 switch (eqStatusDomain
) {
897 case EQUALITY_TRUE_AND_PROPAGATED
:
898 // Should have been propagated to us
902 // Missed propagation - need to add the pair so that theory engine can force propagation
903 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): missed propagation" << std::endl
;
905 case EQUALITY_FALSE_AND_PROPAGATED
:
906 // Should have been propagated to us
909 case EQUALITY_FALSE_IN_MODEL
:
910 // This is unlikely, but I think it could happen
911 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair called when false in model" << std::endl
;
914 // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
919 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl
;
920 addCarePair(x_shared
, y_shared
);
924 void TheoryArrays::computeCareGraph()
926 if (d_sharedArrays
.size() > 0) {
927 CDNodeSet::key_iterator it1
= d_sharedArrays
.key_begin(), it2
, iend
= d_sharedArrays
.key_end();
928 for (; it1
!= iend
; ++it1
) {
929 for (it2
= it1
, ++it2
; it2
!= iend
; ++it2
) {
930 if ((*it1
).getType() != (*it2
).getType()) {
933 EqualityStatus eqStatusArr
= getEqualityStatus((*it1
), (*it2
));
934 if (eqStatusArr
!= EQUALITY_UNKNOWN
) {
937 Assert(d_valuation
.getEqualityStatus((*it1
), (*it2
)) == EQUALITY_UNKNOWN
);
938 addCarePair((*it1
), (*it2
));
939 ++d_numSharedArrayVarSplits
;
945 // Synchronize d_constReadsContext with SAT context
946 Assert(d_constReadsContext
->getLevel() <= getSatContext()->getLevel());
947 while (d_constReadsContext
->getLevel() < getSatContext()->getLevel()) {
948 d_constReadsContext
->push();
951 // Go through the read terms and see if there are any to split on
953 // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
954 // The context is popped at the end. If this loop is interrupted for some reason, we have to make sure the context still
955 // gets popped or the solver will be in an inconsistent state
956 d_constReadsContext
->push();
957 unsigned size
= d_reads
.size();
958 for (unsigned i
= 0; i
< size
; ++ i
) {
959 TNode r1
= d_reads
[i
];
961 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1
<< std::endl
;
962 Assert(d_equalityEngine
.hasTerm(r1
));
965 if (!d_equalityEngine
.isTriggerTerm(x
, THEORY_ARRAY
)) {
966 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl
;
969 Node x_shared
= d_equalityEngine
.getTriggerTermRepresentative(x
, THEORY_ARRAY
);
971 // 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
972 // Also, insert this read in the list at the proper index
974 if (!x_shared
.isConst()) {
975 x_shared
= d_valuation
.getModelValue(x_shared
);
977 if (!x_shared
.isNull()) {
979 CNodeNListMap::iterator it
= d_constReads
.find(x_shared
);
980 if (it
== d_constReads
.end()) {
981 // This is the only x_shared with this model value - no need to create any splits
982 temp
= new(true) CTNodeList(d_constReadsContext
);
983 d_constReads
[x_shared
] = temp
;
987 for (size_t j
= 0; j
< temp
->size(); ++j
) {
988 checkPair(r1
, (*temp
)[j
]);
994 // We don't know the model value for x. Just do brute force examination of all pairs of reads
995 for (unsigned j
= 0; j
< size
; ++j
) {
996 TNode r2
= d_reads
[j
];
997 Assert(d_equalityEngine
.hasTerm(r2
));
1000 for (unsigned j
= 0; j
< d_constReadsList
.size(); ++j
) {
1001 TNode r2
= d_constReadsList
[j
];
1002 Assert(d_equalityEngine
.hasTerm(r2
));
1007 d_constReadsContext
->pop();
1012 /////////////////////////////////////////////////////////////////////////////
1014 /////////////////////////////////////////////////////////////////////////////
1017 void TheoryArrays::collectModelInfo( TheoryModel
* m
, bool fullModel
)
1021 // Compute terms appearing in assertions and shared terms
1022 computeRelevantTerms(termSet
);
1024 // Compute arrays that we need to produce representatives for and also make sure RIntro1 reads are included in the relevant set of reads
1025 NodeManager
* nm
= NodeManager::currentNM();
1026 std::vector
<Node
> arrays
;
1027 bool computeRep
, isArray
;
1028 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(&d_equalityEngine
);
1029 for (; !eqcs_i
.isFinished(); ++eqcs_i
) {
1030 Node eqc
= (*eqcs_i
);
1031 isArray
= eqc
.getType().isArray();
1036 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, &d_equalityEngine
);
1037 for (; !eqc_i
.isFinished(); ++eqc_i
) {
1039 // If this EC is an array type and it contains something other than STORE nodes, we have to compute a representative explicitly
1040 if (isArray
&& termSet
.find(n
) != termSet
.end()) {
1041 if (n
.getKind() == kind::STORE
) {
1042 // Make sure RIntro1 reads are included
1043 Node r
= nm
->mkNode(kind::SELECT
, n
, n
[1]);
1044 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r
<< endl
;
1047 else if (!computeRep
) {
1048 arrays
.push_back(n
);
1055 // Now do a fixed-point iteration to get all reads that need to be included because of RIntro2 rule
1059 eqcs_i
= eq::EqClassesIterator(&d_equalityEngine
);
1060 for (; !eqcs_i
.isFinished(); ++eqcs_i
) {
1061 Node eqc
= (*eqcs_i
);
1062 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, &d_equalityEngine
);
1063 for (; !eqc_i
.isFinished(); ++eqc_i
) {
1065 if (n
.getKind() == kind::SELECT
&& termSet
.find(n
) != termSet
.end()) {
1067 // Find all terms equivalent to n[0] and get corresponding read terms
1068 Node array_eqc
= d_equalityEngine
.getRepresentative(n
[0]);
1069 eq::EqClassIterator array_eqc_i
= eq::EqClassIterator(array_eqc
, &d_equalityEngine
);
1070 for (; !array_eqc_i
.isFinished(); ++array_eqc_i
) {
1071 Node arr
= *array_eqc_i
;
1072 if (arr
.getKind() == kind::STORE
&&
1073 termSet
.find(arr
) != termSet
.end() &&
1074 !d_equalityEngine
.areEqual(arr
[1],n
[1])) {
1075 Node r
= nm
->mkNode(kind::SELECT
, arr
, n
[1]);
1076 if (termSet
.find(r
) == termSet
.end() && d_equalityEngine
.hasTerm(r
)) {
1077 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(a) read: " << r
<< endl
;
1081 r
= nm
->mkNode(kind::SELECT
, arr
[0], n
[1]);
1082 if (termSet
.find(r
) == termSet
.end() && d_equalityEngine
.hasTerm(r
)) {
1083 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(b) read: " << r
<< endl
;
1090 // Find all stores in which n[0] appears and get corresponding read terms
1091 const CTNodeList
* instores
= d_infoMap
.getInStores(array_eqc
);
1093 for(; it
< instores
->size(); ++it
) {
1094 TNode instore
= (*instores
)[it
];
1095 Assert(instore
.getKind()==kind::STORE
);
1096 if (termSet
.find(instore
) != termSet
.end() &&
1097 !d_equalityEngine
.areEqual(instore
[1],n
[1])) {
1098 Node r
= nm
->mkNode(kind::SELECT
, instore
, n
[1]);
1099 if (termSet
.find(r
) == termSet
.end() && d_equalityEngine
.hasTerm(r
)) {
1100 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(c) read: " << r
<< endl
;
1104 r
= nm
->mkNode(kind::SELECT
, instore
[0], n
[1]);
1105 if (termSet
.find(r
) == termSet
.end() && d_equalityEngine
.hasTerm(r
)) {
1106 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(d) read: " << r
<< endl
;
1117 // Send the equality engine information to the model
1118 m
->assertEqualityEngine(&d_equalityEngine
, &termSet
);
1120 // Build a list of all the relevant reads, indexed by the store representative
1121 std::map
<Node
, std::vector
<Node
> > selects
;
1122 set
<Node
>::iterator set_it
= termSet
.begin(), set_it_end
= termSet
.end();
1123 for (; set_it
!= set_it_end
; ++set_it
) {
1125 // If this term is a select, record that the EC rep of its store parameter is being read from using this term
1126 if (n
.getKind() == kind::SELECT
) {
1127 selects
[d_equalityEngine
.getRepresentative(n
[0])].push_back(n
);
1132 DefValMap::iterator it
;
1133 TypeSet defaultValuesSet
;
1135 // Compute all default values already in use
1137 for (size_t i
=0; i
<arrays
.size(); ++i
) {
1138 TNode nrep
= d_equalityEngine
.getRepresentative(arrays
[i
]);
1139 d_mayEqualEqualityEngine
.addTerm(nrep
); // add the term in case it isn't there already
1140 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1141 it
= d_defValues
.find(mayRep
);
1142 if (it
!= d_defValues
.end()) {
1143 defaultValuesSet
.add(nrep
.getType().getArrayConstituentType(), (*it
).second
);
1148 // Loop through all array equivalence classes that need a representative computed
1149 for (size_t i
=0; i
<arrays
.size(); ++i
) {
1150 TNode n
= arrays
[i
];
1151 TNode nrep
= d_equalityEngine
.getRepresentative(n
);
1154 // Compute default value for this array - there is one default value for every mayEqual equivalence class
1155 TNode mayRep
= d_mayEqualEqualityEngine
.getRepresentative(nrep
);
1156 it
= d_defValues
.find(mayRep
);
1157 // If this mayEqual EC doesn't have a default value associated, get the next available default value for the associated array element type
1158 if (it
== d_defValues
.end()) {
1159 TypeNode valueType
= nrep
.getType().getArrayConstituentType();
1160 rep
= defaultValuesSet
.nextTypeEnum(valueType
);
1162 Assert(defaultValuesSet
.getSet(valueType
)->begin() != defaultValuesSet
.getSet(valueType
)->end());
1163 rep
= *(defaultValuesSet
.getSet(valueType
)->begin());
1165 Trace("arrays-models") << "New default value = " << rep
<< endl
;
1166 d_defValues
[mayRep
] = rep
;
1172 // Build the STORE_ALL term with the default value
1173 rep
= nm
->mkConst(ArrayStoreAll(nrep
.getType().toType(), rep
.toExpr()));
1176 std::hash_map
<Node
, Node
, NodeHashFunction
>::iterator it
= d_skolemCache
.find(n
);
1177 if (it
== d_skolemCache
.end()) {
1178 rep
= nm
->mkSkolem("array_collect_model_var", n
.getType(), "base model variable for array collectModelInfo");
1179 d_skolemCache
[n
] = rep
;
1186 // For each read, require that the rep stores the right value
1187 vector
<Node
>& reads
= selects
[nrep
];
1188 for (unsigned j
= 0; j
< reads
.size(); ++j
) {
1189 rep
= nm
->mkNode(kind::STORE
, rep
, reads
[j
][1], reads
[j
]);
1191 m
->assertEquality(n
, rep
, true);
1193 m
->assertRepresentative(rep
);
1198 /////////////////////////////////////////////////////////////////////////////
1200 /////////////////////////////////////////////////////////////////////////////
1203 void TheoryArrays::presolve()
1205 Trace("arrays")<<"Presolving \n";
1209 /////////////////////////////////////////////////////////////////////////////
1211 /////////////////////////////////////////////////////////////////////////////
1214 Node
TheoryArrays::getSkolem(TNode ref
, const string
& name
, const TypeNode
& type
, const string
& comment
, bool makeEqual
)
1217 std::hash_map
<Node
, Node
, NodeHashFunction
>::iterator it
= d_skolemCache
.find(ref
);
1218 if (it
== d_skolemCache
.end()) {
1219 NodeManager
* nm
= NodeManager::currentNM();
1220 skolem
= nm
->mkSkolem(name
, type
, comment
);
1221 d_skolemCache
[ref
] = skolem
;
1224 skolem
= (*it
).second
;
1225 if (d_equalityEngine
.hasTerm(ref
) &&
1226 d_equalityEngine
.hasTerm(skolem
) &&
1227 d_equalityEngine
.areEqual(ref
, skolem
)) {
1231 preRegisterTermInternal(skolem
);
1233 Node d
= skolem
.eqNode(ref
);
1234 Debug("arrays-model-based") << "Asserting skolem equality " << d
<< endl
;
1235 d_equalityEngine
.assertEquality(d
, true, d_true
);
1236 Assert(!d_conflict
);
1237 d_skolemAssertions
.push_back(d
);
1238 d_skolemIndex
= d_skolemIndex
+ 1;
1244 void TheoryArrays::check(Effort e
) {
1245 if (done() && !fullEffort(e
)) {
1249 getOutputChannel().spendResource(options::theoryCheckStep());
1251 TimerStat::CodeTimer
checkTimer(d_checkTime
);
1253 while (!done() && !d_conflict
)
1255 // Get all the assertions
1256 Assertion assertion
= get();
1257 TNode fact
= assertion
.assertion
;
1259 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::check(): processing " << fact
<< std::endl
;
1261 bool polarity
= fact
.getKind() != kind::NOT
;
1262 TNode atom
= polarity
? fact
: fact
[0];
1264 if (!assertion
.isPreregistered
) {
1265 if (atom
.getKind() == kind::EQUAL
) {
1266 if (!d_equalityEngine
.hasTerm(atom
[0])) {
1267 Assert(atom
[0].isConst());
1268 d_equalityEngine
.addTerm(atom
[0]);
1270 if (!d_equalityEngine
.hasTerm(atom
[1])) {
1271 Assert(atom
[1].isConst());
1272 d_equalityEngine
.addTerm(atom
[1]);
1278 switch (fact
.getKind()) {
1280 d_equalityEngine
.assertEquality(fact
, true, fact
);
1283 d_equalityEngine
.assertPredicate(fact
, true, fact
);
1286 if (fact
[0].getKind() == kind::SELECT
) {
1287 d_equalityEngine
.assertPredicate(fact
[0], false, fact
);
1288 } else if (!d_equalityEngine
.areDisequal(fact
[0][0], fact
[0][1], false)) {
1289 // Assert the dis-equality
1290 d_equalityEngine
.assertEquality(fact
[0], false, fact
);
1292 // Apply ArrDiseq Rule if diseq is between arrays
1293 if(fact
[0][0].getType().isArray() && !d_conflict
) {
1294 NodeManager
* nm
= NodeManager::currentNM();
1295 TypeNode indexType
= fact
[0][0].getType()[0];
1296 TNode k
= getSkolem(fact
,"array_ext_index", indexType
, "an extensional lemma index variable from the theory of arrays", false);
1298 Node ak
= nm
->mkNode(kind::SELECT
, fact
[0][0], k
);
1299 Node bk
= nm
->mkNode(kind::SELECT
, fact
[0][1], k
);
1300 Node eq
= ak
.eqNode(bk
);
1301 Node lemma
= fact
[0].orNode(eq
.notNode());
1302 if (options::arraysPropagate() > 0 && d_equalityEngine
.hasTerm(ak
) && d_equalityEngine
.hasTerm(bk
)) {
1303 // Propagate witness disequality - might produce a conflict
1304 d_permRef
.push_back(lemma
);
1305 d_equalityEngine
.assertEquality(eq
, false, lemma
, eq::MERGED_ARRAYS_EXT
);
1308 Trace("arrays-lem")<<"Arrays::addExtLemma " << lemma
<<"\n";
1309 d_out
->lemma(lemma
);
1319 if ((options::arraysEagerLemmas() || fullEffort(e
)) && !d_conflict
&& options::arraysWeakEquivalence()) {
1320 // Replay all array merges to update weak equivalence data structures
1321 context::CDList
<Node
>::iterator it
= d_arrayMerges
.begin(), iend
= d_arrayMerges
.end();
1323 for (; it
!= iend
; ++it
) {
1327 weakEquivMakeRep(b
);
1328 if (weakEquivGetRep(a
) == b
) {
1329 weakEquivAddSecondary(TNode(), a
, b
, eq
);
1332 d_infoMap
.setWeakEquivPointer(b
, a
);
1333 d_infoMap
.setWeakEquivIndex(b
, TNode());
1335 #ifdef CVC4_ASSERTIONS
1336 checkWeakEquiv(false);
1339 #ifdef CVC4_ASSERTIONS
1340 checkWeakEquiv(true);
1343 d_readTableContext
->push();
1345 CTNodeList
* bucketList
= NULL
;
1346 CTNodeList::const_iterator i
= d_reads
.begin(), readsEnd
= d_reads
.end();
1347 for (; i
!= readsEnd
; ++i
) {
1348 const TNode
& r
= *i
;
1350 Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r
<< std::endl
;
1352 // Find the bucket for this read.
1353 mayRep
= d_mayEqualEqualityEngine
.getRepresentative(r
[0]);
1354 iRep
= d_equalityEngine
.getRepresentative(r
[1]);
1355 std::pair
<TNode
, TNode
> key(mayRep
, iRep
);
1356 ReadBucketMap::iterator it
= d_readBucketTable
.find(key
);
1357 if (it
== d_readBucketTable
.end()) {
1358 bucketList
= new(true) CTNodeList(d_readTableContext
);
1359 d_readBucketAllocations
.push_back(bucketList
);
1360 d_readBucketTable
[key
] = bucketList
;
1363 bucketList
= it
->second
;
1365 CTNodeList::const_iterator it2
= bucketList
->begin(), iend
= bucketList
->end();
1366 for (; it2
!= iend
; ++it2
) {
1367 const TNode
& r2
= *it2
;
1368 Assert(r2
.getKind() == kind::SELECT
);
1369 Assert(mayRep
== d_mayEqualEqualityEngine
.getRepresentative(r2
[0]));
1370 Assert(iRep
== d_equalityEngine
.getRepresentative(r2
[1]));
1371 if (d_equalityEngine
.areEqual(r
, r2
)) {
1374 if (weakEquivGetRepIndex(r
[0], r
[1]) == weakEquivGetRepIndex(r2
[0], r
[1])) {
1375 // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1376 vector
<TNode
> conjunctions
;
1377 Assert(d_equalityEngine
.areEqual(r
, Rewriter::rewrite(r
)));
1378 Assert(d_equalityEngine
.areEqual(r2
, Rewriter::rewrite(r2
)));
1379 Node lemma
= Rewriter::rewrite(r
).eqNode(Rewriter::rewrite(r2
)).negate();
1380 d_permRef
.push_back(lemma
);
1381 conjunctions
.push_back(lemma
);
1382 if (r
[1] != r2
[1]) {
1383 d_equalityEngine
.explainEquality(r
[1], r2
[1], true, conjunctions
);
1385 // TODO: get smaller lemmas by eliminating shared parts of path
1386 weakEquivBuildCond(r
[0], r
[1], conjunctions
);
1387 weakEquivBuildCond(r2
[0], r
[1], conjunctions
);
1388 lemma
= mkAnd(conjunctions
, true);
1389 // LSH FIXME: which kind of arrays lemma is this
1390 d_out
->lemma(lemma
, RULE_INVALID
, false, false, true);
1391 d_readTableContext
->pop();
1395 bucketList
->push_back(r
);
1397 d_readTableContext
->pop();
1400 if(!options::arraysEagerLemmas() && fullEffort(e
) && !d_conflict
&& !options::arraysWeakEquivalence()) {
1401 // generate the lemmas on the worklist
1402 Trace("arrays-lem")<<"Arrays::discharging lemmas: "<<d_RowQueue
.size()<<"\n";
1403 while (d_RowQueue
.size() > 0 && !d_conflict
) {
1404 if (dischargeLemmas()) {
1410 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl
;
1414 Node
TheoryArrays::mkAnd(std::vector
<TNode
>& conjunctions
, bool invert
, unsigned startIndex
)
1416 Assert(conjunctions
.size() > 0);
1418 std::set
<TNode
> all
;
1419 std::set
<TNode
> explained
;
1421 unsigned i
= startIndex
;
1423 for (; i
< conjunctions
.size(); ++i
) {
1424 t
= conjunctions
[i
];
1428 else if (t
.getKind() == kind::AND
) {
1429 for(TNode::iterator child_it
= t
.begin(); child_it
!= t
.end(); ++child_it
) {
1430 if (*child_it
== d_true
) {
1433 all
.insert(*child_it
);
1436 else if (t
.getKind() == kind::OR
) {
1437 // Expand explanation resulting from propagating a ROW or EXT lemma
1438 if ((explained
.find(t
) == explained
.end())) {
1439 if (t
[1].getKind() == kind::EQUAL
) {
1441 d_equalityEngine
.explainEquality(t
[1][0], t
[1][1], false, conjunctions
);
1442 explained
.insert(t
);
1445 Assert(t
[1].getKind() == kind::NOT
&& t
[1][0].getKind() == kind::EQUAL
);
1446 Assert(t
[0].getKind() == kind::EQUAL
);
1447 all
.insert(t
[0].notNode());
1448 explained
.insert(t
);
1457 if (all
.size() == 0) {
1458 return invert
? d_false
: d_true
;
1460 if (all
.size() == 1) {
1461 // All the same, or just one
1463 return (*(all
.begin())).negate();
1466 return *(all
.begin());
1470 NodeBuilder
<> conjunction(invert
? kind::OR
: kind::AND
);
1471 std::set
<TNode
>::const_iterator it
= all
.begin();
1472 std::set
<TNode
>::const_iterator it_end
= all
.end();
1473 while (it
!= it_end
) {
1475 conjunction
<< (*it
).negate();
1487 void TheoryArrays::setNonLinear(TNode a
)
1489 if (options::arraysWeakEquivalence()) return;
1490 if (d_infoMap
.isNonLinear(a
)) return;
1492 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a
<< ")\n";
1493 d_infoMap
.setNonLinear(a
);
1496 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1497 const CTNodeList
* st_a
= d_infoMap
.getStores(a
);
1498 const CTNodeList
* inst_a
= d_infoMap
.getInStores(a
);
1502 // Propagate non-linearity down chain of stores
1503 for( ; it
< st_a
->size(); ++it
) {
1504 TNode store
= (*st_a
)[it
];
1505 Assert(store
.getKind()==kind::STORE
);
1506 setNonLinear(store
[0]);
1509 // Instantiate ROW lemmas that were ignored before
1512 for(; it2
< i_a
->size(); ++it2
) {
1513 TNode i
= (*i_a
)[it2
];
1515 for ( ; it
< inst_a
->size(); ++it
) {
1516 TNode store
= (*inst_a
)[it
];
1517 Assert(store
.getKind() == kind::STORE
);
1520 lem
= make_quad(store
, c
, j
, i
);
1521 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1529 * When two array equivalence classes are merged, we may need to apply RIntro1 to a store in one of the EC's
1530 * Here, we check the stores in a to see if any need RIntro1 applied
1531 * We apply RIntro1 whenever:
1532 * (a) a store becomes equal to another store
1533 * (b) a store becomes equal to any term t such that read(t,i) exists
1534 * (c) a store becomes equal to the root array of the store (i.e. store(store(...store(a,i,v)...)) = a)
1536 void TheoryArrays::checkRIntro1(TNode a
, TNode b
)
1538 const CTNodeList
* astores
= d_infoMap
.getStores(a
);
1539 // Apply RIntro1 if applicable
1540 CTNodeList::const_iterator it
= astores
->begin();
1542 if (it
== astores
->end()) {
1543 // No stores in this equivalence class - return
1548 if (it
!= astores
->end()) {
1549 // More than one store: should have already been applied
1550 Assert(d_infoMap
.rIntro1Applied(*it
));
1551 Assert(d_infoMap
.rIntro1Applied(*(--it
)));
1555 // Exactly one store - see if we need to apply RIntro1
1558 Assert(s
.getKind() == kind::STORE
);
1559 if (d_infoMap
.rIntro1Applied(s
)) {
1560 // RIntro1 already applied to s
1564 // Should be no reads from this EC
1565 Assert(d_infoMap
.getIndices(a
)->begin() == d_infoMap
.getIndices(a
)->end());
1568 if (d_infoMap
.getStores(b
)->size() > 0) {
1569 // Case (a): two stores become equal
1573 const CTNodeList
* i_b
= d_infoMap
.getIndices(b
);
1574 if (i_b
->begin() != i_b
->end()) {
1575 // Case (b): there are reads from b
1579 // Get root array of s
1581 while (e1
.getKind() == kind::STORE
) {
1584 Assert(d_equalityEngine
.hasTerm(e1
));
1585 Assert(d_equalityEngine
.hasTerm(b
));
1586 if (d_equalityEngine
.areEqual(e1
, b
)) {
1593 NodeManager
* nm
= NodeManager::currentNM();
1594 d_infoMap
.setRIntro1Applied(s
);
1595 Node ni
= nm
->mkNode(kind::SELECT
, s
, s
[1]);
1596 preRegisterTermInternal(ni
);
1597 d_equalityEngine
.assertEquality(ni
.eqNode(s
[2]), true, d_true
, eq::MERGED_ARRAYS_ROW1
);
1604 void TheoryArrays::mergeArrays(TNode a
, TNode b
)
1606 // Note: a is the new representative
1607 Assert(a
.getType().isArray() && b
.getType().isArray());
1609 if (d_mergeInProgress
) {
1610 // Nested call to mergeArrays, just push on the queue and return
1611 d_mergeQueue
.push(a
.eqNode(b
));
1615 d_mergeInProgress
= true;
1619 Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: " << a
<< "," << b
<< ")\n";
1621 if (options::arraysLazyRIntro1() && !options::arraysWeakEquivalence()) {
1626 if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1627 bool aNL
= d_infoMap
.isNonLinear(a
);
1628 bool bNL
= d_infoMap
.isNonLinear(b
);
1631 // already both marked as non-linear - no need to do anything
1634 // Set b to be non-linear
1640 // Set a to be non-linear
1644 // Check for new non-linear arrays
1645 const CTNodeList
* astores
= d_infoMap
.getStores(a
);
1646 const CTNodeList
* bstores
= d_infoMap
.getStores(b
);
1647 Assert(astores
->size() <= 1 && bstores
->size() <= 1);
1648 if (astores
->size() > 0 && bstores
->size() > 0) {
1656 TNode constArrA
= d_infoMap
.getConstArr(a
);
1657 TNode constArrB
= d_infoMap
.getConstArr(b
);
1658 if (constArrA
.isNull()) {
1659 if (!constArrB
.isNull()) {
1660 d_infoMap
.setConstArr(a
,constArrB
);
1663 else if (!constArrB
.isNull()) {
1664 if (constArrA
!= constArrB
) {
1665 conflict(constArrA
,constArrB
);
1669 // If a and b have different default values associated with their mayequal equivalence classes,
1670 // things get complicated - disallow this for now. -Clark
1671 TNode mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1672 TNode mayRepB
= d_mayEqualEqualityEngine
.getRepresentative(b
);
1674 DefValMap::iterator it
= d_defValues
.find(mayRepA
);
1675 DefValMap::iterator it2
= d_defValues
.find(mayRepB
);
1678 if (it
!= d_defValues
.end()) {
1679 defValue
= (*it
).second
;
1680 if (it2
!= d_defValues
.end() && (defValue
!= (*it2
).second
)) {
1681 throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1684 else if (it2
!= d_defValues
.end()) {
1685 defValue
= (*it2
).second
;
1687 d_mayEqualEqualityEngine
.assertEquality(a
.eqNode(b
), true, d_true
);
1688 Assert(d_mayEqualEqualityEngine
.consistent());
1689 if (!defValue
.isNull()) {
1690 mayRepA
= d_mayEqualEqualityEngine
.getRepresentative(a
);
1691 d_defValues
[mayRepA
] = defValue
;
1694 checkRowLemmas(a
,b
);
1695 checkRowLemmas(b
,a
);
1697 // merge info adds the list of the 2nd argument to the first
1698 d_infoMap
.mergeInfo(a
, b
);
1700 if (options::arraysWeakEquivalence()) {
1701 d_arrayMerges
.push_back(a
.eqNode(b
));
1704 // If no more to do, break
1705 if (d_conflict
|| d_mergeQueue
.empty()) {
1709 // Otherwise, prepare for next iteration
1710 n
= d_mergeQueue
.front();
1715 d_mergeInProgress
= false;
1719 void TheoryArrays::checkStore(TNode a
) {
1720 if (options::arraysWeakEquivalence()) return;
1721 Trace("arrays-cri")<<"Arrays::checkStore "<<a
<<"\n";
1723 if(Trace
.isOn("arrays-cri")) {
1724 d_infoMap
.getInfo(a
)->print();
1726 Assert(a
.getType().isArray());
1727 Assert(a
.getKind()==kind::STORE
);
1731 TNode brep
= d_equalityEngine
.getRepresentative(b
);
1733 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(brep
)) {
1734 const CTNodeList
* js
= d_infoMap
.getIndices(brep
);
1737 for(; it
< js
->size(); ++it
) {
1738 TNode j
= (*js
)[it
];
1739 if (i
== j
) continue;
1740 lem
= make_quad(a
,b
,i
,j
);
1741 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkStore ("<<a
<<", "<<b
<<", "<<i
<<", "<<j
<<")\n";
1748 void TheoryArrays::checkRowForIndex(TNode i
, TNode a
)
1750 if (options::arraysWeakEquivalence()) return;
1751 Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a
<<"\n";
1752 Trace("arrays-cri")<<" index "<<i
<<"\n";
1754 if(Trace
.isOn("arrays-cri")) {
1755 d_infoMap
.getInfo(a
)->print();
1757 Assert(a
.getType().isArray());
1758 Assert(d_equalityEngine
.getRepresentative(a
) == a
);
1760 TNode constArr
= d_infoMap
.getConstArr(a
);
1761 if (!constArr
.isNull()) {
1762 ArrayStoreAll storeAll
= constArr
.getConst
<ArrayStoreAll
>();
1763 Node defValue
= Node::fromExpr(storeAll
.getExpr());
1764 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1765 if (!d_equalityEngine
.hasTerm(selConst
)) {
1766 preRegisterTermInternal(selConst
);
1768 d_equalityEngine
.assertEquality(selConst
.eqNode(defValue
), true, d_true
);
1771 const CTNodeList
* stores
= d_infoMap
.getStores(a
);
1772 const CTNodeList
* instores
= d_infoMap
.getInStores(a
);
1776 for(; it
< stores
->size(); ++it
) {
1777 TNode store
= (*stores
)[it
];
1778 Assert(store
.getKind()==kind::STORE
);
1780 if (i
== j
) continue;
1781 lem
= make_quad(store
, store
[0], j
, i
);
1782 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store
<<", "<<store
[0]<<", "<<j
<<", "<<i
<<")\n";
1786 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(a
)) {
1788 for(; it
< instores
->size(); ++it
) {
1789 TNode instore
= (*instores
)[it
];
1790 Assert(instore
.getKind()==kind::STORE
);
1791 TNode j
= instore
[1];
1792 if (i
== j
) continue;
1793 lem
= make_quad(instore
, instore
[0], j
, i
);
1794 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore
<<", "<<instore
[0]<<", "<<j
<<", "<<i
<<")\n";
1801 // a just became equal to b
1802 // look for new ROW lemmas
1803 void TheoryArrays::checkRowLemmas(TNode a
, TNode b
)
1805 if (options::arraysWeakEquivalence()) return;
1806 Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a
<<"\n";
1807 if(Trace
.isOn("arrays-crl"))
1808 d_infoMap
.getInfo(a
)->print();
1809 Trace("arrays-crl")<<" ------------ and "<<b
<<"\n";
1810 if(Trace
.isOn("arrays-crl"))
1811 d_infoMap
.getInfo(b
)->print();
1813 const CTNodeList
* i_a
= d_infoMap
.getIndices(a
);
1815 TNode constArr
= d_infoMap
.getConstArr(b
);
1816 if (!constArr
.isNull()) {
1817 for( ; it
< i_a
->size(); ++it
) {
1818 TNode i
= (*i_a
)[it
];
1819 Node selConst
= NodeManager::currentNM()->mkNode(kind::SELECT
, constArr
, i
);
1820 if (!d_equalityEngine
.hasTerm(selConst
)) {
1821 preRegisterTermInternal(selConst
);
1826 const CTNodeList
* st_b
= d_infoMap
.getStores(b
);
1827 const CTNodeList
* inst_b
= d_infoMap
.getInStores(b
);
1832 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1833 TNode i
= (*i_a
)[it
];
1835 for ( ; its
< st_b
->size(); ++its
) {
1836 TNode store
= (*st_b
)[its
];
1837 Assert(store
.getKind() == kind::STORE
);
1840 lem
= make_quad(store
, c
, j
, i
);
1841 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1846 if (!options::arraysOptimizeLinear() || d_infoMap
.isNonLinear(b
)) {
1847 for(it
= 0 ; it
< i_a
->size(); ++it
) {
1848 TNode i
= (*i_a
)[it
];
1850 for ( ; its
< inst_b
->size(); ++its
) {
1851 TNode store
= (*inst_b
)[its
];
1852 Assert(store
.getKind() == kind::STORE
);
1855 lem
= make_quad(store
, c
, j
, i
);
1856 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store
<<", "<<c
<<", "<<j
<<", "<<i
<<")\n";
1861 Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1864 void TheoryArrays::propagate(RowLemmaType lem
)
1866 TNode a
= lem
.first
;
1867 TNode b
= lem
.second
;
1868 TNode i
= lem
.third
;
1869 TNode j
= lem
.fourth
;
1871 Assert(a
.getType().isArray() && b
.getType().isArray());
1872 if (d_equalityEngine
.areEqual(a
,b
) ||
1873 d_equalityEngine
.areEqual(i
,j
)) {
1877 NodeManager
* nm
= NodeManager::currentNM();
1878 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1879 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1881 // Try to avoid introducing new read terms: track whether these already exist
1882 bool ajExists
= d_equalityEngine
.hasTerm(aj
);
1883 bool bjExists
= d_equalityEngine
.hasTerm(bj
);
1884 bool bothExist
= ajExists
&& bjExists
;
1886 // If propagating, check propagations
1887 int prop
= options::arraysPropagate();
1889 if (d_equalityEngine
.areDisequal(i
,j
,true) && (bothExist
|| prop
> 1)) {
1890 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj
<<", "<<bj
<<")\n";
1891 Node aj_eq_bj
= aj
.eqNode(bj
);
1892 Node i_eq_j
= i
.eqNode(j
);
1893 Node reason
= nm
->mkNode(kind::OR
, aj_eq_bj
, i_eq_j
);
1894 d_permRef
.push_back(reason
);
1896 preRegisterTermInternal(aj
);
1899 preRegisterTermInternal(bj
);
1901 d_equalityEngine
.assertEquality(aj_eq_bj
, true, reason
, eq::MERGED_ARRAYS_ROW
);
1905 if (bothExist
&& d_equalityEngine
.areDisequal(aj
,bj
,true)) {
1906 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating i = j ("<<i
<<", "<<j
<<")\n";
1907 Node aj_eq_bj
= aj
.eqNode(bj
);
1908 Node i_eq_j
= i
.eqNode(j
);
1909 Node reason
= nm
->mkNode(kind::OR
, i_eq_j
, aj_eq_bj
);
1910 d_permRef
.push_back(reason
);
1911 d_equalityEngine
.assertEquality(i_eq_j
, true, reason
, eq::MERGED_ARRAYS_ROW
);
1918 void TheoryArrays::queueRowLemma(RowLemmaType lem
)
1920 if (d_conflict
|| d_RowAlreadyAdded
.contains(lem
)) {
1923 TNode a
= lem
.first
;
1924 TNode b
= lem
.second
;
1925 TNode i
= lem
.third
;
1926 TNode j
= lem
.fourth
;
1928 Assert(a
.getType().isArray() && b
.getType().isArray());
1929 if (d_equalityEngine
.areEqual(a
,b
) ||
1930 d_equalityEngine
.areEqual(i
,j
)) {
1934 NodeManager
* nm
= NodeManager::currentNM();
1935 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
1936 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
1938 // Try to avoid introducing new read terms: track whether these already exist
1939 bool ajExists
= d_equalityEngine
.hasTerm(aj
);
1940 bool bjExists
= d_equalityEngine
.hasTerm(bj
);
1941 bool bothExist
= ajExists
&& bjExists
;
1943 // If propagating, check propagations
1944 int prop
= options::arraysPropagate();
1949 // If equivalent lemma already exists, don't enqueue this one
1950 if (d_useArrTable
) {
1951 Node tableEntry
= NodeManager::currentNM()->mkNode(kind::ARR_TABLE_FUN
, a
, b
, i
, j
);
1952 if (d_equalityEngine
.getSize(tableEntry
) != 1) {
1957 // Prefer equality between indexes so as not to introduce new read terms
1958 if (options::arraysEagerIndexSplitting() && !bothExist
&& !d_equalityEngine
.areDisequal(i
,j
, false)) {
1959 Node i_eq_j
= d_valuation
.ensureLiteral(i
.eqNode(j
));
1960 getOutputChannel().requirePhase(i_eq_j
, true);
1961 d_decisionRequests
.push(i_eq_j
);
1964 // TODO: maybe add triggers here
1966 if (options::arraysEagerLemmas() || bothExist
) {
1968 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1969 Node aj2
= Rewriter::rewrite(aj
);
1972 preRegisterTermInternal(aj
);
1974 if (!d_equalityEngine
.hasTerm(aj2
)) {
1975 preRegisterTermInternal(aj2
);
1977 d_equalityEngine
.assertEquality(aj
.eqNode(aj2
), true, d_true
);
1979 Node bj2
= Rewriter::rewrite(bj
);
1982 preRegisterTermInternal(bj
);
1984 if (!d_equalityEngine
.hasTerm(bj2
)) {
1985 preRegisterTermInternal(bj2
);
1987 d_equalityEngine
.assertEquality(bj
.eqNode(bj2
), true, d_true
);
1994 Node eq1
= aj2
.eqNode(bj2
);
1995 Node eq1_r
= Rewriter::rewrite(eq1
);
1996 if (eq1_r
== d_true
) {
1997 if (!d_equalityEngine
.hasTerm(aj2
)) {
1998 preRegisterTermInternal(aj2
);
2000 if (!d_equalityEngine
.hasTerm(bj2
)) {
2001 preRegisterTermInternal(bj2
);
2003 d_equalityEngine
.assertEquality(eq1
, true, d_true
);
2007 Node eq2
= i
.eqNode(j
);
2008 Node eq2_r
= Rewriter::rewrite(eq2
);
2009 if (eq2_r
== d_true
) {
2010 d_equalityEngine
.assertEquality(eq2
, true, d_true
);
2014 Node lemma
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
2016 Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lemma
<<"\n";
2017 d_RowAlreadyAdded
.insert(lem
);
2018 d_out
->lemma(lemma
);
2022 d_RowQueue
.push(lem
);
2027 Node
TheoryArrays::getNextDecisionRequest() {
2028 if(! d_decisionRequests
.empty()) {
2029 Node n
= d_decisionRequests
.front();
2030 d_decisionRequests
.pop();
2033 return Node::null();
2038 bool TheoryArrays::dischargeLemmas()
2040 bool lemmasAdded
= false;
2041 size_t sz
= d_RowQueue
.size();
2042 for (unsigned count
= 0; count
< sz
; ++count
) {
2043 RowLemmaType l
= d_RowQueue
.front();
2045 if (d_RowAlreadyAdded
.contains(l
)) {
2053 Assert(a
.getType().isArray() && b
.getType().isArray());
2055 NodeManager
* nm
= NodeManager::currentNM();
2056 Node aj
= nm
->mkNode(kind::SELECT
, a
, j
);
2057 Node bj
= nm
->mkNode(kind::SELECT
, b
, j
);
2058 bool ajExists
= d_equalityEngine
.hasTerm(aj
);
2059 bool bjExists
= d_equalityEngine
.hasTerm(bj
);
2061 // Check for redundant lemma
2062 // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
2063 if (!d_equalityEngine
.hasTerm(i
) || !d_equalityEngine
.hasTerm(j
) || d_equalityEngine
.areEqual(i
,j
) ||
2064 !d_equalityEngine
.hasTerm(a
) || !d_equalityEngine
.hasTerm(b
) || d_equalityEngine
.areEqual(a
,b
) ||
2065 (ajExists
&& bjExists
&& d_equalityEngine
.areEqual(aj
,bj
))) {
2069 int prop
= options::arraysPropagate();
2077 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2078 Node aj2
= Rewriter::rewrite(aj
);
2081 preRegisterTermInternal(aj
);
2083 if (!d_equalityEngine
.hasTerm(aj2
)) {
2084 preRegisterTermInternal(aj2
);
2086 d_equalityEngine
.assertEquality(aj
.eqNode(aj2
), true, d_true
);
2088 Node bj2
= Rewriter::rewrite(bj
);
2091 preRegisterTermInternal(bj
);
2093 if (!d_equalityEngine
.hasTerm(bj2
)) {
2094 preRegisterTermInternal(bj2
);
2096 d_equalityEngine
.assertEquality(bj
.eqNode(bj2
), true, d_true
);
2103 Node eq1
= aj2
.eqNode(bj2
);
2104 Node eq1_r
= Rewriter::rewrite(eq1
);
2105 if (eq1_r
== d_true
) {
2106 if (!d_equalityEngine
.hasTerm(aj2
)) {
2107 preRegisterTermInternal(aj2
);
2109 if (!d_equalityEngine
.hasTerm(bj2
)) {
2110 preRegisterTermInternal(bj2
);
2112 d_equalityEngine
.assertEquality(eq1
, true, d_true
);
2116 Node eq2
= i
.eqNode(j
);
2117 Node eq2_r
= Rewriter::rewrite(eq2
);
2118 if (eq2_r
== d_true
) {
2119 d_equalityEngine
.assertEquality(eq2
, true, d_true
);
2123 Node lem
= nm
->mkNode(kind::OR
, eq2_r
, eq1_r
);
2125 Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lem
<<"\n";
2126 d_RowAlreadyAdded
.insert(l
);
2130 if (options::arraysReduceSharing()) {
2137 void TheoryArrays::conflict(TNode a
, TNode b
) {
2138 if (a
.getKind() == kind::CONST_BOOLEAN
) {
2139 d_conflictNode
= explain(a
.iffNode(b
));
2141 d_conflictNode
= explain(a
.eqNode(b
));
2143 if (!d_inCheckModel
) {
2144 d_out
->conflict(d_conflictNode
);
2150 }/* CVC4::theory::arrays namespace */
2151 }/* CVC4::theory namespace */
2152 }/* CVC4 namespace */