GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 884 1225 72.2 %
Date: 2021-09-10 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
9913
TheoryArrays::TheoryArrays(Env& env,
59
                           OutputChannel& out,
60
                           Valuation valuation,
61
9913
                           std::string name)
62
    : Theory(THEORY_ARRAYS, env, out, valuation, name),
63
      d_numRow(
64
19826
          smtStatisticsRegistry().registerInt(name + "number of Row lemmas")),
65
      d_numExt(
66
19826
          smtStatisticsRegistry().registerInt(name + "number of Ext lemmas")),
67
      d_numProp(
68
19826
          smtStatisticsRegistry().registerInt(name + "number of propagations")),
69
      d_numExplain(
70
19826
          smtStatisticsRegistry().registerInt(name + "number of explanations")),
71
9913
      d_numNonLinear(smtStatisticsRegistry().registerInt(
72
19826
          name + "number of calls to setNonLinear")),
73
9913
      d_numSharedArrayVarSplits(smtStatisticsRegistry().registerInt(
74
19826
          name + "number of shared array var splits")),
75
9913
      d_numGetModelValSplits(smtStatisticsRegistry().registerInt(
76
19826
          name + "number of getModelVal splits")),
77
9913
      d_numGetModelValConflicts(smtStatisticsRegistry().registerInt(
78
19826
          name + "number of getModelVal conflicts")),
79
9913
      d_numSetModelValSplits(smtStatisticsRegistry().registerInt(
80
19826
          name + "number of setModelVal splits")),
81
9913
      d_numSetModelValConflicts(smtStatisticsRegistry().registerInt(
82
19826
          name + "number of setModelVal conflicts")),
83
19826
      d_ppEqualityEngine(userContext(), name + "pp", true),
84
9913
      d_ppFacts(userContext()),
85
      d_rewriter(d_pnm),
86
      d_state(env, valuation),
87
      d_im(env, *this, d_state, d_pnm),
88
      d_literalsToPropagate(context()),
89
      d_literalsToPropagateIndex(context(), 0),
90
      d_isPreRegistered(context()),
91
19826
      d_mayEqualEqualityEngine(context(), name + "mayEqual", true),
92
      d_notify(*this),
93
      d_infoMap(context(), name),
94
      d_mergeQueue(context()),
95
      d_mergeInProgress(false),
96
      d_RowQueue(context()),
97
9913
      d_RowAlreadyAdded(userContext()),
98
      d_sharedArrays(context()),
99
      d_sharedOther(context()),
100
      d_sharedTerms(context(), false),
101
      d_reads(context()),
102
      d_constReadsList(context()),
103
9913
      d_constReadsContext(new context::Context()),
104
      d_contextPopper(context(), d_constReadsContext),
105
      d_skolemIndex(context(), 0),
106
      d_decisionRequests(context()),
107
      d_permRef(context()),
108
      d_modelConstraints(context()),
109
      d_lemmasSaved(context()),
110
      d_defValues(context()),
111
9913
      d_readTableContext(new context::Context()),
112
      d_arrayMerges(context()),
113
      d_inCheckModel(false),
114
9913
      d_dstrat(new TheoryArraysDecisionStrategy(this)),
115
208173
      d_dstratInit(false)
