1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, 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 techniques for sygus explanations. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/sygus/sygus_explain.h" |
17 |
|
|
18 |
|
#include "expr/dtype.h" |
19 |
|
#include "expr/dtype_cons.h" |
20 |
|
#include "smt/logic_exception.h" |
21 |
|
#include "theory/datatypes/sygus_datatype_utils.h" |
22 |
|
#include "theory/datatypes/theory_datatypes_utils.h" |
23 |
|
#include "theory/quantifiers/sygus/sygus_invariance.h" |
24 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
25 |
|
|
26 |
|
using namespace cvc5::kind; |
27 |
|
using namespace std; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
64032 |
void TermRecBuild::addTerm(Node n) |
34 |
|
{ |
35 |
64032 |
d_term.push_back(n); |
36 |
128064 |
std::vector<Node> currc; |
37 |
64032 |
d_kind.push_back(n.getKind()); |
38 |
64032 |
if (n.getMetaKind() == kind::metakind::PARAMETERIZED) |
39 |
|
{ |
40 |
64008 |
currc.push_back(n.getOperator()); |
41 |
64008 |
d_has_op.push_back(true); |
42 |
|
} |
43 |
|
else |
44 |
|
{ |
45 |
24 |
d_has_op.push_back(false); |
46 |
|
} |
47 |
114771 |
for (unsigned i = 0; i < n.getNumChildren(); i++) |
48 |
|
{ |
49 |
50739 |
currc.push_back(n[i]); |
50 |
|
} |
51 |
64032 |
d_children.push_back(currc); |
52 |
64032 |
} |
53 |
|
|
54 |
15129 |
void TermRecBuild::init(Node n) |
55 |
|
{ |
56 |
15129 |
Assert(d_term.empty()); |
57 |
15129 |
addTerm(n); |
58 |
15129 |
} |
59 |
|
|
60 |
48903 |
void TermRecBuild::push(unsigned p) |
61 |
|
{ |
62 |
48903 |
Assert(!d_term.empty()); |
63 |
48903 |
unsigned curr = d_term.size() - 1; |
64 |
48903 |
Assert(d_pos.size() == curr); |
65 |
48903 |
Assert(d_pos.size() + 1 == d_children.size()); |
66 |
48903 |
Assert(p < d_term[curr].getNumChildren()); |
67 |
48903 |
addTerm(d_term[curr][p]); |
68 |
48903 |
d_pos.push_back(p); |
69 |
48903 |
} |
70 |
|
|
71 |
48903 |
void TermRecBuild::pop() |
72 |
|
{ |
73 |
48903 |
Assert(!d_pos.empty()); |
74 |
48903 |
d_pos.pop_back(); |
75 |
48903 |
d_kind.pop_back(); |
76 |
48903 |
d_has_op.pop_back(); |
77 |
48903 |
d_children.pop_back(); |
78 |
48903 |
d_term.pop_back(); |
79 |
48903 |
} |
80 |
|
|
81 |
99603 |
void TermRecBuild::replaceChild(unsigned i, Node r) |
82 |
|
{ |
83 |
99603 |
Assert(!d_term.empty()); |
84 |
99603 |
unsigned curr = d_term.size() - 1; |
85 |
99603 |
unsigned o = d_has_op[curr] ? 1 : 0; |
86 |
99603 |
d_children[curr][i + o] = r; |
87 |
99603 |
} |
88 |
|
|
89 |
|
Node TermRecBuild::getChild(unsigned i) |
90 |
|
{ |
91 |
|
unsigned curr = d_term.size() - 1; |
92 |
|
unsigned o = d_has_op[curr] ? 1 : 0; |
93 |
|
return d_children[curr][i + o]; |
94 |
|
} |
95 |
|
|
96 |
79942 |
Node TermRecBuild::build(unsigned d) |
97 |
|
{ |
98 |
79942 |
Assert(d_pos.size() + 1 == d_term.size()); |
99 |
79942 |
Assert(d < d_term.size()); |
100 |
79942 |
int p = d < d_pos.size() ? d_pos[d] : -2; |
101 |
159884 |
std::vector<Node> children; |
102 |
79942 |
unsigned o = d_has_op[d] ? 1 : 0; |
103 |
320499 |
for (unsigned i = 0; i < d_children[d].size(); i++) |
104 |
|
{ |
105 |
481114 |
Node nc; |
106 |
240557 |
if (p + o == i) |
107 |
|
{ |
108 |
29242 |
nc = build(d + 1); |
109 |
|
} |
110 |
|
else |
111 |
|
{ |
112 |
211315 |
nc = d_children[d][i]; |
113 |
|
} |
114 |
240557 |
children.push_back(nc); |
115 |
|
} |
116 |
159884 |
return NodeManager::currentNM()->mkNode(d_kind[d], children); |
117 |
|
} |
118 |
|
|
119 |
25623 |
void SygusExplain::getExplanationForEquality(Node n, |
120 |
|
Node vn, |
121 |
|
std::vector<Node>& exp) |
122 |
|
{ |
123 |
51246 |
std::map<unsigned, bool> cexc; |
124 |
25623 |
getExplanationForEquality(n, vn, exp, cexc); |
125 |
25623 |
} |
126 |
|
|
127 |
25946 |
void SygusExplain::getExplanationForEquality(Node n, |
128 |
|
Node vn, |
129 |
|
std::vector<Node>& exp, |
130 |
|
std::map<unsigned, bool>& cexc) |
131 |
|
{ |
132 |
|
// since builtin types occur in grammar, types are comparable but not |
133 |
|
// necessarily equal |
134 |
25946 |
Assert(n.getType().isComparableTo(n.getType())); |
135 |
25946 |
if (n == vn) |
136 |
|
{ |
137 |
389 |
return; |
138 |
|
} |
139 |
51503 |
TypeNode tn = n.getType(); |
140 |
25946 |
if (!tn.isDatatype()) |
141 |
|
{ |
142 |
|
// sygus datatype fields that are not sygus datatypes are treated as |
143 |
|
// abstractions only, hence we disregard this field |
144 |
389 |
return; |
145 |
|
} |
146 |
25557 |
Assert(vn.getKind() == kind::APPLY_CONSTRUCTOR); |
147 |
25557 |
const DType& dt = tn.getDType(); |
148 |
25557 |
int i = datatypes::utils::indexOf(vn.getOperator()); |
149 |
51114 |
Node tst = datatypes::utils::mkTester(n, i, dt); |
150 |
25557 |
exp.push_back(tst); |
151 |
41872 |
for (unsigned j = 0; j < vn.getNumChildren(); j++) |
152 |
|
{ |
153 |
16315 |
if (cexc.find(j) == cexc.end()) |
154 |
|
{ |
155 |
|
Node sel = NodeManager::currentNM()->mkNode( |
156 |
32630 |
kind::APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal(tn, j), n); |
157 |
16315 |
getExplanationForEquality(sel, vn[j], exp); |
158 |
|
} |
159 |
|
} |
160 |
|
} |
161 |
|
|
162 |
323 |
Node SygusExplain::getExplanationForEquality(Node n, Node vn) |
163 |
|
{ |
164 |
646 |
std::map<unsigned, bool> cexc; |
165 |
646 |
return getExplanationForEquality(n, vn, cexc); |
166 |
|
} |
167 |
|
|
168 |
323 |
Node SygusExplain::getExplanationForEquality(Node n, |
169 |
|
Node vn, |
170 |
|
std::map<unsigned, bool>& cexc) |
171 |
|
{ |
172 |
646 |
std::vector<Node> exp; |
173 |
323 |
getExplanationForEquality(n, vn, exp, cexc); |
174 |
323 |
Assert(!exp.empty()); |
175 |
394 |
return exp.size() == 1 ? exp[0] |
176 |
717 |
: NodeManager::currentNM()->mkNode(kind::AND, exp); |
177 |
|
} |
178 |
|
|
179 |
|
// we have ( n = vn => eval( n ) = bvr ) ^ vn != vnr , returns exp such that exp |
180 |
|
// => ( eval( n ) = bvr ^ vn != vnr ) |
181 |
64032 |
void SygusExplain::getExplanationFor(TermRecBuild& trb, |
182 |
|
Node n, |
183 |
|
Node vn, |
184 |
|
std::vector<Node>& exp, |
185 |
|
std::map<TypeNode, int>& var_count, |
186 |
|
SygusInvarianceTest& et, |
187 |
|
Node vnr, |
188 |
|
Node& vnr_exp, |
189 |
|
int& sz) |
190 |
|
{ |
191 |
64032 |
Assert(vnr.isNull() || vn != vnr); |
192 |
64032 |
Assert(n.getType().isComparableTo(vn.getType())); |
193 |
128001 |
TypeNode ntn = n.getType(); |
194 |
64032 |
if (!ntn.isDatatype()) |
195 |
|
{ |
196 |
|
// SyGuS datatype fields that are not sygus datatypes are treated as |
197 |
|
// abstractions only, hence we disregard this field. It is important |
198 |
|
// that users of this method pay special attention to any constants, |
199 |
|
// otherwise the explanation n.eqNode(vn) is necessary here. For example, |
200 |
|
// any lemma schema that blocks the current value of an enumerator should |
201 |
|
// not make any assumptions about the value of the arguments of its any |
202 |
|
// constant constructors, since their explanation is not included here. |
203 |
63 |
return; |
204 |
|
} |
205 |
63969 |
Assert(vn.getKind() == APPLY_CONSTRUCTOR); |
206 |
63969 |
Assert(vnr.isNull() || vnr.getKind() == APPLY_CONSTRUCTOR); |
207 |
127938 |
std::map<unsigned, bool> cexc; |
208 |
|
// for each child, |
209 |
|
// check whether replacing that child by a fresh variable |
210 |
|
// also satisfies the invariance test. |
211 |
114669 |
for (unsigned i = 0; i < vn.getNumChildren(); i++) |
212 |
|
{ |
213 |
101400 |
TypeNode xtn = vn[i].getType(); |
214 |
101400 |
Node x = d_tdb->getFreeVarInc(xtn, var_count); |
215 |
50700 |
trb.replaceChild(i, x); |
216 |
101400 |
Node nvn = trb.build(); |
217 |
50700 |
Assert(nvn.getKind() == kind::APPLY_CONSTRUCTOR); |
218 |
50700 |
if (et.is_invariant(d_tdb, nvn, x)) |
219 |
|
{ |
220 |
1797 |
cexc[i] = true; |
221 |
|
// we are tracking term size if positive |
222 |
1797 |
if (sz >= 0) |
223 |
|
{ |
224 |
186 |
int s = datatypes::utils::getSygusTermSize(vn[i]); |
225 |
186 |
sz = sz - s; |
226 |
|
} |
227 |
|
} |
228 |
|
else |
229 |
|
{ |
230 |
|
// revert |
231 |
48903 |
trb.replaceChild(i, vn[i]); |
232 |
|
} |
233 |
|
} |
234 |
63969 |
const DType& dt = ntn.getDType(); |
235 |
63969 |
int cindex = datatypes::utils::indexOf(vn.getOperator()); |
236 |
63969 |
Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors()); |
237 |
127938 |
Node tst = datatypes::utils::mkTester(n, cindex, dt); |
238 |
63969 |
exp.push_back(tst); |
239 |
|
// if the operator of vn is different than vnr, then disunification obligation |
240 |
|
// is met |
241 |
63969 |
if (!vnr.isNull()) |
242 |
|
{ |
243 |
2999 |
if (vnr.getOperator() != vn.getOperator()) |
244 |
|
{ |
245 |
1670 |
vnr = Node::null(); |
246 |
1670 |
vnr_exp = NodeManager::currentNM()->mkConst(true); |
247 |
|
} |
248 |
|
} |
249 |
114669 |
for (unsigned i = 0; i < vn.getNumChildren(); i++) |
250 |
|
{ |
251 |
|
Node sel = NodeManager::currentNM()->mkNode( |
252 |
101400 |
kind::APPLY_SELECTOR_TOTAL, dt[cindex].getSelectorInternal(ntn, i), n); |
253 |
101400 |
Node vnr_c = vnr.isNull() ? vnr : (vn[i] == vnr[i] ? Node::null() : vnr[i]); |
254 |
50700 |
if (cexc.find(i) == cexc.end()) |
255 |
|
{ |
256 |
48903 |
trb.push(i); |
257 |
97806 |
Node vnr_exp_c; |
258 |
48903 |
getExplanationFor( |
259 |
|
trb, sel, vn[i], exp, var_count, et, vnr_c, vnr_exp_c, sz); |
260 |
48903 |
trb.pop(); |
261 |
48903 |
if (!vnr_c.isNull()) |
262 |
|
{ |
263 |
1327 |
Assert(!vnr_exp_c.isNull()); |
264 |
1327 |
if (vnr_exp_c.isConst() || vnr_exp.isNull()) |
265 |
|
{ |
266 |
|
// recursively satisfied the disunification obligation |
267 |
1327 |
if (vnr_exp_c.isConst()) |
268 |
|
{ |
269 |
|
// was successful, don't consider further |
270 |
1327 |
vnr = Node::null(); |
271 |
|
} |
272 |
1327 |
vnr_exp = vnr_exp_c; |
273 |
|
} |
274 |
|
} |
275 |
|
} |
276 |
|
else |
277 |
|
{ |
278 |
|
// if excluded, we may need to add the explanation for this |
279 |
1797 |
if (vnr_exp.isNull() && !vnr_c.isNull()) |
280 |
|
{ |
281 |
3 |
vnr_exp = getExplanationForEquality(sel, vnr[i]); |
282 |
|
} |
283 |
|
} |
284 |
|
} |
285 |
|
} |
286 |
|
|
287 |
|
void SygusExplain::getExplanationFor(Node n, |
288 |
|
Node vn, |
289 |
|
std::vector<Node>& exp, |
290 |
|
SygusInvarianceTest& et, |
291 |
|
Node vnr, |
292 |
|
unsigned& sz) |
293 |
|
{ |
294 |
|
std::map<TypeNode, int> var_count; |
295 |
|
return getExplanationFor(n, vn, exp, et, vnr, var_count, sz); |
296 |
|
} |
297 |
|
|
298 |
1673 |
void SygusExplain::getExplanationFor(Node n, |
299 |
|
Node vn, |
300 |
|
std::vector<Node>& exp, |
301 |
|
SygusInvarianceTest& et, |
302 |
|
Node vnr, |
303 |
|
std::map<TypeNode, int>& var_count, |
304 |
|
unsigned& sz) |
305 |
|
{ |
306 |
|
// naive : |
307 |
|
// return getExplanationForEquality( n, vn, exp ); |
308 |
|
|
309 |
|
// set up the recursion object; |
310 |
3346 |
TermRecBuild trb; |
311 |
1673 |
trb.init(vn); |
312 |
3346 |
Node vnr_exp; |
313 |
1673 |
int sz_use = sz; |
314 |
1673 |
getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz_use); |
315 |
1673 |
Assert(sz_use >= 0); |
316 |
1673 |
sz = sz_use; |
317 |
1673 |
Assert(vnr.isNull() || !vnr_exp.isNull()); |
318 |
1673 |
if (!vnr_exp.isNull() && !vnr_exp.isConst()) |
319 |
|
{ |
320 |
2 |
exp.push_back(vnr_exp.negate()); |
321 |
|
} |
322 |
1673 |
} |
323 |
|
|
324 |
8178 |
void SygusExplain::getExplanationFor(Node n, |
325 |
|
Node vn, |
326 |
|
std::vector<Node>& exp, |
327 |
|
SygusInvarianceTest& et, |
328 |
|
bool strict) |
329 |
|
{ |
330 |
16356 |
std::map<TypeNode, int> var_count; |
331 |
8178 |
getExplanationFor(n, vn, exp, et, var_count, strict); |
332 |
8178 |
} |
333 |
|
|
334 |
16395 |
void SygusExplain::getExplanationFor(Node n, |
335 |
|
Node vn, |
336 |
|
std::vector<Node>& exp, |
337 |
|
SygusInvarianceTest& et, |
338 |
|
std::map<TypeNode, int>& var_count, |
339 |
|
bool strict) |
340 |
|
{ |
341 |
16395 |
if (!strict) |
342 |
|
{ |
343 |
|
// check if it is invariant over the entire node |
344 |
13495 |
TypeNode vtn = vn.getType(); |
345 |
13495 |
Node x = d_tdb->getFreeVarInc(vtn, var_count); |
346 |
8217 |
if (et.is_invariant(d_tdb, x, x)) |
347 |
|
{ |
348 |
2939 |
return; |
349 |
|
} |
350 |
5278 |
var_count[vtn]--; |
351 |
|
} |
352 |
13456 |
int sz = -1; |
353 |
26912 |
TermRecBuild trb; |
354 |
13456 |
trb.init(vn); |
355 |
26912 |
Node vnr; |
356 |
26912 |
Node vnr_exp; |
357 |
13456 |
getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz); |
358 |
|
} |
359 |
|
|
360 |
|
} // namespace quantifiers |
361 |
|
} // namespace theory |
362 |
29505 |
} // namespace cvc5 |