GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 863 1203 71.7 %
Date: 2021-08-14 Branches: 2318 6407 36.2 %

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