116
{
117
9913
  d_true = NodeManager::currentNM()->mkConst<bool>(true);
118
9913
  d_false = NodeManager::currentNM()->mkConst<bool>(false);
119
120
  // The preprocessing congruence kinds
121
9913
  d_ppEqualityEngine.addFunctionKind(kind::SELECT);
122
9913
  d_ppEqualityEngine.addFunctionKind(kind::STORE);
123
124
  // indicate we are using the default theory state object, and the arrays
125
  // inference manager
126
9913
  d_theoryState = &d_state;
127
9913
  d_inferManager = &d_im;
128
9913
}
129
130
29730
TheoryArrays::~TheoryArrays() {
131
9910
  vector<CTNodeList*>::iterator it = d_readBucketAllocations.begin(), iend = d_readBucketAllocations.end();
132
9910
  for (; it != iend; ++it) {
133
    (*it)->deleteSelf();
134
  }
135
9910
  delete d_readTableContext;
136
9910
  CNodeNListMap::iterator it2 = d_constReads.begin();
137
11316
  for( ; it2 != d_constReads.end(); ++it2 ) {
138
703
    it2->second->deleteSelf();
139
  }
140
9910
  delete d_constReadsContext;
141
19820
}
142
143
9913
TheoryRewriter* TheoryArrays::getTheoryRewriter() { return &d_rewriter; }
144
145
3781
ProofRuleChecker* TheoryArrays::getProofChecker() { return &d_checker; }
146
147
9913
bool TheoryArrays::needsEqualityEngine(EeSetupInfo& esi)
148
{
149
9913
  esi.d_notify = &d_notify;
150
9913
  esi.d_name = d_instanceName + "ee";
151
9913
  esi.d_notifyNewClass = true;
152
9913
  esi.d_notifyMerge = true;
153
9913
  return true;
154
}
155
156
9913
void TheoryArrays::finishInit()
157
{
158
9913
  Assert(d_equalityEngine != nullptr);
159
160
  // The kinds we are treating as function application in congruence
161
9913
  d_equalityEngine->addFunctionKind(kind::SELECT);
162
  if (d_ccStore)
163
  {
164
    d_equalityEngine->addFunctionKind(kind::STORE);
165
  }
166
9913
}
167
168
/////////////////////////////////////////////////////////////////////////////
169
// PREPROCESSING
170
/////////////////////////////////////////////////////////////////////////////
171
172
173
3216
bool TheoryArrays::ppDisequal(TNode a, TNode b) {
174
3216
  bool termsExist = d_ppEqualityEngine.hasTerm(a) && d_ppEqualityEngine.hasTerm(b);
175
3216
  Assert(!termsExist || !a.isConst() || !b.isConst() || a == b
176
         || d_ppEqualityEngine.areDisequal(a, b, false));
177
12804
  return ((termsExist && d_ppEqualityEngine.areDisequal(a, b, false)) ||
178
12804
          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
22428
TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems)
301
{
302
  // first, see if we need to expand definitions
303
44856
  TrustNode texp = d_rewriter.expandDefinition(term);
304
22428
  if (!texp.isNull())
305
  {
306
20
    return texp;
307
  }
308
  if (!d_preprocess)
309
  {
310
    return TrustNode::null();
311
  }
312
22408
  d_ppEqualityEngine.addTerm(term);
313
22408
  NodeManager* nm = NodeManager::currentNM();
314
44816
  Node ret;
315
22408
  switch (term.getKind()) {
316
6372
    case kind::SELECT: {
317
      // select(store(a,i,v),j) = select(a,j)
318
      //    IF i != j
319
6372
      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
6372
      break;
323
    }
324
1359
    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
1359
      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
1359
      break;
333
    }
334
1487
    case kind::EQUAL: {
335
1487
      ret = solveWrite(term, d_solveWrite, d_solveWrite2, true);
336
1487
      break;
337
    }
338
13190
    default:
339
13190
      break;
340
  }
341
22408
  if (!ret.isNull() && ret != term)
342
  {
343
48
    return TrustNode::mkTrustRewrite(term, ret, nullptr);
344
  }
345
22360
  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
209206
bool TheoryArrays::propagateLit(TNode literal)
391
{
392
418412
  Debug("arrays") << spaces(context()->getLevel())
393
209206
                  << "TheoryArrays::propagateLit(" << literal << ")"
394
209206
                  << std::endl;
395
396
  // If already in conflict, no more propagation
397
209206
  if (d_state.isInConflict())
398
  {
399
    Debug("arrays") << spaces(context()->getLevel())
400
                    << "TheoryArrays::propagateLit(" << literal
401
                    << "): already in conflict" << std::endl;
402
    return false;
403
  }
404
405
  // Propagate away
406
209206
  if (d_inCheckModel && context()->getLevel() != d_topLevel)
407
  {
408
    return true;
409
  }
410
209206
  bool ok = d_out->propagate(literal);
411
209206
  if (!ok) {
412
1930
    d_state.notifyInConflict();
413
  }
414
209206
  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
92264
void TheoryArrays::preRegisterTermInternal(TNode node)
641
{
642
92264
  if (d_state.isInConflict())
643
  {
644
59868
    return;
645
  }
646
184406
  Debug("arrays") << spaces(context()->getLevel())
647
92203
                  << "TheoryArrays::preRegisterTerm(" << node << ")"
648
92203
                  << std::endl;
649
92203
  Kind nk = node.getKind();
650
92203
  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
13413
    d_equalityEngine->addTriggerPredicate(node);
655
13413
    return;
656
  }
657
  // add to equality engine and the may equality engine
658
111247
  TypeNode nodeType = node.getType();
659
78790
  if (nodeType.isArray())
660
  {
661
    // index type should not be an array, otherwise we throw a logic exception
662
6933
    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
6933
    d_mayEqualEqualityEngine.addTerm(node);
670
  }
671
78790
  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
46333
    return;
679
  }
680
32457
  d_equalityEngine->addTerm(node);
681
682
32457
  switch (node.getKind())
683
  {
684
24006
    case kind::SELECT:
685
    {
686
      // Reads
687
48012
      TNode store = d_equalityEngine->getRepresentative(node[0]);
688
689
      // The may equal needs the store
690
24006
      d_mayEqualEqualityEngine.addTerm(store);
691
692
24006
      Assert((d_isPreRegistered.insert(node), true));
693
694
24006
      Assert(d_equalityEngine->getRepresentative(store) == store);
695
24006
      d_infoMap.addIndex(store, node[1]);
696
697
      // Synchronize d_constReadsContext with SAT context
698
24006
      Assert(d_constReadsContext->getLevel() <= context()->getLevel());
699
62042
      while (d_constReadsContext->getLevel() < context()->getLevel())
700
      {
701
19018
        d_constReadsContext->push();
702
      }
703
704
      // Record read in sharing data structure
705
48012
      TNode index = d_equalityEngine->getRepresentative(node[1]);
706
24006
      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
19412
        d_reads.push_back(node);
725
      }
726
727
24006
      checkRowForIndex(node[1], store);
728
24006
      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
7273
  default:
798
    // Variables etc, already processed above
799
7273
    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
38614
void TheoryArrays::preRegisterTerm(TNode node)
808
{
809
38614
  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
38614
  if (node.getKind() == kind::SELECT && node.getType().isBoolean()) {
814
97
    d_equalityEngine->addTriggerPredicate(node);
815
  }
816
38614
}
817
818
void TheoryArrays::explain(TNode literal, Node& explanation)
819
{
820
  ++d_numExplain;
821
  Debug("arrays") << spaces(context()->getLevel()) << "TheoryArrays::explain("
822
                  << 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
12665
TrustNode TheoryArrays::explain(TNode literal)
840
{
841
12665
  return d_im.explainLit(literal);
842
}
843
844
/////////////////////////////////////////////////////////////////////////////
845
// SHARING
846
/////////////////////////////////////////////////////////////////////////////
847
848
87144
void TheoryArrays::notifySharedTerm(TNode t)
849
{
850
174288
  Debug("arrays::sharing") << spaces(context()->getLevel())
851
87144
                           << "TheoryArrays::notifySharedTerm(" << t << ")"
852
87144
                           << std::endl;
853
87144
  if (t.getType().isArray()) {
854
2490
    d_sharedArrays.insert(t);
855
  }
856
  else {
857
#ifdef CVC5_ASSERTIONS
858
84654
    d_sharedOther.insert(t);
859
#endif
860
84654
    d_sharedTerms = true;
861
  }
862
87144
}
863
864
266881
void TheoryArrays::checkPair(TNode r1, TNode r2)
865
{
866
266881
  Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1 << " and " << r2 << std::endl;
867
868
268670
  TNode x = r1[1];
869
268670
  TNode y = r2[1];
870
266881
  Assert(d_equalityEngine->isTriggerTerm(x, THEORY_ARRAYS));
871
872
1067524
  if (d_equalityEngine->hasTerm(x) && d_equalityEngine->hasTerm(y)
873
1569010
      && (d_equalityEngine->areEqual(x, y)
874
309416
          || d_equalityEngine->areDisequal(x, y, false)))
875
  {
876
234605
    Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality known, skipping" << std::endl;
877
234605
    return;
878
  }
879
880
  // If the terms are already known to be equal, we are also in good shape
881
32276
  if (d_equalityEngine->areEqual(r1, r2))
882
  {
883
1944
    Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl;
884
1944
    return;
885
  }
886
887
30332
  if (r1[0] != r2[0]) {
888
    // If arrays are known to be disequal, or cannot become equal, we can continue
889
26232
    Assert(d_mayEqualEqualityEngine.hasTerm(r1[0])
890
           && d_mayEqualEqualityEngine.hasTerm(r2[0]));
891
104928
    if (r1[0].getType() != r2[0].getType()
892
104928
        || d_equalityEngine->areDisequal(r1[0], r2[0], false))
893
    {
894
3886
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl;
895
3886
      return;
896
    }
897
22346
    else if (!d_mayEqualEqualityEngine.areEqual(r1[0], r2[0])) {
898
69
      return;
899
    }
900
  }
901
902
26377
  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
28128
      d_equalityEngine->getTriggerTermRepresentative(x, THEORY_ARRAYS);
911
  TNode y_shared =
912
28128
      d_equalityEngine->getTriggerTermRepresentative(y, THEORY_ARRAYS);
913
26339
  EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared);
914
26339
  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
1789
    default:
936
      // Covers EQUALITY_TRUE_IN_MODEL (common case) and EQUALITY_UNKNOWN
937
1789
      break;
938
  }
939
940
  // Add this pair
941
1789
  Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl;
942
1789
  addCarePair(x_shared, y_shared);
943
}
944
945
946
7219
void TheoryArrays::computeCareGraph()
947
{
948
7219
  if (d_sharedArrays.size() > 0) {
949
364
    CDNodeSet::key_iterator it1 = d_sharedArrays.key_begin(), it2, iend = d_sharedArrays.key_end();
950
3720
    for (; it1 != iend; ++it1) {
951
15724
      for (it2 = it1, ++it2; it2 != iend; ++it2) {
952
14046
        if ((*it1).getType() != (*it2).getType()) {
953
4455
          continue;
954
        }
955
9591
        EqualityStatus eqStatusArr = getEqualityStatus((*it1), (*it2));
956
9591
        if (eqStatusArr != EQUALITY_UNKNOWN) {
957
9469
          continue;
958
        }
959
122
        Assert(d_valuation.getEqualityStatus((*it1), (*it2))
960
               == EQUALITY_UNKNOWN);
961
122
        addCarePair((*it1), (*it2));
962
122
        ++d_numSharedArrayVarSplits;
963
244
        return;
964
      }
965
    }
966
  }
967
7097
  if (d_sharedTerms) {
968
    // Synchronize d_constReadsContext with SAT context
969
619
    Assert(d_constReadsContext->getLevel() <= context()->getLevel());
970
9439
    while (d_constReadsContext->getLevel() < context()->getLevel())
971
    {
972
4410
      d_constReadsContext->push();
973
    }
974
975
    // Go through the read terms and see if there are any to split on
976
977
    // Give constReadsContext a push so that all the work it does here is erased - models can change if context changes at all
978
    // The context is popped at the end.  If this loop is interrupted for some reason, we have to make sure the context still
979
    // gets popped or the solver will be in an inconsistent state
980
619
    d_constReadsContext->push();
981
619
    unsigned size = d_reads.size();
982
6880
    for (unsigned i = 0; i < size; ++ i) {
983
12297
      TNode r1 = d_reads[i];
984
985
6261
      Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking read " << r1 << std::endl;
986
6261
      Assert(d_equalityEngine->hasTerm(r1));
987
12297
      TNode x = r1[1];
988
989
6486
      if (!d_equalityEngine->isTriggerTerm(x, THEORY_ARRAYS))
990
      {
991
225
        Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
992
225
        continue;
993
      }
994
      Node x_shared =
995
12072
          d_equalityEngine->getTriggerTermRepresentative(x, THEORY_ARRAYS);
996
997
      // 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
998
      // Also, insert this read in the list at the proper index
999
1000
6036
      if (!x_shared.isConst()) {
1001
2712
        x_shared = d_valuation.getModelValue(x_shared);
1002
      }
1003
6036
      if (!x_shared.isNull()) {
1004
        CTNodeList* temp;
1005
4411
        CNodeNListMap::iterator it = d_constReads.find(x_shared);
1006
4411
        if (it == d_constReads.end()) {
1007
          // This is the only x_shared with this model value - no need to create any splits
1008
193
          temp = new(true) CTNodeList(d_constReadsContext);
1009
193
          d_constReads[x_shared] = temp;
1010
        }
1011
        else {
1012
4218
          temp = (*it).second;
1013
207138
          for (size_t j = 0; j < temp->size(); ++j) {
1014
202920
            checkPair(r1, (*temp)[j]);
1015
          }
1016
        }
1017
4411
        temp->push_back(r1);
1018
      }
1019
      else {
1020
        // We don't know the model value for x.  Just do brute force examination of all pairs of reads
1021
57473
        for (unsigned j = 0; j < size; ++j) {
1022
111696
          TNode r2 = d_reads[j];
1023
55848
          Assert(d_equalityEngine->hasTerm(r2));
1024
55848
          checkPair(r1,r2);
1025
        }
1026
9738
        for (unsigned j = 0; j < d_constReadsList.size(); ++j) {
1027
16226
          TNode r2 = d_constReadsList[j];
1028
8113
          Assert(d_equalityEngine->hasTerm(r2));
1029
8113
          checkPair(r1,r2);
1030
        }
1031
      }
1032
    }
1033
619
    d_constReadsContext->pop();
1034
  }
1035
}
1036
1037
1038
/////////////////////////////////////////////////////////////////////////////
1039
// MODEL GENERATION
1040
/////////////////////////////////////////////////////////////////////////////
1041
1042
5056
bool TheoryArrays::collectModelValues(TheoryModel* m,
1043
                                      const std::set<Node>& termSet)
1044
{
1045
  // termSet contains terms appearing in assertions and shared terms, and also
1046
  // includes additional reads due to the RIntro1 and RIntro2 rules.
1047
5056
  NodeManager* nm = NodeManager::currentNM();
1048
  // Compute arrays that we need to produce representatives for
1049
10112
  std::vector<Node> arrays;
1050
1051
5056
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
1052
31148
  for (; !eqcs_i.isFinished(); ++eqcs_i)
1053
  {
1054
14028
    Node eqc = (*eqcs_i);
1055
13046
    if (!eqc.getType().isArray())
1056
    {
1057
      // not an array, skip
1058
12064
      continue;
1059
    }
1060
982
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
1061
1654
    for (; !eqc_i.isFinished(); ++eqc_i)
1062
    {
1063
1363
      Node n = *eqc_i;
1064
      // If this EC is an array type and it contains something other than STORE
1065
      // nodes, we have to compute a representative explicitly
1066
1027
      if (termSet.find(n) != termSet.end())
1067
      {
1068
987
        if (n.getKind() != kind::STORE)
1069
        {
1070
691
          arrays.push_back(n);
1071
691
          break;
1072
        }
1073
      }
1074
    }
1075
  }
1076
1077
  // Build a list of all the relevant reads, indexed by the store representative
1078
10112
  std::map<Node, std::vector<Node> > selects;
1079
5056
  set<Node>::iterator set_it = termSet.begin(), set_it_end = termSet.end();
1080
20618
  for (; set_it != set_it_end; ++set_it)
1081
  {
1082
15562
    Node n = *set_it;
1083
    // If this term is a select, record that the EC rep of its store parameter
1084
    // is being read from using this term
1085
7781
    if (n.getKind() == kind::SELECT)
1086
    {
1087
4038
      selects[d_equalityEngine->getRepresentative(n[0])].push_back(n);
1088
    }
1089
  }
1090
1091
10112
  Node rep;
1092
5056
  DefValMap::iterator it;
1093
10112
  TypeSet defaultValuesSet;
1094
1095
  // Compute all default values already in use
1096
  // if (fullModel) {
1097
5747
  for (size_t i = 0; i < arrays.size(); ++i)
1098
  {
1099
1382
    TNode nrep = d_equalityEngine->getRepresentative(arrays[i]);
1100
691
    d_mayEqualEqualityEngine.addTerm(
1101
        nrep);  // add the term in case it isn't there already
1102
1382
    TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1103
691
    it = d_defValues.find(mayRep);
1104
691
    if (it != d_defValues.end())
1105
    {
1106
146
      defaultValuesSet.add(nrep.getType().getArrayConstituentType(),
1107
146
                           (*it).second);
1108
    }
1109
  }
1110
  //}
1111
1112
  // Loop through all array equivalence classes that need a representative
1113
  // computed
1114
5747
  for (size_t i = 0; i < arrays.size(); ++i)
1115
  {
1116
1382
    TNode n = arrays[i];
1117
1382
    TNode nrep = d_equalityEngine->getRepresentative(n);
1118
1119
    // if (fullModel) {
1120
    // Compute default value for this array - there is one default value for
1121
    // every mayEqual equivalence class
1122
1382
    TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep);
1123
691
    it = d_defValues.find(mayRep);
1124
    // If this mayEqual EC doesn't have a default value associated, get the next
1125
    // available default value for the associated array element type
1126
691
    if (it == d_defValues.end())
1127
    {
1128
954
      TypeNode valueType = nrep.getType().getArrayConstituentType();
1129
477
      rep = defaultValuesSet.nextTypeEnum(valueType);
1130
477
      if (rep.isNull())
1131
      {
1132
        Assert(defaultValuesSet.getSet(valueType)->begin()
1133
               != defaultValuesSet.getSet(valueType)->end());
1134
        rep = *(defaultValuesSet.getSet(valueType)->begin());
1135
      }
1136
477
      Trace("arrays-models") << "New default value = " << rep << endl;
1137
477
      d_defValues[mayRep] = rep;
1138
    }
1139
    else
1140
    {
1141
214
      rep = (*it).second;
1142
    }
1143
1144
    // Build the STORE_ALL term with the default value
1145
691
    rep = nm->mkConst(ArrayStoreAll(nrep.getType(), rep));
1146
    /*
1147
  }
1148
  else {
1149
    std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(n);
1150
    if (it == d_skolemCache.end()) {
1151
      rep = nm->mkSkolem("array_collect_model_var", n.getType(), "base model
1152
  variable for array collectModelInfo"); d_skolemCache[n] = rep;
1153
    }
1154
    else {
1155
      rep = (*it).second;
1156
    }
1157
  }
1158
*/
1159
1160
    // For each read, require that the rep stores the right value
1161
691
    vector<Node>& reads = selects[nrep];
1162
3349
    for (unsigned j = 0; j < reads.size(); ++j)
1163
    {
1164
2658
      rep = nm->mkNode(kind::STORE, rep, reads[j][1], reads[j]);
1165
    }
1166
691
    if (!m->assertEquality(n, rep, true))
1167
    {
1168
      return false;
1169
    }
1170
691
    if (!n.isConst())
1171
    {
1172
626
      m->assertSkeleton(rep);
1173
    }
1174
  }
1175
5056
  return true;
1176
}
1177
1178
/////////////////////////////////////////////////////////////////////////////
1179
// NOTIFICATIONS
1180
/////////////////////////////////////////////////////////////////////////////
1181
1182
1183
15237
void TheoryArrays::presolve()
1184
{
1185
15237
  Trace("arrays")<<"Presolving \n";
1186
15237
  if (!d_dstratInit)
1187
  {
1188
9437
    d_dstratInit = true;
1189
    // add the decision strategy, which is user-context-independent
1190
18874
    d_im.getDecisionManager()->registerStrategy(
1191
        DecisionManager::STRAT_ARRAYS,
1192
9437
        d_dstrat.get(),
1193
        DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT);
1194
  }
1195
15237
}
1196
1197
1198
/////////////////////////////////////////////////////////////////////////////
1199
// MAIN SOLVER
1200
/////////////////////////////////////////////////////////////////////////////
1201
1202
3019
Node TheoryArrays::getSkolem(TNode ref)
1203
{
1204
  // the call to SkolemCache::getExtIndexSkolem should be deterministic, but use
1205
  // cache anyways for now
1206
3019
  Node skolem;
1207
3019
  std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(ref);
1208
3019
  if (it == d_skolemCache.end()) {
1209
570
    Assert(ref.getKind() == kind::NOT && ref[0].getKind() == kind::EQUAL);
1210
    // make the skolem using the skolem cache utility
1211
570
    skolem = SkolemCache::getExtIndexSkolem(ref);
1212
570
    d_skolemCache[ref] = skolem;
1213
  }
1214
  else {
1215
2449
    skolem = (*it).second;
1216
  }
1217
1218
3019
  Debug("pf::array") << "Pregistering a Skolem" << std::endl;
1219
3019
  preRegisterTermInternal(skolem);
1220
3019
  Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl;
1221
1222
3019
  Debug("pf::array") << "getSkolem DONE" << std::endl;
1223
3019
  return skolem;
1224
}
1225
1226
93667
void TheoryArrays::postCheck(Effort level)
1227
{
1228
281001
  if ((options::arraysEagerLemmas() || fullEffort(level))
1229
121083
      && !d_state.isInConflict() && options::arraysWeakEquivalence())
1230
  {
1231
    // Replay all array merges to update weak equivalence data structures
1232
    context::CDList<Node>::iterator it = d_arrayMerges.begin(), iend = d_arrayMerges.end();
1233
    TNode a, b, eq;
1234
    for (; it != iend; ++it) {
1235
      eq = *it;
1236
      a = eq[0];
1237
      b = eq[1];
1238
      weakEquivMakeRep(b);
1239
      if (weakEquivGetRep(a) == b) {
1240
        weakEquivAddSecondary(TNode(), a, b, eq);
1241
      }
1242
      else {
1243
        d_infoMap.setWeakEquivPointer(b, a);
1244
        d_infoMap.setWeakEquivIndex(b, TNode());
1245
      }
1246
#ifdef CVC5_ASSERTIONS
1247
      checkWeakEquiv(false);
1248
#endif
1249
    }
1250
#ifdef CVC5_ASSERTIONS
1251
    checkWeakEquiv(true);
1252
#endif
1253
1254
    d_readTableContext->push();
1255
    TNode mayRep, iRep;
1256
    CTNodeList* bucketList = NULL;
1257
    CTNodeList::const_iterator i = d_reads.begin(), readsEnd = d_reads.end();
1258
    for (; i != readsEnd; ++i) {
1259
      const TNode& r = *i;
1260
1261
      Debug("arrays::weak") << "TheoryArrays::check(): checking read " << r << std::endl;
1262
1263
      // Find the bucket for this read.
1264
      mayRep = d_mayEqualEqualityEngine.getRepresentative(r[0]);
1265
      iRep = d_equalityEngine->getRepresentative(r[1]);
1266
      std::pair<TNode, TNode> key(mayRep, iRep);
1267
      ReadBucketMap::iterator rbm_it = d_readBucketTable.find(key);
1268
      if (rbm_it == d_readBucketTable.end())
1269
      {
1270
        bucketList = new(true) CTNodeList(d_readTableContext);
1271
        d_readBucketAllocations.push_back(bucketList);
1272
        d_readBucketTable[key] = bucketList;
1273
      }
1274
      else {
1275
        bucketList = rbm_it->second;
1276
      }
1277
      CTNodeList::const_iterator ctnl_it = bucketList->begin(),
1278
                                 ctnl_iend = bucketList->end();
1279
      for (; ctnl_it != ctnl_iend; ++ctnl_it)
1280
      {
1281
        const TNode& r2 = *ctnl_it;
1282
        Assert(r2.getKind() == kind::SELECT);
1283
        Assert(mayRep == d_mayEqualEqualityEngine.getRepresentative(r2[0]));
1284
        Assert(iRep == d_equalityEngine->getRepresentative(r2[1]));
1285
        if (d_equalityEngine->areEqual(r, r2))
1286
        {
1287
          continue;
1288
        }
1289
        if (weakEquivGetRepIndex(r[0], r[1]) == weakEquivGetRepIndex(r2[0], r[1])) {
1290
          // add lemma: r[1] = r2[1] /\ cond(r[0],r2[0]) => r = r2
1291
          vector<TNode> conjunctions;
1292
          Assert(d_equalityEngine->areEqual(r, Rewriter::rewrite(r)));
1293
          Assert(d_equalityEngine->areEqual(r2, Rewriter::rewrite(r2)));
1294
          Node lemma = Rewriter::rewrite(r).eqNode(Rewriter::rewrite(r2)).negate();
1295
          d_permRef.push_back(lemma);
1296
          conjunctions.push_back(lemma);
1297
          if (r[1] != r2[1]) {
1298
            d_equalityEngine->explainEquality(r[1], r2[1], true, conjunctions);
1299
          }
1300
          // TODO: get smaller lemmas by eliminating shared parts of path
1301
          weakEquivBuildCond(r[0], r[1], conjunctions);
1302
          weakEquivBuildCond(r2[0], r[1], conjunctions);
1303
          lemma = mkAnd(conjunctions, true);
1304
          // LSH FIXME: which kind of arrays lemma is this
1305
          Trace("arrays-lem")
1306
              << "Arrays::addExtLemma (weak-eq) " << lemma << "\n";
1307
          d_out->lemma(lemma, LemmaProperty::SEND_ATOMS);
1308
          d_readTableContext->pop();
1309
          Trace("arrays") << spaces(context()->getLevel())
1310
                          << "Arrays::check(): done" << endl;
1311
          return;
1312
        }
1313
      }
1314
      bucketList->push_back(r);
1315
    }
1316
    d_readTableContext->pop();
1317
  }
1318
1319
281001
  if (!options::arraysEagerLemmas() && fullEffort(level)
1320
121083
      && !d_state.isInConflict() && !options::arraysWeakEquivalence())
1321
  {
1322
    // generate the lemmas on the worklist
1323
27410
    Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue.size() << "\n";
1324
27934
    while (d_RowQueue.size() > 0 && !d_state.isInConflict())
1325
    {
1326
1006
      if (dischargeLemmas()) {
1327
744
        break;
1328
      }
1329
    }
1330
  }
1331
1332
187334
  Trace("arrays") << spaces(context()->getLevel()) << "Arrays::check(): done"
1333
93667
                  << endl;
1334
}
1335
1336
217275
bool TheoryArrays::preNotifyFact(
1337
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
1338
{
1339
217275
  if (!isInternal && !isPrereg)
1340
  {
1341
168477
    if (atom.getKind() == kind::EQUAL)
1342
    {
1343
168415
      if (!d_equalityEngine->hasTerm(atom[0]))
1344
      {
1345
1672
        Assert(atom[0].isConst());
1346
1672
        d_equalityEngine->addTerm(atom[0]);
1347
      }
1348
168415
      if (!d_equalityEngine->hasTerm(atom[1]))
1349
      {
1350
543
        Assert(atom[1].isConst());
1351
543
        d_equalityEngine->addTerm(atom[1]);
1352
      }
1353
    }
1354
  }
1355
217275
  return false;
1356
}
1357
1358
217272
void TheoryArrays::notifyFact(TNode atom, bool pol, TNode fact, bool isInternal)
1359
{
1360
  // if a disequality
1361
217272
  if (atom.getKind() == kind::EQUAL && !pol && !isInternal)
1362
  {
1363
    // Notice that this should be an external assertion, since we do not
1364
    // internally infer disequalities.
1365
    // Apply ArrDiseq Rule if diseq is between arrays
1366
43115
    if (fact[0][0].getType().isArray() && !d_state.isInConflict())
1367
    {
1368
3019
      NodeManager* nm = NodeManager::currentNM();
1369
1370
6038
      TNode k;
1371
      // k is the skolem for this disequality.
1372
6038
      Debug("pf::array") << "Check: kind::NOT: array theory making a skolem"
1373
3019
                          << std::endl;
1374
1375
      // If not in replay mode, generate a fresh skolem variable
1376
3019
      k = getSkolem(fact);
1377
1378
6038
      Node ak = nm->mkNode(kind::SELECT, fact[0][0], k);
1379
6038
      Node bk = nm->mkNode(kind::SELECT, fact[0][1], k);
1380
6038
      Node eq = ak.eqNode(bk);
1381
6038
      Node lemma = fact[0].orNode(eq.notNode());
1382
1383
9057
      if (options::arraysPropagate() > 0 && d_equalityEngine->hasTerm(ak)
1384
8383
          && d_equalityEngine->hasTerm(bk))
1385
      {
1386
        // Propagate witness disequality - might produce a conflict
1387
4690
        Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1388
2345
                           << "\teq = " << eq << std::endl
1389
2345
                           << "\treason = " << fact << std::endl;
1390
2345
        d_im.assertInference(eq, false, InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1391
2345
        ++d_numProp;
1392
      }
1393
1394
      // If this is the solution pass, generate the lemma. Otherwise, don't
1395
      // generate it - as this is the lemma that we're reproving...
1396
3019
      Trace("arrays-lem") << "Arrays::addExtLemma " << lemma << "\n";
1397
3019
      d_im.arrayLemma(eq.notNode(), InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1398
3019
      ++d_numExt;
1399
    }
1400
    else
1401
    {
1402
80192
      Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem"
1403
40096
                         << std::endl;
1404
40096
      d_modelConstraints.push_back(fact);
1405
    }
1406
  }
1407
217272
}
1408
1409
Node TheoryArrays::mkAnd(std::vector<TNode>& conjunctions, bool invert, unsigned startIndex)
1410
{
1411
  if (conjunctions.empty())
1412
  {
1413
    return invert ? d_false : d_true;
1414
  }
1415
1416
  std::set<TNode> all;
1417
1418
  unsigned i = startIndex;
1419
  TNode t;
1420
  for (; i < conjunctions.size(); ++i) {
1421
    t = conjunctions[i];
1422
    if (t == d_true) {
1423
      continue;
1424
    }
1425
    else if (t.getKind() == kind::AND) {
1426
      for(TNode::iterator child_it = t.begin(); child_it != t.end(); ++child_it) {
1427
        if (*child_it == d_true) {
1428
          continue;
1429
        }
1430
        all.insert(*child_it);
1431
      }
1432
    }
1433
    else {
1434
      all.insert(t);
1435
    }
1436
  }
1437
1438
  if (all.size() == 0) {
1439
    return invert ? d_false : d_true;
1440
  }
1441
  if (all.size() == 1) {
1442
    // All the same, or just one
1443
    if (invert) {
1444
      return (*(all.begin())).negate();
1445
    }
1446
    else {
1447
      return *(all.begin());
1448
    }
1449
  }
1450
1451
  NodeBuilder conjunction(invert ? kind::OR : kind::AND);
1452
  std::set<TNode>::const_iterator it = all.begin();
1453
  std::set<TNode>::const_iterator it_end = all.end();
1454
  while (it != it_end) {
1455
    if (invert) {
1456
      conjunction << (*it).negate();
1457
    }
1458
    else {
1459
      conjunction << *it;
1460
    }
1461
    ++ it;
1462
  }
1463
1464
  return conjunction;
1465
}
1466
1467
1468
1927
void TheoryArrays::setNonLinear(TNode a)
1469
{
1470
2122
  if (options::arraysWeakEquivalence()) return;
1471
1927
  if (d_infoMap.isNonLinear(a)) return;
1472
1473
3464
  Trace("arrays") << spaces(context()->getLevel()) << "Arrays::setNonLinear ("
1474
1732
                  << a << ")\n";
1475
1732
  d_infoMap.setNonLinear(a);
1476
1732
  ++d_numNonLinear;
1477
1478
1732
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1479
1732
  const CTNodeList* st_a = d_infoMap.getStores(a);
1480
1732
  const CTNodeList* inst_a = d_infoMap.getInStores(a);
1481
1482
1732
  size_t it = 0;
1483
1484
  // Propagate non-linearity down chain of stores
1485
4140
  for( ; it < st_a->size(); ++it) {
1486
2408
    TNode store = (*st_a)[it];
1487
1204
    Assert(store.getKind() == kind::STORE);
1488
1204
    setNonLinear(store[0]);
1489
  }
1490
1491
  // Instantiate ROW lemmas that were ignored before
1492
1732
  size_t it2 = 0;
1493
3464
  RowLemmaType lem;
1494
20668
  for(; it2 < i_a->size(); ++it2) {
1495
18936
    TNode i = (*i_a)[it2];
1496
9468
    it = 0;
1497
28480
    for ( ; it < inst_a->size(); ++it) {
1498
19012
      TNode store = (*inst_a)[it];
1499
9506
      Assert(store.getKind() == kind::STORE);
1500
19012
      TNode j = store[1];
1501
19012
      TNode c = store[0];
1502
9506
      lem = std::make_tuple(store, c, j, i);
1503
19012
      Trace("arrays-lem") << spaces(context()->getLevel())
1504
9506
                          << "Arrays::setNonLinear (" << store << ", " << c
1505
9506
                          << ", " << j << ", " << i << ")\n";
1506
9506
      queueRowLemma(lem);
1507
    }
1508
  }
1509
1510
}
1511
1512
5813
void TheoryArrays::mergeArrays(TNode a, TNode b)
1513
{
1514
  // Note: a is the new representative
1515
5813
  Assert(a.getType().isArray() && b.getType().isArray());
1516
1517
5813
  if (d_mergeInProgress) {
1518
    // Nested call to mergeArrays, just push on the queue and return
1519
    d_mergeQueue.push(a.eqNode(b));
1520
    return;
1521
  }
1522
1523
5813
  d_mergeInProgress = true;
1524
1525
11626
  Node n;
1526
  while (true) {
1527
    // Normally, a is its own representative, but it's possible for a to have
1528
    // been merged with another array after it got queued up by the equality engine,
1529
    // so we take its representative to be safe.
1530
5813
    a = d_equalityEngine->getRepresentative(a);
1531
5813
    Assert(d_equalityEngine->getRepresentative(b) == a);
1532
11626
    Trace("arrays-merge") << spaces(context()->getLevel()) << "Arrays::merge: ("
1533
5813
                          << a << ", " << b << ")\n";
1534
1535
5813
    if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1536
4101
      bool aNL = d_infoMap.isNonLinear(a);
1537
4101
      bool bNL = d_infoMap.isNonLinear(b);
1538
4101
      if (aNL) {
1539
260
        if (bNL) {
1540
          // already both marked as non-linear - no need to do anything
1541
        }
1542
        else {
1543
          // Set b to be non-linear
1544
233
          setNonLinear(b);
1545
        }
1546
      }
1547
      else {
1548
3841
        if (bNL) {
1549
          // Set a to be non-linear
1550
48
          setNonLinear(a);
1551
        }
1552
        else {
1553
          // Check for new non-linear arrays
1554
3793
          const CTNodeList* astores = d_infoMap.getStores(a);
1555
3793
          const CTNodeList* bstores = d_infoMap.getStores(b);
1556
3793
          Assert(astores->size() <= 1 && bstores->size() <= 1);
1557
3793
          if (astores->size() > 0 && bstores->size() > 0) {
1558
221
            setNonLinear(a);
1559
221
            setNonLinear(b);
1560
          }
1561
        }
1562
      }
1563
    }
1564
1565
5816
    TNode constArrA = d_infoMap.getConstArr(a);
1566
5816
    TNode constArrB = d_infoMap.getConstArr(b);
1567
5813
    if (constArrA.isNull()) {
1568
5650
      if (!constArrB.isNull()) {
1569
        d_infoMap.setConstArr(a,constArrB);
1570
      }
1571
    }
1572
163
    else if (!constArrB.isNull()) {
1573
      if (constArrA != constArrB) {
1574
        conflict(constArrA,constArrB);
1575
      }
1576
    }
1577
1578
5816
    TNode mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1579
5816
    TNode mayRepB = d_mayEqualEqualityEngine.getRepresentative(b);
1580
1581
    // If a and b have different default values associated with their mayequal equivalence classes,
1582
    // things get complicated.  Similarly, if two mayequal equivalence classes have different
1583
    // constant representatives, it's not clear what to do. - disallow these cases for now.  -Clark
1584
5813
    DefValMap::iterator it = d_defValues.find(mayRepA);
1585
5813
    DefValMap::iterator it2 = d_defValues.find(mayRepB);
1586
5816
    TNode defValue;
1587
1588
5813
    if (it != d_defValues.end()) {
1589
260
      defValue = (*it).second;
1590
521
      if ((it2 != d_defValues.end() && (defValue != (*it2).second)) ||
1591
516
          (mayRepA.isConst() && mayRepB.isConst() && mayRepA != mayRepB)) {
1592
3
        throw LogicException("Array theory solver does not yet support write-chains connecting two different constant arrays");
1593
      }
1594
    }
1595
5553
    else if (it2 != d_defValues.end()) {
1596
      defValue = (*it2).second;
1597
    }
1598
5810
    d_mayEqualEqualityEngine.assertEquality(a.eqNode(b), true, d_true);
1599
5810
    Assert(d_mayEqualEqualityEngine.consistent());
1600
5810
    if (!defValue.isNull()) {
1601
257
      mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1602
257
      d_defValues[mayRepA] = defValue;
1603
    }
1604
1605
5810
    checkRowLemmas(a,b);
1606
5810
    checkRowLemmas(b,a);
1607
1608
    // merge info adds the list of the 2nd argument to the first
1609
5810
    d_infoMap.mergeInfo(a, b);
1610
1611
5810
    if (options::arraysWeakEquivalence()) {
1612
      d_arrayMerges.push_back(a.eqNode(b));
1613
    }
1614
1615
    // If no more to do, break
1616
5810
    if (d_state.isInConflict() || d_mergeQueue.empty())
1617
    {
1618
5810
      break;
1619
    }
1620
1621
    // Otherwise, prepare for next iteration
1622
    n = d_mergeQueue.front();
1623
    a = n[0];
1624
    b = n[1];
1625
    d_mergeQueue.pop();
1626
  }
1627
5810
  d_mergeInProgress = false;
1628
}
1629
1630
1631
1112
void TheoryArrays::checkStore(TNode a) {
1632
1112
  if (options::arraysWeakEquivalence()) return;
1633
1112
  Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
1634
1635
1112
  if(Trace.isOn("arrays-cri")) {
1636
    d_infoMap.getInfo(a)->print();
1637
  }
1638
1112
  Assert(a.getType().isArray());
1639
1112
  Assert(a.getKind() == kind::STORE);
1640
2224
  TNode b = a[0];
1641
2224
  TNode i = a[1];
1642
1643
2224
  TNode brep = d_equalityEngine->getRepresentative(b);
1644
1645
1112
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(brep)) {
1646
258
    const CTNodeList* js = d_infoMap.getIndices(brep);
1647
258
    size_t it = 0;
1648
516
    RowLemmaType lem;
1649
828
    for(; it < js->size(); ++it) {
1650
546
      TNode j = (*js)[it];
1651
285
      if (i == j) continue;
1652
261
      lem = std::make_tuple(a, b, i, j);
1653
522
      Trace("arrays-lem") << spaces(context()->getLevel())
1654
261
                          << "Arrays::checkStore (" << a << ", " << b << ", "
1655
261
                          << i << ", " << j << ")\n";
1656
261
      queueRowLemma(lem);
1657
    }
1658
  }
1659
}
1660
1661
1662
24006
void TheoryArrays::checkRowForIndex(TNode i, TNode a)
1663
{
1664
24006
  if (options::arraysWeakEquivalence()) return;
1665
24006
  Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a<<"\n";
1666
24006
  Trace("arrays-cri")<<"                   index "<<i<<"\n";
1667
1668
24006
  if(Trace.isOn("arrays-cri")) {
1669
    d_infoMap.getInfo(a)->print();
1670
  }
1671
24006
  Assert(a.getType().isArray());
1672
24006
  Assert(d_equalityEngine->getRepresentative(a) == a);
1673
1674
48012
  TNode constArr = d_infoMap.getConstArr(a);
1675
24006
  if (!constArr.isNull()) {
1676
510
    ArrayStoreAll storeAll = constArr.getConst<ArrayStoreAll>();
1677
510
    Node defValue = storeAll.getValue();
1678
510
    Node selConst = NodeManager::currentNM()->mkNode(kind::SELECT, constArr, i);
1679
255
    if (!d_equalityEngine->hasTerm(selConst))
1680
    {
1681
9
      preRegisterTermInternal(selConst);
1682
    }
1683
    // not currently supported in proofs, use THEORY_INFERENCE
1684
255
    d_im.assertInference(selConst.eqNode(defValue),
1685
                         true,
1686
                         InferenceId::ARRAYS_CONST_ARRAY_DEFAULT,
1687
                         d_true,
1688
                         PfRule::THEORY_INFERENCE);
1689
  }
1690
1691
24006
  const CTNodeList* stores = d_infoMap.getStores(a);
1692
24006
  const CTNodeList* instores = d_infoMap.getInStores(a);
1693
24006
  size_t it = 0;
1694
48012
  RowLemmaType lem;
1695
1696
54524
  for(; it < stores->size(); ++it) {
1697
30405
    TNode store = (*stores)[it];
1698
15259
    Assert(store.getKind() == kind::STORE);
1699
30405
    TNode j = store[1];
1700
15259
    if (i == j) continue;
1701
15146
    lem = std::make_tuple(store, store[0], j, i);
1702
30292
    Trace("arrays-lem") << spaces(context()->getLevel())
1703
15146
                        << "Arrays::checkRowForIndex (" << store << ", "
1704
15146
                        << store[0] << ", " << j << ", " << i << ")\n";
1705
15146
    queueRowLemma(lem);
1706
  }
1707
1708
24006
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(a)) {
1709
7659
    it = 0;
1710
20377
    for(; it < instores->size(); ++it) {
1711
12471
      TNode instore = (*instores)[it];
1712
6359
      Assert(instore.getKind() == kind::STORE);
1713
12471
      TNode j = instore[1];
1714
6359
      if (i == j) continue;
1715
6112
      lem = std::make_tuple(instore, instore[0], j, i);
1716
12224
      Trace("arrays-lem") << spaces(context()->getLevel())
1717
6112
                          << "Arrays::checkRowForIndex (" << instore << ", "
1718
6112
                          << instore[0] << ", " << j << ", " << i << ")\n";
1719
6112
      queueRowLemma(lem);
1720
    }
1721
  }
1722
}
1723
1724
1725
// a just became equal to b
1726
// look for new ROW lemmas
1727
11620
void TheoryArrays::checkRowLemmas(TNode a, TNode b)
1728
{
1729
11620
  if (options::arraysWeakEquivalence()) return;
1730
11620
  Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
1731
11620
  if(Trace.isOn("arrays-crl"))
1732
    d_infoMap.getInfo(a)->print();
1733
11620
  Trace("arrays-crl")<<"  ------------  and "<<b<<"\n";
1734
11620
  if(Trace.isOn("arrays-crl"))
1735
    d_infoMap.getInfo(b)->print();
1736
1737
11620
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1738
11620
  size_t it = 0;
1739
23240
  TNode constArr = d_infoMap.getConstArr(b);
1740
11620
  if (!constArr.isNull()) {
1741
627
    for( ; it < i_a->size(); ++it) {
1742
464
      TNode i = (*i_a)[it];
1743
464
      Node selConst = NodeManager::currentNM()->mkNode(kind::SELECT, constArr, i);
1744
232
      if (!d_equalityEngine->hasTerm(selConst))
1745
      {
1746
232
        preRegisterTermInternal(selConst);
1747
      }
1748
    }
1749
  }
1750
1751
11620
  const CTNodeList* st_b = d_infoMap.getStores(b);
1752
11620
  const CTNodeList* inst_b = d_infoMap.getInStores(b);
1753
  size_t its;
1754
1755
23240
  RowLemmaType lem;
1756
1757
63242
  for(it = 0 ; it < i_a->size(); ++it) {
1758
103244
    TNode i = (*i_a)[it];
1759
51622
    its = 0;
1760
100096
    for ( ; its < st_b->size(); ++its) {
1761
48474
      TNode store = (*st_b)[its];
1762
24237
      Assert(store.getKind() == kind::STORE);
1763
48474
      TNode j = store[1];
1764
48474
      TNode c = store[0];
1765
24237
      lem = std::make_tuple(store, c, j, i);
1766
48474
      Trace("arrays-lem") << spaces(context()->getLevel())
1767
24237
                          << "Arrays::checkRowLemmas (" << store << ", " << c
1768
24237
                          << ", " << j << ", " << i << ")\n";
1769
24237
      queueRowLemma(lem);
1770
    }
1771
  }
1772
1773
11620
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(b)) {
1774
29048
    for(it = 0 ; it < i_a->size(); ++it ) {
1775
49144
      TNode i = (*i_a)[it];
1776
24572
      its = 0;
1777
49174
      for ( ; its < inst_b->size(); ++its) {
1778
24602
        TNode store = (*inst_b)[its];
1779
12301
        Assert(store.getKind() == kind::STORE);
1780
24602
        TNode j = store[1];
1781
24602
        TNode c = store[0];
1782
12301
        lem = std::make_tuple(store, c, j, i);
1783
24602
        Trace("arrays-lem")
1784
24602
            << spaces(context()->getLevel()) << "Arrays::checkRowLemmas ("
1785
12301
            << store << ", " << c << ", " << j << ", " << i << ")\n";
1786
12301
        queueRowLemma(lem);
1787
      }
1788
    }
1789
  }
