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