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