GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_enumerator.h Lines: 9 9 100.0 %
Date: 2021-09-07 Branches: 0 0 0.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Mathias Preiner
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
 * sygus_enumerator
14
 */
15
16
#include "cvc5_private.h"
17
18
#ifndef CVC5__THEORY__QUANTIFIERS__SYGUS_ENUMERATOR_H
19
#define CVC5__THEORY__QUANTIFIERS__SYGUS_ENUMERATOR_H
20
21
#include <map>
22
#include <unordered_set>
23
24
#include "expr/node.h"
25
#include "expr/type_node.h"
26
#include "theory/quantifiers/sygus/enum_val_generator.h"
27
#include "theory/quantifiers/sygus/sygus_enumerator_callback.h"
28
#include "theory/quantifiers/sygus/term_database_sygus.h"
29
30
namespace cvc5 {
31
namespace theory {
32
namespace quantifiers {
33
34
class SynthConjecture;
35
class SygusPbe;
36
37
/** SygusEnumerator
38
 *
39
 * This class is used for enumerating all terms of a sygus datatype type. At
40
 * a high level, it is used as an alternative approach to sygus datatypes
41
 * solver as a candidate generator in a synthesis loop. It filters terms based
42
 * on redundancy criteria, for instance, it does not generate two terms whose
43
 * builtin terms (TermDb::sygusToBuiltin) can be shown to be equivalent via
44
 * rewriting. It enumerates terms in order of sygus term size
45
 * (TermDb::getSygusTermSize).
46
 *
47
 * It also can be configured to enumerates sygus terms with free variables,
48
 * (as opposed to variables bound in the formal arguments list of the
49
 * function-to-synthesize), where each free variable appears in exactly one
50
 * subterm. For grammar:
51
 *   S -> 0 | 1 | x | S+S
52
 * this enumerator will generate the stream:
53
 *   z1, C_0, C_1, C_x, C_+(z1, z2), C_+(z1, C_1), C_+(C_1, C_1) ...
54
 * and so on, where z1 and z2 are variables of sygus datatype type S. We call
55
 * these "shapes". This feature can be enabled by setting enumShapes to true
56
 * in the constructor below.
57
 */
58
class SygusEnumerator : public EnumValGenerator
59
{
60
 public:
61
  /**
62
   * @param tds Pointer to the term database, required if enumShapes or
63
   * enumAnyConstHoles is true, or if we want to include symmetry breaking from
64
   * lemmas stored in the sygus term database,
65
   * @param sec Pointer to the callback, required e.g. if we wish to do
66
   * conjecture-specific symmetry breaking
67
   * @param s Pointer to the statistics
68
   * @param enumShapes If true, this enumerator will generate terms having any
69
   * number of free variables
70
   * @param enumAnyConstHoles If true, this enumerator will generate terms where
71
   * free variables are the arguments to any-constant constructors.
72
   */
73
  SygusEnumerator(TermDbSygus* tds = nullptr,
74
                  SygusEnumeratorCallback* sec = nullptr,
75
                  SygusStatistics* s = nullptr,
76
                  bool enumShapes = false,
77
                  bool enumAnyConstHoles = false);
78
332
  ~SygusEnumerator() {}
79
  /** initialize this class with enumerator e */
80
  void initialize(Node e) override;
81
  /** Inform this generator that abstract value v was enumerated. */
82
  void addValue(Node v) override;
83
  /** increment */
84
  bool increment() override;
85
  /** Get the next concrete value generated by this class. */
86
  Node getCurrent() override;
87
  /** Are we enumerating shapes? */
88
  bool isEnumShapes() const;
89
90
 private:
91
  /** pointer to term database sygus */
92
  TermDbSygus* d_tds;
93
  /** pointer to the enumerator callback we are using (if any) */
94
  SygusEnumeratorCallback* d_sec;
95
  /** if we allocated a default sygus enumerator callback */
96
  std::unique_ptr<SygusEnumeratorCallbackDefault> d_secd;
97
  /** pointer to the statistics */
98
  SygusStatistics* d_stats;
99
  /** Whether we are enumerating shapes */
100
  bool d_enumShapes;
101
  /** Whether we are enumerating free variables as arguments to any-constant
102
   * constructors */
103
  bool d_enumAnyConstHoles;
104
  /** Term cache
105
   *
106
   * This stores a list of terms for a given sygus type. The key features of
107
   * this data structure are that terms are stored in order of size,
108
   * indices can be recorded that indicate where terms of size n begin for each
109
   * natural number n, and redundancy criteria are used for discarding terms
110
   * that are not relevant. This includes discarding terms whose builtin version
111
   * is the same up to T-rewriting with another, or is equivalent under
112
   * examples, if the conjecture in question is in examples form and
113
   * sygusSymBreakPbe is enabled.
114
   *
115
   * This class also computes static information about sygus types that is
116
   * relevant for enumeration. Primarily, this includes mapping constructors
117
   * to "constructor classes". Two sygus constructors can be placed in the same
118
   * constructor class if their constructor weight is equal, and the tuple
119
   * of their argument types are the same. For example, for:
120
   *   A -> A+B | A-B | A*B | A+A | A | x
121
   * The first three constructors above can be placed in the same constructor
122
   * class, assuming they have identical weights. Constructor classes are used
123
   * as an optimization when enumerating terms, since they expect the same
124
   * tuple of argument terms for constructing a term of a fixed size.
125
   *
126
   * Constructor classes are allocated such that the constructor weights are
127
   * in ascending order. This allows us to avoid constructors whose weight
128
   * is greater than n while we are trying to enumerate terms of sizes strictly
129
   * less than n.
130
   *
131
   * Constructor class 0 is reserved for nullary operators with weight 0. This
132
   * is an optimization so that such constructors can be skipped for sizes
133
   * greater than 0, since we know all terms generated by these constructors
134
   * have size 0.
135
   */
136
310
  class TermCache
137
  {
138
   public:
139
    TermCache();
140
    /** initialize this cache */
141
    void initialize(SygusStatistics* s,
142
                    Node e,
143
                    TypeNode tn,
144
                    SygusEnumeratorCallback* sec = nullptr);
145
    /** get last constructor class index for weight
146
     *
147
     * This returns a minimal index n such that all constructor classes at
148
     * index < n have weight at most w.
149
     */
150
    unsigned getLastConstructorClassIndexForWeight(unsigned w) const;
151
    /** get num constructor classes */
152
    unsigned getNumConstructorClasses() const;
153
    /** get the constructor indices for constructor class i */
154
    void getConstructorClass(unsigned i, std::vector<unsigned>& cclass) const;
155
    /** get argument types for constructor class i */
156
    void getTypesForConstructorClass(unsigned i,
157
                                     std::vector<TypeNode>& types) const;
158
    /** get constructor weight for constructor class i */
159
    unsigned getWeightForConstructorClass(unsigned i) const;
160
161
    /**
162
     * Add sygus term n to this cache, return true if the term was unique based
163
     * on the redundancy criteria used by this class.
164
     */
165
    bool addTerm(Node n);
166
    /**
167
     * Indicate to this cache that we are finished enumerating terms of the
168
     * current size.
169
     */
170
    void pushEnumSizeIndex();
171
    /** Get the current size of terms that we are enumerating */
172
    unsigned getEnumSize() const;
173
    /** get the index at which size s terms start, where s <= getEnumSize() */
174
    unsigned getIndexForSize(unsigned s) const;
175
    /** get the index^th term successfully added to this cache */
176
    Node getTerm(unsigned index) const;
177
    /** get the number of terms successfully added to this cache */
178
    unsigned getNumTerms() const;
179
    /** are we finished enumerating terms? */
180
    bool isComplete() const;
181
    /** set that we are finished enumerating terms */
182
    void setComplete();
183
184
   private:
185
    /** reference to the statistics of parent */
186
    SygusStatistics* d_stats;
187
    /** the enumerator this cache is for */
188
    Node d_enum;
189
    /** the sygus type of terms in this cache */
190
    TypeNode d_tn;
191
    /** Pointer to the callback (used for symmetry breaking). */
192
    SygusEnumeratorCallback* d_sec;
193
    //-------------------------static information about type
194
    /** is d_tn a sygus type? */
195
    bool d_isSygusType;
196
    /** number of constructor classes */
197
    unsigned d_numConClasses;
198
    /** Map from weights to the starting constructor class for that weight. */
199
    std::map<unsigned, unsigned> d_weightToCcIndex;
200
    /** Information for each constructor class */
201
    class ConstructorClass
202
    {
203
     public:
204
806
      ConstructorClass() : d_weight(0) {}
205
806
      ~ConstructorClass() {}
206
      /** The indices of the constructors in this constructor class */
207
      std::vector<unsigned> d_cons;
208
      /** The argument types of the constructor class */
209
      std::vector<TypeNode> d_types;
210
      /** Constructor weight */
211
      unsigned d_weight;
212
    };
213
    std::map<unsigned, ConstructorClass> d_cclass;
214
    /** constructor to indices */
215
    std::map<unsigned, std::vector<unsigned>> d_cToCIndices;
216
    //-------------------------end static information about type
217
218
    /** the list of sygus terms we have enumerated */
219
    std::vector<Node> d_terms;
220
    /** the set of builtin terms corresponding to the above list */
221
    std::unordered_set<Node> d_bterms;
222
    /**
223
     * The index of first term whose size is greater than or equal to that size,
224
     * if it exists.
225
     */
226
    std::map<unsigned, unsigned> d_sizeStartIndex;
227
    /** the maximum size of terms we have stored in this cache so far */
228
    unsigned d_sizeEnum;
229
    /** whether this term cache is complete */
230
    bool d_isComplete;
231
  };
232
  /** above cache for each sygus type */
233
  std::map<TypeNode, TermCache> d_tcache;
234
  /** initialize term cache for type tn */
235
  void initializeTermCache(TypeNode tn);
236
237
  /** virtual class for term enumerators */
238
  class TermEnum
239
  {
240
   public:
241
    TermEnum();
242
3607
    virtual ~TermEnum() {}
243
    /** get the current size of terms we are enumerating */
244
    unsigned getCurrentSize();
245
    /** get the current term of the enumerator */
246
    virtual Node getCurrent() = 0;
247
    /** increment the enumerator, return false if the enumerator is finished */
248
    virtual bool increment() = 0;
249
250
   protected:
251
    /** pointer to the sygus enumerator class */
252
    SygusEnumerator* d_se;
253
    /** the (sygus) type of terms we are enumerating */
254
    TypeNode d_tn;
255
    /** the current size of terms we are enumerating */
256
    unsigned d_currSize;
257
  };
258
  class TermEnumMaster;
259
  /** A "slave" enumerator
260
   *
261
   * A slave enumerator simply iterates over an index in a given term cache,
262
   * and relies on a pointer to a "master" enumerator to generate new terms
263
   * whenever necessary.
264
   *
265
   * This class maintains the following invariants, for tc=d_se->d_tcache[d_tn]:
266
   * (1) d_index < tc.getNumTerms(),
267
   * (2) d_currSize is the term size of tc.getTerm( d_index ),
268
   * (3) d_hasIndexNextEnd is (d_currSize < tc.getEnumSize()),
269
   * (4) If d_hasIndexNextEnd is true, then
270
   *       d_indexNextEnd = tc.getIndexForSize(d_currSize+1), and
271
   *       d_indexNextEnd > d_index.
272
   *
273
   * Intuitively, these invariants say (1) d_index is a valid index in the
274
   * term cache, (2) d_currSize is the sygus term size of getCurrent(), (3)
275
   * d_hasIndexNextEnd indicates whether d_indexNextEnd is valid, and (4)
276
   * d_indexNextEnd is the index in the term cache where terms of the current
277
   * size end, if such an index exists.
278
   */
279
3297
  class TermEnumSlave : public TermEnum
280
  {
281
   public:
282
    TermEnumSlave();
283
    /**
284
     * Initialize this enumerator to enumerate terms of type tn whose size is in
285
     * the range [sizeMin, sizeMax], inclusive. If this function returns true,
286
     * then getCurrent() will return the first term in the stream, which is
287
     * non-empty. Further terms are generated by increment()/getCurrent().
288
     */
289
    bool initialize(SygusEnumerator* se,
290
                    TypeNode tn,
291
                    unsigned sizeMin,
292
                    unsigned sizeMax);
293
    /** get the current term of the enumerator */
294
    Node getCurrent() override;
295
    /** increment the enumerator */
296
    bool increment() override;
297
298
   private:
299
    /** the maximum size of terms this enumerator should enumerate */
300
    unsigned d_sizeLim;
301
    /** the current index in the term cache we are considering */
302
    unsigned d_index;
303
    /** the index in the term cache where terms of the current size end */
304
    unsigned d_indexNextEnd;
305
    /** whether d_indexNextEnd refers to a valid index */
306
    bool d_hasIndexNextEnd;
307
    /** pointer to the master enumerator of type d_tn */
308
    TermEnum* d_master;
309
    /** validate invariants on d_index, d_indexNextEnd, d_hasIndexNextEnd */
310
    bool validateIndex();
311
    /** validate invariants on  d_indexNextEnd, d_hasIndexNextEnd  */
312
    void validateIndexNextEnd();
313
  };
314
  /** Class for "master" enumerators
315
   *
316
   * This enumerator is used to generate new terms of a given type d_tn. It
317
   * generates all terms of type d_tn that are not redundant based on the
318
   * current criteria.
319
   *
320
   * To enumerate terms, this class performs the following steps as necessary
321
   * during a call to increment():
322
   * - Fix the size of terms "d_currSize" we are currently enumerating,
323
   * - Set the constructor class "d_consClassNum" whose constructors are the top
324
   * symbol of the current term we are constructing. All constructors from this
325
   * class have the same weight, which we store in d_ccWeight,
326
   * - Initialize the current children "d_children" so that the sum of their
327
   * current sizes is exactly (d_currSize - d_ccWeight),
328
   * - Increment the current set of children until a new tuple of terms is
329
   * considered,
330
   * - Set the constructor "d_consNum" from within the constructor class,
331
   * - Build the current term and check whether it is not redundant according
332
   * to the term cache of its type.
333
   *
334
   * We say this enumerator is active if initialize(...) returns true, and the
335
   * last call (if any) to increment returned true.
336
   *
337
   * This class maintains the following invariants, for tc=d_se->d_tcache[d_tn],
338
   * if it is active:
339
   * (1) getCurrent() is tc.getTerm(tc.getNumTerms()-1),
340
   * (2) tc.pushEnumSizeIndex() has been called by this class exactly
341
   * getCurrentSize() times.
342
   * In other words, (1) getCurrent() is always the last term in the term cache
343
   * of the type of this enumerator tc, and (2) the size counter of tc is in
344
   * sync with the current size of this enumerator, that is, the master
345
   * enumerator is responsible for communicating size boundaries to its term
346
   * cache.
347
   */
348
294
  class TermEnumMaster : public TermEnum
349
  {
350
   public:
351
    TermEnumMaster();
352
    /**
353
     * Initialize this enumerator to enumerate (all) terms of type tn, modulo
354
     * the redundancy criteria used by this class.
355
     */
356
    bool initialize(SygusEnumerator* se, TypeNode tn);
357
    /** get the current term of the enumerator */
358
    Node getCurrent() override;
359
    /** increment the enumerator
360
     *
361
     * Returns true if there are more terms to enumerate, in which case a
362
     * subsequent call to getCurrent() returns the next enumerated term. This
363
     * method returns false if the last call to increment() has yet to
364
     * terminate. This can happen for recursive datatypes where a slave
365
     * enumerator may request that this class increment the next term. We avoid
366
     * an infinite loop by guarding this with the d_isIncrementing flag and
367
     * return false.
368
     */
369
    bool increment() override;
370
371
   private:
372
    /** pointer to term database sygus */
373
    TermDbSygus* d_tds;
374
    /** are we enumerating shapes? */
375
    bool d_enumShapes;
376
    /** have we initialized the shape enumeration? */
377
    bool d_enumShapesInit;
378
    /** are we currently inside a increment() call? */
379
    bool d_isIncrementing;
380
    /** cache for getCurrent() */
381
    Node d_currTerm;
382
    /** is d_currTerm set */
383
    bool d_currTermSet;
384
    //----------------------------- current constructor class information
385
    /** the next constructor class we are using */
386
    unsigned d_consClassNum;
387
    /** the constructors in the current constructor class */
388
    std::vector<unsigned> d_ccCons;
389
    /** the types of the current constructor class */
390
    std::vector<TypeNode> d_ccTypes;
391
    /** the operator weight for the constructor class */
392
    unsigned d_ccWeight;
393
    //----------------------------- end current constructor class information
394
    /** If >0, 1 + the index in d_ccCons we are considering */
395
    unsigned d_consNum;
396
    /** the child enumerators for this enumerator */
397
    std::map<unsigned, TermEnumSlave> d_children;
398
    /** the current sum of current sizes of the enumerators in d_children */
399
    unsigned d_currChildSize;
400
    /**
401
     * The number of indices, starting from zero, in d_children that point to
402
     * initialized slave enumerators.
403
     */
404
    unsigned d_childrenValid;
405
    /** initialize children
406
     *
407
     * Initialize all the remaining uninitialized children of this enumerator.
408
     * If this method returns true, then each of d_children are
409
     * initialized to be slave enumerators of the argument types indicated by
410
     * d_ccTypes, and the sum of their current sizes (d_currChildSize) is
411
     * exactly (d_currSize - d_ccWeight). If this method returns false, then
412
     * it was not possible to initialize the children such that they meet the
413
     * size requirements, and such that the assignments from children to size
414
     * has not already been considered. In this case, all updates to d_children
415
     * are reverted and d_currChildSize and d_childrenValid are reset to their
416
     * values prior to this call.
417
     */
418
    bool initializeChildren();
419
    /** initialize child
420
     *
421
     * Initialize child i to enumerate terms whose size is at least sizeMin,
422
     * and whose maximum size is the largest size such that we can still
423
     * construct terms for the given constructor class and the current children
424
     * whose size is not more than the current size d_currSize. Additionally,
425
     * if i is the last child, we modify sizeMin such that it is exactly the
426
     * size of terms needed for the children to sum up to size d_currSize.
427
     */
428
    bool initializeChild(unsigned i, unsigned sizeMin);
429
    /** increment internal, helper for increment() */
430
    bool incrementInternal();
431
    /**
432
     * The vector children is a set of terms given to
433
     *    NodeManager::mkNode(APPLY_CONSTRUCTOR, children)
434
     * This converts children so that all sygus free variables are unique. Note
435
     * that the first child is a constructor operator and should be skipped.
436
     */
437
    void childrenToShape(std::vector<Node>& children);
438
    /**
439
     * Convert n into shape based on the variable counters. For example if
440
     * vcounter is { Int -> 7 }, then (+ x1 x2) is converted to (+ x7 x8) and
441
     * vouncter is updated to { Int -> 9 }.
442
     */
443
    Node convertShape(Node n, std::map<TypeNode, int>& vcounter);
444
  };
445
  /** an interpreted value enumerator
446
   *
447
   * This enumerator uses the builtin type enumerator for a given type. It
448
   * is used to fill in concrete holes into "any constant" constructors
449
   * when sygus-repair-const is not enabled. The number of terms of size n
450
   * is m^n, where m is configurable via --sygus-active-gen-enum-cfactor=m.
451
   */
452
22
  class TermEnumMasterInterp : public TermEnum
453
  {
454
   public:
455
    TermEnumMasterInterp(TypeNode tn);
456
    /** initialize this enumerator */
457
    bool initialize(SygusEnumerator* se, TypeNode tn);
458
    /** get the current term of the enumerator */
459
    Node getCurrent() override;
460
    /** increment the enumerator */
461
    bool increment() override;
462
463
   private:
464
    /** the type enumerator */
465
    TypeEnumerator d_te;
466
    /** the current number of terms we are enumerating for the given size */
467
    unsigned d_currNumConsts;
468
    /** the next end threshold */
469
    unsigned d_nextIndexEnd;
470
  };
471
  /** a free variable enumerator
472
   *
473
   * This enumerator enumerates canonical free variables for a given type.
474
   * The n^th variable in this stream is assigned size n. This enumerator is
475
   * used in conjunction with sygus-repair-const to generate solutions with
476
   * constant holes. In this approach, free variables are arguments to
477
   * any-constant constructors. This is required so that terms with holes are
478
   * unique up to rewriting when appropriate.
479
   */
480
5
  class TermEnumMasterFv : public TermEnum
481
  {
482
   public:
483
    TermEnumMasterFv();
484
    /** initialize this enumerator */
485
    bool initialize(SygusEnumerator* se, TypeNode tn);
486
    /** get the current term of the enumerator */
487
    Node getCurrent() override;
488
    /** increment the enumerator */
489
    bool increment() override;
490
  };
491
  /** the master enumerator for each sygus type */
492
  std::map<TypeNode, TermEnumMaster> d_masterEnum;
493
  /** the master enumerator for each non-sygus type */
494
  std::map<TypeNode, TermEnumMasterFv> d_masterEnumFv;
495
  /** the master enumerator for each non-sygus type */
496
  std::map<TypeNode, std::unique_ptr<TermEnumMasterInterp>> d_masterEnumInt;
497
  /** the sygus enumerator this class is for */
498
  Node d_enum;
499
  /** the type of d_enum */
500
  TypeNode d_etype;
501
  /** pointer to the master enumerator of type d_etype */
502
  TermEnum* d_tlEnum;
503
  /** the abort size, caches the value of --sygus-abort-size */
504
  int d_abortSize;
505
  /** get master enumerator for type tn */
506
  TermEnum* getMasterEnumForType(TypeNode tn);
507
  //-------------------------------- externally specified symmetry breaking
508
  /** set of constructors we disallow at top level
509
   *
510
   * A constructor C is disallowed at the top level if a symmetry breaking
511
   * lemma that entails ~is-C( d_enum ) was registered to
512
   * TermDbSygus::registerSymBreakLemma.
513
   */
514
  std::unordered_set<Node> d_sbExcTlCons;
515
  //-------------------------------- end externally specified symmetry breaking
516
};
517
518
}  // namespace quantifiers
519
}  // namespace theory
520
}  // namespace cvc5
521
522
#endif /* CVC5__THEORY__QUANTIFIERS__SYGUS_ENUMERATOR_H */