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