1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, 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_unif_strat |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY__QUANTIFIERS__SYGUS_UNIF_STRAT_H |
19 |
|
#define CVC5__THEORY__QUANTIFIERS__SYGUS_UNIF_STRAT_H |
20 |
|
|
21 |
|
#include <map> |
22 |
|
|
23 |
|
#include "expr/node.h" |
24 |
|
#include "smt/env_obj.h" |
25 |
|
|
26 |
|
namespace cvc5 { |
27 |
|
namespace theory { |
28 |
|
namespace quantifiers { |
29 |
|
|
30 |
|
class TermDbSygus; |
31 |
|
|
32 |
|
/** roles for enumerators |
33 |
|
* |
34 |
|
* This indicates the role of an enumerator that is allocated by approaches |
35 |
|
* for synthesis-by-unification (see details below). |
36 |
|
* io : the enumerator should enumerate values that are overall solutions |
37 |
|
* for the function-to-synthesize, |
38 |
|
* ite_condition : the enumerator should enumerate values that are useful |
39 |
|
* in ite conditions in the ITE strategy, |
40 |
|
* concat_term : the enumerator should enumerate values that are used as |
41 |
|
* components of string concatenation solutions. |
42 |
|
*/ |
43 |
|
enum EnumRole |
44 |
|
{ |
45 |
|
enum_invalid, |
46 |
|
enum_io, |
47 |
|
enum_ite_condition, |
48 |
|
enum_concat_term, |
49 |
|
}; |
50 |
|
std::ostream& operator<<(std::ostream& os, EnumRole r); |
51 |
|
|
52 |
|
/** roles for strategy nodes |
53 |
|
* |
54 |
|
* This indicates the role of a strategy node, which is a subprocedure of |
55 |
|
* SygusUnif::constructSolution (see details below). |
56 |
|
* equal : the node constructed must be equal to the overall solution for |
57 |
|
* the function-to-synthesize, |
58 |
|
* string_prefix/suffix : the node constructed must be a prefix/suffix |
59 |
|
* of the function-to-synthesize, |
60 |
|
* ite_condition : the node constructed must be a condition that makes some |
61 |
|
* active input examples true and some input examples false. |
62 |
|
*/ |
63 |
|
enum NodeRole |
64 |
|
{ |
65 |
|
role_invalid, |
66 |
|
role_equal, |
67 |
|
role_string_prefix, |
68 |
|
role_string_suffix, |
69 |
|
role_ite_condition, |
70 |
|
}; |
71 |
|
std::ostream& operator<<(std::ostream& os, NodeRole r); |
72 |
|
|
73 |
|
/** enumerator role for node role */ |
74 |
|
EnumRole getEnumeratorRoleForNodeRole(NodeRole r); |
75 |
|
|
76 |
|
/** strategy types |
77 |
|
* |
78 |
|
* This indicates a strategy for synthesis-by-unification (see details below). |
79 |
|
* ITE : strategy for constructing if-then-else solutions via decision |
80 |
|
* tree learning techniques, |
81 |
|
* CONCAT_PREFIX/SUFFIX : strategy for constructing string concatenation |
82 |
|
* solutions via a divide and conquer approach, |
83 |
|
* ID : identity strategy used for calling strategies on child type through |
84 |
|
* an identity function. |
85 |
|
*/ |
86 |
|
enum StrategyType |
87 |
|
{ |
88 |
|
strat_INVALID, |
89 |
|
strat_ITE, |
90 |
|
strat_CONCAT_PREFIX, |
91 |
|
strat_CONCAT_SUFFIX, |
92 |
|
strat_ID, |
93 |
|
}; |
94 |
|
std::ostream& operator<<(std::ostream& os, StrategyType st); |
95 |
|
|
96 |
|
/** virtual base class for context in synthesis-by-unification approaches */ |
97 |
84 |
class UnifContext |
98 |
|
{ |
99 |
|
public: |
100 |
84 |
virtual ~UnifContext() {} |
101 |
|
|
102 |
|
/** Get the current role |
103 |
|
* |
104 |
|
* In a particular context when constructing solutions in synthesis by |
105 |
|
* unification, we may be solving based on a modified role. For example, |
106 |
|
* if we are currently synthesizing x in a solution ("a" ++ x), we are |
107 |
|
* synthesizing the string suffix of the overall solution. In this case, this |
108 |
|
* function returns role_string_suffix. |
109 |
|
*/ |
110 |
|
virtual NodeRole getCurrentRole() = 0; |
111 |
|
/** is return value modified? |
112 |
|
* |
113 |
|
* This returns true if we are currently in a state where the return value |
114 |
|
* of the solution has been modified, e.g. by a previous node that solved |
115 |
|
* for a string prefix. |
116 |
|
*/ |
117 |
6657 |
bool isReturnValueModified() { return getCurrentRole() != role_equal; } |
118 |
|
}; |
119 |
|
|
120 |
|
/** |
121 |
|
* This class stores information regarding an enumerator, including |
122 |
|
* information regarding the role of this enumerator (see EnumRole), its |
123 |
|
* parent, whether it is templated, its slave enumerators, and so on. |
124 |
|
* |
125 |
|
* We say an enumerator is a master enumerator if it is the variable that |
126 |
|
* we use to enumerate values for its sort. Master enumerators may have |
127 |
|
* (possibly multiple) slave enumerators, stored in d_enum_slave. When |
128 |
|
* constructing a sygus unifciation strategy, we make the first enumerator for |
129 |
|
* each type a master enumerator, and any additional ones slaves of it. |
130 |
|
*/ |
131 |
257 |
class EnumInfo |
132 |
|
{ |
133 |
|
public: |
134 |
257 |
EnumInfo() : d_role(enum_io), d_is_conditional(false) {} |
135 |
|
/** initialize this class |
136 |
|
* |
137 |
|
* role is the "role" the enumerator plays in the high-level strategy, |
138 |
|
* which is one of enum_* above. |
139 |
|
*/ |
140 |
|
void initialize(EnumRole role); |
141 |
|
/** is this enumerator associated with a template? */ |
142 |
4614 |
bool isTemplated() { return !d_template.isNull(); } |
143 |
|
/** set conditional |
144 |
|
* |
145 |
|
* This flag is set to true if this enumerator may not apply to all |
146 |
|
* input/output examples. For example, if this enumerator is used |
147 |
|
* as an output value beneath a conditional in an instance of strat_ITE, |
148 |
|
* then this enumerator is conditional. |
149 |
|
*/ |
150 |
121 |
void setConditional() { d_is_conditional = true; } |
151 |
|
/** returns if this enumerator is conditional */ |
152 |
401 |
bool isConditional() { return d_is_conditional; } |
153 |
|
/** get the role of this enumerator */ |
154 |
28818 |
EnumRole getRole() { return d_role; } |
155 |
|
/** enumerator template |
156 |
|
* |
157 |
|
* If d_template non-null, enumerated values V are immediately transformed to |
158 |
|
* d_template { d_template_arg -> V }. |
159 |
|
*/ |
160 |
|
Node d_template; |
161 |
|
Node d_template_arg; |
162 |
|
/** |
163 |
|
* Slave enumerators of this enumerator. These are other enumerators that |
164 |
|
* have the same type, but a different role in the strategy tree. We |
165 |
|
* generally |
166 |
|
* only use one enumerator per type, and hence these slaves are notified when |
167 |
|
* values are enumerated for this enumerator. |
168 |
|
*/ |
169 |
|
std::vector<Node> d_enum_slave; |
170 |
|
|
171 |
|
private: |
172 |
|
/** the role of this enumerator (one of enum_* above). */ |
173 |
|
EnumRole d_role; |
174 |
|
/** is this enumerator conditional */ |
175 |
|
bool d_is_conditional; |
176 |
|
}; |
177 |
|
|
178 |
|
class EnumTypeInfoStrat; |
179 |
|
|
180 |
|
/** represents a node in the strategy graph |
181 |
|
* |
182 |
|
* It contains a list of possible strategies which are tried during calls |
183 |
|
* to constructSolution. |
184 |
|
*/ |
185 |
|
class StrategyNode |
186 |
|
{ |
187 |
|
public: |
188 |
244 |
StrategyNode() {} |
189 |
|
~StrategyNode(); |
190 |
|
/** the set of strategies to try at this node in the strategy graph */ |
191 |
|
std::vector<EnumTypeInfoStrat*> d_strats; |
192 |
|
}; |
193 |
|
|
194 |
|
/** |
195 |
|
* Stores all enumerators and strategies for a SyGuS datatype type. |
196 |
|
*/ |
197 |
153 |
class EnumTypeInfo |
198 |
|
{ |
199 |
|
public: |
200 |
153 |
EnumTypeInfo() {} |
201 |
|
/** the type that this information is for */ |
202 |
|
TypeNode d_this_type; |
203 |
|
/** map from enum roles to enumerators for this type */ |
204 |
|
std::map<EnumRole, Node> d_enum; |
205 |
|
/** map from node roles to strategy nodes */ |
206 |
|
std::map<NodeRole, StrategyNode> d_snodes; |
207 |
|
/** get strategy node for node role */ |
208 |
|
StrategyNode& getStrategyNode(NodeRole nrole); |
209 |
|
}; |
210 |
|
|
211 |
|
/** represents a strategy for a SyGuS datatype type |
212 |
|
* |
213 |
|
* This represents a possible strategy to apply when processing a strategy |
214 |
|
* node in constructSolution. When applying the strategy represented by this |
215 |
|
* class, we may make recursive calls to the children of the strategy, |
216 |
|
* given in d_cenum. If all recursive calls to constructSolution for these |
217 |
|
* children are successful, say: |
218 |
|
* constructSolution( d_cenum[1], ... ) = t1, |
219 |
|
* ..., |
220 |
|
* constructSolution( d_cenum[n], ... ) = tn, |
221 |
|
* Then, the solution returned by this strategy is |
222 |
|
* d_sol_templ * { d_sol_templ_args -> (t1,...,tn) } |
223 |
|
* where * is application of substitution. |
224 |
|
*/ |
225 |
338 |
class EnumTypeInfoStrat |
226 |
|
{ |
227 |
|
public: |
228 |
|
/** the type of strategy this represents */ |
229 |
|
StrategyType d_this; |
230 |
|
/** the sygus datatype constructor that induced this strategy |
231 |
|
* |
232 |
|
* For example, this may be a sygus datatype whose sygus operator is ITE, |
233 |
|
* if the strategy type above is strat_ITE. |
234 |
|
*/ |
235 |
|
Node d_cons; |
236 |
|
/** children of this strategy */ |
237 |
|
std::vector<std::pair<Node, NodeRole> > d_cenum; |
238 |
|
/** the arguments for the (templated) solution */ |
239 |
|
std::vector<Node> d_sol_templ_args; |
240 |
|
/** the template for the solution */ |
241 |
|
Node d_sol_templ; |
242 |
|
/** Returns true if argument is valid strategy in unification context x */ |
243 |
|
bool isValid(UnifContext& x); |
244 |
|
}; |
245 |
|
|
246 |
|
/** |
247 |
|
* flags for extra restrictions to be inferred during redundant operators |
248 |
|
* learning |
249 |
|
*/ |
250 |
107 |
struct StrategyRestrictions |
251 |
|
{ |
252 |
107 |
StrategyRestrictions() : d_iteReturnBoolConst(false), d_iteCondOnlyAtoms(true) |
253 |
|
{ |
254 |
107 |
} |
255 |
|
/** |
256 |
|
* if this flag is true then staticLearnRedundantOps will also try to make |
257 |
|
* the return value of boolean ITEs to be restricted to constants |
258 |
|
*/ |
259 |
|
bool d_iteReturnBoolConst; |
260 |
|
/** |
261 |
|
* if this flag is true then staticLearnRedundantOps will also try to make |
262 |
|
* the condition values of ITEs to be restricted to atoms |
263 |
|
*/ |
264 |
|
bool d_iteCondOnlyAtoms; |
265 |
|
/** |
266 |
|
* A list of unused strategies. This maps strategy points to the indices |
267 |
|
* in StrategyNode::d_strats that are not used by the caller of |
268 |
|
* staticLearnRedundantOps, and hence should not be taken into account |
269 |
|
* when doing redundant operator learning. |
270 |
|
*/ |
271 |
|
std::map<Node, std::unordered_set<unsigned>> d_unused_strategies; |
272 |
|
}; |
273 |
|
|
274 |
|
/** |
275 |
|
* Stores strategy and enumeration information for a function-to-synthesize. |
276 |
|
* |
277 |
|
* When this class is initialized, we construct a "strategy tree" based on |
278 |
|
* the grammar of the function to synthesize f. This tree is represented by |
279 |
|
* the above classes. |
280 |
|
*/ |
281 |
321 |
class SygusUnifStrategy : protected EnvObj |
282 |
|
{ |
283 |
|
public: |
284 |
107 |
SygusUnifStrategy(Env& env) : EnvObj(env), d_tds(nullptr) {} |
285 |
|
/** initialize |
286 |
|
* |
287 |
|
* This initializes this class with function-to-synthesize f. We also call |
288 |
|
* f the candidate variable. |
289 |
|
* |
290 |
|
* This call constructs a set of enumerators for the relevant subfields of |
291 |
|
* the grammar of f and adds them to enums. |
292 |
|
*/ |
293 |
|
void initialize(TermDbSygus* tds, Node f, std::vector<Node>& enums); |
294 |
|
/** Get the root enumerator for this class */ |
295 |
|
Node getRootEnumerator() const; |
296 |
|
/** |
297 |
|
* Get the enumerator info for enumerator e, where e must be an enumerator |
298 |
|
* initialized by this class (in enums after a call to initialize). |
299 |
|
*/ |
300 |
|
EnumInfo& getEnumInfo(Node e); |
301 |
|
/** |
302 |
|
* Get the enumerator type info for sygus type t, where t must be the type |
303 |
|
* of some enumerator initialized by this class |
304 |
|
*/ |
305 |
|
EnumTypeInfo& getEnumTypeInfo(TypeNode tn); |
306 |
|
|
307 |
|
/** static learn redundant operators |
308 |
|
* |
309 |
|
* This learns static lemmas for pruning enumerative space based on the |
310 |
|
* strategy for the function-to-synthesize of this class, and stores these |
311 |
|
* into strategy_lemmas. |
312 |
|
* |
313 |
|
* These may correspond to static symmetry breaking predicates (for example, |
314 |
|
* those that exclude ITE from enumerators whose role is enum_io when the |
315 |
|
* strategy is ITE_strat). |
316 |
|
* |
317 |
|
* then the module may also try to apply the given pruning restrictions (see |
318 |
|
* StrategyRestrictions for more details) |
319 |
|
*/ |
320 |
|
void staticLearnRedundantOps( |
321 |
|
std::map<Node, std::vector<Node>>& strategy_lemmas, |
322 |
|
StrategyRestrictions& restrictions); |
323 |
|
/** |
324 |
|
* creates the default restrictions when they are not given and calls the |
325 |
|
* above function |
326 |
|
*/ |
327 |
|
void staticLearnRedundantOps( |
328 |
|
std::map<Node, std::vector<Node>>& strategy_lemmas); |
329 |
|
|
330 |
|
/** debug print this strategy on Trace c */ |
331 |
|
void debugPrint(const char* c); |
332 |
|
|
333 |
|
private: |
334 |
|
/** pointer to the term database sygus */ |
335 |
|
TermDbSygus* d_tds; |
336 |
|
/** The candidate variable this strategy is for */ |
337 |
|
Node d_candidate; |
338 |
|
/** maps enumerators to relevant information */ |
339 |
|
std::map<Node, EnumInfo> d_einfo; |
340 |
|
/** list of all enumerators for the function-to-synthesize */ |
341 |
|
std::vector<Node> d_esym_list; |
342 |
|
/** Info for sygus datatype type occurring in a field of d_root */ |
343 |
|
std::map<TypeNode, EnumTypeInfo> d_tinfo; |
344 |
|
/** |
345 |
|
* The root sygus datatype for the function-to-synthesize, |
346 |
|
* which encodes the overall syntactic restrictions on the space |
347 |
|
* of solutions. |
348 |
|
*/ |
349 |
|
TypeNode d_root; |
350 |
|
/** |
351 |
|
* Maps sygus datatypes to their master enumerator. This is the (single) |
352 |
|
* enumerator of that type that we enumerate values for. |
353 |
|
*/ |
354 |
|
std::map<TypeNode, Node> d_master_enum; |
355 |
|
/** Initialize necessary information for (sygus) type tn */ |
356 |
|
void initializeType(TypeNode tn); |
357 |
|
|
358 |
|
//-----------------------debug printing |
359 |
|
/** print ind indentations on trace c */ |
360 |
|
static void indent(const char* c, int ind); |
361 |
|
//-----------------------end debug printing |
362 |
|
|
363 |
|
//------------------------------ strategy registration |
364 |
|
/** build strategy graph |
365 |
|
* |
366 |
|
* This builds the strategy for enumerated values of type tn for the given |
367 |
|
* role of nrole, for solutions to function-to-synthesize of this class. |
368 |
|
*/ |
369 |
|
void buildStrategyGraph(TypeNode tn, NodeRole nrole); |
370 |
|
/** register enumerator |
371 |
|
* |
372 |
|
* This registers that et is an enumerator of type tn, having enumerator |
373 |
|
* role enum_role. |
374 |
|
* |
375 |
|
* inSearch is whether we will enumerate values based on this enumerator. |
376 |
|
* A strategy node is represented by a (enumerator, node role) pair. Hence, |
377 |
|
* we may use enumerators for which this flag is false to represent strategy |
378 |
|
* nodes that have child strategies. |
379 |
|
*/ |
380 |
|
void registerStrategyPoint(Node et, |
381 |
|
TypeNode tn, |
382 |
|
EnumRole enum_role, |
383 |
|
bool inSearch); |
384 |
|
/** infer template */ |
385 |
|
bool inferTemplate(unsigned k, |
386 |
|
Node n, |
387 |
|
std::map<Node, unsigned>& templ_var_index, |
388 |
|
std::map<unsigned, unsigned>& templ_injection); |
389 |
|
/** helper for static learn redundant operators |
390 |
|
* |
391 |
|
* (e, nrole) specify the strategy node in the graph we are currently |
392 |
|
* analyzing, visited stores the nodes we have already visited. |
393 |
|
* |
394 |
|
* This method builds the mapping needs_cons, which maps (master) enumerators |
395 |
|
* to a map from the constructors that it needs. |
396 |
|
*/ |
397 |
|
void staticLearnRedundantOps( |
398 |
|
Node e, |
399 |
|
NodeRole nrole, |
400 |
|
std::map<Node, std::map<NodeRole, bool>>& visited, |
401 |
|
std::map<Node, std::map<unsigned, bool>>& needs_cons, |
402 |
|
StrategyRestrictions& restrictions); |
403 |
|
/** finish initialization of the strategy tree |
404 |
|
* |
405 |
|
* (e, nrole) specify the strategy node in the graph we are currently |
406 |
|
* analyzing, visited stores the nodes we have already visited. |
407 |
|
* |
408 |
|
* isCond is whether the current enumerator is conditional (beneath a |
409 |
|
* conditional of an strat_ITE strategy). |
410 |
|
*/ |
411 |
|
void finishInit(Node e, |
412 |
|
NodeRole nrole, |
413 |
|
std::map<Node, std::map<NodeRole, bool> >& visited, |
414 |
|
bool isCond); |
415 |
|
/** helper for debug print |
416 |
|
* |
417 |
|
* Prints the node e with role nrole on Trace(c). |
418 |
|
* |
419 |
|
* (e, nrole) specify the strategy node in the graph we are currently |
420 |
|
* analyzing, visited stores the nodes we have already visited. |
421 |
|
* |
422 |
|
* ind is the current level of indentation (for debugging) |
423 |
|
*/ |
424 |
|
void debugPrint(const char* c, |
425 |
|
Node e, |
426 |
|
NodeRole nrole, |
427 |
|
std::map<Node, std::map<NodeRole, bool> >& visited, |
428 |
|
int ind); |
429 |
|
//------------------------------ end strategy registration |
430 |
|
}; |
431 |
|
|
432 |
|
} // namespace quantifiers |
433 |
|
} // namespace theory |
434 |
|
} // namespace cvc5 |
435 |
|
|
436 |
|
#endif /* CVC5__THEORY__QUANTIFIERS__SYGUS_UNIF_H */ |