GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 865 1205 71.8 %
Date: 2021-08-17 Branches: 2358 6493 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
9850
TheoryArrays::TheoryArrays(Env& env,
59
                           OutputChannel& out,
60
                           Valuation valuation,
61
9850
                           std::string name)
62
    : Theory(THEORY_ARRAYS, env, out, valuation, name),
63
      d_numRow(
64
19700
          smtStatisticsRegistry().registerInt(name + "number of Row lemmas")),
65
      d_numExt(
66
19700
          smtStatisticsRegistry().registerInt(name + "number of Ext lemmas")),
67
      d_numProp(
68
19700
          smtStatisticsRegistry().registerInt(name + "number of propagations")),
69
      d_numExplain(
70
19700
          smtStatisticsRegistry().registerInt(name + "number of explanations")),
71
9850
      d_numNonLinear(smtStatisticsRegistry().registerInt(
72
19700
          name + "number of calls to setNonLinear")),
73
9850
      d_numSharedArrayVarSplits(smtStatisticsRegistry().registerInt(
74
19700
          name + "number of shared array var splits")),
75
9850
      d_numGetModelValSplits(smtStatisticsRegistry().registerInt(
76
19700
          name + "number of getModelVal splits")),
77
9850
      d_numGetModelValConflicts(smtStatisticsRegistry().registerInt(
78
19700
          name + "number of getModelVal conflicts")),
79
9850
      d_numSetModelValSplits(smtStatisticsRegistry().registerInt(
80
19700
          name + "number of setModelVal splits")),
81
9850
      d_numSetModelValConflicts(smtStatisticsRegistry().registerInt(
82
19700
          name + "number of setModelVal conflicts")),
83
19700
      d_ppEqualityEngine(getUserContext(), name + "pp", true),
84
9850
      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
19700
      d_mayEqualEqualityEngine(getSatContext(), name + "mayEqual", true),
92
      d_notify(*this),
93
      d_backtracker(getSatContext()),
94
      d_infoMap(getSatContext(), &d_backtracker, name),
95
      d_mergeQueue(getSatContext()),
96
      d_mergeInProgress(false),
97
      d_RowQueue(getSatContext()),
98
9850
      d_RowAlreadyAdded(getUserContext()),
99
      d_sharedArrays(getSatContext()),
100
      d_sharedOther(getSatContext()),
101
      d_sharedTerms(getSatContext(), false),
102
      d_reads(getSatContext()),
103
      d_constReadsList(getSatContext()),
104
9850
      d_constReadsContext(new context::Context()),
105
      d_contextPopper(getSatContext(), d_constReadsContext),
106
      d_skolemIndex(getSatContext(), 0),
107
      d_decisionRequests(getSatContext()),
108
      d_permRef(getSatContext()),
109
      d_modelConstraints(getSatContext()),
110
      d_lemmasSaved(getSatContext()),
111
      d_defValues(getSatContext()),
112
9850
      d_readTableContext(new context::Context()),
113
      d_arrayMerges(getSatContext()),
114
      d_inCheckModel(false),
115
9850
      d_dstrat(new TheoryArraysDecisionStrategy(this)),
116
206850
      d_dstratInit(false)
