1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Mathias Preiner, Aina Niemetz |
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 |
|
* Implementation of sygus_eval_unfold. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/sygus/sygus_eval_unfold.h" |
17 |
|
|
18 |
|
#include "expr/dtype_cons.h" |
19 |
|
#include "expr/sygus_datatype.h" |
20 |
|
#include "options/quantifiers_options.h" |
21 |
|
#include "theory/datatypes/sygus_datatype_utils.h" |
22 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
23 |
|
#include "theory/rewriter.h" |
24 |
|
|
25 |
|
using namespace std; |
26 |
|
using namespace cvc5::kind; |
27 |
|
using namespace cvc5::context; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
1900 |
SygusEvalUnfold::SygusEvalUnfold(Env& env, TermDbSygus* tds) |
34 |
1900 |
: EnvObj(env), d_tds(tds) |
35 |
|
{ |
36 |
1900 |
} |
37 |
|
|
38 |
327019 |
void SygusEvalUnfold::registerEvalTerm(Node n) |
39 |
|
{ |
40 |
327019 |
Assert(options::sygusEvalUnfold()); |
41 |
|
// is this a sygus evaluation function application? |
42 |
327019 |
if (n.getKind() != DT_SYGUS_EVAL) |
43 |
|
{ |
44 |
644356 |
return; |
45 |
|
} |
46 |
7160 |
if (d_eval_processed.find(n) != d_eval_processed.end()) |
47 |
|
{ |
48 |
4638 |
return; |
49 |
|
} |
50 |
5044 |
Trace("sygus-eval-unfold") |
51 |
2522 |
<< "SygusEvalUnfold: register eval term : " << n << std::endl; |
52 |
2522 |
d_eval_processed.insert(n); |
53 |
5044 |
TypeNode tn = n[0].getType(); |
54 |
|
// since n[0] is an evaluation head, we know tn is a sygus datatype |
55 |
2522 |
Assert(tn.isDatatype()); |
56 |
2522 |
const DType& dt = tn.getDType(); |
57 |
2522 |
Assert(dt.isSygus()); |
58 |
2522 |
if (n[0].getKind() == APPLY_CONSTRUCTOR) |
59 |
|
{ |
60 |
|
// constructors should be unfolded and reduced already |
61 |
|
Assert(false); |
62 |
|
return; |
63 |
|
} |
64 |
|
// register this evaluation term with its head |
65 |
2522 |
d_evals[n[0]].push_back(n); |
66 |
5044 |
Node var_list = dt.getSygusVarList(); |
67 |
2522 |
d_eval_args[n[0]].push_back(std::vector<Node>()); |
68 |
9613 |
for (unsigned j = 1, size = n.getNumChildren(); j < size; j++) |
69 |
|
{ |
70 |
7091 |
d_eval_args[n[0]].back().push_back(n[j]); |
71 |
|
} |
72 |
5044 |
Node a = TermDbSygus::getAnchor(n[0]); |
73 |
2522 |
d_subterms[a].insert(n[0]); |
74 |
|
} |
75 |
|
|
76 |
10986 |
void SygusEvalUnfold::registerModelValue(Node a, |
77 |
|
Node v, |
78 |
|
std::vector<Node>& terms, |
79 |
|
std::vector<Node>& vals, |
80 |
|
std::vector<Node>& exps) |
81 |
|
{ |
82 |
10986 |
std::map<Node, std::unordered_set<Node> >::iterator its = d_subterms.find(a); |
83 |
10986 |
if (its == d_subterms.end()) |
84 |
|
{ |
85 |
3452 |
return; |
86 |
|
} |
87 |
7534 |
NodeManager* nm = NodeManager::currentNM(); |
88 |
7534 |
SygusExplain* sy_exp = d_tds->getExplain(); |
89 |
15068 |
Trace("sygus-eval-unfold") |
90 |
15068 |
<< "SygusEvalUnfold: " << a << ", has " << its->second.size() |
91 |
7534 |
<< " registered subterms." << std::endl; |
92 |
26154 |
for (const Node& n : its->second) |
93 |
|
{ |
94 |
18620 |
Trace("sygus-eval-unfold-debug") << "...process : " << n << std::endl; |
95 |
|
std::map<Node, std::vector<std::vector<Node> > >::iterator it = |
96 |
18620 |
d_eval_args.find(n); |
97 |
18620 |
if (it != d_eval_args.end() && !it->second.empty()) |
98 |
|
{ |
99 |
37240 |
TNode at = a; |
100 |
37240 |
TNode vt = v; |
101 |
37240 |
Node vn = n.substitute(at, vt); |
102 |
18620 |
vn = rewrite(vn); |
103 |
18620 |
unsigned start = d_node_mv_args_proc[n][vn]; |
104 |
|
// get explanation in terms of testers |
105 |
37240 |
std::vector<Node> antec_exp; |
106 |
18620 |
sy_exp->getExplanationForEquality(n, vn, antec_exp); |
107 |
|
Node antec = |
108 |
37240 |
antec_exp.size() == 1 ? antec_exp[0] : nm->mkNode(AND, antec_exp); |
109 |
|
// Node antec = n.eqNode( vn ); |
110 |
37240 |
TypeNode tn = n.getType(); |
111 |
|
// Check if the sygus type has any symbolic constructors. This will |
112 |
|
// impact how the unfolding is computed below. |
113 |
18620 |
SygusTypeInfo& sti = d_tds->getTypeInfo(tn); |
114 |
18620 |
bool hasSymCons = sti.hasSubtermSymbolicCons(); |
115 |
|
// n occurs as an evaluation head, thus it has sygus datatype type |
116 |
18620 |
Assert(tn.isDatatype()); |
117 |
18620 |
const DType& dt = tn.getDType(); |
118 |
18620 |
Assert(dt.isSygus()); |
119 |
37240 |
Trace("sygus-eval-unfold") |
120 |
18620 |
<< "SygusEvalUnfold: Register model value : " << vn << " for " << n |
121 |
18620 |
<< std::endl; |
122 |
18620 |
unsigned curr_size = it->second.size(); |
123 |
37240 |
Trace("sygus-eval-unfold") |
124 |
18620 |
<< "...it has " << curr_size << " evaluations, already processed " |
125 |
18620 |
<< start << "." << std::endl; |
126 |
37240 |
Node bTerm = d_tds->sygusToBuiltin(vn, tn); |
127 |
18620 |
Trace("sygus-eval-unfold") << "Built-in term : " << bTerm << std::endl; |
128 |
37240 |
std::vector<Node> vars; |
129 |
37240 |
Node var_list = dt.getSygusVarList(); |
130 |
73023 |
for (const Node& var : var_list) |
131 |
|
{ |
132 |
54403 |
vars.push_back(var); |
133 |
|
} |
134 |
|
// evaluation children |
135 |
37240 |
std::vector<Node> eval_children; |
136 |
18620 |
eval_children.push_back(n); |
137 |
|
// for each evaluation |
138 |
44704 |
for (unsigned i = start; i < curr_size; i++) |
139 |
|
{ |
140 |
52168 |
Node res; |
141 |
52168 |
Node expn; |
142 |
|
// should we unfold? |
143 |
26084 |
bool do_unfold = false; |
144 |
26084 |
if (options::sygusEvalUnfoldBool()) |
145 |
|
{ |
146 |
52168 |
Node bTermUse = bTerm; |
147 |
26084 |
if (bTerm.getKind() == APPLY_UF) |
148 |
|
{ |
149 |
|
// if the builtin term is non-beta-reduced application of lambda, |
150 |
|
// we look at the body of the lambda. |
151 |
|
Node bTermOp = bTerm.getOperator(); |
152 |
|
if (bTermOp.getKind() == LAMBDA) |
153 |
|
{ |
154 |
|
bTermUse = bTermOp[0]; |
155 |
|
} |
156 |
|
} |
157 |
26084 |
if (bTermUse.getKind() == ITE || bTermUse.getType().isBoolean()) |
158 |
|
{ |
159 |
7095 |
do_unfold = true; |
160 |
|
} |
161 |
|
} |
162 |
26084 |
if (do_unfold || hasSymCons) |
163 |
|
{ |
164 |
|
// note that this is replicated for different values |
165 |
16334 |
std::map<Node, Node> vtm; |
166 |
16334 |
std::vector<Node> exp; |
167 |
8167 |
vtm[n] = vn; |
168 |
8167 |
eval_children.insert( |
169 |
16334 |
eval_children.end(), it->second[i].begin(), it->second[i].end()); |
170 |
16334 |
Node eval_fun = nm->mkNode(DT_SYGUS_EVAL, eval_children); |
171 |
8167 |
eval_children.resize(1); |
172 |
|
// If we explicitly asked to unfold, we use single step, otherwise |
173 |
|
// we use multi step. |
174 |
8167 |
res = unfold(eval_fun, vtm, exp, true, !do_unfold); |
175 |
8167 |
Trace("sygus-eval-unfold") << "Unfold returns " << res << std::endl; |
176 |
16334 |
expn = exp.size() == 1 ? exp[0] : nm->mkNode(AND, exp); |
177 |
|
} |
178 |
|
else |
179 |
|
{ |
180 |
35834 |
EvalSygusInvarianceTest esit; |
181 |
17917 |
eval_children.insert( |
182 |
35834 |
eval_children.end(), it->second[i].begin(), it->second[i].end()); |
183 |
35834 |
Node conj = nm->mkNode(DT_SYGUS_EVAL, eval_children); |
184 |
17917 |
eval_children[0] = vn; |
185 |
35834 |
Node eval_fun = nm->mkNode(DT_SYGUS_EVAL, eval_children); |
186 |
17917 |
res = d_tds->rewriteNode(eval_fun); |
187 |
35834 |
Trace("sygus-eval-unfold") |
188 |
17917 |
<< "Evaluate with unfolding returns " << res << std::endl; |
189 |
17917 |
esit.init(conj, n, res); |
190 |
17917 |
eval_children.resize(1); |
191 |
17917 |
eval_children[0] = n; |
192 |
|
|
193 |
|
// evaluate with minimal explanation |
194 |
35834 |
std::vector<Node> mexp; |
195 |
17917 |
sy_exp->getExplanationFor(n, vn, mexp, esit); |
196 |
17917 |
Assert(!mexp.empty()); |
197 |
17917 |
expn = mexp.size() == 1 ? mexp[0] : nm->mkNode(AND, mexp); |
198 |
|
} |
199 |
26084 |
Assert(!res.isNull()); |
200 |
26084 |
terms.push_back(d_evals[n][i]); |
201 |
26084 |
vals.push_back(res); |
202 |
26084 |
exps.push_back(expn); |
203 |
52168 |
Trace("sygus-eval-unfold") |
204 |
26084 |
<< "Conclude : " << d_evals[n][i] << " == " << res << std::endl; |
205 |
26084 |
Trace("sygus-eval-unfold") << " from " << expn << std::endl; |
206 |
|
} |
207 |
18620 |
d_node_mv_args_proc[n][vn] = curr_size; |
208 |
|
} |
209 |
|
} |
210 |
|
} |
211 |
|
|
212 |
10727 |
Node SygusEvalUnfold::unfold(Node en, |
213 |
|
std::map<Node, Node>& vtm, |
214 |
|
std::vector<Node>& exp, |
215 |
|
bool track_exp, |
216 |
|
bool doRec) |
217 |
|
{ |
218 |
10727 |
if (en.getKind() != DT_SYGUS_EVAL) |
219 |
|
{ |
220 |
|
Assert(en.isConst()); |
221 |
|
return en; |
222 |
|
} |
223 |
21454 |
Trace("sygus-eval-unfold-debug") |
224 |
10727 |
<< "Unfold : " << en << ", track exp is " << track_exp << ", doRec is " |
225 |
10727 |
<< doRec << std::endl; |
226 |
21454 |
Node ev = en[0]; |
227 |
10727 |
if (track_exp) |
228 |
|
{ |
229 |
10727 |
std::map<Node, Node>::iterator itv = vtm.find(en[0]); |
230 |
10727 |
Assert(itv != vtm.end()); |
231 |
10727 |
if (itv != vtm.end()) |
232 |
|
{ |
233 |
10727 |
ev = itv->second; |
234 |
|
} |
235 |
10727 |
Assert(en[0].getType() == ev.getType()); |
236 |
10727 |
Assert(ev.isConst()); |
237 |
|
} |
238 |
21454 |
Trace("sygus-eval-unfold-debug") |
239 |
10727 |
<< "Unfold model value is : " << ev << std::endl; |
240 |
10727 |
AlwaysAssert(ev.getKind() == APPLY_CONSTRUCTOR); |
241 |
21454 |
std::vector<Node> args; |
242 |
35693 |
for (unsigned i = 1, nchild = en.getNumChildren(); i < nchild; i++) |
243 |
|
{ |
244 |
24966 |
args.push_back(en[i]); |
245 |
|
} |
246 |
|
|
247 |
21454 |
TypeNode headType = en[0].getType(); |
248 |
10727 |
NodeManager* nm = NodeManager::currentNM(); |
249 |
10727 |
const DType& dt = headType.getDType(); |
250 |
10727 |
unsigned i = datatypes::utils::indexOf(ev.getOperator()); |
251 |
10727 |
if (track_exp) |
252 |
|
{ |
253 |
|
// explanation |
254 |
21454 |
Node ee = nm->mkNode(APPLY_TESTER, dt[i].getTester(), en[0]); |
255 |
10727 |
if (std::find(exp.begin(), exp.end(), ee) == exp.end()) |
256 |
|
{ |
257 |
10727 |
exp.push_back(ee); |
258 |
|
} |
259 |
|
} |
260 |
|
// if we are a symbolic constructor, unfolding returns the subterm itself |
261 |
21454 |
Node sop = dt[i].getSygusOp(); |
262 |
10727 |
if (sop.getAttribute(SygusAnyConstAttribute())) |
263 |
|
{ |
264 |
1980 |
Trace("sygus-eval-unfold-debug") |
265 |
990 |
<< "...it is an any-constant constructor" << std::endl; |
266 |
990 |
Assert(dt[i].getNumArgs() == 1); |
267 |
|
// If the argument to evaluate is itself concrete, then we use its |
268 |
|
// argument; otherwise we return its selector. |
269 |
990 |
if (en[0].getKind() == APPLY_CONSTRUCTOR) |
270 |
|
{ |
271 |
|
Trace("sygus-eval-unfold-debug") |
272 |
|
<< "...return (from constructor) " << en[0][0] << std::endl; |
273 |
|
return en[0][0]; |
274 |
|
} |
275 |
|
else |
276 |
|
{ |
277 |
|
Node ret = nm->mkNode( |
278 |
1980 |
APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal(headType, 0), en[0]); |
279 |
1980 |
Trace("sygus-eval-unfold-debug") |
280 |
990 |
<< "...return (from constructor) " << ret << std::endl; |
281 |
990 |
return ret; |
282 |
|
} |
283 |
|
} |
284 |
|
|
285 |
9737 |
Assert(!dt.isParametric()); |
286 |
19474 |
std::map<int, Node> pre; |
287 |
26634 |
for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++) |
288 |
|
{ |
289 |
33794 |
std::vector<Node> cc; |
290 |
33794 |
Node s; |
291 |
|
// get the j^th subfield of en |
292 |
16897 |
if (en[0].getKind() == APPLY_CONSTRUCTOR) |
293 |
|
{ |
294 |
|
// if it is a concrete constructor application, as an optimization, |
295 |
|
// just return the argument |
296 |
|
s = en[0][j]; |
297 |
|
} |
298 |
|
else |
299 |
|
{ |
300 |
50691 |
s = nm->mkNode( |
301 |
33794 |
APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal(headType, j), en[0]); |
302 |
|
} |
303 |
16897 |
cc.push_back(s); |
304 |
16897 |
if (track_exp) |
305 |
|
{ |
306 |
|
// update vtm map |
307 |
16897 |
vtm[s] = ev[j]; |
308 |
|
} |
309 |
16897 |
cc.insert(cc.end(), args.begin(), args.end()); |
310 |
33794 |
Node argj = nm->mkNode(DT_SYGUS_EVAL, cc); |
311 |
16897 |
if (doRec) |
312 |
|
{ |
313 |
2560 |
Trace("sygus-eval-unfold-debug") << "Recurse on " << s << std::endl; |
314 |
|
// evaluate recursively |
315 |
2560 |
argj = unfold(argj, vtm, exp, track_exp, doRec); |
316 |
|
} |
317 |
16897 |
pre[j] = argj; |
318 |
|
} |
319 |
19474 |
Node ret = d_tds->mkGeneric(dt, i, pre); |
320 |
|
// apply the appropriate substitution to ret |
321 |
9737 |
ret = datatypes::utils::applySygusArgs(dt, sop, ret, args); |
322 |
19474 |
Trace("sygus-eval-unfold-debug") |
323 |
9737 |
<< "Applied sygus args : " << ret << std::endl; |
324 |
|
// rewrite |
325 |
9737 |
ret = rewrite(ret); |
326 |
9737 |
Trace("sygus-eval-unfold-debug") << "Rewritten : " << ret << std::endl; |
327 |
9737 |
return ret; |
328 |
|
} |
329 |
|
|
330 |
|
} // namespace quantifiers |
331 |
|
} // namespace theory |
332 |
31137 |
} // namespace cvc5 |