1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Mathias Preiner, Andres Noetzli |
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 skolemization utility. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/skolemize.h" |
17 |
|
|
18 |
|
#include "expr/dtype.h" |
19 |
|
#include "expr/dtype_cons.h" |
20 |
|
#include "expr/skolem_manager.h" |
21 |
|
#include "options/quantifiers_options.h" |
22 |
|
#include "options/smt_options.h" |
23 |
|
#include "proof/proof.h" |
24 |
|
#include "proof/proof_node_manager.h" |
25 |
|
#include "theory/quantifiers/quantifiers_attributes.h" |
26 |
|
#include "theory/quantifiers/quantifiers_state.h" |
27 |
|
#include "theory/quantifiers/term_registry.h" |
28 |
|
#include "theory/quantifiers/term_util.h" |
29 |
|
#include "theory/rewriter.h" |
30 |
|
#include "theory/sort_inference.h" |
31 |
|
|
32 |
|
using namespace cvc5::kind; |
33 |
|
|
34 |
|
namespace cvc5 { |
35 |
|
namespace theory { |
36 |
|
namespace quantifiers { |
37 |
|
|
38 |
9854 |
Skolemize::Skolemize(QuantifiersState& qs, |
39 |
|
TermRegistry& tr, |
40 |
9854 |
ProofNodeManager* pnm) |
41 |
|
: d_qstate(qs), |
42 |
|
d_treg(tr), |
43 |
9854 |
d_skolemized(qs.getUserContext()), |
44 |
|
d_pnm(pnm), |
45 |
|
d_epg(pnm == nullptr ? nullptr |
46 |
|
: new EagerProofGenerator( |
47 |
19708 |
pnm, qs.getUserContext(), "Skolemize::epg")) |
48 |
|
{ |
49 |
9854 |
} |
50 |
|
|
51 |
10604 |
TrustNode Skolemize::process(Node q) |
52 |
|
{ |
53 |
10604 |
Assert(q.getKind() == FORALL); |
54 |
|
// do skolemization |
55 |
10604 |
if (d_skolemized.find(q) != d_skolemized.end()) |
56 |
|
{ |
57 |
8211 |
return TrustNode::null(); |
58 |
|
} |
59 |
4786 |
Node lem; |
60 |
2393 |
ProofGenerator* pg = nullptr; |
61 |
5121 |
if (isProofEnabled() && !options::dtStcInduction() |
62 |
2714 |
&& !options::intWfInduction()) |
63 |
|
{ |
64 |
|
// if using proofs and not using induction, we use the justified |
65 |
|
// skolemization |
66 |
320 |
NodeManager* nm = NodeManager::currentNM(); |
67 |
320 |
SkolemManager* skm = nm->getSkolemManager(); |
68 |
640 |
std::vector<Node> echildren(q.begin(), q.end()); |
69 |
320 |
echildren[1] = echildren[1].notNode(); |
70 |
640 |
Node existsq = nm->mkNode(EXISTS, echildren); |
71 |
640 |
Node res = skm->mkSkolemize(existsq, d_skolem_constants[q], "skv"); |
72 |
640 |
Node qnot = q.notNode(); |
73 |
640 |
CDProof cdp(d_pnm); |
74 |
320 |
cdp.addStep(res, PfRule::SKOLEMIZE, {qnot}, {}); |
75 |
640 |
std::shared_ptr<ProofNode> pf = cdp.getProofFor(res); |
76 |
640 |
std::vector<Node> assumps; |
77 |
320 |
assumps.push_back(qnot); |
78 |
640 |
std::shared_ptr<ProofNode> pfs = d_pnm->mkScope({pf}, assumps); |
79 |
320 |
lem = nm->mkNode(IMPLIES, qnot, res); |
80 |
320 |
d_epg->setProofFor(lem, pfs); |
81 |
320 |
pg = d_epg.get(); |
82 |
640 |
Trace("quantifiers-sk") |
83 |
320 |
<< "Skolemize (with proofs) : " << d_skolem_constants[q] << " for " |
84 |
320 |
<< std::endl; |
85 |
320 |
Trace("quantifiers-sk") << " " << q << std::endl; |
86 |
320 |
Trace("quantifiers-sk") << " " << res << std::endl; |
87 |
|
} |
88 |
|
else |
89 |
|
{ |
90 |
|
// otherwise, we use the more general skolemization with inductive |
91 |
|
// strengthening, which does not support proofs |
92 |
4146 |
Node body = getSkolemizedBody(q); |
93 |
4146 |
NodeBuilder nb(kind::OR); |
94 |
2073 |
nb << q << body.notNode(); |
95 |
2073 |
lem = nb; |
96 |
|
} |
97 |
2393 |
d_skolemized[q] = lem; |
98 |
|
// triggered when skolemizing |
99 |
2393 |
d_treg.processSkolemization(q, d_skolem_constants[q]); |
100 |
2393 |
return TrustNode::mkTrustLemma(lem, pg); |
101 |
|
} |
102 |
|
|
103 |
33 |
bool Skolemize::getSkolemConstants(Node q, std::vector<Node>& skolems) |
104 |
|
{ |
105 |
|
std::unordered_map<Node, std::vector<Node>>::iterator it = |
106 |
33 |
d_skolem_constants.find(q); |
107 |
33 |
if (it != d_skolem_constants.end()) |
108 |
|
{ |
109 |
33 |
skolems.insert(skolems.end(), it->second.begin(), it->second.end()); |
110 |
33 |
return true; |
111 |
|
} |
112 |
|
return false; |
113 |
|
} |
114 |
|
|
115 |
|
Node Skolemize::getSkolemConstant(Node q, unsigned i) |
116 |
|
{ |
117 |
|
std::unordered_map<Node, std::vector<Node>>::iterator it = |
118 |
|
d_skolem_constants.find(q); |
119 |
|
if (it != d_skolem_constants.end()) |
120 |
|
{ |
121 |
|
if (i < it->second.size()) |
122 |
|
{ |
123 |
|
return it->second[i]; |
124 |
|
} |
125 |
|
} |
126 |
|
return Node::null(); |
127 |
|
} |
128 |
|
|
129 |
114 |
void Skolemize::getSelfSel(const DType& dt, |
130 |
|
const DTypeConstructor& dc, |
131 |
|
Node n, |
132 |
|
TypeNode ntn, |
133 |
|
std::vector<Node>& selfSel) |
134 |
|
{ |
135 |
228 |
TypeNode tspec; |
136 |
114 |
if (dt.isParametric()) |
137 |
|
{ |
138 |
|
tspec = dc.getSpecializedConstructorType(n.getType()); |
139 |
|
Trace("sk-ind-debug") << "Specialized constructor type : " << tspec |
140 |
|
<< std::endl; |
141 |
|
Assert(tspec.getNumChildren() == dc.getNumArgs()); |
142 |
|
} |
143 |
228 |
Trace("sk-ind-debug") << "Check self sel " << dc.getName() << " " |
144 |
114 |
<< dt.getName() << std::endl; |
145 |
114 |
NodeManager* nm = NodeManager::currentNM(); |
146 |
219 |
for (unsigned j = 0; j < dc.getNumArgs(); j++) |
147 |
|
{ |
148 |
210 |
std::vector<Node> ssc; |
149 |
105 |
if (dt.isParametric()) |
150 |
|
{ |
151 |
|
Trace("sk-ind-debug") << "Compare " << tspec[j] << " " << ntn |
152 |
|
<< std::endl; |
153 |
|
if (tspec[j] == ntn) |
154 |
|
{ |
155 |
|
ssc.push_back(n); |
156 |
|
} |
157 |
|
} |
158 |
|
else |
159 |
|
{ |
160 |
210 |
TypeNode tn = dc[j].getRangeType(); |
161 |
105 |
Trace("sk-ind-debug") << "Compare " << tn << " " << ntn << std::endl; |
162 |
105 |
if (tn == ntn) |
163 |
|
{ |
164 |
57 |
ssc.push_back(n); |
165 |
|
} |
166 |
|
} |
167 |
162 |
for (unsigned k = 0; k < ssc.size(); k++) |
168 |
|
{ |
169 |
|
Node ss = nm->mkNode( |
170 |
114 |
APPLY_SELECTOR_TOTAL, dc.getSelectorInternal(n.getType(), j), n); |
171 |
57 |
if (std::find(selfSel.begin(), selfSel.end(), ss) == selfSel.end()) |
172 |
|
{ |
173 |
57 |
selfSel.push_back(ss); |
174 |
|
} |
175 |
|
} |
176 |
|
} |
177 |
114 |
} |
178 |
|
|
179 |
2088 |
Node Skolemize::mkSkolemizedBody(Node f, |
180 |
|
Node n, |
181 |
|
std::vector<TypeNode>& argTypes, |
182 |
|
std::vector<TNode>& fvs, |
183 |
|
std::vector<Node>& sk, |
184 |
|
Node& sub, |
185 |
|
std::vector<unsigned>& sub_vars) |
186 |
|
{ |
187 |
2088 |
NodeManager* nm = NodeManager::currentNM(); |
188 |
2088 |
SkolemManager* sm = nm->getSkolemManager(); |
189 |
2088 |
Assert(sk.empty() || sk.size() == f[0].getNumChildren()); |
190 |
|
// calculate the variables and substitution |
191 |
4176 |
std::vector<TNode> ind_vars; |
192 |
4176 |
std::vector<unsigned> ind_var_indicies; |
193 |
4176 |
std::vector<TNode> vars; |
194 |
4176 |
std::vector<unsigned> var_indicies; |
195 |
5369 |
for (unsigned i = 0; i < f[0].getNumChildren(); i++) |
196 |
|
{ |
197 |
3281 |
if (isInductionTerm(f[0][i])) |
198 |
|
{ |
199 |
86 |
ind_vars.push_back(f[0][i]); |
200 |
86 |
ind_var_indicies.push_back(i); |
201 |
|
} |
202 |
|
else |
203 |
|
{ |
204 |
3195 |
vars.push_back(f[0][i]); |
205 |
3195 |
var_indicies.push_back(i); |
206 |
|
} |
207 |
6562 |
Node s; |
208 |
|
// make the new function symbol or use existing |
209 |
3281 |
if (i >= sk.size()) |
210 |
|
{ |
211 |
3258 |
if (argTypes.empty()) |
212 |
|
{ |
213 |
9726 |
s = sm->mkDummySkolem( |
214 |
6484 |
"skv", f[0][i].getType(), "created during skolemization"); |
215 |
|
} |
216 |
|
else |
217 |
|
{ |
218 |
32 |
TypeNode typ = nm->mkFunctionType(argTypes, f[0][i].getType()); |
219 |
|
Node op = sm->mkDummySkolem( |
220 |
32 |
"skop", typ, "op created during pre-skolemization"); |
221 |
|
// DOTHIS: set attribute on op, marking that it should not be selected |
222 |
|
// as trigger |
223 |
32 |
std::vector<Node> funcArgs; |
224 |
16 |
funcArgs.push_back(op); |
225 |
16 |
funcArgs.insert(funcArgs.end(), fvs.begin(), fvs.end()); |
226 |
16 |
s = nm->mkNode(kind::APPLY_UF, funcArgs); |
227 |
|
} |
228 |
3258 |
sk.push_back(s); |
229 |
|
} |
230 |
|
else |
231 |
|
{ |
232 |
23 |
Assert(sk[i].getType() == f[0][i].getType()); |
233 |
|
} |
234 |
|
} |
235 |
2088 |
Node ret; |
236 |
2088 |
if (vars.empty()) |
237 |
|
{ |
238 |
63 |
ret = n; |
239 |
|
} |
240 |
|
else |
241 |
|
{ |
242 |
4050 |
std::vector<Node> var_sk; |
243 |
5220 |
for (unsigned i = 0; i < var_indicies.size(); i++) |
244 |
|
{ |
245 |
3195 |
var_sk.push_back(sk[var_indicies[i]]); |
246 |
|
} |
247 |
2025 |
Assert(vars.size() == var_sk.size()); |
248 |
2025 |
ret = n.substitute(vars.begin(), vars.end(), var_sk.begin(), var_sk.end()); |
249 |
|
} |
250 |
2088 |
if (!ind_vars.empty()) |
251 |
|
{ |
252 |
63 |
Trace("sk-ind") << "Ind strengthen : (not " << f << ")" << std::endl; |
253 |
63 |
Trace("sk-ind") << "Skolemized is : " << ret << std::endl; |
254 |
126 |
Node n_str_ind; |
255 |
126 |
TypeNode tn = ind_vars[0].getType(); |
256 |
126 |
Node k = sk[ind_var_indicies[0]]; |
257 |
126 |
Node nret = ret.substitute(ind_vars[0], k); |
258 |
|
// note : everything is under a negation |
259 |
|
// the following constructs ~( R( x, k ) => ~P( x ) ) |
260 |
63 |
if (options::dtStcInduction() && tn.isDatatype()) |
261 |
|
{ |
262 |
57 |
const DType& dt = tn.getDType(); |
263 |
114 |
std::vector<Node> disj; |
264 |
171 |
for (unsigned i = 0; i < dt.getNumConstructors(); i++) |
265 |
|
{ |
266 |
228 |
std::vector<Node> selfSel; |
267 |
114 |
getSelfSel(dt, dt[i], k, tn, selfSel); |
268 |
228 |
std::vector<Node> conj; |
269 |
114 |
conj.push_back(nm->mkNode(APPLY_TESTER, dt[i].getTester(), k).negate()); |
270 |
171 |
for (unsigned j = 0; j < selfSel.size(); j++) |
271 |
|
{ |
272 |
57 |
conj.push_back(ret.substitute(ind_vars[0], selfSel[j]).negate()); |
273 |
|
} |
274 |
114 |
disj.push_back(conj.size() == 1 ? conj[0] : nm->mkNode(OR, conj)); |
275 |
|
} |
276 |
57 |
Assert(!disj.empty()); |
277 |
57 |
n_str_ind = disj.size() == 1 ? disj[0] : nm->mkNode(AND, disj); |
278 |
|
} |
279 |
6 |
else if (options::intWfInduction() && tn.isInteger()) |
280 |
|
{ |
281 |
12 |
Node icond = nm->mkNode(GEQ, k, nm->mkConst(Rational(0))); |
282 |
12 |
Node iret = ret.substitute(ind_vars[0], |
283 |
12 |
nm->mkNode(MINUS, k, nm->mkConst(Rational(1)))) |
284 |
12 |
.negate(); |
285 |
6 |
n_str_ind = nm->mkNode(OR, icond.negate(), iret); |
286 |
6 |
n_str_ind = nm->mkNode(AND, icond, n_str_ind); |
287 |
|
} |
288 |
|
else |
289 |
|
{ |
290 |
|
Trace("sk-ind") << "Unknown induction for term : " << ind_vars[0] |
291 |
|
<< ", type = " << tn << std::endl; |
292 |
|
Assert(false); |
293 |
|
} |
294 |
63 |
Trace("sk-ind") << "Strengthening is : " << n_str_ind << std::endl; |
295 |
|
|
296 |
126 |
std::vector<Node> rem_ind_vars; |
297 |
63 |
rem_ind_vars.insert( |
298 |
126 |
rem_ind_vars.end(), ind_vars.begin() + 1, ind_vars.end()); |
299 |
63 |
if (!rem_ind_vars.empty()) |
300 |
|
{ |
301 |
38 |
Node bvl = nm->mkNode(BOUND_VAR_LIST, rem_ind_vars); |
302 |
19 |
nret = nm->mkNode(FORALL, bvl, nret); |
303 |
19 |
nret = Rewriter::rewrite(nret); |
304 |
19 |
sub = nret; |
305 |
19 |
sub_vars.insert( |
306 |
38 |
sub_vars.end(), ind_var_indicies.begin() + 1, ind_var_indicies.end()); |
307 |
19 |
n_str_ind = nm->mkNode(FORALL, bvl, n_str_ind.negate()).negate(); |
308 |
|
} |
309 |
63 |
ret = nm->mkNode(OR, nret, n_str_ind); |
310 |
|
} |
311 |
4176 |
Trace("quantifiers-sk-debug") << "mkSkolem body for " << f |
312 |
2088 |
<< " returns : " << ret << std::endl; |
313 |
|
// if it has an instantiation level, set the skolemized body to that level |
314 |
2088 |
if (f.hasAttribute(InstLevelAttribute())) |
315 |
|
{ |
316 |
|
QuantAttributes::setInstantiationLevelAttr( |
317 |
|
ret, f.getAttribute(InstLevelAttribute())); |
318 |
|
} |
319 |
|
|
320 |
2088 |
Trace("quantifiers-sk") << "Skolemize : " << sk << " for " << std::endl; |
321 |
2088 |
Trace("quantifiers-sk") << " " << f << std::endl; |
322 |
|
|
323 |
4176 |
return ret; |
324 |
|
} |
325 |
|
|
326 |
2073 |
Node Skolemize::getSkolemizedBody(Node f) |
327 |
|
{ |
328 |
2073 |
Assert(f.getKind() == FORALL); |
329 |
2073 |
std::unordered_map<Node, Node>::iterator it = d_skolem_body.find(f); |
330 |
2073 |
if (it == d_skolem_body.end()) |
331 |
|
{ |
332 |
4128 |
std::vector<TypeNode> fvTypes; |
333 |
4128 |
std::vector<TNode> fvs; |
334 |
4128 |
Node sub; |
335 |
4128 |
std::vector<unsigned> sub_vars; |
336 |
|
Node ret = mkSkolemizedBody( |
337 |
4128 |
f, f[1], fvTypes, fvs, d_skolem_constants[f], sub, sub_vars); |
338 |
2064 |
d_skolem_body[f] = ret; |
339 |
|
// store sub quantifier information |
340 |
2064 |
if (!sub.isNull()) |
341 |
|
{ |
342 |
|
// if we are skolemizing one at a time, we already know the skolem |
343 |
|
// constants of the sub-quantified formula, store them |
344 |
19 |
Assert(d_skolem_constants[sub].empty()); |
345 |
42 |
for (unsigned i = 0; i < sub_vars.size(); i++) |
346 |
|
{ |
347 |
23 |
d_skolem_constants[sub].push_back(d_skolem_constants[f][sub_vars[i]]); |
348 |
|
} |
349 |
|
} |
350 |
2064 |
Assert(d_skolem_constants[f].size() == f[0].getNumChildren()); |
351 |
2064 |
SortInference* si = d_qstate.getSortInference(); |
352 |
2064 |
if (si != nullptr) |
353 |
|
{ |
354 |
|
for (unsigned i = 0; i < d_skolem_constants[f].size(); i++) |
355 |
|
{ |
356 |
|
// carry information for sort inference |
357 |
|
si->setSkolemVar(f, f[0][i], d_skolem_constants[f][i]); |
358 |
|
} |
359 |
|
} |
360 |
2064 |
return ret; |
361 |
|
} |
362 |
9 |
return it->second; |
363 |
|
} |
364 |
|
|
365 |
8969 |
bool Skolemize::isInductionTerm(Node n) |
366 |
|
{ |
367 |
17938 |
TypeNode tn = n.getType(); |
368 |
8969 |
if (options::dtStcInduction() && tn.isDatatype()) |
369 |
|
{ |
370 |
3909 |
const DType& dt = tn.getDType(); |
371 |
3909 |
return !dt.isCodatatype(); |
372 |
|
} |
373 |
5060 |
if (options::intWfInduction() && tn.isInteger()) |
374 |
|
{ |
375 |
1088 |
return true; |
376 |
|
} |
377 |
3972 |
return false; |
378 |
|
} |
379 |
|
|
380 |
12 |
void Skolemize::getSkolemTermVectors( |
381 |
|
std::map<Node, std::vector<Node> >& sks) const |
382 |
|
{ |
383 |
12 |
std::unordered_map<Node, std::vector<Node>>::const_iterator itk; |
384 |
21 |
for (const auto& p : d_skolemized) |
385 |
|
{ |
386 |
18 |
Node q = p.first; |
387 |
9 |
itk = d_skolem_constants.find(q); |
388 |
9 |
Assert(itk != d_skolem_constants.end()); |
389 |
9 |
sks[q].insert(sks[q].end(), itk->second.begin(), itk->second.end()); |
390 |
|
} |
391 |
12 |
} |
392 |
|
|
393 |
2393 |
bool Skolemize::isProofEnabled() const { return d_epg != nullptr; } |
394 |
|
|
395 |
|
} // namespace quantifiers |
396 |
|
} // namespace theory |
397 |
29349 |
} // namespace cvc5 |