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

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Mathias Preiner, Abdalrhman Mohamed
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
 * Class that encapsulates techniques for a single (SyGuS) synthesis
14
 * conjecture.
15
 */
16
17
#include "cvc5_private.h"
18
19
#ifndef CVC5__THEORY__QUANTIFIERS__SYNTH_CONJECTURE_H
20
#define CVC5__THEORY__QUANTIFIERS__SYNTH_CONJECTURE_H
21
22
#include <memory>
23
24
#include "theory/quantifiers/expr_miner_manager.h"
25
#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
26
#include "theory/quantifiers/sygus/cegis.h"
27
#include "theory/quantifiers/sygus/cegis_core_connective.h"
28
#include "theory/quantifiers/sygus/cegis_unif.h"
29
#include "theory/quantifiers/sygus/example_eval_cache.h"
30
#include "theory/quantifiers/sygus/example_infer.h"
31
#include "theory/quantifiers/sygus/sygus_process_conj.h"
32
#include "theory/quantifiers/sygus/sygus_repair_const.h"
33
#include "theory/quantifiers/sygus/sygus_stats.h"
34
#include "theory/quantifiers/sygus/template_infer.h"
35
36
namespace cvc5 {
37
namespace theory {
38
namespace quantifiers {
39
40
class CegGrammarConstructor;
41
class SygusPbe;
42
class SygusStatistics;
43
44
/**
45
 * A base class for generating values for actively-generated enumerators.
46
 * At a high level, the job of this class is to accept a stream of "abstract
47
 * values" a1, ..., an, ..., and generate a (possibly larger) stream of
48
 * "concrete values" c11, ..., c1{m_1}, ..., cn1, ... cn{m_n}, ....
49
 */
50
167
class EnumValGenerator
51
{
52
 public:
53
167
  virtual ~EnumValGenerator() {}
54
  /** initialize this class with enumerator e */
55
  virtual void initialize(Node e) = 0;
56
  /** Inform this generator that abstract value v was enumerated. */
57
  virtual void addValue(Node v) = 0;
58
  /**
59
   * Increment this value generator. If this returns false, then we are out of
60
   * values. If this returns true, getCurrent(), if non-null, returns the
61
   * current term.
62
   *
63
   * Notice that increment() may return true and afterwards it may be the case
64
   * getCurrent() is null. We do this so that increment() does not take too
65
   * much time per call, which can be the case for grammars where it is
66
   * difficult to find the next (non-redundant) term. Returning true with
67
   * a null current term gives the caller the chance to interleave other
68
   * reasoning.
69
   */
70
  virtual bool increment() = 0;
71
  /** Get the current concrete value generated by this class. */
72
  virtual Node getCurrent() = 0;
73
};
74
75
/** a synthesis conjecture
76
 * This class implements approaches for a synthesis conjecture, given by data
77
 * member d_quant.
78
 * This includes both approaches for synthesis in Reynolds et al CAV 2015. It
79
 * determines which approach and optimizations are applicable to the
80
 * conjecture, and has interfaces for implementing them.
81
 */
82
class SynthConjecture
83
{
84
 public:
85
  SynthConjecture(QuantifiersState& qs,
86
                  QuantifiersInferenceManager& qim,
87
                  QuantifiersRegistry& qr,
88
                  TermRegistry& tr,
89
                  SygusStatistics& s);
90
  ~SynthConjecture();
91
  /** presolve */
92
  void presolve();
93
  /** get original version of conjecture */
94
4629
  Node getConjecture() const { return d_quant; }
95
  /** get deep embedding version of conjecture */
96
  Node getEmbeddedConjecture() const { return d_embed_quant; }
97
  //-------------------------------for counterexample-guided check/refine
98
  /** whether the conjecture is waiting for a call to doCheck below */
99
  bool needsCheck();
100
  /** whether the conjecture is waiting for a call to doRefine below */
101
  bool needsRefinement() const;
102
  /** do syntax-guided enumerative check
103
   *
104
   * This is step 2(a) of Figure 3 of Reynolds et al CAV 2015.
105
   *
106
   * The method returns true if this conjecture is finished trying solutions
107
   * for the given call to SynthEngine::check.
108
   *
109
   * Notice that we make multiple calls to doCheck on one call to
110
   * SynthEngine::check. For example, if we are using an actively-generated
111
   * enumerator, one enumerated (abstract) term may correspond to multiple
112
   * concrete terms t1, ..., tn to check, where we make up to n calls to doCheck
113
   * when each of t1, ..., tn fails to satisfy the current refinement lemmas.
114
   */
115
  bool doCheck(std::vector<Node>& lems);
116
  /** do refinement
117
   *
118
   * This is step 2(b) of Figure 3 of Reynolds et al CAV 2015.
119
   *
120
   * This method is run when needsRefinement() returns true, indicating that
121
   * the last call to doCheck found a counterexample to the last candidate.
122
   *
123
   * This method adds a refinement lemma on the output channel of quantifiers
124
   * engine. If the refinement lemma is a duplicate, then we manually
125
   * exclude the current candidate via excludeCurrentSolution. This should
126
   * only occur when the synthesis conjecture for the current candidate fails
127
   * to evaluate to false for a given counterexample point, but regardless its
128
   * negation is satisfiable for the current candidate and that point. This is
129
   * exclusive to theories with partial functions, e.g. (non-linear) division.
130
   *
131
   * This method returns true if a lemma was added on the output channel, and
132
   * false otherwise.
133
   */
134
  bool doRefine();
135
  //-------------------------------end for counterexample-guided check/refine
136
  /**
137
   * Prints the current synthesis solution to output stream out. This is
138
   * currently used for printing solutions for sygusStream only. We do not
139
   * enclose solutions in parentheses.
140
   */
141
  void printSynthSolutionInternal(std::ostream& out);
142
  /** get synth solutions
143
   *
144
   * This method returns true if this class has a solution available to the
145
   * conjecture that it was assigned.
146
   *
147
   * Let q be the synthesis conjecture assigned to this class.
148
   * This method adds entries to sol_map[q] that map functions-to-synthesize to
149
   * their builtin solution, which has the same type. For example, for synthesis
150
   * conjecture exists f. forall x. f( x )>x, this function will update
151
   * sol_map[q] to contain the entry:
152
   *   f -> (lambda x. x+1)
153
   */
154
  bool getSynthSolutions(std::map<Node, std::map<Node, Node> >& sol_map);
155
  /**
156
   * The feasible guard whose semantics are "this conjecture is feasiable".
157
   * This is "G" in Figure 3 of Reynolds et al CAV 2015.
158
   */
159
  Node getGuard() const;
160
  /** is ground */
161
  bool isGround() { return d_inner_vars.empty(); }
162
  /** are we using single invocation techniques */
163
  bool isSingleInvocation() const;
164
  /** preregister conjecture
165
   * This is used as a heuristic for solution reconstruction, so that we
166
   * remember expressions in the conjecture before preprocessing, since they
167
   * may be helpful during solution reconstruction (Figure 5 of Reynolds et al
168
   * CAV 2015)
169
   */
170
  void preregisterConjecture(Node q);
171
  /** assign conjecture q to this class */
172
  void assign(Node q);
173
  /** has a conjecture been assigned to this class */
174
578
  bool isAssigned() { return !d_embed_quant.isNull(); }
175
  /**
176
   * Get model value for term n.
177
   */
178
  Node getModelValue(Node n);
179
180
  /** get utility for static preprocessing and analysis of conjectures */
181
297
  SynthConjectureProcess* getProcess() { return d_ceg_proc.get(); }
182
  /** get constant repair utility */
183
125
  SygusRepairConst* getRepairConst() { return d_sygus_rconst.get(); }
184
  /** get example inference utility */
185
12836
  ExampleInfer* getExampleInfer() { return d_exampleInfer.get(); }
186
  /** get the example evaluation cache utility for enumerator e */
187
  ExampleEvalCache* getExampleEvalCache(Node e);
188
  /** get program by examples module */
189
  SygusPbe* getPbe() { return d_ceg_pbe.get(); }
190
  /** get the symmetry breaking predicate for type */
191
  Node getSymmetryBreakingPredicate(
192
      Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
193
  /** print out debug information about this conjecture */
194
  void debugPrint(const char* c);
195
  /** check side condition
196
   *
197
   * This returns false if the solution { d_candidates -> cvals } does not
198
   * satisfy the side condition of the conjecture maintained by this class,
199
   * if it exists, and true otherwise.
200
   */
201
  bool checkSideCondition(const std::vector<Node>& cvals) const;
202
203
  /** get a reference to the statistics of parent */
204
  SygusStatistics& getSygusStatistics() { return d_stats; };
205
206
 private:
207
  /** Reference to the quantifiers state */
208
  QuantifiersState& d_qstate;
209
  /** Reference to the quantifiers inference manager */
210
  QuantifiersInferenceManager& d_qim;
211
  /** The quantifiers registry */
212
  QuantifiersRegistry& d_qreg;
213
  /** Reference to the term registry */
214
  TermRegistry& d_treg;
215
  /** reference to the statistics of parent */
216
  SygusStatistics& d_stats;
217
  /** term database sygus of d_qe */
218
  TermDbSygus* d_tds;
219
  /** The feasible guard. */
220
  Node d_feasible_guard;
221
  /**
222
   * Do we have a solution in this solve context? This flag is reset to false
223
   * on every call to presolve.
224
   */
225
  bool d_hasSolution;
226
  /** the decision strategy for the feasible guard */
227
  std::unique_ptr<DecisionStrategy> d_feasible_strategy;
228
  /** single invocation utility */
229
  std::unique_ptr<CegSingleInv> d_ceg_si;
230
  /** template inference utility */
231
  std::unique_ptr<SygusTemplateInfer> d_templInfer;
232
  /** utility for static preprocessing and analysis of conjectures */
233
  std::unique_ptr<SynthConjectureProcess> d_ceg_proc;
234
  /** grammar utility */
235
  std::unique_ptr<CegGrammarConstructor> d_ceg_gc;
236
  /** repair constant utility */
237
  std::unique_ptr<SygusRepairConst> d_sygus_rconst;
238
  /** example inference utility */
239
  std::unique_ptr<ExampleInfer> d_exampleInfer;
240
  /** example evaluation cache utility for each enumerator */
241
  std::map<Node, std::unique_ptr<ExampleEvalCache> > d_exampleEvalCache;
242
243
  //------------------------modules
244
  /** program by examples module */
245
  std::unique_ptr<SygusPbe> d_ceg_pbe;
246
  /** CEGIS module */
247
  std::unique_ptr<Cegis> d_ceg_cegis;
248
  /** CEGIS UNIF module */
249
  std::unique_ptr<CegisUnif> d_ceg_cegisUnif;
250
  /** connective core utility */
251
  std::unique_ptr<CegisCoreConnective> d_sygus_ccore;
252
  /** the set of active modules (subset of the above list) */
253
  std::vector<SygusModule*> d_modules;
254
  /** master module
255
   *
256
   * This is the module (one of those above) that takes sole responsibility
257
   * for this conjecture, determined during assign(...).
258
   */
259
  SygusModule* d_master;
260
  //------------------------end modules
261
262
  //------------------------enumerators
263
  /**
264
   * Get model values for terms n, store in vector v. This method returns true
265
   * if and only if all values added to v are non-null.
266
   *
267
   * The argument activeIncomplete indicates whether n contains an active
268
   * enumerator that is currently not finished enumerating values, but returned
269
   * null on a call to getEnumeratedValue. This value is used for determining
270
   * whether we should call getEnumeratedValues again within a call to
271
   * SynthConjecture::check.
272
   *
273
   * It removes terms from n that correspond to "inactive" enumerators, that
274
   * is, enumerators whose values have been exhausted.
275
   */
276
  bool getEnumeratedValues(std::vector<Node>& n,
277
                           std::vector<Node>& v,
278
                           bool& activeIncomplete);
279
  /**
280
   * Get model value for term n. If n has a value that was excluded by
281
   * datatypes sygus symmetry breaking, this method returns null. It sets
282
   * activeIncomplete to true if there is an actively-generated enumerator whose
283
   * current value is null but it has not finished generating values.
284
   */
285
  Node getEnumeratedValue(Node n, bool& activeIncomplete);
286
  /** enumerator generators for each actively-generated enumerator */
287
  std::map<Node, std::unique_ptr<EnumValGenerator> > d_evg;
288
  /**
289
   * Map from enumerators to whether they are currently being
290
   * "actively-generated". That is, we are in a state where we have called
291
   * d_evg[e].addValue(v) for some v, and d_evg[e].getNext() has not yet
292
   * returned null. The range of this map stores the abstract value that
293
   * we are currently generating values from.
294
   */
295
  std::map<Node, Node> d_ev_curr_active_gen;
296
  /** the current waiting value of each actively-generated enumerator, if any
297
   *
298
   * This caches values that are actively generated and that we have not yet
299
   * passed to a call to SygusModule::constructCandidates. An example of when
300
   * this may occur is when there are two actively-generated enumerators e1 and
301
   * e2. Say on some iteration we actively-generate v1 for e1, the value
302
   * of e2 was excluded by symmetry breaking, and say the current master sygus
303
   * module does not handle partial models. Hence, we abort the current check.
304
   * We remember that the value of e1 was v1 by storing it here, so that on
305
   * a future check when v2 has a proper value, it is returned.
306
   */
307
  std::map<Node, Node> d_ev_active_gen_waiting;
308
  /** the first value enumerated for each actively-generated enumerator
309
   *
310
   * This is to implement an optimization that only guards the blocking lemma
311
   * for the first value of an actively-generated enumerator.
312
   */
313
  std::map<Node, Node> d_ev_active_gen_first_val;
314
  //------------------------end enumerators
315
316
  /** list of constants for quantified formula
317
   * The outer Skolems for the negation of d_embed_quant.
318
   */
319
  std::vector<Node> d_candidates;
320
  /** base instantiation
321
   * If d_embed_quant is forall d. exists y. P( d, y ), then
322
   * this is the formula  exists y. P( d_candidates, y ). Notice that
323
   * (exists y. F) is shorthand above for ~( forall y. ~F ).
324
   */
325
  Node d_base_inst;
326
  /** list of variables on inner quantification */
327
  std::vector<Node> d_inner_vars;
328
  /**
329
   * The set of skolems for the current "verification" lemma, if one exists.
330
   * This may be added to during calls to doCheck(). The model values for these
331
   * skolems are analyzed during doRefine().
332
   */
333
  std::vector<Node> d_ce_sk_vars;
334
  /**
335
   * If we have already tested the satisfiability of the current verification
336
   * lemma, this stores the model values of d_ce_sk_vars in the current
337
   * (satisfiable, failed) verification lemma.
338
   */
339
  std::vector<Node> d_ce_sk_var_mvs;
340
  /**
341
   * Whether the above vector has been set. We have this flag since the above
342
   * vector may be set to empty (e.g. for ground synthesis conjectures).
343
   */
344
  bool d_set_ce_sk_vars;
345
346
  /** the asserted (negated) conjecture */
347
  Node d_quant;
348
  /**
349
   * The side condition for solving the conjecture, after conversion to deep
350
   * embedding.
351
   */
352
  Node d_embedSideCondition;
353
  /** (negated) conjecture after simplification */
354
  Node d_simp_quant;
355
  /** (negated) conjecture after simplification, conversion to deep embedding */
356
  Node d_embed_quant;
357
  /** candidate information */
358
1845
  class CandidateInfo
359
  {
360
   public:
361
1845
    CandidateInfo() {}
362
    /** list of terms we have instantiated candidates with */
363
    std::vector<Node> d_inst;
364
  };
365
  std::map<Node, CandidateInfo> d_cinfo;
366
  /**
367
   * The first index of an instantiation in CandidateInfo::d_inst that we have
368
   * not yet tried to repair.
369
   */
370
  unsigned d_repair_index;
371
  /** number of times we have called doRefine */
372
  unsigned d_refine_count;
373
  /** record solution (this is used to construct solutions later) */
374
  void recordSolution(std::vector<Node>& vs);
375
  /** get synth solutions internal
376
   *
377
   * This function constructs the body of solutions for all
378
   * functions-to-synthesize in this conjecture and stores them in sols, in
379
   * order. For each solution added to sols, we add an integer indicating what
380
   * kind of solution n is, where if sols[i] = n, then
381
   *   if status[i] = 0: n is the (builtin term) corresponding to the solution,
382
   *   if status[i] = 1: n is the sygus representation of the solution.
383
   * We store builtin versions under some conditions (such as when the sygus
384
   * grammar is being ignored).
385
   *
386
   * This consults the single invocation module to get synthesis solutions if
387
   * isSingleInvocation() returns true.
388
   *
389
   * For example, for conjecture exists fg. forall x. f(x)>g(x), this function
390
   * may set ( sols, status ) to ( { x+1, d_x() }, { 1, 0 } ), where d_x() is
391
   * the sygus datatype constructor corresponding to variable x.
392
   */
393
  bool getSynthSolutionsInternal(std::vector<Node>& sols,
394
                                 std::vector<int8_t>& status);
395
  //-------------------------------- sygus stream
396
  /**
397
   * Prints the current synthesis solution to the output stream indicated by
398
   * the Options object, send a lemma blocking the current solution to the
399
   * output channel, which we refer to as a "stream exclusion lemma".
400
   *
401
   * The argument enums is the set of enumerators that comprise the current
402
   * solution, and values is their current values.
403
   */
404
  void printAndContinueStream(const std::vector<Node>& enums,
405
                              const std::vector<Node>& values);
406
  /** exclude the current solution { enums -> values } */
407
  void excludeCurrentSolution(const std::vector<Node>& enums,
408
                              const std::vector<Node>& values);
409
  /**
410
   * Whether we have guarded a stream exclusion lemma when using sygusStream.
411
   * This is an optimization that allows us to guard only the first stream
412
   * exclusion lemma.
413
   */
414
  bool d_guarded_stream_exc;
415
  //-------------------------------- end sygus stream
416
  /** expression miner managers for each function-to-synthesize
417
   *
418
   * Notice that for each function-to-synthesize, we enumerate a stream of
419
   * candidate solutions, where each of these streams is independent. Thus,
420
   * we maintain separate expression miner managers for each of them.
421
   *
422
   * This is used for the sygusRewSynth() option to synthesize new candidate
423
   * rewrite rules.
424
   */
425
  std::map<Node, ExpressionMinerManager> d_exprm;
426
};
427
428
}  // namespace quantifiers
429
}  // namespace theory
430
}  // namespace cvc5
431
432
#endif