1790
11620
  Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1791
}
1792
1793
22768
void TheoryArrays::propagateRowLemma(RowLemmaType lem)
1794
{
1795
45536
  Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1796
22768
                     << options::arraysPropagate() << std::endl;
1797
1798
35718
  TNode a, b, i, j;
1799
22768
  std::tie(a, b, i, j) = lem;
1800
1801
22768
  Assert(a.getType().isArray() && b.getType().isArray());
1802
22768
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1803
  {
1804
    return;
1805
  }
1806
1807
22768
  NodeManager* nm = NodeManager::currentNM();
1808
35718
  Node aj = nm->mkNode(kind::SELECT, a, j);
1809
35718
  Node bj = nm->mkNode(kind::SELECT, b, j);
1810
1811
  // Try to avoid introducing new read terms: track whether these already exist
1812
22768
  bool ajExists = d_equalityEngine->hasTerm(aj);
1813
22768
  bool bjExists = d_equalityEngine->hasTerm(bj);
1814
22768
  bool bothExist = ajExists && bjExists;
1815
1816
  // If propagating, check propagations
1817
22768
  int prop = options::arraysPropagate();
1818
22768
  if (prop > 0) {
1819
22768
    if (d_equalityEngine->areDisequal(i, j, true) && (bothExist || prop > 1))
1820
    {
1821
19628
      Trace("arrays-lem") << spaces(context()->getLevel())
1822
9814
                          << "Arrays::queueRowLemma: propagating aj = bj ("
1823
9814
                          << aj << ", " << bj << ")\n";
1824
19628
      Node aj_eq_bj = aj.eqNode(bj);
1825
      Node reason =
1826
19628
          (i.isConst() && j.isConst()) ? d_true : i.eqNode(j).notNode();
1827
9814
      d_permRef.push_back(reason);
1828
9814
      if (!ajExists) {
1829
2987
        preRegisterTermInternal(aj);
1830
      }
1831
9814
      if (!bjExists) {
1832
5692
        preRegisterTermInternal(bj);
1833
      }
1834
9814
      d_im.assertInference(
1835
          aj_eq_bj, true, InferenceId::ARRAYS_READ_OVER_WRITE, reason, PfRule::ARRAYS_READ_OVER_WRITE);
1836
9814
      ++d_numProp;
1837
9814
      return;
1838
    }
1839
12954
    if (bothExist && d_equalityEngine->areDisequal(aj, bj, true))
1840
    {
1841
8
      Trace("arrays-lem") << spaces(context()->getLevel())
1842
4
                          << "Arrays::queueRowLemma: propagating i = j (" << i
1843
4
                          << ", " << j << ")\n";
1844
      Node reason =
1845
8
          (aj.isConst() && bj.isConst()) ? d_true : aj.eqNode(bj).notNode();
1846
8
      Node j_eq_i = j.eqNode(i);
1847
4
      d_im.assertInference(
1848
          j_eq_i, true, InferenceId::ARRAYS_READ_OVER_WRITE_CONTRA, reason, PfRule::ARRAYS_READ_OVER_WRITE_CONTRA);
1849
4
      ++d_numProp;
1850
4
      return;
1851
    }
1852
  }
1853
}
1854
1855
67563
void TheoryArrays::queueRowLemma(RowLemmaType lem)
1856
{
1857
67563
  Debug("pf::array") << "Array solver: queue row lemma called" << std::endl;
1858
1859
67563
  if (d_state.isInConflict() || d_RowAlreadyAdded.contains(lem))
1860
  {
1861
79826
    return;
1862
  }
1863
55300
  TNode a, b, i, j;
1864
35517
  std::tie(a, b, i, j) = lem;
1865
1866
35517
  Assert(a.getType().isArray() && b.getType().isArray());
1867
35517
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1868
  {
1869
14611
    return;
1870
  }
1871
1872
20906
  NodeManager* nm = NodeManager::currentNM();
1873
40689
  Node aj = nm->mkNode(kind::SELECT, a, j);
1874
40689
  Node bj = nm->mkNode(kind::SELECT, b, j);
1875
1876
  // Try to avoid introducing new read terms: track whether these already exist
1877
20906
  bool ajExists = d_equalityEngine->hasTerm(aj);
1878
20906
  bool bjExists = d_equalityEngine->hasTerm(bj);
1879
20906
  bool bothExist = ajExists && bjExists;
1880
1881
  // If propagating, check propagations
1882
20906
  int prop = options::arraysPropagate();
1883
20906
  if (prop > 0) {
1884
20906
    propagateRowLemma(lem);
1885
  }
1886
1887
  // Prefer equality between indexes so as not to introduce new read terms
1888
55368
  if (options::arraysEagerIndexSplitting() && !bothExist
1889
53065
      && !d_equalityEngine->areDisequal(i, j, false))
1890
  {
1891
11236
    Node i_eq_j;
1892
5618
    i_eq_j = d_valuation.ensureLiteral(i.eqNode(j));  // TODO: think about this
1893
#if 0
1894
    i_eq_j = i.eqNode(j);
1895
#endif
1896
5618
    getOutputChannel().requirePhase(i_eq_j, true);
1897
5618
    d_decisionRequests.push(i_eq_j);
1898
  }
1899
1900
  // TODO: maybe add triggers here
1901
1902
20906
  if (options::arraysEagerLemmas() || bothExist)
1903
  {
1904
    // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1905
4033
    Node aj2 = Rewriter::rewrite(aj);
1906
2578
    if (aj != aj2) {
1907
1123
      if (!ajExists) {
1908
        preRegisterTermInternal(aj);
1909
      }
1910
1123
      if (!d_equalityEngine->hasTerm(aj2))
1911
      {
1912
        preRegisterTermInternal(aj2);
1913
      }
1914
1123
      d_im.assertInference(aj.eqNode(aj2),
1915
                           true,
1916
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
1917
                           d_true,
1918
                           PfRule::MACRO_SR_PRED_INTRO);
1919
    }
1920
4033
    Node bj2 = Rewriter::rewrite(bj);
1921
2578
    if (bj != bj2) {
1922
732
      if (!bjExists) {
1923
        preRegisterTermInternal(bj);
1924
      }
1925
732
      if (!d_equalityEngine->hasTerm(bj2))
1926
      {
1927
        preRegisterTermInternal(bj2);
1928
      }
1929
732
      d_im.assertInference(bj.eqNode(bj2),
1930
                           true,
1931
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
1932
                           d_true,
1933
                           PfRule::MACRO_SR_PRED_INTRO);
1934
    }
1935
2578
    if (aj2 == bj2) {
1936
1123
      return;
1937
    }
1938
1939
    // construct lemma
1940
2910
    Node eq1 = aj2.eqNode(bj2);
1941
2910
    Node eq1_r = Rewriter::rewrite(eq1);
1942
1455
    if (eq1_r == d_true) {
1943
      if (!d_equalityEngine->hasTerm(aj2))
1944
      {
1945
        preRegisterTermInternal(aj2);
1946
      }
1947
      if (!d_equalityEngine->hasTerm(bj2))
1948
      {
1949
        preRegisterTermInternal(bj2);
1950
      }
1951
      d_im.assertInference(eq1,
1952
                           true,
1953
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
1954
                           d_true,
1955
                           PfRule::MACRO_SR_PRED_INTRO);
1956
      return;
1957
    }
1958
1959
2910
    Node eq2 = i.eqNode(j);
1960
2910
    Node eq2_r = Rewriter::rewrite(eq2);
1961
1455
    if (eq2_r == d_true) {
1962
      d_im.assertInference(eq2,
1963
                           true,
1964
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
1965
                           d_true,
1966
                           PfRule::MACRO_SR_PRED_INTRO);
1967
      return;
1968
    }
1969
1970
2910
    Node lemma = nm->mkNode(kind::OR, eq2_r, eq1_r);
1971
1972
1455
    Trace("arrays-lem") << "Arrays::addRowLemma (1) adding " << lemma << "\n";
1973
1455
    d_RowAlreadyAdded.insert(lem);
1974
    // use non-rewritten nodes
1975
2910
    d_im.arrayLemma(
1976
2910
        aj.eqNode(bj), InferenceId::ARRAYS_READ_OVER_WRITE, eq2.notNode(), PfRule::ARRAYS_READ_OVER_WRITE);
1977
1455
    ++d_numRow;
1978
  }
1979
  else {
1980
18328
    d_RowQueue.push(lem);
1981
  }
1982
}
1983
1984
2753055
Node TheoryArrays::getNextDecisionRequest()
1985
{
1986
2753055
  if(! d_decisionRequests.empty()) {
1987
31572
    Node n = d_decisionRequests.front();
1988
15786
    d_decisionRequests.pop();
1989
15786
    return n;
1990
  }
1991
2737269
  return Node::null();
1992
}
1993
1994
1995
1006
bool TheoryArrays::dischargeLemmas()
1996
{
1997
1006
  bool lemmasAdded = false;
1998
1006
  size_t sz = d_RowQueue.size();
1999
14409
  for (unsigned count = 0; count < sz; ++count) {
2000
15213
    RowLemmaType l = d_RowQueue.front();
2001
13781
    d_RowQueue.pop();
2002
13781
    if (d_RowAlreadyAdded.contains(l)) {
2003
7849
      continue;
2004
    }
2005
2006
7364
    TNode a, b, i, j;
2007
5932
    std::tie(a, b, i, j) = l;
2008
5932
    Assert(a.getType().isArray() && b.getType().isArray());
2009
2010
5932
    NodeManager* nm = NodeManager::currentNM();
2011
7364
    Node aj = nm->mkNode(kind::SELECT, a, j);
2012
7364
    Node bj = nm->mkNode(kind::SELECT, b, j);
2013
5932
    bool ajExists = d_equalityEngine->hasTerm(aj);
2014
5932
    bool bjExists = d_equalityEngine->hasTerm(bj);
2015
2016
    // Check for redundant lemma
2017
    // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
2018
23728
    if (!d_equalityEngine->hasTerm(i) || !d_equalityEngine->hasTerm(j)
2019
11864
        || d_equalityEngine->areEqual(i, j) || !d_equalityEngine->hasTerm(a)
2020
9662
        || !d_equalityEngine->hasTerm(b) || d_equalityEngine->areEqual(a, b)
2021
21219
        || (ajExists && bjExists && d_equalityEngine->areEqual(aj, bj)))
2022
    {
2023
4070
      continue;
2024
    }
2025
2026
1862
    int prop = options::arraysPropagate();
2027
1862
    if (prop > 0) {
2028
1862
      propagateRowLemma(l);
2029
1862
      if (d_state.isInConflict())
2030
      {
2031
378
        return true;
2032
      }
2033
    }
2034
2035
    // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2036
2916
    Node aj2 = Rewriter::rewrite(aj);
2037
1484
    if (aj != aj2) {
2038
52
      if (!ajExists) {
2039
52
        preRegisterTermInternal(aj);
2040
      }
2041
52
      if (!d_equalityEngine->hasTerm(aj2))
2042
      {
2043
        preRegisterTermInternal(aj2);
2044
      }
2045
52
      d_im.assertInference(aj.eqNode(aj2),
2046
                           true,
2047
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
2048
                           d_true,
2049
                           PfRule::MACRO_SR_PRED_INTRO);
2050
    }
2051
2916
    Node bj2 = Rewriter::rewrite(bj);
2052
1484
    if (bj != bj2) {
2053
69
      if (!bjExists) {
2054
6
        preRegisterTermInternal(bj);
2055
      }
2056
69
      if (!d_equalityEngine->hasTerm(bj2))
2057
      {
2058
        preRegisterTermInternal(bj2);
2059
      }
2060
69
      d_im.assertInference(bj.eqNode(bj2),
2061
                           true,
2062
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
2063
                           d_true,
2064
                           PfRule::MACRO_SR_PRED_INTRO);
2065
    }
2066
1484
    if (aj2 == bj2) {
2067
52
      continue;
2068
    }
2069
2070
    // construct lemma
2071
2864
    Node eq1 = aj2.eqNode(bj2);
2072
2864
    Node eq1_r = Rewriter::rewrite(eq1);
2073
1432
    if (eq1_r == d_true) {
2074
      if (!d_equalityEngine->hasTerm(aj2))
2075
      {
2076
        preRegisterTermInternal(aj2);
2077
      }
2078
      if (!d_equalityEngine->hasTerm(bj2))
2079
      {
2080
        preRegisterTermInternal(bj2);
2081
      }
2082
      d_im.assertInference(eq1,
2083
                           true,
2084
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
2085
                           d_true,
2086
                           PfRule::MACRO_SR_PRED_INTRO);
2087
      continue;
2088
    }
2089
2090
2864
    Node eq2 = i.eqNode(j);
2091
2864
    Node eq2_r = Rewriter::rewrite(eq2);
2092
1432
    if (eq2_r == d_true) {
2093
      d_im.assertInference(eq2,
2094
                           true,
2095
                           InferenceId::ARRAYS_EQ_TAUTOLOGY,
2096
                           d_true,
2097
                           PfRule::MACRO_SR_PRED_INTRO);
2098
      continue;
2099
    }
2100
2101
2864
    Node lem = nm->mkNode(kind::OR, eq2_r, eq1_r);
2102
2103
1432
    Trace("arrays-lem") << "Arrays::addRowLemma (2) adding " << lem << "\n";
2104
1432
    d_RowAlreadyAdded.insert(l);
2105
    // use non-rewritten nodes, theory preprocessing will rewrite
2106
2864
    d_im.arrayLemma(
2107
2864
        aj.eqNode(bj), InferenceId::ARRAYS_READ_OVER_WRITE, eq2.notNode(), PfRule::ARRAYS_READ_OVER_WRITE);
2108
1432
    ++d_numRow;
2109
1432
    lemmasAdded = true;
2110
1432
    if (options::arraysReduceSharing()) {
2111
      return true;
2112
    }
2113
  }
2114
628
  return lemmasAdded;
2115
}
2116
2117
69
void TheoryArrays::conflict(TNode a, TNode b) {
2118
69
  Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl;
2119
69
  if (d_inCheckModel)
2120
  {
2121
    // if in check model, don't send the conflict
2122
    d_state.notifyInConflict();
2123
    return;
2124
  }
2125
69
  d_im.conflictEqConstantMerge(a, b);
2126
}
2127
2128
9913
TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2129
9913
    TheoryArrays* ta)
