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