GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 884 1225 72.2 %
Date: 2021-09-17 Branches: 2356 6489 36.3 %

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