1 /********************* */
2 /*! \file theory_arrays.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Clark Barrett, Morgan Deters, Guy Katz
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8 ** in the top-level source directory) and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
11 **
12 ** \brief Implementation of the theory of arrays.
13 **
14 ** Implementation of the theory of arrays.
15 **/
16
17 #include "theory/arrays/theory_arrays.h"
18
19 #include <map>
20 #include <memory>
21
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/array_proof.h"
27 #include "proof/proof_manager.h"
28 #include "proof/theory_proof.h"
29 #include "smt/command.h"
30 #include "smt/logic_exception.h"
31 #include "smt/smt_statistics_registry.h"
32 #include "theory/rewriter.h"
33 #include "theory/theory_model.h"
34 #include "theory/valuation.h"
35
36 using namespace std;
37
38 namespace CVC4 {
39 namespace theory {
40 namespace arrays {
41
42 // These are the options that produce the best empirical results on QF_AX benchmarks.
43 // eagerLemmas = true
44 // eagerIndexSplitting = false
45
46 // Use static configuration of options for now
47 const bool d_ccStore = false;
48 const bool d_useArrTable = 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_lazyRIntro1 = true;
57 //bool d_eagerIndexSplitting = false;
58
TheoryArrays(context::Context * c,context::UserContext * u,OutputChannel & out,Valuation valuation,const LogicInfo & logicInfo,std::string name)59 TheoryArrays::TheoryArrays(context::Context* c,
60 context::UserContext* u,
61 OutputChannel& out,
62 Valuation valuation,
63 const LogicInfo& logicInfo,
64 std::string name)
65 : Theory(THEORY_ARRAYS, c, u, out, valuation, logicInfo, name),
66 d_numRow(name + "theory::arrays::number of Row lemmas", 0),
67 d_numExt(name + "theory::arrays::number of Ext lemmas", 0),
68 d_numProp(name + "theory::arrays::number of propagations", 0),
69 d_numExplain(name + "theory::arrays::number of explanations", 0),
70 d_numNonLinear(name + "theory::arrays::number of calls to setNonLinear",
71 0),
72 d_numSharedArrayVarSplits(
73 name + "theory::arrays::number of shared array var splits", 0),
74 d_numGetModelValSplits(
75 name + "theory::arrays::number of getModelVal splits", 0),
76 d_numGetModelValConflicts(
77 name + "theory::arrays::number of getModelVal conflicts", 0),
78 d_numSetModelValSplits(
79 name + "theory::arrays::number of setModelVal splits", 0),
80 d_numSetModelValConflicts(
81 name + "theory::arrays::number of setModelVal conflicts", 0),
82 d_ppEqualityEngine(u, name + "theory::arrays::pp", true),
83 d_ppFacts(u),
84 // d_ppCache(u),
85 d_literalsToPropagate(c),
86 d_literalsToPropagateIndex(c, 0),
87 d_isPreRegistered(c),
88 d_mayEqualEqualityEngine(c, name + "theory::arrays::mayEqual", true),
89 d_notify(*this),
90 d_equalityEngine(d_notify, c, name + "theory::arrays", true),
91 d_conflict(c, false),
92 d_backtracker(c),
93 d_infoMap(c, &d_backtracker, name),
94 d_mergeQueue(c),
95 d_mergeInProgress(false),
96 d_RowQueue(c),
97 d_RowAlreadyAdded(u),
98 d_sharedArrays(c),
99 d_sharedOther(c),
100 d_sharedTerms(c, false),
101 d_reads(c),
102 d_constReadsList(c),
103 d_constReadsContext(new context::Context()),
104 d_contextPopper(c, d_constReadsContext),
105 d_skolemIndex(c, 0),
106 d_decisionRequests(c),
107 d_permRef(c),
108 d_modelConstraints(c),
109 d_lemmasSaved(c),
110 d_defValues(c),
111 d_readTableContext(new context::Context()),
112 d_arrayMerges(c),
113 d_inCheckModel(false),
114 d_proofReconstruction(&d_equalityEngine),
115 d_dstrat(new TheoryArraysDecisionStrategy(this))
116 {
117 smtStatisticsRegistry()->registerStat(&d_numRow);
118 smtStatisticsRegistry()->registerStat(&d_numExt);
119 smtStatisticsRegistry()->registerStat(&d_numProp);
120 smtStatisticsRegistry()->registerStat(&d_numExplain);
121 smtStatisticsRegistry()->registerStat(&d_numNonLinear);
122 smtStatisticsRegistry()->registerStat(&d_numSharedArrayVarSplits);
123 smtStatisticsRegistry()->registerStat(&d_numGetModelValSplits);
124 smtStatisticsRegistry()->registerStat(&d_numGetModelValConflicts);
125 smtStatisticsRegistry()->registerStat(&d_numSetModelValSplits);
126 smtStatisticsRegistry()->registerStat(&d_numSetModelValConflicts);
127
128 d_true = NodeManager::currentNM()->mkConst<bool>(true);
129 d_false = NodeManager::currentNM()->mkConst<bool>(false);
130
131 // The preprocessing congruence kinds
132 d_ppEqualityEngine.addFunctionKind(kind::SELECT);
133 d_ppEqualityEngine.addFunctionKind(kind::STORE);
134
135 // The kinds we are treating as function application in congruence
136 d_equalityEngine.addFunctionKind(kind::SELECT);
137 if (d_ccStore) {
138 d_equalityEngine.addFunctionKind(kind::STORE);
139 }
140 if (d_useArrTable) {
141 d_equalityEngine.addFunctionKind(kind::ARR_TABLE_FUN);
142 }
143
144 d_reasonRow = d_equalityEngine.getFreshMergeReasonType();
145 d_reasonRow1 = d_equalityEngine.getFreshMergeReasonType();
146 d_reasonExt = d_equalityEngine.getFreshMergeReasonType();
147
148 d_proofReconstruction.setRowMergeTag(d_reasonRow);
149 d_proofReconstruction.setRow1MergeTag(d_reasonRow1);
150 d_proofReconstruction.setExtMergeTag(d_reasonExt);
151
152 d_equalityEngine.addPathReconstructionTrigger(d_reasonRow, &d_proofReconstruction);
153 d_equalityEngine.addPathReconstructionTrigger(d_reasonRow1, &d_proofReconstruction);
154 d_equalityEngine.addPathReconstructionTrigger(d_reasonExt, &d_proofReconstruction);
155 }
156
~TheoryArrays()157 TheoryArrays::~TheoryArrays() {
158 vector<CTNodeList*>::iterator it = d_readBucketAllocations.begin(), iend = d_readBucketAllocations.end();
159 for (; it != iend; ++it) {
160 (*it)->deleteSelf();
161 }
162 delete d_readTableContext;
163 CNodeNListMap::iterator it2 = d_constReads.begin();
164 for( ; it2 != d_constReads.end(); ++it2 ) {
165 it2->second->deleteSelf();
166 }
167 delete d_constReadsContext;
168 smtStatisticsRegistry()->unregisterStat(&d_numRow);
169 smtStatisticsRegistry()->unregisterStat(&d_numExt);
170 smtStatisticsRegistry()->unregisterStat(&d_numProp);
171 smtStatisticsRegistry()->unregisterStat(&d_numExplain);
172 smtStatisticsRegistry()->unregisterStat(&d_numNonLinear);
173 smtStatisticsRegistry()->unregisterStat(&d_numSharedArrayVarSplits);
174 smtStatisticsRegistry()->unregisterStat(&d_numGetModelValSplits);
175 smtStatisticsRegistry()->unregisterStat(&d_numGetModelValConflicts);
176 smtStatisticsRegistry()->unregisterStat(&d_numSetModelValSplits);
177 smtStatisticsRegistry()->unregisterStat(&d_numSetModelValConflicts);
178 }
179
setMasterEqualityEngine(eq::EqualityEngine * eq)180 void TheoryArrays::setMasterEqualityEngine(eq::EqualityEngine* eq) {
181 d_equalityEngine.setMasterEqualityEngine(eq);
182 }
183
184 /////////////////////////////////////////////////////////////////////////////
185 // PREPROCESSING
186 /////////////////////////////////////////////////////////////////////////////
187
188
ppDisequal(TNode a,TNode b)189 bool TheoryArrays::ppDisequal(TNode a, TNode b) {
190 bool termsExist = d_ppEqualityEngine.hasTerm(a) && d_ppEqualityEngine.hasTerm(b);
191 Assert(!termsExist || !a.isConst() || !b.isConst() || a == b || d_ppEqualityEngine.areDisequal(a, b, false));
192 return ((termsExist && d_ppEqualityEngine.areDisequal(a, b, false)) ||
193 Rewriter::rewrite(a.eqNode(b)) == d_false);
194 }
195
196
solveWrite(TNode term,bool solve1,bool solve2,bool ppCheck)197 Node TheoryArrays::solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck)
198 {
199 if (!solve1) {
200 return term;
201 }
202 if (term[0].getKind() != kind::STORE &&
203 term[1].getKind() != kind::STORE) {
204 return term;
205 }
206 TNode left = term[0];
207 TNode right = term[1];
208 int leftWrites = 0, rightWrites = 0;
209
210 // Count nested writes
211 TNode e1 = left;
212 while (e1.getKind() == kind::STORE) {
213 ++leftWrites;
214 e1 = e1[0];
215 }
216
217 TNode e2 = right;
218 while (e2.getKind() == kind::STORE) {
219 ++rightWrites;
220 e2 = e2[0];
221 }
222
223 if (rightWrites > leftWrites) {
224 TNode tmp = left;
225 left = right;
226 right = tmp;
227 int tmpWrites = leftWrites;
228 leftWrites = rightWrites;
229 rightWrites = tmpWrites;
230 }
231
232 NodeManager* nm = NodeManager::currentNM();
233 if (rightWrites == 0) {
234 if (e1 != e2) {
235 return term;
236 }
237 // write(store, index_0, v_0, index_1, v_1, ..., index_n, v_n) = store IFF
238 //
239 // read(store, index_n) = v_n &
240 // index_{n-1} != index_n -> read(store, index_{n-1}) = v_{n-1} &
241 // (index_{n-2} != index_{n-1} & index_{n-2} != index_n) -> read(store, index_{n-2}) = v_{n-2} &
242 // ...
243 // (index_1 != index_2 & ... & index_1 != index_n) -> read(store, index_1) = v_1
244 // (index_0 != index_1 & index_0 != index_2 & ... & index_0 != index_n) -> read(store, index_0) = v_0
245 TNode write_i, write_j, index_i, index_j;
246 Node conc;
247 NodeBuilder<> result(kind::AND);
248 int i, j;
249 write_i = left;
250 for (i = leftWrites-1; i >= 0; --i) {
251 index_i = write_i[1];
252
253 // build: [index_i /= index_n && index_i /= index_(n-1) &&
254 // ... && index_i /= index_(i+1)] -> read(store, index_i) = v_i
255 write_j = left;
256 {
257 NodeBuilder<> hyp(kind::AND);
258 for (j = leftWrites - 1; j > i; --j) {
259 index_j = write_j[1];
260 if (!ppCheck || !ppDisequal(index_i, index_j)) {
261 Node hyp2(index_i.eqNode(index_j));
262 hyp << hyp2.notNode();
263 }
264 write_j = write_j[0];
265 }
266
267 Node r1 = nm->mkNode(kind::SELECT, e1, index_i);
268 conc = r1.eqNode(write_i[2]);
269 if (hyp.getNumChildren() != 0) {
270 if (hyp.getNumChildren() == 1) {
271 conc = hyp.getChild(0).impNode(conc);
272 }
273 else {
274 r1 = hyp;
275 conc = r1.impNode(conc);
276 }
277 }
278
279 // And into result
280 result << conc;
281
282 // Prepare for next iteration
283 write_i = write_i[0];
284 }
285 }
286 Assert(result.getNumChildren() > 0);
287 if (result.getNumChildren() == 1) {
288 return result.getChild(0);
289 }
290 return result;
291 }
292 else {
293 if (!solve2) {
294 return term;
295 }
296 // store(...) = store(a,i,v) ==>
297 // store(store(...),i,select(a,i)) = a && select(store(...),i)=v
298 Node l = left;
299 Node tmp;
300 NodeBuilder<> nb(kind::AND);
301 while (right.getKind() == kind::STORE) {
302 tmp = nm->mkNode(kind::SELECT, l, right[1]);
303 nb << tmp.eqNode(right[2]);
304 tmp = nm->mkNode(kind::SELECT, right[0], right[1]);
305 l = nm->mkNode(kind::STORE, l, right[1], tmp);
306 right = right[0];
307 }
308 nb << solveWrite(l.eqNode(right), solve1, solve2, ppCheck);
309 return nb;
310 }
311 Unreachable();
312 return term;
313 }
314
315
ppRewrite(TNode term)316 Node TheoryArrays::ppRewrite(TNode term) {
317 if (!d_preprocess) return term;
318 d_ppEqualityEngine.addTerm(term);
319 switch (term.getKind()) {
320 case kind::SELECT: {
321 // select(store(a,i,v),j) = select(a,j)
322 // IF i != j
323 if (term[0].getKind() == kind::STORE && ppDisequal(term[0][1], term[1])) {
324 return NodeBuilder<2>(kind::SELECT) << term[0][0] << term[1];
325 }
326 break;
327 }
328 case kind::STORE: {
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 = NodeBuilder<3>(kind::STORE) << term[0][0] << term[1] << term[2];
333 Node outer = NodeBuilder<3>(kind::STORE) << inner << term[0][1] << term[0][2];
334 return outer;
335 }
336 break;
337 }
338 case kind::EQUAL: {
339 return solveWrite(term, d_solveWrite, d_solveWrite2, true);
340 break;
341 }
342 default:
343 break;
344 }
345 return term;
346 }
347
348
ppAssert(TNode in,SubstitutionMap & outSubstitutions)349 Theory::PPAssertStatus TheoryArrays::ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
350 switch(in.getKind()) {
351 case kind::EQUAL:
352 {
353 d_ppFacts.push_back(in);
354 d_ppEqualityEngine.assertEquality(in, true, in);
355 if (in[0].isVar() && !expr::hasSubterm(in[1], in[0])
356 && (in[1].getType()).isSubtypeOf(in[0].getType()))
357 {
358 outSubstitutions.addSubstitution(in[0], in[1]);
359 return PP_ASSERT_STATUS_SOLVED;
360 }
361 if (in[1].isVar() && !expr::hasSubterm(in[0], in[1])
362 && (in[0].getType()).isSubtypeOf(in[1].getType()))
363 {
364 outSubstitutions.addSubstitution(in[1], in[0]);
365 return PP_ASSERT_STATUS_SOLVED;
366 }
367 break;
368 }
369 case kind::NOT:
370 {
371 d_ppFacts.push_back(in);
372 if (in[0].getKind() == kind::EQUAL ) {
373 Node a = in[0][0];
374 Node b = in[0][1];
375 d_ppEqualityEngine.assertEquality(in[0], false, in);
376 }
377 break;
378 }
379 default:
380 break;
381 }
382 return PP_ASSERT_STATUS_UNSOLVED;
383 }
384
385
386 /////////////////////////////////////////////////////////////////////////////
387 // T-PROPAGATION / REGISTRATION
388 /////////////////////////////////////////////////////////////////////////////
389
390
propagate(TNode literal)391 bool TheoryArrays::propagate(TNode literal)
392 {
393 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::propagate(" << literal << ")" << std::endl;
394
395 // If already in conflict, no more propagation
396 if (d_conflict) {
397 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::propagate(" << literal << "): already in conflict" << std::endl;
398 return false;
399 }
400
401 // Propagate away
402 if (d_inCheckModel && getSatContext()->getLevel() != d_topLevel) {
403 return true;
404 }
405 bool ok = d_out->propagate(literal);
406 if (!ok) {
407 d_conflict = true;
408 }
409 return ok;
410 }/* TheoryArrays::propagate(TNode) */
411
412
explain(TNode literal,std::vector<TNode> & assumptions,eq::EqProof * proof)413 void TheoryArrays::explain(TNode literal, std::vector<TNode>& assumptions,
414 eq::EqProof* proof) {
415 // Do the work
416 bool polarity = literal.getKind() != kind::NOT;
417 TNode atom = polarity ? literal : literal[0];
418 //eq::EqProof * eqp = new eq::EqProof;
419 // eq::EqProof * eqp = NULL;
420 if (atom.getKind() == kind::EQUAL) {
421 d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions, proof);
422 } else {
423 d_equalityEngine.explainPredicate(atom, polarity, assumptions, proof);
424 }
425 if(proof){
426 Debug("pf::array") << " Proof is : " << std::endl;
427 proof->debug_print("pf::array");
428 }
429
430 Debug("pf::array") << "Array: explain( " << literal << " ):" << std::endl << "\t";
431 for (unsigned i = 0; i < assumptions.size(); ++i) {
432 Debug("pf::array") << assumptions[i] << " ";
433 }
434 Debug("pf::array") << std::endl;
435 }
436
weakEquivGetRep(TNode node)437 TNode TheoryArrays::weakEquivGetRep(TNode node) {
438 TNode pointer;
439 while (true) {
440 pointer = d_infoMap.getWeakEquivPointer(node);
441 if (pointer.isNull()) {
442 return node;
443 }
444 node = pointer;
445 }
446 }
447
weakEquivGetRepIndex(TNode node,TNode index)448 TNode TheoryArrays::weakEquivGetRepIndex(TNode node, TNode index) {
449 Assert(!index.isNull());
450 TNode pointer, index2;
451 while (true) {
452 pointer = d_infoMap.getWeakEquivPointer(node);
453 if (pointer.isNull()) {
454 return node;
455 }
456 index2 = d_infoMap.getWeakEquivIndex(node);
457 if (index2.isNull() || !d_equalityEngine.areEqual(index, index2)) {
458 node = pointer;
459 }
460 else {
461 TNode secondary = d_infoMap.getWeakEquivSecondary(node);
462 if (secondary.isNull()) {
463 return node;
464 }
465 node = secondary;
466 }
467 }
468 }
469
visitAllLeaves(TNode reason,vector<TNode> & conjunctions)470 void TheoryArrays::visitAllLeaves(TNode reason, vector<TNode>& conjunctions) {
471 switch (reason.getKind()) {
472 case kind::AND:
473 Assert(reason.getNumChildren() == 2);
474 visitAllLeaves(reason[0], conjunctions);
475 visitAllLeaves(reason[1], conjunctions);
476 break;
477 case kind::NOT:
478 conjunctions.push_back(reason);
479 break;
480 case kind::EQUAL:
481 d_equalityEngine.explainEquality(reason[0], reason[1], true, conjunctions);
482 break;
483 default:
484 Unreachable();
485 }
486 }
487
weakEquivBuildCond(TNode node,TNode index,vector<TNode> & conjunctions)488 void TheoryArrays::weakEquivBuildCond(TNode node, TNode index, vector<TNode>& conjunctions) {
489 Assert(!index.isNull());
490 TNode pointer, index2;
491 while (true) {
492 pointer = d_infoMap.getWeakEquivPointer(node);
493 if (pointer.isNull()) {
494 return;
495 }
496 index2 = d_infoMap.getWeakEquivIndex(node);
497 if (index2.isNull()) {
498 // Null index means these two nodes became equal: explain the equality.
499 d_equalityEngine.explainEquality(node, pointer, true, conjunctions);
500 node = pointer;
501 }
502 else if (!d_equalityEngine.areEqual(index, index2)) {
503 // If indices are not equal in current context, need to add that to the lemma.
504 Node reason = index.eqNode(index2).notNode();
505 d_permRef.push_back(reason);
506 conjunctions.push_back(reason);
507 node = pointer;
508 }
509 else {
510 TNode secondary = d_infoMap.getWeakEquivSecondary(node);
511 if (secondary.isNull()) {
512 return;
513 }
514 TNode reason = d_infoMap.getWeakEquivSecondaryReason(node);
515 Assert(!reason.isNull());
516 visitAllLeaves(reason, conjunctions);
517 node = secondary;
518 }
519 }
520 }
521
weakEquivMakeRep(TNode node)522 void TheoryArrays::weakEquivMakeRep(TNode node) {
523 TNode pointer = d_infoMap.getWeakEquivPointer(node);
524 if (pointer.isNull()) {
525 return;
526 }
527 weakEquivMakeRep(pointer);
528 d_infoMap.setWeakEquivPointer(pointer, node);
529 d_infoMap.setWeakEquivIndex(pointer, d_infoMap.getWeakEquivIndex(node));
530 d_infoMap.setWeakEquivPointer(node, TNode());
531 weakEquivMakeRepIndex(node);
532 }
533
weakEquivMakeRepIndex(TNode node)534 void TheoryArrays::weakEquivMakeRepIndex(TNode node) {
535 TNode secondary = d_infoMap.getWeakEquivSecondary(node);
536 if (secondary.isNull()) {
537 return;
538 }
539 TNode index = d_infoMap.getWeakEquivIndex(node);
540 Assert(!index.isNull());
541 TNode index2 = d_infoMap.getWeakEquivIndex(secondary);
542 Node reason;
543 TNode next;
544 while (index2.isNull() || !d_equalityEngine.areEqual(index, index2)) {
545 next = d_infoMap.getWeakEquivPointer(secondary);
546 d_infoMap.setWeakEquivSecondary(node, next);
547 reason = d_infoMap.getWeakEquivSecondaryReason(node);
548 if (index2.isNull()) {
549 reason = reason.andNode(secondary.eqNode(next));
550 }
551 else {
552 reason = reason.andNode(index.eqNode(index2).notNode());
553 }
554 d_permRef.push_back(reason);
555 d_infoMap.setWeakEquivSecondaryReason(node, reason);
556 if (next.isNull()) {
557 return;
558 }
559 secondary = next;
560 index2 = d_infoMap.getWeakEquivIndex(secondary);
561 }
562 weakEquivMakeRepIndex(secondary);
563 d_infoMap.setWeakEquivSecondary(secondary, node);
564 d_infoMap.setWeakEquivSecondaryReason(secondary, d_infoMap.getWeakEquivSecondaryReason(node));
565 d_infoMap.setWeakEquivSecondary(node, TNode());
566 d_infoMap.setWeakEquivSecondaryReason(node, TNode());
567 }
568
weakEquivAddSecondary(TNode index,TNode arrayFrom,TNode arrayTo,TNode reason)569 void TheoryArrays::weakEquivAddSecondary(TNode index, TNode arrayFrom, TNode arrayTo, TNode reason) {
570 std::unordered_set<TNode, TNodeHashFunction> marked;
571 vector<TNode> index_trail;
572 vector<TNode>::iterator it, iend;
573 Node equivalence_trail = reason;
574 Node current_reason;
575 TNode pointer, indexRep;
576 if (!index.isNull()) {
577 index_trail.push_back(index);
578 marked.insert(d_equalityEngine.getRepresentative(index));
579 }
580 while (arrayFrom != arrayTo) {
581 index = d_infoMap.getWeakEquivIndex(arrayFrom);
582 pointer = d_infoMap.getWeakEquivPointer(arrayFrom);
583 if (!index.isNull()) {
584 indexRep = d_equalityEngine.getRepresentative(index);
585 if (marked.find(indexRep) == marked.end() && weakEquivGetRepIndex(arrayFrom, index) != arrayTo) {
586 weakEquivMakeRepIndex(arrayFrom);
587 d_infoMap.setWeakEquivSecondary(arrayFrom, arrayTo);
588 current_reason = equivalence_trail;
589 for (it = index_trail.begin(), iend = index_trail.end(); it != iend; ++it) {
590 current_reason = current_reason.andNode(index.eqNode(*it).notNode());
591 }
592 d_permRef.push_back(current_reason);
593 d_infoMap.setWeakEquivSecondaryReason(arrayFrom, current_reason);
594 }
595 marked.insert(indexRep);
596 }
597 else {
598 equivalence_trail = equivalence_trail.andNode(arrayFrom.eqNode(pointer));
599 }
600 arrayFrom = pointer;
601 }
602 }
603
checkWeakEquiv(bool arraysMerged)604 void TheoryArrays::checkWeakEquiv(bool arraysMerged) {
605 eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_mayEqualEqualityEngine);
606 for (; !eqcs_i.isFinished(); ++eqcs_i) {
607 Node eqc = (*eqcs_i);
608 if (!eqc.getType().isArray()) {
609 continue;
610 }
611 eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &d_mayEqualEqualityEngine);
612 TNode rep = d_mayEqualEqualityEngine.getRepresentative(*eqc_i);
613 TNode weakEquivRep = weakEquivGetRep(rep);
614 for (; !eqc_i.isFinished(); ++eqc_i) {
615 TNode n = *eqc_i;
616 Assert(!arraysMerged || weakEquivGetRep(n) == weakEquivRep);
617 TNode pointer = d_infoMap.getWeakEquivPointer(n);
618 TNode index = d_infoMap.getWeakEquivIndex(n);
619 TNode secondary = d_infoMap.getWeakEquivSecondary(n);
620 Assert(pointer.isNull() == (weakEquivGetRep(n) == n));
621 Assert(!pointer.isNull() || secondary.isNull());
622 Assert(!index.isNull() || secondary.isNull());
623 Assert(d_infoMap.getWeakEquivSecondaryReason(n).isNull() || !secondary.isNull());
624 if (!pointer.isNull()) {
625 if (index.isNull()) {
626 Assert(d_equalityEngine.areEqual(n, pointer));
627 }
628 else {
629 Assert((n.getKind() == kind::STORE && n[0] == pointer && n[1] == index) ||
630 (pointer.getKind() == kind::STORE && pointer[0] == n && pointer[1] == index));
631 }
632 }
633 }
634 }
635 }
636
637 /**
638 * Stores in d_infoMap the following information for each term a of type array:
639 *
640 * - all i, such that there exists a term a[i] or a = store(b i v)
641 * (i.e. all indices it is being read atl; store(b i v) is implicitly read at
642 * position i due to the implicit axiom store(b i v)[i] = v )
643 *
644 * - all the stores a is congruent to (this information is context dependent)
645 *
646 * - all store terms of the form store (a i v) (i.e. in which a appears
647 * directly; this is invariant because no new store terms are created)
648 *
649 * Note: completeness depends on having pre-register called on all the input
650 * terms before starting to instantiate lemmas.
651 */
preRegisterTermInternal(TNode node)652 void TheoryArrays::preRegisterTermInternal(TNode node)
653 {
654 if (d_conflict) {
655 return;
656 }
657 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node << ")" << std::endl;
658 switch (node.getKind()) {
659 case kind::EQUAL:
660 // Add the trigger for equality
661 // NOTE: note that if the equality is true or false already, it might not be added
662 d_equalityEngine.addTriggerEquality(node);
663 break;
664 case kind::SELECT: {
665 // Invariant: array terms should be preregistered before being added to the equality engine
666 if (d_equalityEngine.hasTerm(node)) {
667 Assert(d_isPreRegistered.find(node) != d_isPreRegistered.end());
668 return;
669 }
670 // Reads
671 TNode store = d_equalityEngine.getRepresentative(node[0]);
672
673 // The may equal needs the store
674 d_mayEqualEqualityEngine.addTerm(store);
675
676 if (node.getType().isArray())
677 {
678 d_mayEqualEqualityEngine.addTerm(node);
679 d_equalityEngine.addTriggerTerm(node, THEORY_ARRAYS);
680 }
681 else
682 {
683 d_equalityEngine.addTerm(node);
684 }
685 Assert((d_isPreRegistered.insert(node), true));
686
687 if (options::arraysLazyRIntro1() && !options::arraysWeakEquivalence()) {
688 // Apply RIntro1 rule to any stores equal to store if not done already
689 const CTNodeList* stores = d_infoMap.getStores(store);
690 CTNodeList::const_iterator it = stores->begin();
691 if (it != stores->end()) {
692 NodeManager* nm = NodeManager::currentNM();
693 TNode s = *it;
694 if (!d_infoMap.rIntro1Applied(s)) {
695 d_infoMap.setRIntro1Applied(s);
696 Assert(s.getKind()==kind::STORE);
697 Node ni = nm->mkNode(kind::SELECT, s, s[1]);
698 if (ni != node) {
699 preRegisterTermInternal(ni);
700 }
701 d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true, d_reasonRow1);
702 Assert(++it == stores->end());
703 }
704 }
705 }
706
707 Assert(d_equalityEngine.getRepresentative(store) == store);
708 d_infoMap.addIndex(store, node[1]);
709
710 // Synchronize d_constReadsContext with SAT context
711 Assert(d_constReadsContext->getLevel() <= getSatContext()->getLevel());
712 while (d_constReadsContext->getLevel() < getSatContext()->getLevel()) {
713 d_constReadsContext->push();
714 }
715
716 // Record read in sharing data structure
717 TNode index = d_equalityEngine.getRepresentative(node[1]);
718 if (!options::arraysWeakEquivalence() && index.isConst()) {
719 CTNodeList* temp;
720 CNodeNListMap::iterator it = d_constReads.find(index);
721 if (it == d_constReads.end()) {
722 temp = new(true) CTNodeList(d_constReadsContext);
723 d_constReads[index] = temp;
724 }
725 else {
726 temp = (*it).second;
727 }
728 temp->push_back(node);
729 d_constReadsList.push_back(node);
730 }
731 else {
732 d_reads.push_back(node);
733 }
734
735 checkRowForIndex(node[1], store);
736 break;
737 }
738 case kind::STORE: {
739 if (d_equalityEngine.hasTerm(node)) {
740 break;
741 }
742 d_equalityEngine.addTriggerTerm(node, THEORY_ARRAYS);
743
744 TNode a = d_equalityEngine.getRepresentative(node[0]);
745
746 if (node.isConst()) {
747 // Can't use d_mayEqualEqualityEngine to merge node with a because they are both constants,
748 // so just set the default value manually for node.
749 Assert(a == node[0]);
750 d_mayEqualEqualityEngine.addTerm(node);
751 Assert(d_mayEqualEqualityEngine.getRepresentative(node) == node);
752 Assert(d_mayEqualEqualityEngine.getRepresentative(a) == a);
753 DefValMap::iterator it = d_defValues.find(a);
754 Assert(it != d_defValues.end());
755 d_defValues[node] = (*it).second;
756 }
757 else {
758 d_mayEqualEqualityEngine.assertEquality(node.eqNode(a), true, d_true);
759 Assert(d_mayEqualEqualityEngine.consistent());
760 }
761
762 if (!options::arraysLazyRIntro1() || options::arraysWeakEquivalence()) {
763 TNode i = node[1];
764 TNode v = node[2];
765 NodeManager* nm = NodeManager::currentNM();
766 Node ni = nm->mkNode(kind::SELECT, node, i);
767 if (!d_equalityEngine.hasTerm(ni)) {
768 preRegisterTermInternal(ni);
769 }
770
771 // Apply RIntro1 Rule
772 d_equalityEngine.assertEquality(ni.eqNode(v), true, d_true, d_reasonRow1);
773 }
774
775 d_infoMap.addStore(node, node);
776 d_infoMap.addInStore(a, node);
777 d_infoMap.setModelRep(node, node);
778
779 //Add-Store for Weak Equivalence
780 if (options::arraysWeakEquivalence()) {
781 Assert(weakEquivGetRep(node[0]) == weakEquivGetRep(a));
782 Assert(weakEquivGetRep(node) == node);
783 d_infoMap.setWeakEquivPointer(node, node[0]);
784 d_infoMap.setWeakEquivIndex(node, node[1]);
785 #ifdef CVC4_ASSERTIONS
786 checkWeakEquiv(false);
787 #endif
788 }
789
790 checkStore(node);
791 break;
792 }
793 case kind::STORE_ALL: {
794 if (d_equalityEngine.hasTerm(node)) {
795 break;
796 }
797 ArrayStoreAll storeAll = node.getConst<ArrayStoreAll>();
798 Node defaultValue = Node::fromExpr(storeAll.getExpr());
799 if (!defaultValue.isConst()) {
800 throw LogicException("Array theory solver does not yet support non-constant default values for arrays");
801 }
802 d_infoMap.setConstArr(node, node);
803 d_mayEqualEqualityEngine.addTerm(node);
804 Assert(d_mayEqualEqualityEngine.getRepresentative(node) == node);
805 d_equalityEngine.addTriggerTerm(node, THEORY_ARRAYS);
806 d_defValues[node] = defaultValue;
807 break;
808 }
809 default:
810 // Variables etc
811 if (node.getType().isArray()) {
812 // The may equal needs the node
813 d_mayEqualEqualityEngine.addTerm(node);
814 d_equalityEngine.addTriggerTerm(node, THEORY_ARRAYS);
815 Assert(d_equalityEngine.getSize(node) == 1);
816 }
817 else {
818 d_equalityEngine.addTerm(node);
819 }
820
821 break;
822 }
823 // Invariant: preregistered terms are exactly the terms in the equality engine
824 // Disabled, see comment above for kind::EQUAL
825 // Assert(d_equalityEngine.hasTerm(node) || !d_equalityEngine.consistent());
826 }
827
828
preRegisterTerm(TNode node)829 void TheoryArrays::preRegisterTerm(TNode node)
830 {
831 preRegisterTermInternal(node);
832 // If we have a select from an array of Bools, mark it as a term that can be propagated.
833 // Note: do this here instead of in preRegisterTermInternal to prevent internal select
834 // terms from being propagated out (as this results in an assertion failure).
835 if (node.getKind() == kind::SELECT && node.getType().isBoolean()) {
836 d_equalityEngine.addTriggerPredicate(node);
837 }
838 }
839
840
propagate(Effort e)841 void TheoryArrays::propagate(Effort e)
842 {
843 // direct propagation now
844 }
845
846
explain(TNode literal)847 Node TheoryArrays::explain(TNode literal) {
848 Node explanation = explain(literal, NULL);
849 return explanation;
850 }
851
explain(TNode literal,eq::EqProof * proof)852 Node TheoryArrays::explain(TNode literal, eq::EqProof* proof) {
853 ++d_numExplain;
854 Debug("arrays") << spaces(getSatContext()->getLevel())
855 << "TheoryArrays::explain(" << literal << ")" << std::endl;
856 std::vector<TNode> assumptions;
857 explain(literal, assumptions, proof);
858 return mkAnd(assumptions);
859 }
860
861 /////////////////////////////////////////////////////////////////////////////
862 // SHARING
863 /////////////////////////////////////////////////////////////////////////////
864
865
addSharedTerm(TNode t)866 void TheoryArrays::addSharedTerm(TNode t) {
867 Debug("arrays::sharing") << spaces(getSatContext()->getLevel()) << "TheoryArrays::addSharedTerm(" << t << ")" << std::endl;
868 d_equalityEngine.addTriggerTerm(t, THEORY_ARRAYS);
869 if (t.getType().isArray()) {
870 d_sharedArrays.insert(t);
871 }
872 else {
873 #ifdef CVC4_ASSERTIONS
874 d_sharedOther.insert(t);
875 #endif
876 d_sharedTerms = true;
877 }
878 }
879
880
getEqualityStatus(TNode a,TNode b)881 EqualityStatus TheoryArrays::getEqualityStatus(TNode a, TNode b) {
882 Assert(d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b));
883 if (d_equalityEngine.areEqual(a, b)) {
884 // The terms are implied to be equal
885 return EQUALITY_TRUE;
886 }
887 else if (d_equalityEngine.areDisequal(a, b, false)) {
888 // The terms are implied to be dis-equal
889 return EQUALITY_FALSE;
890 }
891 return EQUALITY_UNKNOWN;//FALSE_IN_MODEL;
892 }
893
894
checkPair(TNode r1,TNode r2)895 void TheoryArrays::checkPair(TNode r1, TNode r2)
896 {
897 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1 << " and " << r2 << std::endl;
898
899 TNode x = r1[1];
900 TNode y = r2[1];
901 Assert(d_equalityEngine.isTriggerTerm(x, THEORY_ARRAYS));
902
903 if (d_equalityEngine.hasTerm(x) && d_equalityEngine.hasTerm(y) &&
904 (d_equalityEngine.areEqual(x,y) || d_equalityEngine.areDisequal(x,y,false))) {
905 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl;
906 return;
907 }
908
909 // If the terms are already known to be equal, we are also in good shape
910 if (d_equalityEngine.areEqual(r1, r2)) {
911 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl;
912 return;
913 }
914
915 if (r1[0] != r2[0]) {
916 // If arrays are known to be disequal, or cannot become equal, we can continue
917 Assert(d_mayEqualEqualityEngine.hasTerm(r1[0]) && d_mayEqualEqualityEngine.hasTerm(r2[0]));
918 if (r1[0].getType() != r2[0].getType() ||
919 d_equalityEngine.areDisequal(r1[0], r2[0], false)) {
920 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl;
921 return;
922 }
923 else if (!d_mayEqualEqualityEngine.areEqual(r1[0], r2[0])) {
924 return;
925 }
926 }
927
928 if (!d_equalityEngine.isTriggerTerm(y, THEORY_ARRAYS)) {
929 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
930 return;
931 }
932
933 // Get representative trigger terms
934 TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_ARRAYS);
935 TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_ARRAYS);
936 EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared);
937 switch (eqStatusDomain) {
938 case EQUALITY_TRUE_AND_PROPAGATED:
939 // Should have been propagated to us
940 Assert(false);
941 break;
942 case EQUALITY_TRUE:
943 // Missed propagation - need to add the pair so that theory engine can force propagation
944 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): missed propagation" << std::endl;
945 break;
946 case EQUALITY_FALSE_AND_PROPAGATED:
947 // Should have been propagated to us
948 Assert(false);
949 case EQUALITY_FALSE:
950 case EQUALITY_FALSE_IN_MODEL:
951 // This is unlikely, but I think it could happen
952 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair called when false in model" << std::endl;
953 return;
954 default:
955 // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
956 break;
957 }
958
959 // Add this pair
960 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl;
961 addCarePair(x_shared, y_shared);
962 }
963
964
computeCareGraph()965 void TheoryArrays::computeCareGraph()
966 {
967 if (d_sharedArrays.size() > 0) {
968 CDNodeSet::key_iterator it1 = d_sharedArrays.key_begin(), it2, iend = d_sharedArrays.key_end();
969 for (; it1 != iend; ++it1) {
970 for (it2 = it1, ++it2; it2 != iend; ++it2) {
971 if ((*it1).getType() != (*it2).getType()) {
972 continue;
973 }
974 EqualityStatus eqStatusArr = getEqualityStatus((*it1), (*it2));
975 if (eqStatusArr != EQUALITY_UNKNOWN) {
976 continue;
977 }
978 Assert(d_valuation.getEqualityStatus((*it1), (*it2)) == EQUALITY_UNKNOWN);
979 addCarePair((*it1), (*it2));
980 ++d_numSharedArrayVarSplits;
981 return;
982 }
983 }
984 }
985 if (d_sharedTerms) {
986 // Synchronize d_constReadsContext with SAT context
987 Assert(d_constReadsContext->getLevel() <= getSatContext()->getLevel());
988 while (d_constReadsContext->getLevel() < getSatContext()->getLevel()) {
989 d_constReadsContext->push();
990 }
991
992 // Go through the read terms and see if there are any to split on
993
994 // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
995 // The context is popped at the end. If this loop is interrupted for some reason, we have to make sure the context still
996 // gets popped or the solver will be in an inconsistent state
997 d_constReadsContext->push();
998 unsigned size = d_reads.size();
999 for (unsigned i = 0; i < size; ++ i) {
1000 TNode r1 = d_reads[i];
1001
1002 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1 << std::endl;
1003 Assert(d_equalityEngine.hasTerm(r1));
1004 TNode x = r1[1];
1005
1006 if (!d_equalityEngine.isTriggerTerm(x, THEORY_ARRAYS)) {
1007 Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
1008 continue;
1009 }
1010 Node x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_ARRAYS);
1011
1012 // 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
1013 // Also, insert this read in the list at the proper index
1014
1015 if (!x_shared.isConst()) {
1016 x_shared = d_valuation.getModelValue(x_shared);
1017 }
1018 if (!x_shared.isNull()) {
1019 CTNodeList* temp;
1020 CNodeNListMap::iterator it = d_constReads.find(x_shared);
1021 if (it == d_constReads.end()) {
1022 // This is the only x_shared with this model value - no need to create any splits
1023 temp = new(true) CTNodeList(d_constReadsContext);
1024 d_constReads[x_shared] = temp;
1025 }
1026 else {
1027 temp = (*it).second;
1028 for (size_t j = 0; j < temp->size(); ++j) {
1029 checkPair(r1, (*temp)[j]);
1030 }
1031 }
1032 temp->push_back(r1);
1033 }
1034 else {
1035 // We don't know the model value for x. Just do brute force examination of all pairs of reads
1036 for (unsigned j = 0; j < size; ++j) {
1037 TNode r2 = d_reads[j];
1038 Assert(d_equalityEngine.hasTerm(r2));
1039 checkPair(r1,r2);
1040 }
1041 for (unsigned j = 0; j < d_constReadsList.size(); ++j) {
1042 TNode r2 = d_constReadsList[j];
1043 Assert(d_equalityEngine.hasTerm(r2));
1044 checkPair(r1,r2);
1045 }
1046 }
1047 }
1048 d_constReadsContext->pop();
1049 }
1050 }
1051
1052
1053 /////////////////////////////////////////////////////////////////////////////
1054 // MODEL GENERATION
1055 /////////////////////////////////////////////////////////////////////////////
1056
collectModelInfo(TheoryModel * m)1057 bool TheoryArrays::collectModelInfo(TheoryModel* m)
1058 {
1059 set<Node> termSet;
1060
1061 // Compute terms appearing in assertions and shared terms
1062 computeRelevantTerms(termSet);
1063
1064 // Compute arrays that we need to produce representatives for and also make sure RIntro1 reads are included in the relevant set of reads
1065 NodeManager* nm = NodeManager::currentNM();
1066 std::vector<Node> arrays;
1067 bool computeRep, isArray;
1068 eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine);
1069 for (; !eqcs_i.isFinished(); ++eqcs_i) {
1070 Node eqc = (*eqcs_i);
1071 isArray = eqc.getType().isArray();
1072 if (!isArray) {
1073 continue;
1074 }
1075 computeRep = false;
1076 eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &d_equalityEngine);
1077 for (; !eqc_i.isFinished(); ++eqc_i) {
1078 Node n = *eqc_i;
1079 // If this EC is an array type and it contains something other than STORE nodes, we have to compute a representative explicitly
1080 if (isArray && termSet.find(n) != termSet.end()) {
1081 if (n.getKind() == kind::STORE) {
1082 // Make sure RIntro1 reads are included
1083 Node r = nm->mkNode(kind::SELECT, n, n[1]);
1084 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r << endl;
1085 termSet.insert(r);
1086 }
1087 else if (!computeRep) {
1088 arrays.push_back(n);
1089 computeRep = true;
1090 }
1091 }
1092 }
1093 }
1094
1095 // Now do a fixed-point iteration to get all reads that need to be included because of RIntro2 rule
1096 bool changed;
1097 do {
1098 changed = false;
1099 eqcs_i = eq::EqClassesIterator(&d_equalityEngine);
1100 for (; !eqcs_i.isFinished(); ++eqcs_i) {
1101 Node eqc = (*eqcs_i);
1102 eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &d_equalityEngine);
1103 for (; !eqc_i.isFinished(); ++eqc_i) {
1104 Node n = *eqc_i;
1105 if (n.getKind() == kind::SELECT && termSet.find(n) != termSet.end()) {
1106
1107 // Find all terms equivalent to n[0] and get corresponding read terms
1108 Node array_eqc = d_equalityEngine.getRepresentative(n[0]);
1109 eq::EqClassIterator array_eqc_i = eq::EqClassIterator(array_eqc, &d_equalityEngine);
1110 for (; !array_eqc_i.isFinished(); ++array_eqc_i) {
1111 Node arr = *array_eqc_i;
1112 if (arr.getKind() == kind::STORE &&
1113 termSet.find(arr) != termSet.end() &&
1114 !d_equalityEngine.areEqual(arr[1],n[1])) {
1115 Node r = nm->mkNode(kind::SELECT, arr, n[1]);
1116 if (termSet.find(r) == termSet.end() && d_equalityEngine.hasTerm(r)) {
1117 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(a) read: " << r << endl;
1118 termSet.insert(r);
1119 changed = true;
1120 }
1121 r = nm->mkNode(kind::SELECT, arr[0], n[1]);
1122 if (termSet.find(r) == termSet.end() && d_equalityEngine.hasTerm(r)) {
1123 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(b) read: " << r << endl;
1124 termSet.insert(r);
1125 changed = true;
1126 }
1127 }
1128 }
1129
1130 // Find all stores in which n[0] appears and get corresponding read terms
1131 const CTNodeList* instores = d_infoMap.getInStores(array_eqc);
1132 size_t it = 0;
1133 for(; it < instores->size(); ++it) {
1134 TNode instore = (*instores)[it];
1135 Assert(instore.getKind()==kind::STORE);
1136 if (termSet.find(instore) != termSet.end() &&
1137 !d_equalityEngine.areEqual(instore[1],n[1])) {
1138 Node r = nm->mkNode(kind::SELECT, instore, n[1]);
1139 if (termSet.find(r) == termSet.end() && d_equalityEngine.hasTerm(r)) {
1140 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(c) read: " << r << endl;
1141 termSet.insert(r);
1142 changed = true;
1143 }
1144 r = nm->mkNode(kind::SELECT, instore[0], n[1]);
1145 if (termSet.find(r) == termSet.end() && d_equalityEngine.hasTerm(r)) {
1146 Trace("arrays::collectModelInfo") << "TheoryArrays::collectModelInfo, adding RIntro2(d) read: " << r << endl;
1147 termSet.insert(r);
1148 changed = true;
1149 }
1150 }
1151 }
1152 }
1153 }
1154 }
1155 } while (changed);
1156
1157 // Send the equality engine information to the model
1158 if (!m->assertEqualityEngine(&d_equalityEngine, &termSet))
1159 {
1160 return false;
1161 }
1162
1163 // Build a list of all the relevant reads, indexed by the store representative
1164 std::map<Node, std::vector<Node> > selects;
1165 set<Node>::iterator set_it = termSet.begin(), set_it_end = termSet.end();
1166 for (; set_it != set_it_end; ++set_it) {
1167 Node n = *set_it;
1168 // If this term is a select, record that the EC rep of its store parameter is being read from using this term
1169 if (n.getKind() == kind::SELECT) {
1170 selects[d_equalityEngine.getRepresentative(n[0])].push_back(n);
1171 }
1172 }
1173
1174 Node rep;
1175 DefValMap::iterator it;
1176 TypeSet defaultValuesSet;
1177
1178 // Compute all default values already in use
1179 //if (fullModel) {
1180 for (size_t i=0; i<arrays.size(); ++i) {
1181 TNode nrep = d_equalityEngine.getRepresentative(arrays[i]);
1182 d_mayEqualEqualityEngine.addTerm(nrep); // add the term in case it isn't there already
1183 TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1184 it = d_defValues.find(mayRep);
1185 if (it != d_defValues.end()) {
1186 defaultValuesSet.add(nrep.getType().getArrayConstituentType(), (*it).second);
1187 }
1188 }
1189 //}
1190
1191 // Loop through all array equivalence classes that need a representative computed
1192 for (size_t i=0; i<arrays.size(); ++i) {
1193 TNode n = arrays[i];
1194 TNode nrep = d_equalityEngine.getRepresentative(n);
1195
1196 //if (fullModel) {
1197 // Compute default value for this array - there is one default value for every mayEqual equivalence class
1198 TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1199 it = d_defValues.find(mayRep);
1200 // If this mayEqual EC doesn't have a default value associated, get the next available default value for the associated array element type
1201 if (it == d_defValues.end()) {
1202 TypeNode valueType = nrep.getType().getArrayConstituentType();
1203 rep = defaultValuesSet.nextTypeEnum(valueType);
1204 if (rep.isNull()) {
1205 Assert(defaultValuesSet.getSet(valueType)->begin() != defaultValuesSet.getSet(valueType)->end());
1206 rep = *(defaultValuesSet.getSet(valueType)->begin());
1207 }
1208 Trace("arrays-models") << "New default value = " << rep << endl;
1209 d_defValues[mayRep] = rep;
1210 }
1211 else {
1212 rep = (*it).second;
1213 }
1214
1215 // Build the STORE_ALL term with the default value
1216 rep = nm->mkConst(ArrayStoreAll(nrep.getType().toType(), rep.toExpr()));
1217 /*
1218 }
1219 else {
1220 std::unordered_map<Node, Node, NodeHashFunction>::iterator it = d_skolemCache.find(n);
1221 if (it == d_skolemCache.end()) {
1222 rep = nm->mkSkolem("array_collect_model_var", n.getType(), "base model variable for array collectModelInfo");
1223 d_skolemCache[n] = rep;
1224 }
1225 else {
1226 rep = (*it).second;
1227 }
1228 }
1229 */
1230
1231 // For each read, require that the rep stores the right value
1232 vector<Node>& reads = selects[nrep];
1233 for (unsigned j = 0; j < reads.size(); ++j) {
1234 rep = nm->mkNode(kind::STORE, rep, reads[j][1], reads[j]);
1235 }
1236 if (!m->assertEquality(n, rep, true))
1237 {
1238 return false;
1239 }
1240 if (!n.isConst()) {
1241 m->assertSkeleton(rep);
1242 }
1243 }
1244 return true;
1245 }
1246
1247 /////////////////////////////////////////////////////////////////////////////
1248 // NOTIFICATIONS
1249 /////////////////////////////////////////////////////////////////////////////
1250
1251
presolve()1252 void TheoryArrays::presolve()
1253 {
1254 Trace("arrays")<<"Presolving \n";
1255 // add the decision strategy
1256 getDecisionManager()->registerStrategy(DecisionManager::STRAT_ARRAYS,
1257 d_dstrat.get());
1258 }
1259
1260
1261 /////////////////////////////////////////////////////////////////////////////
1262 // MAIN SOLVER
1263 /////////////////////////////////////////////////////////////////////////////
1264
1265
getSkolem(TNode ref,const string & name,const TypeNode & type,const string & comment,bool makeEqual)1266 Node TheoryArrays::getSkolem(TNode ref, const string& name, const TypeNode& type, const string& comment, bool makeEqual)
1267 {
1268 Node skolem;
1269 std::unordered_map<Node, Node, NodeHashFunction>::iterator it = d_skolemCache.find(ref);
1270 if (it == d_skolemCache.end()) {
1271 NodeManager* nm = NodeManager::currentNM();
1272 skolem = nm->mkSkolem(name, type, comment);
1273 d_skolemCache[ref] = skolem;
1274 }
1275 else {
1276 skolem = (*it).second;
1277 if (d_equalityEngine.hasTerm(ref) &&
1278 d_equalityEngine.hasTerm(skolem) &&
1279 d_equalityEngine.areEqual(ref, skolem)) {
1280 makeEqual = false;
1281 }
1282 }
1283
1284 Debug("pf::array") << "Pregistering a Skolem" << std::endl;
1285 preRegisterTermInternal(skolem);
1286 Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl;
1287
1288 if (makeEqual) {
1289 Node d = skolem.eqNode(ref);
1290 Debug("arrays-model-based") << "Asserting skolem equality " << d << endl;
1291 d_equalityEngine.assertEquality(d, true, d_true);
1292 Assert(!d_conflict);
1293 d_skolemAssertions.push_back(d);
1294 d_skolemIndex = d_skolemIndex + 1;
1295 }
1296
1297 Debug("pf::array") << "getSkolem DONE" << std::endl;
1298 return skolem;
1299 }
1300
1301
check(Effort e)1302 void TheoryArrays::check(Effort e) {
1303 if (done() && !fullEffort(e)) {
1304 return;
1305 }
1306
1307 getOutputChannel().spendResource(options::theoryCheckStep());
1308
1309 TimerStat::CodeTimer checkTimer(d_checkTime);
1310
1311 while (!done() && !d_conflict)
1312 {
1313 // Get all the assertions
1314 Assertion assertion = get();
1315 TNode fact = assertion.assertion;
1316
1317 Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::check(): processing " << fact << std::endl;
1318
1319 bool polarity = fact.getKind() != kind::NOT;
1320 TNode atom = polarity ? fact : fact[0];
1321
1322 if (!assertion.isPreregistered) {
1323 if (atom.getKind() == kind::EQUAL) {
1324 if (!d_equalityEngine.hasTerm(atom[0])) {
1325 Assert(atom[0].isConst());
1326 d_equalityEngine.addTerm(atom[0]);
1327 }
1328 if (!d_equalityEngine.hasTerm(atom[1])) {
1329 Assert(atom[1].isConst());
1330 d_equalityEngine.addTerm(atom[1]);
1331 }
1332 }
1333 }
1334
1335 // Do the work
1336 switch (fact.getKind()) {
1337 case kind::EQUAL:
1338 d_equalityEngine.assertEquality(fact, true, fact);
1339 break;
1340 case kind::SELECT:
1341 d_equalityEngine.assertPredicate(fact, true, fact);
1342 break;
1343 case kind::NOT:
1344 if (fact[0].getKind() == kind::SELECT) {
1345 d_equalityEngine.assertPredicate(fact[0], false, fact);
1346 } else if (!d_equalityEngine.areDisequal(fact[0][0], fact[0][1], false)) {
1347 // Assert the dis-equality
1348 d_equalityEngine.assertEquality(fact[0], false, fact);
1349
1350 // Apply ArrDiseq Rule if diseq is between arrays
1351 if(fact[0][0].getType().isArray() && !d_conflict) {
1352 if (d_conflict) { Debug("pf::array") << "Entering the skolemization branch" << std::endl; }
1353
1354 NodeManager* nm = NodeManager::currentNM();
1355 TypeNode indexType = fact[0][0].getType()[0];
1356
1357 TNode k;
1358 // k is the skolem for this disequality.
1359 if (!d_proofsEnabled) {
1360 Debug("pf::array") << "Check: kind::NOT: array theory making a skolem" << std::endl;
1361
1362 // If not in replay mode, generate a fresh skolem variable
1363 k = getSkolem(fact,
1364 "array_ext_index",
1365 indexType,
1366 "an extensional lemma index variable from the theory of arrays",
1367 false);
1368
1369 // Register this skolem for the proof replay phase
1370 PROOF(ProofManager::getSkolemizationManager()->registerSkolem(fact, k));
1371 } else {
1372 if (!ProofManager::getSkolemizationManager()->hasSkolem(fact)) {
1373 // In the solution pass we didn't need this skolem. Therefore, we don't need it
1374 // in this reply pass, either.
1375 break;
1376 }
1377
1378 // Reuse the same skolem as in the solution pass
1379 k = ProofManager::getSkolemizationManager()->getSkolem(fact);
1380 Debug("pf::array") << "Skolem = " << k << std::endl;
1381 }
1382
1383 Node ak = nm->mkNode(kind::SELECT, fact[0][0], k);
1384 Node bk = nm->mkNode(kind::SELECT, fact[0][1], k);
1385 Node eq = ak.eqNode(bk);
1386 Node lemma = fact[0].orNode(eq.notNode());
1387
1388 // In solve mode we don't care if ak and bk are registered. If they aren't, they'll be registered
1389 // when we output the lemma. However, in replay need the lemma to be propagated, and so we
1390 // preregister manually.
1391 if (d_proofsEnabled) {
1392 if (!d_equalityEngine.hasTerm(ak)) { preRegisterTermInternal(ak); }
1393 if (!d_equalityEngine.hasTerm(bk)) { preRegisterTermInternal(bk); }
1394 }
1395
1396 if (options::arraysPropagate() > 0 && d_equalityEngine.hasTerm(ak) && d_equalityEngine.hasTerm(bk)) {
1397 // Propagate witness disequality - might produce a conflict
1398 d_permRef.push_back(lemma);
1399 Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1400 << "\teq = " << eq << std::endl
1401 << "\treason = " << fact << std::endl;
1402
1403 d_equalityEngine.assertEquality(eq, false, fact, d_reasonExt);
1404 ++d_numProp;
1405 }
1406
1407 if (!d_proofsEnabled) {
1408 // If this is the solution pass, generate the lemma. Otherwise, don't generate it -
1409 // as this is the lemma that we're reproving...
1410 Trace("arrays-lem")<<"Arrays::addExtLemma " << lemma <<"\n";
1411 d_out->lemma(lemma);
1412 ++d_numExt;
1413 }
1414 } else {
1415 Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem" << std::endl;
1416 d_modelConstraints.push_back(fact);
1417 }
1418 }
1419 break;
1420 default:
1421 Unreachable();
1422 break;
1423 }
1424 }
1425
1426 if ((options::arraysEagerLemmas() || fullEffort(e)) && !d_conflict && options::arraysWeakEquivalence()) {
1427 // Replay all array merges to update weak equivalence data structures
1428 context::CDList<Node>::iterator it = d_arrayMerges.begin(), iend = d_arrayMerges.end();
1429 TNode a, b, eq;
1430 for (; it != iend; ++it) {
1431 eq = *it;
1432 a = eq[0];
1433 b = eq[1];
1434 weakEquivMakeRep(b);
1435 if (weakEquivGetRep(a) == b) {
1436 weakEquivAddSecondary(TNode(), a, b, eq);
1437 }
1438 else {
1439 d_infoMap.setWeakEquivPointer(b, a);
1440 d_infoMap.setWeakEquivIndex(b, TNode());
1441 }
1442 #ifdef CVC4_ASSERTIONS
1443 checkWeakEquiv(false);
1444 #endif
1445 }
1446 #ifdef CVC4_ASSERTIONS
1447 checkWeakEquiv(true);
1448 #endif
1449
1450 d_readTableContext->push();
1451 TNode mayRep, iRep;
1452 CTNodeList* bucketList = NULL;
1453 CTNodeList::const_iterator i = d_reads.begin(), readsEnd = d_reads.end();
1454 for (; i != readsEnd; ++i) {
1455 const TNode& r = *i;
1456
1457 Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r << std::endl;
1458
1459 // Find the bucket for this read.
1460 mayRep = d_mayEqualEqualityEngine.getRepresentative(r[0]);
1461 iRep = d_equalityEngine.getRepresentative(r[1]);
1462 std::pair<TNode, TNode> key(mayRep, iRep);
1463 ReadBucketMap::iterator it = d_readBucketTable.find(key);
1464 if (it == d_readBucketTable.end()) {
1465 bucketList = new(true) CTNodeList(d_readTableContext);
1466 d_readBucketAllocations.push_back(bucketList);
1467 d_readBucketTable[key] = bucketList;
1468 }
1469 else {
1470 bucketList = it->second;
1471 }
1472 CTNodeList::const_iterator it2 = bucketList->begin(), iend = bucketList->end();
1473 for (; it2 != iend; ++it2) {
1474 const TNode& r2 = *it2;
1475 Assert(r2.getKind() == kind::SELECT);
1476 Assert(mayRep == d_mayEqualEqualityEngine.getRepresentative(r2[0]));
1477 Assert(iRep == d_equalityEngine.getRepresentative(r2[1]));
1478 if (d_equalityEngine.areEqual(r, r2)) {
1479 continue;
1480 }
1481 if (weakEquivGetRepIndex(r[0], r[1]) == weakEquivGetRepIndex(r2[0], r[1])) {
1482 // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1483 vector<TNode> conjunctions;
1484 Assert(d_equalityEngine.areEqual(r, Rewriter::rewrite(r)));
1485 Assert(d_equalityEngine.areEqual(r2, Rewriter::rewrite(r2)));
1486 Node lemma = Rewriter::rewrite(r).eqNode(Rewriter::rewrite(r2)).negate();
1487 d_permRef.push_back(lemma);
1488 conjunctions.push_back(lemma);
1489 if (r[1] != r2[1]) {
1490 d_equalityEngine.explainEquality(r[1], r2[1], true, conjunctions);
1491 }
1492 // TODO: get smaller lemmas by eliminating shared parts of path
1493 weakEquivBuildCond(r[0], r[1], conjunctions);
1494 weakEquivBuildCond(r2[0], r[1], conjunctions);
1495 lemma = mkAnd(conjunctions, true);
1496 // LSH FIXME: which kind of arrays lemma is this
1497 Trace("arrays-lem") << "Arrays::addExtLemma " << lemma <<"\n";
1498 d_out->lemma(lemma, RULE_INVALID, false, false, true);
1499 d_readTableContext->pop();
1500 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl;
1501 return;
1502 }
1503 }
1504 bucketList->push_back(r);
1505 }
1506 d_readTableContext->pop();
1507 }
1508
1509 if(!options::arraysEagerLemmas() && fullEffort(e) && !d_conflict && !options::arraysWeakEquivalence()) {
1510 // generate the lemmas on the worklist
1511 Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue.size() << "\n";
1512 while (d_RowQueue.size() > 0 && !d_conflict) {
1513 if (dischargeLemmas()) {
1514 break;
1515 }
1516 }
1517 }
1518
1519 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl;
1520 }
1521
1522
mkAnd(std::vector<TNode> & conjunctions,bool invert,unsigned startIndex)1523 Node TheoryArrays::mkAnd(std::vector<TNode>& conjunctions, bool invert, unsigned startIndex)
1524 {
1525 Assert(conjunctions.size() > 0);
1526
1527 std::set<TNode> all;
1528 std::set<TNode> explained;
1529
1530 unsigned i = startIndex;
1531 TNode t;
1532 for (; i < conjunctions.size(); ++i) {
1533 t = conjunctions[i];
1534 if (t == d_true) {
1535 continue;
1536 }
1537 else if (t.getKind() == kind::AND) {
1538 for(TNode::iterator child_it = t.begin(); child_it != t.end(); ++child_it) {
1539 if (*child_it == d_true) {
1540 continue;
1541 }
1542 all.insert(*child_it);
1543 }
1544 }
1545 else if (t.getKind() == kind::OR) {
1546 // Expand explanation resulting from propagating a ROW or EXT lemma
1547 if ((explained.find(t) == explained.end())) {
1548 if (t[1].getKind() == kind::EQUAL) {
1549 // ROW lemma
1550 d_equalityEngine.explainEquality(t[1][0], t[1][1], false, conjunctions);
1551 explained.insert(t);
1552 } else {
1553 // EXT lemma
1554 Assert(t[1].getKind() == kind::NOT && t[1][0].getKind() == kind::EQUAL);
1555 Assert(t[0].getKind() == kind::EQUAL);
1556 all.insert(t[0].notNode());
1557 explained.insert(t);
1558 }
1559 }
1560 }
1561 else {
1562 all.insert(t);
1563 }
1564 }
1565
1566 if (all.size() == 0) {
1567 return invert ? d_false : d_true;
1568 }
1569 if (all.size() == 1) {
1570 // All the same, or just one
1571 if (invert) {
1572 return (*(all.begin())).negate();
1573 }
1574 else {
1575 return *(all.begin());
1576 }
1577 }
1578
1579 NodeBuilder<> conjunction(invert ? kind::OR : kind::AND);
1580 std::set<TNode>::const_iterator it = all.begin();
1581 std::set<TNode>::const_iterator it_end = all.end();
1582 while (it != it_end) {
1583 if (invert) {
1584 conjunction << (*it).negate();
1585 }
1586 else {
1587 conjunction << *it;
1588 }
1589 ++ it;
1590 }
1591
1592 return conjunction;
1593 }
1594
1595
setNonLinear(TNode a)1596 void TheoryArrays::setNonLinear(TNode a)
1597 {
1598 if (options::arraysWeakEquivalence()) return;
1599 if (d_infoMap.isNonLinear(a)) return;
1600
1601 Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a << ")\n";
1602 d_infoMap.setNonLinear(a);
1603 ++d_numNonLinear;
1604
1605 const CTNodeList* i_a = d_infoMap.getIndices(a);
1606 const CTNodeList* st_a = d_infoMap.getStores(a);
1607 const CTNodeList* inst_a = d_infoMap.getInStores(a);
1608
1609 size_t it = 0;
1610
1611 // Propagate non-linearity down chain of stores
1612 for( ; it < st_a->size(); ++it) {
1613 TNode store = (*st_a)[it];
1614 Assert(store.getKind()==kind::STORE);
1615 setNonLinear(store[0]);
1616 }
1617
1618 // Instantiate ROW lemmas that were ignored before
1619 size_t it2 = 0;
1620 RowLemmaType lem;
1621 for(; it2 < i_a->size(); ++it2) {
1622 TNode i = (*i_a)[it2];
1623 it = 0;
1624 for ( ; it < inst_a->size(); ++it) {
1625 TNode store = (*inst_a)[it];
1626 Assert(store.getKind() == kind::STORE);
1627 TNode j = store[1];
1628 TNode c = store[0];
1629 lem = std::make_tuple(store, c, j, i);
1630 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1631 queueRowLemma(lem);
1632 }
1633 }
1634
1635 }
1636
1637 /*****
1638 * When two array equivalence classes are merged, we may need to apply RIntro1 to a store in one of the EC's
1639 * Here, we check the stores in a to see if any need RIntro1 applied
1640 * We apply RIntro1 whenever:
1641 * (a) a store becomes equal to another store
1642 * (b) a store becomes equal to any term t such that read(t,i) exists
1643 * (c) a store becomes equal to the root array of the store (i.e. store(store(...store(a,i,v)...)) = a)
1644 */
checkRIntro1(TNode a,TNode b)1645 void TheoryArrays::checkRIntro1(TNode a, TNode b)
1646 {
1647 const CTNodeList* astores = d_infoMap.getStores(a);
1648 // Apply RIntro1 if applicable
1649 CTNodeList::const_iterator it = astores->begin();
1650
1651 if (it == astores->end()) {
1652 // No stores in this equivalence class - return
1653 return;
1654 }
1655
1656 ++it;
1657 if (it != astores->end()) {
1658 // More than one store: should have already been applied
1659 Assert(d_infoMap.rIntro1Applied(*it));
1660 Assert(d_infoMap.rIntro1Applied(*(--it)));
1661 return;
1662 }
1663
1664 // Exactly one store - see if we need to apply RIntro1
1665 --it;
1666 TNode s = *it;
1667 Assert(s.getKind() == kind::STORE);
1668 if (d_infoMap.rIntro1Applied(s)) {
1669 // RIntro1 already applied to s
1670 return;
1671 }
1672
1673 // Should be no reads from this EC
1674 Assert(d_infoMap.getIndices(a)->begin() == d_infoMap.getIndices(a)->end());
1675
1676 bool apply = false;
1677 if (d_infoMap.getStores(b)->size() > 0) {
1678 // Case (a): two stores become equal
1679 apply = true;
1680 }
1681 else {
1682 const CTNodeList* i_b = d_infoMap.getIndices(b);
1683 if (i_b->begin() != i_b->end()) {
1684 // Case (b): there are reads from b
1685 apply = true;
1686 }
1687 else {
1688 // Get root array of s
1689 TNode e1 = s[0];
1690 while (e1.getKind() == kind::STORE) {
1691 e1 = e1[0];
1692 }
1693 Assert(d_equalityEngine.hasTerm(e1));
1694 Assert(d_equalityEngine.hasTerm(b));
1695 if (d_equalityEngine.areEqual(e1, b)) {
1696 apply = true;
1697 }
1698 }
1699 }
1700
1701 if (apply) {
1702 NodeManager* nm = NodeManager::currentNM();
1703 d_infoMap.setRIntro1Applied(s);
1704 Node ni = nm->mkNode(kind::SELECT, s, s[1]);
1705 preRegisterTermInternal(ni);
1706 d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true, d_reasonRow1);
1707 }
1708 }
1709
1710
1711
1712
mergeArrays(TNode a,TNode b)1713 void TheoryArrays::mergeArrays(TNode a, TNode b)
1714 {
1715 // Note: a is the new representative
1716 Assert(a.getType().isArray() && b.getType().isArray());
1717
1718 if (d_mergeInProgress) {
1719 // Nested call to mergeArrays, just push on the queue and return
1720 d_mergeQueue.push(a.eqNode(b));
1721 return;
1722 }
1723
1724 d_mergeInProgress = true;
1725
1726 Node n;
1727 while (true) {
1728 // Normally, a is its own representative, but it's possible for a to have
1729 // been merged with another array after it got queued up by the equality engine,
1730 // so we take its representative to be safe.
1731 a = d_equalityEngine.getRepresentative(a);
1732 Assert(d_equalityEngine.getRepresentative(b) == a);
1733 Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: (" << a << ", " << b << ")\n";
1734
1735 if (options::arraysLazyRIntro1() && !options::arraysWeakEquivalence()) {
1736 checkRIntro1(a, b);
1737 checkRIntro1(b, a);
1738 }
1739
1740 if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1741 bool aNL = d_infoMap.isNonLinear(a);
1742 bool bNL = d_infoMap.isNonLinear(b);
1743 if (aNL) {
1744 if (bNL) {
1745 // already both marked as non-linear - no need to do anything
1746 }
1747 else {
1748 // Set b to be non-linear
1749 setNonLinear(b);
1750 }
1751 }
1752 else {
1753 if (bNL) {
1754 // Set a to be non-linear
1755 setNonLinear(a);
1756 }
1757 else {
1758 // Check for new non-linear arrays
1759 const CTNodeList* astores = d_infoMap.getStores(a);
1760 const CTNodeList* bstores = d_infoMap.getStores(b);
1761 Assert(astores->size() <= 1 && bstores->size() <= 1);
1762 if (astores->size() > 0 && bstores->size() > 0) {
1763 setNonLinear(a);
1764 setNonLinear(b);
1765 }
1766 }
1767 }
1768 }
1769
1770 TNode constArrA = d_infoMap.getConstArr(a);
1771 TNode constArrB = d_infoMap.getConstArr(b);
1772 if (constArrA.isNull()) {
1773 if (!constArrB.isNull()) {
1774 d_infoMap.setConstArr(a,constArrB);
1775 }
1776 }
1777 else if (!constArrB.isNull()) {
1778 if (constArrA != constArrB) {
1779 conflict(constArrA,constArrB);
1780 }
1781 }
1782
1783 TNode mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1784 TNode mayRepB = d_mayEqualEqualityEngine.getRepresentative(b);
1785
1786 // If a and b have different default values associated with their mayequal equivalence classes,
1787 // things get complicated. Similarly, if two mayequal equivalence classes have different
1788 // constant representatives, it's not clear what to do. - disallow these cases for now. -Clark
1789 DefValMap::iterator it = d_defValues.find(mayRepA);
1790 DefValMap::iterator it2 = d_defValues.find(mayRepB);
1791 TNode defValue;
1792
1793 if (it != d_defValues.end()) {
1794 defValue = (*it).second;
1795 if ((it2 != d_defValues.end() && (defValue != (*it2).second)) ||
1796 (mayRepA.isConst() && mayRepB.isConst() && mayRepA != mayRepB)) {
1797 throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1798 }
1799 }
1800 else if (it2 != d_defValues.end()) {
1801 defValue = (*it2).second;
1802 }
1803 d_mayEqualEqualityEngine.assertEquality(a.eqNode(b), true, d_true);
1804 Assert(d_mayEqualEqualityEngine.consistent());
1805 if (!defValue.isNull()) {
1806 mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1807 d_defValues[mayRepA] = defValue;
1808 }
1809
1810 checkRowLemmas(a,b);
1811 checkRowLemmas(b,a);
1812
1813 // merge info adds the list of the 2nd argument to the first
1814 d_infoMap.mergeInfo(a, b);
1815
1816 if (options::arraysWeakEquivalence()) {
1817 d_arrayMerges.push_back(a.eqNode(b));
1818 }
1819
1820 // If no more to do, break
1821 if (d_conflict || d_mergeQueue.empty()) {
1822 break;
1823 }
1824
1825 // Otherwise, prepare for next iteration
1826 n = d_mergeQueue.front();
1827 a = n[0];
1828 b = n[1];
1829 d_mergeQueue.pop();
1830 }
1831 d_mergeInProgress = false;
1832 }
1833
1834
checkStore(TNode a)1835 void TheoryArrays::checkStore(TNode a) {
1836 if (options::arraysWeakEquivalence()) return;
1837 Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
1838
1839 if(Trace.isOn("arrays-cri")) {
1840 d_infoMap.getInfo(a)->print();
1841 }
1842 Assert(a.getType().isArray());
1843 Assert(a.getKind()==kind::STORE);
1844 TNode b = a[0];
1845 TNode i = a[1];
1846
1847 TNode brep = d_equalityEngine.getRepresentative(b);
1848
1849 if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(brep)) {
1850 const CTNodeList* js = d_infoMap.getIndices(brep);
1851 size_t it = 0;
1852 RowLemmaType lem;
1853 for(; it < js->size(); ++it) {
1854 TNode j = (*js)[it];
1855 if (i == j) continue;
1856 lem = std::make_tuple(a, b, i, j);
1857 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkStore ("<<a<<", "<<b<<", "<<i<<", "<<j<<")\n";
1858 queueRowLemma(lem);
1859 }
1860 }
1861 }
1862
1863
checkRowForIndex(TNode i,TNode a)1864 void TheoryArrays::checkRowForIndex(TNode i, TNode a)
1865 {
1866 if (options::arraysWeakEquivalence()) return;
1867 Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a<<"\n";
1868 Trace("arrays-cri")<<" index "<<i<<"\n";
1869
1870 if(Trace.isOn("arrays-cri")) {
1871 d_infoMap.getInfo(a)->print();
1872 }
1873 Assert(a.getType().isArray());
1874 Assert(d_equalityEngine.getRepresentative(a) == a);
1875
1876 TNode constArr = d_infoMap.getConstArr(a);
1877 if (!constArr.isNull()) {
1878 ArrayStoreAll storeAll = constArr.getConst<ArrayStoreAll>();
1879 Node defValue = Node::fromExpr(storeAll.getExpr());
1880 Node selConst = NodeManager::currentNM()->mkNode(kind::SELECT, constArr, i);
1881 if (!d_equalityEngine.hasTerm(selConst)) {
1882 preRegisterTermInternal(selConst);
1883 }
1884 d_equalityEngine.assertEquality(selConst.eqNode(defValue), true, d_true);
1885 }
1886
1887 const CTNodeList* stores = d_infoMap.getStores(a);
1888 const CTNodeList* instores = d_infoMap.getInStores(a);
1889 size_t it = 0;
1890 RowLemmaType lem;
1891
1892 for(; it < stores->size(); ++it) {
1893 TNode store = (*stores)[it];
1894 Assert(store.getKind()==kind::STORE);
1895 TNode j = store[1];
1896 if (i == j) continue;
1897 lem = std::make_tuple(store, store[0], j, i);
1898 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
1899 queueRowLemma(lem);
1900 }
1901
1902 if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(a)) {
1903 it = 0;
1904 for(; it < instores->size(); ++it) {
1905 TNode instore = (*instores)[it];
1906 Assert(instore.getKind()==kind::STORE);
1907 TNode j = instore[1];
1908 if (i == j) continue;
1909 lem = std::make_tuple(instore, instore[0], j, i);
1910 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore<<", "<<instore[0]<<", "<<j<<", "<<i<<")\n";
1911 queueRowLemma(lem);
1912 }
1913 }
1914 }
1915
1916
1917 // a just became equal to b
1918 // look for new ROW lemmas
checkRowLemmas(TNode a,TNode b)1919 void TheoryArrays::checkRowLemmas(TNode a, TNode b)
1920 {
1921 if (options::arraysWeakEquivalence()) return;
1922 Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
1923 if(Trace.isOn("arrays-crl"))
1924 d_infoMap.getInfo(a)->print();
1925 Trace("arrays-crl")<<" ------------ and "<<b<<"\n";
1926 if(Trace.isOn("arrays-crl"))
1927 d_infoMap.getInfo(b)->print();
1928
1929 const CTNodeList* i_a = d_infoMap.getIndices(a);
1930 size_t it = 0;
1931 TNode constArr = d_infoMap.getConstArr(b);
1932 if (!constArr.isNull()) {
1933 for( ; it < i_a->size(); ++it) {
1934 TNode i = (*i_a)[it];
1935 Node selConst = NodeManager::currentNM()->mkNode(kind::SELECT, constArr, i);
1936 if (!d_equalityEngine.hasTerm(selConst)) {
1937 preRegisterTermInternal(selConst);
1938 }
1939 }
1940 }
1941
1942 const CTNodeList* st_b = d_infoMap.getStores(b);
1943 const CTNodeList* inst_b = d_infoMap.getInStores(b);
1944 size_t its;
1945
1946 RowLemmaType lem;
1947
1948 for(it = 0 ; it < i_a->size(); ++it) {
1949 TNode i = (*i_a)[it];
1950 its = 0;
1951 for ( ; its < st_b->size(); ++its) {
1952 TNode store = (*st_b)[its];
1953 Assert(store.getKind() == kind::STORE);
1954 TNode j = store[1];
1955 TNode c = store[0];
1956 lem = std::make_tuple(store, c, j, i);
1957 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1958 queueRowLemma(lem);
1959 }
1960 }
1961
1962 if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(b)) {
1963 for(it = 0 ; it < i_a->size(); ++it ) {
1964 TNode i = (*i_a)[it];
1965 its = 0;
1966 for ( ; its < inst_b->size(); ++its) {
1967 TNode store = (*inst_b)[its];
1968 Assert(store.getKind() == kind::STORE);
1969 TNode j = store[1];
1970 TNode c = store[0];
1971 lem = std::make_tuple(store, c, j, i);
1972 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1973 queueRowLemma(lem);
1974 }
1975 }
1976 }
1977 Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1978 }
1979
propagate(RowLemmaType lem)1980 void TheoryArrays::propagate(RowLemmaType lem)
1981 {
1982 Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1983 << options::arraysPropagate() << std::endl;
1984
1985 TNode a, b, i, j;
1986 std::tie(a, b, i, j) = lem;
1987
1988 Assert(a.getType().isArray() && b.getType().isArray());
1989 if (d_equalityEngine.areEqual(a,b) ||
1990 d_equalityEngine.areEqual(i,j)) {
1991 return;
1992 }
1993
1994 NodeManager* nm = NodeManager::currentNM();
1995 Node aj = nm->mkNode(kind::SELECT, a, j);
1996 Node bj = nm->mkNode(kind::SELECT, b, j);
1997
1998 // Try to avoid introducing new read terms: track whether these already exist
1999 bool ajExists = d_equalityEngine.hasTerm(aj);
2000 bool bjExists = d_equalityEngine.hasTerm(bj);
2001 bool bothExist = ajExists && bjExists;
2002
2003 // If propagating, check propagations
2004 int prop = options::arraysPropagate();
2005 if (prop > 0) {
2006 if (d_equalityEngine.areDisequal(i,j,true) && (bothExist || prop > 1)) {
2007 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj<<", "<<bj<<")\n";
2008 Node aj_eq_bj = aj.eqNode(bj);
2009 Node i_eq_j = i.eqNode(j);
2010 Node reason = nm->mkNode(kind::OR, aj_eq_bj, i_eq_j);
2011 d_permRef.push_back(reason);
2012 if (!ajExists) {
2013 preRegisterTermInternal(aj);
2014 }
2015 if (!bjExists) {
2016 preRegisterTermInternal(bj);
2017 }
2018 d_equalityEngine.assertEquality(aj_eq_bj, true, reason, d_reasonRow);
2019 ++d_numProp;
2020 return;
2021 }
2022 if (bothExist && d_equalityEngine.areDisequal(aj,bj,true)) {
2023 Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating i = j ("<<i<<", "<<j<<")\n";
2024 Node aj_eq_bj = aj.eqNode(bj);
2025 Node i_eq_j = i.eqNode(j);
2026 Node reason = nm->mkNode(kind::OR, i_eq_j, aj_eq_bj);
2027 d_permRef.push_back(reason);
2028 d_equalityEngine.assertEquality(i_eq_j, true, reason, d_reasonRow);
2029 ++d_numProp;
2030 return;
2031 }
2032 }
2033 }
2034
queueRowLemma(RowLemmaType lem)2035 void TheoryArrays::queueRowLemma(RowLemmaType lem)
2036 {
2037 Debug("pf::array") << "Array solver: queue row lemma called" << std::endl;
2038
2039 if (d_conflict || d_RowAlreadyAdded.contains(lem)) {
2040 return;
2041 }
2042 TNode a, b, i, j;
2043 std::tie(a, b, i, j) = lem;
2044
2045 Assert(a.getType().isArray() && b.getType().isArray());
2046 if (d_equalityEngine.areEqual(a,b) ||
2047 d_equalityEngine.areEqual(i,j)) {
2048 return;
2049 }
2050
2051 NodeManager* nm = NodeManager::currentNM();
2052 Node aj = nm->mkNode(kind::SELECT, a, j);
2053 Node bj = nm->mkNode(kind::SELECT, b, j);
2054
2055 // Try to avoid introducing new read terms: track whether these already exist
2056 bool ajExists = d_equalityEngine.hasTerm(aj);
2057 bool bjExists = d_equalityEngine.hasTerm(bj);
2058 bool bothExist = ajExists && bjExists;
2059
2060 // If propagating, check propagations
2061 int prop = options::arraysPropagate();
2062 if (prop > 0) {
2063 propagate(lem);
2064 }
2065
2066 // If equivalent lemma already exists, don't enqueue this one
2067 if (d_useArrTable) {
2068 Node tableEntry = NodeManager::currentNM()->mkNode(kind::ARR_TABLE_FUN, a, b, i, j);
2069 if (d_equalityEngine.getSize(tableEntry) != 1) {
2070 return;
2071 }
2072 }
2073
2074 // Prefer equality between indexes so as not to introduce new read terms
2075 if (options::arraysEagerIndexSplitting() && !bothExist && !d_equalityEngine.areDisequal(i,j, false)) {
2076 Node i_eq_j;
2077 if (!d_proofsEnabled) {
2078 i_eq_j = d_valuation.ensureLiteral(i.eqNode(j)); // TODO: think about this
2079 } else {
2080 i_eq_j = i.eqNode(j);
2081 }
2082
2083 getOutputChannel().requirePhase(i_eq_j, true);
2084 d_decisionRequests.push(i_eq_j);
2085 }
2086
2087 // TODO: maybe add triggers here
2088
2089 if ((options::arraysEagerLemmas() || bothExist) && !d_proofsEnabled) {
2090 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2091 Node aj2 = Rewriter::rewrite(aj);
2092 if (aj != aj2) {
2093 if (!ajExists) {
2094 preRegisterTermInternal(aj);
2095 }
2096 if (!d_equalityEngine.hasTerm(aj2)) {
2097 preRegisterTermInternal(aj2);
2098 }
2099 d_equalityEngine.assertEquality(aj.eqNode(aj2), true, d_true);
2100 }
2101 Node bj2 = Rewriter::rewrite(bj);
2102 if (bj != bj2) {
2103 if (!bjExists) {
2104 preRegisterTermInternal(bj);
2105 }
2106 if (!d_equalityEngine.hasTerm(bj2)) {
2107 preRegisterTermInternal(bj2);
2108 }
2109 d_equalityEngine.assertEquality(bj.eqNode(bj2), true, d_true);
2110 }
2111 if (aj2 == bj2) {
2112 return;
2113 }
2114
2115 // construct lemma
2116 Node eq1 = aj2.eqNode(bj2);
2117 Node eq1_r = Rewriter::rewrite(eq1);
2118 if (eq1_r == d_true) {
2119 if (!d_equalityEngine.hasTerm(aj2)) {
2120 preRegisterTermInternal(aj2);
2121 }
2122 if (!d_equalityEngine.hasTerm(bj2)) {
2123 preRegisterTermInternal(bj2);
2124 }
2125 d_equalityEngine.assertEquality(eq1, true, d_true);
2126 return;
2127 }
2128
2129 Node eq2 = i.eqNode(j);
2130 Node eq2_r = Rewriter::rewrite(eq2);
2131 if (eq2_r == d_true) {
2132 d_equalityEngine.assertEquality(eq2, true, d_true);
2133 return;
2134 }
2135
2136 Node lemma = nm->mkNode(kind::OR, eq2_r, eq1_r);
2137
2138 Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lemma<<"\n";
2139 d_RowAlreadyAdded.insert(lem);
2140 d_out->lemma(lemma);
2141 ++d_numRow;
2142 }
2143 else {
2144 d_RowQueue.push(lem);
2145 }
2146 }
2147
getNextDecisionRequest()2148 Node TheoryArrays::getNextDecisionRequest()
2149 {
2150 if(! d_decisionRequests.empty()) {
2151 Node n = d_decisionRequests.front();
2152 d_decisionRequests.pop();
2153 return n;
2154 }
2155 return Node::null();
2156 }
2157
2158
dischargeLemmas()2159 bool TheoryArrays::dischargeLemmas()
2160 {
2161 bool lemmasAdded = false;
2162 size_t sz = d_RowQueue.size();
2163 for (unsigned count = 0; count < sz; ++count) {
2164 RowLemmaType l = d_RowQueue.front();
2165 d_RowQueue.pop();
2166 if (d_RowAlreadyAdded.contains(l)) {
2167 continue;
2168 }
2169
2170 TNode a, b, i, j;
2171 std::tie(a, b, i, j) = l;
2172 Assert(a.getType().isArray() && b.getType().isArray());
2173
2174 NodeManager* nm = NodeManager::currentNM();
2175 Node aj = nm->mkNode(kind::SELECT, a, j);
2176 Node bj = nm->mkNode(kind::SELECT, b, j);
2177 bool ajExists = d_equalityEngine.hasTerm(aj);
2178 bool bjExists = d_equalityEngine.hasTerm(bj);
2179
2180 // Check for redundant lemma
2181 // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
2182 if (!d_equalityEngine.hasTerm(i) || !d_equalityEngine.hasTerm(j) || d_equalityEngine.areEqual(i,j) ||
2183 !d_equalityEngine.hasTerm(a) || !d_equalityEngine.hasTerm(b) || d_equalityEngine.areEqual(a,b) ||
2184 (ajExists && bjExists && d_equalityEngine.areEqual(aj,bj))) {
2185 continue;
2186 }
2187
2188 int prop = options::arraysPropagate();
2189 if (prop > 0) {
2190 propagate(l);
2191 if (d_conflict) {
2192 return true;
2193 }
2194 }
2195
2196 // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2197 Node aj2 = Rewriter::rewrite(aj);
2198 if (aj != aj2) {
2199 if (!ajExists) {
2200 preRegisterTermInternal(aj);
2201 }
2202 if (!d_equalityEngine.hasTerm(aj2)) {
2203 preRegisterTermInternal(aj2);
2204 }
2205 d_equalityEngine.assertEquality(aj.eqNode(aj2), true, d_true);
2206 }
2207 Node bj2 = Rewriter::rewrite(bj);
2208 if (bj != bj2) {
2209 if (!bjExists) {
2210 preRegisterTermInternal(bj);
2211 }
2212 if (!d_equalityEngine.hasTerm(bj2)) {
2213 preRegisterTermInternal(bj2);
2214 }
2215 d_equalityEngine.assertEquality(bj.eqNode(bj2), true, d_true);
2216
2217 }
2218 if (aj2 == bj2) {
2219 continue;
2220 }
2221
2222 // construct lemma
2223 Node eq1 = aj2.eqNode(bj2);
2224 Node eq1_r = Rewriter::rewrite(eq1);
2225 if (eq1_r == d_true) {
2226 if (!d_equalityEngine.hasTerm(aj2)) {
2227 preRegisterTermInternal(aj2);
2228 }
2229 if (!d_equalityEngine.hasTerm(bj2)) {
2230 preRegisterTermInternal(bj2);
2231 }
2232 d_equalityEngine.assertEquality(eq1, true, d_true);
2233 continue;
2234 }
2235
2236 Node eq2 = i.eqNode(j);
2237 Node eq2_r = Rewriter::rewrite(eq2);
2238 if (eq2_r == d_true) {
2239 d_equalityEngine.assertEquality(eq2, true, d_true);
2240 continue;
2241 }
2242
2243 Node lem = nm->mkNode(kind::OR, eq2_r, eq1_r);
2244
2245 Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lem<<"\n";
2246 d_RowAlreadyAdded.insert(l);
2247 d_out->lemma(lem);
2248 ++d_numRow;
2249 lemmasAdded = true;
2250 if (options::arraysReduceSharing()) {
2251 return true;
2252 }
2253 }
2254 return lemmasAdded;
2255 }
2256
conflict(TNode a,TNode b)2257 void TheoryArrays::conflict(TNode a, TNode b) {
2258 Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl;
2259 std::shared_ptr<eq::EqProof> proof = d_proofsEnabled ?
2260 std::make_shared<eq::EqProof>() : nullptr;
2261
2262 d_conflictNode = explain(a.eqNode(b), proof.get());
2263
2264 if (!d_inCheckModel) {
2265 std::unique_ptr<ProofArray> proof_array;
2266
2267 if (d_proofsEnabled) {
2268 proof->debug_print("pf::array");
2269 proof_array.reset(new ProofArray(proof,
2270 /*row=*/d_reasonRow,
2271 /*row1=*/d_reasonRow1,
2272 /*ext=*/d_reasonExt));
2273 }
2274
2275 d_out->conflict(d_conflictNode, std::move(proof_array));
2276 }
2277
2278 d_conflict = true;
2279 }
2280
TheoryArraysDecisionStrategy(TheoryArrays * ta)2281 TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2282 TheoryArrays* ta)
2283 : d_ta(ta)
2284 {
2285 }
initialize()2286 void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
getNextDecisionRequest()2287 Node TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2288 {
2289 return d_ta->getNextDecisionRequest();
2290 }
identify() const2291 std::string TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2292 {
2293 return std::string("th_arrays_dec");
2294 }
2295
2296 }/* CVC4::theory::arrays namespace */
2297 }/* CVC4::theory namespace */
2298 }/* CVC4 namespace */
2299