1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Abdalrhman Mohamed, Andrew Reynolds |
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 |
|
* Utility for reconstructing terms to match a grammar. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY__QUANTIFIERS__SYGUS_RECONSTRUCT_H |
19 |
|
#define CVC5__THEORY__QUANTIFIERS__SYGUS_RECONSTRUCT_H |
20 |
|
|
21 |
|
#include <map> |
22 |
|
#include <vector> |
23 |
|
|
24 |
|
#include "theory/quantifiers/sygus/rcons_obligation.h" |
25 |
|
#include "theory/quantifiers/sygus/rcons_type_info.h" |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace theory { |
29 |
|
namespace quantifiers { |
30 |
|
|
31 |
|
using BuiltinSet = std::unordered_set<Node>; |
32 |
|
using TypeBuiltinSetMap = std::unordered_map<TypeNode, BuiltinSet>; |
33 |
|
|
34 |
|
/** SygusReconstruct |
35 |
|
* |
36 |
|
* This class implements an algorithm for reconstructing a given term such that |
37 |
|
* the reconstructed term is equivalent to the original term and its syntax |
38 |
|
* matches a specified grammar. |
39 |
|
* |
40 |
|
* Goal: find a term g in sygus type T0 that is equivalent to builtin term t0. |
41 |
|
* |
42 |
|
* rcons(t0, T0) returns g |
43 |
|
* { |
44 |
|
* Obs: A set of pairs to reconstruct into T, where each pair is of the form |
45 |
|
* (k, ts), where k is a skolem of a sygus type T and ts is a set of |
46 |
|
* builtin terms of the type encoded by T. |
47 |
|
* |
48 |
|
* Terms: A map from skolems k to a set of builtin terms in the pairs (k, ts). |
49 |
|
* That is, Terms[k] = ts. |
50 |
|
* |
51 |
|
* Sol: A map from skolems k to (possibly null) sygus terms of type T |
52 |
|
* representing solutions to the obligations. |
53 |
|
* |
54 |
|
* CandSols : A map from a skolem k to a set of possible solutions for its |
55 |
|
* corresponding obligation. Whenever there is a successful match, |
56 |
|
* it is added to this set. |
57 |
|
* |
58 |
|
* Pool : A map from a sygus type T to a set of enumerated terms in T. |
59 |
|
* The terms in this pool are patterns whose builtin analogs are used |
60 |
|
* for matching against the terms to reconstruct ts in (k, ts). |
61 |
|
* |
62 |
|
* let k0 be a fresh skolem of sygus type T0 |
63 |
|
* Obs[T0] += (k0, {t0}) |
64 |
|
* |
65 |
|
* TermsToRecons[T0] = {t0} |
66 |
|
* |
67 |
|
* while Sol[k0] == null |
68 |
|
* // map from T to terms pending addition to TermsToRecons |
69 |
|
* TermsToRecons' = {} |
70 |
|
* // enumeration phase |
71 |
|
* for each subfield type T of T0 |
72 |
|
* // enumerated terms may contain variables zs ranging over all terms of |
73 |
|
* // their type (subfield types of T0) |
74 |
|
* s[zs] = nextEnum(T) |
75 |
|
* if (s[zs] is ground) |
76 |
|
* builtin = rewrite(toBuiltIn(s[zs])) |
77 |
|
* // let X be the theory the solver is invoked with |
78 |
|
* find (k, ts) in Obs s.t. |=_X ts[0] = builtin |
79 |
|
* if no such pair exists |
80 |
|
* k = newVar(T) |
81 |
|
* ts = {builtin} |
82 |
|
* Obs += (k, ts) |
83 |
|
* markSolved((k, ts), s[zs]) |
84 |
|
* else if no s' in Pool[T] and matcher sigma s.t. |
85 |
|
* rewrite(toBuiltIn(s')) * sigma = rewrite(toBuiltIn(s[zs])) |
86 |
|
* Pool[T] += s[zs] |
87 |
|
* for each t in TermsToRecons[T] |
88 |
|
* TermsToRecons' += matchNewObs(t, s[zs]) |
89 |
|
* // match phase |
90 |
|
* while TermsToRecons' != {} |
91 |
|
* TermsToRecons'' = {} |
92 |
|
* for each subfield type T of T0 |
93 |
|
* for each t in TermsToRecons'[T] |
94 |
|
* TermsToRecons'[T] += t |
95 |
|
* for each s[zs] in Pool[T] |
96 |
|
* TermsToRecons'' += matchNewObs(t, s[zs]) |
97 |
|
* TermsToRecons' = TermsToRecons'' |
98 |
|
* g = Sol[k0] |
99 |
|
* instantiate free variables of g with arbitrary sygus datatype values |
100 |
|
* } |
101 |
|
* |
102 |
|
* matchNewObs(t, s[zs]) returns TermsToRecons' |
103 |
|
* { |
104 |
|
* u = rewrite(toBuiltIn(s[zs])) |
105 |
|
* if match(u, t) == {toBuiltin(zs) -> sts} |
106 |
|
* Sub = {} // substitution map from zs to corresponding new vars ks |
107 |
|
* for each (z, st) in {zs -> sts} |
108 |
|
* // let X be the theory the solver is invoked with |
109 |
|
* if exists (k, ts) in Obs s.t. !=_X ts[0] = st |
110 |
|
* ts += st |
111 |
|
* Sub[z] = k |
112 |
|
* else |
113 |
|
* sk = newVar(typeOf(z)) |
114 |
|
* Sub[z] = sk |
115 |
|
* TermsToRecons'[typeOf(z)] += st |
116 |
|
* if Sol[sk] != null forall (z, sk) in Sub |
117 |
|
* markSolved(k, s{Sub}) |
118 |
|
* else |
119 |
|
* CandSol[k] += s{Sub} |
120 |
|
* } |
121 |
|
* |
122 |
|
* markSolved((k, ts), s) |
123 |
|
* { |
124 |
|
* if Sol[k] != null |
125 |
|
* return |
126 |
|
* Sol[k] = s |
127 |
|
* CandSols[k] += s |
128 |
|
* Stack = {k} |
129 |
|
* while Stack != {} |
130 |
|
* k' = pop(Stack) |
131 |
|
* for all k'' in CandSols keys |
132 |
|
* for all s'[k'] in CandSols[k''] |
133 |
|
* s'{k' -> Sol[k']} |
134 |
|
* if s' does not contain free variables in Obs |
135 |
|
* Sol[k''] = s' |
136 |
|
* push(Stack, k'') |
137 |
|
* } |
138 |
|
*/ |
139 |
2302 |
class SygusReconstruct : public expr::NotifyMatch |
140 |
|
{ |
141 |
|
public: |
142 |
|
/** |
143 |
|
* Constructor. |
144 |
|
* |
145 |
|
* @param tds database for sygus terms |
146 |
|
* @param s statistics managed for the synth engine |
147 |
|
*/ |
148 |
|
SygusReconstruct(TermDbSygus* tds, SygusStatistics& s); |
149 |
|
|
150 |
|
/** Reconstruct solution. |
151 |
|
* |
152 |
|
* Return (if possible) a sygus encoding of a node that is equivalent to sol |
153 |
|
* and whose syntax matches the grammar corresponding to sygus datatype stn. |
154 |
|
* |
155 |
|
* For example, given: |
156 |
|
* sol = (* 2 x) |
157 |
|
* stn = A sygus datatype encoding the grammar |
158 |
|
* Start -> (c_PLUS Start Start) | c_x | c_0 | c_1 |
159 |
|
* This method may return (c_PLUS c_x c_x) and set reconstructed to 1. |
160 |
|
* |
161 |
|
* @param sol the target term |
162 |
|
* @param stn the sygus datatype type encoding the syntax restrictions |
163 |
|
* @param reconstructed the flag to update, set to 1 if we successfully return |
164 |
|
* a node, otherwise it is set to -1 |
165 |
|
* @param enumLimit a value to limit the effort spent by this class (roughly |
166 |
|
* equal to the number of intermediate terms to try) |
167 |
|
* @return the reconstructed sygus term |
168 |
|
*/ |
169 |
|
Node reconstructSolution(Node sol, |
170 |
|
TypeNode stn, |
171 |
|
int8_t& reconstructed, |
172 |
|
uint64_t enumLimit); |
173 |
|
|
174 |
|
private: |
175 |
|
/** Match builtin term `t` with pattern `sz`. |
176 |
|
* |
177 |
|
* This function matches the builtin term to reconstruct `t` with the builtin |
178 |
|
* analog of the pattern `sz`. If the match succeeds, `sz` is added to the set |
179 |
|
* of candidate solutions for the obligation `ob` corresponding to the builtin |
180 |
|
* term `t` and a set of new sub-terms to reconstruct is returned. If there |
181 |
|
* are no new sub-terms to reconstruct, then `sz` is considered a solution to |
182 |
|
* obligation `ob` and `markSolved(ob, sz)` is called. For example, given: |
183 |
|
* |
184 |
|
* Obs = [(k0, {(+ 1 1)})] |
185 |
|
* Pool[T0] = {(c_+ z1 z2)} |
186 |
|
* CandSols = {} |
187 |
|
* |
188 |
|
* Then calling `matchNewObs((+ 1 1), (c_+ z1 z2))` will result in: |
189 |
|
* |
190 |
|
* Obs = [(k0, {(+ 1 1)}), (k1, {1})] |
191 |
|
* Pool[T0] = {(c_+ z1 z2)} |
192 |
|
* CandSols = {k0 -> {(c_+ k1 k1)}} |
193 |
|
* |
194 |
|
* and will return `{T0 -> {1}}`. |
195 |
|
* |
196 |
|
* Notice that the patterns/shapes in pool are generic, and thus, contain vars |
197 |
|
* `z` as opposed to skolems `k`. Also, notice that `(c_+ z1 z2)` is |
198 |
|
* instantiated with only one skolem `k1`, which is then added to the set of |
199 |
|
* obligations. That's because the builtin analogs of `z1` and `z2` match with |
200 |
|
* the same builtin term `1`. |
201 |
|
* |
202 |
|
* @param t builtin term we need to reconstruct |
203 |
|
* @param sz a pattern to match against `t` |
204 |
|
* @return a set of new builtin terms to reconstruct if the match succeeds |
205 |
|
*/ |
206 |
|
TypeBuiltinSetMap matchNewObs(Node t, Node sz); |
207 |
|
|
208 |
|
/** mark obligation `ob` as solved. |
209 |
|
* |
210 |
|
* This function first marks `s` as the complete/constant solution for |
211 |
|
* `ob`. Then it substitutes all instances of `ob` in partial solutions to |
212 |
|
* other obligations with `s`. The function repeats those two steps for each |
213 |
|
* partial solution that gets completed because of the second step. For |
214 |
|
* example, given: |
215 |
|
* |
216 |
|
* CandSols = { |
217 |
|
* k0 -> {(c_+ k1 c_1)}, |
218 |
|
* k1 -> {(c_* k2 c_x)}, |
219 |
|
* k2 -> {c_2} |
220 |
|
* } |
221 |
|
* Sol = {} |
222 |
|
* |
223 |
|
* Then calling `markSolved(k2, c_2)` will result in: |
224 |
|
* |
225 |
|
* CandSols = { |
226 |
|
* k0 -> {(c_+ k1 c_1), (c_+ (c_* c_2 c_x) c_1)}, |
227 |
|
* k1 -> {(c_* k2 c_x), (c_* c_2 c_x)}, |
228 |
|
* k2 -> {c_2} |
229 |
|
* } |
230 |
|
* Sol = { |
231 |
|
* k0 -> (c_+ (c_* c_2 c_x) c_1), |
232 |
|
* k1 -> (c_* c_2 c_x), |
233 |
|
* k2 -> c_2 |
234 |
|
* } |
235 |
|
* |
236 |
|
* @param ob free var to mark as solved and substitute |
237 |
|
* @param sol solution to `ob` |
238 |
|
*/ |
239 |
|
void markSolved(RConsObligation* ob, Node s); |
240 |
|
|
241 |
|
/** |
242 |
|
* Initialize a sygus enumerator and a candidate rewrite database for each |
243 |
|
* sygus datatype type. |
244 |
|
* |
245 |
|
* @param stn The sygus datatype type encoding the syntax restrictions |
246 |
|
*/ |
247 |
|
void initialize(TypeNode stn); |
248 |
|
|
249 |
|
/** |
250 |
|
* Remove reconstructed terms from the given set of terms to reconstruct. |
251 |
|
* |
252 |
|
* @param termsToRecons a set of terms to reconstruct possibly containing |
253 |
|
* already reconstructed ones |
254 |
|
*/ |
255 |
|
void removeReconstructedTerms(TypeBuiltinSetMap& termsToRecons); |
256 |
|
|
257 |
|
/** |
258 |
|
* Replace all variables in `n` with ground values. Before, calling `match`, |
259 |
|
* `matchNewObs` rewrites the builtin analog of `n` which contains variables. |
260 |
|
* The rewritten term, however, may contain fewer variables: |
261 |
|
* |
262 |
|
* rewrite((ite true z1 z2)) = z1 // n = (c_ite c_true z1 z2) |
263 |
|
* |
264 |
|
* In such cases, `matchNewObs` replaces the remaining variables (`z1`) with |
265 |
|
* obligations and ignores the eliminated ones (`z2`). Since the values of the |
266 |
|
* eliminated variables do not matter, they are replaced with some ground |
267 |
|
* values by calling this method. |
268 |
|
* |
269 |
|
* @param n a term containing variables |
270 |
|
* @return `n` with all vars in `n` replaced with ground values |
271 |
|
*/ |
272 |
|
Node mkGround(Node n) const; |
273 |
|
|
274 |
|
/** |
275 |
|
* A notification that s is equal to n * { vars -> subs }. This function |
276 |
|
* should return false if we do not wish to be notified of further matches. |
277 |
|
*/ |
278 |
|
bool notify(Node s, |
279 |
|
Node n, |
280 |
|
std::vector<Node>& vars, |
281 |
|
std::vector<Node>& subs) override; |
282 |
|
|
283 |
|
/** |
284 |
|
* Reset the state of this SygusReconstruct object. |
285 |
|
*/ |
286 |
|
void clear(); |
287 |
|
|
288 |
|
/** |
289 |
|
* Print the pool of patterns/shape used in the matching phase. |
290 |
|
* |
291 |
|
* \note requires enabling "sygus-rcons" trace |
292 |
|
* |
293 |
|
* @param pool a pool of patterns/shapes to print |
294 |
|
*/ |
295 |
|
void printPool( |
296 |
|
const std::unordered_map<TypeNode, std::vector<Node>>& pool) const; |
297 |
|
|
298 |
|
/** pointer to the sygus term database */ |
299 |
|
TermDbSygus* d_tds; |
300 |
|
/** reference to the statistics of parent */ |
301 |
|
SygusStatistics& d_stats; |
302 |
|
|
303 |
|
/** a list of obligations to solve */ |
304 |
|
std::vector<std::unique_ptr<RConsObligation>> d_obs; |
305 |
|
/** a map from a sygus datatype type to its reconstruction info */ |
306 |
|
std::unordered_map<TypeNode, RConsTypeInfo> d_stnInfo; |
307 |
|
|
308 |
|
/** a map from an obligation's skolem to its sygus solution (if it exists) */ |
309 |
|
std::unordered_map<TNode, TNode> d_sol; |
310 |
|
|
311 |
|
/** a map from a candidate solution to its sub-obligations */ |
312 |
|
std::unordered_map<Node, std::vector<RConsObligation*>> d_subObs; |
313 |
|
/** a map from a candidate solution to its parent obligation */ |
314 |
|
std::unordered_map<Node, RConsObligation*> d_parentOb; |
315 |
|
|
316 |
|
/** a cache of sygus variables treated as ground terms by matching */ |
317 |
|
std::unordered_map<Node, Node> d_sygusVars; |
318 |
|
|
319 |
|
/** A trie for filtering out redundant terms from the paterns pool */ |
320 |
|
expr::MatchTrie d_poolTrie; |
321 |
|
}; |
322 |
|
|
323 |
|
} // namespace quantifiers |
324 |
|
} // namespace theory |
325 |
|
} // namespace cvc5 |
326 |
|
|
327 |
|
#endif // CVC5__THEORY__QUANTIFIERS__SYGUS_RECONSTRUCT_H |