117
{
118
9850
  d_true = NodeManager::currentNM()->mkConst<bool>(true);
119
9850
  d_false = NodeManager::currentNM()->mkConst<bool>(false);
120
121
  // The preprocessing congruence kinds
122
9850
  d_ppEqualityEngine.addFunctionKind(kind::SELECT);
123
9850
  d_ppEqualityEngine.addFunctionKind(kind::STORE);
124
125
  // indicate we are using the default theory state object, and the arrays
126
  // inference manager
127
9850
  d_theoryState = &d_state;
128
9850
  d_inferManager = &d_im;
129
9850
}
130
131
29550
TheoryArrays::~TheoryArrays() {
132
9850
  vector<CTNodeList*>::iterator it = d_readBucketAllocations.begin(), iend = d_readBucketAllocations.end();
133
9850
  for (; it != iend; ++it) {
134
    (*it)->deleteSelf();
135
  }
136
9850
  delete d_readTableContext;
137
9850
  CNodeNListMap::iterator it2 = d_constReads.begin();
138
11264
  for( ; it2 != d_constReads.end(); ++it2 ) {
139
707
    it2->second->deleteSelf();
140
  }
141
9850
  delete d_constReadsContext;
142
19700
}
143
144
9850
TheoryRewriter* TheoryArrays::getTheoryRewriter() { return &d_rewriter; }
145
146
3766
ProofRuleChecker* TheoryArrays::getProofChecker() { return &d_checker; }
147
148
9850
bool TheoryArrays::needsEqualityEngine(EeSetupInfo& esi)
149
{
150
9850
  esi.d_notify = &d_notify;
151
9850
  esi.d_name = d_instanceName + "ee";
152
9850
  esi.d_notifyNewClass = true;
153
9850
  esi.d_notifyMerge = true;
154
9850
  return true;
155
}
156
157
9850
void TheoryArrays::finishInit()
158
{
159
9850
  Assert(d_equalityEngine != nullptr);
160
161
  // The kinds we are treating as function application in congruence
162
9850
  d_equalityEngine->addFunctionKind(kind::SELECT);
163
  if (d_ccStore)
164
  {
165
    d_equalityEngine->addFunctionKind(kind::STORE);
166
  }
167
9850
}
168
169
/////////////////////////////////////////////////////////////////////////////
170
// PREPROCESSING
171
/////////////////////////////////////////////////////////////////////////////
172
173
174
3221
bool TheoryArrays::ppDisequal(TNode a, TNode b) {
175
3221
  bool termsExist = d_ppEqualityEngine.hasTerm(a) && d_ppEqualityEngine.hasTerm(b);
176
3221
  Assert(!termsExist || !a.isConst() || !b.isConst() || a == b
177
         || d_ppEqualityEngine.areDisequal(a, b, false));
178
12824
  return ((termsExist && d_ppEqualityEngine.areDisequal(a, b, false)) ||
179
12824
          Rewriter::rewrite(a.eqNode(b)) == d_false);
180
}
181
182
183
1487
Node TheoryArrays::solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck)
184
{
185
1487
  if (!solve1) {
186
    return term;
187
  }
188
5660
  if (term[0].getKind() != kind::STORE &&
189
4173
      term[1].getKind() != kind::STORE) {
190
1185
    return term;
191
  }
192
604
  TNode left = term[0];
193
604
  TNode right = term[1];
194
302
  int leftWrites = 0, rightWrites = 0;
195
196
  // Count nested writes
197
604
  TNode e1 = left;
198
1106
  while (e1.getKind() == kind::STORE) {
199
402
    ++leftWrites;
200
402
    e1 = e1[0];
201
  }
202
203
604
  TNode e2 = right;
204
1490
  while (e2.getKind() == kind::STORE) {
205
594
    ++rightWrites;
206
594
    e2 = e2[0];
207
  }
208
209
302
  if (rightWrites > leftWrites) {
210
376
    TNode tmp = left;
211
188
    left = right;
212
188
    right = tmp;
213
188
    int tmpWrites = leftWrites;
214
188
    leftWrites = rightWrites;
215
188
    rightWrites = tmpWrites;
216
  }
217
218
302
  NodeManager* nm = NodeManager::currentNM();
219
302
  if (rightWrites == 0) {
220
208
    if (e1 != e2) {
221
160
      return term;
222
    }
223
    // write(store, index_0, v_0, index_1, v_1, ..., index_n, v_n) = store IFF
224
    //
225
    // read(store, index_n) = v_n &
226
    // index_{n-1} != index_n -> read(store, index_{n-1}) = v_{n-1} &
227
    // (index_{n-2} != index_{n-1} & index_{n-2} != index_n) -> read(store, index_{n-2}) = v_{n-2} &
228
    // ...
229
    // (index_1 != index_2 & ... & index_1 != index_n) -> read(store, index_1) = v_1
230
    // (index_0 != index_1 & index_0 != index_2 & ... & index_0 != index_n) -> read(store, index_0) = v_0
231
96
    TNode write_i, write_j, index_i, index_j;
232
96
    Node conc;
233
96
    NodeBuilder result(kind::AND);
234
    int i, j;
235
48
    write_i = left;
236
134
    for (i = leftWrites-1; i >= 0; --i) {
237
86
      index_i = write_i[1];
238
239
      // build: [index_i /= index_n && index_i /= index_(n-1) &&
240
      //         ... && index_i /= index_(i+1)] -> read(store, index_i) = v_i
241
86
      write_j = left;
242
      {
243
172
        NodeBuilder hyp(kind::AND);
244
138
        for (j = leftWrites - 1; j > i; --j) {
245
52
          index_j = write_j[1];
246
52
          if (!ppCheck || !ppDisequal(index_i, index_j)) {
247
44
            Node hyp2(index_i.eqNode(index_j));
248
22
            hyp << hyp2.notNode();
249
          }
250
52
          write_j = write_j[0];
251
        }
252
253
172
        Node r1 = nm->mkNode(kind::SELECT, e1, index_i);
254
86
        conc = r1.eqNode(write_i[2]);
255
86
        if (hyp.getNumChildren() != 0) {
256
22
          if (hyp.getNumChildren() == 1) {
257
22
            conc = hyp.getChild(0).impNode(conc);
258
          }
259
          else {
260
            r1 = hyp;
261
            conc = r1.impNode(conc);
262
          }
263
        }
264
265
        // And into result
266
86
        result << conc;
267
268
        // Prepare for next iteration
269
86
        write_i = write_i[0];
270
      }
271
    }
272
48
    Assert(result.getNumChildren() > 0);
273
48
    if (result.getNumChildren() == 1) {
274
20
      return result.getChild(0);
275
    }
276
28
    return result;
277
  }
278
  else {
279
94
    if (!solve2) {
280
94
      return term;
281
    }
282
    // store(...) = store(a,i,v) ==>
283
    // store(store(...),i,select(a,i)) = a && select(store(...),i)=v
284
    Node l = left;
285
    Node tmp;
286
    NodeBuilder nb(kind::AND);
287
    while (right.getKind() == kind::STORE) {
288
      tmp = nm->mkNode(kind::SELECT, l, right[1]);
289
      nb << tmp.eqNode(right[2]);
290
      tmp = nm->mkNode(kind::SELECT, right[0], right[1]);
291
      l = nm->mkNode(kind::STORE, l, right[1], tmp);
292
      right = right[0];
293
    }
294
    nb << solveWrite(l.eqNode(right), solve1, solve2, ppCheck);
295
    return nb;
296
  }
297
  Unreachable();
298
  return term;
299
}
300
301
22439
TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems)
302
{
303
  // first, see if we need to expand definitions
304
44878
  TrustNode texp = d_rewriter.expandDefinition(term);
305
22439
  if (!texp.isNull())
306
  {
307
20
    return texp;
308
  }
309
  if (!d_preprocess)
310
  {
311
    return TrustNode::null();
312
  }
313
22419
  d_ppEqualityEngine.addTerm(term);
314
22419
  NodeManager* nm = NodeManager::currentNM();
315
44838
  Node ret;
316
22419
  switch (term.getKind()) {
317
6382
    case kind::SELECT: {
318
      // select(store(a,i,v),j) = select(a,j)
319
      //    IF i != j
320
6382
      if (term[0].getKind() == kind::STORE && ppDisequal(term[0][1], term[1])) {
321
        ret = nm->mkNode(kind::SELECT, term[0][0], term[1]);
322
      }
323
6382
      break;
324
    }
325
1358
    case kind::STORE: {
326
      // store(store(a,i,v),j,w) = store(store(a,j,w),i,v)
327
      //    IF i != j and j comes before i in the ordering
328
1358
      if (term[0].getKind() == kind::STORE && (term[1] < term[0][1]) && ppDisequal(term[1],term[0][1])) {
329
        Node inner = nm->mkNode(kind::STORE, term[0][0], term[1], term[2]);
330
        Node outer = nm->mkNode(kind::STORE, inner, term[0][1], term[0][2]);
331
        ret = outer;
332
      }
333
1358
      break;
334
    }
335
1487
    case kind::EQUAL: {
336
1487
      ret = solveWrite(term, d_solveWrite, d_solveWrite2, true);
337
1487
      break;
338
    }
339
13192
    default:
340
13192
      break;
341
  }
342
22419
  if (!ret.isNull() && ret != term)
343
  {
344
48
    return TrustNode::mkTrustRewrite(term, ret, nullptr);
345
  }
346
22371
  return TrustNode::null();
347
}
348
349
228
Theory::PPAssertStatus TheoryArrays::ppAssert(
350
    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
351
{
352
456
  TNode in = tin.getNode();
353
228
  switch(in.getKind()) {
354
152
    case kind::EQUAL:
355
    {
356
152
      d_ppFacts.push_back(in);
357
152
      d_ppEqualityEngine.assertEquality(in, true, in);
358
152
      if (in[0].isVar() && isLegalElimination(in[0], in[1]))
359
      {
360
101
        outSubstitutions.addSubstitutionSolved(in[0], in[1], tin);
361
101
        return PP_ASSERT_STATUS_SOLVED;
362
      }
363
51
      if (in[1].isVar() && isLegalElimination(in[1], in[0]))
364
      {
365
        outSubstitutions.addSubstitutionSolved(in[1], in[0], tin);
366
        return PP_ASSERT_STATUS_SOLVED;
367
      }
368
51
      break;
369
    }
370
48
    case kind::NOT:
371
    {
372
48
      d_ppFacts.push_back(in);
373
48
      if (in[0].getKind() == kind::EQUAL ) {
374
96
        Node a = in[0][0];
375
96
        Node b = in[0][1];
376
48
        d_ppEqualityEngine.assertEquality(in[0], false, in);
377
      }
378
48
      break;
379
    }
380
28
    default:
381
28
      break;
382
  }
383
127
  return PP_ASSERT_STATUS_UNSOLVED;
384
}
385
386
387
/////////////////////////////////////////////////////////////////////////////
388
// T-PROPAGATION / REGISTRATION
389
/////////////////////////////////////////////////////////////////////////////
390
391
209502
bool TheoryArrays::propagateLit(TNode literal)
392
{
393
419004
  Debug("arrays") << spaces(getSatContext()->getLevel())
394
209502
                  << "TheoryArrays::propagateLit(" << literal << ")"
395
209502
                  << std::endl;
396
397
  // If already in conflict, no more propagation
398
209502
  if (d_state.isInConflict())
399
  {
400
    Debug("arrays") << spaces(getSatContext()->getLevel())
401
                    << "TheoryArrays::propagateLit(" << literal
402
                    << "): already in conflict" << std::endl;
403
    return false;
404
  }
405
406
  // Propagate away
407
209502
  if (d_inCheckModel && getSatContext()->getLevel() != d_topLevel) {
408
    return true;
409
  }
410
209502
  bool ok = d_out->propagate(literal);
411
209502
  if (!ok) {
412
1960
    d_state.notifyInConflict();
413
  }
414
209502
  return ok;
415
}/* TheoryArrays::propagate(TNode) */
416
417
418
TNode TheoryArrays::weakEquivGetRep(TNode node) {
419
  TNode pointer;
420
  while (true) {
421
    pointer = d_infoMap.getWeakEquivPointer(node);
422
    if (pointer.isNull()) {
423
      return node;
424
    }
425
    node = pointer;
426
  }
427
}
428
429
TNode TheoryArrays::weakEquivGetRepIndex(TNode node, TNode index) {
430
  Assert(!index.isNull());
431
  TNode pointer, index2;
432
  while (true) {
433
    pointer = d_infoMap.getWeakEquivPointer(node);
434
    if (pointer.isNull()) {
435
      return node;
436
    }
437
    index2 = d_infoMap.getWeakEquivIndex(node);
438
    if (index2.isNull() || !d_equalityEngine->areEqual(index, index2))
439
    {
440
      node = pointer;
441
    }
442
    else {
443
      TNode secondary = d_infoMap.getWeakEquivSecondary(node);
444
      if (secondary.isNull()) {
445
        return node;
446
      }
447
      node = secondary;
448
    }
449
  }
450
}
451
452
void TheoryArrays::visitAllLeaves(TNode reason, vector<TNode>& conjunctions) {
453
  switch (reason.getKind()) {
454
    case kind::AND:
455
      Assert(reason.getNumChildren() == 2);
456
      visitAllLeaves(reason[0], conjunctions);
457
      visitAllLeaves(reason[1], conjunctions);
458
      break;
459
    case kind::NOT:
460
      conjunctions.push_back(reason);
461
      break;
462
    case kind::EQUAL:
463
      d_equalityEngine->explainEquality(
464
          reason[0], reason[1], true, conjunctions);
465
      break;
466
    default:
467
      Unreachable();
468
  }
469
}
470
471
void TheoryArrays::weakEquivBuildCond(TNode node, TNode index, vector<TNode>& conjunctions) {
472
  Assert(!index.isNull());
473
  TNode pointer, index2;
474
  while (true) {
475
    pointer = d_infoMap.getWeakEquivPointer(node);
476
    if (pointer.isNull()) {
477
      return;
478
    }
479
    index2 = d_infoMap.getWeakEquivIndex(node);
480
    if (index2.isNull()) {
481
      // Null index means these two nodes became equal: explain the equality.
482
      d_equalityEngine->explainEquality(node, pointer, true, conjunctions);
483
      node = pointer;
484
    }
485
    else if (!d_equalityEngine->areEqual(index, index2))
486
    {
487
      // If indices are not equal in current context, need to add that to the lemma.
488
      Node reason = index.eqNode(index2).notNode();
489
      d_permRef.push_back(reason);
490
      conjunctions.push_back(reason);
491
      node = pointer;
492
    }
493
    else {
494
      TNode secondary = d_infoMap.getWeakEquivSecondary(node);
495
      if (secondary.isNull()) {
496
        return;
497
      }
498
      TNode reason = d_infoMap.getWeakEquivSecondaryReason(node);
499
      Assert(!reason.isNull());
500
      visitAllLeaves(reason, conjunctions);
501
      node = secondary;
502
    }
503
  }
504
}
505
506
void TheoryArrays::weakEquivMakeRep(TNode node) {
507
  TNode pointer = d_infoMap.getWeakEquivPointer(node);
508
  if (pointer.isNull()) {
509
    return;
510
  }
511
  weakEquivMakeRep(pointer);
512
  d_infoMap.setWeakEquivPointer(pointer, node);
513
  d_infoMap.setWeakEquivIndex(pointer, d_infoMap.getWeakEquivIndex(node));
514
  d_infoMap.setWeakEquivPointer(node, TNode());
515
  weakEquivMakeRepIndex(node);
516
}
517
518
void TheoryArrays::weakEquivMakeRepIndex(TNode node) {
519
  TNode secondary = d_infoMap.getWeakEquivSecondary(node);
520
  if (secondary.isNull()) {
521
    return;
522
  }
523
  TNode index = d_infoMap.getWeakEquivIndex(node);
524
  Assert(!index.isNull());
525
  TNode index2 = d_infoMap.getWeakEquivIndex(secondary);
526
  Node reason;
527
  TNode next;
528
  while (index2.isNull() || !d_equalityEngine->areEqual(index, index2))
529
  {
530
    next = d_infoMap.getWeakEquivPointer(secondary);
531
    d_infoMap.setWeakEquivSecondary(node, next);
532
    reason = d_infoMap.getWeakEquivSecondaryReason(node);
533
    if (index2.isNull()) {
534
      reason = reason.andNode(secondary.eqNode(next));
535
    }
536
    else {
537
      reason = reason.andNode(index.eqNode(index2).notNode());
538
    }
539
    d_permRef.push_back(reason);
540
    d_infoMap.setWeakEquivSecondaryReason(node, reason);
541
    if (next.isNull()) {
542
      return;
543
    }
544
    secondary = next;
545
    index2 = d_infoMap.getWeakEquivIndex(secondary);
546
  }
547
  weakEquivMakeRepIndex(secondary);
548
  d_infoMap.setWeakEquivSecondary(secondary, node);
549
  d_infoMap.setWeakEquivSecondaryReason(secondary, d_infoMap.getWeakEquivSecondaryReason(node));
550
  d_infoMap.setWeakEquivSecondary(node, TNode());
551
  d_infoMap.setWeakEquivSecondaryReason(node, TNode());
552
}
553
554
void TheoryArrays::weakEquivAddSecondary(TNode index, TNode arrayFrom, TNode arrayTo, TNode reason) {
555
  std::unordered_set<TNode> marked;
556
  vector<TNode> index_trail;
557
  vector<TNode>::iterator it, iend;
558
  Node equivalence_trail = reason;
559
  Node current_reason;
560
  TNode pointer, indexRep;
561
  if (!index.isNull()) {
562
    index_trail.push_back(index);
563
    marked.insert(d_equalityEngine->getRepresentative(index));
564
  }
565
  while (arrayFrom != arrayTo) {
566
    index = d_infoMap.getWeakEquivIndex(arrayFrom);
567
    pointer = d_infoMap.getWeakEquivPointer(arrayFrom);
568
    if (!index.isNull()) {
569
      indexRep = d_equalityEngine->getRepresentative(index);
570
      if (marked.find(indexRep) == marked.end() && weakEquivGetRepIndex(arrayFrom, index) != arrayTo) {
571
        weakEquivMakeRepIndex(arrayFrom);
572
        d_infoMap.setWeakEquivSecondary(arrayFrom, arrayTo);
573
        current_reason = equivalence_trail;
574
        for (it = index_trail.begin(), iend = index_trail.end(); it != iend; ++it) {
575
          current_reason = current_reason.andNode(index.eqNode(*it).notNode());
576
        }
577
        d_permRef.push_back(current_reason);
578
        d_infoMap.setWeakEquivSecondaryReason(arrayFrom, current_reason);
579
      }
580
      marked.insert(indexRep);
581
    }
582
    else {
583
      equivalence_trail = equivalence_trail.andNode(arrayFrom.eqNode(pointer));
584
    }
585
    arrayFrom = pointer;
586
  }
587
}
588
589
void TheoryArrays::checkWeakEquiv(bool arraysMerged) {
590
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_mayEqualEqualityEngine);
591
  for (; !eqcs_i.isFinished(); ++eqcs_i) {
592
    Node eqc = (*eqcs_i);
593
    if (!eqc.getType().isArray()) {
594
      continue;
595
    }
596
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &d_mayEqualEqualityEngine);
597
    TNode rep = d_mayEqualEqualityEngine.getRepresentative(*eqc_i);
598
    TNode weakEquivRep = weakEquivGetRep(rep);
599
    for (; !eqc_i.isFinished(); ++eqc_i) {
600
      TNode n = *eqc_i;
601
      Assert(!arraysMerged || weakEquivGetRep(n) == weakEquivRep);
602
      TNode pointer = d_infoMap.getWeakEquivPointer(n);
603
      TNode index = d_infoMap.getWeakEquivIndex(n);
604
      TNode secondary = d_infoMap.getWeakEquivSecondary(n);
605
      Assert(pointer.isNull() == (weakEquivGetRep(n) == n));
606
      Assert(!pointer.isNull() || secondary.isNull());
607
      Assert(!index.isNull() || secondary.isNull());
608
      Assert(d_infoMap.getWeakEquivSecondaryReason(n).isNull()
609
             || !secondary.isNull());
610
      if (!pointer.isNull()) {
611
        if (index.isNull()) {
612
          Assert(d_equalityEngine->areEqual(n, pointer));
613
        }
614
        else {
615
          Assert(
616
              (n.getKind() == kind::STORE && n[0] == pointer && n[1] == index)
617
              || (pointer.getKind() == kind::STORE && pointer[0] == n
618
                  && pointer[1] == index));
619
        }
620
      }
621
    }
622
  }
