1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Hanna Lachnitt, Haniel Barbosa |
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 |
|
* The module for processing proof nodes into Alethe proof nodes |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "proof/alethe/alethe_post_processor.h" |
17 |
|
|
18 |
|
#include "expr/node_algorithm.h" |
19 |
|
#include "proof/proof.h" |
20 |
|
#include "proof/proof_checker.h" |
21 |
|
#include "proof/proof_node_algorithm.h" |
22 |
|
#include "theory/builtin/proof_checker.h" |
23 |
|
#include "util/rational.h" |
24 |
|
|
25 |
|
namespace cvc5 { |
26 |
|
|
27 |
|
namespace proof { |
28 |
|
|
29 |
|
AletheProofPostprocessCallback::AletheProofPostprocessCallback( |
30 |
|
ProofNodeManager* pnm, AletheNodeConverter& anc) |
31 |
|
: d_pnm(pnm), d_anc(anc) |
32 |
|
{ |
33 |
|
NodeManager* nm = NodeManager::currentNM(); |
34 |
|
d_cl = nm->mkBoundVar("cl", nm->sExprType()); |
35 |
|
} |
36 |
|
|
37 |
|
bool AletheProofPostprocessCallback::shouldUpdate(std::shared_ptr<ProofNode> pn, |
38 |
|
const std::vector<Node>& fa, |
39 |
|
bool& continueUpdate) |
40 |
|
{ |
41 |
|
return pn->getRule() != PfRule::ALETHE_RULE; |
42 |
|
} |
43 |
|
|
44 |
|
bool AletheProofPostprocessCallback::update(Node res, |
45 |
|
PfRule id, |
46 |
|
const std::vector<Node>& children, |
47 |
|
const std::vector<Node>& args, |
48 |
|
CDProof* cdp, |
49 |
|
bool& continueUpdate) |
50 |
|
{ |
51 |
|
Trace("alethe-proof") << "- Alethe post process callback " << res << " " << id |
52 |
|
<< " " << children << " / " << args << std::endl; |
53 |
|
|
54 |
|
NodeManager* nm = NodeManager::currentNM(); |
55 |
|
std::vector<Node> new_args = std::vector<Node>(); |
56 |
|
|
57 |
|
switch (id) |
58 |
|
{ |
59 |
|
// To keep the original shape of the proof node it is necessary to rederive |
60 |
|
// the original conclusion. However, the term that should be printed might |
61 |
|
// be different from that conclusion. Thus, it is stored as an additional |
62 |
|
// argument in the proof node. Usually, the only difference is an additional |
63 |
|
// cl operator or the outmost or operator being replaced by a cl operator. |
64 |
|
// |
65 |
|
// When steps are added to the proof that have not been there previously, |
66 |
|
// it is unwise to add them in the same manner. To illustrate this the |
67 |
|
// following counterexample shows the pitfalls of this approach: |
68 |
|
// |
69 |
|
// (or a (or b c)) (not a) |
70 |
|
// --------------------------- RESOLUTION |
71 |
|
// (or b c) |
72 |
|
// |
73 |
|
// is converted into an Alethe proof that should be printed as |
74 |
|
// |
75 |
|
// (cl a (or b c)) (cl (not a)) |
76 |
|
// -------------------------------- RESOLUTION |
77 |
|
// (cl (or b c)) |
78 |
|
// --------------- OR |
79 |
|
// (cl b c) |
80 |
|
// |
81 |
|
// Here, (cl (or b c)) and (cl b c) cannot correspond to the same proof node |
82 |
|
// (or b c). Thus, we build a new proof node using the kind SEXPR |
83 |
|
// that is then printed as (cl (or b c)). We denote this wrapping of a proof |
84 |
|
// node by using an extra pair of parenthesis, i.e. ((or b c)) is the proof |
85 |
|
// node printed as (cl (or b c)). |
86 |
|
// |
87 |
|
// |
88 |
|
// Some proof rules have a close correspondence in Alethe. There are two |
89 |
|
// very frequent patterns that, to avoid repetition, are described here and |
90 |
|
// referred to in the comments on the specific proof rules below. |
91 |
|
// |
92 |
|
// The first pattern, which will be called singleton pattern in the |
93 |
|
// following, adds the original proof node F with the corresponding rule R' |
94 |
|
// of the Alethe calculus and uses the same premises as the original proof |
95 |
|
// node (P1:F1) ... (Pn:Fn). However, the conclusion is printed as (cl F). |
96 |
|
// |
97 |
|
// This means a cvc5 rule R that looks as follows: |
98 |
|
// |
99 |
|
// (P1:F1) ... (Pn:Fn) |
100 |
|
// --------------------- R |
101 |
|
// F |
102 |
|
// |
103 |
|
// is transformed into: |
104 |
|
// |
105 |
|
// (P1:F1) ... (Pn:Fn) |
106 |
|
// --------------------- R' |
107 |
|
// (cl F)* |
108 |
|
// |
109 |
|
// * the corresponding proof node is F |
110 |
|
// |
111 |
|
// The second pattern, which will be called clause pattern in the following, |
112 |
|
// has a disjunction (or G1 ... Gn) as conclusion. It also adds the orignal |
113 |
|
// proof node (or G1 ... Gn) with the corresponding rule R' of the Alethe |
114 |
|
// calculus and uses the same premises as the original proof node (P1:F1) |
115 |
|
// ... (Pn:Fn). However, the conclusion is printed as (cl G1 ... Gn), i.e. |
116 |
|
// the or is replaced by the cl operator. |
117 |
|
// |
118 |
|
// This means a cvc5 rule R that looks as follows: |
119 |
|
// |
120 |
|
// (P1:F1) ... (Pn:Fn) |
121 |
|
// --------------------- R |
122 |
|
// (or G1 ... Gn) |
123 |
|
// |
124 |
|
// Is transformed into: |
125 |
|
// |
126 |
|
// (P1:F1) ... (Pn:Fn) |
127 |
|
// --------------------- R' |
128 |
|
// (cl G1 ... Gn)* |
129 |
|
// |
130 |
|
// * the corresponding proof node is (or G1 ... Gn) |
131 |
|
// |
132 |
|
//================================================= Core rules |
133 |
|
//======================== Assume and Scope |
134 |
|
case PfRule::ASSUME: |
135 |
|
{ |
136 |
|
return addAletheStep(AletheRule::ASSUME, res, res, children, {}, *cdp); |
137 |
|
} |
138 |
|
// See proof_rule.h for documentation on the SCOPE rule. This comment uses |
139 |
|
// variable names as introduced there. Since the SCOPE rule originally |
140 |
|
// concludes |
141 |
|
// (=> (and F1 ... Fn) F) or (not (and F1 ... Fn)) but the ANCHOR rule |
142 |
|
// concludes (cl (not F1) ... (not Fn) F), to keep the original shape of the |
143 |
|
// proof node it is necessary to rederive the original conclusion. The |
144 |
|
// transformation is described below, depending on the form of SCOPE's |
145 |
|
// conclusion. |
146 |
|
// |
147 |
|
// Note that after the original conclusion is rederived the new proof node |
148 |
|
// will actually have to be printed, respectively, (cl (=> (and F1 ... Fn) |
149 |
|
// F)) or (cl (not (and F1 ... Fn))). |
150 |
|
// |
151 |
|
// Let (not (and F1 ... Fn))^i denote the repetition of (not (and F1 ... |
152 |
|
// Fn)) for i times. |
153 |
|
// |
154 |
|
// T1: |
155 |
|
// |
156 |
|
// P |
157 |
|
// ----- ANCHOR ------- ... ------- AND_POS |
158 |
|
// VP1 VP2_1 ... VP2_n |
159 |
|
// ------------------------------------ RESOLUTION |
160 |
|
// VP2a |
161 |
|
// ------------------------------------ REORDER |
162 |
|
// VP2b |
163 |
|
// ------ DUPLICATED_LITERALS ------- IMPLIES_NEG1 |
164 |
|
// VP3 VP4 |
165 |
|
// ------------------------------------ RESOLUTION ------- IMPLIES_NEG2 |
166 |
|
// VP5 VP6 |
167 |
|
// ----------------------------------------------------------- RESOLUTION |
168 |
|
// VP7 |
169 |
|
// |
170 |
|
// VP1: (cl (not F1) ... (not Fn) F) |
171 |
|
// VP2_i: (cl (not (and F1 ... Fn)) Fi), for i = 1 to n |
172 |
|
// VP2a: (cl F (not (and F1 ... Fn))^n) |
173 |
|
// VP2b: (cl (not (and F1 ... Fn))^n F) |
174 |
|
// VP3: (cl (not (and F1 ... Fn)) F) |
175 |
|
// VP4: (cl (=> (and F1 ... Fn) F) (and F1 ... Fn))) |
176 |
|
// VP5: (cl (=> (and F1 ... Fn) F) F) |
177 |
|
// VP6: (cl (=> (and F1 ... Fn) F) (not F)) |
178 |
|
// VP7: (cl (=> (and F1 ... Fn) F) (=> (and F1 ... Fn) F)) |
179 |
|
// |
180 |
|
// Note that if n = 1, then the ANCHOR step yields (cl (not F1) F), which is |
181 |
|
// the same as VP3. Since VP1 = VP3, the steps for that transformation are |
182 |
|
// not generated. |
183 |
|
// |
184 |
|
// |
185 |
|
// If F = false: |
186 |
|
// |
187 |
|
// --------- IMPLIES_SIMPLIFY |
188 |
|
// T1 VP9 |
189 |
|
// --------- DUPLICATED_LITERALS --------- EQUIV_1 |
190 |
|
// VP8 VP10 |
191 |
|
// -------------------------------------------- RESOLUTION |
192 |
|
// (cl (not (and F1 ... Fn)))* |
193 |
|
// |
194 |
|
// VP8: (cl (=> (and F1 ... Fn))) (cl (not (=> (and F1 ... Fn) false)) |
195 |
|
// (not (and F1 ... Fn))) |
196 |
|
// VP9: (cl (= (=> (and F1 ... Fn) false) (not (and F1 ... Fn)))) |
197 |
|
// |
198 |
|
// |
199 |
|
// Otherwise, |
200 |
|
// T1 |
201 |
|
// ------------------------------ DUPLICATED_LITERALS |
202 |
|
// (cl (=> (and F1 ... Fn) F))** |
203 |
|
// |
204 |
|
// |
205 |
|
// * the corresponding proof node is (not (and F1 ... Fn)) |
206 |
|
// ** the corresponding proof node is (=> (and F1 ... Fn) F) |
207 |
|
case PfRule::SCOPE: |
208 |
|
{ |
209 |
|
bool success = true; |
210 |
|
|
211 |
|
// Build vp1 |
212 |
|
std::vector<Node> negNode{d_cl}; |
213 |
|
std::vector<Node> sanitized_args; |
214 |
|
for (Node arg : args) |
215 |
|
{ |
216 |
|
negNode.push_back(arg.notNode()); // (not F1) ... (not Fn) |
217 |
|
sanitized_args.push_back(d_anc.convert(arg)); |
218 |
|
} |
219 |
|
negNode.push_back(children[0]); // (cl (not F1) ... (not Fn) F) |
220 |
|
Node vp1 = nm->mkNode(kind::SEXPR, negNode); |
221 |
|
success &= addAletheStep(AletheRule::ANCHOR_SUBPROOF, |
222 |
|
vp1, |
223 |
|
vp1, |
224 |
|
children, |
225 |
|
sanitized_args, |
226 |
|
*cdp); |
227 |
|
|
228 |
|
Node andNode, vp3; |
229 |
|
if (args.size() == 1) |
230 |
|
{ |
231 |
|
vp3 = vp1; |
232 |
|
andNode = args[0]; // F1 |
233 |
|
} |
234 |
|
else |
235 |
|
{ |
236 |
|
// Build vp2i |
237 |
|
andNode = nm->mkNode(kind::AND, args); // (and F1 ... Fn) |
238 |
|
std::vector<Node> premisesVP2 = {vp1}; |
239 |
|
std::vector<Node> notAnd = {d_cl, children[0]}; // cl F |
240 |
|
Node vp2_i; |
241 |
|
for (size_t i = 0, size = args.size(); i < size; i++) |
242 |
|
{ |
243 |
|
vp2_i = nm->mkNode(kind::SEXPR, d_cl, andNode.notNode(), args[i]); |
244 |
|
success &= |
245 |
|
addAletheStep(AletheRule::AND_POS, vp2_i, vp2_i, {}, {}, *cdp); |
246 |
|
premisesVP2.push_back(vp2_i); |
247 |
|
notAnd.push_back(andNode.notNode()); // cl F (not (and F1 ... Fn))^i |
248 |
|
} |
249 |
|
|
250 |
|
Node vp2a = nm->mkNode(kind::SEXPR, notAnd); |
251 |
|
success &= addAletheStep( |
252 |
|
AletheRule::RESOLUTION, vp2a, vp2a, premisesVP2, {}, *cdp); |
253 |
|
|
254 |
|
notAnd.erase(notAnd.begin() + 1); //(cl (not (and F1 ... Fn))^n) |
255 |
|
notAnd.push_back(children[0]); //(cl (not (and F1 ... Fn))^n F) |
256 |
|
Node vp2b = nm->mkNode(kind::SEXPR, notAnd); |
257 |
|
success &= |
258 |
|
addAletheStep(AletheRule::REORDER, vp2b, vp2b, {vp2a}, {}, *cdp); |
259 |
|
|
260 |
|
vp3 = nm->mkNode(kind::SEXPR, d_cl, andNode.notNode(), children[0]); |
261 |
|
success &= addAletheStep( |
262 |
|
AletheRule::DUPLICATED_LITERALS, vp3, vp3, {vp2b}, {}, *cdp); |
263 |
|
} |
264 |
|
|
265 |
|
Node vp8 = nm->mkNode( |
266 |
|
kind::SEXPR, d_cl, nm->mkNode(kind::IMPLIES, andNode, children[0])); |
267 |
|
|
268 |
|
Node vp4 = nm->mkNode(kind::SEXPR, d_cl, vp8[1], andNode); |
269 |
|
success &= |
270 |
|
addAletheStep(AletheRule::IMPLIES_NEG1, vp4, vp4, {}, {}, *cdp); |
271 |
|
|
272 |
|
Node vp5 = nm->mkNode(kind::SEXPR, d_cl, vp8[1], children[0]); |
273 |
|
success &= |
274 |
|
addAletheStep(AletheRule::RESOLUTION, vp5, vp5, {vp4, vp3}, {}, *cdp); |
275 |
|
|
276 |
|
Node vp6 = nm->mkNode(kind::SEXPR, d_cl, vp8[1], children[0].notNode()); |
277 |
|
success &= |
278 |
|
addAletheStep(AletheRule::IMPLIES_NEG2, vp6, vp6, {}, {}, *cdp); |
279 |
|
|
280 |
|
Node vp7 = nm->mkNode(kind::SEXPR, d_cl, vp8[1], vp8[1]); |
281 |
|
success &= |
282 |
|
addAletheStep(AletheRule::RESOLUTION, vp7, vp7, {vp5, vp6}, {}, *cdp); |
283 |
|
|
284 |
|
if (children[0] != nm->mkConst(false)) |
285 |
|
{ |
286 |
|
success &= addAletheStep( |
287 |
|
AletheRule::DUPLICATED_LITERALS, res, vp8, {vp7}, {}, *cdp); |
288 |
|
} |
289 |
|
else |
290 |
|
{ |
291 |
|
success &= addAletheStep( |
292 |
|
AletheRule::DUPLICATED_LITERALS, vp8, vp8, {vp7}, {}, *cdp); |
293 |
|
|
294 |
|
Node vp9 = |
295 |
|
nm->mkNode(kind::SEXPR, |
296 |
|
d_cl, |
297 |
|
nm->mkNode(kind::EQUAL, vp8[1], andNode.notNode())); |
298 |
|
|
299 |
|
success &= |
300 |
|
addAletheStep(AletheRule::IMPLIES_SIMPLIFY, vp9, vp9, {}, {}, *cdp); |
301 |
|
|
302 |
|
Node vp10 = |
303 |
|
nm->mkNode(kind::SEXPR, d_cl, vp8[1].notNode(), andNode.notNode()); |
304 |
|
success &= |
305 |
|
addAletheStep(AletheRule::EQUIV1, vp10, vp10, {vp9}, {}, *cdp); |
306 |
|
|
307 |
|
success &= addAletheStep(AletheRule::RESOLUTION, |
308 |
|
res, |
309 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
310 |
|
{vp8, vp10}, |
311 |
|
{}, |
312 |
|
*cdp); |
313 |
|
} |
314 |
|
|
315 |
|
return success; |
316 |
|
} |
317 |
|
// The rule is translated according to the theory id tid and the outermost |
318 |
|
// connective of the first term in the conclusion F, since F always has the |
319 |
|
// form (= t1 t2) where t1 is the term being rewritten. This is not an exact |
320 |
|
// translation but should work in most cases. |
321 |
|
// |
322 |
|
// E.g. if F is (= (* 0 d) 0) and tid = THEORY_ARITH, then prod_simplify |
323 |
|
// is correctly guessed as the rule. |
324 |
|
case PfRule::THEORY_REWRITE: |
325 |
|
{ |
326 |
|
AletheRule vrule = AletheRule::UNDEFINED; |
327 |
|
Node t = res[0]; |
328 |
|
|
329 |
|
theory::TheoryId tid; |
330 |
|
if (!theory::builtin::BuiltinProofRuleChecker::getTheoryId(args[1], tid)) |
331 |
|
{ |
332 |
|
return addAletheStep( |
333 |
|
vrule, res, nm->mkNode(kind::SEXPR, d_cl, res), children, {}, *cdp); |
334 |
|
} |
335 |
|
switch (tid) |
336 |
|
{ |
337 |
|
case theory::TheoryId::THEORY_BUILTIN: |
338 |
|
{ |
339 |
|
switch (t.getKind()) |
340 |
|
{ |
341 |
|
case kind::ITE: |
342 |
|
{ |
343 |
|
vrule = AletheRule::ITE_SIMPLIFY; |
344 |
|
break; |
345 |
|
} |
346 |
|
case kind::EQUAL: |
347 |
|
{ |
348 |
|
vrule = AletheRule::EQ_SIMPLIFY; |
349 |
|
break; |
350 |
|
} |
351 |
|
case kind::AND: |
352 |
|
{ |
353 |
|
vrule = AletheRule::AND_SIMPLIFY; |
354 |
|
break; |
355 |
|
} |
356 |
|
case kind::OR: |
357 |
|
{ |
358 |
|
vrule = AletheRule::OR_SIMPLIFY; |
359 |
|
break; |
360 |
|
} |
361 |
|
case kind::NOT: |
362 |
|
{ |
363 |
|
vrule = AletheRule::NOT_SIMPLIFY; |
364 |
|
break; |
365 |
|
} |
366 |
|
case kind::IMPLIES: |
367 |
|
{ |
368 |
|
vrule = AletheRule::IMPLIES_SIMPLIFY; |
369 |
|
break; |
370 |
|
} |
371 |
|
case kind::WITNESS: |
372 |
|
{ |
373 |
|
vrule = AletheRule::QNT_SIMPLIFY; |
374 |
|
break; |
375 |
|
} |
376 |
|
default: |
377 |
|
{ |
378 |
|
// In this case the rule is undefined |
379 |
|
} |
380 |
|
} |
381 |
|
break; |
382 |
|
} |
383 |
|
case theory::TheoryId::THEORY_BOOL: |
384 |
|
{ |
385 |
|
vrule = AletheRule::BOOL_SIMPLIFY; |
386 |
|
break; |
387 |
|
} |
388 |
|
case theory::TheoryId::THEORY_UF: |
389 |
|
{ |
390 |
|
if (t.getKind() == kind::EQUAL) |
391 |
|
{ |
392 |
|
// A lot of these seem to be symmetry rules but not all... |
393 |
|
vrule = AletheRule::EQUIV_SIMPLIFY; |
394 |
|
} |
395 |
|
break; |
396 |
|
} |
397 |
|
case theory::TheoryId::THEORY_ARITH: |
398 |
|
{ |
399 |
|
switch (t.getKind()) |
400 |
|
{ |
401 |
|
case kind::DIVISION: |
402 |
|
{ |
403 |
|
vrule = AletheRule::DIV_SIMPLIFY; |
404 |
|
break; |
405 |
|
} |
406 |
|
case kind::PRODUCT: |
407 |
|
{ |
408 |
|
vrule = AletheRule::PROD_SIMPLIFY; |
409 |
|
break; |
410 |
|
} |
411 |
|
case kind::MINUS: |
412 |
|
{ |
413 |
|
vrule = AletheRule::MINUS_SIMPLIFY; |
414 |
|
break; |
415 |
|
} |
416 |
|
case kind::UMINUS: |
417 |
|
{ |
418 |
|
vrule = AletheRule::UNARY_MINUS_SIMPLIFY; |
419 |
|
break; |
420 |
|
} |
421 |
|
case kind::PLUS: |
422 |
|
{ |
423 |
|
vrule = AletheRule::SUM_SIMPLIFY; |
424 |
|
break; |
425 |
|
} |
426 |
|
case kind::MULT: |
427 |
|
{ |
428 |
|
vrule = AletheRule::PROD_SIMPLIFY; |
429 |
|
break; |
430 |
|
} |
431 |
|
case kind::EQUAL: |
432 |
|
{ |
433 |
|
vrule = AletheRule::EQUIV_SIMPLIFY; |
434 |
|
break; |
435 |
|
} |
436 |
|
case kind::LT: |
437 |
|
{ |
438 |
|
[[fallthrough]]; |
439 |
|
} |
440 |
|
case kind::GT: |
441 |
|
{ |
442 |
|
[[fallthrough]]; |
443 |
|
} |
444 |
|
case kind::GEQ: |
445 |
|
{ |
446 |
|
[[fallthrough]]; |
447 |
|
} |
448 |
|
case kind::LEQ: |
449 |
|
{ |
450 |
|
vrule = AletheRule::COMP_SIMPLIFY; |
451 |
|
break; |
452 |
|
} |
453 |
|
case kind::CAST_TO_REAL: |
454 |
|
{ |
455 |
|
return addAletheStep(AletheRule::LA_GENERIC, |
456 |
|
res, |
457 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
458 |
|
children, |
459 |
|
{nm->mkConst(Rational(1))}, |
460 |
|
*cdp); |
461 |
|
} |
462 |
|
default: |
463 |
|
{ |
464 |
|
// In this case the rule is undefined |
465 |
|
} |
466 |
|
} |
467 |
|
break; |
468 |
|
} |
469 |
|
case theory::TheoryId::THEORY_QUANTIFIERS: |
470 |
|
{ |
471 |
|
vrule = AletheRule::QUANTIFIER_SIMPLIFY; |
472 |
|
break; |
473 |
|
} |
474 |
|
default: |
475 |
|
{ |
476 |
|
// In this case the rule is undefined |
477 |
|
}; |
478 |
|
} |
479 |
|
return addAletheStep( |
480 |
|
vrule, res, nm->mkNode(kind::SEXPR, d_cl, res), children, {}, *cdp); |
481 |
|
} |
482 |
|
// ======== Resolution and N-ary Resolution |
483 |
|
// See proof_rule.h for documentation on the RESOLUTION and CHAIN_RESOLUTION |
484 |
|
// rule. This comment uses variable names as introduced there. |
485 |
|
// |
486 |
|
// Because the RESOLUTION rule is merely a special case of CHAIN_RESOLUTION, |
487 |
|
// the same translation can be used for both. |
488 |
|
// |
489 |
|
// The main complication for the translation is that in the case the |
490 |
|
// conclusion C is (or G1 ... Gn), the result is ambigous. E.g., |
491 |
|
// |
492 |
|
// (cl F1 (or F2 F3)) (cl (not F1)) |
493 |
|
// -------------------------------------- RESOLUTION |
494 |
|
// (cl (or F2 F3)) |
495 |
|
// |
496 |
|
// (cl F1 F2 F3) (cl (not F1)) |
497 |
|
// -------------------------------------- RESOLUTION |
498 |
|
// (cl F2 F3) |
499 |
|
// |
500 |
|
// both (cl (or F2 F3)) and (cl F2 F3) correspond to the same proof node (or |
501 |
|
// F2 F3). One way to deal with this issue is for the translation to keep |
502 |
|
// track of the current clause generated after each resolution (the |
503 |
|
// resolvent) and then compare it to the result. E.g. in the first case |
504 |
|
// current_resolvent = {(or F2 F3)} indicates that the result is a singleton |
505 |
|
// clause, while in the second current_resolvent = {F2,F3}, indicating the |
506 |
|
// result is a non-singleton clause. |
507 |
|
// |
508 |
|
// It is always clear what clauses to add to current_resolvent, except for |
509 |
|
// when a child is an assumption or the result of an equality resolution |
510 |
|
// step. In these cases it might be necessary to add an additional or step. |
511 |
|
// |
512 |
|
// If for any Ci, rule(Ci) = ASSUME or rule(Ci) = EQ_RESOLVE and Ci = (or F1 |
513 |
|
// ... Fn) and Ci != L_{i-1} (for C1, C1 != L_1) then: |
514 |
|
// |
515 |
|
// (Pi:Ci) |
516 |
|
// ---------------------- OR |
517 |
|
// (VPi:(cl F1 ... Fn)) |
518 |
|
// |
519 |
|
// Otherwise VPi = Ci. |
520 |
|
// |
521 |
|
// However to determine whether C is a singleton clause or not it is not |
522 |
|
// necessary to calculate the complete current resolvent. Instead it |
523 |
|
// suffices to find the last introduction of the conclusion as a subterm of |
524 |
|
// a child and then check if it is eliminated by a later resolution step. If |
525 |
|
// the conclusion was not introduced as a subterm it has to be a |
526 |
|
// non-singleton clause. If it was introduced but not eliminated, it follows |
527 |
|
// that it is indeed not a singleton clause and should be printed as (cl F1 |
528 |
|
// ... Fn) instead of (cl (or F1 ... Fn)). |
529 |
|
// |
530 |
|
// This procedure is possible since the proof is already structured in a |
531 |
|
// certain way. It can never contain a second occurrence of a literal when |
532 |
|
// the first occurrence we found was eliminated from the proof. E.g., |
533 |
|
// |
534 |
|
// (cl (not (or a b))) (cl (or a b) (or a b)) |
535 |
|
// --------------------------------------------- |
536 |
|
// (cl (or a b)) |
537 |
|
// |
538 |
|
// is not possible because of the semantics of CHAIN_RESOLUTION, which only |
539 |
|
// removes one occurence of the resolvent in the resolving clauses. |
540 |
|
// |
541 |
|
// |
542 |
|
// If C = (or F1 ... Fn) is a non-singleton clause, then: |
543 |
|
// |
544 |
|
// VP1 ... VPn |
545 |
|
// ------------------ RESOLUTION |
546 |
|
// (cl F1 ... Fn)* |
547 |
|
// |
548 |
|
// Else if, C = false: |
549 |
|
// |
550 |
|
// VP1 ... VPn |
551 |
|
// ------------------ RESOLUTION |
552 |
|
// (cl)* |
553 |
|
// |
554 |
|
// Otherwise: |
555 |
|
// |
556 |
|
// VP1 ... VPn |
557 |
|
// ------------------ RESOLUTION |
558 |
|
// (cl C)* |
559 |
|
// |
560 |
|
// * the corresponding proof node is C |
561 |
|
case PfRule::RESOLUTION: |
562 |
|
case PfRule::CHAIN_RESOLUTION: |
563 |
|
{ |
564 |
|
Node trueNode = nm->mkConst(true); |
565 |
|
Node falseNode = nm->mkConst(false); |
566 |
|
std::vector<Node> new_children = children; |
567 |
|
|
568 |
|
// If a child F = (or F1 ... Fn) is the result of ASSUME or |
569 |
|
// EQ_RESOLVE it might be necessary to add an additional step with the |
570 |
|
// Alethe or rule since otherwise it will be used as (cl (or F1 ... Fn)). |
571 |
|
|
572 |
|
// The first child is used as a non-singleton clause if it is not equal |
573 |
|
// to its pivot L_1. Since it's the first clause in the resolution it can |
574 |
|
// only be equal to the pivot in the case the polarity is true. |
575 |
|
if (children[0].getKind() == kind::OR |
576 |
|
&& (args[0] != trueNode || children[0] != args[1])) |
577 |
|
{ |
578 |
|
std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[0]); |
579 |
|
if (childPf->getRule() == PfRule::ASSUME |
580 |
|
|| childPf->getRule() == PfRule::EQ_RESOLVE) |
581 |
|
{ |
582 |
|
// Add or step |
583 |
|
std::vector<Node> subterms{d_cl}; |
584 |
|
subterms.insert( |
585 |
|
subterms.end(), children[0].begin(), children[0].end()); |
586 |
|
Node conclusion = nm->mkNode(kind::SEXPR, subterms); |
587 |
|
addAletheStep( |
588 |
|
AletheRule::OR, conclusion, conclusion, {children[0]}, {}, *cdp); |
589 |
|
new_children[0] = conclusion; |
590 |
|
} |
591 |
|
} |
592 |
|
|
593 |
|
// For all other children C_i the procedure is similar. There is however a |
594 |
|
// key difference in the choice of the pivot element which is now the |
595 |
|
// L_{i-1}, i.e. the pivot of the child with the result of the i-1 |
596 |
|
// resolution steps between the children before it. Therefore, if the |
597 |
|
// policy id_{i-1} is true, the pivot has to appear negated in the child |
598 |
|
// in which case it should not be a (cl (or F1 ... Fn)) node. The same is |
599 |
|
// true if it isn't the pivot element. |
600 |
|
for (std::size_t i = 1, size = children.size(); i < size; ++i) |
601 |
|
{ |
602 |
|
if (children[i].getKind() == kind::OR |
603 |
|
&& (args[2 * (i - 1)] != falseNode |
604 |
|
|| args[2 * (i - 1) + 1] != children[i])) |
605 |
|
{ |
606 |
|
std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[i]); |
607 |
|
if (childPf->getRule() == PfRule::ASSUME |
608 |
|
|| childPf->getRule() == PfRule::EQ_RESOLVE) |
609 |
|
{ |
610 |
|
// Add or step |
611 |
|
std::vector<Node> lits{d_cl}; |
612 |
|
lits.insert(lits.end(), children[i].begin(), children[i].end()); |
613 |
|
Node conclusion = nm->mkNode(kind::SEXPR, lits); |
614 |
|
addAletheStep(AletheRule::OR, |
615 |
|
conclusion, |
616 |
|
conclusion, |
617 |
|
{children[i]}, |
618 |
|
{}, |
619 |
|
*cdp); |
620 |
|
new_children[i] = conclusion; |
621 |
|
} |
622 |
|
} |
623 |
|
} |
624 |
|
|
625 |
|
if (!expr::isSingletonClause(res, children, args)) |
626 |
|
{ |
627 |
|
return addAletheStepFromOr( |
628 |
|
AletheRule::RESOLUTION, res, new_children, {}, *cdp); |
629 |
|
} |
630 |
|
return addAletheStep(AletheRule::RESOLUTION, |
631 |
|
res, |
632 |
|
res == falseNode |
633 |
|
? nm->mkNode(kind::SEXPR, d_cl) |
634 |
|
: nm->mkNode(kind::SEXPR, d_cl, res), |
635 |
|
new_children, |
636 |
|
{}, |
637 |
|
*cdp); |
638 |
|
} |
639 |
|
// ======== Factoring |
640 |
|
// See proof_rule.h for documentation on the FACTORING rule. This comment |
641 |
|
// uses variable names as introduced there. |
642 |
|
// |
643 |
|
// If C2 = (or F1 ... Fn) but C1 != (or C2 ... C2), then VC2 = (cl F1 ... |
644 |
|
// Fn) Otherwise, VC2 = (cl C2). |
645 |
|
// |
646 |
|
// P |
647 |
|
// ------- DUPLICATED_LITERALS |
648 |
|
// VC2* |
649 |
|
// |
650 |
|
// * the corresponding proof node is C2 |
651 |
|
case PfRule::FACTORING: |
652 |
|
{ |
653 |
|
if (res.getKind() == kind::OR) |
654 |
|
{ |
655 |
|
for (const Node& child : children[0]) |
656 |
|
{ |
657 |
|
if (child != res) |
658 |
|
{ |
659 |
|
return addAletheStepFromOr( |
660 |
|
AletheRule::DUPLICATED_LITERALS, res, children, {}, *cdp); |
661 |
|
} |
662 |
|
} |
663 |
|
} |
664 |
|
return addAletheStep(AletheRule::DUPLICATED_LITERALS, |
665 |
|
res, |
666 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
667 |
|
children, |
668 |
|
{}, |
669 |
|
*cdp); |
670 |
|
} |
671 |
|
// ======== Split |
672 |
|
// See proof_rule.h for documentation on the SPLIT rule. This comment |
673 |
|
// uses variable names as introduced there. |
674 |
|
// |
675 |
|
// --------- NOT_NOT --------- NOT_NOT |
676 |
|
// VP1 VP2 |
677 |
|
// -------------------------------- RESOLUTION |
678 |
|
// (cl F (not F))* |
679 |
|
// |
680 |
|
// VP1: (cl (not (not (not F))) F) |
681 |
|
// VP2: (cl (not (not (not (not F)))) (not F)) |
682 |
|
// |
683 |
|
// * the corresponding proof node is (or F (not F)) |
684 |
|
case PfRule::SPLIT: |
685 |
|
{ |
686 |
|
Node vp1 = nm->mkNode( |
687 |
|
kind::SEXPR, d_cl, args[0].notNode().notNode().notNode(), args[0]); |
688 |
|
Node vp2 = nm->mkNode(kind::SEXPR, |
689 |
|
d_cl, |
690 |
|
args[0].notNode().notNode().notNode().notNode(), |
691 |
|
args[0].notNode()); |
692 |
|
|
693 |
|
return addAletheStep(AletheRule::NOT_NOT, vp2, vp2, {}, {}, *cdp) |
694 |
|
&& addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp) |
695 |
|
&& addAletheStepFromOr(AletheRule::RESOLUTION, res, {vp1, vp2}, {}, *cdp); |
696 |
|
} |
697 |
|
// ======== Equality resolution |
698 |
|
// See proof_rule.h for documentation on the EQ_RESOLVE rule. This |
699 |
|
// comment uses variable names as introduced there. |
700 |
|
// |
701 |
|
// If F1 = (or G1 ... Gn), then P1 will be printed as (cl G1 ... Gn) but |
702 |
|
// needs to be printed as (cl (or G1 ... Gn)). The only exception to this |
703 |
|
// are ASSUME steps that are always printed as (cl (or G1 ... Gn)) and |
704 |
|
// EQ_RESOLVE steps themselves. |
705 |
|
// |
706 |
|
// ------ ... ------ OR_NEG |
707 |
|
// P1 VP21 ... VP2n |
708 |
|
// ---------------------------- RESOLUTION |
709 |
|
// VP3 |
710 |
|
// ---------------------------- DUPLICATED_LITERALS |
711 |
|
// VP4 |
712 |
|
// |
713 |
|
// for i=1 to n, VP2i: (cl (or G1 ... Gn) (not Gi)) |
714 |
|
// VP3: (cl (or G1 ... Gn)^n) |
715 |
|
// VP4: (cl (or (G1 ... Gn)) |
716 |
|
// |
717 |
|
// Let child1 = VP4. |
718 |
|
// |
719 |
|
// |
720 |
|
// Otherwise, child1 = P1. |
721 |
|
// |
722 |
|
// |
723 |
|
// Then, if F2 = false: |
724 |
|
// |
725 |
|
// ------ EQUIV_POS2 |
726 |
|
// VP1 P2 child1 |
727 |
|
// --------------------------------- RESOLUTION |
728 |
|
// (cl)* |
729 |
|
// |
730 |
|
// Otherwise: |
731 |
|
// |
732 |
|
// ------ EQUIV_POS2 |
733 |
|
// VP1 P2 child1 |
734 |
|
// --------------------------------- RESOLUTION |
735 |
|
// (cl F2)* |
736 |
|
// |
737 |
|
// VP1: (cl (not (= F1 F2)) (not F1) F2) |
738 |
|
// |
739 |
|
// * the corresponding proof node is F2 |
740 |
|
case PfRule::EQ_RESOLVE: |
741 |
|
{ |
742 |
|
bool success = true; |
743 |
|
Node vp1 = |
744 |
|
nm->mkNode(kind::SEXPR, |
745 |
|
{d_cl, children[1].notNode(), children[0].notNode(), res}); |
746 |
|
Node child1 = children[0]; |
747 |
|
|
748 |
|
// Transform (cl F1 ... Fn) into (cl (or F1 ... Fn)) |
749 |
|
if (children[0].notNode() != children[1].notNode() |
750 |
|
&& children[0].getKind() == kind::OR) |
751 |
|
{ |
752 |
|
PfRule pr = cdp->getProofFor(child1)->getRule(); |
753 |
|
if (pr != PfRule::ASSUME && pr != PfRule::EQ_RESOLVE) |
754 |
|
{ |
755 |
|
std::vector<Node> clauses{d_cl}; |
756 |
|
clauses.insert(clauses.end(), |
757 |
|
children[0].begin(), |
758 |
|
children[0].end()); //(cl G1 ... Gn) |
759 |
|
|
760 |
|
std::vector<Node> vp2Nodes{children[0]}; |
761 |
|
std::vector<Node> resNodes{d_cl}; |
762 |
|
for (size_t i = 0, size = children[0].getNumChildren(); i < size; i++) |
763 |
|
{ |
764 |
|
Node vp2i = nm->mkNode( |
765 |
|
kind::SEXPR, |
766 |
|
d_cl, |
767 |
|
children[0], |
768 |
|
children[0][i].notNode()); //(cl (or G1 ... Gn) (not Gi)) |
769 |
|
success &= |
770 |
|
addAletheStep(AletheRule::OR_NEG, vp2i, vp2i, {}, {}, *cdp); |
771 |
|
vp2Nodes.push_back(vp2i); |
772 |
|
resNodes.push_back(children[0]); |
773 |
|
} |
774 |
|
Node vp3 = nm->mkNode(kind::SEXPR, resNodes); |
775 |
|
success &= addAletheStep( |
776 |
|
AletheRule::RESOLUTION, vp3, vp3, vp2Nodes, {}, *cdp); |
777 |
|
|
778 |
|
Node vp4 = nm->mkNode(kind::SEXPR, d_cl, children[0]); |
779 |
|
success &= addAletheStep( |
780 |
|
AletheRule::DUPLICATED_LITERALS, vp4, vp4, {vp3}, {}, *cdp); |
781 |
|
child1 = vp4; |
782 |
|
} |
783 |
|
} |
784 |
|
|
785 |
|
success &= addAletheStep(AletheRule::EQUIV_POS2, vp1, vp1, {}, {}, *cdp); |
786 |
|
|
787 |
|
return success &= addAletheStep(AletheRule::RESOLUTION, |
788 |
|
res, |
789 |
|
res == nm->mkConst(false) |
790 |
|
? nm->mkNode(kind::SEXPR, d_cl) |
791 |
|
: nm->mkNode(kind::SEXPR, d_cl, res), |
792 |
|
{vp1, children[1], child1}, |
793 |
|
{}, |
794 |
|
*cdp); |
795 |
|
} |
796 |
|
// ======== Modus ponens |
797 |
|
// See proof_rule.h for documentation on the MODUS_PONENS rule. This comment |
798 |
|
// uses variable names as introduced there. |
799 |
|
// |
800 |
|
// (P2:(=> F1 F2)) |
801 |
|
// ------------------------ IMPLIES |
802 |
|
// (VP1:(cl (not F1) F2)) (P1:F1) |
803 |
|
// -------------------------------------------- RESOLUTION |
804 |
|
// (cl F2)* |
805 |
|
// |
806 |
|
// * the corresponding proof node is F2 |
807 |
|
case PfRule::MODUS_PONENS: |
808 |
|
{ |
809 |
|
Node vp1 = nm->mkNode(kind::SEXPR, d_cl, children[0].notNode(), res); |
810 |
|
|
811 |
|
return addAletheStep( |
812 |
|
AletheRule::IMPLIES, vp1, vp1, {children[1]}, {}, *cdp) |
813 |
|
&& addAletheStep(AletheRule::RESOLUTION, |
814 |
|
res, |
815 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
816 |
|
{vp1, children[0]}, |
817 |
|
{}, |
818 |
|
*cdp); |
819 |
|
} |
820 |
|
// ======== Double negation elimination |
821 |
|
// See proof_rule.h for documentation on the NOT_NOT_ELIM rule. This comment |
822 |
|
// uses variable names as introduced there. |
823 |
|
// |
824 |
|
// ---------------------------------- NOT_NOT |
825 |
|
// (VP1:(cl (not (not (not F))) F)) (P:(not (not F))) |
826 |
|
// ------------------------------------------------------------- RESOLUTION |
827 |
|
// (cl F)* |
828 |
|
// |
829 |
|
// * the corresponding proof node is F |
830 |
|
case PfRule::NOT_NOT_ELIM: |
831 |
|
{ |
832 |
|
Node vp1 = nm->mkNode(kind::SEXPR, d_cl, children[0].notNode(), res); |
833 |
|
|
834 |
|
return addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp) |
835 |
|
&& addAletheStep(AletheRule::RESOLUTION, |
836 |
|
res, |
837 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
838 |
|
{vp1, children[0]}, |
839 |
|
{}, |
840 |
|
*cdp); |
841 |
|
} |
842 |
|
// ======== Contradiction |
843 |
|
// See proof_rule.h for documentation on the CONTRA rule. This |
844 |
|
// comment uses variable names as introduced there. |
845 |
|
// |
846 |
|
// P1 P2 |
847 |
|
// --------- RESOLUTION |
848 |
|
// (cl)* |
849 |
|
// |
850 |
|
// * the corresponding proof node is false |
851 |
|
case PfRule::CONTRA: |
852 |
|
{ |
853 |
|
return addAletheStep(AletheRule::RESOLUTION, |
854 |
|
res, |
855 |
|
nm->mkNode(kind::SEXPR, d_cl), |
856 |
|
children, |
857 |
|
{}, |
858 |
|
*cdp); |
859 |
|
} |
860 |
|
// ======== And elimination |
861 |
|
// This rule is translated according to the singleton pattern. |
862 |
|
case PfRule::AND_ELIM: |
863 |
|
{ |
864 |
|
return addAletheStep(AletheRule::AND, |
865 |
|
res, |
866 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
867 |
|
children, |
868 |
|
{}, |
869 |
|
*cdp); |
870 |
|
} |
871 |
|
// ======== And introduction |
872 |
|
// See proof_rule.h for documentation on the AND_INTRO rule. This |
873 |
|
// comment uses variable names as introduced there. |
874 |
|
// |
875 |
|
// |
876 |
|
// ----- AND_NEG |
877 |
|
// VP1 P1 ... Pn |
878 |
|
// -------------------------- RESOLUTION |
879 |
|
// (cl (and F1 ... Fn))* |
880 |
|
// |
881 |
|
// VP1:(cl (and F1 ... Fn) (not F1) ... (not Fn)) |
882 |
|
// |
883 |
|
// * the corresponding proof node is (and F1 ... Fn) |
884 |
|
case PfRule::AND_INTRO: |
885 |
|
{ |
886 |
|
std::vector<Node> neg_Nodes = {d_cl,res}; |
887 |
|
for (size_t i = 0, size = children.size(); i < size; i++) |
888 |
|
{ |
889 |
|
neg_Nodes.push_back(children[i].notNode()); |
890 |
|
} |
891 |
|
Node vp1 = nm->mkNode(kind::SEXPR, neg_Nodes); |
892 |
|
|
893 |
|
std::vector<Node> new_children = {vp1}; |
894 |
|
new_children.insert(new_children.end(), children.begin(), children.end()); |
895 |
|
|
896 |
|
return addAletheStep(AletheRule::AND_NEG, vp1, vp1, {}, {}, *cdp) |
897 |
|
&& addAletheStep(AletheRule::RESOLUTION, |
898 |
|
res, |
899 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
900 |
|
new_children, |
901 |
|
{}, |
902 |
|
*cdp); |
903 |
|
} |
904 |
|
// ======== Not Or elimination |
905 |
|
// This rule is translated according to the singleton pattern. |
906 |
|
case PfRule::NOT_OR_ELIM: |
907 |
|
{ |
908 |
|
return addAletheStep(AletheRule::NOT_OR, |
909 |
|
res, |
910 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
911 |
|
children, |
912 |
|
{}, |
913 |
|
*cdp); |
914 |
|
} |
915 |
|
// ======== Implication elimination |
916 |
|
// This rule is translated according to the clause pattern. |
917 |
|
case PfRule::IMPLIES_ELIM: |
918 |
|
{ |
919 |
|
return addAletheStepFromOr(AletheRule::IMPLIES, res, children, {}, *cdp); |
920 |
|
} |
921 |
|
// ======== Not Implication elimination version 1 |
922 |
|
// This rule is translated according to the singleton pattern. |
923 |
|
case PfRule::NOT_IMPLIES_ELIM1: |
924 |
|
{ |
925 |
|
return addAletheStep(AletheRule::NOT_IMPLIES1, |
926 |
|
res, |
927 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
928 |
|
children, |
929 |
|
{}, |
930 |
|
*cdp); |
931 |
|
} |
932 |
|
// ======== Not Implication elimination version 2 |
933 |
|
// This rule is translated according to the singleton pattern. |
934 |
|
case PfRule::NOT_IMPLIES_ELIM2: |
935 |
|
{ |
936 |
|
return addAletheStep(AletheRule::NOT_IMPLIES2, |
937 |
|
res, |
938 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
939 |
|
children, |
940 |
|
{}, |
941 |
|
*cdp); |
942 |
|
} |
943 |
|
// ======== Various elimination rules |
944 |
|
// The following rules are all translated according to the clause pattern. |
945 |
|
case PfRule::EQUIV_ELIM1: |
946 |
|
{ |
947 |
|
return addAletheStepFromOr(AletheRule::EQUIV1, res, children, {}, *cdp); |
948 |
|
} |
949 |
|
case PfRule::EQUIV_ELIM2: |
950 |
|
{ |
951 |
|
return addAletheStepFromOr(AletheRule::EQUIV2, res, children, {}, *cdp); |
952 |
|
} |
953 |
|
case PfRule::NOT_EQUIV_ELIM1: |
954 |
|
{ |
955 |
|
return addAletheStepFromOr( |
956 |
|
AletheRule::NOT_EQUIV1, res, children, {}, *cdp); |
957 |
|
} |
958 |
|
case PfRule::NOT_EQUIV_ELIM2: |
959 |
|
{ |
960 |
|
return addAletheStepFromOr( |
961 |
|
AletheRule::NOT_EQUIV2, res, children, {}, *cdp); |
962 |
|
} |
963 |
|
case PfRule::XOR_ELIM1: |
964 |
|
{ |
965 |
|
return addAletheStepFromOr(AletheRule::XOR1, res, children, {}, *cdp); |
966 |
|
} |
967 |
|
case PfRule::XOR_ELIM2: |
968 |
|
{ |
969 |
|
return addAletheStepFromOr(AletheRule::XOR2, res, children, {}, *cdp); |
970 |
|
} |
971 |
|
case PfRule::NOT_XOR_ELIM1: |
972 |
|
{ |
973 |
|
return addAletheStepFromOr(AletheRule::NOT_XOR1, res, children, {}, *cdp); |
974 |
|
} |
975 |
|
case PfRule::NOT_XOR_ELIM2: |
976 |
|
{ |
977 |
|
return addAletheStepFromOr(AletheRule::NOT_XOR2, res, children, {}, *cdp); |
978 |
|
} |
979 |
|
case PfRule::ITE_ELIM1: |
980 |
|
{ |
981 |
|
return addAletheStepFromOr(AletheRule::ITE2, res, children, {}, *cdp); |
982 |
|
} |
983 |
|
case PfRule::ITE_ELIM2: |
984 |
|
{ |
985 |
|
return addAletheStepFromOr(AletheRule::ITE1, res, children, {}, *cdp); |
986 |
|
} |
987 |
|
case PfRule::NOT_ITE_ELIM1: |
988 |
|
{ |
989 |
|
return addAletheStepFromOr(AletheRule::NOT_ITE2, res, children, {}, *cdp); |
990 |
|
} |
991 |
|
case PfRule::NOT_ITE_ELIM2: |
992 |
|
{ |
993 |
|
return addAletheStepFromOr(AletheRule::NOT_ITE1, res, children, {}, *cdp); |
994 |
|
} |
995 |
|
//================================================= De Morgan rules |
996 |
|
// ======== Not And |
997 |
|
// This rule is translated according to the clause pattern. |
998 |
|
case PfRule::NOT_AND: |
999 |
|
{ |
1000 |
|
return addAletheStepFromOr(AletheRule::NOT_AND, res, children, {}, *cdp); |
1001 |
|
} |
1002 |
|
|
1003 |
|
//================================================= CNF rules |
1004 |
|
// The following rules are all translated according to the clause pattern. |
1005 |
|
case PfRule::CNF_AND_POS: |
1006 |
|
{ |
1007 |
|
return addAletheStepFromOr(AletheRule::AND_POS, res, children, {}, *cdp); |
1008 |
|
} |
1009 |
|
case PfRule::CNF_AND_NEG: |
1010 |
|
{ |
1011 |
|
return addAletheStepFromOr(AletheRule::AND_NEG, res, children, {}, *cdp); |
1012 |
|
} |
1013 |
|
case PfRule::CNF_OR_POS: |
1014 |
|
{ |
1015 |
|
return addAletheStepFromOr(AletheRule::OR_POS, res, children, {}, *cdp); |
1016 |
|
} |
1017 |
|
case PfRule::CNF_OR_NEG: |
1018 |
|
{ |
1019 |
|
return addAletheStepFromOr(AletheRule::OR_NEG, res, children, {}, *cdp); |
1020 |
|
} |
1021 |
|
case PfRule::CNF_IMPLIES_POS: |
1022 |
|
{ |
1023 |
|
return addAletheStepFromOr( |
1024 |
|
AletheRule::IMPLIES_POS, res, children, {}, *cdp); |
1025 |
|
} |
1026 |
|
case PfRule::CNF_IMPLIES_NEG1: |
1027 |
|
{ |
1028 |
|
return addAletheStepFromOr( |
1029 |
|
AletheRule::IMPLIES_NEG1, res, children, {}, *cdp); |
1030 |
|
} |
1031 |
|
case PfRule::CNF_IMPLIES_NEG2: |
1032 |
|
{ |
1033 |
|
return addAletheStepFromOr( |
1034 |
|
AletheRule::IMPLIES_NEG2, res, children, {}, *cdp); |
1035 |
|
} |
1036 |
|
case PfRule::CNF_EQUIV_POS1: |
1037 |
|
{ |
1038 |
|
return addAletheStepFromOr( |
1039 |
|
AletheRule::EQUIV_POS2, res, children, {}, *cdp); |
1040 |
|
} |
1041 |
|
case PfRule::CNF_EQUIV_POS2: |
1042 |
|
{ |
1043 |
|
return addAletheStepFromOr( |
1044 |
|
AletheRule::EQUIV_POS1, res, children, {}, *cdp); |
1045 |
|
} |
1046 |
|
case PfRule::CNF_EQUIV_NEG1: |
1047 |
|
{ |
1048 |
|
return addAletheStepFromOr( |
1049 |
|
AletheRule::EQUIV_NEG2, res, children, {}, *cdp); |
1050 |
|
} |
1051 |
|
case PfRule::CNF_EQUIV_NEG2: |
1052 |
|
{ |
1053 |
|
return addAletheStepFromOr( |
1054 |
|
AletheRule::EQUIV_NEG1, res, children, {}, *cdp); |
1055 |
|
} |
1056 |
|
case PfRule::CNF_XOR_POS1: |
1057 |
|
{ |
1058 |
|
return addAletheStepFromOr(AletheRule::XOR_POS1, res, children, {}, *cdp); |
1059 |
|
} |
1060 |
|
case PfRule::CNF_XOR_POS2: |
1061 |
|
{ |
1062 |
|
return addAletheStepFromOr(AletheRule::XOR_POS2, res, children, {}, *cdp); |
1063 |
|
} |
1064 |
|
case PfRule::CNF_XOR_NEG1: |
1065 |
|
{ |
1066 |
|
return addAletheStepFromOr(AletheRule::XOR_NEG2, res, children, {}, *cdp); |
1067 |
|
} |
1068 |
|
case PfRule::CNF_XOR_NEG2: |
1069 |
|
{ |
1070 |
|
return addAletheStepFromOr(AletheRule::XOR_NEG1, res, children, {}, *cdp); |
1071 |
|
} |
1072 |
|
case PfRule::CNF_ITE_POS1: |
1073 |
|
{ |
1074 |
|
return addAletheStepFromOr(AletheRule::ITE_POS2, res, children, {}, *cdp); |
1075 |
|
} |
1076 |
|
case PfRule::CNF_ITE_POS2: |
1077 |
|
{ |
1078 |
|
return addAletheStepFromOr(AletheRule::ITE_POS1, res, children, {}, *cdp); |
1079 |
|
} |
1080 |
|
case PfRule::CNF_ITE_NEG1: |
1081 |
|
{ |
1082 |
|
return addAletheStepFromOr(AletheRule::ITE_NEG2, res, children, {}, *cdp); |
1083 |
|
} |
1084 |
|
case PfRule::CNF_ITE_NEG2: |
1085 |
|
{ |
1086 |
|
return addAletheStepFromOr(AletheRule::ITE_NEG1, res, children, {}, *cdp); |
1087 |
|
} |
1088 |
|
// ======== CNF ITE Pos version 3 |
1089 |
|
// |
1090 |
|
// ----- ITE_POS1 ----- ITE_POS2 |
1091 |
|
// VP1 VP2 |
1092 |
|
// ------------------------------- RESOLUTION |
1093 |
|
// VP3 |
1094 |
|
// ------------------------------- REORDER |
1095 |
|
// VP4 |
1096 |
|
// ------------------------------- DUPLICATED_LITERALS |
1097 |
|
// (cl (not (ite C F1 F2)) F1 F2) |
1098 |
|
// |
1099 |
|
// VP1: (cl (not (ite C F1 F2)) C F2) |
1100 |
|
// VP2: (cl (not (ite C F1 F2)) (not C) F1) |
1101 |
|
// VP3: (cl (not (ite C F1 F2)) F2 (not (ite C F1 F2)) F1) |
1102 |
|
// VP4: (cl (not (ite C F1 F2)) (not (ite C F1 F2)) F1 F2) |
1103 |
|
// |
1104 |
|
// * the corresponding proof node is (or (not (ite C F1 F2)) F1 F2) |
1105 |
|
case PfRule::CNF_ITE_POS3: |
1106 |
|
{ |
1107 |
|
Node vp1 = nm->mkNode(kind::SEXPR, {d_cl, res[0], args[0][0], res[2]}); |
1108 |
|
Node vp2 = |
1109 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], args[0][0].notNode(), res[1]}); |
1110 |
|
Node vp3 = |
1111 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], res[2], res[0], res[1]}); |
1112 |
|
Node vp4 = |
1113 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], res[0], res[1], res[2]}); |
1114 |
|
|
1115 |
|
return addAletheStep(AletheRule::ITE_POS1, vp1, vp1, {}, {}, *cdp) |
1116 |
|
&& addAletheStep(AletheRule::ITE_POS2, vp2, vp2, {}, {}, *cdp) |
1117 |
|
&& addAletheStep( |
1118 |
|
AletheRule::RESOLUTION, vp3, vp3, {vp1, vp2}, {}, *cdp) |
1119 |
|
&& addAletheStep(AletheRule::REORDER, vp4, vp4, {vp3}, {}, *cdp) |
1120 |
|
&& addAletheStepFromOr( |
1121 |
|
AletheRule::DUPLICATED_LITERALS, res, {vp4}, {}, *cdp); |
1122 |
|
} |
1123 |
|
// ======== CNF ITE Neg version 3 |
1124 |
|
// |
1125 |
|
// ----- ITE_NEG1 ----- ITE_NEG2 |
1126 |
|
// VP1 VP2 |
1127 |
|
// ------------------------------- RESOLUTION |
1128 |
|
// VP3 |
1129 |
|
// ------------------------------- REORDER |
1130 |
|
// VP4 |
1131 |
|
// ------------------------------- DUPLICATED_LITERALS |
1132 |
|
// (cl (ite C F1 F2) C (not F2)) |
1133 |
|
// |
1134 |
|
// VP1: (cl (ite C F1 F2) C (not F2)) |
1135 |
|
// VP2: (cl (ite C F1 F2) (not C) (not F1)) |
1136 |
|
// VP3: (cl (ite C F1 F2) (not F2) (ite C F1 F2) (not F1)) |
1137 |
|
// VP4: (cl (ite C F1 F2) (ite C F1 F2) (not F1) (not F2)) |
1138 |
|
// |
1139 |
|
// * the corresponding proof node is (or (ite C F1 F2) C (not F2)) |
1140 |
|
case PfRule::CNF_ITE_NEG3: |
1141 |
|
{ |
1142 |
|
Node vp1 = nm->mkNode(kind::SEXPR, {d_cl, res[0], args[0][0], res[2]}); |
1143 |
|
Node vp2 = |
1144 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], args[0][0].notNode(), res[1]}); |
1145 |
|
Node vp3 = |
1146 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], res[2], res[0], res[1]}); |
1147 |
|
Node vp4 = |
1148 |
|
nm->mkNode(kind::SEXPR, {d_cl, res[0], res[0], res[1], res[2]}); |
1149 |
|
|
1150 |
|
return addAletheStep(AletheRule::ITE_NEG1, vp1, vp1, {}, {}, *cdp) |
1151 |
|
&& addAletheStep(AletheRule::ITE_NEG2, vp2, vp2, {}, {}, *cdp) |
1152 |
|
&& addAletheStep( |
1153 |
|
AletheRule::RESOLUTION, vp3, vp3, {vp1, vp2}, {}, *cdp) |
1154 |
|
&& addAletheStep(AletheRule::REORDER, vp4, vp4, {vp3}, {}, *cdp) |
1155 |
|
&& addAletheStepFromOr( |
1156 |
|
AletheRule::DUPLICATED_LITERALS, res, {vp4}, {}, *cdp); |
1157 |
|
} |
1158 |
|
default: |
1159 |
|
{ |
1160 |
|
return addAletheStep(AletheRule::UNDEFINED, |
1161 |
|
res, |
1162 |
|
nm->mkNode(kind::SEXPR, d_cl, res), |
1163 |
|
children, |
1164 |
|
args, |
1165 |
|
*cdp); |
1166 |
|
} |
1167 |
|
} |
1168 |
|
} |
1169 |
|
|
1170 |
|
bool AletheProofPostprocessCallback::addAletheStep( |
1171 |
|
AletheRule rule, |
1172 |
|
Node res, |
1173 |
|
Node conclusion, |
1174 |
|
const std::vector<Node>& children, |
1175 |
|
const std::vector<Node>& args, |
1176 |
|
CDProof& cdp) |
1177 |
|
{ |
1178 |
|
// delete attributes |
1179 |
|
Node sanitized_conclusion = conclusion; |
1180 |
|
if (expr::hasClosure(conclusion)) |
1181 |
|
{ |
1182 |
|
sanitized_conclusion = d_anc.convert(conclusion); |
1183 |
|
} |
1184 |
|
|
1185 |
|
std::vector<Node> new_args = std::vector<Node>(); |
1186 |
|
new_args.push_back( |
1187 |
|
NodeManager::currentNM()->mkConst<Rational>(static_cast<unsigned>(rule))); |
1188 |
|
new_args.push_back(res); |
1189 |
|
new_args.push_back(sanitized_conclusion); |
1190 |
|
new_args.insert(new_args.end(), args.begin(), args.end()); |
1191 |
|
Trace("alethe-proof") << "... add Alethe step " << res << " / " << conclusion |
1192 |
|
<< " " << rule << " " << children << " / " << new_args |
1193 |
|
<< std::endl; |
1194 |
|
return cdp.addStep(res, PfRule::ALETHE_RULE, children, new_args); |
1195 |
|
} |
1196 |
|
|
1197 |
|
bool AletheProofPostprocessCallback::addAletheStepFromOr( |
1198 |
|
AletheRule rule, |
1199 |
|
Node res, |
1200 |
|
const std::vector<Node>& children, |
1201 |
|
const std::vector<Node>& args, |
1202 |
|
CDProof& cdp) |
1203 |
|
{ |
1204 |
|
std::vector<Node> subterms = {d_cl}; |
1205 |
|
subterms.insert(subterms.end(), res.begin(), res.end()); |
1206 |
|
Node conclusion = NodeManager::currentNM()->mkNode(kind::SEXPR, subterms); |
1207 |
|
return addAletheStep(rule, res, conclusion, children, args, cdp); |
1208 |
|
} |
1209 |
|
|
1210 |
|
AletheProofPostprocessFinalCallback::AletheProofPostprocessFinalCallback( |
1211 |
|
ProofNodeManager* pnm, AletheNodeConverter& anc) |
1212 |
|
: d_pnm(pnm), d_anc(anc) |
1213 |
|
{ |
1214 |
|
NodeManager* nm = NodeManager::currentNM(); |
1215 |
|
d_cl = nm->mkBoundVar("cl", nm->sExprType()); |
1216 |
|
} |
1217 |
|
|
1218 |
|
bool AletheProofPostprocessFinalCallback::shouldUpdate( |
1219 |
|
std::shared_ptr<ProofNode> pn, |
1220 |
|
const std::vector<Node>& fa, |
1221 |
|
bool& continueUpdate) |
1222 |
|
{ |
1223 |
|
return false; |
1224 |
|
} |
1225 |
|
|
1226 |
|
bool AletheProofPostprocessFinalCallback::update( |
1227 |
|
Node res, |
1228 |
|
PfRule id, |
1229 |
|
const std::vector<Node>& children, |
1230 |
|
const std::vector<Node>& args, |
1231 |
|
CDProof* cdp, |
1232 |
|
bool& continueUpdate) |
1233 |
|
{ |
1234 |
|
return true; |
1235 |
|
} |
1236 |
|
|
1237 |
|
AletheProofPostprocess::AletheProofPostprocess(ProofNodeManager* pnm, |
1238 |
|
AletheNodeConverter& anc) |
1239 |
|
: d_pnm(pnm), d_cb(d_pnm, anc), d_fcb(d_pnm, anc) |
1240 |
|
{ |
1241 |
|
} |
1242 |
|
|
1243 |
|
AletheProofPostprocess::~AletheProofPostprocess() {} |
1244 |
|
|
1245 |
|
void AletheProofPostprocess::process(std::shared_ptr<ProofNode> pf) {} |
1246 |
|
|
1247 |
|
} // namespace proof |
1248 |
|
|
1249 |
31137 |
} // namespace cvc5 |