GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 867 1207 71.8 %
Date: 2021-08-01 Branches: 2338 6451 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
9838
TheoryArrays::TheoryArrays(context::Context* c,
59
                           context::UserContext* u,
60
                           OutputChannel& out,
61
                           Valuation valuation,
62
                           const LogicInfo& logicInfo,
63
                           ProofNodeManager* pnm,
64
9838
                           std::string name)
65
    : Theory(THEORY_ARRAYS, c, u, out, valuation, logicInfo, pnm, name),
66
      d_numRow(
67
19676
          smtStatisticsRegistry().registerInt(name + "number of Row lemmas")),
68
      d_numExt(
69
19676
          smtStatisticsRegistry().registerInt(name + "number of Ext lemmas")),
70
      d_numProp(
71
19676
          smtStatisticsRegistry().registerInt(name + "number of propagations")),
72
      d_numExplain(
73
19676
          smtStatisticsRegistry().registerInt(name + "number of explanations")),
74
9838
      d_numNonLinear(smtStatisticsRegistry().registerInt(
75
19676
          name + "number of calls to setNonLinear")),
76
9838
      d_numSharedArrayVarSplits(smtStatisticsRegistry().registerInt(
77
19676
          name + "number of shared array var splits")),
78
9838
      d_numGetModelValSplits(smtStatisticsRegistry().registerInt(
79
19676
          name + "number of getModelVal splits")),
80
9838
      d_numGetModelValConflicts(smtStatisticsRegistry().registerInt(
81
19676
          name + "number of getModelVal conflicts")),
82
9838
      d_numSetModelValSplits(smtStatisticsRegistry().registerInt(
83
19676
          name + "number of setModelVal splits")),
84
9838
      d_numSetModelValConflicts(smtStatisticsRegistry().registerInt(
85
19676
          name + "number of setModelVal conflicts")),
86
19676
      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
19676
      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
9838
      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
9838
      d_readTableContext(new context::Context()),
116
      d_arrayMerges(c),
117
      d_inCheckModel(false),
118
9838
      d_dstrat(new TheoryArraysDecisionStrategy(this)),
119
186922
      d_dstratInit(false)