623
}
624
625
/**
626
 * Stores in d_infoMap the following information for each term a of type array:
627
 *
628
 *    - all i, such that there exists a term a[i] or a = store(b i v)
629
 *      (i.e. all indices it is being read atl; store(b i v) is implicitly read at
630
 *      position i due to the implicit axiom store(b i v)[i] = v )
631
 *
632
 *    - all the stores a is congruent to (this information is context dependent)
633
 *
634
 *    - all store terms of the form store (a i v) (i.e. in which a appears
635
 *      directly; this is invariant because no new store terms are created)
636
 *
637
 * Note: completeness depends on having pre-register called on all the input
638
 *       terms before starting to instantiate lemmas.
639
 */
640
97057
void TheoryArrays::preRegisterTermInternal(TNode node)
641
{
642
97057
  if (d_state.isInConflict())
643
  {
644
64630
    return;
645
  }
646
193992
  Debug("arrays") << spaces(getSatContext()->getLevel())
647
96996
                  << "TheoryArrays::preRegisterTerm(" << node << ")"
648
96996
                  << std::endl;
649
96996
  Kind nk = node.getKind();
650
96996
  if (nk == kind::EQUAL)
651
  {
652
    // Add the trigger for equality
653
    // NOTE: note that if the equality is true or false already, it might not be added
654
13421
    d_equalityEngine->addTriggerPredicate(node);
655
13421
    return;
656
  }
657
  // add to equality engine and the may equality engine
658
116063
  TypeNode nodeType = node.getType();
659
83575
  if (nodeType.isArray())
660
  {
661
    // index type should not be an array, otherwise we throw a logic exception
662
6949
    if (nodeType.getArrayIndexType().isArray())
663
    {
664
      std::stringstream ss;
665
      ss << "Arrays cannot be indexed by array types, offending array type is "
666
         << nodeType;
667
      throw LogicException(ss.str());
668
    }
669
6949
    d_mayEqualEqualityEngine.addTerm(node);
670
  }
671
83575
  if (d_equalityEngine->hasTerm(node))
672
  {
673
    // Notice that array terms may be added to its equality engine before
674
    // being preregistered in the central equality engine architecture.
675
    // Prior to this, an assertion in this case was:
676
    // Assert(nk != kind::SELECT
677
    //         || d_isPreRegistered.find(node) != d_isPreRegistered.end());
678
51087
    return;
679
  }
680
32488
  d_equalityEngine->addTerm(node);
681
682
32488
  switch (node.getKind())
683
  {
684
24028
    case kind::SELECT:
685
    {
686
      // Reads
687
48056
      TNode store = d_equalityEngine->getRepresentative(node[0]);
688
689
      // The may equal needs the store
690
24028
      d_mayEqualEqualityEngine.addTerm(store);
691
692
24028
      Assert((d_isPreRegistered.insert(node), true));
693
694
24028
      Assert(d_equalityEngine->getRepresentative(store) == store);
695
24028
      d_infoMap.addIndex(store, node[1]);
696
697
      // Synchronize d_constReadsContext with SAT context
698
24028
      Assert(d_constReadsContext->getLevel() <= getSatContext()->getLevel());
699
61924
      while (d_constReadsContext->getLevel() < getSatContext()->getLevel())
700
      {
701
18948
        d_constReadsContext->push();
702
      }
703
704
      // Record read in sharing data structure
705
48056
      TNode index = d_equalityEngine->getRepresentative(node[1]);
706
24028
      if (!options::arraysWeakEquivalence() && index.isConst())
707
      {
708
        CTNodeList* temp;
709
4594
        CNodeNListMap::iterator it = d_constReads.find(index);
710
4594
        if (it == d_constReads.end())
711
        {
712
510
          temp = new (true) CTNodeList(d_constReadsContext);
713
510
          d_constReads[index] = temp;
714
        }
715
        else
716
        {
717
4084
          temp = (*it).second;
718
        }
719
4594
        temp->push_back(node);
720
4594
        d_constReadsList.push_back(node);
721
      }
722
      else
723
      {
724
19434
        d_reads.push_back(node);
725
      }
726
727
24028
      checkRowForIndex(node[1], store);
728
24028
      break;
729
    }
730
1112
    case kind::STORE:
731
    {
732
2224
      TNode a = d_equalityEngine->getRepresentative(node[0]);
733
734
1112
      if (node.isConst())
735
      {
736
        // Can't use d_mayEqualEqualityEngine to merge node with a because they
737
        // are both constants, so just set the default value manually for node.
738
22
        Assert(a == node[0]);
739
22
        d_mayEqualEqualityEngine.addTerm(node);
740
22
        Assert(d_mayEqualEqualityEngine.getRepresentative(node) == node);
741
22
        Assert(d_mayEqualEqualityEngine.getRepresentative(a) == a);
742
22
        DefValMap::iterator it = d_defValues.find(a);
743
22
        Assert(it != d_defValues.end());
744
22
        d_defValues[node] = (*it).second;
745
      }
746
      else
747
      {
748
1090
        d_mayEqualEqualityEngine.assertEquality(node.eqNode(a), true, d_true);
749
1090
        Assert(d_mayEqualEqualityEngine.consistent());
750
      }
751
752
2224
      TNode i = node[1];
753
2224
      TNode v = node[2];
754
1112
      NodeManager* nm = NodeManager::currentNM();
755
2224
      Node ni = nm->mkNode(kind::SELECT, node, i);
756
1112
      if (!d_equalityEngine->hasTerm(ni))
757
      {
758
1112
        preRegisterTermInternal(ni);
759
      }
760
      // Apply RIntro1 Rule
761
1112
      d_im.assertInference(ni.eqNode(v),
762
                           true,
763
                           InferenceId::ARRAYS_READ_OVER_WRITE_1,
764
                           d_true,
765
                           PfRule::ARRAYS_READ_OVER_WRITE_1);
766
767
1112
      d_infoMap.addStore(node, node);
768
1112
      d_infoMap.addInStore(a, node);
769
1112
      d_infoMap.setModelRep(node, node);
770
771
      // Add-Store for Weak Equivalence
772
1112
      if (options::arraysWeakEquivalence())
773
      {
774
        Assert(weakEquivGetRep(node[0]) == weakEquivGetRep(a));
775
        Assert(weakEquivGetRep(node) == node);
776
        d_infoMap.setWeakEquivPointer(node, node[0]);
777
        d_infoMap.setWeakEquivIndex(node, node[1]);
778
#ifdef CVC5_ASSERTIONS
779
        checkWeakEquiv(false);
780
#endif
781
    }
782
783
1112
    checkStore(node);
784
1112
    break;
785
  }
786
66
  case kind::STORE_ALL: {
787
132
    ArrayStoreAll storeAll = node.getConst<ArrayStoreAll>();
788
132
    Node defaultValue = storeAll.getValue();
789
66
    if (!defaultValue.isConst()) {
790
      throw LogicException("Array theory solver does not yet support non-constant default values for arrays");
791
    }
792
66
    d_infoMap.setConstArr(node, node);
793
66
    Assert(d_mayEqualEqualityEngine.getRepresentative(node) == node);
794
66
    d_defValues[node] = defaultValue;
795
66
    break;
796
  }
797
7282
  default:
798
    // Variables etc, already processed above
799
7282
    break;
800
  }
801
  // Invariant: preregistered terms are exactly the terms in the equality engine
802
  // Disabled, see comment above for kind::EQUAL
803
  // Assert(d_equalityEngine->hasTerm(node) ||
804
  // !d_equalityEngine->consistent());
805
}
806
807
38651
void TheoryArrays::preRegisterTerm(TNode node)
808
{
809
38651
  preRegisterTermInternal(node);
810
  // If we have a select from an array of Bools, mark it as a term that can be propagated.
811
  // Note: do this here instead of in preRegisterTermInternal to prevent internal select
812
  // terms from being propagated out (as this results in an assertion failure).
813
38651
  if (node.getKind() == kind::SELECT && node.getType().isBoolean()) {
814
97
    d_equalityEngine->addTriggerPredicate(node);
815
  }
816
38651
}
817
818
void TheoryArrays::explain(TNode literal, Node& explanation)
819
{
820
  ++d_numExplain;
821
  Debug("arrays") << spaces(getSatContext()->getLevel())
822
                  << "TheoryArrays::explain(" << literal << ")" << std::endl;
823
  std::vector<TNode> assumptions;
824
  // Do the work
825
  bool polarity = literal.getKind() != kind::NOT;
826
  TNode atom = polarity ? literal : literal[0];
827
  if (atom.getKind() == kind::EQUAL)
828
  {
829
    d_equalityEngine->explainEquality(
830
        atom[0], atom[1], polarity, assumptions, nullptr);
831
  }
832
  else
833
  {
834
    d_equalityEngine->explainPredicate(atom, polarity, assumptions, nullptr);
835
  }
836
  explanation = mkAnd(assumptions);
837
}
838
839
12769
TrustNode TheoryArrays::explain(TNode literal)
840
{
841
12769
  return d_im.explainLit(literal);
842
}
843
844
/////////////////////////////////////////////////////////////////////////////
845
// SHARING
846
/////////////////////////////////////////////////////////////////////////////
847
848
87222
void TheoryArrays::notifySharedTerm(TNode t)
849
{
850
174444
  Debug("arrays::sharing") << spaces(getSatContext()->getLevel())
851
87222
                           << "TheoryArrays::notifySharedTerm(" << t << ")"
852
87222
                           << std::endl;
853
87222
  if (t.getType().isArray()) {
854
2490
    d_sharedArrays.insert(t);
855
  }
856
  else {
857
#ifdef CVC5_ASSERTIONS
858
84732
    d_sharedOther.insert(t);
859
#endif
860
84732
    d_sharedTerms = true;
861
  }
862
87222
}
863
864
267682
void TheoryArrays::checkPair(TNode r1, TNode r2)
865
{
866
267682
  Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1 << " and " << r2 << std::endl;
867
868
269547
  TNode x = r1[1];
869
269547
  TNode y = r2[1];
870
267682
  Assert(d_equalityEngine->isTriggerTerm(x, THEORY_ARRAYS));
871
872
1070728
  if (d_equalityEngine->hasTerm(x) && d_equalityEngine->hasTerm(y)
873
1573627
      && (d_equalityEngine->areEqual(x, y)
874
310398
          || d_equalityEngine->areDisequal(x, y, false)))
875
  {
876
235217
    Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl;
877
235217
    return;
878
  }
879
880
  // If the terms are already known to be equal, we are also in good shape
881
32465
  if (d_equalityEngine->areEqual(r1, r2))
882
  {
883
1942
    Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl;
884
1942
    return;
885
  }
886
887
30523
  if (r1[0] != r2[0]) {
888
    // If arrays are known to be disequal, or cannot become equal, we can continue
889
26383
    Assert(d_mayEqualEqualityEngine.hasTerm(r1[0])
890
           && d_mayEqualEqualityEngine.hasTerm(r2[0]));
891
105532
    if (r1[0].getType() != r2[0].getType()
892
105532
        || d_equalityEngine->areDisequal(r1[0], r2[0], false))
893
    {
894
4001
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl;
895
4001
      return;
896
    }
897
22382
    else if (!d_mayEqualEqualityEngine.areEqual(r1[0], r2[0])) {
898
69
      return;
899
    }
900
  }
901
902
26453
  if (!d_equalityEngine->isTriggerTerm(y, THEORY_ARRAYS))
903
  {
904
38
    Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
905
38
    return;
906
  }
907
908
  // Get representative trigger terms
909
  TNode x_shared =
910
28280
      d_equalityEngine->getTriggerTermRepresentative(x, THEORY_ARRAYS);
911
  TNode y_shared =
912
28280
      d_equalityEngine->getTriggerTermRepresentative(y, THEORY_ARRAYS);
913
26415
  EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared);
