GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/preprocessing/util/ite_utilities.h Lines: 8 43 18.6 %
Date: 2021-09-18 Branches: 4 44 9.1 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Aina Niemetz, 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
 * Simplifications for ITE expressions.
14
 *
15
 * This module implements preprocessing phases designed to simplify ITE
16
 * expressions.  Based on:
17
 * Kim, Somenzi, Jin.  Efficient Term-ITE Conversion for SMT.  FMCAD 2009.
18
 * Burch, Jerry.  Techniques for Verifying Superscalar Microprocessors.  DAC
19
 *'96
20
 */
21
22
#include "cvc5_private.h"
23
24
#ifndef CVC5__ITE_UTILITIES_H
25
#define CVC5__ITE_UTILITIES_H
26
27
#include <unordered_map>
28
#include <vector>
29
30
#include "expr/node.h"
31
#include "smt/env_obj.h"
32
#include "util/hash.h"
33
#include "util/statistics_stats.h"
34
35
namespace cvc5 {
36
37
namespace preprocessing {
38
39
class AssertionPipeline;
40
41
namespace util {
42
43
class ITECompressor;
44
class ITESimplifier;
45
class ITECareSimplifier;
46
47
/**
48
 * A caching visitor that computes whether a node contains a term ite.
49
 */
50
class ContainsTermITEVisitor
51
{
52
 public:
53
  ContainsTermITEVisitor();
54
  ~ContainsTermITEVisitor();
55
56
  /** returns true if a node contains a term ite. */
57
  bool containsTermITE(TNode n);
58
59
  /** Garbage collects the cache. */
60
  void garbageCollect();
61
62
  /** returns the size of the cache. */
63
  size_t cache_size() const { return d_cache.size(); }
64
65
 private:
66
  typedef std::unordered_map<Node, bool> NodeBoolMap;
67
  NodeBoolMap d_cache;
68
};
69
70
class ITEUtilities : protected EnvObj
71
{
72
 public:
73
  ITEUtilities(Env& env);
74
  ~ITEUtilities();
75
76
  Node simpITE(TNode assertion);
77
78
  bool simpIteDidALotOfWorkHeuristic() const;
79
80
  /* returns false if an assertion is discovered to be equal to false. */
81
  bool compress(AssertionPipeline* assertionsToPreprocess);
82
83
  Node simplifyWithCare(TNode e);
84
85
  void clear();
86
87
  ContainsTermITEVisitor* getContainsVisitor()
88
  {
89
    return d_containsVisitor.get();
90
  }
91
92
2
  bool containsTermITE(TNode n)
93
  {
94
2
    return d_containsVisitor->containsTermITE(n);
95
  }
96
97
 private:
98
  std::unique_ptr<ContainsTermITEVisitor> d_containsVisitor;
99
  ITECompressor* d_compressor;
100
  ITESimplifier* d_simplifier;
101
  ITECareSimplifier* d_careSimp;
102
};
103
104
class IncomingArcCounter
105
{
106
 public:
107
  IncomingArcCounter(bool skipVars = false, bool skipConstants = false);
108
  ~IncomingArcCounter();
109
  void computeReachability(const std::vector<Node>& assertions);
110
111
7109
  inline uint32_t lookupIncoming(Node n) const
112
  {
113
7109
    NodeCountMap::const_iterator it = d_reachCount.find(n);
114
7109
    if (it == d_reachCount.end())
115
    {
116
      return 0;
117
    }
118
    else
119
    {
120
7109
      return (*it).second;
121
    }
122
  }
123
  void clear();
124
125
 private:
126
  typedef std::unordered_map<Node, uint32_t> NodeCountMap;
127
  NodeCountMap d_reachCount;
128
129
  bool d_skipVariables;
130
  bool d_skipConstants;
131
};
132
133
class TermITEHeightCounter
134
{
135
 public:
136
  TermITEHeightCounter();
137
  ~TermITEHeightCounter();
138
139
  /**
140
   * Compute and [potentially] cache the termITEHeight() of e.
141
   * The term ite height equals the maximum number of term ites
142
   * encountered on any path from e to a leaf.
143
   * Inductively:
144
   *  - termITEHeight(leaves) = 0
145
   *  - termITEHeight(e: term-ite(c, t, e) ) =
146
   *     1 + max(termITEHeight(t), termITEHeight(e)) ; Don't include c
147
   *  - termITEHeight(e not term ite) = max_{c in children(e))
148
   * (termITEHeight(c))
149
   */
150
  uint32_t termITEHeight(TNode e);
151
152
  /** Clear the cache. */
153
  void clear();
154
155
  /** Size of the cache. */
156
  size_t cache_size() const;
157
158
 private:
159
  typedef std::unordered_map<Node, uint32_t> NodeCountMap;
160
  NodeCountMap d_termITEHeight;
161
}; /* class TermITEHeightCounter */
162
163
/**
164
 * A routine designed to undo the potentially large blow up
165
 * due to expansion caused by the ite simplifier.
166
 */
167
class ITECompressor : protected EnvObj
168
{
169
 public:
170
  ITECompressor(Env& env, ContainsTermITEVisitor* contains);
171
  ~ITECompressor();
172
173
  /* returns false if an assertion is discovered to be equal to false. */
174
  bool compress(AssertionPipeline* assertionsToPreprocess);
175
176
  /* garbage Collects the compressor. */
177
  void garbageCollect();
178
179
 private:
180
  class Statistics
181
  {
182
   public:
183
    IntStat d_compressCalls;
184
    IntStat d_skolemsAdded;
185
    Statistics(StatisticsRegistry& reg);
186
  };
187
188
  void reset();
189
190
  Node push_back_boolean(Node original, Node compressed);
191
  bool multipleParents(TNode c);
192
  Node compressBooleanITEs(Node toCompress);
193
  Node compressTerm(Node toCompress);
194
  Node compressBoolean(Node toCompress);
195
196
  Node d_true;  /* Copy of true. */
197
  Node d_false; /* Copy of false. */
198
199
  ContainsTermITEVisitor* d_contains;
200
  AssertionPipeline* d_assertions;
201
  IncomingArcCounter d_incoming;
202
203
  typedef std::unordered_map<Node, Node> NodeMap;
204
  NodeMap d_compressed;
205
206
  Statistics d_statistics;
207
}; /* class ITECompressor */
208
209
class ITESimplifier : protected EnvObj
210
{
211
 public:
212
  ITESimplifier(Env& env, ContainsTermITEVisitor* d_containsVisitor);
213
  ~ITESimplifier();
214
215
  Node simpITE(TNode assertion);
216
217
  bool doneALotOfWorkHeuristic() const;
218
  void clearSimpITECaches();
219
220
 private:
221
  using NodeVec = std::vector<Node>;
222
  using ConstantLeavesMap = std::unordered_map<Node, NodeVec*>;
223
  using NodePair = std::pair<Node, Node>;
224
  using NodePairHashFunction =
225
      PairHashFunction<Node, Node, std::hash<Node>, std::hash<Node>>;
226
  using NodePairMap = std::unordered_map<NodePair, Node, NodePairHashFunction>;
227
228
  class Statistics
229
  {
230
   public:
231
    IntStat d_maxNonConstantsFolded;
232
    IntStat d_unexpected;
233
    IntStat d_unsimplified;
234
    IntStat d_exactMatchFold;
235
    IntStat d_binaryPredFold;
236
    IntStat d_specialEqualityFolds;
237
    IntStat d_simpITEVisits;
238
239
    HistogramStat<uint32_t> d_inSmaller;
240
241
    Statistics(StatisticsRegistry& reg);
242
  };
243
244
2043
  inline bool containsTermITE(TNode n)
245
  {
246
2043
    return d_containsVisitor->containsTermITE(n);
247
  }
248
249
  inline uint32_t termITEHeight(TNode e)
250
  {
251
    return d_termITEHeight.termITEHeight(e);
252
  }
253
254
  // d_constantLeaves satisfies the following invariants:
255
  // not containsTermITE(x) then !isKey(x)
256
  // containsTermITE(x):
257
  // - not isKey(x) then this value is uncomputed
258
  // - d_constantLeaves[x] == NULL, then this contains a non-constant leaf
259
  // - d_constantLeaves[x] != NULL, then this contains a sorted list of constant
260
  // leaf
261
  bool isConstantIte(TNode e);
262
263
  /** If its not a constant and containsTermITE(ite),
264
   * returns a sorted NodeVec of the leaves. */
265
  NodeVec* computeConstantLeaves(TNode ite);
266
267
  /* transforms */
268
  Node transformAtom(TNode atom);
269
  Node attemptConstantRemoval(TNode atom);
270
  Node attemptLiftEquality(TNode atom);
271
  Node attemptEagerRemoval(TNode atom);
272
273
  // Given ConstantIte trees lcite and rcite,
274
  // return a boolean expression equivalent to (= lcite rcite)
275
  Node intersectConstantIte(TNode lcite, TNode rcite);
276
277
  // Given ConstantIte tree cite and a constant c,
278
  // return a boolean expression equivalent to (= lcite c)
279
  Node constantIteEqualsConstant(TNode cite, TNode c);
280
281
  Node replaceOver(Node n, Node replaceWith, Node simpVar);
282
  Node replaceOverTermIte(Node term, Node simpAtom, Node simpVar);
283
284
  bool leavesAreConst(TNode e, theory::TheoryId tid);
285
  bool leavesAreConst(TNode e);
286
287
  Node simpConstants(TNode simpContext, TNode iteNode, TNode simpVar);
288
289
  Node createSimpContext(TNode c, Node& iteNode, Node& simpVar);
290
291
  Node d_true;
292
  Node d_false;
293
294
  ContainsTermITEVisitor* d_containsVisitor;
295
296
  TermITEHeightCounter d_termITEHeight;
297
298
  // ConstantIte is a small inductive sublanguage:
299
  //     constant
300
  // or  termITE(cnd, ConstantIte, ConstantIte)
301
  ConstantLeavesMap d_constantLeaves;
302
303
  // Lists all of the vectors in d_constantLeaves for fast deletion.
304
  std::vector<NodeVec*> d_allocatedConstantLeaves;
305
306
  uint32_t d_citeEqConstApplications;
307
308
  NodePairMap d_constantIteEqualsConstantCache;
309
  NodePairMap d_replaceOverCache;
310
  NodePairMap d_replaceOverTermIteCache;
311
312
  std::unordered_map<Node, bool> d_leavesConstCache;
313
314
  NodePairMap d_simpConstCache;
315
  std::unordered_map<TypeNode, Node> d_simpVars;
316
  Node getSimpVar(TypeNode t);
317
318
  typedef std::unordered_map<Node, Node> NodeMap;
319
  NodeMap d_simpContextCache;
320
321
  NodeMap d_simpITECache;
322
  Node simpITEAtom(TNode atom);
323
324
  Statistics d_statistics;
325
};
326
327
class ITECareSimplifier
328
{
329
 public:
330
  ITECareSimplifier();
331
  ~ITECareSimplifier();
332
333
  Node simplifyWithCare(TNode e);
334
335
  void clear();
336
337
 private:
338
  /**
339
   * This should always equal the number of care sets allocated by
340
   * this object - the number of these that have been deleted. This is
341
   * initially 0 and should always be 0 at the *start* of
342
   * ~ITECareSimplifier().
343
   */
344
  unsigned d_careSetsOutstanding;
345
346
  Node d_true;
347
  Node d_false;
348
349
  typedef std::unordered_map<TNode, Node> TNodeMap;
350
351
  class CareSetPtr;
352
  class CareSetPtrVal
353
  {
354
   public:
355
    bool safeToGarbageCollect() const { return d_refCount == 0; }
356
357
   private:
358
    friend class ITECareSimplifier::CareSetPtr;
359
    ITECareSimplifier& d_iteSimplifier;
360
    unsigned d_refCount;
361
    std::set<Node> d_careSet;
362
    CareSetPtrVal(ITECareSimplifier& simp)
363
        : d_iteSimplifier(simp), d_refCount(1)
364
    {
365
    }
366
  }; /* class ITECareSimplifier::CareSetPtrVal */
367
368
  std::vector<CareSetPtrVal*> d_usedSets;
369
  void careSetPtrGC(CareSetPtrVal* val) { d_usedSets.push_back(val); }
370
371
  class CareSetPtr
372
  {
373
    CareSetPtrVal* d_val;
374
    CareSetPtr(CareSetPtrVal* val) : d_val(val) {}
375
376
   public:
377
    CareSetPtr() : d_val(NULL) {}
378
    CareSetPtr(const CareSetPtr& cs)
379
    {
380
      d_val = cs.d_val;
381
      if (d_val != NULL)
382
      {
383
        ++(d_val->d_refCount);
384
      }
385
    }
386
    ~CareSetPtr()
387
    {
388
      if (d_val != NULL && (--(d_val->d_refCount) == 0))
389
      {
390
        d_val->d_iteSimplifier.careSetPtrGC(d_val);
391
      }
392
    }
393
    CareSetPtr& operator=(const CareSetPtr& cs)
394
    {
395
      if (d_val != cs.d_val)
396
      {
397
        if (d_val != NULL && (--(d_val->d_refCount) == 0))
398
        {
399
          d_val->d_iteSimplifier.careSetPtrGC(d_val);
400
        }
401
        d_val = cs.d_val;
402
        if (d_val != NULL)
403
        {
404
          ++(d_val->d_refCount);
405
        }
406
      }
407
      return *this;
408
    }
409
    std::set<Node>& getCareSet() { return d_val->d_careSet; }
410
411
    static CareSetPtr mkNew(ITECareSimplifier& simp);
412
    static CareSetPtr recycle(CareSetPtrVal* val)
413
    {
414
      Assert(val != NULL && val->d_refCount == 0);
415
      val->d_refCount = 1;
416
      return CareSetPtr(val);
417
    }
418
  }; /* class ITECareSimplifier::CareSetPtr */
419
420
  CareSetPtr getNewSet();
421
422
  typedef std::map<TNode, CareSetPtr> CareMap;
423
  void updateQueue(CareMap& queue, TNode e, CareSetPtr& careSet);
424
  Node substitute(TNode e, TNodeMap& substTable, TNodeMap& cache);
425
};
426
427
}  // namespace util
428
}  // namespace preprocessing
429
}  // namespace cvc5
430
431
#endif