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