914
26415
  switch (eqStatusDomain) {
915
    case EQUALITY_TRUE_AND_PROPAGATED:
916
      // Should have been propagated to us
917
      Assert(false);
918
      break;
919
    case EQUALITY_TRUE:
920
      // Missed propagation - need to add the pair so that theory engine can force propagation
921
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): missed propagation" << std::endl;
922
      break;
923
    case EQUALITY_FALSE_AND_PROPAGATED:
924
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair "
925
                                  "called when false in model"
926
                               << std::endl;
927
      // Should have been propagated to us
928
      Assert(false);
929
      break;
930
24550
    case EQUALITY_FALSE: CVC5_FALLTHROUGH;
931
    case EQUALITY_FALSE_IN_MODEL:
932
      // This is unlikely, but I think it could happen
933
24550
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checkPair called when false in model" << std::endl;
934
24550
      return;
935
1865
    default:
936
      // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
937
1865
      break;
938
  }
939
940
  // Add this pair
941
1865
  Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl;
942
1865
  addCarePair(x_shared, y_shared);
943
}
944
945
946
7540
void TheoryArrays::computeCareGraph()
947
{
948
7540
  if (d_sharedArrays.size() > 0) {
949
378
    CDNodeSet::key_iterator it1 = d_sharedArrays.key_begin(), it2, iend = d_sharedArrays.key_end();
950
3842
    for (; it1 != iend; ++it1) {
951
15892
      for (it2 = it1, ++it2; it2 != iend; ++it2) {
952
14160
        if ((*it1).getType() != (*it2).getType()) {
953
4455
          continue;
954
        }
955
9705
        EqualityStatus eqStatusArr = getEqualityStatus((*it1), (*it2));
956
9705
        if (eqStatusArr != EQUALITY_UNKNOWN) {
957
9581
          continue;
958
        }
959
124
        Assert(d_valuation.getEqualityStatus((*it1), (*it2))
960
               == EQUALITY_UNKNOWN);
961
124
        addCarePair((*it1), (*it2));
962
124
        ++d_numSharedArrayVarSplits;
963
248
        return;
964
      }
965
    }
966
  }
967
7416
  if (d_sharedTerms) {
968
    // Synchronize d_constReadsContext with SAT context
969
544
    Assert(d_constReadsContext->getLevel() <= getSatContext()->getLevel());
970
9008
    while (d_constReadsContext->getLevel() < getSatContext()->getLevel()) {
971
4232
      d_constReadsContext->push();
972
    }
973
974
    // Go through the read terms and see if there are any to split on
975
976
    // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
977
    // The context is popped at the end.  If this loop is interrupted for some reason, we have to make sure the context still
978
    // gets popped or the solver will be in an inconsistent state
979
544
    d_constReadsContext->push();
980
544
    unsigned size = d_reads.size();
981
6820
    for (unsigned i = 0; i < size; ++ i) {
982
12337
      TNode r1 = d_reads[i];
983
984
6276
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1 << std::endl;
985
6276
      Assert(d_equalityEngine->hasTerm(r1));
986
12337
      TNode x = r1[1];
987
988
6491
      if (!d_equalityEngine->isTriggerTerm(x, THEORY_ARRAYS))
989
      {
990
215
        Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
991
215
        continue;
992
      }
993
      Node x_shared =
994
12122
          d_equalityEngine->getTriggerTermRepresentative(x, THEORY_ARRAYS);
995
996
      // 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
997
      // Also, insert this read in the list at the proper index
998
999
6061
      if (!x_shared.isConst()) {
1000
2737
        x_shared = d_valuation.getModelValue(x_shared);
1001
      }
1002
6061
      if (!x_shared.isNull()) {
1003
        CTNodeList* temp;
1004
4423
        CNodeNListMap::iterator it = d_constReads.find(x_shared);
1005
4423
        if (it == d_constReads.end()) {
1006
          // This is the only x_shared with this model value - no need to create any splits
1007
197
          temp = new(true) CTNodeList(d_constReadsContext);
1008
197
          d_constReads[x_shared] = temp;
1009
        }
1010
        else {
1011
4226
          temp = (*it).second;
1012
207906
          for (size_t j = 0; j < temp->size(); ++j) {
1013
203680
            checkPair(r1, (*temp)[j]);
1014
          }
1015
        }
1016
4423
        temp->push_back(r1);
1017
      }
1018
      else {
1019
        // We don't know the model value for x.  Just do brute force examination of all pairs of reads
1020
57527
        for (unsigned j = 0; j < size; ++j) {
1021
111778
          TNode r2 = d_reads[j];
1022
55889
          Assert(d_equalityEngine->hasTerm(r2));
1023
55889
          checkPair(r1,r2);
1024
        }
1025
9751
        for (unsigned j = 0; j < d_constReadsList.size(); ++j) {
1026
16226
          TNode r2 = d_constReadsList[j];
1027
8113
          Assert(d_equalityEngine->hasTerm(r2));
1028
8113
          checkPair(r1,r2);
1029
        }
1030
      }
1031
    }
1032
544
    d_constReadsContext->pop();
1033
  }
1034
}
1035
1036
1037
/////////////////////////////////////////////////////////////////////////////
1038
// MODEL GENERATION
1039
/////////////////////////////////////////////////////////////////////////////
1040
1041
5311
bool TheoryArrays::collectModelValues(TheoryModel* m,
1042
                                      const std::set<Node>& termSet)
