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 equality proof checker. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/builtin/proof_checker.h" |
17 |
|
|
18 |
|
#include "expr/skolem_manager.h" |
19 |
|
#include "smt/env.h" |
20 |
|
#include "smt/term_formula_removal.h" |
21 |
|
#include "theory/evaluator.h" |
22 |
|
#include "theory/rewriter.h" |
23 |
|
#include "theory/substitutions.h" |
24 |
|
#include "theory/theory.h" |
25 |
|
|
26 |
|
using namespace cvc5::kind; |
27 |
|
|
28 |
|
namespace cvc5 { |
29 |
|
namespace theory { |
30 |
|
namespace builtin { |
31 |
|
|
32 |
9940 |
BuiltinProofRuleChecker::BuiltinProofRuleChecker(Env& env) : d_env(env) {} |
33 |
|
|
34 |
3794 |
void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) |
35 |
|
{ |
36 |
3794 |
pc->registerChecker(PfRule::ASSUME, this); |
37 |
3794 |
pc->registerChecker(PfRule::SCOPE, this); |
38 |
3794 |
pc->registerChecker(PfRule::SUBS, this); |
39 |
3794 |
pc->registerChecker(PfRule::REWRITE, this); |
40 |
3794 |
pc->registerChecker(PfRule::EVALUATE, this); |
41 |
3794 |
pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this); |
42 |
3794 |
pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this); |
43 |
3794 |
pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this); |
44 |
3794 |
pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this); |
45 |
3794 |
pc->registerChecker(PfRule::THEORY_REWRITE, this); |
46 |
3794 |
pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this); |
47 |
|
// trusted rules |
48 |
3794 |
pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1); |
49 |
3794 |
pc->registerTrustedChecker(PfRule::PREPROCESS, this, 3); |
50 |
3794 |
pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 3); |
51 |
3794 |
pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 3); |
52 |
3794 |
pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS_LEMMA, this, 3); |
53 |
3794 |
pc->registerTrustedChecker(PfRule::THEORY_EXPAND_DEF, this, 3); |
54 |
3794 |
pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 3); |
55 |
3794 |
pc->registerTrustedChecker(PfRule::TRUST_REWRITE, this, 1); |
56 |
3794 |
pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1); |
57 |
3794 |
pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1); |
58 |
3794 |
pc->registerTrustedChecker(PfRule::TRUST_SUBS_EQ, this, 3); |
59 |
3794 |
pc->registerTrustedChecker(PfRule::THEORY_INFERENCE, this, 3); |
60 |
3794 |
} |
61 |
|
|
62 |
1488874 |
Node BuiltinProofRuleChecker::applySubstitutionRewrite( |
63 |
|
Node n, |
64 |
|
const std::vector<Node>& exp, |
65 |
|
MethodId ids, |
66 |
|
MethodId ida, |
67 |
|
MethodId idr) |
68 |
|
{ |
69 |
2977748 |
Node nks = applySubstitution(n, exp, ids, ida); |
70 |
2977748 |
return d_env.getRewriter()->rewriteViaMethod(nks, idr); |
71 |
|
} |
72 |
|
|
73 |
2994381 |
bool BuiltinProofRuleChecker::getSubstitutionForLit(Node exp, |
74 |
|
TNode& var, |
75 |
|
TNode& subs, |
76 |
|
MethodId ids) |
77 |
|
{ |
78 |
2994381 |
if (ids == MethodId::SB_DEFAULT) |
79 |
|
{ |
80 |
2978529 |
if (exp.getKind() != EQUAL) |
81 |
|
{ |
82 |
|
return false; |
83 |
|
} |
84 |
2978529 |
var = exp[0]; |
85 |
2978529 |
subs = exp[1]; |
86 |
|
} |
87 |
15852 |
else if (ids == MethodId::SB_LITERAL) |
88 |
|
{ |
89 |
15852 |
bool polarity = exp.getKind() != NOT; |
90 |
15852 |
var = polarity ? exp : exp[0]; |
91 |
15852 |
subs = NodeManager::currentNM()->mkConst(polarity); |
92 |
|
} |
93 |
|
else if (ids == MethodId::SB_FORMULA) |
94 |
|
{ |
95 |
|
var = exp; |
96 |
|
subs = NodeManager::currentNM()->mkConst(true); |
97 |
|
} |
98 |
|
else |
99 |
|
{ |
100 |
|
Assert(false) << "BuiltinProofRuleChecker::applySubstitution: no " |
101 |
|
"substitution for " |
102 |
|
<< ids << std::endl; |
103 |
|
return false; |
104 |
|
} |
105 |
2994381 |
return true; |
106 |
|
} |
107 |
|
|
108 |
2994381 |
bool BuiltinProofRuleChecker::getSubstitutionFor(Node exp, |
109 |
|
std::vector<TNode>& vars, |
110 |
|
std::vector<TNode>& subs, |
111 |
|
std::vector<TNode>& from, |
112 |
|
MethodId ids) |
113 |
|
{ |
114 |
5988762 |
TNode v; |
115 |
5988762 |
TNode s; |
116 |
2994381 |
if (exp.getKind() == AND && ids == MethodId::SB_DEFAULT) |
117 |
|
{ |
118 |
|
for (const Node& ec : exp) |
119 |
|
{ |
120 |
|
// non-recursive, do not use nested AND |
121 |
|
if (!getSubstitutionForLit(ec, v, s, ids)) |
122 |
|
{ |
123 |
|
return false; |
124 |
|
} |
125 |
|
vars.push_back(v); |
126 |
|
subs.push_back(s); |
127 |
|
from.push_back(ec); |
128 |
|
} |
129 |
|
return true; |
130 |
|
} |
131 |
2994381 |
bool ret = getSubstitutionForLit(exp, v, s, ids); |
132 |
2994381 |
vars.push_back(v); |
133 |
2994381 |
subs.push_back(s); |
134 |
2994381 |
from.push_back(exp); |
135 |
2994381 |
return ret; |
136 |
|
} |
137 |
|
|
138 |
|
Node BuiltinProofRuleChecker::applySubstitution(Node n, |
139 |
|
Node exp, |
140 |
|
MethodId ids, |
141 |
|
MethodId ida) |
142 |
|
{ |
143 |
|
std::vector<Node> expv{exp}; |
144 |
|
return applySubstitution(n, expv, ids, ida); |
145 |
|
} |
146 |
|
|
147 |
1511251 |
Node BuiltinProofRuleChecker::applySubstitution(Node n, |
148 |
|
const std::vector<Node>& exp, |
149 |
|
MethodId ids, |
150 |
|
MethodId ida) |
151 |
|
{ |
152 |
3022502 |
std::vector<TNode> vars; |
153 |
3022502 |
std::vector<TNode> subs; |
154 |
3022502 |
std::vector<TNode> from; |
155 |
3954686 |
for (size_t i = 0, nexp = exp.size(); i < nexp; i++) |
156 |
|
{ |
157 |
2443435 |
if (exp[i].isNull()) |
158 |
|
{ |
159 |
|
return Node::null(); |
160 |
|
} |
161 |
2443435 |
if (!getSubstitutionFor(exp[i], vars, subs, from, ids)) |
162 |
|
{ |
163 |
|
return Node::null(); |
164 |
|
} |
165 |
|
} |
166 |
1511251 |
if (ida == MethodId::SBA_SIMUL) |
167 |
|
{ |
168 |
|
// simply apply the simultaneous substitution now |
169 |
|
return n.substitute(vars.begin(), vars.end(), subs.begin(), subs.end()); |
170 |
|
} |
171 |
1511251 |
else if (ida == MethodId::SBA_FIXPOINT) |
172 |
|
{ |
173 |
60848 |
SubstitutionMap sm; |
174 |
2439409 |
for (size_t i = 0, nvars = vars.size(); i < nvars; i++) |
175 |
|
{ |
176 |
2408985 |
sm.addSubstitution(vars[i], subs[i]); |
177 |
|
} |
178 |
60848 |
Node ns = sm.apply(n); |
179 |
30424 |
return ns; |
180 |
|
} |
181 |
1480827 |
Assert(ida == MethodId::SBA_SEQUENTIAL); |
182 |
|
// we prefer n traversals of the term to n^2/2 traversals of range terms |
183 |
2961654 |
Node ns = n; |
184 |
1515277 |
for (size_t i = 0, nvars = vars.size(); i < nvars; i++) |
185 |
|
{ |
186 |
68900 |
TNode v = vars[(nvars - 1) - i]; |
187 |
68900 |
TNode s = subs[(nvars - 1) - i]; |
188 |
34450 |
ns = ns.substitute(v, s); |
189 |
|
} |
190 |
1480827 |
return ns; |
191 |
|
} |
192 |
|
|
193 |
1171453 |
Node BuiltinProofRuleChecker::checkInternal(PfRule id, |
194 |
|
const std::vector<Node>& children, |
195 |
|
const std::vector<Node>& args) |
196 |
|
{ |
197 |
1171453 |
NodeManager * nm = NodeManager::currentNM(); |
198 |
|
// compute what was proven |
199 |
1171453 |
if (id == PfRule::ASSUME) |
200 |
|
{ |
201 |
|
Assert(children.empty()); |
202 |
|
Assert(args.size() == 1); |
203 |
|
Assert(args[0].getType().isBoolean()); |
204 |
|
return args[0]; |
205 |
|
} |
206 |
1171453 |
else if (id == PfRule::SCOPE) |
207 |
|
{ |
208 |
116838 |
Assert(children.size() == 1); |
209 |
116838 |
if (args.empty()) |
210 |
|
{ |
211 |
|
// no antecedant |
212 |
|
return children[0]; |
213 |
|
} |
214 |
233676 |
Node ant = nm->mkAnd(args); |
215 |
|
// if the conclusion is false, its the negated antencedant only |
216 |
116838 |
if (children[0].isConst() && !children[0].getConst<bool>()) |
217 |
|
{ |
218 |
68593 |
return ant.notNode(); |
219 |
|
} |
220 |
48245 |
return nm->mkNode(IMPLIES, ant, children[0]); |
221 |
|
} |
222 |
1054615 |
else if (id == PfRule::SUBS) |
223 |
|
{ |
224 |
|
Assert(children.size() > 0); |
225 |
|
Assert(1 <= args.size() && args.size() <= 2); |
226 |
|
MethodId ids = MethodId::SB_DEFAULT; |
227 |
|
if (args.size() == 2 && !getMethodId(args[1], ids)) |
228 |
|
{ |
229 |
|
return Node::null(); |
230 |
|
} |
231 |
|
std::vector<Node> exp; |
232 |
|
for (size_t i = 0, nchild = children.size(); i < nchild; i++) |
233 |
|
{ |
234 |
|
exp.push_back(children[i]); |
235 |
|
} |
236 |
|
Node res = applySubstitution(args[0], exp, ids); |
237 |
|
if (res.isNull()) |
238 |
|
{ |
239 |
|
return Node::null(); |
240 |
|
} |
241 |
|
return args[0].eqNode(res); |
242 |
|
} |
243 |
1054615 |
else if (id == PfRule::REWRITE) |
244 |
|
{ |
245 |
2737 |
Assert(children.empty()); |
246 |
2737 |
Assert(1 <= args.size() && args.size() <= 2); |
247 |
2737 |
MethodId idr = MethodId::RW_REWRITE; |
248 |
2737 |
if (args.size() == 2 && !getMethodId(args[1], idr)) |
249 |
|
{ |
250 |
|
return Node::null(); |
251 |
|
} |
252 |
5474 |
Node res = d_env.getRewriter()->rewriteViaMethod(args[0], idr); |
253 |
2737 |
if (res.isNull()) |
254 |
|
{ |
255 |
|
return Node::null(); |
256 |
|
} |
257 |
2737 |
return args[0].eqNode(res); |
258 |
|
} |
259 |
1051878 |
else if (id == PfRule::EVALUATE) |
260 |
|
{ |
261 |
28345 |
Assert(children.empty()); |
262 |
28345 |
Assert(args.size() == 1); |
263 |
56690 |
Node res = d_env.getRewriter()->rewriteViaMethod(args[0], MethodId::RW_EVALUATE); |
264 |
28345 |
if (res.isNull()) |
265 |
|
{ |
266 |
|
return Node::null(); |
267 |
|
} |
268 |
28345 |
return args[0].eqNode(res); |
269 |
|
} |
270 |
1023533 |
else if (id == PfRule::MACRO_SR_EQ_INTRO) |
271 |
|
{ |
272 |
25326 |
Assert(1 <= args.size() && args.size() <= 4); |
273 |
|
MethodId ids, ida, idr; |
274 |
25326 |
if (!getMethodIds(args, ids, ida, idr, 1)) |
275 |
|
{ |
276 |
|
return Node::null(); |
277 |
|
} |
278 |
50652 |
Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr); |
279 |
25326 |
if (res.isNull()) |
280 |
|
{ |
281 |
|
return Node::null(); |
282 |
|
} |
283 |
25326 |
return args[0].eqNode(res); |
284 |
|
} |
285 |
998207 |
else if (id == PfRule::MACRO_SR_PRED_INTRO) |
286 |
|
{ |
287 |
19276 |
Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " |
288 |
9638 |
<< args[0] << std::endl; |
289 |
9638 |
Assert(1 <= args.size() && args.size() <= 4); |
290 |
|
MethodId ids, ida, idr; |
291 |
9638 |
if (!getMethodIds(args, ids, ida, idr, 1)) |
292 |
|
{ |
293 |
|
return Node::null(); |
294 |
|
} |
295 |
19276 |
Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr); |
296 |
9638 |
if (res.isNull()) |
297 |
|
{ |
298 |
|
return Node::null(); |
299 |
|
} |
300 |
9638 |
Trace("builtin-pfcheck") << "Result is " << res << std::endl; |
301 |
19276 |
Trace("builtin-pfcheck") << "Witness form is " |
302 |
9638 |
<< SkolemManager::getOriginalForm(res) << std::endl; |
303 |
|
// **** NOTE: can rewrite the witness form here. This enables certain lemmas |
304 |
|
// to be provable, e.g. (= k t) where k is a purification Skolem for t. |
305 |
9638 |
res = Rewriter::rewrite(SkolemManager::getOriginalForm(res)); |
306 |
9638 |
if (!res.isConst() || !res.getConst<bool>()) |
307 |
|
{ |
308 |
4 |
Trace("builtin-pfcheck") |
309 |
2 |
<< "Failed to rewrite to true, res=" << res << std::endl; |
310 |
2 |
return Node::null(); |
311 |
|
} |
312 |
9636 |
Trace("builtin-pfcheck") << "...success" << std::endl; |
313 |
9636 |
return args[0]; |
314 |
|
} |
315 |
988569 |
else if (id == PfRule::MACRO_SR_PRED_ELIM) |
316 |
|
{ |
317 |
1272 |
Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " |
318 |
636 |
<< args.size() << std::endl; |
319 |
636 |
Assert(children.size() >= 1); |
320 |
636 |
Assert(args.size() <= 3); |
321 |
1272 |
std::vector<Node> exp; |
322 |
636 |
exp.insert(exp.end(), children.begin() + 1, children.end()); |
323 |
|
MethodId ids, ida, idr; |
324 |
636 |
if (!getMethodIds(args, ids, ida, idr, 0)) |
325 |
|
{ |
326 |
|
return Node::null(); |
327 |
|
} |
328 |
1272 |
Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr); |
329 |
636 |
Trace("builtin-pfcheck") << "Returned " << res1 << std::endl; |
330 |
636 |
return res1; |
331 |
|
} |
332 |
987933 |
else if (id == PfRule::MACRO_SR_PRED_TRANSFORM) |
333 |
|
{ |
334 |
1453274 |
Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " |
335 |
726637 |
<< args.size() << std::endl; |
336 |
726637 |
Assert(children.size() >= 1); |
337 |
726637 |
Assert(1 <= args.size() && args.size() <= 4); |
338 |
726637 |
Assert(args[0].getType().isBoolean()); |
339 |
|
MethodId ids, ida, idr; |
340 |
726637 |
if (!getMethodIds(args, ids, ida, idr, 1)) |
341 |
|
{ |
342 |
|
return Node::null(); |
343 |
|
} |
344 |
1453274 |
std::vector<Node> exp; |
345 |
726637 |
exp.insert(exp.end(), children.begin() + 1, children.end()); |
346 |
1453274 |
Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr); |
347 |
1453274 |
Node res2 = applySubstitutionRewrite(args[0], exp, ids, ida, idr); |
348 |
|
// if not already equal, do rewriting |
349 |
726637 |
if (res1 != res2) |
350 |
|
{ |
351 |
|
// can rewrite the witness forms |
352 |
798 |
res1 = Rewriter::rewrite(SkolemManager::getOriginalForm(res1)); |
353 |
798 |
res2 = Rewriter::rewrite(SkolemManager::getOriginalForm(res2)); |
354 |
798 |
if (res1.isNull() || res1 != res2) |
355 |
|
{ |
356 |
218 |
Trace("builtin-pfcheck") << "Failed to match results" << std::endl; |
357 |
218 |
Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl; |
358 |
218 |
return Node::null(); |
359 |
|
} |
360 |
|
} |
361 |
726419 |
return args[0]; |
362 |
|
} |
363 |
261296 |
else if (id == PfRule::REMOVE_TERM_FORMULA_AXIOM) |
364 |
|
{ |
365 |
3232 |
Assert(children.empty()); |
366 |
3232 |
Assert(args.size() == 1); |
367 |
3232 |
return RemoveTermFormulas::getAxiomFor(args[0]); |
368 |
|
} |
369 |
258064 |
else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA |
370 |
257182 |
|| id == PfRule::THEORY_PREPROCESS |
371 |
256176 |
|| id == PfRule::THEORY_PREPROCESS_LEMMA |
372 |
255957 |
|| id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM |
373 |
255944 |
|| id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE |
374 |
2896 |
|| id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS |
375 |
2896 |
|| id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ |
376 |
2843 |
|| id == PfRule::THEORY_INFERENCE) |
377 |
|
{ |
378 |
|
// "trusted" rules |
379 |
258064 |
Assert(!args.empty()); |
380 |
258064 |
Assert(args[0].getType().isBoolean()); |
381 |
258064 |
return args[0]; |
382 |
|
} |
383 |
|
|
384 |
|
// no rule |
385 |
|
return Node::null(); |
386 |
|
} |
387 |
|
|
388 |
109 |
bool BuiltinProofRuleChecker::getTheoryId(TNode n, TheoryId& tid) |
389 |
|
{ |
390 |
|
uint32_t i; |
391 |
109 |
if (!getUInt32(n, i)) |
392 |
|
{ |
393 |
|
return false; |
394 |
|
} |
395 |
109 |
tid = static_cast<TheoryId>(i); |
396 |
109 |
return true; |
397 |
|
} |
398 |
|
|
399 |
251235 |
Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid) |
400 |
|
{ |
401 |
|
return NodeManager::currentNM()->mkConst( |
402 |
251235 |
Rational(static_cast<uint32_t>(tid))); |
403 |
|
} |
404 |
|
|
405 |
|
} // namespace builtin |
406 |
|
} // namespace theory |
407 |
29574 |
} // namespace cvc5 |