120
{
121
9838
  d_true = NodeManager::currentNM()->mkConst<bool>(true);
122
9838
  d_false = NodeManager::currentNM()->mkConst<bool>(false);
123
124
  // The preprocessing congruence kinds
125
9838
  d_ppEqualityEngine.addFunctionKind(kind::SELECT);
126
9838
  d_ppEqualityEngine.addFunctionKind(kind::STORE);
127
128
  // indicate we are using the default theory state object, and the arrays
129
  // inference manager
130
9838
  d_theoryState = &d_state;
131
9838
  d_inferManager = &d_im;
132
9838
}
133
134
29514
TheoryArrays::~TheoryArrays() {
135
9838
  vector<CTNodeList*>::iterator it = d_readBucketAllocations.begin(), iend = d_readBucketAllocations.end();
136
9838
  for (; it != iend; ++it) {
137
    (*it)->deleteSelf();
138
  }
139
9838
  delete d_readTableContext;
140
9838
  CNodeNListMap::iterator it2 = d_constReads.begin();
141
11216
  for( ; it2 != d_constReads.end(); ++it2 ) {
142
689
    it2->second->deleteSelf();
143
  }
144
9838
  delete d_constReadsContext;
145
19676
}
146
147
9838
TheoryRewriter* TheoryArrays::getTheoryRewriter() { return &d_rewriter; }
148
149
3756
ProofRuleChecker* TheoryArrays::getProofChecker() { return &d_checker; }
150
151
9838
bool TheoryArrays::needsEqualityEngine(EeSetupInfo& esi)
152
{
153
9838
  esi.d_notify = &d_notify;
154
9838
  esi.d_name = d_instanceName + "ee";
155
9838
  esi.d_notifyNewClass = true;
156
9838
  esi.d_notifyMerge = true;
157
9838
  return true;
158
}
159
160
9838
void TheoryArrays::finishInit()
161
{
162
9838
  Assert(d_equalityEngine != nullptr);
163
164
  // The kinds we are treating as function application in congruence
165
9838
  d_equalityEngine->addFunctionKind(kind::SELECT);
166
  if (d_ccStore)
167
  {
168
    d_equalityEngine->addFunctionKind(kind::STORE);
169
  }
170
9838
}
171
172
/////////////////////////////////////////////////////////////////////////////
173
// PREPROCESSING
174
/////////////////////////////////////////////////////////////////////////////
175
176
177
2994
bool TheoryArrays::ppDisequal(TNode a, TNode b) {
178
2994
  bool termsExist = d_ppEqualityEngine.hasTerm(a) && d_ppEqualityEngine.hasTerm(b);
179
2994
  Assert(!termsExist || !a.isConst() || !b.isConst() || a == b
180
         || d_ppEqualityEngine.areDisequal(a, b, false));
181
11916
  return ((termsExist && d_ppEqualityEngine.areDisequal(a, b, false)) ||
182
11916
          Rewriter::rewrite(a.eqNode(b)) == d_false);
183
}
184
185
186
1484
Node TheoryArrays::solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck)
187
{
188
1484
  if (!solve1) {
189
    return term;
190
  }
191
5654
  if (term[0].getKind() != kind::STORE &&
192
4170
      term[1].getKind() != kind::STORE) {
193
1185
    return term;
194
  }
195
598
  TNode left = term[0];
196
598
  TNode right = term[1];
197
299
  int leftWrites = 0, rightWrites = 0;
198
199
  // Count nested writes
200
598
  TNode e1 = left;
201
1043
  while (e1.getKind() == kind::STORE) {
202
372
    ++leftWrites;
203
372
    e1 = e1[0];
204
  }
205
206
598
  TNode e2 = right;
207
1427
  while (e2.getKind() == kind::STORE) {
208
564
    ++rightWrites;
209
564
    e2 = e2[0];
210
  }
211
212
299
  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
299
  NodeManager* nm = NodeManager::currentNM();
222
299
  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
91
    if (!solve2) {
283
91
      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
22158
TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems)
305
{
306
  // first, see if we need to expand definitions
307
44316
  TrustNode texp = d_rewriter.expandDefinition(term);
308
22158
  if (!texp.isNull())
309
  {
310
20
    return texp;
311
  }
312
  if (!d_preprocess)
313
  {
314
    return TrustNode::null();
315
  }
316
22138
  d_ppEqualityEngine.addTerm(term);
317
22138
  NodeManager* nm = NodeManager::currentNM();
318
44276
  Node ret;
319
22138
  switch (term.getKind()) {
320
6167
    case kind::SELECT: {
321
      // select(store(a,i,v),j) = select(a,j)
322
      //    IF i != j
323
6167
      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
6167
      break;
327
    }
328
1304
    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
1304
      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
1304
      break;
337
    }
338
1484
    case kind::EQUAL: {
339
1484
      ret = solveWrite(term, d_solveWrite, d_solveWrite2, true);
340
1484
      break;
341
    }
342
13183
    default:
343
13183
      break;
344
  }
345
22138
  if (!ret.isNull() && ret != term)
346
  {
347
48
    return TrustNode::mkTrustRewrite(term, ret, nullptr);
348
  }
349
22090
  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
202009
bool TheoryArrays::propagateLit(TNode literal)
395
{
396
404018
  Debug("arrays") << spaces(getSatContext()->getLevel())
397
202009
                  << "TheoryArrays::propagateLit(" << literal << ")"
398
202009
                  << std::endl;
399
400
  // If already in conflict, no more propagation
401
202009
  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
202009
  if (d_inCheckModel && getSatContext()->getLevel() != d_topLevel) {
411
    return true;
412
  }
413
202009
  bool ok = d_out->propagate(literal);
414
202009
  if (!ok) {
415
1909
    d_state.notifyInConflict();
416
  }
417
202009
  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
94713
void TheoryArrays::preRegisterTermInternal(TNode node)
644
{
645
94713
  if (d_state.isInConflict())
646
  {
647
63298
    return;
648
  }
649
189304
  Debug("arrays") << spaces(getSatContext()->getLevel())
650
94652
                  << "TheoryArrays::preRegisterTerm(" << node << ")"
651
94652
                  << std::endl;
652
94652
  Kind nk = node.getKind();
653
94652
  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
13418
    d_equalityEngine->addTriggerPredicate(node);
658
13418
    return;
659
  }
660
  // add to equality engine and the may equality engine
661
112710
  TypeNode nodeType = node.getType();
662
81234
  if (nodeType.isArray())
663
  {
664
    // index type should not be an array, otherwise we throw a logic exception
665
6835
    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
6835
    d_mayEqualEqualityEngine.addTerm(node);
673
  }
674
81234
  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
49758
    return;
682
  }
683
31476
  d_equalityEngine->addTerm(node);
684
685
31476
  switch (node.getKind())
686
  {
687
23127
    case kind::SELECT:
688
    {
689
      // Reads
690
46254
      TNode store = d_equalityEngine->getRepresentative(node[0]);
691
692
      // The may equal needs the store
693
23127
      d_mayEqualEqualityEngine.addTerm(store);
694
695
23127
      Assert((d_isPreRegistered.insert(node), true));
696
697
23127
      Assert(d_equalityEngine->getRepresentative(store) == store);
698
23127
      d_infoMap.addIndex(store, node[1]);
699
700
      // Synchronize d_constReadsContext with SAT context
701
23127
      Assert(d_constReadsContext->getLevel() <= getSatContext()->getLevel());
702
61289
      while (d_constReadsContext->getLevel() < getSatContext()->getLevel())
703
      {
704
19081
        d_constReadsContext->push();
705
      }
706
707
      // Record read in sharing data structure
708
46254
      TNode index = d_equalityEngine->getRepresentative(node[1]);
709
145047
      if (!options::arraysWeakEquivalence() && index.isConst())
710
      {
711
        CTNodeList* temp;
712
4223
        CNodeNListMap::iterator it = d_constReads.find(index);
713
4223
        if (it == d_constReads.end())
714
        {
715
492
          temp = new (true) CTNodeList(d_constReadsContext);
716
492
          d_constReads[index] = temp;
717
        }
718
        else
719
        {
720
3731
          temp = (*it).second;
721
        }
722
4223
        temp->push_back(node);
723
4223
        d_constReadsList.push_back(node);
724
      }
725
      else
726
      {
727
18904
        d_reads.push_back(node);
728
      }
729
730
23127
      checkRowForIndex(node[1], store);
731
23127
      break;
732
    }
733
1058
    case kind::STORE:
734
    {
735
2116
      TNode a = d_equalityEngine->getRepresentative(node[0]);
736
737
1058
      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
1036
        d_mayEqualEqualityEngine.assertEquality(node.eqNode(a), true, d_true);
752
1036
        Assert(d_mayEqualEqualityEngine.consistent());
753
      }
754
755
2116
      TNode i = node[1];
756
2116
      TNode v = node[2];
757
1058
      NodeManager* nm = NodeManager::currentNM();
758
2116
      Node ni = nm->mkNode(kind::SELECT, node, i);
759
1058
      if (!d_equalityEngine->hasTerm(ni))
760
      {
761
1058
        preRegisterTermInternal(ni);
762
      }
763
      // Apply RIntro1 Rule
764
1058
      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
1058
      d_infoMap.addStore(node, node);
771
1058
      d_infoMap.addInStore(a, node);
772
1058
      d_infoMap.setModelRep(node, node);
773
774
      // Add-Store for Weak Equivalence
775
1058
      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
1058
    checkStore(node);
787
1058
    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
7225
  default:
801
    // Variables etc, already processed above
802
7225
    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
37844
void TheoryArrays::preRegisterTerm(TNode node)
811
{
812
37844
  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
37844
  if (node.getKind() == kind::SELECT && node.getType().isBoolean()) {
817
97
    d_equalityEngine->addTriggerPredicate(node);
818
  }
819
37844
}
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
10986
TrustNode TheoryArrays::explain(TNode literal)
843
{
844
10986
  return d_im.explainLit(literal);
845
}
846
847
/////////////////////////////////////////////////////////////////////////////
848
// SHARING
849
/////////////////////////////////////////////////////////////////////////////
850
851
82995
void TheoryArrays::notifySharedTerm(TNode t)
852
{
853
165990
  Debug("arrays::sharing") << spaces(getSatContext()->getLevel())
854
82995
                           << "TheoryArrays::notifySharedTerm(" << t << ")"
855
82995
                           << std::endl;
856
82995
  if (t.getType().isArray()) {
857
2490
    d_sharedArrays.insert(t);
858
  }
859
  else {
860
#ifdef CVC5_ASSERTIONS
861
80505
    d_sharedOther.insert(t);
862
#endif
863
80505
    d_sharedTerms = true;
864
  }
865
82995
}
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
15189
void TheoryArrays::presolve()
1186
{
1187
15189
  Trace("arrays")<<"Presolving \n";
1188
15189
  if (!d_dstratInit)
1189
  {
1190
9389
    d_dstratInit = true;
1191
    // add the decision strategy, which is user-context-independent
1192
18778
    d_im.getDecisionManager()->registerStrategy(
1193
        DecisionManager::STRAT_ARRAYS,
1194
9389
        d_dstrat.get(),
1195
        DecisionManager::STRAT_SCOPE_CTX_INDEPENDENT);
1196
  }
1197
15189
}
1198
1199
1200
/////////////////////////////////////////////////////////////////////////////
1201
// MAIN SOLVER
1202
/////////////////////////////////////////////////////////////////////////////
1203
1204
3040
Node TheoryArrays::getSkolem(TNode ref)
1205
{
1206
  // the call to SkolemCache::getExtIndexSkolem should be deterministic, but use
1207
  // cache anyways for now
1208
3040
  Node skolem;
1209
3040
  std::unordered_map<Node, Node>::iterator it = d_skolemCache.find(ref);
1210
3040
  if (it == d_skolemCache.end()) {
1211
571
    Assert(ref.getKind() == kind::NOT && ref[0].getKind() == kind::EQUAL);
1212
    // make the skolem using the skolem cache utility
1213
571
    skolem = SkolemCache::getExtIndexSkolem(ref);
1214
571
    d_skolemCache[ref] = skolem;
1215
  }
1216
  else {
1217
2469
    skolem = (*it).second;
1218
  }
1219
1220
3040
  Debug("pf::array") << "Pregistering a Skolem" << std::endl;
1221
3040
  preRegisterTermInternal(skolem);
1222
3040
  Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl;
1223
1224
3040
  Debug("pf::array") << "getSkolem DONE" << std::endl;
1225
3040
  return skolem;
1226
}
1227
1228
90256
void TheoryArrays::postCheck(Effort level)
1229
{
1230
471534
  if ((options::arraysEagerLemmas() || fullEffort(level))
1231
115428
      && !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
270768
  if (!options::arraysEagerLemmas() && fullEffort(level)
1321
115428
      && !d_state.isInConflict() && !options::arraysWeakEquivalence())
1322
  {
1323
    // generate the lemmas on the worklist
1324
25154
    Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue.size() << "\n";
1325
25754
    while (d_RowQueue.size() > 0 && !d_state.isInConflict())
1326
    {
1327
1032
      if (dischargeLemmas()) {
1328
732
        break;
1329
      }
1330
    }
1331
  }
1332
1333
90256
  Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl;
1334
}
1335
1336
209255
bool TheoryArrays::preNotifyFact(
1337
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
1338
{
1339
209255
  if (!isInternal && !isPrereg)
1340
  {
1341
161333
    if (atom.getKind() == kind::EQUAL)
1342
    {
1343
161271
      if (!d_equalityEngine->hasTerm(atom[0]))
1344
      {
1345
1547
        Assert(atom[0].isConst());
1346
1547
        d_equalityEngine->addTerm(atom[0]);
1347
      }
1348
161271
      if (!d_equalityEngine->hasTerm(atom[1]))
1349
      {
1350
531
        Assert(atom[1].isConst());
1351
531
        d_equalityEngine->addTerm(atom[1]);
1352
      }
1353
    }
1354
  }
1355
209255
  return false;
1356
}
1357
1358
209252
void TheoryArrays::notifyFact(TNode atom, bool pol, TNode fact, bool isInternal)
1359
{
1360
  // if a disequality
1361
209252
  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
42688
    if (fact[0][0].getType().isArray() && !d_state.isInConflict())
1367
    {
1368
3040
      NodeManager* nm = NodeManager::currentNM();
1369
1370
6080
      TNode k;
1371
      // k is the skolem for this disequality.
1372
6080
      Debug("pf::array") << "Check: kind::NOT: array theory making a skolem"
1373
3040
                          << std::endl;
1374
1375
      // If not in replay mode, generate a fresh skolem variable
1376
3040
      k = getSkolem(fact);
1377
1378
6080
      Node ak = nm->mkNode(kind::SELECT, fact[0][0], k);
1379
6080
      Node bk = nm->mkNode(kind::SELECT, fact[0][1], k);
1380
6080
      Node eq = ak.eqNode(bk);
1381
6080
      Node lemma = fact[0].orNode(eq.notNode());
1382
1383
78313
      if (options::arraysPropagate() > 0 && d_equalityEngine->hasTerm(ak)
1384
8445
          && d_equalityEngine->hasTerm(bk))
1385
      {
1386
        // Propagate witness disequality - might produce a conflict
1387
4730
        Debug("pf::array") << "Asserting to the equality engine:" << std::endl
1388
2365
                           << "\teq = " << eq << std::endl
1389
2365
                           << "\treason = " << fact << std::endl;
1390
2365
        d_im.assertInference(eq, false, InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1391
2365
        ++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
3040
      Trace("arrays-lem") << "Arrays::addExtLemma " << lemma << "\n";
1397
3040
      d_im.arrayLemma(eq.notNode(), InferenceId::ARRAYS_EXT, fact, PfRule::ARRAYS_EXT);
1398
3040
      ++d_numExt;
1399
    }
1400
    else
1401
    {
1402
79296
      Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem"
1403
39648
                         << std::endl;
1404
39648
      d_modelConstraints.push_back(fact);
1405
    }
1406
  }
1407
209252
}
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
1767
void TheoryArrays::setNonLinear(TNode a)
1469
{
1470
1954
  if (options::arraysWeakEquivalence()) return;
1471
1767
  if (d_infoMap.isNonLinear(a)) return;
1472
1473
1580
  Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::setNonLinear (" << a << ")\n";
1474
1580
  d_infoMap.setNonLinear(a);
1475
1580
  ++d_numNonLinear;
1476
1477
1580
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1478
1580
  const CTNodeList* st_a = d_infoMap.getStores(a);
1479
1580
  const CTNodeList* inst_a = d_infoMap.getInStores(a);
1480
1481
1580
  size_t it = 0;
1482
1483
  // Propagate non-linearity down chain of stores
1484
3700
  for( ; it < st_a->size(); ++it) {
1485
2120
    TNode store = (*st_a)[it];
1486
1060
    Assert(store.getKind() == kind::STORE);
1487
1060
    setNonLinear(store[0]);
1488
  }
1489
1490
  // Instantiate ROW lemmas that were ignored before
1491
1580
  size_t it2 = 0;
1492
3160
  RowLemmaType lem;
1493
19248
  for(; it2 < i_a->size(); ++it2) {
1494
17668
    TNode i = (*i_a)[it2];
1495
8834
    it = 0;
1496
26614
    for ( ; it < inst_a->size(); ++it) {
1497
17780
      TNode store = (*inst_a)[it];
1498
8890
      Assert(store.getKind() == kind::STORE);
1499
17780
      TNode j = store[1];
1500
17780
      TNode c = store[0];
1501
8890
      lem = std::make_tuple(store, c, j, i);
1502
8890
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::setNonLinear ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1503
8890
      queueRowLemma(lem);
1504
    }
1505
  }
1506
1507
}
1508
1509
5799
void TheoryArrays::mergeArrays(TNode a, TNode b)
1510
{
1511
  // Note: a is the new representative
1512
5799
  Assert(a.getType().isArray() && b.getType().isArray());
1513
1514
5799
  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
5799
  d_mergeInProgress = true;
1521
1522
11598
  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
5799
    a = d_equalityEngine->getRepresentative(a);
1528
5799
    Assert(d_equalityEngine->getRepresentative(b) == a);
1529
5799
    Trace("arrays-merge") << spaces(getSatContext()->getLevel()) << "Arrays::merge: (" << a << ", " << b << ")\n";
1530
1531
47375
    if (options::arraysOptimizeLinear() && !options::arraysWeakEquivalence()) {
1532
4087
      bool aNL = d_infoMap.isNonLinear(a);
1533
4087
      bool bNL = d_infoMap.isNonLinear(b);
1534
4087
      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
3827
        if (bNL) {
1545
          // Set a to be non-linear
1546
48
          setNonLinear(a);
1547
        }
1548
        else {
1549
          // Check for new non-linear arrays
1550
3779
          const CTNodeList* astores = d_infoMap.getStores(a);
1551
3779
          const CTNodeList* bstores = d_infoMap.getStores(b);
1552
3779
          Assert(astores->size() <= 1 && bstores->size() <= 1);
1553
3779
          if (astores->size() > 0 && bstores->size() > 0) {
1554
213
            setNonLinear(a);
1555
213
            setNonLinear(b);
1556
          }
1557
        }
1558
      }
1559
    }
1560
1561
5802
    TNode constArrA = d_infoMap.getConstArr(a);
1562
5802
    TNode constArrB = d_infoMap.getConstArr(b);
1563
5799
    if (constArrA.isNull()) {
1564
5636
      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
5802
    TNode mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1575
5802
    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
5799
    DefValMap::iterator it = d_defValues.find(mayRepA);
1581
5799
    DefValMap::iterator it2 = d_defValues.find(mayRepB);
1582
5802
    TNode defValue;
1583
1584
5799
    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
5539
    else if (it2 != d_defValues.end()) {
1592
      defValue = (*it2).second;
1593
    }
1594
5796
    d_mayEqualEqualityEngine.assertEquality(a.eqNode(b), true, d_true);
1595
5796
    Assert(d_mayEqualEqualityEngine.consistent());
1596
5796
    if (!defValue.isNull()) {
1597
257
      mayRepA = d_mayEqualEqualityEngine.getRepresentative(a);
1598
257
      d_defValues[mayRepA] = defValue;
1599
    }
1600
1601
5796
    checkRowLemmas(a,b);
1602
5796
    checkRowLemmas(b,a);
1603
1604
    // merge info adds the list of the 2nd argument to the first
1605
5796
    d_infoMap.mergeInfo(a, b);
1606
1607
5796
    if (options::arraysWeakEquivalence()) {
1608
      d_arrayMerges.push_back(a.eqNode(b));
1609
    }
1610
1611
    // If no more to do, break
1612
5796
    if (d_state.isInConflict() || d_mergeQueue.empty())
1613
    {
1614
5796
      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
5796
  d_mergeInProgress = false;
1624
}
1625
1626
1627
1058
void TheoryArrays::checkStore(TNode a) {
1628
1058
  if (options::arraysWeakEquivalence()) return;
1629
1058
  Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
1630
1631
1058
  if(Trace.isOn("arrays-cri")) {
1632
    d_infoMap.getInfo(a)->print();
1633
  }
1634
1058
  Assert(a.getType().isArray());
1635
1058
  Assert(a.getKind() == kind::STORE);
1636
2116
  TNode b = a[0];
1637
2116
  TNode i = a[1];
1638
1639
2116
  TNode brep = d_equalityEngine->getRepresentative(b);
1640
1641
1058
  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
23127
void TheoryArrays::checkRowForIndex(TNode i, TNode a)
1657
{
1658
23127
  if (options::arraysWeakEquivalence()) return;
1659
23127
  Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a<<"\n";
1660
23127
  Trace("arrays-cri")<<"                   index "<<i<<"\n";
1661
1662
23127
  if(Trace.isOn("arrays-cri")) {
1663
    d_infoMap.getInfo(a)->print();
1664
  }
1665
23127
  Assert(a.getType().isArray());
1666
23127
  Assert(d_equalityEngine->getRepresentative(a) == a);
1667
1668
46254
  TNode constArr = d_infoMap.getConstArr(a);
1669
23127
  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::UNKNOWN,
1680
                         d_true,
1681
                         PfRule::ARRAYS_TRUST);
1682
  }
1683
1684
23127
  const CTNodeList* stores = d_infoMap.getStores(a);
1685
23127
  const CTNodeList* instores = d_infoMap.getInStores(a);
1686
23127
  size_t it = 0;
1687
46254
  RowLemmaType lem;
1688
1689
52023
  for(; it < stores->size(); ++it) {
1690
28783
    TNode store = (*stores)[it];
1691
14448
    Assert(store.getKind() == kind::STORE);
1692
28783
    TNode j = store[1];
1693
14448
    if (i == j) continue;
1694
14335
    lem = std::make_tuple(store, store[0], j, i);
1695
14335
    Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
1696
14335
    queueRowLemma(lem);
1697
  }
1698
1699
23127
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(a)) {
1700
7459
    it = 0;
1701
19831
    for(; it < instores->size(); ++it) {
1702
12161
      TNode instore = (*instores)[it];
1703
6186
      Assert(instore.getKind() == kind::STORE);
1704
12161
      TNode j = instore[1];
1705
6186
      if (i == j) continue;
1706
5975
      lem = std::make_tuple(instore, instore[0], j, i);
1707
5975
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore<<", "<<instore[0]<<", "<<j<<", "<<i<<")\n";
1708
5975
      queueRowLemma(lem);
1709
    }
1710
  }
1711
}
1712
1713
1714
// a just became equal to b
1715
// look for new ROW lemmas
1716
11592
void TheoryArrays::checkRowLemmas(TNode a, TNode b)
1717
{
1718
11592
  if (options::arraysWeakEquivalence()) return;
1719
11592
  Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
1720
11592
  if(Trace.isOn("arrays-crl"))
1721
    d_infoMap.getInfo(a)->print();
1722
11592
  Trace("arrays-crl")<<"  ------------  and "<<b<<"\n";
1723
11592
  if(Trace.isOn("arrays-crl"))
1724
    d_infoMap.getInfo(b)->print();
1725
1726
11592
  const CTNodeList* i_a = d_infoMap.getIndices(a);
1727
11592
  size_t it = 0;
1728
23184
  TNode constArr = d_infoMap.getConstArr(b);
1729
11592
  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
11592
  const CTNodeList* st_b = d_infoMap.getStores(b);
1741
11592
  const CTNodeList* inst_b = d_infoMap.getInStores(b);
1742
  size_t its;
1743
1744
23184
  RowLemmaType lem;
1745
1746
63134
  for(it = 0 ; it < i_a->size(); ++it) {
1747
103084
    TNode i = (*i_a)[it];
1748
51542
    its = 0;
1749
99806
    for ( ; its < st_b->size(); ++its) {
1750
48264
      TNode store = (*st_b)[its];
1751
24132
      Assert(store.getKind() == kind::STORE);
1752
48264
      TNode j = store[1];
1753
48264
      TNode c = store[0];
1754
24132
      lem = std::make_tuple(store, c, j, i);
1755
24132
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
1756
24132
      queueRowLemma(lem);
1757
    }
1758
  }
1759
1760
11592
  if (!options::arraysOptimizeLinear() || d_infoMap.isNonLinear(b)) {
1761
28978
    for(it = 0 ; it < i_a->size(); ++it ) {
1762
49036
      TNode i = (*i_a)[it];
1763
24518
      its = 0;
1764
49120
      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
11592
  Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
1776
}
1777
1778
22051
void TheoryArrays::propagateRowLemma(RowLemmaType lem)
1779
{
1780
44102
  Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = "
1781
22051
                     << options::arraysPropagate() << std::endl;
1782
1783
34864
  TNode a, b, i, j;
1784
22051
  std::tie(a, b, i, j) = lem;
1785
1786
22051
  Assert(a.getType().isArray() && b.getType().isArray());
1787
22051
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1788
  {
1789
    return;
1790
  }
1791
1792
22051
  NodeManager* nm = NodeManager::currentNM();
1793
34864
  Node aj = nm->mkNode(kind::SELECT, a, j);
1794
34864
  Node bj = nm->mkNode(kind::SELECT, b, j);
1795
1796
  // Try to avoid introducing new read terms: track whether these already exist
1797
22051
  bool ajExists = d_equalityEngine->hasTerm(aj);
1798
22051
  bool bjExists = d_equalityEngine->hasTerm(bj);
1799
22051
  bool bothExist = ajExists && bjExists;
1800
1801
  // If propagating, check propagations
1802
22051
  int prop = options::arraysPropagate();
1803
22051
  if (prop > 0) {
1804
22051
    if (d_equalityEngine->areDisequal(i, j, true) && (bothExist || prop > 1))
1805
    {
1806
9234
      Trace("arrays-lem") << spaces(getSatContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj<<", "<<bj<<")\n";
1807
18468
      Node aj_eq_bj = aj.eqNode(bj);
1808
      Node reason =
1809
18468
          (i.isConst() && j.isConst()) ? d_true : i.eqNode(j).notNode();
1810
9234
      d_permRef.push_back(reason);
1811
9234
      if (!ajExists) {
1812
2785
        preRegisterTermInternal(aj);
1813
      }
1814
9234
      if (!bjExists) {
1815
5567
        preRegisterTermInternal(bj);
1816
      }
1817
9234
      d_im.assertInference(
1818
          aj_eq_bj, true, InferenceId::ARRAYS_READ_OVER_WRITE, reason, PfRule::ARRAYS_READ_OVER_WRITE);
1819
9234
      ++d_numProp;
1820
9234
      return;
1821
    }
1822
12817
    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
65894
void TheoryArrays::queueRowLemma(RowLemmaType lem)
1837
{
1838
65894
  Debug("pf::array") << "Array solver: queue row lemma called" << std::endl;
1839
1840
65894
  if (d_state.isInConflict() || d_RowAlreadyAdded.contains(lem))
1841
  {
1842
77906
    return;
1843
  }
1844
53882
  TNode a, b, i, j;
1845
34609
  std::tie(a, b, i, j) = lem;
1846
1847
34609
  Assert(a.getType().isArray() && b.getType().isArray());
1848
34609
  if (d_equalityEngine->areEqual(a, b) || d_equalityEngine->areEqual(i, j))
1849
  {
1850
14355
    return;
1851
  }
1852
1853
20254
  NodeManager* nm = NodeManager::currentNM();
1854
39527
  Node aj = nm->mkNode(kind::SELECT, a, j);
1855
39527
  Node bj = nm->mkNode(kind::SELECT, b, j);
1856
1857
  // Try to avoid introducing new read terms: track whether these already exist
1858
20254
  bool ajExists = d_equalityEngine->hasTerm(aj);
1859
20254
  bool bjExists = d_equalityEngine->hasTerm(bj);
1860
20254
  bool bothExist = ajExists && bjExists;
1861
1862
  // If propagating, check propagations
1863
20254
  int prop = options::arraysPropagate();
1864
20254
  if (prop > 0) {
1865
20254
    propagateRowLemma(lem);
1866
  }
1867
1868
  // Prefer equality between indexes so as not to introduce new read terms
1869
73666
  if (options::arraysEagerIndexSplitting() && !bothExist
1870
51388
      && !d_equalityEngine->areDisequal(i, j, false))
1871
  {
1872
11004
    Node i_eq_j;
1873
5502
    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
5502
    getOutputChannel().requirePhase(i_eq_j, true);
1878
5502
    d_decisionRequests.push(i_eq_j);
1879
  }
1880
1881
  // TODO: maybe add triggers here
1882
1883
20254
  if (options::arraysEagerLemmas() || bothExist)
1884
  {
1885
    // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
1886
3617
    Node aj2 = Rewriter::rewrite(aj);
1887
2299
    if (aj != aj2) {
1888
981
      if (!ajExists) {
1889
        preRegisterTermInternal(aj);
1890
      }
1891
981
      if (!d_equalityEngine->hasTerm(aj2))
1892
      {
1893
        preRegisterTermInternal(aj2);
1894
      }
1895
2943
      d_im.assertInference(
1896
1962
          aj.eqNode(aj2), true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
1897
    }
1898
3617
    Node bj2 = Rewriter::rewrite(bj);
1899
2299
    if (bj != bj2) {
1900
626
      if (!bjExists) {
1901
        preRegisterTermInternal(bj);
1902
      }
1903
626
      if (!d_equalityEngine->hasTerm(bj2))
1904
      {
1905
        preRegisterTermInternal(bj2);
1906
      }
1907
1878
      d_im.assertInference(
1908
1252
          bj.eqNode(bj2), true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
1909
    }
1910
2299
    if (aj2 == bj2) {
1911
981
      return;
1912
    }
1913
1914
    // construct lemma
1915
2636
    Node eq1 = aj2.eqNode(bj2);
1916
2636
    Node eq1_r = Rewriter::rewrite(eq1);
1917
1318
    if (eq1_r == d_true) {
1918
      if (!d_equalityEngine->hasTerm(aj2))
1919
      {
1920
        preRegisterTermInternal(aj2);
1921
      }
1922
      if (!d_equalityEngine->hasTerm(bj2))
1923
      {
1924
        preRegisterTermInternal(bj2);
1925
      }
1926
      d_im.assertInference(eq1, true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
1927
      return;
1928
    }
1929
1930
2636
    Node eq2 = i.eqNode(j);
1931
2636
    Node eq2_r = Rewriter::rewrite(eq2);
1932
1318
    if (eq2_r == d_true) {
1933
      d_im.assertInference(eq2, true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
1934
      return;
1935
    }
1936
1937
2636
    Node lemma = nm->mkNode(kind::OR, eq2_r, eq1_r);
1938
1939
1318
    Trace("arrays-lem") << "Arrays::addRowLemma (1) adding " << lemma << "\n";
1940
1318
    d_RowAlreadyAdded.insert(lem);
1941
    // use non-rewritten nodes
1942
2636
    d_im.arrayLemma(
1943
2636
        aj.eqNode(bj), InferenceId::ARRAYS_READ_OVER_WRITE, eq2.notNode(), PfRule::ARRAYS_READ_OVER_WRITE);
1944
1318
    ++d_numRow;
1945
  }
1946
  else {
1947
17955
    d_RowQueue.push(lem);
1948
  }
1949
}
1950
1951
2690684
Node TheoryArrays::getNextDecisionRequest()
1952
{
1953
2690684
  if(! d_decisionRequests.empty()) {
1954
30030
    Node n = d_decisionRequests.front();
1955
15015
    d_decisionRequests.pop();
1956
15015
    return n;
1957
  }
1958
2675669
  return Node::null();
1959
}
1960
1961
1962
1032
bool TheoryArrays::dischargeLemmas()
1963
{
1964
1032
  bool lemmasAdded = false;
1965
1032
  size_t sz = d_RowQueue.size();
1966
14145
  for (unsigned count = 0; count < sz; ++count) {
1967
14858
    RowLemmaType l = d_RowQueue.front();
1968
13487
    d_RowQueue.pop();
1969
13487
    if (d_RowAlreadyAdded.contains(l)) {
1970
7620
      continue;
1971
    }
1972
1973
7238
    TNode a, b, i, j;
1974
5867
    std::tie(a, b, i, j) = l;
1975
5867
    Assert(a.getType().isArray() && b.getType().isArray());
1976
1977
5867
    NodeManager* nm = NodeManager::currentNM();
1978
7238
    Node aj = nm->mkNode(kind::SELECT, a, j);
1979
7238
    Node bj = nm->mkNode(kind::SELECT, b, j);
1980
5867
    bool ajExists = d_equalityEngine->hasTerm(aj);
1981
5867
    bool bjExists = d_equalityEngine->hasTerm(bj);
1982
1983
    // Check for redundant lemma
1984
    // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
1985
23468
    if (!d_equalityEngine->hasTerm(i) || !d_equalityEngine->hasTerm(j)
1986
11734
        || d_equalityEngine->areEqual(i, j) || !d_equalityEngine->hasTerm(a)
1987
9527
        || !d_equalityEngine->hasTerm(b) || d_equalityEngine->areEqual(a, b)
1988
20941
        || (ajExists && bjExists && d_equalityEngine->areEqual(aj, bj)))
1989
    {
1990
4070
      continue;
1991
    }
1992
1993
1797
    int prop = options::arraysPropagate();
1994
1797
    if (prop > 0) {
1995
1797
      propagateRowLemma(l);
1996
1797
      if (d_state.isInConflict())
1997
      {
1998
374
        return true;
1999
      }
2000
    }
2001
2002
    // Make sure that any terms introduced by rewriting are appropriately stored in the equality database
2003
2794
    Node aj2 = Rewriter::rewrite(aj);
2004
1423
    if (aj != aj2) {
2005
52
      if (!ajExists) {
2006
52
        preRegisterTermInternal(aj);
2007
      }
2008
52
      if (!d_equalityEngine->hasTerm(aj2))
2009
      {
2010
        preRegisterTermInternal(aj2);
2011
      }
2012
156
      d_im.assertInference(
2013
104
          aj.eqNode(aj2), true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
2014
    }
2015
2794
    Node bj2 = Rewriter::rewrite(bj);
2016
1423
    if (bj != bj2) {
2017
69
      if (!bjExists) {
2018
6
        preRegisterTermInternal(bj);
2019
      }
2020
69
      if (!d_equalityEngine->hasTerm(bj2))
2021
      {
2022
        preRegisterTermInternal(bj2);
2023
      }
2024
207
      d_im.assertInference(
2025
138
          bj.eqNode(bj2), true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
2026
    }
2027
1423
    if (aj2 == bj2) {
2028
52
      continue;
2029
    }
2030
2031
    // construct lemma
2032
2742
    Node eq1 = aj2.eqNode(bj2);
2033
2742
    Node eq1_r = Rewriter::rewrite(eq1);
2034
1371
    if (eq1_r == d_true) {
2035
      if (!d_equalityEngine->hasTerm(aj2))
2036
      {
2037
        preRegisterTermInternal(aj2);
2038
      }
2039
      if (!d_equalityEngine->hasTerm(bj2))
2040
      {
2041
        preRegisterTermInternal(bj2);
2042
      }
2043
      d_im.assertInference(eq1, true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
2044
      continue;
2045
    }
2046
2047
2742
    Node eq2 = i.eqNode(j);
2048
2742
    Node eq2_r = Rewriter::rewrite(eq2);
2049
1371
    if (eq2_r == d_true) {
2050
      d_im.assertInference(eq2, true, InferenceId::UNKNOWN, d_true, PfRule::MACRO_SR_PRED_INTRO);
2051
      continue;
2052
    }
2053
2054
2742
    Node lem = nm->mkNode(kind::OR, eq2_r, eq1_r);
2055
2056
1371
    Trace("arrays-lem") << "Arrays::addRowLemma (2) adding " << lem << "\n";
2057
1371
    d_RowAlreadyAdded.insert(l);
2058
    // use non-rewritten nodes, theory preprocessing will rewrite
2059
2742
    d_im.arrayLemma(
2060
2742
        aj.eqNode(bj), InferenceId::ARRAYS_READ_OVER_WRITE, eq2.notNode(), PfRule::ARRAYS_READ_OVER_WRITE);
2061
1371
    ++d_numRow;
2062
1371
    lemmasAdded = true;
2063
2742
    if (options::arraysReduceSharing()) {
2064
      return true;
2065
    }
2066
  }
2067
658
  return lemmasAdded;
2068
}
2069
2070
69
void TheoryArrays::conflict(TNode a, TNode b) {
2071
69
  Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl;
2072
69
  if (d_inCheckModel)
2073
  {
2074
    // if in check model, don't send the conflict
2075
    d_state.notifyInConflict();
2076
    return;
2077
  }
2078
69
  d_im.conflictEqConstantMerge(a, b);
2079
}
2080
2081
9838
TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy(
2082
9838
    TheoryArrays* ta)
2083
9838
    : d_ta(ta)
2084
{
2085
9838
}
2086
9389
void TheoryArrays::TheoryArraysDecisionStrategy::initialize() {}
2087
2690684
Node TheoryArrays::TheoryArraysDecisionStrategy::getNextDecisionRequest()
2088
{
2089
2690684
  return d_ta->getNextDecisionRequest();
2090
}
2091
2700073
std::string TheoryArrays::TheoryArraysDecisionStrategy::identify() const
2092
{
2093
2700073
  return std::string("th_arrays_dec");
2094
}
2095
2096
5311
void TheoryArrays::computeRelevantTerms(std::set<Node>& termSet)
2097
{
2098
5311
  NodeManager* nm = NodeManager::currentNM();
2099
  // make sure RIntro1 reads are included in the relevant set of reads
2100
5311
  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2101
31577
  for (; !eqcs_i.isFinished(); ++eqcs_i)
2102
  {
2103
14039
    Node eqc = (*eqcs_i);
2104
13133
    if (!eqc.getType().isArray())
2105
    {
2106
      // not an array, skip
2107
12227
      continue;
2108
    }
2109
906
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2110
4546
    for (; !eqc_i.isFinished(); ++eqc_i)
2111
    {
2112
3640
      Node n = *eqc_i;
2113
1820
      if (termSet.find(n) != termSet.end())
2114
      {
2115
1776
        if (n.getKind() == kind::STORE)
2116
        {
2117
          // Make sure RIntro1 reads are included
2118
906
          Node r = nm->mkNode(kind::SELECT, n, n[1]);
2119
906
          Trace("arrays::collectModelInfo")
2120
453
              << "TheoryArrays::collectModelInfo, adding RIntro1 read: " << r
2121
453
              << endl;
2122
453
          termSet.insert(r);
2123
        }
2124
      }
2125
    }
2126
  }
2127
2128
  // Now do a fixed-point iteration to get all reads that need to be included
2129
  // because of RIntro2 rule
2130
  bool changed;
2131
5392
  do
2132
  {
2133
5392
    changed = false;
2134
5392
    eqcs_i = eq::EqClassesIterator(d_equalityEngine);
2135
34258
    for (; !eqcs_i.isFinished(); ++eqcs_i)
2136
    {
2137
28866
      Node eqc = (*eqcs_i);
2138
14433
      eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
2139
68053
      for (; !eqc_i.isFinished(); ++eqc_i)
2140
      {
2141
53620
        Node n = *eqc_i;
2142
26810
        if (n.getKind() == kind::SELECT && termSet.find(n) != termSet.end())
2143
        {
2144
          // Find all terms equivalent to n[0] and get corresponding read terms
2145
13624
          Node array_eqc = d_equalityEngine->getRepresentative(n[0]);
2146
          eq::EqClassIterator array_eqc_i =
2147
6812
              eq::EqClassIterator(array_eqc, d_equalityEngine);
2148
47246
          for (; !array_eqc_i.isFinished(); ++array_eqc_i)
2149
          {
2150
40434
            Node arr = *array_eqc_i;
2151
40434
            if (arr.getKind() == kind::STORE
2152
27568
                && termSet.find(arr) != termSet.end()
2153
47785
                && !d_equalityEngine->areEqual(arr[1], n[1]))
2154
            {
2155
8682
              Node r = nm->mkNode(kind::SELECT, arr, n[1]);
2156
8682
              if (termSet.find(r) == termSet.end()
2157
8682
                  && d_equalityEngine->hasTerm(r))
2158
              {
2159
42
                Trace("arrays::collectModelInfo")
2160
                    << "TheoryArrays::collectModelInfo, adding RIntro2(a) "
2161
21
                       "read: "
2162
21
                    << r << endl;
2163
21
                termSet.insert(r);
2164
21
                changed = true;
2165
              }
2166
4341
              r = nm->mkNode(kind::SELECT, arr[0], n[1]);
2167
8682
              if (termSet.find(r) == termSet.end()
2168
8682
                  && d_equalityEngine->hasTerm(r))
2169
              {
2170
96
                Trace("arrays::collectModelInfo")
2171
                    << "TheoryArrays::collectModelInfo, adding RIntro2(b) "
2172
48
                       "read: "
2173
48
                    << r << endl;
2174
48
                termSet.insert(r);
2175
48
                changed = true;
2176
              }
2177
            }
2178
          }
2179
2180
          // Find all stores in which n[0] appears and get corresponding read
2181
          // terms
2182
6812
          const CTNodeList* instores = d_infoMap.getInStores(array_eqc);
2183
6812
          size_t it = 0;
2184
20458
          for (; it < instores->size(); ++it)
2185
          {
2186
13646
            TNode instore = (*instores)[it];
2187
6823
            Assert(instore.getKind() == kind::STORE);
2188
13646
            if (termSet.find(instore) != termSet.end()
2189
13646
                && !d_equalityEngine->areEqual(instore[1], n[1]))
2190
            {
2191
8912
              Node r = nm->mkNode(kind::SELECT, instore, n[1]);
2192
8912
              if (termSet.find(r) == termSet.end()
2193
8912
                  && d_equalityEngine->hasTerm(r))
2194
              {
2195
444
                Trace("arrays::collectModelInfo")
2196
                    << "TheoryArrays::collectModelInfo, adding RIntro2(c) "
2197
222
                       "read: "
2198
222
                    << r << endl;
2199
222
                termSet.insert(r);
2200
222
                changed = true;
2201
              }
2202
4456
              r = nm->mkNode(kind::SELECT, instore[0], n[1]);
2203
8912
              if (termSet.find(r) == termSet.end()
2204
8912
                  && d_equalityEngine->hasTerm(r))
2205
              {
2206
88
                Trace("arrays::collectModelInfo")
2207
                    << "TheoryArrays::collectModelInfo, adding RIntro2(d) "
2208
44
                       "read: "
2209
44
                    << r << endl;
2210
44
                termSet.insert(r);
2211
44
                changed = true;
2212
              }
2213
            }
2214
          }
2215
        }
2216
      }
2217
    }
2218
  } while (changed);
2219
5311
}
2220
2221
}  // namespace arrays
2222
}  // namespace theory
2223
29280
}  // namespace cvc5