GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 889 1230 72.3 %
Date: 2021-09-29 Branches: 2353 6485 36.3 %

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