GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.cpp Lines: 896 1225 73.1 %
Date: 2021-11-07 Branches: 2364 6457 36.6 %

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