GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays.h Lines: 41 42 97.6 %
Date: 2021-08-06 Branches: 60 108 55.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Morgan Deters, Andrew Reynolds, Clark Barrett
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
 * Theory of arrays.
14
 */
15
16
#include "cvc5_private.h"
17
18
#ifndef CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H
19
#define CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H
20
21
#include <tuple>
22
#include <unordered_map>
23
24
#include "context/cdhashmap.h"
25
#include "context/cdhashset.h"
26
#include "context/cdqueue.h"
27
#include "theory/arrays/array_info.h"
28
#include "theory/arrays/inference_manager.h"
29
#include "theory/arrays/proof_checker.h"
30
#include "theory/arrays/theory_arrays_rewriter.h"
31
#include "theory/decision_strategy.h"
32
#include "theory/theory.h"
33
#include "theory/theory_state.h"
34
#include "theory/uf/equality_engine.h"
35
#include "util/statistics_stats.h"
36
37
namespace cvc5 {
38
namespace theory {
39
namespace arrays {
40
41
/**
42
 * Decision procedure for arrays.
43
 *
44
 * Overview of decision procedure:
45
 *
46
 * Preliminary notation:
47
 *   Stores(a)  = {t | a ~ t and t = store( _ _ _ )}
48
 *   InStores(a) = {t | t = store (b _ _) and a ~ b }
49
 *   Indices(a) = {i | there exists a term b[i] such that a ~ b or store(b i v)}
50
 *   ~ represents the equivalence relation based on the asserted equalities in the
51
 *   current context.
52
 *
53
 * The rules implemented are the following:
54
 *             store(b i v)
55
 *     Row1 -------------------
56
 *          store(b i v)[i] = v
57
 *
58
 *           store(b i v)  a'[j]
59
 *     Row ---------------------- [ a' ~ store(b i v) or a' ~ b ]
60
 *           i = j OR a[j] = b[j]
61
 *
62
 *          a  b same kind arrays
63
 *     Ext ------------------------ [ a!= b in current context, k new var]
64
 *           a = b OR a[k] != b[k]p
65
 *
66
 *
67
 *  The Row1 one rule is implemented implicitly as follows:
68
 *     - for each store(b i v) term add the following equality to the congruence
69
 *       closure store(b i v)[i] = v
70
 *     - if one of the literals in a conflict is of the form store(b i v)[i] = v
71
 *       remove it from the conflict
72
 *
73
 *  Because new store terms are not created, we need to check if we need to
74
 *  instantiate a new Row axiom in the following cases:
75
 *     1. the congruence relation changes (i.e. two terms get merged)
76
 *         - when a new equality between array terms a = b is asserted we check if
77
 *           we can instantiate a Row lemma for all pairs of indices i where a is
78
 *           being read and stores
79
 *         - this is only done during full effort check
80
 *     2. a new read term is created either as a consequences of an Ext lemma or a
81
 *        Row lemma
82
 *         - this is implemented in the checkRowForIndex method which is called
83
 *           when preregistering a term of the form a[i].
84
 *         - as a consequence lemmas are instantiated even before full effort check
85
 *
86
 *  The Ext axiom is instantiated when a disequality is asserted during full effort
87
 *  check. Ext lemmas are stored in a cache to prevent instantiating essentially
88
 *  the same lemma multiple times.
89
 */
90
91
781923
static inline std::string spaces(int level)
92
{
93
781923
  std::string indentStr(level, ' ');
94
781923
  return indentStr;
95
}
96
97
class TheoryArrays : public Theory {
98
99
  /////////////////////////////////////////////////////////////////////////////
100
  // MISC
101
  /////////////////////////////////////////////////////////////////////////////
102
103
 private:
104
105
  /** True node for predicates = true */
106
  Node d_true;
107
108
  /** True node for predicates = false */
109
  Node d_false;
110
111
  // Statistics
112
113
  /** number of Row lemmas */
114
  IntStat d_numRow;
115
  /** number of Ext lemmas */
116
  IntStat d_numExt;
117
  /** number of propagations */
118
  IntStat d_numProp;
119
  /** number of explanations */
120
  IntStat d_numExplain;
121
  /** calls to non-linear */
122
  IntStat d_numNonLinear;
123
  /** splits on array variables */
124
  IntStat d_numSharedArrayVarSplits;
125
  /** splits in getModelVal */
126
  IntStat d_numGetModelValSplits;
127
  /** conflicts in getModelVal */
128
  IntStat d_numGetModelValConflicts;
129
  /** splits in setModelVal */
130
  IntStat d_numSetModelValSplits;
131
  /** conflicts in setModelVal */
132
  IntStat d_numSetModelValConflicts;
133
134
 public:
135
  TheoryArrays(context::Context* c,
136
               context::UserContext* u,
137
               OutputChannel& out,
138
               Valuation valuation,
139
               const LogicInfo& logicInfo,
140
               ProofNodeManager* pnm = nullptr,
141
               std::string name = "theory::arrays::");
142
  ~TheoryArrays();
143
144
  //--------------------------------- initialization
145
  /** get the official theory rewriter of this theory */
146
  TheoryRewriter* getTheoryRewriter() override;
147
  /** get the proof checker of this theory */
148
  ProofRuleChecker* getProofChecker() override;
149
  /**
150
   * Returns true if we need an equality engine. If so, we initialize the
151
   * information regarding how it should be setup. For details, see the
152
   * documentation in Theory::needsEqualityEngine.
153
   */
154
  bool needsEqualityEngine(EeSetupInfo& esi) override;
155
  /** finish initialization */
156
  void finishInit() override;
157
  //--------------------------------- end initialization
158
159
  std::string identify() const override { return std::string("TheoryArrays"); }
160
161
  /////////////////////////////////////////////////////////////////////////////
162
  // PREPROCESSING
163
  /////////////////////////////////////////////////////////////////////////////
164
165
 private:
166
167
  // PPNotifyClass: dummy template class for d_ppEqualityEngine - notifications not used
168
  class PPNotifyClass {
169
  public:
170
    bool notify(TNode propagation) { return true; }
171
    void notify(TNode t1, TNode t2) { }
172
  };
173
174
  /** The notify class for d_ppEqualityEngine */
175
  PPNotifyClass d_ppNotify;
176
177
  /** Equaltity engine */
178
  eq::EqualityEngine d_ppEqualityEngine;
179
180
  // List of facts learned by preprocessor - needed for permanent ref for benefit of d_ppEqualityEngine
181
  context::CDList<Node> d_ppFacts;
182
183
  Node preprocessTerm(TNode term);
184
  Node recursivePreprocessTerm(TNode term);
185
  bool ppDisequal(TNode a, TNode b);
186
  Node solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck);
187
188
  /** The theory rewriter for this theory. */
189
  TheoryArraysRewriter d_rewriter;
190
  /** A (default) theory state object */
191
  TheoryState d_state;
192
  /** The arrays inference manager */
193
  InferenceManager d_im;
194
195
 public:
196
  PPAssertStatus ppAssert(TrustNode tin,
197
                          TrustSubstitutionMap& outSubstitutions) override;
198
  TrustNode ppRewrite(TNode atom, std::vector<SkolemLemma>& lems) override;
199
200
  /////////////////////////////////////////////////////////////////////////////
201
  // T-PROPAGATION / REGISTRATION
202
  /////////////////////////////////////////////////////////////////////////////
203
204
 private:
205
  /** Literals to propagate */
206
  context::CDList<Node> d_literalsToPropagate;
207
208
  /** Index of the next literal to propagate */
209
  context::CDO<unsigned> d_literalsToPropagateIndex;
210
211
  /** Should be called to propagate the literal.  */
212
  bool propagateLit(TNode literal);
213
214
  /** Explain why this literal is true by building an explanation */
215
  void explain(TNode literal, Node& exp);
216
217
  /** For debugging only- checks invariants about when things are preregistered*/
218
  context::CDHashSet<Node> d_isPreRegistered;
219
220
  /** Helper for preRegisterTerm, also used internally */
221
  void preRegisterTermInternal(TNode n);
222
223
 public:
224
  void preRegisterTerm(TNode n) override;
225
  TrustNode explain(TNode n) override;
226
227
  /////////////////////////////////////////////////////////////////////////////
228
  // SHARING
229
  /////////////////////////////////////////////////////////////////////////////
230
231
 private:
232
  class MayEqualNotifyClass {
233
  public:
234
    bool notify(TNode propagation) { return true; }
235
    void notify(TNode t1, TNode t2) { }
236
  };
237
238
  /** The notify class for d_mayEqualEqualityEngine */
239
  MayEqualNotifyClass d_mayEqualNotify;
240
241
  /** Equaltity engine for determining if two arrays might be equal */
242
  eq::EqualityEngine d_mayEqualEqualityEngine;
243
244
  // Helper for computeCareGraph
245
  void checkPair(TNode r1, TNode r2);
246
247
 public:
248
  void notifySharedTerm(TNode t) override;
249
  void computeCareGraph() override;
250
  bool isShared(TNode t)
251
  {
252
    return (d_sharedArrays.find(t) != d_sharedArrays.end());
253
  }
254
255
  /////////////////////////////////////////////////////////////////////////////
256
  // MODEL GENERATION
257
  /////////////////////////////////////////////////////////////////////////////
258
259
 public:
260
  /** Collect model values in m based on the relevant terms given by termSet */
261
  bool collectModelValues(TheoryModel* m,
262
                          const std::set<Node>& termSet) override;
263
264
  /////////////////////////////////////////////////////////////////////////////
265
  // NOTIFICATIONS
266
  /////////////////////////////////////////////////////////////////////////////
267
268
269
  void presolve() override;
270
9847
  void shutdown() override {}
271
272
  /////////////////////////////////////////////////////////////////////////////
273
  // MAIN SOLVER
274
  /////////////////////////////////////////////////////////////////////////////
275
276
  //--------------------------------- standard check
277
  /** Post-check, called after the fact queue of the theory is processed. */
278
  void postCheck(Effort level) override;
279
  /** Pre-notify fact, return true if processed. */
280
  bool preNotifyFact(TNode atom,
281
                     bool pol,
282
                     TNode fact,
283
                     bool isPrereg,
284
                     bool isInternal) override;
285
  /** Notify fact */
286
  void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override;
287
  //--------------------------------- end standard check
288
289
 private:
290
  TNode weakEquivGetRep(TNode node);
291
  TNode weakEquivGetRepIndex(TNode node, TNode index);
292
  void visitAllLeaves(TNode reason, std::vector<TNode>& conjunctions);
293
  void weakEquivBuildCond(TNode node, TNode index, std::vector<TNode>& conjunctions);
294
  void weakEquivMakeRep(TNode node);
295
  void weakEquivMakeRepIndex(TNode node);
296
  void weakEquivAddSecondary(TNode index, TNode arrayFrom, TNode arrayTo, TNode reason);
297
  void checkWeakEquiv(bool arraysMerged);
298
299
  // NotifyClass: template helper class for d_equalityEngine - handles call-back from congruence closure module
300
9853
  class NotifyClass : public eq::EqualityEngineNotify {
301
    TheoryArrays& d_arrays;
302
  public:
303
9853
    NotifyClass(TheoryArrays& arrays): d_arrays(arrays) {}
304
305
54157
    bool eqNotifyTriggerPredicate(TNode predicate, bool value) override
306
    {
307
108314
      Debug("arrays::propagate")
308
108314
          << spaces(d_arrays.getSatContext()->getLevel())
309
54157
          << "NotifyClass::eqNotifyTriggerPredicate(" << predicate << ", "
310
54157
          << (value ? "true" : "false") << ")" << std::endl;
311
      // Just forward to arrays
312
54157
      if (value) {
313
36842
        return d_arrays.propagateLit(predicate);
314
      }
315
17315
      return d_arrays.propagateLit(predicate.notNode());
316
    }
317
318
155345
    bool eqNotifyTriggerTermEquality(TheoryId tag,
319
                                     TNode t1,
320
                                     TNode t2,
321
                                     bool value) override
322
    {
323
155345
      Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl;
324
155345
      if (value) {
325
        // Propagate equality between shared terms
326
128547
        return d_arrays.propagateLit(t1.eqNode(t2));
327
      }
328
26798
      return d_arrays.propagateLit(t1.eqNode(t2).notNode());
329
    }
330
331
69
    void eqNotifyConstantTermMerge(TNode t1, TNode t2) override
332
    {
333
69
      Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl;
334
69
      d_arrays.conflict(t1, t2);
335
69
    }
336
337
45277
    void eqNotifyNewClass(TNode t) override
338
    {
339
45277
      d_arrays.preRegisterTermInternal(t);
340
45277
    }
341
291718
    void eqNotifyMerge(TNode t1, TNode t2) override
342
    {
343
291718
      if (t1.getType().isArray()) {
344
5810
        d_arrays.mergeArrays(t1, t2);
345
      }
346
291715
    }
347
31463
    void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override {}
348
  };
349
350
  /** The notify class for d_equalityEngine */
351
  NotifyClass d_notify;
352
353
  /** The proof checker */
354
  ArraysProofRuleChecker d_checker;
355
356
  /** Conflict when merging constants */
357
  void conflict(TNode a, TNode b);
358
359
  /** The conflict node */
360
  Node d_conflictNode;
361
362
  /**
363
   * Context dependent map from a congruence class canonical representative of
364
   * type array to an Info pointer that keeps track of information useful to axiom
365
   * instantiation
366
   */
367
368
  Backtracker<TNode> d_backtracker;
369
  ArrayInfo d_infoMap;
370
371
  context::CDQueue<Node> d_mergeQueue;
372
373
  bool d_mergeInProgress;
374
375
  using RowLemmaType = std::tuple<TNode, TNode, TNode, TNode>;
376
377
  context::CDQueue<RowLemmaType> d_RowQueue;
378
  context::CDHashSet<RowLemmaType, RowLemmaTypeHashFunction > d_RowAlreadyAdded;
379
380
  typedef context::CDHashSet<Node> CDNodeSet;
381
382
  CDNodeSet d_sharedArrays;
383
  CDNodeSet d_sharedOther;
384
  context::CDO<bool> d_sharedTerms;
385
386
  // Map from constant values to read terms that read from that values equal to that constant value in the current model
387
  // When a new read term is created, we check the index to see if we know the model value.  If so, we add it to d_constReads (and d_constReadsList)
388
  // If not, we push it onto d_reads and figure out where it goes at computeCareGraph time.
389
  // d_constReadsList is used as a backup in case we can't compute the model at computeCareGraph time.
390
  typedef std::unordered_map<Node, CTNodeList*> CNodeNListMap;
391
  CNodeNListMap d_constReads;
392
  context::CDList<TNode> d_reads;
393
  context::CDList<TNode> d_constReadsList;
394
  context::Context* d_constReadsContext;
395
  /** Helper class to keep d_constReadsContext in sync with satContext */
396
9853
  class ContextPopper : public context::ContextNotifyObj {
397
    context::Context* d_satContext;
398
    context::Context* d_contextToPop;
399
  protected:
400
3108484
   void contextNotifyPop() override
401
   {
402
3108484
     if (d_contextToPop->getLevel() > d_satContext->getLevel())
403
     {
404
23180
       d_contextToPop->pop();
405
     }
406
3108484
    }
407
  public:
408
9853
    ContextPopper(context::Context* context, context::Context* contextToPop)
409
9853
      :context::ContextNotifyObj(context), d_satContext(context),
410
9853
       d_contextToPop(contextToPop)
411
9853
    {}
412
413
  };/* class ContextPopper */
414
  ContextPopper d_contextPopper;
415
416
  std::unordered_map<Node, Node> d_skolemCache;
417
  context::CDO<unsigned> d_skolemIndex;
418
  std::vector<Node> d_skolemAssertions;
419
420
  // The decision requests we have for the core
421
  context::CDQueue<Node> d_decisionRequests;
422
423
  // List of nodes that need permanent references in this context
424
  context::CDList<Node> d_permRef;
425
  context::CDList<Node> d_modelConstraints;
426
  context::CDHashSet<Node> d_lemmasSaved;
427
  std::vector<Node> d_lemmas;
428
429
  // Default values for each mayEqual equivalence class
430
  typedef context::CDHashMap<Node, Node> DefValMap;
431
  DefValMap d_defValues;
432
433
  typedef std::unordered_map<std::pair<TNode, TNode>, CTNodeList*, TNodePairHashFunction> ReadBucketMap;
434
  ReadBucketMap d_readBucketTable;
435
  context::Context* d_readTableContext;
436
  context::CDList<Node> d_arrayMerges;
437
  std::vector<CTNodeList*> d_readBucketAllocations;
438
439
  Node getSkolem(TNode ref);
440
  Node mkAnd(std::vector<TNode>& conjunctions, bool invert = false, unsigned startIndex = 0);
441
  void setNonLinear(TNode a);
442
  Node removeRepLoops(TNode a, TNode rep);
443
  Node expandStores(TNode s, std::vector<TNode>& assumptions, bool checkLoop = false, TNode a = TNode(), TNode b = TNode());
444
  void mergeArrays(TNode a, TNode b);
445
  void checkStore(TNode a);
446
  void checkRowForIndex(TNode i, TNode a);
447
  void checkRowLemmas(TNode a, TNode b);
448
  void propagateRowLemma(RowLemmaType lem);
449
  void queueRowLemma(RowLemmaType lem);
450
  bool dischargeLemmas();
451
452
  std::vector<Node> d_decisions;
453
  bool d_inCheckModel;
454
  int d_topLevel;
455
456
  /**
457
   * The decision strategy for the theory of arrays, which calls the
458
   * getNextDecisionEngineRequest function below.
459
   */
460
19706
  class TheoryArraysDecisionStrategy : public DecisionStrategy
461
  {
462
   public:
463
    TheoryArraysDecisionStrategy(TheoryArrays* ta);
464
    /** initialize */
465
    void initialize() override;
466
    /** get next decision request */
467
    Node getNextDecisionRequest() override;
468
    /** identify */
469
    std::string identify() const override;
470
471
   private:
472
    /** pointer to the theory of arrays */
473
    TheoryArrays* d_ta;
474
  };
475
  /** an instance of the above decision strategy */
476
  std::unique_ptr<TheoryArraysDecisionStrategy> d_dstrat;
477
  /** Have we registered the above strategy? (context-independent) */
478
  bool d_dstratInit;
479
  /** get the next decision request
480
   *
481
   * If the "arrays-eager-index" option is enabled, then whenever a
482
   * read-over-write lemma is generated, a decision request is also generated
483
   * for the comparison between the indexes that appears in the lemma.
484
   */
485
  Node getNextDecisionRequest();
486
  /**
487
   * Compute relevant terms. This includes select nodes for the
488
   * RIntro1 and RIntro2 rules.
489
   */
490
  void computeRelevantTerms(std::set<Node>& termSet) override;
491
};/* class TheoryArrays */
492
493
}  // namespace arrays
494
}  // namespace theory
495
}  // namespace cvc5
496
497
#endif /* CVC5__THEORY__ARRAYS__THEORY_ARRAYS_H */