2130
9913
    : d_ta(ta)
2131
{
2132
9913
}
2133
9437
void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
2134
2753055
Node TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2135
{
2136
2753055
  return d_ta->getNextDecisionRequest();
2137
}
2138
2762492
std::string TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2139
{
2140
2762492
  return std::string("th_arrays_dec");
2141
}
2142
2143
5056
void TheoryArrays::computeRelevantTerms(std::set<Node>& termSet)
2144
{
2145
5056
  NodeManager* nm = NodeManager::currentNM();
2146
  // make sure RIntro1 reads are included in the relevant set of reads
2147
5056
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2148
31148
  for (; !eqcs_i.isFinished(); ++eqcs_i)
2149
  {
2150
14028
    Node eqc = (*eqcs_i);
2151
13046
    if (!eqc.getType().isArray())
2152
    {
2153
      // not an array, skip
2154
12064
      continue;
2155
    }
2156
982
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2157
4766
    for (; !eqc_i.isFinished(); ++eqc_i)
2158
    {
2159
3784
      Node n = *eqc_i;
2160
1892
      if (termSet.find(n) != termSet.end())
2161
      {
2162
1849
        if (n.getKind() == kind::STORE)
2163
        {
2164
          // Make sure RIntro1 reads are included
2165
900
          Node r = nm->mkNode(kind::SELECT, n, n[1]);
2166
900
          Trace("arrays::collectModelInfo")
2167
450
              << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r
2168
450
              << endl;
2169
450
          termSet.insert(r);
2170
        }
2171
      }
2172
    }
2173
  }
2174
2175
  // Now do a fixed-point iteration to get all reads that need to be included
2176
  // because of RIntro2 rule
2177
  bool changed;
2178
5137
  do
2179
  {
2180
5137
    changed = false;
2181
5137
    eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2182
33829
    for (; !eqcs_i.isFinished(); ++eqcs_i)
2183
    {
2184
28692
      Node eqc = (*eqcs_i);
2185
14346
      eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2186
68112
      for (; !eqc_i.isFinished(); ++eqc_i)
2187
      {
2188
53766
        Node n = *eqc_i;
2189
26883
        if (n.getKind() == kind::SELECT && termSet.find(n) != termSet.end())
2190
        {
2191
          // Find all terms equivalent to n[0] and get corresponding read terms
2192
13960
          Node array_eqc = d_equalityEngine->getRepresentative(n[0]);
2193
          eq::EqClassIterator array_eqc_i =
2194
6980
              eq::EqClassIterator(array_eqc, d_equalityEngine);
2195
47734
          for (; !array_eqc_i.isFinished(); ++array_eqc_i)
2196
          {
2197
40754
            Node arr = *array_eqc_i;
2198
40754
            if (arr.getKind() == kind::STORE
2199
27722
                && termSet.find(arr) != termSet.end()
2200
48099
                && !d_equalityEngine->areEqual(arr[1], n[1]))
2201
            {
2202
8682
              Node r = nm->mkNode(kind::SELECT, arr, n[1]);
2203
8682
              if (termSet.find(r) == termSet.end()
2204
8682
                  && d_equalityEngine->hasTerm(r))
2205
              {
2206
42
                Trace("arrays::collectModelInfo")
2207
                    << "TheoryArrays::collectModelInfo, adding RIntro2(a) "
2208
21
                       "read: "
2209
21
                    << r << endl;
2210
21
                termSet.insert(r);
2211
21
                changed = true;
2212
              }
2213
4341
              r = nm->mkNode(kind::SELECT, arr[0], n[1]);
2214
8682
              if (termSet.find(r) == termSet.end()
2215
8682
                  && d_equalityEngine->hasTerm(r))
2216
              {
2217
96
                Trace("arrays::collectModelInfo")
2218
                    << "TheoryArrays::collectModelInfo, adding RIntro2(b) "
2219
48
                       "read: "
2220
48
                    << r << endl;
2221
48
                termSet.insert(r);
2222
48
                changed = true;
2223
              }
2224
            }
2225
          }
2226
2227
          // Find all stores in which n[0] appears and get corresponding read
2228
          // terms
2229
6980
          const CTNodeList* instores = d_infoMap.getInStores(array_eqc);
2230
6980
          size_t it = 0;
2231
20622
          for (; it < instores->size(); ++it)
2232
          {
2233
13642
            TNode instore = (*instores)[it];
2234
6821
            Assert(instore.getKind() == kind::STORE);
2235
13642
            if (termSet.find(instore) != termSet.end()
2236
13642
                && !d_equalityEngine->areEqual(instore[1], n[1]))
2237
            {
2238
8912
              Node r = nm->mkNode(kind::SELECT, instore, n[1]);
2239
8912
              if (termSet.find(r) == termSet.end()
2240
8912
                  && d_equalityEngine->hasTerm(r))
2241
              {
2242
444
                Trace("arrays::collectModelInfo")
2243
                    << "TheoryArrays::collectModelInfo, adding RIntro2(c) "
2244
222
                       "read: "
2245
222
                    << r << endl;
2246
222
                termSet.insert(r);
2247
222
                changed = true;
2248
              }
2249
4456
              r = nm->mkNode(kind::SELECT, instore[0], n[1]);
2250
8912
              if (termSet.find(r) == termSet.end()
2251
8912
                  && d_equalityEngine->hasTerm(r))
2252
              {
2253
88
                Trace("arrays::collectModelInfo")
2254
                    << "TheoryArrays::collectModelInfo, adding RIntro2(d) "
2255
44
                       "read: "
2256
44
                    << r << endl;
2257
44
                termSet.insert(r);
2258
44
                changed = true;
2259
              }
2260
            }
2261
          }
2262
        }
2263
      }
2264
    }
2265
  } while (changed);
2266
5056
}
2267
2268
}  // namespace arrays
2269
}  // namespace theory
2270
29502
}  // namespace cvc5