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 |