1043
{
1044
  // termSet contains terms appearing in assertions and shared terms, and also
1045
  // includes additional reads due to the RIntro1 and RIntro2 rules.
1046
5311
  NodeManager* nm = NodeManager::currentNM();
1047
  // Compute arrays that we need to produce representatives for
1048
10622
  std::vector<Node> arrays;
1049
1050
5311
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
1051
31577
  for (; !eqcs_i.isFinished(); ++eqcs_i)
1052
  {
1053
14039
    Node eqc = (*eqcs_i);
1054
13133
    if (!eqc.getType().isArray())
1055
    {
1056
      // not an array, skip
1057
12227
      continue;
1058
    }
1059
906
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
1060
1578
    for (; !eqc_i.isFinished(); ++eqc_i)
1061
    {
1062
1287
      Node n = *eqc_i;
1063
      // If this EC is an array type and it contains something other than STORE
1064
      // nodes, we have to compute a representative explicitly
1065
951
      if (termSet.find(n) != termSet.end())
1066
      {
1067
910
        if (n.getKind() != kind::STORE)
1068
        {
1069
615
          arrays.push_back(n);
1070
615
          break;
1071
        }
1072
      }
1073
    }
1074
  }
1075
1076
  // Build a list of all the relevant reads, indexed by the store representative
1077
10622
  std::map<Node, std::vector<Node> > selects;
1078
5311
  set<Node>::iterator set_it = termSet.begin(), set_it_end = termSet.end();
1079
20057
  for (; set_it != set_it_end; ++set_it)
1080
  {
1081
14746
    Node n = *set_it;
1082
    // If this term is a select, record that the EC rep of its store parameter
1083
    // is being read from using this term
1084
7373
    if (n.getKind() == kind::SELECT)
1085
    {
1086
3870
      selects[d_equalityEngine->getRepresentative(n[0])].push_back(n);
1087
    }
1088
  }
1089
1090
10622
  Node rep;
1091
5311
  DefValMap::iterator it;
1092
10622
  TypeSet defaultValuesSet;
1093
1094
  // Compute all default values already in use
1095
  // if (fullModel) {
1096
5926
  for (size_t i = 0; i < arrays.size(); ++i)
1097
  {
1098
1230
    TNode nrep = d_equalityEngine->getRepresentative(arrays[i]);
1099
615
    d_mayEqualEqualityEngine.addTerm(
1100
        nrep);  // add the term in case it isn't there already
1101
1230
    TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1102
615
    it = d_defValues.find(mayRep);
1103
615
    if (it != d_defValues.end())
1104
    {
1105
115
      defaultValuesSet.add(nrep.getType().getArrayConstituentType(),
1106
115
                           (*it).second);
1107
    }
1108
  }
1109
  //}
1110
1111
  // Loop through all array equivalence classes that need a representative
1112
  // computed
1113
5926
  for (size_t i = 0; i < arrays.size(); ++i)
1114
  {
1115
1230
    TNode n = arrays[i];
1116
1230
    TNode nrep = d_equalityEngine->getRepresentative(n);
1117
1118
    // if (fullModel) {
1119
    // Compute default value for this array - there is one default value for
1120
    // every mayEqual equivalence class
1121
1230
    TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1122
615
    it = d_defValues.find(mayRep);
1123
    // If this mayEqual EC doesn't have a default value associated, get the next
1124
    // available default value for the associated array element type
1125
615
    if (it == d_defValues.end())
1126
    {
1127
856
      TypeNode valueType = nrep.getType().getArrayConstituentType();
1128
428
      rep = defaultValuesSet.nextTypeEnum(valueType);
1129
428
      if (rep.isNull())
1130
      {
1131
        Assert(defaultValuesSet.getSet(valueType)->begin()
1132
               != defaultValuesSet.getSet(valueType)->end());
1133
        rep = *(defaultValuesSet.getSet(valueType)->begin());
1134
      }
1135
428
      Trace("arrays-models") << "New default value = " << rep << endl;
1136
428
      d_defValues[mayRep] = rep;
1137
    }
1138
    else
1139
    {
1140
187
      rep = (*it).second;
1141
    }
1142
1143
    // Build the STORE_ALL term with the default value
1144
615
    rep = nm->mkConst(ArrayStoreAll(nrep.getType(), rep));
1145
    /*
1146
  }
1147
  else {
1148
    std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(n);
1149
    if (it == d_skolemCache.end()) {
1150
      rep = nm->mkSkolem("array_collect_model_var", n.getType(), "base model
1151
  variable for array collectModelInfo"); d_skolemCache[n] = rep;
1152
    }
1153
    else {
1154
      rep = (*it).second;
1155
    }
1156
  }
1157
*/
1158
1159
    // For each read, require that the rep stores the right value
1160
615
    vector<Node>& reads = selects[nrep];
1161
3107
    for (unsigned j = 0; j < reads.size(); ++j)
1162
    {
1163
2492
      rep = nm->mkNode(kind::STORE, rep, reads[j][1], reads[j]);
1164
    }
1165
615
    if (!m->assertEquality(n, rep, true))
1166
    {
1167
      return false;
1168
    }
1169
615
    if (!n.isConst())
1170
    {
1171
550
      m->assertSkeleton(rep);
1172
    }
1173
  }
1174
5311
  return true;
1175
}
1176
1177
/////////////////////////////////////////////////////////////////////////////
1178
// NOTIFICATIONS
1179
/////////////////////////////////////////////////////////////////////////////
1180
1181
1182
15201
void TheoryArrays::presolve()
1183
{
1184
15201
  Trace("arrays")<<"Presolving \n";
1185
15201
  if (!d_dstratInit)
1186
  {
1187
9401
    d_dstratInit = true;
1188
    // add the decision strategy, which is user-context-independent
1189
18802
    d_im.getDecisionManager()->registerStrategy(
1190
        DecisionManager::STRAT_ARRAYS,
1191
9401
        d_dstrat.get(),
1192
        DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT);
1193
  }
1194
15201
}
1195
1196
1197
/////////////////////////////////////////////////////////////////////////////
1198
// MAIN SOLVER
1199
/////////////////////////////////////////////////////////////////////////////
1200
1201
3045
Node TheoryArrays::getSkolem(TNode ref)
1202
{
1203
  // the call to SkolemCache::getExtIndexSkolem should be deterministic, but use
1204
  // cache anyways for now
1205
3045
  Node skolem;
1206
3045
  std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(ref);
1207
3045
  if (it == d_skolemCache.end()) {
1208
574
    Assert(ref.getKind() == kind::NOT && ref[0].getKind() == kind::EQUAL);
1209
    // make the skolem using the skolem cache utility
1210
574
    skolem = SkolemCache::getExtIndexSkolem(ref);
1211
574
    d_skolemCache[ref] = skolem;
1212
  }
1213
  else {
1214
2471
    skolem = (*it).second;
1215
  }
1216
1217
3045
  Debug("pf::array") << "Pregistering a Skolem" << std::endl;
1218
3045
  preRegisterTermInternal(skolem);
1219
3045
  Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl;
1220
1221
3045
  Debug("pf::array") << "getSkolem DONE" << std::endl;
1222
3045
  return skolem;
1223
}
1224
1225
93768
void TheoryArrays::postCheck(Effort level)
1226
{
1227
281304
  if ((options::arraysEagerLemmas() || fullEffort(level))
1228
121190
      && !d_state.isInConflict() && options::arraysWeakEquivalence())
1229
  {
1230
    // Replay all array merges to update weak equivalence data structures
1231
    context::CDList<Node>::iterator it = d_arrayMerges.begin(), iend = d_arrayMerges.end();
1232
    TNode a, b, eq;
1233
    for (; it != iend; ++it) {
1234
      eq = *it;
1235
      a = eq[0];
1236
      b = eq[1];
1237
      weakEquivMakeRep(b);
1238
      if (weakEquivGetRep(a) == b) {
1239
        weakEquivAddSecondary(TNode(), a, b, eq);
1240
      }
1241
      else {
1242
        d_infoMap.setWeakEquivPointer(b, a);
1243
        d_infoMap.setWeakEquivIndex(b, TNode());
1244
      }
1245
#ifdef CVC5_ASSERTIONS
1246
      checkWeakEquiv(false);
1247
#endif
1248
    }
1249
#ifdef CVC5_ASSERTIONS
1250
    checkWeakEquiv(true);
1251
#endif
1252
1253
    d_readTableContext->push();
1254
    TNode mayRep, iRep;
1255
    CTNodeList* bucketList = NULL;
1256
    CTNodeList::const_iterator i = d_reads.begin(), readsEnd = d_reads.end();
1257
    for (; i != readsEnd; ++i) {
1258
      const TNode& r = *i;
1259
1260
      Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r << std::endl;
1261
1262
      // Find the bucket for this read.
1263
      mayRep = d_mayEqualEqualityEngine.getRepresentative(r[0]);
1264
      iRep = d_equalityEngine->getRepresentative(r[1]);
1265
      std::pair<TNode, TNode> key(mayRep, iRep);
1266
      ReadBucketMap::iterator rbm_it = d_readBucketTable.find(key);
1267
      if (rbm_it == d_readBucketTable.end())
1268
      {
1269
        bucketList = new(true) CTNodeList(d_readTableContext);
1270
        d_readBucketAllocations.push_back(bucketList);
1271
        d_readBucketTable[key] = bucketList;
1272
      }
1273
      else {
1274
        bucketList = rbm_it->second;
1275
      }
1276
      CTNodeList::const_iterator ctnl_it = bucketList->begin(),
1277
                                 ctnl_iend = bucketList->end();
1278
      for (; ctnl_it != ctnl_iend; ++ctnl_it)
1279
      {
1280
        const TNode& r2 = *ctnl_it;
1281
        Assert(r2.getKind() == kind::SELECT);
1282
        Assert(mayRep == d_mayEqualEqualityEngine.getRepresentative(r2[0]));
1283
        Assert(iRep == d_equalityEngine->getRepresentative(r2[1]));
1284
        if (d_equalityEngine->areEqual(r, r2))
1285
        {
1286
          continue;
1287
        }
1288
        if (weakEquivGetRepIndex(r[0], r[1]) == weakEquivGetRepIndex(r2[0], r[1])) {
1289
          // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1290
          vector<TNode> conjunctions;
1291
          Assert(d_equalityEngine->areEqual(r, Rewriter::rewrite(r)));
1292
          Assert(d_equalityEngine->areEqual(r2, Rewriter::rewrite(r2)));
1293
          Node lemma = Rewriter::rewrite(r).eqNode(Rewriter::rewrite(r2)).negate();
1294
          d_permRef.push_back(lemma);
1295
          conjunctions.push_back(lemma);
1296
          if (r[1] != r2[1]) {
1297
            d_equalityEngine->explainEquality(r[1], r2[1], true, conjunctions);
1298
          }
1299
          // TODO: get smaller lemmas by eliminating shared parts of path
1300
          weakEquivBuildCond(r[0], r[1], conjunctions);
1301
          weakEquivBuildCond(r2[0], r[1], conjunctions);
1302
          lemma = mkAnd(conjunctions, true);
1303
          // LSH FIXME: which kind of arrays lemma is this
1304
          Trace("arrays-lem")
1305
              << "Arrays::addExtLemma (weak-eq) " << lemma << "\n";
1306
          d_out->lemma(lemma, LemmaProperty::SEND_ATOMS);
1307
          d_readTableContext->pop();
1308
          Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl;
1309
          return;
1310
        }
1311
      }
1312
      bucketList->push_back(r);
1313
    }
1314
    d_readTableContext->pop();
1315
  }
1316
1317
281304
  if (!options::arraysEagerLemmas() && fullEffort(level)
1318
121190
      && !d_state.isInConflict() && !options::arraysWeakEquivalence())
1319
  {
1320
    // generate the lemmas on the worklist
1321
27404
    Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue.size() << "\n";
1322
28004
    while (d_RowQueue.size() > 0 && !d_state.isInConflict())
1323
    {
1324
1049
      if (dischargeLemmas()) {
1325
749
        break;
1326
      }
1327
    }
1328
  }
1329
1330
93768
  Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl;
1331
}
1332
1333
217542
bool TheoryArrays::preNotifyFact(
1334
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
1335
{
1336
217542
  if (!isInternal && !isPrereg)
1337
  {
1338
168722
    if (atom.getKind() == kind::EQUAL)
1339
    {
1340
168660
      if (!d_equalityEngine->hasTerm(atom[0]))
1341
      {
1342
1683
        Assert(atom[0].isConst());
1343
1683
        d_equalityEngine->addTerm(atom[0]);
1344
      }
1345
168660
      if (!d_equalityEngine->hasTerm(atom[1]))
1346
      {
1347
543
        Assert(atom[1].isConst());
1348
543
        d_equalityEngine->addTerm(atom[1]);
1349
      }
1350
    }
1351
  }
1352
217542
  return false;
1353
}
1354
1355
217539
void TheoryArrays::notifyFact(TNode atom, bool pol, TNode fact, bool isInternal)
1356
{
1357
  // if a disequality
1358
217539
  if (atom.getKind() == kind::EQUAL && !pol && !isInternal)
1359
  {
1360
    // Notice that this should be an external assertion, since we do not
1361
    // internally infer disequalities.
1362
    // Apply ArrDiseq Rule if diseq is between arrays
1363
43219
    if (fact[0][0].getType().isArray() && !d_state.isInConflict())
1364
    {
1365
3045
      NodeManager* nm = NodeManager::currentNM();
1366
1367
6090
      TNode k;
1368
      // k is the skolem for this disequality.
1369
6090
      Debug("pf::array") << "Check: kind::NOT: array theory making a skolem"
1370
3045
                          << std::endl;
1371
1372
      // If not in replay mode, generate a fresh skolem variable
1373
3045
      k = getSkolem(fact);
1374
1375
6090
      Node ak = nm->mkNode(kind::SELECT, fact[0][0], k);
1376
6090
      Node bk = nm->mkNode(kind::SELECT, fact[0][1], k);
1377
6090
      Node eq = ak.eqNode(bk);
1378
6090
      Node lemma = fact[0].orNode(eq.notNode());
1379
1380
9135
      if (options::arraysPropagate() > 0 && d_equalityEngine->hasTerm(ak)
1381
8457
          && d_equalityEngine->hasTerm(bk))
1382
      {
1383
        // Propagate witness disequality - might produce a conflict
1384
4734
        Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1385
2367
                           << "\teq = " << eq << std::endl
1386
2367
                           << "\treason = " << fact << std::endl;
1387
2367
        d_im.assertInference(eq, false, InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1388
2367
        ++d_numProp;
1389
      }
1390
1391
      // If this is the solution pass, generate the lemma. Otherwise, don't
1392
      // generate it - as this is the lemma that we're reproving...
1393
3045
      Trace("arrays-lem") << "Arrays::addExtLemma " << lemma << "\n";
1394
3045
      d_im.arrayLemma(eq.notNode(), InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1395
3045
      ++d_numExt;
1396
    }
1397
    else
1398
    {
1399
80348
      Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem"
1400
40174
                         << std::endl;
1401
40174
      d_modelConstraints.push_back(fact);
1402
    }
1403
  }
1404
217539
}
1405
1406
Node TheoryArrays::mkAnd(std::vector<TNode>& conjunctions, bool invert, unsigned startIndex)
1407
{
1408
  if (conjunctions.empty())
1409
  {
1410
    return invert ? d_false : d_true;
1411
  }
1412
1413
  std::set<TNode> all;
1414
1415
  unsigned i = startIndex;
1416
  TNode t;
1417
  for (; i < conjunctions.size(); ++i) {
1418
    t = conjunctions[i];
1419
    if (t == d_true) {
1420
      continue;
1421
    }
1422
    else if (t.getKind() == kind::AND) {
1423
      for(TNode::iterator child_it = t.begin(); child_it != t.end(); ++child_it) {
1424
        if (*child_it == d_true) {
1425
          continue;
1426
        }
1427
        all.insert(*child_it);
1428
      }
1429
    }
1430
    else {
1431
      all.insert(t);
1432
    }
1433
  }
1434
1435
  if (all.size() == 0) {
1436
    return invert ? d_false : d_true;
1437
  }
1438
  if (all.size() == 1) {
1439
    // All the same, or just one
1440
    if (invert) {
1441
      return (*(all.begin())).negate();
1442
    }
1443
    else {
1444
      return *(all.begin());
1445
    }
1446
  }
1447
1448
  NodeBuilder conjunction(invert ? kind::OR : kind::AND);
1449
  std::set<TNode>::const_iterator it = all.begin();
1450
  std::set<TNode>::const_iterator it_end = all.end();
1451
  while (it != it_end) {
1452
    if (invert) {
1453
      conjunction << (*it).negate();
1454
    }
1455
    else {
1456
      conjunction << *it;
1457
    }
1458
    ++ it;
1459
  }
1460
1461
  return conjunction;
1462
}
1463
1464
1465
1927
void TheoryArrays::setNonLinear(TNode a)
1466
{
1467
2122
  if (options::arraysWeakEquivalence()) return;
1468
1927
  if (d_infoMap.isNonLinear(a)) return;
1469
1470
1732
  Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a << ")\n";
1471
1732
  d_infoMap.setNonLinear(a);
1472
1732
  ++d_numNonLinear;
1473
1474
1732
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1475
1732
  const CTNodeList* st_a = d_infoMap.getStores(a);
1476
1732
  const CTNodeList* inst_a = d_infoMap.getInStores(a);
1477
1478
1732
  size_t it = 0;
1479
1480
  // Propagate non-linearity down chain of stores
1481
4140
  for( ; it < st_a->size(); ++it) {
1482
2408
    TNode store = (*st_a)[it];
1483
1204
    Assert(store.getKind() == kind::STORE);
1484
1204
    setNonLinear(store[0]);
1485
  }
1486
1487
  // Instantiate ROW lemmas that were ignored before
1488
1732
  size_t it2 = 0;
1489
3464
  RowLemmaType lem;
1490
20668
  for(; it2 < i_a->size(); ++it2) {
1491
18936
    TNode i = (*i_a)[it2];
1492
9468
    it = 0;
1493
28480
    for ( ; it < inst_a->size(); ++it) {
1494
19012
      TNode store = (*inst_a)[it];
1495
9506
      Assert(store.getKind() == kind::STORE);
1496
19012
      TNode j = store[1];
1497
19012
      TNode c = store[0];
1498
9506
      lem = std::make_tuple(store, c, j, i);
1499
9506
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1500
9506
      queueRowLemma(lem);
1501
    }
1502
  }
1503
1504
}
1505
1506
5807
void TheoryArrays::mergeArrays(TNode a, TNode b)
1507
{
1508
  // Note: a is the new representative
1509
5807
  Assert(a.getType().isArray() && b.getType().isArray());
1510
1511
5807
  if (d_mergeInProgress) {
1512
    // Nested call to mergeArrays, just push on the queue and return
1513
    d_mergeQueue.push(a.eqNode(b));
1514
    return;
1515
  }
1516
1517
5807
  d_mergeInProgress = true;
1518
1519
11614
  Node n;
1520
  while (true) {
1521
    // Normally, a is its own representative, but it's possible for a to have
1522
    // been merged with another array after it got queued up by the equality engine,
1523
    // so we take its representative to be safe.
1524
5807
    a = d_equalityEngine->getRepresentative(a);
1525
5807
    Assert(d_equalityEngine->getRepresentative(b) == a);
1526
5807
    Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: (" << a << ", " << b << ")\n";
1527
1528
5807
    if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1529
4095
      bool aNL = d_infoMap.isNonLinear(a);
1530
4095
      bool bNL = d_infoMap.isNonLinear(b);
1531
4095
      if (aNL) {
1532
260
        if (bNL) {
1533
          // already both marked as non-linear - no need to do anything
1534
        }
1535
        else {
1536
          // Set b to be non-linear
1537
233
          setNonLinear(b);
1538
        }
1539
      }
1540
      else {
1541
3835
        if (bNL) {
1542
          // Set a to be non-linear
1543
48
          setNonLinear(a);
1544
        }
1545
        else {
1546
          // Check for new non-linear arrays
1547
3787
          const CTNodeList* astores = d_infoMap.getStores(a);
1548
3787
          const CTNodeList* bstores = d_infoMap.getStores(b);
1549
3787
          Assert(astores->size() <= 1 && bstores->size() <= 1);
1550
3787
          if (astores->size() > 0 && bstores->size() > 0) {
1551
221
            setNonLinear(a);
1552
221
            setNonLinear(b);
1553
          }
1554
        }
1555
      }
1556
    }
1557
1558
5810
    TNode constArrA = d_infoMap.getConstArr(a);
1559
5810
    TNode constArrB = d_infoMap.getConstArr(b);
1560
5807
    if (constArrA.isNull()) {
1561
5644
      if (!constArrB.isNull()) {
1562
        d_infoMap.setConstArr(a,constArrB);
1563
      }
1564
    }
1565
163
    else if (!constArrB.isNull()) {
1566
      if (constArrA != constArrB) {
1567
        conflict(constArrA,constArrB);
1568
      }
1569
    }
1570
1571
5810
    TNode mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1572
5810
    TNode mayRepB = d_mayEqualEqualityEngine.getRepresentative(b);
1573
1574
    // If a and b have different default values associated with their mayequal equivalence classes,
1575
    // things get complicated.  Similarly, if two mayequal equivalence classes have different
1576
    // constant representatives, it's not clear what to do. - disallow these cases for now.  -Clark
1577
5807
    DefValMap::iterator it = d_defValues.find(mayRepA);
1578
5807
    DefValMap::iterator it2 = d_defValues.find(mayRepB);
1579
5810
    TNode defValue;
1580
1581
5807
    if (it != d_defValues.end()) {
1582
260
      defValue = (*it).second;
1583
521
      if ((it2 != d_defValues.end() && (defValue != (*it2).second)) ||
1584
516
          (mayRepA.isConst() && mayRepB.isConst() && mayRepA != mayRepB)) {
1585
3
        throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1586
      }
1587
    }
1588
5547
    else if (it2 != d_defValues.end()) {
1589
      defValue = (*it2).second;
1590
    }
1591
5804
    d_mayEqualEqualityEngine.assertEquality(a.eqNode(b), true, d_true);
1592
5804
    Assert(d_mayEqualEqualityEngine.consistent());
1593
5804
    if (!defValue.isNull()) {
1594
257
      mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1595
257
      d_defValues[mayRepA] = defValue;
1596
    }
1597
1598
5804
    checkRowLemmas(a,b);
1599
5804
    checkRowLemmas(b,a);
1600
1601
    // merge info adds the list of the 2nd argument to the first
1602
5804
    d_infoMap.mergeInfo(a, b);
1603
1604
5804
    if (options::arraysWeakEquivalence()) {
1605
      d_arrayMerges.push_back(a.eqNode(b));
1606
    }
1607
1608
    // If no more to do, break
1609
5804
    if (d_state.isInConflict() || d_mergeQueue.empty())
1610
    {
1611
5804
      break;
1612
    }
1613
1614
    // Otherwise, prepare for next iteration
1615
    n = d_mergeQueue.front();
1616
    a = n[0];
1617
    b = n[1];
1618
    d_mergeQueue.pop();
1619
  }
1620
5804
  d_mergeInProgress = false;
1621
}
1622
1623
1624
1112
void TheoryArrays::checkStore(TNode a) {
1625
1112
  if (options::arraysWeakEquivalence()) return;
1626
1112
  Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
1627
1628
1112
  if(Trace.isOn("arrays-cri")) {
1629
    d_infoMap.getInfo(a)->print();
1630
  }
1631
1112
  Assert(a.getType().isArray());
1632
1112
  Assert(a.getKind() == kind::STORE);
1633
2224
  TNode b = a[0];
1634
2224
  TNode i = a[1];
1635
1636
2224
  TNode brep = d_equalityEngine->getRepresentative(b);
1637
1638
1112
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(brep)) {
1639
258
    const CTNodeList* js = d_infoMap.getIndices(brep);
1640
258
    size_t it = 0;
1641
516
    RowLemmaType lem;
1642
828
    for(; it < js->size(); ++it) {
1643
546
      TNode j = (*js)[it];
1644
285
      if (i == j) continue;
1645
261
      lem = std::make_tuple(a, b, i, j);
1646
261
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkStore ("<<a<<", "<<b<<", "<<i<<", "<<j<<")\n";
1647
261
      queueRowLemma(lem);
1648
    }
1649
  }
1650
}
1651
1652
1653
24028
void TheoryArrays::checkRowForIndex(TNode i, TNode a)
1654
{
1655
24028
  if (options::arraysWeakEquivalence()) return;
1656
24028
  Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a<<"\n";
1657
24028
  Trace("arrays-cri")<<"                   index "<<i<<"\n";
1658
1659
24028
  if(Trace.isOn("arrays-cri")) {
1660
    d_infoMap.getInfo(a)->print();
1661
  }
1662
24028
  Assert(a.getType().isArray());
1663
24028
  Assert(d_equalityEngine->getRepresentative(a) == a);
1664
1665
48056
  TNode constArr = d_infoMap.getConstArr(a);
1666
24028
  if (!constArr.isNull()) {
1667
510
    ArrayStoreAll storeAll = constArr.getConst<ArrayStoreAll>();
1668
510
    Node defValue = storeAll.getValue();
1669
510
    Node selConst = NodeManager::currentNM()->mkNode(kind::SELECT, constArr, i);
1670
255
    if (!d_equalityEngine->hasTerm(selConst))
1671
    {
1672
9
      preRegisterTermInternal(selConst);
1673
    }
1674
255
    d_im.assertInference(selConst.eqNode(defValue),
1675
                         true,
1676
                         InferenceId::ARRAYS_CONST_ARRAY_DEFAULT,
1677
                         d_true,
1678
                         PfRule::ARRAYS_TRUST);
1679
  }
1680
1681
24028
  const CTNodeList* stores = d_infoMap.getStores(a);
1682
24028
  const CTNodeList* instores = d_infoMap.getInStores(a);
1683
24028
  size_t it = 0;
1684
48056
  RowLemmaType lem;
1685
1686
54536
  for(; it < stores->size(); ++it) {
1687
30395
    TNode store = (*stores)[it];
1688
15254
    Assert(store.getKind() == kind::STORE);
1689
30395
    TNode j = store[1];
1690
15254
    if (i == j) continue;
1691
15141
    lem = std::make_tuple(store, store[0], j, i);
1692
15141
    Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
1693
15141
    queueRowLemma(lem);
1694
  }
1695
1696
24028
  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
11608
void TheoryArrays::checkRowLemmas(TNode a, TNode b)
1714
{
1715
11608
  if (options::arraysWeakEquivalence()) return;
1716
11608
  Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
1717
11608
  if(Trace.isOn("arrays-crl"))
1718
    d_infoMap.getInfo(a)->print();
1719
11608
  Trace("arrays-crl")<<"  ------------  and "<<b<<"\n";
1720
11608
  if(Trace.isOn("arrays-crl"))
1721
    d_infoMap.getInfo(b)->print();
1722
1723
11608
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1724
11608
  size_t it = 0;
1725
23216
  TNode constArr = d_infoMap.getConstArr(b);
1726
11608
  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
11608
  const CTNodeList* st_b = d_infoMap.getStores(b);
1738
11608
  const CTNodeList* inst_b = d_infoMap.getInStores(b);
1739
  size_t its;
1740
1741
23216
  RowLemmaType lem;
1742
1743
63204
  for(it = 0 ; it < i_a->size(); ++it) {
1744
103192
    TNode i = (*i_a)[it];
1745
51596
    its = 0;
1746
99968
    for ( ; its < st_b->size(); ++its) {
1747
48372
      TNode store = (*st_b)[its];
1748
24186
      Assert(store.getKind() == kind::STORE);
1749
48372
      TNode j = store[1];
1750
48372
      TNode c = store[0];
1751
24186
      lem = std::make_tuple(store, c, j, i);
1752
24186
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1753
24186
      queueRowLemma(lem);
1754
    }
1755
  }
1756
1757
11608
  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
11608
  Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1773
}
1774
1775
22784
void TheoryArrays::propagateRowLemma(RowLemmaType lem)
1776
{
1777
45568
  Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1778
22784
                     << options::arraysPropagate() << std::endl;
1779
1780
35744
  TNode a, b, i, j;
1781
22784
  std::tie(a, b, i, j) = lem;
1782
1783
22784
  Assert(a.getType().isArray() && b.getType().isArray());
1784
22784
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1785
  {
1786
    return;
1787
  }
1788
1789
22784
  NodeManager* nm = NodeManager::currentNM();
1790
35744
  Node aj = nm->mkNode(kind::SELECT, a, j);
1791
35744
  Node bj = nm->mkNode(kind::SELECT, b, j);
1792
1793
  // Try to avoid introducing new read terms: track whether these already exist
1794
22784
  bool ajExists = d_equalityEngine->hasTerm(aj);
1795
22784
  bool bjExists = d_equalityEngine->hasTerm(bj);
1796
22784
  bool bothExist = ajExists && bjExists;
1797
1798
  // If propagating, check propagations
1799
22784
  int prop = options::arraysPropagate();
1800
22784
  if (prop > 0) {
1801
22784
    if (d_equalityEngine->areDisequal(i, j, true) && (bothExist || prop > 1))
1802
    {
1803
9820
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj<<", "<<bj<<")\n";
1804
19640
      Node aj_eq_bj = aj.eqNode(bj);
1805
      Node reason =
1806
19640
          (i.isConst() && j.isConst()) ? d_true : i.eqNode(j).notNode();
1807
9820
      d_permRef.push_back(reason);
1808
9820
      if (!ajExists) {
1809
2991
        preRegisterTermInternal(aj);
1810
      }
1811
9820
      if (!bjExists) {
1812
5682
        preRegisterTermInternal(bj);
1813
      }
1814
9820
      d_im.assertInference(
1815
          aj_eq_bj, true, InferenceId::ARRAYS_READ_OVER_WRITE, reason, PfRule::ARRAYS_READ_OVER_WRITE);
1816
9820
      ++d_numProp;
1817
9820
      return;
1818
    }
1819
12964
    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
67507
void TheoryArrays::queueRowLemma(RowLemmaType lem)
1834
{
1835
67507
  Debug("pf::array") << "Array solver: queue row lemma called" << std::endl;
1836
1837
67507
  if (d_state.isInConflict() || d_RowAlreadyAdded.contains(lem))
1838
  {
1839
79767
    return;
1840
  }
1841
55247
  TNode a, b, i, j;
1842
35455
  std::tie(a, b, i, j) = lem;
1843
1844
35455
  Assert(a.getType().isArray() && b.getType().isArray());
1845
35455
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1846
  {
1847
14540
    return;
1848
  }
1849
1850
20915
  NodeManager* nm = NodeManager::currentNM();
1851
40707
  Node aj = nm->mkNode(kind::SELECT, a, j);
1852
40707
  Node bj = nm->mkNode(kind::SELECT, b, j);
1853
1854
  // Try to avoid introducing new read terms: track whether these already exist
1855
20915
  bool ajExists = d_equalityEngine->hasTerm(aj);
1856
20915
  bool bjExists = d_equalityEngine->hasTerm(bj);
1857
20915
  bool bothExist = ajExists && bjExists;
1858
1859
  // If propagating, check propagations
1860
20915
  int prop = options::arraysPropagate();
1861
20915
  if (prop > 0) {
1862
20915
    propagateRowLemma(lem);
1863
  }
1864
1865
  // Prefer equality between indexes so as not to introduce new read terms
1866
55395
  if (options::arraysEagerIndexSplitting() && !bothExist
1867
53092
      && !d_equalityEngine->areDisequal(i, j, false))
1868
  {
1869
11258
    Node i_eq_j;
1870
5629
    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
5629
    getOutputChannel().requirePhase(i_eq_j, true);
1875
5629
    d_decisionRequests.push(i_eq_j);
1876
  }
1877
1878
  // TODO: maybe add triggers here
1879
1880
20915
  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
18337
    d_RowQueue.push(lem);
1959
  }
1960
}
1961
1962
2643586
Node TheoryArrays::getNextDecisionRequest()
1963
{
1964
2643586
  if(! d_decisionRequests.empty()) {
1965
31618
    Node n = d_decisionRequests.front();
1966
15809
    d_decisionRequests.pop();
1967
15809
    return n;
1968
  }
1969
2627777
  return Node::null();
1970
}
1971
1972
1973
1049
bool TheoryArrays::dischargeLemmas()
1974
{
1975
1049
  bool lemmasAdded = false;
1976
1049
  size_t sz = d_RowQueue.size();
1977
14518
  for (unsigned count = 0; count < sz; ++count) {
1978
15286
    RowLemmaType l = d_RowQueue.front();
1979
13851
    d_RowQueue.pop();
1980
13851
    if (d_RowAlreadyAdded.contains(l)) {
1981
7863
      continue;
1982
    }
1983
1984
7423
    TNode a, b, i, j;
1985
5988
    std::tie(a, b, i, j) = l;
1986
5988
    Assert(a.getType().isArray() && b.getType().isArray());
1987
1988
5988
    NodeManager* nm = NodeManager::currentNM();
1989
7423
    Node aj = nm->mkNode(kind::SELECT, a, j);
1990
7423
    Node bj = nm->mkNode(kind::SELECT, b, j);
1991
5988
    bool ajExists = d_equalityEngine->hasTerm(aj);
1992
5988
    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
23952
    if (!d_equalityEngine->hasTerm(i) || !d_equalityEngine->hasTerm(j)
1997
11976
        || d_equalityEngine->areEqual(i, j) || !d_equalityEngine->hasTerm(a)
1998
9744
        || !d_equalityEngine->hasTerm(b) || d_equalityEngine->areEqual(a, b)
1999
21400
        || (ajExists && bjExists && d_equalityEngine->areEqual(aj, bj)))
2000
    {
2001
4119
      continue;
2002
    }
2003
2004
1869
    int prop = options::arraysPropagate();
2005
1869
    if (prop > 0) {
2006
1869
      propagateRowLemma(l);
2007
1869
      if (d_state.isInConflict())
2008
      {
2009
382
        return true;
2010
      }
2011
    }
2012
2013
    // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2014
2922
    Node aj2 = Rewriter::rewrite(aj);
2015
1487
    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
2922
    Node bj2 = Rewriter::rewrite(bj);
2030
1487
    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
1487
    if (aj2 == bj2) {
2045
52
      continue;
2046
    }
2047
2048
    // construct lemma
2049
2870
    Node eq1 = aj2.eqNode(bj2);
2050
2870
    Node eq1_r = Rewriter::rewrite(eq1);
2051
1435
    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
2870
    Node eq2 = i.eqNode(j);
2069
2870
    Node eq2_r = Rewriter::rewrite(eq2);
2070
1435
    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
2870
    Node lem = nm->mkNode(kind::OR, eq2_r, eq1_r);
2080
2081
1435
    Trace("arrays-lem") << "Arrays::addRowLemma (2) adding " << lem << "\n";
2082
1435
    d_RowAlreadyAdded.insert(l);
2083
    // use non-rewritten nodes, theory preprocessing will rewrite
2084
2870
    d_im.arrayLemma(
2085
2870
        aj.eqNode(bj), InferenceId::ARRAYS_READ_OVER_WRITE, eq2.notNode(), PfRule::ARRAYS_READ_OVER_WRITE);
2086
1435
    ++d_numRow;
2087
1435
    lemmasAdded = true;
2088
1435
    if (options::arraysReduceSharing()) {
2089
      return true;
2090
    }
2091
  }
2092
667
  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
9850
TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2107
9850
    TheoryArrays* ta)
2108
9850
    : d_ta(ta)
2109
{
2110
9850
}
2111
9401
void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
2112
2643586
Node TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2113
{
2114
2643586
  return d_ta->getNextDecisionRequest();
2115
}
2116
2652987
std::string TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2117
{
2118
2652987
  return std::string("th_arrays_dec");
2119
}
2120
2121
5311
void TheoryArrays::computeRelevantTerms(std::set<Node>& termSet)
2122
{
2123
5311
  NodeManager* nm = NodeManager::currentNM();
2124
  // make sure RIntro1 reads are included in the relevant set of reads
2125
5311
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2126
31577
  for (; !eqcs_i.isFinished(); ++eqcs_i)
2127
  {
2128
14039
    Node eqc = (*eqcs_i);
2129
13133
    if (!eqc.getType().isArray())
2130
    {
2131
      // not an array, skip
2132
12227
      continue;
2133
    }
2134
906
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2135
4546
    for (; !eqc_i.isFinished(); ++eqc_i)
2136
    {
2137
3640
      Node n = *eqc_i;
2138
1820
      if (termSet.find(n) != termSet.end())
2139
      {
2140
1776
        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
5392
  do
2157
  {
2158
5392
    changed = false;
2159
5392
    eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2160
34258
    for (; !eqcs_i.isFinished(); ++eqcs_i)
2161
    {
2162
28866
      Node eqc = (*eqcs_i);
2163
14433
      eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2164
68053
      for (; !eqc_i.isFinished(); ++eqc_i)
2165
      {
2166
53620
        Node n = *eqc_i;
2167
26810
        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
13624
          Node array_eqc = d_equalityEngine->getRepresentative(n[0]);
2171
          eq::EqClassIterator array_eqc_i =
2172
6812
              eq::EqClassIterator(array_eqc, d_equalityEngine);
2173
47246
          for (; !array_eqc_i.isFinished(); ++array_eqc_i)
2174
          {
2175
40434
            Node arr = *array_eqc_i;
2176
40434
            if (arr.getKind() == kind::STORE
2177
27568
                && termSet.find(arr) != termSet.end()
2178
47785
                && !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
6812
          const CTNodeList* instores = d_infoMap.getInStores(array_eqc);
2208
6812
          size_t it = 0;
2209
20458
          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
5311
}
2245
2246
}  // namespace arrays
2247
}  // namespace theory
2248
29337
}  // namespace cvc5