Branch data Line data Source code
1 : : /******************************************************************************
2 : : * Top contributors (to current version):
3 : : * Haniel Barbosa, Hanna Lachnitt, Aina Niemetz
4 : : *
5 : : * This file is part of the cvc5 project.
6 : : *
7 : : * Copyright (c) 2009-2025 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 <sstream>
19 : :
20 : : #include "expr/node_algorithm.h"
21 : : #include "expr/skolem_manager.h"
22 : : #include "proof/alethe/alethe_post_processor_algorithm.h"
23 : : #include "proof/alethe/alethe_proof_rule.h"
24 : : #include "proof/proof.h"
25 : : #include "proof/proof_checker.h"
26 : : #include "proof/proof_node_algorithm.h"
27 : : #include "proof/proof_node_manager.h"
28 : : #include "proof/resolution_proofs_util.h"
29 : : #include "rewriter/rewrite_proof_rule.h"
30 : : #include "smt/env.h"
31 : : #include "theory/builtin/proof_checker.h"
32 : : #include "util/rational.h"
33 : :
34 : : using namespace cvc5::internal::kind;
35 : :
36 : : namespace cvc5::internal {
37 : :
38 : : namespace proof {
39 : :
40 : : std::unordered_map<Kind, AletheRule> s_bvKindToAletheRule = {
41 : : {Kind::BITVECTOR_COMP, AletheRule::BV_BITBLAST_STEP_BVCOMP},
42 : : {Kind::BITVECTOR_ULT, AletheRule::BV_BITBLAST_STEP_BVULT},
43 : : {Kind::BITVECTOR_ULE, AletheRule::BV_BITBLAST_STEP_BVULE},
44 : : {Kind::BITVECTOR_SLT, AletheRule::BV_BITBLAST_STEP_BVSLT},
45 : : {Kind::BITVECTOR_AND, AletheRule::BV_BITBLAST_STEP_BVAND},
46 : : {Kind::BITVECTOR_OR, AletheRule::BV_BITBLAST_STEP_BVOR},
47 : : {Kind::BITVECTOR_XOR, AletheRule::BV_BITBLAST_STEP_BVXOR},
48 : : {Kind::BITVECTOR_XNOR, AletheRule::BV_BITBLAST_STEP_BVXNOR},
49 : : {Kind::BITVECTOR_NOT, AletheRule::BV_BITBLAST_STEP_BVNOT},
50 : : {Kind::BITVECTOR_ADD, AletheRule::BV_BITBLAST_STEP_BVADD},
51 : : {Kind::BITVECTOR_NEG, AletheRule::BV_BITBLAST_STEP_BVNEG},
52 : : {Kind::BITVECTOR_MULT, AletheRule::BV_BITBLAST_STEP_BVMULT},
53 : : {Kind::BITVECTOR_CONCAT, AletheRule::BV_BITBLAST_STEP_CONCAT},
54 : : {Kind::CONST_BITVECTOR, AletheRule::BV_BITBLAST_STEP_CONST},
55 : : {Kind::BITVECTOR_EXTRACT, AletheRule::BV_BITBLAST_STEP_EXTRACT},
56 : : {Kind::BITVECTOR_SIGN_EXTEND, AletheRule::BV_BITBLAST_STEP_SIGN_EXTEND},
57 : : {Kind::EQUAL, AletheRule::BV_BITBLAST_STEP_BVEQUAL},
58 : : };
59 : :
60 : 2116 : AletheProofPostprocessCallback::AletheProofPostprocessCallback(
61 : 2116 : Env& env, AletheNodeConverter& anc, bool resPivots)
62 : 2116 : : EnvObj(env), d_anc(anc), d_resPivots(resPivots)
63 : : {
64 : 2116 : NodeManager* nm = nodeManager();
65 : 2116 : d_cl = NodeManager::mkBoundVar("cl", nm->sExprType());
66 : 2116 : d_true = nm->mkConst(true);
67 : 2116 : d_false = nm->mkConst(false);
68 : 2116 : }
69 : :
70 : 2661 : const std::string& AletheProofPostprocessCallback::getError()
71 : : {
72 : 2661 : return d_reasonForConversionFailure;
73 : : }
74 : :
75 : 3048880 : bool AletheProofPostprocessCallback::shouldUpdate(
76 : : std::shared_ptr<ProofNode> pn,
77 : : CVC5_UNUSED const std::vector<Node>& fa,
78 : : CVC5_UNUSED bool& continueUpdate)
79 : : {
80 : 3048880 : return d_reasonForConversionFailure.empty()
81 [ + + ][ + + ]: 3048880 : && pn->getRule() != ProofRule::ALETHE_RULE;
82 : : }
83 : :
84 : 2154140 : bool AletheProofPostprocessCallback::shouldUpdatePost(
85 : : std::shared_ptr<ProofNode> pn, CVC5_UNUSED const std::vector<Node>& fa)
86 : : {
87 [ + + ][ - + ]: 2154140 : if (!d_reasonForConversionFailure.empty() || pn->getArguments().empty())
[ + + ]
88 : : {
89 : 280768 : return false;
90 : : }
91 : 1873370 : AletheRule rule = getAletheRule(pn->getArguments()[0]);
92 [ + + ]: 1760510 : return rule == AletheRule::RESOLUTION_OR || rule == AletheRule::REORDERING
93 [ + + ][ + + ]: 3633890 : || rule == AletheRule::CONTRACTION;
94 : : }
95 : :
96 : 24 : bool AletheProofPostprocessCallback::updateTheoryRewriteProofRewriteRule(
97 : : Node res,
98 : : const std::vector<Node>& children,
99 : : CVC5_UNUSED const std::vector<Node>& args,
100 : : CDProof* cdp,
101 : : ProofRewriteRule di)
102 : : {
103 : 24 : NodeManager* nm = nodeManager();
104 : 48 : std::vector<Node> new_args = std::vector<Node>();
105 [ + - ][ + - ]: 24 : switch (di)
[ - - ][ - - ]
[ - + ]
106 : : {
107 : : // ======== DISTINCT_ELIM
108 : : // ======== DISTINCT_CARD_CONFLICT
109 : : // Both rules are translated according to the clauses pattern to
110 : : // distinct_elim. The only exception to this is when DISTINCT_ELIM results
111 : : // in a term with exactly two boolean arguments. The Alethe distinct_elim
112 : : // rule has a special handling in this case and rewrites the distinct term
113 : : // to False directly (as the DISTINCT_CARD_CONFLICT rule does in cvc5).
114 : : // Instead, we output a RARE_REWRITE step using the distinct_two_bool_elim
115 : : // rule.
116 : : //
117 : : // (define-rule distinct_bin_bool_elim ((t1 Bool) (t2 Bool))
118 : : // (distinct t1 t2)
119 : : // (not (= t1 t2)))
120 : 1 : case ProofRewriteRule::DISTINCT_ELIM:
121 : : case ProofRewriteRule::DISTINCT_CARD_CONFLICT:
122 : : {
123 : 2 : Node eq = res[0];
124 : 2 : Node t1 = eq[0];
125 [ - + ][ - + ]: 2 : if (di == ProofRewriteRule::DISTINCT_ELIM && t1.getType().isBoolean()
[ - - ]
126 [ + - ][ - - ]: 2 : && eq.getNumChildren() == 2)
[ + - ]
127 : : {
128 : 0 : return addAletheStep(
129 : : AletheRule::RARE_REWRITE,
130 : : res,
131 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
132 : : {},
133 : 0 : {nm->mkRawSymbol("\"distinct_bin_bool_elim\"", nm->sExprType()),
134 : : t1,
135 : : eq[1]},
136 : 0 : *cdp);
137 : : }
138 : 2 : return addAletheStep(AletheRule::DISTINCT_ELIM,
139 : : res,
140 : 2 : nm->mkNode(Kind::SEXPR, d_cl, res),
141 : : children,
142 : : new_args,
143 : 1 : *cdp);
144 : : }
145 : : // ======== EXISTS_ELIM
146 : : // This rule is translated according to the clause pattern.
147 : 0 : case ProofRewriteRule::EXISTS_ELIM:
148 : : {
149 : 0 : return addAletheStep(AletheRule::CONNECTIVE_DEF,
150 : : res,
151 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
152 : : {},
153 : : {},
154 : 0 : *cdp);
155 : : }
156 : : // ======== QUANT_MERGE_PRENEX
157 : : // This rule is translated according to the clause pattern.
158 : 1 : case ProofRewriteRule::QUANT_MERGE_PRENEX:
159 : : {
160 : 4 : return addAletheStep(AletheRule::QNT_JOIN,
161 : : res,
162 : 2 : nm->mkNode(Kind::SEXPR, d_cl, res),
163 : : {},
164 : : {},
165 : 1 : *cdp);
166 : : }
167 : : // ======== QUANT_MINISCOPE_AND
168 : : // This rule is translated according to the clause pattern.
169 : 0 : case ProofRewriteRule::QUANT_MINISCOPE_AND:
170 : : {
171 : 0 : return addAletheStep(AletheRule::MINISCOPE_DISTRIBUTE,
172 : : res,
173 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
174 : : {},
175 : : {},
176 : 0 : *cdp);
177 : : }
178 : : // ======== QUANT_MINISCOPE_OR
179 : : // This rule is translated according to the clause pattern.
180 : 0 : case ProofRewriteRule::QUANT_MINISCOPE_OR:
181 : : {
182 : 0 : return addAletheStep(AletheRule::MINISCOPE_SPLIT,
183 : : res,
184 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
185 : : {},
186 : : {},
187 : 0 : *cdp);
188 : : }
189 : : // ======== QUANT_MINISCOPE_ITE
190 : : // This rule is translated according to the clause pattern.
191 : 0 : case ProofRewriteRule::QUANT_MINISCOPE_ITE:
192 : : {
193 : 0 : return addAletheStep(AletheRule::MINISCOPE_ITE,
194 : : res,
195 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
196 : : {},
197 : : {},
198 : 0 : *cdp);
199 : : }
200 : : // ======== QUANT_UNUSED_VARS
201 : : // This rule is translated according to the clause pattern.
202 : 0 : case ProofRewriteRule::QUANT_UNUSED_VARS:
203 : : {
204 : 0 : return addAletheStep(AletheRule::QNT_RM_UNUSED,
205 : : res,
206 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
207 : : {},
208 : : {},
209 : 0 : *cdp);
210 : : }
211 : : // ======== BV_BITWISE_SLICING
212 : : // This rule is translated according to the clause pattern.
213 : 0 : case ProofRewriteRule::BV_BITWISE_SLICING:
214 : : {
215 : 0 : return addAletheStep(AletheRule::BV_BITWISE_SLICING,
216 : : res,
217 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
218 : : children,
219 : : {},
220 : 0 : *cdp);
221 : : }
222 : : // ======== BV_REPEAT_ELIM
223 : : // This rule is translated according to the clause pattern.
224 : 0 : case ProofRewriteRule::BV_REPEAT_ELIM:
225 : : {
226 : 0 : return addAletheStep(AletheRule::BV_REPEAT_ELIM,
227 : : res,
228 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
229 : : children,
230 : : {},
231 : 0 : *cdp);
232 : : }
233 : 22 : default: break;
234 : : }
235 : 22 : return false;
236 : : }
237 : :
238 : 1146720 : bool AletheProofPostprocessCallback::update(Node res,
239 : : ProofRule id,
240 : : const std::vector<Node>& children,
241 : : const std::vector<Node>& args,
242 : : CDProof* cdp,
243 : : CVC5_UNUSED bool& continueUpdate)
244 : : {
245 [ + - ]: 2293450 : Trace("alethe-proof") << "...Alethe pre-update " << res << " " << id << " "
246 : 1146720 : << children << " / " << args << std::endl;
247 : :
248 : 1146720 : NodeManager* nm = nodeManager();
249 : 2293450 : std::vector<Node> new_args = std::vector<Node>();
250 : :
251 : : // See proof_rule.h for documentation on the proof rules translated below. Any
252 : : // comment might use variable names as introduced there.
253 [ + + ][ - + ]: 1146720 : switch (id)
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ][ - + ]
[ + - ][ - + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
254 : : {
255 : : // To keep the original shape of the proof node it is necessary to rederive
256 : : // the original conclusion. However, the term that should be printed might
257 : : // be different from that conclusion. Thus, it is stored as an additional
258 : : // argument in the proof node. Usually, the only difference is an additional
259 : : // cl operator or the outmost or operator being replaced by a cl operator.
260 : : //
261 : : // When steps are added to the proof that have not been there previously,
262 : : // it is unwise to add them in the same manner. To illustrate this the
263 : : // following counterexample shows the pitfalls of this approach:
264 : : //
265 : : // (or a (or b c)) (not a)
266 : : // --------------------------- RESOLUTION
267 : : // (or b c)
268 : : //
269 : : // is converted into an Alethe proof that should be printed as
270 : : //
271 : : // (cl a (or b c)) (cl (not a))
272 : : // -------------------------------- RESOLUTION
273 : : // (cl (or b c))
274 : : // --------------- OR
275 : : // (cl b c)
276 : : //
277 : : // Here, (cl (or b c)) and (cl b c) cannot correspond to the same proof node
278 : : // (or b c). Thus, we build a new proof node using the kind SEXPR
279 : : // that is then printed as (cl (or b c)).
280 : : //
281 : : // Adding an OR node to a premises will take place in the finalize function
282 : : // where in the case that a step is printed as (cl (or F1 ... Fn)) but used
283 : : // as (cl F1 ... Fn) an OR step is added to transform it to this very thing.
284 : : // This is necessary for rules that work on clauses, i.e. RESOLUTION,
285 : : // CHAIN_RESOLUTION, REORDERING and FACTORING.
286 : : //
287 : : // Some proof rules have a close correspondence in Alethe. There are two
288 : : // very frequent patterns that, to avoid repetition, are described here and
289 : : // referred to in the comments on the specific proof rules below.
290 : : //
291 : : // The first pattern, which will be called singleton pattern in the
292 : : // following, adds the original proof node F with the corresponding rule R'
293 : : // of the Alethe calculus and uses the same premises as the original proof
294 : : // node (P1:F1) ... (Pn:Fn). However, the conclusion is printed as (cl F).
295 : : //
296 : : // This means a cvc5 rule R that looks as follows:
297 : : //
298 : : // (P1:F1) ... (Pn:Fn)
299 : : // --------------------- R
300 : : // F
301 : : //
302 : : // is transformed into:
303 : : //
304 : : // (P1:F1) ... (Pn:Fn)
305 : : // --------------------- R'
306 : : // (cl F)*
307 : : //
308 : : // * the corresponding proof node is F
309 : : //
310 : : // The second pattern, which will be called clause pattern in the following,
311 : : // has a disjunction (or G1 ... Gn) as conclusion. It also adds the orignal
312 : : // proof node (or G1 ... Gn) with the corresponding rule R' of the Alethe
313 : : // calculus and uses the same premises as the original proof node (P1:F1)
314 : : // ... (Pn:Fn). However, the conclusion is printed as (cl G1 ... Gn), i.e.
315 : : // the or is replaced by the cl operator.
316 : : //
317 : : // This means a cvc5 rule R that looks as follows:
318 : : //
319 : : // (P1:F1) ... (Pn:Fn)
320 : : // --------------------- R
321 : : // (or G1 ... Gn)
322 : : //
323 : : // Is transformed into:
324 : : //
325 : : // (P1:F1) ... (Pn:Fn)
326 : : // --------------------- R'
327 : : // (cl G1 ... Gn)*
328 : : //
329 : : // * the corresponding proof node is (or G1 ... Gn)
330 : : //
331 : : // The documentation for each translation below will use variable names as
332 : : // defined in the original documentation of the rules in proof_rule.h.
333 : : //================================================= Core rules
334 : : //======================== Assume and Scope
335 : : // nothing happens
336 : 128756 : case ProofRule::ASSUME:
337 : : {
338 : 128756 : return false;
339 : : }
340 : : // The SCOPE rule is translated into Alethe using the "subproof" rule. The
341 : : // conclusion is either (=> (and F1 ... Fn) F) or (not (and F1 ... Fn)), so
342 : : // it must be converted into (cl (not F1) ... (not Fn) F), and extra steps
343 : : // must be added to derive the original conclusion, which is the one to be
344 : : // used in the steps depending on this one.
345 : : //
346 : : // The following transformation is applied. Let (not (and F1 ... Fn))^i
347 : : // denote the repetition of (not (and F1 ... Fn)) for i times.
348 : : //
349 : : // T1:
350 : : //
351 : : // -------------------------------- anchor
352 : : // ---- assume ---- assume
353 : : // F1 ... Fn
354 : : // ...
355 : : // -----
356 : : // F
357 : : // ----- subproof ------- ... ------- and_pos
358 : : // VP1 VP2_1 ... VP2_n
359 : : // ------------------------------------ resolution
360 : : // VP2a
361 : : // ------------------------------------ reordering
362 : : // VP2b
363 : : // ------ contraction ------- implies_neg1
364 : : // VP3 VP4
365 : : // ------------------------------------ resolution ------- implies_neg2
366 : : // VP5 VP6
367 : : // ----------------------------------------------------------- resolution
368 : : // VP7
369 : : //
370 : : // VP1: (cl (not F1) ... (not Fn) F)
371 : : // VP2_i: (cl (not (and F1 ... Fn)) Fi), for i = 1 to n
372 : : // VP2a: (cl F (not (and F1 ... Fn))^n)
373 : : // VP2b: (cl (not (and F1 ... Fn))^n F)
374 : : // VP3: (cl (not (and F1 ... Fn)) F)
375 : : // VP4: (cl (=> (and F1 ... Fn) F) (and F1 ... Fn)))
376 : : // VP5: (cl (=> (and F1 ... Fn) F) F)
377 : : // VP6: (cl (=> (and F1 ... Fn) F) (not F))
378 : : // VP7: (cl (=> (and F1 ... Fn) F) (=> (and F1 ... Fn) F))
379 : : //
380 : : // Note that if n = 1, then the "subproof" step yields (cl (not F1) F),
381 : : // which is the same as VP3. Since VP1 = VP3, the steps for that
382 : : // transformation are not generated.
383 : : //
384 : : //
385 : : // If F = false:
386 : : //
387 : : // --------- implies_simplify
388 : : // T1 VP9
389 : : // --------- contraction --------- equiv_1
390 : : // VP8 VP10
391 : : // -------------------------------------------- resolution
392 : : // (cl (not (and F1 ... Fn)))*
393 : : //
394 : : // VP8: (cl (=> (and F1 ... Fn) false))
395 : : // VP9: (cl (= (=> (and F1 ... Fn) false) (not (and F1 ... Fn))))
396 : : // VP10: (cl (not (=> (and F1 ... Fn) false)) (not (and F1 ... Fn)))
397 : : //
398 : : // Otherwise,
399 : : // T1
400 : : // ------------------------------ contraction
401 : : // (cl (=> (and F1 ... Fn) F))**
402 : : //
403 : : //
404 : : // * the corresponding proof node is (not (and F1 ... Fn))
405 : : // ** the corresponding proof node is (=> (and F1 ... Fn) F)
406 : 34416 : case ProofRule::SCOPE:
407 : : {
408 : 34416 : bool success = true;
409 : :
410 : : // Build vp1
411 : 137664 : std::vector<Node> negNode{d_cl};
412 [ + + ]: 217894 : for (const Node& arg : args)
413 : : {
414 : 183478 : negNode.push_back(arg.notNode()); // (not F1) ... (not Fn)
415 : : }
416 : 34416 : negNode.push_back(children[0]); // (cl (not F1) ... (not Fn) F)
417 : 68832 : Node vp1 = nm->mkNode(Kind::SEXPR, negNode);
418 : 68832 : success &= addAletheStep(
419 : 34416 : AletheRule::ANCHOR_SUBPROOF, vp1, vp1, children, args, *cdp);
420 : :
421 : 68832 : Node andNode, vp3;
422 [ + + ]: 34416 : if (args.size() == 1)
423 : : {
424 : 7179 : vp3 = vp1;
425 : 7179 : andNode = args[0]; // F1
426 : : }
427 : : else
428 : : {
429 : : // Build vp2i
430 : 27237 : andNode = nm->mkNode(Kind::AND, args); // (and F1 ... Fn)
431 : 108948 : std::vector<Node> premisesVP2 = {vp1};
432 : 136185 : std::vector<Node> notAnd = {d_cl, children[0]}; // cl F
433 : 54474 : Node vp2_i;
434 [ + + ]: 203536 : for (size_t i = 0, size = args.size(); i < size; i++)
435 : : {
436 : 176299 : vp2_i = nm->mkNode(Kind::SEXPR, d_cl, andNode.notNode(), args[i]);
437 : 352598 : success &= addAletheStep(AletheRule::AND_POS,
438 : : vp2_i,
439 : : vp2_i,
440 : : {},
441 : 528897 : std::vector<Node>{nm->mkConstInt(i)},
442 : 176299 : *cdp);
443 : 176299 : premisesVP2.push_back(vp2_i);
444 : 176299 : notAnd.push_back(andNode.notNode()); // cl F (not (and F1 ... Fn))^i
445 : : }
446 : :
447 : 54474 : Node vp2a = nm->mkNode(Kind::SEXPR, notAnd);
448 [ + + ]: 27237 : if (d_resPivots)
449 : : {
450 : 10 : std::vector<Node> newArgs;
451 [ + + ]: 43 : for (const Node& arg : args)
452 : : {
453 : 33 : newArgs.push_back(arg);
454 : 33 : newArgs.push_back(d_false);
455 : : }
456 : 20 : success &= addAletheStep(
457 : 10 : AletheRule::RESOLUTION, vp2a, vp2a, premisesVP2, newArgs, *cdp);
458 : : }
459 : : else
460 : : {
461 : 54454 : success &= addAletheStep(AletheRule::RESOLUTION,
462 : : vp2a,
463 : : vp2a,
464 : : premisesVP2,
465 : 54454 : std::vector<Node>(),
466 : 27227 : *cdp);
467 : : }
468 : :
469 : 27237 : notAnd.erase(notAnd.begin() + 1); //(cl (not (and F1 ... Fn))^n)
470 : 27237 : notAnd.push_back(children[0]); //(cl (not (and F1 ... Fn))^n F)
471 : 27237 : Node vp2b = nm->mkNode(Kind::SEXPR, notAnd);
472 : 27237 : success &=
473 : 54474 : addAletheStep(AletheRule::REORDERING, vp2b, vp2b, {vp2a}, {}, *cdp);
474 : :
475 : 27237 : vp3 = nm->mkNode(Kind::SEXPR, d_cl, andNode.notNode(), children[0]);
476 : 27237 : success &=
477 : 54474 : addAletheStep(AletheRule::CONTRACTION, vp3, vp3, {vp2b}, {}, *cdp);
478 : : }
479 : :
480 : : Node vp8 = nm->mkNode(
481 : 103248 : Kind::SEXPR, d_cl, nm->mkNode(Kind::IMPLIES, andNode, children[0]));
482 : :
483 : 103248 : Node vp4 = nm->mkNode(Kind::SEXPR, d_cl, vp8[1], andNode);
484 : 34416 : success &=
485 : 34416 : addAletheStep(AletheRule::IMPLIES_NEG1, vp4, vp4, {}, {}, *cdp);
486 : :
487 : 103248 : Node vp5 = nm->mkNode(Kind::SEXPR, d_cl, vp8[1], children[0]);
488 [ + + ][ - - ]: 137664 : success &= addAletheStep(AletheRule::RESOLUTION,
489 : : vp5,
490 : : vp5,
491 : : {vp4, vp3},
492 : 68854 : d_resPivots ? std::vector<Node>{andNode, d_true}
493 : : : std::vector<Node>(),
494 : 103248 : *cdp);
495 : :
496 : 103248 : Node vp6 = nm->mkNode(Kind::SEXPR, d_cl, vp8[1], children[0].notNode());
497 : 34416 : success &=
498 : 34416 : addAletheStep(AletheRule::IMPLIES_NEG2, vp6, vp6, {}, {}, *cdp);
499 : :
500 : 68832 : Node vp7 = nm->mkNode(Kind::SEXPR, d_cl, vp8[1], vp8[1]);
501 : 34416 : success &=
502 [ + + ][ - - ]: 137664 : addAletheStep(AletheRule::RESOLUTION,
503 : : vp7,
504 : : vp7,
505 : : {vp5, vp6},
506 : 68854 : d_resPivots ? std::vector<Node>{children[0], d_true}
507 : : : std::vector<Node>(),
508 : 103248 : *cdp);
509 : :
510 [ + + ]: 34416 : if (children[0] != d_false)
511 : : {
512 : 25366 : success &=
513 : 50732 : addAletheStep(AletheRule::CONTRACTION, res, vp8, {vp7}, {}, *cdp);
514 : : }
515 : : else
516 : : {
517 : 9050 : success &=
518 : 18100 : addAletheStep(AletheRule::CONTRACTION, vp8, vp8, {vp7}, {}, *cdp);
519 : :
520 : : Node vp9 =
521 : : nm->mkNode(Kind::SEXPR,
522 : 9050 : d_cl,
523 : 27150 : nm->mkNode(Kind::EQUAL, vp8[1], andNode.notNode()));
524 : 9050 : success &=
525 : 9050 : addAletheStep(AletheRule::IMPLIES_SIMPLIFY, vp9, vp9, {}, {}, *cdp);
526 : :
527 : : Node vp10 =
528 : 18100 : nm->mkNode(Kind::SEXPR, d_cl, vp8[1].notNode(), andNode.notNode());
529 : 9050 : success &=
530 : 18100 : addAletheStep(AletheRule::EQUIV1, vp10, vp10, {vp9}, {}, *cdp);
531 : :
532 [ + + ][ - - ]: 45250 : success &= addAletheStep(AletheRule::RESOLUTION,
533 : : res,
534 : 18100 : nm->mkNode(Kind::SEXPR, d_cl, res),
535 : : {vp8, vp10},
536 : 18100 : d_resPivots ? std::vector<Node>{vp8[1], d_true}
537 : : : std::vector<Node>(),
538 : 27150 : *cdp);
539 : : }
540 : :
541 : 34416 : return success;
542 : : }
543 : : // ======== Absorb
544 : : //
545 : : // ------- ac_simp ------- <op>_simplify
546 : : // VP1 VP2
547 : : // ------------------------- trans
548 : : // res
549 : : //
550 : : // VP1: (cl (= t tf))
551 : : // VP2: (cl (= tf z))
552 : : //
553 : : // where tf = applyAcSimp(t) and <op> is the top-level operator of t. Note
554 : : // that ac_simp is over-eager in flattening the formula but since this step
555 : : // simplifies to a zero element this does not matter and only impacts
556 : : // performance marginally.
557 : 0 : case ProofRule::ABSORB:
558 : : {
559 : 0 : std::map<Node, Node> emptyMap;
560 : 0 : Node t = res[0];
561 : 0 : Node tf = applyAcSimp(d_env, emptyMap, t);
562 : 0 : Kind k = res.getKind();
563 : : // if the simplification did not result in a term that would simplify to
564 : : // the expected constant, abort. For this the kind of tf must be the same
565 : : // as of t and one of its arguments must be res[1].
566 : 0 : bool success = false;
567 [ - - ]: 0 : for (const Node& ch : tf)
568 : : {
569 [ - - ]: 0 : if (ch == res[1])
570 : : {
571 : 0 : success = true;
572 : 0 : break;
573 : : }
574 : : }
575 : 0 : if (!success || k != tf.getKind())
576 : : {
577 : 0 : return addAletheStep(AletheRule::HOLE,
578 : : res,
579 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
580 : : {},
581 : : {},
582 : 0 : *cdp);
583 : : }
584 : 0 : Node vp1 = nm->mkNode(Kind::EQUAL, t, tf);
585 : 0 : Node vp2 = nm->mkNode(Kind::EQUAL, tf, res[1]);
586 : : // if the kind was not one of these, the simplification above would have failed
587 : 0 : Assert(k == Kind::OR || k == Kind::AND);
588 : 0 : AletheRule rule =
589 [ - - ]: 0 : k == Kind::OR ? AletheRule::OR_SIMPLIFY : AletheRule::AND_SIMPLIFY;
590 [ - - ][ - - ]: 0 : return addAletheStep(AletheRule::AC_SIMP,
[ - - ][ - - ]
591 : : vp1,
592 : 0 : nm->mkNode(Kind::SEXPR, d_cl, vp1),
593 : : {},
594 : : {},
595 : : *cdp)
596 : 0 : && addAletheStep(
597 : 0 : rule, vp2, nm->mkNode(Kind::SEXPR, d_cl, vp2), {}, {}, *cdp)
598 : 0 : && addAletheStep(AletheRule::TRANS,
599 : : res,
600 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
601 : : {vp1, vp2},
602 : : {},
603 [ - - ]: 0 : *cdp);
604 : : }
605 : : // ======== Encode equality introduction
606 : : // This rule is translated according to the singleton pattern.
607 : 6 : case ProofRule::ENCODE_EQ_INTRO:
608 : : {
609 : 24 : return addAletheStep(AletheRule::REFL,
610 : : res,
611 : 12 : nm->mkNode(Kind::SEXPR, d_cl, res),
612 : : {},
613 : : {},
614 : 6 : *cdp);
615 : : }
616 : : // The conversion is into a "rare_rewrite" step where the first argument is
617 : : // a string literal with the name of the rewrite, followed by the arguments,
618 : : // where lists are built using the Alethe operator "rare-list", which takes
619 : : // 0 or more arguments.
620 : 3409 : case ProofRule::DSL_REWRITE:
621 : : {
622 : : // get the name
623 : : ProofRewriteRule di;
624 : 3409 : Node rule;
625 [ + - ]: 3409 : if (rewriter::getRewriteRule(args[0], di))
626 : : {
627 : 3409 : std::stringstream ss;
628 : 3409 : ss << "\"" << di << "\"";
629 : 3409 : rule = NodeManager::mkRawSymbol(ss.str(), nm->sExprType());
630 : : }
631 : : else
632 : : {
633 : 0 : Unreachable();
634 : : }
635 : 3409 : new_args.push_back(rule);
636 [ + + ]: 9587 : for (int i = 1, size = args.size(); i < size; i++)
637 : : {
638 [ + - ]: 6178 : if (!args[i].isNull())
639 : : {
640 [ - + ]: 6178 : if (args[i].toString() == "")
641 : : { // TODO: better way
642 : 0 : new_args.push_back(
643 : 0 : NodeManager::mkBoundVar("rare-list", nm->sExprType()));
644 : : }
645 [ - + ]: 6178 : else if (args[i].getKind() == Kind::SEXPR)
646 : : {
647 : : std::vector<Node> list_arg{
648 : 0 : NodeManager::mkBoundVar("rare-list", nm->sExprType())};
649 : 0 : list_arg.insert(list_arg.end(), args[i].begin(), args[i].end());
650 : 0 : new_args.push_back(nm->mkNode(Kind::SEXPR, list_arg));
651 : : }
652 : : else
653 : : {
654 : 6178 : new_args.push_back(args[i]);
655 : : }
656 : : }
657 : : }
658 : 6818 : return addAletheStep(AletheRule::RARE_REWRITE,
659 : : res,
660 : 6818 : nm->mkNode(Kind::SEXPR, d_cl, res),
661 : : children,
662 : : new_args,
663 : 3409 : *cdp);
664 : : }
665 : 24 : case ProofRule::THEORY_REWRITE:
666 : : {
667 : : ProofRewriteRule di;
668 : :
669 [ + - ]: 24 : if (rewriter::getRewriteRule(args[0], di))
670 : : {
671 [ + + ]: 24 : if (updateTheoryRewriteProofRewriteRule(res, children, args, cdp, di))
672 : : {
673 : 2 : return true;
674 : : }
675 : 22 : std::stringstream ss;
676 : 22 : ss << "\"" << di << "\"";
677 : 22 : new_args.push_back(NodeManager::mkRawSymbol(ss.str(), nm->sExprType()));
678 : : }
679 : 44 : return addAletheStep(AletheRule::HOLE,
680 : : res,
681 : 44 : nm->mkNode(Kind::SEXPR, d_cl, res),
682 : : children,
683 : : new_args,
684 : 22 : *cdp);
685 : : }
686 : : // Both ARITH_POLY_NORM and EVALUATE, which are used by the Rare
687 : : // elaboration, are captured by the "rare_rewrite" rule.
688 : 428 : case ProofRule::ARITH_POLY_NORM:
689 : : {
690 : 2140 : return addAletheStep(
691 : : AletheRule::RARE_REWRITE,
692 : : res,
693 : 856 : nm->mkNode(Kind::SEXPR, d_cl, res),
694 : : children,
695 : 856 : {NodeManager::mkRawSymbol("\"arith-poly-norm\"", nm->sExprType())},
696 : 856 : *cdp);
697 : : }
698 : 3106 : case ProofRule::EVALUATE:
699 : : {
700 : 15530 : return addAletheStep(
701 : : AletheRule::RARE_REWRITE,
702 : : res,
703 : 6212 : nm->mkNode(Kind::SEXPR, d_cl, res),
704 : : children,
705 : 6212 : {NodeManager::mkRawSymbol("\"evaluate\"", nm->sExprType())},
706 : 6212 : *cdp);
707 : : }
708 : : // If the trusted rule is a theory lemma from arithmetic, we try to phrase
709 : : // it with "lia_generic".
710 : 5608 : case ProofRule::TRUST:
711 : : {
712 : : // check for case where the trust step is introducing an equality between
713 : : // a term and another whose Alethe conversion is itself, in which case we
714 : : // justify this as a REFL step. This happens with trusted purification
715 : : // steps, for example.
716 : 11216 : Node resConv = d_anc.maybeConvert(res);
717 [ + + ]: 5607 : if (!resConv.isNull() && resConv.getKind() == Kind::EQUAL
718 : 11215 : && resConv[0] == resConv[1])
719 : : {
720 : 0 : return addAletheStep(AletheRule::REFL,
721 : : res,
722 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
723 : : children,
724 : : {},
725 : 0 : *cdp);
726 : : }
727 : : TrustId tid;
728 : 5608 : bool hasTrustId = getTrustId(args[0], tid);
729 [ + - ][ + + ]: 5608 : if (hasTrustId && tid == TrustId::THEORY_LEMMA)
730 : : {
731 : : // if we are in the arithmetic case, we rather add a LIA_GENERIC step
732 [ - + ][ - - ]: 24 : if (res.getKind() == Kind::NOT && res[0].getKind() == Kind::AND)
[ - + ][ - + ]
[ - - ]
733 : : {
734 [ - - ]: 0 : Trace("alethe-proof") << "... test each arg if ineq\n";
735 : 0 : bool allIneqs = true;
736 [ - - ]: 0 : for (const Node& arg : res[0])
737 : : {
738 [ - - ]: 0 : Node toTest = arg.getKind() == Kind::NOT ? arg[0] : arg;
739 : 0 : Kind k = toTest.getKind();
740 [ - - ][ - - ]: 0 : if (k != Kind::LT && k != Kind::LEQ && k != Kind::GT
[ - - ]
741 [ - - ][ - - ]: 0 : && k != Kind::GEQ && k != Kind::EQUAL)
742 : : {
743 [ - - ]: 0 : Trace("alethe-proof") << "... arg " << arg << " not ineq\n";
744 : 0 : allIneqs = false;
745 : 0 : break;
746 : : }
747 : : }
748 [ - - ]: 0 : if (allIneqs)
749 : : {
750 : 0 : return addAletheStep(AletheRule::LIA_GENERIC,
751 : : res,
752 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
753 : : children,
754 : : {},
755 : 0 : *cdp);
756 : : }
757 : : }
758 : : }
759 : 11216 : std::stringstream ss;
760 [ + - ]: 5608 : if (hasTrustId)
761 : : {
762 : 5608 : ss << "\"" << tid << "\"";
763 : : cvc5::internal::theory::TheoryId thid;
764 [ + - ]: 5608 : if (theory::builtin::BuiltinProofRuleChecker::getTheoryId(args[0],
765 : : thid))
766 : : {
767 : 5608 : ss << " \"" << thid << "\"";
768 : : }
769 : : }
770 : : else
771 : : {
772 : 0 : ss << "\"" << args[0] << "\"";
773 : : }
774 : : std::vector<Node> newArgs{
775 : 22432 : NodeManager::mkRawSymbol(ss.str(), nm->sExprType())};
776 : 5608 : newArgs.insert(newArgs.end(), args.begin() + 1, args.end());
777 : 11216 : return addAletheStep(AletheRule::HOLE,
778 : : res,
779 : 11216 : nm->mkNode(Kind::SEXPR, d_cl, res),
780 : : children,
781 : : newArgs,
782 : 5608 : *cdp);
783 : : }
784 : : // ======== Resolution and N-ary Resolution
785 : : // Because the RESOLUTION rule is merely a special case of CHAIN_RESOLUTION,
786 : : // the same translation can be used for both.
787 : : //
788 : : // The main complication for the translation of the rule is that in the case
789 : : // that the conclusion is (or G1 ... Gn), the result is ambigous. E.g.,
790 : : //
791 : : // (cl F1 (or F2 F3)) (cl (not F1))
792 : : // -------------------------------------- resolution
793 : : // (cl (or F2 F3))
794 : : //
795 : : // (cl F1 F2 F3) (cl (not F1))
796 : : // -------------------------------------- resolution
797 : : // (cl F2 F3)
798 : : //
799 : : // both (cl (or F2 F3)) and (cl F2 F3) could be represented by the same node
800 : : // (or F2 F3). Thus, it has to be checked if the conclusion C is a singleton
801 : : // clause or not.
802 : : //
803 : : // If C = (or F1 ... Fn) is a non-singleton clause, then:
804 : : //
805 : : // VP1 ... VPn
806 : : // ------------------ resolution
807 : : // (cl F1 ... Fn)*
808 : : //
809 : : // Else if, C = false:
810 : : //
811 : : // VP1 ... VPn
812 : : // ------------------ resolution
813 : : // (cl)*
814 : : //
815 : : // Otherwise:
816 : : //
817 : : // VP1 ... VPn
818 : : // ------------------ resolution
819 : : // (cl C)*
820 : : //
821 : : // * the corresponding proof node is C
822 : 60134 : case ProofRule::RESOLUTION:
823 : : case ProofRule::CHAIN_RESOLUTION:
824 : : case ProofRule::CHAIN_M_RESOLUTION:
825 : : {
826 : 120268 : std::vector<Node> cargs;
827 [ + + ]: 60134 : if (id == ProofRule::CHAIN_RESOLUTION)
828 : : {
829 [ + + ]: 123 : for (size_t i = 0, nargs = args[0].getNumChildren(); i < nargs; i++)
830 : : {
831 : 80 : cargs.push_back(args[0][i]);
832 : 80 : cargs.push_back(args[1][i]);
833 : : }
834 : : }
835 [ + + ]: 60091 : else if (id == ProofRule::CHAIN_M_RESOLUTION)
836 : : {
837 [ + - ][ + - ]: 98948 : Assert(args.size() == 3
[ - + ][ - - ]
838 : : && args[1].getNumChildren() == args[2].getNumChildren());
839 : : // Alethe expects the polarity/literals to be interleaved
840 [ + + ]: 1689390 : for (size_t i = 0, nsteps = args[1].getNumChildren(); i < nsteps; i++)
841 : : {
842 : 1639910 : cargs.push_back(args[1][i]);
843 : 1639910 : cargs.push_back(args[2][i]);
844 : : }
845 : : }
846 : : else
847 : : {
848 : 10617 : cargs = args;
849 : : }
850 : 120268 : Node conclusion;
851 [ + + ]: 60134 : if (!isSingletonClause(res, children, cargs))
852 : : {
853 : 131754 : std::vector<Node> concChildren{d_cl};
854 : 43918 : concChildren.insert(concChildren.end(), res.begin(), res.end());
855 : 43918 : conclusion = nm->mkNode(Kind::SEXPR, concChildren);
856 : : }
857 [ + + ]: 16216 : else if (res == d_false)
858 : : {
859 : 1212 : conclusion = nm->mkNode(Kind::SEXPR, d_cl);
860 : : }
861 : : else
862 : : {
863 : 15004 : conclusion = nm->mkNode(Kind::SEXPR, d_cl, res);
864 : : }
865 : : // checker expects opposite order. We always keep the pivots because we
866 : : // need them to compute in updatePost whether we will add OR steps. If
867 : : // d_resPivots is off we will remove the pivots after that.
868 : 60134 : std::vector<Node> newArgs;
869 [ + + ]: 1710740 : for (size_t i = 0, size = cargs.size(); i < size; i = i + 2)
870 : : {
871 : 1650610 : newArgs.push_back(cargs[i + 1]);
872 : 1650610 : newArgs.push_back(cargs[i]);
873 : : }
874 : 120268 : return addAletheStep(
875 : 60134 : AletheRule::RESOLUTION_OR, res, conclusion, children, newArgs, *cdp);
876 : : }
877 : : // ======== Factoring
878 : : //
879 : : // If C2 = (or F1 ... Fn) but C1 != (or C2 ... C2), then VC2 = (cl F1 ...
880 : : // Fn) Otherwise, VC2 = (cl C2).
881 : : //
882 : : // P
883 : : // ------- contraction
884 : : // VC2*
885 : : //
886 : : // * the corresponding proof node is C2
887 : 74 : case ProofRule::FACTORING:
888 : : {
889 [ + - ]: 74 : if (res.getKind() == Kind::OR)
890 : : {
891 [ + - ]: 74 : for (const Node& child : children[0])
892 : : {
893 [ + - ]: 74 : if (child != res)
894 : : {
895 : 148 : return addAletheStepFromOr(
896 : 74 : AletheRule::CONTRACTION, res, children, {}, *cdp);
897 : : }
898 : : }
899 : : }
900 : 0 : return addAletheStep(AletheRule::CONTRACTION,
901 : : res,
902 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
903 : : children,
904 : : {},
905 : 0 : *cdp);
906 : : }
907 : : // ======== Reordering
908 : : // This rule is translated according to the clauses pattern.
909 : 55374 : case ProofRule::REORDERING:
910 : : {
911 : 110748 : return addAletheStepFromOr(
912 : 55374 : AletheRule::REORDERING, res, children, {}, *cdp);
913 : : }
914 : : // ======== Split
915 : : //
916 : : // --------- not_not --------- not_not
917 : : // VP1 VP2
918 : : // -------------------------------- resolution
919 : : // (cl F (not F))*
920 : : //
921 : : // VP1: (cl (not (not (not F))) F)
922 : : // VP2: (cl (not (not (not (not F)))) (not F))
923 : : //
924 : : // * the corresponding proof node is (or F (not F))
925 : 4 : case ProofRule::SPLIT:
926 : : {
927 : : Node vp1 = nm->mkNode(
928 : 12 : Kind::SEXPR, d_cl, args[0].notNode().notNode().notNode(), args[0]);
929 : : Node vp2 = nm->mkNode(Kind::SEXPR,
930 : 4 : d_cl,
931 : 8 : args[0].notNode().notNode().notNode().notNode(),
932 : 16 : args[0].notNode());
933 [ + - ][ + - ]: 4 : return addAletheStep(AletheRule::NOT_NOT, vp2, vp2, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
934 : 8 : && addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp)
935 : 20 : && addAletheStepFromOr(
936 : : AletheRule::RESOLUTION,
937 : : res,
938 : : {vp1, vp2},
939 : 4 : d_resPivots
940 : 8 : ? std::vector<Node>{args[0].notNode().notNode().notNode(),
941 : 0 : d_true}
942 : : : std::vector<Node>(),
943 [ + - ]: 12 : *cdp);
944 : : }
945 : : // ======== Equality resolution
946 : : //
947 : : // ------ EQUIV_POS2
948 : : // VP1 P2 P1
949 : : // --------------------------------- resolution
950 : : // (cl F2)*
951 : : //
952 : : // VP1: (cl (not (= F1 F2)) (not F1) F2)
953 : : //
954 : : // * the corresponding proof node is F2
955 : 53668 : case ProofRule::EQ_RESOLVE:
956 : : {
957 : : Node equivPos2Cl =
958 : : nm->mkNode(Kind::SEXPR,
959 : 322008 : {d_cl, children[1].notNode(), children[0].notNode(), res});
960 : 53668 : bool success = addAletheStep(
961 : : AletheRule::EQUIV_POS2, equivPos2Cl, equivPos2Cl, {}, {}, *cdp);
962 : : // we will use an RESOLUTION_OR step for the resolution because the proof
963 : : // of children[0], if it is for (or t1 ... tn), may actually conclude (cl
964 : : // t1 ... tn). Using RESOLUTION_OR will guarantee that in post-visit time
965 : : // the resolution step is fixed if need be
966 : : return success
967 : 535600 : && addAletheStep(
968 : : AletheRule::RESOLUTION_OR,
969 : : res,
970 : 107201 : nm->mkNode(Kind::SEXPR, d_cl, res),
971 : 107066 : {equivPos2Cl, children[1], children[0]},
972 : 321333 : std::vector<Node>{children[1], d_false, children[0], d_false},
973 [ + + ]: 214267 : *cdp);
974 : : }
975 : : // ======== Modus ponens
976 : : //
977 : : // (P2:(=> F1 F2))
978 : : // ------------------------ implies
979 : : // (VP1:(cl (not F1) F2)) (P1:F1)
980 : : // -------------------------------------------- resolution
981 : : // (cl F2)*
982 : : //
983 : : // * the corresponding proof node is F2
984 : 28233 : case ProofRule::MODUS_PONENS:
985 : : {
986 : 56466 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
987 : :
988 : 84699 : return addAletheStep(
989 : 28233 : AletheRule::IMPLIES, vp1, vp1, {children[1]}, {}, *cdp)
990 : 254091 : && addAletheStep(AletheRule::RESOLUTION,
991 : : res,
992 : 56465 : nm->mkNode(Kind::SEXPR, d_cl, res),
993 : 28232 : {vp1, children[0]},
994 : 28232 : d_resPivots
995 : 56465 : ? std::vector<Node>{children[0], d_false}
996 : : : std::vector<Node>(),
997 [ + + ]: 84697 : *cdp);
998 : : }
999 : : // ======== Double negation elimination
1000 : : //
1001 : : // ---------------------------------- not_not
1002 : : // (VP1:(cl (not (not (not F))) F)) (P:(not (not F)))
1003 : : // ------------------------------------------------------------- resolution
1004 : : // (cl F)*
1005 : : //
1006 : : // * the corresponding proof node is F
1007 : 85 : case ProofRule::NOT_NOT_ELIM:
1008 : : {
1009 : 170 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
1010 : :
1011 [ + - ][ + - ]: 85 : return addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1012 : 680 : && addAletheStep(AletheRule::RESOLUTION,
1013 : : res,
1014 : 170 : nm->mkNode(Kind::SEXPR, d_cl, res),
1015 : 85 : {vp1, children[0]},
1016 : 85 : d_resPivots
1017 : 170 : ? std::vector<Node>{children[0], d_false}
1018 : : : std::vector<Node>(),
1019 [ + - ]: 255 : *cdp);
1020 : : }
1021 : : // ======== Contradiction
1022 : : //
1023 : : // P1 P2
1024 : : // --------- resolution
1025 : : // (cl)*
1026 : : //
1027 : : // * the corresponding proof node is false
1028 : 2316 : case ProofRule::CONTRA:
1029 : : {
1030 : 4632 : return addAletheStep(AletheRule::RESOLUTION,
1031 : : res,
1032 : 4632 : nm->mkNode(Kind::SEXPR, d_cl),
1033 : : children,
1034 : 4632 : d_resPivots ? std::vector<Node>{children[0], d_true}
1035 : : : std::vector<Node>(),
1036 : 2316 : *cdp);
1037 : : }
1038 : : // ======== And elimination
1039 : : // This rule is translated according to the singleton pattern.
1040 : 8724 : case ProofRule::AND_ELIM:
1041 : : {
1042 : 17448 : return addAletheStep(AletheRule::AND,
1043 : : res,
1044 : 17448 : nm->mkNode(Kind::SEXPR, d_cl, res),
1045 : : children,
1046 : : args,
1047 : 8724 : *cdp);
1048 : : }
1049 : : // ======== And introduction
1050 : : //
1051 : : //
1052 : : // ----- and_neg
1053 : : // VP1 P1 ... Pn
1054 : : // -------------------------- resolution
1055 : : // (cl (and F1 ... Fn))*
1056 : : //
1057 : : // VP1:(cl (and F1 ... Fn) (not F1) ... (not Fn))
1058 : : //
1059 : : // * the corresponding proof node is (and F1 ... Fn)
1060 : 28266 : case ProofRule::AND_INTRO:
1061 : : {
1062 : 141330 : std::vector<Node> neg_Nodes = {d_cl, res};
1063 [ + + ]: 112949 : for (size_t i = 0, size = children.size(); i < size; i++)
1064 : : {
1065 : 84683 : neg_Nodes.push_back(children[i].notNode());
1066 : : }
1067 : 56532 : Node vp1 = nm->mkNode(Kind::SEXPR, neg_Nodes);
1068 : :
1069 : 113064 : std::vector<Node> new_children = {vp1};
1070 : 28266 : new_children.insert(new_children.end(), children.begin(), children.end());
1071 : 28266 : std::vector<Node> newArgs;
1072 [ - + ]: 28266 : if (d_resPivots)
1073 : : {
1074 [ - - ]: 0 : for (const Node& child : children)
1075 : : {
1076 : 0 : newArgs.push_back(child);
1077 : 0 : newArgs.push_back(d_false);
1078 : : }
1079 : : }
1080 [ + - ][ + - ]: 28266 : return addAletheStep(AletheRule::AND_NEG, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1081 [ + - ][ + - ]: 84798 : && addAletheStep(AletheRule::RESOLUTION,
[ - - ]
1082 : : res,
1083 : 56532 : nm->mkNode(Kind::SEXPR, d_cl, res),
1084 : : new_children,
1085 : : newArgs,
1086 [ + - ]: 28266 : *cdp);
1087 : : }
1088 : : // ======== Not Or elimination
1089 : : // This rule is translated according to the singleton pattern.
1090 : 81 : case ProofRule::NOT_OR_ELIM:
1091 : : {
1092 : 162 : return addAletheStep(AletheRule::NOT_OR,
1093 : : res,
1094 : 162 : nm->mkNode(Kind::SEXPR, d_cl, res),
1095 : : children,
1096 : : args,
1097 : 81 : *cdp);
1098 : : }
1099 : : // ======== Implication elimination
1100 : : // This rule is translated according to the clause pattern.
1101 : 12597 : case ProofRule::IMPLIES_ELIM:
1102 : : {
1103 : 12597 : return addAletheStepFromOr(AletheRule::IMPLIES, res, children, {}, *cdp);
1104 : : }
1105 : : // ======== Not Implication elimination version 1
1106 : : // This rule is translated according to the singleton pattern.
1107 : 96 : case ProofRule::NOT_IMPLIES_ELIM1:
1108 : : {
1109 : 384 : return addAletheStep(AletheRule::NOT_IMPLIES1,
1110 : : res,
1111 : 192 : nm->mkNode(Kind::SEXPR, d_cl, res),
1112 : : children,
1113 : : {},
1114 : 96 : *cdp);
1115 : : }
1116 : : // ======== Not Implication elimination version 2
1117 : : // This rule is translated according to the singleton pattern.
1118 : 105 : case ProofRule::NOT_IMPLIES_ELIM2:
1119 : : {
1120 : 420 : return addAletheStep(AletheRule::NOT_IMPLIES2,
1121 : : res,
1122 : 210 : nm->mkNode(Kind::SEXPR, d_cl, res),
1123 : : children,
1124 : : {},
1125 : 105 : *cdp);
1126 : : }
1127 : : // ======== Various elimination rules
1128 : : // The following rules are all translated according to the clause pattern.
1129 : 1196 : case ProofRule::EQUIV_ELIM1:
1130 : : {
1131 : 1196 : return addAletheStepFromOr(AletheRule::EQUIV1, res, children, {}, *cdp);
1132 : : }
1133 : 833 : case ProofRule::EQUIV_ELIM2:
1134 : : {
1135 : 833 : return addAletheStepFromOr(AletheRule::EQUIV2, res, children, {}, *cdp);
1136 : : }
1137 : 39 : case ProofRule::NOT_EQUIV_ELIM1:
1138 : : {
1139 : 78 : return addAletheStepFromOr(
1140 : 39 : AletheRule::NOT_EQUIV1, res, children, {}, *cdp);
1141 : : }
1142 : 39 : case ProofRule::NOT_EQUIV_ELIM2:
1143 : : {
1144 : 78 : return addAletheStepFromOr(
1145 : 39 : AletheRule::NOT_EQUIV2, res, children, {}, *cdp);
1146 : : }
1147 : 4 : case ProofRule::XOR_ELIM1:
1148 : : {
1149 : 4 : return addAletheStepFromOr(AletheRule::XOR1, res, children, {}, *cdp);
1150 : : }
1151 : 2 : case ProofRule::XOR_ELIM2:
1152 : : {
1153 : 2 : return addAletheStepFromOr(AletheRule::XOR2, res, children, {}, *cdp);
1154 : : }
1155 : 0 : case ProofRule::NOT_XOR_ELIM1:
1156 : : {
1157 : 0 : return addAletheStepFromOr(AletheRule::NOT_XOR1, res, children, {}, *cdp);
1158 : : }
1159 : 0 : case ProofRule::NOT_XOR_ELIM2:
1160 : : {
1161 : 0 : return addAletheStepFromOr(AletheRule::NOT_XOR2, res, children, {}, *cdp);
1162 : : }
1163 : 664 : case ProofRule::ITE_ELIM1:
1164 : : {
1165 : 664 : return addAletheStepFromOr(AletheRule::ITE2, res, children, {}, *cdp);
1166 : : }
1167 : 834 : case ProofRule::ITE_ELIM2:
1168 : : {
1169 : 834 : return addAletheStepFromOr(AletheRule::ITE1, res, children, {}, *cdp);
1170 : : }
1171 : 0 : case ProofRule::NOT_ITE_ELIM1:
1172 : : {
1173 : 0 : return addAletheStepFromOr(AletheRule::NOT_ITE2, res, children, {}, *cdp);
1174 : : }
1175 : 0 : case ProofRule::NOT_ITE_ELIM2:
1176 : : {
1177 : 0 : return addAletheStepFromOr(AletheRule::NOT_ITE1, res, children, {}, *cdp);
1178 : : }
1179 : : //================================================= De Morgan rules
1180 : : // ======== Not And
1181 : : // This rule is translated according to the clause pattern.
1182 : 8020 : case ProofRule::NOT_AND:
1183 : : {
1184 : 8020 : return addAletheStepFromOr(AletheRule::NOT_AND, res, children, {}, *cdp);
1185 : : }
1186 : :
1187 : : //================================================= CNF rules
1188 : : // The following rules are all translated according to the clause pattern.
1189 : 21712 : case ProofRule::CNF_AND_POS:
1190 : : {
1191 : 43424 : return addAletheStepFromOr(AletheRule::AND_POS,
1192 : : res,
1193 : : children,
1194 : 65136 : std::vector<Node>{args.back()},
1195 : 21712 : *cdp);
1196 : : }
1197 : 11495 : case ProofRule::CNF_AND_NEG:
1198 : : {
1199 : 11495 : return addAletheStepFromOr(AletheRule::AND_NEG, res, children, {}, *cdp);
1200 : : }
1201 : 3536 : case ProofRule::CNF_OR_POS:
1202 : : {
1203 : 3536 : return addAletheStepFromOr(AletheRule::OR_POS, res, children, {}, *cdp);
1204 : : }
1205 : 3646 : case ProofRule::CNF_OR_NEG:
1206 : : {
1207 : 7292 : return addAletheStepFromOr(AletheRule::OR_NEG,
1208 : : res,
1209 : : children,
1210 : 10938 : std::vector<Node>{args.back()},
1211 : 3646 : *cdp);
1212 : : }
1213 : 51 : case ProofRule::CNF_IMPLIES_POS:
1214 : : {
1215 : 102 : return addAletheStepFromOr(
1216 : 51 : AletheRule::IMPLIES_POS, res, children, {}, *cdp);
1217 : : }
1218 : 77 : case ProofRule::CNF_IMPLIES_NEG1:
1219 : : {
1220 : 154 : return addAletheStepFromOr(
1221 : 77 : AletheRule::IMPLIES_NEG1, res, children, {}, *cdp);
1222 : : }
1223 : 223 : case ProofRule::CNF_IMPLIES_NEG2:
1224 : : {
1225 : 446 : return addAletheStepFromOr(
1226 : 223 : AletheRule::IMPLIES_NEG2, res, children, {}, *cdp);
1227 : : }
1228 : 1283 : case ProofRule::CNF_EQUIV_POS1:
1229 : : {
1230 : 2566 : return addAletheStepFromOr(
1231 : 1283 : AletheRule::EQUIV_POS2, res, children, {}, *cdp);
1232 : : }
1233 : 1306 : case ProofRule::CNF_EQUIV_POS2:
1234 : : {
1235 : 2612 : return addAletheStepFromOr(
1236 : 1306 : AletheRule::EQUIV_POS1, res, children, {}, *cdp);
1237 : : }
1238 : 1004 : case ProofRule::CNF_EQUIV_NEG1:
1239 : : {
1240 : 2008 : return addAletheStepFromOr(
1241 : 1004 : AletheRule::EQUIV_NEG2, res, children, {}, *cdp);
1242 : : }
1243 : 804 : case ProofRule::CNF_EQUIV_NEG2:
1244 : : {
1245 : 1608 : return addAletheStepFromOr(
1246 : 804 : AletheRule::EQUIV_NEG1, res, children, {}, *cdp);
1247 : : }
1248 : 1500 : case ProofRule::CNF_XOR_POS1:
1249 : : {
1250 : 1500 : return addAletheStepFromOr(AletheRule::XOR_POS1, res, children, {}, *cdp);
1251 : : }
1252 : 563 : case ProofRule::CNF_XOR_POS2:
1253 : : {
1254 : 563 : return addAletheStepFromOr(AletheRule::XOR_POS2, res, children, {}, *cdp);
1255 : : }
1256 : 675 : case ProofRule::CNF_XOR_NEG1:
1257 : : {
1258 : 675 : return addAletheStepFromOr(AletheRule::XOR_NEG2, res, children, {}, *cdp);
1259 : : }
1260 : 688 : case ProofRule::CNF_XOR_NEG2:
1261 : : {
1262 : 688 : return addAletheStepFromOr(AletheRule::XOR_NEG1, res, children, {}, *cdp);
1263 : : }
1264 : 465 : case ProofRule::CNF_ITE_POS1:
1265 : : {
1266 : 465 : return addAletheStepFromOr(AletheRule::ITE_POS2, res, children, {}, *cdp);
1267 : : }
1268 : 570 : case ProofRule::CNF_ITE_POS2:
1269 : : {
1270 : 570 : return addAletheStepFromOr(AletheRule::ITE_POS1, res, children, {}, *cdp);
1271 : : }
1272 : 269 : case ProofRule::CNF_ITE_NEG1:
1273 : : {
1274 : 269 : return addAletheStepFromOr(AletheRule::ITE_NEG2, res, children, {}, *cdp);
1275 : : }
1276 : 404 : case ProofRule::CNF_ITE_NEG2:
1277 : : {
1278 : 404 : return addAletheStepFromOr(AletheRule::ITE_NEG1, res, children, {}, *cdp);
1279 : : }
1280 : : // ======== CNF ITE Pos version 3
1281 : : //
1282 : : // ----- ite_pos1 ----- ite_pos2
1283 : : // VP1 VP2
1284 : : // ------------------------------- resolution
1285 : : // VP3
1286 : : // ------------------------------- reordering
1287 : : // VP4
1288 : : // ------------------------------- contraction
1289 : : // (cl (not (ite C F1 F2)) F1 F2)
1290 : : //
1291 : : // VP1: (cl (not (ite C F1 F2)) C F2)
1292 : : // VP2: (cl (not (ite C F1 F2)) (not C) F1)
1293 : : // VP3: (cl (not (ite C F1 F2)) F2 (not (ite C F1 F2)) F1)
1294 : : // VP4: (cl (not (ite C F1 F2)) (not (ite C F1 F2)) F1 F2)
1295 : : //
1296 : : // * the corresponding proof node is (or (not (ite C F1 F2)) F1 F2)
1297 : 745 : case ProofRule::CNF_ITE_POS3:
1298 : : {
1299 : 5215 : Node vp1 = nm->mkNode(Kind::SEXPR, {d_cl, res[0], args[0][0], res[2]});
1300 : : Node vp2 =
1301 : 5215 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], args[0][0].notNode(), res[1]});
1302 : : Node vp3 =
1303 : 5960 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], res[2], res[0], res[1]});
1304 : : Node vp4 =
1305 : 5215 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], res[0], res[1], res[2]});
1306 : :
1307 [ + - ][ + - ]: 745 : return addAletheStep(AletheRule::ITE_POS1, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1308 : 1490 : && addAletheStep(AletheRule::ITE_POS2, vp2, vp2, {}, {}, *cdp)
1309 : 2980 : && addAletheStep(AletheRule::RESOLUTION,
1310 : : vp3,
1311 : : vp3,
1312 : : {vp1, vp2},
1313 : 745 : d_resPivots
1314 : 1490 : ? std::vector<Node>{args[0][0], d_true}
1315 : : : std::vector<Node>(),
1316 : 1490 : *cdp)
1317 : 2235 : && addAletheStep(AletheRule::REORDERING, vp4, vp4, {vp3}, {}, *cdp)
1318 : 2980 : && addAletheStepFromOr(
1319 [ + - ]: 1490 : AletheRule::CONTRACTION, res, {vp4}, {}, *cdp);
1320 : : }
1321 : : // ======== CNF ITE Neg version 3
1322 : : //
1323 : : // ----- ite_neg1 ----- ite_neg2
1324 : : // VP1 VP2
1325 : : // ------------------------------- resolution
1326 : : // VP3
1327 : : // ------------------------------- reordering
1328 : : // VP4
1329 : : // ------------------------------- contraction
1330 : : // (cl (ite C F1 F2) C (not F2))
1331 : : //
1332 : : // VP1: (cl (ite C F1 F2) C (not F2))
1333 : : // VP2: (cl (ite C F1 F2) (not C) (not F1))
1334 : : // VP3: (cl (ite C F1 F2) (not F2) (ite C F1 F2) (not F1))
1335 : : // VP4: (cl (ite C F1 F2) (ite C F1 F2) (not F1) (not F2))
1336 : : //
1337 : : // * the corresponding proof node is (or (ite C F1 F2) C (not F2))
1338 : 274 : case ProofRule::CNF_ITE_NEG3:
1339 : : {
1340 : 1918 : Node vp1 = nm->mkNode(Kind::SEXPR, {d_cl, res[0], args[0][0], res[2]});
1341 : : Node vp2 =
1342 : 1918 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], args[0][0].notNode(), res[1]});
1343 : : Node vp3 =
1344 : 2192 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], res[2], res[0], res[1]});
1345 : : Node vp4 =
1346 : 1918 : nm->mkNode(Kind::SEXPR, {d_cl, res[0], res[0], res[1], res[2]});
1347 : :
1348 [ + - ][ + - ]: 274 : return addAletheStep(AletheRule::ITE_NEG1, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1349 : 548 : && addAletheStep(AletheRule::ITE_NEG2, vp2, vp2, {}, {}, *cdp)
1350 : 1096 : && addAletheStep(AletheRule::RESOLUTION,
1351 : : vp3,
1352 : : vp3,
1353 : : {vp1, vp2},
1354 : 274 : d_resPivots
1355 : 548 : ? std::vector<Node>{args[0][0], d_true}
1356 : : : std::vector<Node>(),
1357 : 548 : *cdp)
1358 : 822 : && addAletheStep(AletheRule::REORDERING, vp4, vp4, {vp3}, {}, *cdp)
1359 : 1096 : && addAletheStepFromOr(
1360 [ + - ]: 548 : AletheRule::CONTRACTION, res, {vp4}, {}, *cdp);
1361 : : }
1362 : : //================================================= Equality rules
1363 : : // The following rules are all translated according to the singleton
1364 : : // pattern.
1365 : 53830 : case ProofRule::REFL:
1366 : : {
1367 : 215320 : return addAletheStep(AletheRule::REFL,
1368 : : res,
1369 : 107660 : nm->mkNode(Kind::SEXPR, d_cl, res),
1370 : : children,
1371 : : {},
1372 : 53830 : *cdp);
1373 : : }
1374 : 49170 : case ProofRule::SYMM:
1375 : : {
1376 : 245850 : return addAletheStep(
1377 [ + + ]: 49170 : res.getKind() == Kind::NOT ? AletheRule::NOT_SYMM : AletheRule::SYMM,
1378 : : res,
1379 : 98340 : nm->mkNode(Kind::SEXPR, d_cl, res),
1380 : : children,
1381 : : {},
1382 : 49170 : *cdp);
1383 : : }
1384 : 157654 : case ProofRule::TRANS:
1385 : : {
1386 : 630616 : return addAletheStep(AletheRule::TRANS,
1387 : : res,
1388 : 315308 : nm->mkNode(Kind::SEXPR, d_cl, res),
1389 : : children,
1390 : : {},
1391 : 157654 : *cdp);
1392 : : }
1393 : : // ======== Congruence
1394 : : // In the case that the kind of the function symbol f? is FORALL or
1395 : : // EXISTS, the cong rule needs to be converted into a bind rule:
1396 : : //
1397 : : // (cl (= F G))
1398 : : // -------------------- bind, z1 ... zn (= y1 z1) ... (= yn zn)
1399 : : // (= (Q ((y1 T1) ... (yn Tn)) F) (Q ((z1 T1) ... (zn Tn)) G))
1400 : : //
1401 : : // Otherwise, the rule is regular congruence:
1402 : : //
1403 : : // P1 ... Pn
1404 : : // -------------------------------------------------------- cong
1405 : : // (cl (= (<kind> f? t1 ... tn) (<kind> f? s1 ... sn)))
1406 : 246135 : case ProofRule::CONG:
1407 : : case ProofRule::NARY_CONG:
1408 : : {
1409 [ + + ]: 246135 : if (res[0].isClosure())
1410 : : {
1411 : : // collect rhs variables
1412 : 3195 : new_args.insert(new_args.end(), res[1][0].begin(), res[1][0].end());
1413 [ + + ]: 9455 : for (size_t i = 0, size = res[0][0].getNumChildren(); i < size; ++i)
1414 : : {
1415 : 6260 : new_args.push_back(res[0][0][i].eqNode(res[1][0][i]));
1416 : : }
1417 : 3195 : Kind k = res[0].getKind();
1418 : 6390 : return addAletheStep(AletheRule::ANCHOR_BIND,
1419 : : res,
1420 : 6390 : nm->mkNode(Kind::SEXPR, d_cl, res),
1421 : : // be sure to ignore premise for pattern
1422 [ + + ]: 143 : (k == Kind::FORALL || k == Kind::EXISTS)
1423 : 9671 : ? std::vector<Node>{children[0]}
1424 : : : children,
1425 : : new_args,
1426 : 3195 : *cdp);
1427 : : }
1428 : 971760 : return addAletheStep(AletheRule::CONG,
1429 : : res,
1430 : 485880 : nm->mkNode(Kind::SEXPR, d_cl, res),
1431 : : children,
1432 : : {},
1433 : 242940 : *cdp);
1434 : : }
1435 : 112 : case ProofRule::HO_CONG:
1436 : : {
1437 : 448 : return addAletheStep(AletheRule::HO_CONG,
1438 : : res,
1439 : 224 : nm->mkNode(Kind::SEXPR, d_cl, res),
1440 : : children,
1441 : : {},
1442 : 112 : *cdp);
1443 : : }
1444 : : // ======== True intro
1445 : : //
1446 : : // ------------------------------- EQUIV_SIMPLIFY
1447 : : // (VP1:(cl (= (= F true) F)))
1448 : : // ------------------------------- EQUIV2
1449 : : // (VP2:(cl (= F true) (not F))) P
1450 : : // -------------------------------------------- RESOLUTION
1451 : : // (cl (= F true))*
1452 : : //
1453 : : // * the corresponding proof node is (= F true)
1454 : 759 : case ProofRule::TRUE_INTRO:
1455 : : {
1456 : 2277 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, res.eqNode(children[0]));
1457 : 1518 : Node vp2 = nm->mkNode(Kind::SEXPR, d_cl, res, children[0].notNode());
1458 [ + - ][ + - ]: 759 : return addAletheStep(AletheRule::EQUIV_SIMPLIFY, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1459 : 2275 : && addAletheStep(AletheRule::EQUIV2, vp2, vp2, {vp1}, {}, *cdp)
1460 : 6066 : && addAletheStep(AletheRule::RESOLUTION,
1461 : : res,
1462 : 1517 : nm->mkNode(Kind::SEXPR, d_cl, res),
1463 : 758 : {vp2, children[0]},
1464 : 758 : d_resPivots
1465 : 1517 : ? std::vector<Node>{children[0], d_false}
1466 : : : std::vector<Node>(),
1467 [ + + ]: 2275 : *cdp);
1468 : : }
1469 : : // ======== True elim
1470 : : //
1471 : : // ------------------------------- EQUIV_SIMPLIFY
1472 : : // (VP1:(cl (= (= F true) F)))
1473 : : // ------------------------------- EQUIV1
1474 : : // (VP2:(cl (not (= F true)) F)) P
1475 : : // -------------------------------------------- RESOLUTION
1476 : : // (cl F)*
1477 : : //
1478 : : // * the corresponding proof node is F
1479 : 2903 : case ProofRule::TRUE_ELIM:
1480 : : {
1481 : 8709 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].eqNode(res));
1482 : 5806 : Node vp2 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
1483 [ + - ][ + - ]: 2903 : return addAletheStep(AletheRule::EQUIV_SIMPLIFY, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1484 : 8709 : && addAletheStep(AletheRule::EQUIV1, vp2, vp2, {vp1}, {}, *cdp)
1485 : 23224 : && addAletheStep(AletheRule::RESOLUTION,
1486 : : res,
1487 : 5806 : nm->mkNode(Kind::SEXPR, d_cl, res),
1488 : 2903 : {vp2, children[0]},
1489 : 2903 : d_resPivots
1490 : 5806 : ? std::vector<Node>{children[0], d_false}
1491 : : : std::vector<Node>(),
1492 [ + - ]: 8709 : *cdp);
1493 : : }
1494 : : // ======== False intro
1495 : : //
1496 : : // ----- EQUIV_SIMPLIFY
1497 : : // VP1
1498 : : // ----- EQUIV2 ----- NOT_NOT
1499 : : // VP2 VP3
1500 : : // ---------------------- RESOLUTION
1501 : : // VP4 P
1502 : : // -------------------------------------- RESOLUTION
1503 : : // (cl (= F false))*
1504 : : //
1505 : : // VP1: (cl (= (= F false) (not F)))
1506 : : // VP2: (cl (= F false) (not (not F)))
1507 : : // VP3: (cl (not (not (not F))) F)
1508 : : // VP4: (cl (= F false) F)
1509 : : //
1510 : : // * the corresponding proof node is (= F false)
1511 : 1560 : case ProofRule::FALSE_INTRO:
1512 : : {
1513 : 4680 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, res.eqNode(children[0]));
1514 : 4680 : Node vp2 = nm->mkNode(Kind::SEXPR, d_cl, res, children[0].notNode());
1515 : : Node vp3 = nm->mkNode(
1516 : 4680 : Kind::SEXPR, d_cl, children[0].notNode().notNode(), children[0][0]);
1517 : 3120 : Node vp4 = nm->mkNode(Kind::SEXPR, d_cl, res, children[0][0]);
1518 : :
1519 [ + - ][ + - ]: 1560 : return addAletheStep(AletheRule::EQUIV_SIMPLIFY, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1520 : 4678 : && addAletheStep(AletheRule::EQUIV2, vp2, vp2, {vp1}, {}, *cdp)
1521 : 3119 : && addAletheStep(AletheRule::NOT_NOT, vp3, vp3, {}, {}, *cdp)
1522 : 6237 : && addAletheStep(
1523 : : AletheRule::RESOLUTION,
1524 : : vp4,
1525 : : vp4,
1526 : : {vp2, vp3},
1527 : 3129 : d_resPivots ? std::vector<Node>{children[0].notNode(), d_true}
1528 : : : std::vector<Node>(),
1529 : 3118 : *cdp)
1530 : 12474 : && addAletheStep(AletheRule::RESOLUTION,
1531 : : res,
1532 : 3119 : nm->mkNode(Kind::SEXPR, d_cl, res),
1533 : 1559 : {vp4, children[0]},
1534 : 1559 : d_resPivots
1535 : 3129 : ? std::vector<Node>{children[0][0], d_true}
1536 : : : std::vector<Node>(),
1537 [ + + ]: 4678 : *cdp);
1538 : : }
1539 : : // ======== False elim
1540 : : //
1541 : : // ----- EQUIV_SIMPLIFY
1542 : : // VP1
1543 : : // ----- EQUIV1
1544 : : // VP2 P
1545 : : // ---------------------- RESOLUTION
1546 : : // (cl (not F))*
1547 : : //
1548 : : // VP1: (cl (= (= F false) (not F)))
1549 : : // VP2: (cl (not (= F false)) (not F))
1550 : : // VP3: (cl (not (not (not F))) F)
1551 : : // VP4: (cl (= F false) F)
1552 : : //
1553 : : // * the corresponding proof node is (not F)
1554 : 1448 : case ProofRule::FALSE_ELIM:
1555 : : {
1556 : 4344 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].eqNode(res));
1557 : 2896 : Node vp2 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
1558 : :
1559 [ + - ][ + - ]: 1448 : return addAletheStep(AletheRule::EQUIV_SIMPLIFY, vp1, vp1, {}, {}, *cdp)
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
1560 : 4344 : && addAletheStep(AletheRule::EQUIV1, vp2, vp2, {vp1}, {}, *cdp)
1561 : 11584 : && addAletheStep(AletheRule::RESOLUTION,
1562 : : res,
1563 : 2896 : nm->mkNode(Kind::SEXPR, d_cl, res),
1564 : 1448 : {vp2, children[0]},
1565 : 1448 : d_resPivots
1566 : 2906 : ? std::vector<Node>{children[0], d_false}
1567 : : : std::vector<Node>(),
1568 [ + - ]: 4344 : *cdp);
1569 : : }
1570 : : //================================================= Skolems rules
1571 : : // ======== Skolem intro
1572 : : // Since this rule just equates a term to its purification skolem, whose
1573 : : // conversion is the term itself, the converted conclusion is an equality
1574 : : // between the same terms.
1575 : 2727 : case ProofRule::SKOLEM_INTRO:
1576 : : {
1577 : 10908 : return addAletheStep(AletheRule::REFL,
1578 : : res,
1579 : 5454 : nm->mkNode(Kind::SEXPR, d_cl, res),
1580 : : {},
1581 : : {},
1582 : 2727 : *cdp);
1583 : : }
1584 : : // ======== If-then-else equivalence
1585 : : //
1586 : : // ------- rare_rewrite, ite_eq
1587 : : // VP1
1588 : : // ------- equiv2 ---------- true
1589 : : // VP2 d_true
1590 : : // ----------------------------------------------- resolution
1591 : : // (cl (C?(= (C?t1:t2) t1):(= (C?t1:t2) t2)))*
1592 : : //
1593 : : // VP1: (cl (= (C?(= (C?t1:t2) t1):(= (C?t1:t2) t2)) true))
1594 : : // VP2: (cl (C?(= (C?t1:t2) t1):(= (C?t1:t2) t2)) (not true))
1595 : : // d_true: (cl true)
1596 : : //
1597 : : // * the corresponding proof node is (C?(= (C?t1:t2) t1):(= (C?t1:t2) t2))
1598 : : //
1599 : : // (define-rule ite_eq ((C bool) (t1 ?) (t2 ?)) (ite C (= (C?t1:t2) t1) (=
1600 : : // (C?t1:t2) t2)) true)
1601 : : //
1602 : 818 : case ProofRule::ITE_EQ:
1603 : : {
1604 : 2454 : Node vp1 = nm->mkNode(Kind::EQUAL, res, d_true);
1605 : 2454 : Node vp2 = nm->mkNode(Kind::OR, res, d_true.notNode());
1606 : 818 : Node args_0 = args[0];
1607 : 5726 : return addAletheStep(AletheRule::RARE_REWRITE,
1608 : : vp1,
1609 : 1636 : nm->mkNode(Kind::SEXPR, d_cl, vp1),
1610 : : {},
1611 [ + - ][ - - ]: 818 : {nm->mkRawSymbol("\"ite-eq\"", nm->sExprType()),
1612 : : args_0[0],
1613 : : args_0[1],
1614 : : args_0[2]},
1615 : 3272 : *cdp)
1616 : 2454 : && addAletheStepFromOr(AletheRule::EQUIV2, vp2, {vp1}, {}, *cdp)
1617 : 3272 : && addAletheStep(AletheRule::TRUE,
1618 : 818 : d_true,
1619 : 1636 : nm->mkNode(Kind::SEXPR, d_cl, d_true),
1620 : : {},
1621 : : {},
1622 : : *cdp)
1623 : 8180 : && addAletheStep(AletheRule::RESOLUTION,
1624 : : res,
1625 : 1636 : nm->mkNode(Kind::SEXPR, d_cl, res),
1626 : 818 : {vp2, d_true},
1627 : 1636 : d_resPivots ? std::vector<Node>{d_true, d_false}
1628 : : : std::vector<Node>(),
1629 [ + - ]: 2454 : *cdp);
1630 : : }
1631 : : // ======== Skolemize
1632 : : //
1633 : : // In cvc5 this is applied solely to terms (not (forall (...) F)),
1634 : : // concluding (not F*sigma'), where sigma' is the cumulative substitution
1635 : : // built from sigma1...sigma_n, and each sigma_i replaces xi by the choice
1636 : : // term (epsilon ((xi Ti)) (forall ((xi+1 Ti+1) ... (xn+1 Tn+1)) (not
1637 : : // F))). The resulting Alethe Skolemization step is:
1638 : : //
1639 : : // ---------------- refl
1640 : : // (= F F*sigma')
1641 : : // ------------------------- anchor_sko_forall, sigma_1, ..., sigma_n
1642 : : // (= (forall ((x1 T1) ... (xn Tn)) F) F*sigma')
1643 : : // ----------------------------------------------- cong
1644 : : // (= (not (forall ((x1 T1) ... (xn Tn)) F)) (not F*sigma'))
1645 : : //
1646 : : // Then, we eliminate the equality to obtain (not F*sigma) from the premise:
1647 : : //
1648 : : // ---- equiv_pos2
1649 : : // VP1 (= (not (forall (...) F)) (not F*sigma')) (not (forall (...) F))
1650 : : // ------------------------------------------------------------- resolution
1651 : : // (not F*sigma')
1652 : : //
1653 : : // VP1 :
1654 : : // (cl
1655 : : // (not (= (not (forall (...) F)) (not F*sigma')))
1656 : : // (not (not (forall (...) F)))
1657 : : // (not F*sigma'))
1658 : : //
1659 : : // Note that F*sigma' is equivalent to F*sigma once its Skolem terms are
1660 : : // lifted to choice terms by the node converter.
1661 : 74 : case ProofRule::SKOLEMIZE:
1662 : : {
1663 : 74 : bool success = true;
1664 : 148 : Node quant = children[0][0], skolemized = res[0];
1665 : 148 : Assert(children[0].getKind() == Kind::NOT
1666 : : && children[0][0].getKind() == Kind::FORALL);
1667 : 148 : Node eq = quant[1].eqNode(skolemized);
1668 : : // add rfl step for final replacement
1669 : 222 : Node premise = nm->mkNode(Kind::SEXPR, d_cl, eq);
1670 : 74 : success &=
1671 : 74 : addAletheStep(AletheRule::REFL, premise, premise, {}, {}, *cdp);
1672 : 222 : std::vector<Node> bVars{quant[0].begin(), quant[0].end()};
1673 : 148 : std::vector<Node> skoSubstitutions;
1674 : 74 : SkolemManager* sm = nm->getSkolemManager();
1675 : 74 : const std::map<Node, Node>& skolemDefs = d_anc.getSkolemDefinitions();
1676 [ + + ]: 187 : for (size_t i = 0, size = quant[0].getNumChildren(); i < size; ++i)
1677 : : {
1678 : : // Make the Skolem corresponding to this variable and retrieve its
1679 : : // conversion from the node converter
1680 : 565 : std::vector<Node> cacheVals{quant, nm->mkConstInt(Rational(i))};
1681 : : Node sk =
1682 : 113 : sm->mkSkolemFunction(SkolemId::QUANTIFIERS_SKOLEMIZE, cacheVals);
1683 [ - + ][ - + ]: 113 : Assert(!sk.isNull());
[ - - ]
1684 [ - + ]: 113 : if (options().proof.proofAletheDefineSkolems)
1685 : : {
1686 : 0 : skoSubstitutions.push_back(quant[0][i].eqNode(sk));
1687 : 0 : continue;
1688 : : }
1689 : 113 : auto it = skolemDefs.find(sk);
1690 : 113 : Assert(it != skolemDefs.end()) << sk << " " << skolemDefs;
1691 : 113 : skoSubstitutions.push_back(quant[0][i].eqNode(it->second));
1692 : : }
1693 [ - + ][ - + ]: 74 : Assert(!d_anc.convert(quant.eqNode(skolemized)).isNull());
[ - - ]
1694 : : Node conclusion = nm->mkNode(
1695 : 222 : Kind::SEXPR, d_cl, d_anc.convert(quant.eqNode(skolemized)));
1696 : : // add the sko step
1697 : 222 : success &= addAletheStep(AletheRule::ANCHOR_SKO_FORALL,
1698 : : conclusion,
1699 : : conclusion,
1700 : : {premise},
1701 : : skoSubstitutions,
1702 : 148 : *cdp);
1703 : : // add congruence step with NOT for the forall case
1704 : : Node newConclusion = nm->mkNode(
1705 : 222 : Kind::SEXPR, d_cl, (quant.notNode()).eqNode(skolemized.notNode()));
1706 : 222 : success &= addAletheStep(AletheRule::CONG,
1707 : : newConclusion,
1708 : : newConclusion,
1709 : : {conclusion},
1710 : : {},
1711 : 148 : *cdp);
1712 : 74 : conclusion = newConclusion;
1713 : : // now equality resolution reasoning
1714 : : Node vp1 = nm->mkNode(
1715 : : Kind::SEXPR,
1716 : 444 : {d_cl, conclusion[1].notNode(), children[0].notNode(), res});
1717 : 74 : success &= addAletheStep(AletheRule::EQUIV_POS2, vp1, vp1, {}, {}, *cdp);
1718 : : return success
1719 : 666 : && addAletheStep(AletheRule::RESOLUTION,
1720 : : res,
1721 : 148 : nm->mkNode(Kind::SEXPR, d_cl, res),
1722 : 74 : {vp1, conclusion, children[0]},
1723 : 148 : d_resPivots ? std::vector<Node>{conclusion[1],
1724 : 0 : d_false,
1725 : 0 : children[0],
1726 : 0 : d_false}
1727 : : : std::vector<Node>(),
1728 [ + - ]: 296 : *cdp);
1729 : : }
1730 : : // ======== Bitvector
1731 : : //
1732 : : // ------------------------ BV_BITBLAST_STEP_BV<KIND>
1733 : : // (cl (= t bitblast(t)))
1734 : 4 : case ProofRule::BV_EAGER_ATOM:
1735 : : {
1736 : 8 : Assert(res.getKind() == Kind::EQUAL && res[0][0] == res[1]);
1737 : 8 : Node newRes = res[0][0].eqNode(res[1]);
1738 : 16 : return addAletheStep(AletheRule::REFL,
1739 : : res,
1740 : 8 : nm->mkNode(Kind::SEXPR, d_cl, newRes),
1741 : : children,
1742 : : {},
1743 : 4 : *cdp);
1744 : : }
1745 : : // ------------------------ BV_BITBLAST_STEP_BV<KIND>
1746 : : // (cl (= t bitblast(t)))
1747 : 5839 : case ProofRule::BV_BITBLAST_STEP:
1748 : : {
1749 : 5839 : Kind k = res[0].getKind();
1750 : : // no checking for those yet in Carcara or Isabelle, so we produce holes
1751 [ + + ][ + + ]: 5839 : if (k == Kind::BITVECTOR_UDIV || k == Kind::BITVECTOR_UREM
1752 [ + + ][ + + ]: 5830 : || k == Kind::BITVECTOR_SHL || k == Kind::BITVECTOR_LSHR
1753 [ + + ]: 5721 : || k == Kind::BITVECTOR_ASHR)
1754 : : {
1755 : 672 : return addAletheStep(AletheRule::HOLE,
1756 : : res,
1757 : 336 : nm->mkNode(Kind::SEXPR, d_cl, res),
1758 : : children,
1759 : : {},
1760 : 168 : *cdp);
1761 : : }
1762 : : // if the term being bitblasted is a variable or a nonbv term, then this
1763 : : // is a "bitblast var" step
1764 : 5671 : auto it = s_bvKindToAletheRule.find(k);
1765 [ + + ]: 27634 : return addAletheStep(it == s_bvKindToAletheRule.end()
1766 : : ? AletheRule::BV_BITBLAST_STEP_VAR
1767 : 4950 : : it->second,
1768 : : res,
1769 : 11342 : nm->mkNode(Kind::SEXPR, d_cl, res),
1770 : : children,
1771 : : {},
1772 : 5671 : *cdp);
1773 : : }
1774 : : //================================================= Quantifiers rules
1775 : : // ======== Instantiate
1776 : : //
1777 : : // ----- FORALL_INST, t1 ... tn
1778 : : // VP1
1779 : : // ----- OR
1780 : : // VP2 P
1781 : : // -------------------- RESOLUTION
1782 : : // (cl F*sigma)^
1783 : : //
1784 : : // VP1: (cl (or (not (forall ((x1 T1) ... (xn Tn)) F*sigma)
1785 : : // VP2: (cl (not (forall ((x1 T1) ... (xn Tn)) F)) F*sigma)
1786 : : //
1787 : : // ^ the corresponding proof node is F*sigma
1788 : 847 : case ProofRule::INSTANTIATE:
1789 : : {
1790 : : Node vp1 = nm->mkNode(
1791 : 2541 : Kind::SEXPR, d_cl, nm->mkNode(Kind::OR, children[0].notNode(), res));
1792 : 1694 : Node vp2 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
1793 [ + - ][ + - ]: 847 : return addAletheStep(AletheRule::FORALL_INST,
[ - - ][ - - ]
[ - - ]
1794 : : vp1,
1795 : : vp1,
1796 : : {},
1797 : 1694 : std::vector<Node>{args[0].begin(), args[0].end()},
1798 : : *cdp)
1799 : 2539 : && addAletheStep(AletheRule::OR, vp2, vp2, {vp1}, {}, *cdp)
1800 : 7617 : && addAletheStep(AletheRule::RESOLUTION,
1801 : : res,
1802 : 1693 : nm->mkNode(Kind::SEXPR, d_cl, res),
1803 : 846 : {vp2, children[0]},
1804 : 846 : d_resPivots
1805 : 1693 : ? std::vector<Node>{children[0], d_false}
1806 : : : std::vector<Node>(),
1807 [ + + ]: 2539 : *cdp);
1808 : : }
1809 : : // ======== Alpha Equivalence
1810 : : //
1811 : : // Given the formula F := (forall ((y1 A1) ... (yn An)) G) and a
1812 : : // substitution sigma, the resulting Alethe steps justifying the conclusion
1813 : : // (= F F*sigma) depend on a number of conditions, which are detailed below.
1814 : : //
1815 : : // If sigma is the identity, F*sigma is the same as F, and the resulting
1816 : : // step is
1817 : : //
1818 : : // ------------------ refl
1819 : : // (cl (= F F))
1820 : : //
1821 : : // When sigma is the substitution {y1->z1, ..., yn->zn, ..., yn+k->zn+k}, we
1822 : : // are in the case where G has quantifiers whose variables are yn+1 ... yn+k
1823 : : // and they will be renamed to zn+1 ... zn+k. The generated Alethe proof is
1824 : : //
1825 : : // --------------------------------------- refl
1826 : : // (cl (= G G*{y1 -> z1, ..., yn -> zn}))
1827 : : // --------------------------------- bind, z1 ... zn (= y1 z1) ... (= yn zn)
1828 : : // (cl (= F (forall ((z1 A1) ... (zn An)) G*{y1 -> z1, ..., yn -> zn})))
1829 : : //
1830 : : // i.e., we drop the suffix of the substitution beyond the variables of the
1831 : : // outermost quantifier. This is valid in Alethe because the validity of
1832 : : // "refl", under a rule that introduces a context, such as "bind", is itself
1833 : : // tested modulo alpha-equivalence. An alternative would be to use the rest
1834 : : // of the substitution to do new "bind" steps for the innermost quantifiers.
1835 : : //
1836 : : // If sigma contains more than one variable with the same name but with
1837 : : // different types (which makes them different for cvc5 but not in the
1838 : : // substitution induced by Alethe's context), we introduce an intermediate
1839 : : // alpha equivalence step with fresh variables. For example if F := (forall
1840 : : // ((y1 A1) (y2 A2)) G) and sigma is the substitution {y1 -> z1, y2 -> y1},
1841 : : // where the "y1" in the right hand side has another type "T" other than
1842 : : // "A2", then the resulting steps are
1843 : : //
1844 : : // --------------------------------------- refl
1845 : : // (cl (= G G*{y1 -> @v1, y2 -> @v2})
1846 : : // --------------------------------- bind, @v1 @v2 (= y1 @v1) (= y1 @v2)
1847 : : // (cl (= F (forall ((@v1 A1) (@v2 A2)) G*{y1 -> @v1, y2 -> @v2})
1848 : : //
1849 : : // --------------------------------------------------- refl
1850 : : // (cl (= G*{y1->@v1, y2->@v2} (G*{y1->@v1, y2->@v2})*{@v1->z1, @v1->y1})
1851 : : // --------------------------------------- bind, z1 y1 (= @v1 z1) (= @v2 z2)
1852 : : // (cl (=
1853 : : // (forall ((@v1 A1) (@v2 A2)) G*{y1 -> @v1, y2 -> @v2})
1854 : : // (forall ((z1 A1) (y1 A2)) (G*{y1->@v1, y2->@v2})*{@v1->z1, @v1->y1})))
1855 : : //
1856 : : // and finally a transitivity step to conclude an equality between F and
1857 : : // (forall ((z1 A1) (y1 A2)) (G*{y1->@v1, y2->@v2})*{@v1->z1, @v1->y1}).
1858 : : //
1859 : : // If none of the conditions above applies, the resulting Alethe steps are
1860 : : //
1861 : : // ------------------ refl
1862 : : // (cl (= G G*sigma))
1863 : : // -------------------- bind, z1 ... zn (= y1 z1) ... (= yn zn)
1864 : : // (cl (= F (forall ((z1 A1) ... (zn An)) G*sigma)))
1865 : 20 : case ProofRule::ALPHA_EQUIV:
1866 : : {
1867 : : // If y1 ... yn are mapped to y1 ... yn it suffices to use a refl step
1868 : 20 : bool allSame = true;
1869 : 40 : std::unordered_set<std::string> lhsNames;
1870 [ + + ]: 56 : for (size_t i = 0, size = res[0][0].getNumChildren(); i < size; ++i)
1871 : : {
1872 : 144 : Node v0 = res[0][0][i], v1 = res[1][0][i];
1873 : 36 : lhsNames.insert(v0.getName());
1874 [ + + ][ - + ]: 36 : allSame = allSame && v0 == v1;
1875 : : }
1876 : : // when no renaming, no-op
1877 [ - + ]: 20 : if (allSame)
1878 : : {
1879 : 0 : return addAletheStep(AletheRule::REFL,
1880 : : res,
1881 : 0 : nm->mkNode(Kind::SEXPR, d_cl, res),
1882 : : {},
1883 : : {},
1884 : 0 : *cdp);
1885 : : }
1886 : : // check if any name in the rhs variables clashes with a name in the lhs
1887 : 20 : bool hasClash = false;
1888 [ + + ]: 56 : for (const Node& v : res[1][0])
1889 : : {
1890 [ + + ]: 36 : if (lhsNames.count(v.getName()))
1891 : : {
1892 : 8 : hasClash = true;
1893 : : }
1894 : : }
1895 : 20 : bool success = true;
1896 : 40 : Node lhsQ = res[0];
1897 : 40 : std::vector<Node> transEqs;
1898 [ + + ]: 20 : if (hasClash)
1899 : : {
1900 : : // create the variables to substitute the lhs variables
1901 : 12 : std::vector<Node> freshRenaming;
1902 [ + + ]: 18 : for (const Node& v : res[0][0])
1903 : : {
1904 : 12 : freshRenaming.emplace_back(NodeManager::mkBoundVar(v.getType()));
1905 : : }
1906 : 18 : std::vector<Node> vars{res[0][0].begin(), res[0][0].end()};
1907 : : Node lhsRenamed = res[0].substitute(vars.begin(),
1908 : : vars.end(),
1909 : : freshRenaming.begin(),
1910 : 12 : freshRenaming.end());
1911 : : // Reflexivity over the quantified bodies
1912 : : Node reflConc =
1913 : 18 : nm->mkNode(Kind::SEXPR, d_cl, res[0][1].eqNode(lhsRenamed[1]));
1914 : 6 : addAletheStep(AletheRule::REFL, reflConc, reflConc, {}, {}, *cdp);
1915 : : // Collect RHS variables first for arguments, then add the entries for
1916 : : // the substitution. In a "bind" rule we must always list all the
1917 : : // variables
1918 : 12 : std::vector<Node> bindArgs{freshRenaming.begin(), freshRenaming.end()};
1919 [ + + ]: 18 : for (size_t i = 0, size = freshRenaming.size(); i < size; ++i)
1920 : : {
1921 : 12 : bindArgs.push_back(vars[i].eqNode(freshRenaming[i]));
1922 : : }
1923 : 6 : transEqs.push_back(res[0].eqNode(lhsRenamed));
1924 : 24 : success &= addAletheStep(AletheRule::ANCHOR_BIND,
1925 : 6 : transEqs.back(),
1926 : 12 : nm->mkNode(Kind::SEXPR, d_cl, transEqs.back()),
1927 : : {reflConc},
1928 : : bindArgs,
1929 : 12 : *cdp);
1930 : 6 : lhsQ = lhsRenamed;
1931 : : }
1932 : : // Reflexivity over the quantified bodies
1933 : 60 : Node reflConc = nm->mkNode(Kind::SEXPR, d_cl, lhsQ[1].eqNode(res[1][1]));
1934 : 20 : addAletheStep(AletheRule::REFL, reflConc, reflConc, {}, {}, *cdp);
1935 : : // Collect RHS variables first for arguments, then add the entries for
1936 : : // the substitution. In a "bind" rule we must always list all the
1937 : : // variables
1938 : 60 : std::vector<Node> bindArgs{res[1][0].begin(), res[1][0].end()};
1939 [ + + ]: 56 : for (size_t i = 0, size = res[1][0].getNumChildren(); i < size; ++i)
1940 : : {
1941 : 36 : bindArgs.push_back(lhsQ[0][i].eqNode(res[1][0][i]));
1942 : : }
1943 : 20 : transEqs.push_back(lhsQ.eqNode(res[1]));
1944 : 80 : success &= addAletheStep(AletheRule::ANCHOR_BIND,
1945 : 20 : transEqs.back(),
1946 : 40 : nm->mkNode(Kind::SEXPR, d_cl, transEqs.back()),
1947 : : {reflConc},
1948 : : bindArgs,
1949 : 40 : *cdp);
1950 [ + + ][ - + ]: 20 : Assert(!hasClash || transEqs.size() == 2);
[ - + ][ - - ]
1951 [ + + ]: 20 : if (hasClash)
1952 : : {
1953 : : return success
1954 : 24 : && addAletheStep(AletheRule::TRANS,
1955 : : res,
1956 : 12 : nm->mkNode(Kind::SEXPR, d_cl, res),
1957 : : transEqs,
1958 : : {},
1959 [ + - ]: 6 : *cdp);
1960 : : }
1961 [ - + ][ - + ]: 14 : Assert(cdp->hasStep(res));
[ - - ]
1962 : 14 : return success;
1963 : : }
1964 : : // ======== Variable reordering
1965 : : // Let X = ((x1 T1) ... (xn Tn)), Y = ((y1 U1) ... (yn Un))
1966 : : // and Z = ((x1 T1) ... (xn Tn) (y1 U1) ... (yn Un))
1967 : : // Then, res is (cl (= (forall ((x1 T1) ... (xn Tn)) F) (forall ((y1 U1) ...
1968 : : // (yn Un)) F)))
1969 : : //
1970 : : // ----- QNT_RM_UNUSED
1971 : : // VP1
1972 : : // ----- SYMM ----- QNT_RM_UNUSED
1973 : : // VP2 VP3
1974 : : // ----------------- TRANS
1975 : : // res
1976 : : //
1977 : : // VP1: (cl (= (forall Z F) (forall X F)))
1978 : : // VP2: (cl (= (forall X F) (forall Z F)))
1979 : : // VP3: (cl (= (forall Z F) (forall Y F)))
1980 : 4 : case ProofRule::QUANT_VAR_REORDERING:
1981 : : {
1982 : 8 : Node forall_X = res[0];
1983 : 8 : Node forall_Y = res[1];
1984 : 8 : Node F = forall_X[1];
1985 : 8 : Node X = forall_X[0];
1986 : 8 : Node Y = forall_Y[0];
1987 : 8 : std::vector<Node> Z(X.begin(), X.end());
1988 : 4 : Z.insert(Z.end(), Y.begin(), Y.end());
1989 : : Node forall_Z =
1990 : 12 : nm->mkNode(Kind::FORALL, nm->mkNode(Kind::BOUND_VAR_LIST, Z), F);
1991 : 12 : Node vp1 = nm->mkNode(Kind::EQUAL, forall_Z, forall_X);
1992 : 12 : Node vp2 = nm->mkNode(Kind::EQUAL, forall_X, forall_Z);
1993 : 8 : Node vp3 = nm->mkNode(Kind::EQUAL, forall_Z, forall_Y);
1994 : :
1995 [ + - ][ - - ]: 8 : return addAletheStep(AletheRule::QNT_RM_UNUSED,
[ - - ][ - - ]
1996 : : vp1,
1997 : 8 : nm->mkNode(Kind::SEXPR, d_cl, vp1),
1998 : : {},
1999 : : {},
2000 : : *cdp)
2001 : 20 : && addAletheStep(AletheRule::SYMM,
2002 : : vp2,
2003 : 8 : nm->mkNode(Kind::SEXPR, d_cl, vp2),
2004 : : {vp1},
2005 : : {},
2006 : 4 : *cdp)
2007 : 16 : && addAletheStep(AletheRule::QNT_RM_UNUSED,
2008 : : vp3,
2009 : 8 : nm->mkNode(Kind::SEXPR, d_cl, vp3),
2010 : : {},
2011 : : {},
2012 : : *cdp)
2013 : 32 : && addAletheStep(AletheRule::TRANS,
2014 : : res,
2015 : 8 : nm->mkNode(Kind::SEXPR, d_cl, res),
2016 : : {vp2, vp3},
2017 : : {},
2018 [ + - ]: 12 : *cdp);
2019 : : }
2020 : : //================================================= Arithmetic rules
2021 : : // ======== Adding Scaled Inequalities
2022 : : //
2023 : : // -------------------------------------- LA_GENERIC
2024 : : // (cl (not P1) ... (not Pn) (>< t1 t2)) P1 ... Pn
2025 : : // ------------------------------------------------------------- RESOLUTION
2026 : : // (cl (>< t1 t2))*
2027 : : //
2028 : : // * the corresponding proof node is (>< t1 t2)
2029 : 6303 : case ProofRule::ARITH_SUM_UB:
2030 : : {
2031 : : // if the conclusion were an equality we'd need to phrase LA_GENERIC in
2032 : : // terms of disequalities, but ARITH_SUM_UB does not have equalities as
2033 : : // conclusions
2034 [ - + ][ - + ]: 6303 : Assert(res.getKind() != Kind::EQUAL);
[ - - ]
2035 : 12606 : Node one = nm->mkConstReal(Rational(1));
2036 : 12606 : Node minusOne = nm->mkConstReal(Rational(-1));
2037 : 12606 : std::vector<Node> resArgs;
2038 : 12606 : std::vector<Node> resChildren;
2039 : 25212 : std::vector<Node> lits{d_cl};
2040 [ + + ]: 37059 : for (const Node& child : children)
2041 : : {
2042 : 30756 : lits.push_back(child.notNode());
2043 : : // equalities are multiplied by minus 1 rather than 1
2044 [ + + ]: 30756 : new_args.push_back(child.getKind() == Kind::EQUAL ? minusOne : one);
2045 : 30756 : resArgs.push_back(child);
2046 : 30756 : resArgs.push_back(d_false);
2047 : : }
2048 : 6303 : lits.push_back(res);
2049 : 6303 : new_args.push_back(one);
2050 : 6303 : Node laGen = nm->mkNode(Kind::SEXPR, lits);
2051 : 6303 : addAletheStep(AletheRule::LA_GENERIC, laGen, laGen, {}, new_args, *cdp);
2052 : 6303 : resChildren.push_back(laGen);
2053 : 6303 : resChildren.insert(resChildren.end(), children.begin(), children.end());
2054 : 12606 : return addAletheStep(AletheRule::RESOLUTION,
2055 : : res,
2056 : 12606 : nm->mkNode(Kind::SEXPR, d_cl, res),
2057 : : resChildren,
2058 [ - + ]: 12606 : d_resPivots ? resArgs : std::vector<Node>(),
2059 : 6303 : *cdp);
2060 : : }
2061 : : // Direct translation
2062 : 5612 : case ProofRule::ARITH_MULT_POS:
2063 : : case ProofRule::ARITH_MULT_NEG:
2064 : : {
2065 [ + + ]: 22448 : return addAletheStep(id == ProofRule::ARITH_MULT_POS
2066 : : ? AletheRule::LA_MULT_POS
2067 : : : AletheRule::LA_MULT_NEG,
2068 : : res,
2069 : 11224 : nm->mkNode(Kind::SEXPR, d_cl, res),
2070 : : children,
2071 : : {},
2072 : 5612 : *cdp);
2073 : : }
2074 : : // ======== Tightening Strict Integer Upper Bounds
2075 : : //
2076 : : // ----- LA_GENERIC, 1
2077 : : // VP1 P
2078 : : // ------------------------------------- RESOLUTION
2079 : : // (cl (<= i greatestIntLessThan(c)))*
2080 : : //
2081 : : // VP1: (cl (not (< i c)) (<= i greatestIntLessThan(c)))
2082 : : //
2083 : : // * the corresponding proof node is (<= i greatestIntLessThan(c))
2084 : 1326 : case ProofRule::INT_TIGHT_UB:
2085 : : {
2086 : 3978 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
2087 : 6630 : std::vector<Node> new_children = {vp1, children[0]};
2088 : 1326 : Node one = nm->mkConstReal(Rational(1));
2089 : 1326 : new_args.push_back(one);
2090 : 1326 : new_args.push_back(one);
2091 [ + - ][ + - ]: 1326 : return addAletheStep(AletheRule::LA_GENERIC, vp1, vp1, {}, new_args, *cdp)
[ - - ][ - - ]
[ - - ]
2092 [ + - ][ + - ]: 3978 : && addAletheStep(AletheRule::RESOLUTION,
[ - - ]
2093 : : res,
2094 : 2652 : nm->mkNode(Kind::SEXPR, d_cl, res),
2095 : : new_children,
2096 : 1326 : d_resPivots
2097 : 2652 : ? std::vector<Node>{children[0], d_false}
2098 : : : std::vector<Node>(),
2099 [ + - ]: 1326 : *cdp);
2100 : : }
2101 : : // ======== Tightening Strict Integer Lower Bounds
2102 : : //
2103 : : // ----- LA_GENERIC, 1
2104 : : // VP1 P
2105 : : // ------------------------------------- RESOLUTION
2106 : : // (cl (>= i leastIntGreaterThan(c)))*
2107 : : //
2108 : : // VP1: (cl (not (> i c)) (>= i leastIntGreaterThan(c)))
2109 : : //
2110 : : // * the corresponding proof node is (>= i leastIntGreaterThan(c))
2111 : 133 : case ProofRule::INT_TIGHT_LB:
2112 : : {
2113 : 399 : Node vp1 = nm->mkNode(Kind::SEXPR, d_cl, children[0].notNode(), res);
2114 : 665 : std::vector<Node> new_children = {vp1, children[0]};
2115 : 133 : Node one = nm->mkConstReal(Rational(1));
2116 : 133 : new_args.push_back(one);
2117 : 133 : new_args.push_back(one);
2118 [ + - ][ + - ]: 133 : return addAletheStep(AletheRule::LA_GENERIC, vp1, vp1, {}, new_args, *cdp)
[ - - ][ - - ]
[ - - ]
2119 [ + - ][ + - ]: 399 : && addAletheStep(AletheRule::RESOLUTION,
[ - - ]
2120 : : res,
2121 : 266 : nm->mkNode(Kind::SEXPR, d_cl, res),
2122 : : new_children,
2123 : 133 : d_resPivots
2124 : 266 : ? std::vector<Node>{children[0], d_false}
2125 : : : std::vector<Node>(),
2126 [ + - ]: 133 : *cdp);
2127 : : }
2128 : : // ======== Trichotomy of the reals
2129 : : //
2130 : : // C is always of the format (= x c), (> x c) or (< x c). It has to be
2131 : : // concluded from A, B, which are (=> x c), (<= x c), or (not (= x c)). In
2132 : : // some cases, rather than (=> x c) we can actually have its negation, i.e.,
2133 : : // (not (< x c)), which is accounted for below.
2134 : : //
2135 : : // The convertion into Alethe is based on la_disequality, which has much
2136 : : // the same semantics as ARITH_TRICHOTOMY. The following subproof is
2137 : : // common to all the cases (we will refer to it as PI_0):
2138 : : //
2139 : : // ------------------------------------------------------ la_disequality
2140 : : // (cl (or (= x c) (not (<= x c)) (not (<= c x))))
2141 : : // -------------------------------------------------------- or
2142 : : // (cl (= x c) (not (<= x c)) (not (<= c x)))
2143 : : //
2144 : : // The transformations also use the COMP_SIMPLIFY rule in Alethe, which
2145 : : // connects strict and non-strict inequalities. The details for each
2146 : : // conversion are given for each case.
2147 : 874 : case ProofRule::ARITH_TRICHOTOMY:
2148 : : {
2149 : 874 : bool success = true;
2150 : 1748 : Node equal, lesser, greater;
2151 : 874 : Kind k = res.getKind();
2152 [ + + ][ + + ]: 874 : Assert(k == Kind::EQUAL || k == Kind::GT || k == Kind::LT)
[ - + ][ - + ]
[ - - ]
2153 : 0 : << "kind is " << k << "\n";
2154 : 1748 : Node x = res[0], c = res[1];
2155 : : switch (k)
2156 : : {
2157 : 568 : case Kind::EQUAL:
2158 : : {
2159 [ + - ]: 568 : Trace("alethe-proof") << "..case EQUAL\n";
2160 : 1136 : Node leq, geq;
2161 [ + + ]: 568 : if (children[0].getKind() == Kind::LEQ)
2162 : : {
2163 : 20 : leq = children[0];
2164 : 20 : geq = children[1];
2165 : : }
2166 : : else
2167 : : {
2168 : 548 : leq = children[1];
2169 : 548 : geq = children[0];
2170 : : }
2171 : 1704 : Node leqInverted = nm->mkNode(Kind::LEQ, geq[1], geq[0]);
2172 : : // The subproof built is (where @p1 is the premise for "geq", @p2 is
2173 : : // "leqInverted")
2174 : : //
2175 : : // PI_1:
2176 : : // with @p0: (= (=> x c) (<= c x))
2177 : : // with @p1: (=> x c)
2178 : : // with @p2: (<= c x)
2179 : : //
2180 : : // ----- comp_simplify -------------------equiv_pos2 --- geq
2181 : : // @p0 (cl (not @p0) (not @p1) @p2) @p1
2182 : : // ---------------------------------------------------- resolution
2183 : : // @p2
2184 : : //
2185 : : // Then we combine with the proof PI_0 and use the other premise
2186 : : // (for "leq")
2187 : : //
2188 : : // --------- leq
2189 : : // PI_0 (<= x c) PI_1
2190 : : // --------------------------- resolution
2191 : : // (= x c)
2192 : : //
2193 : : // where (= x c) is the expected result
2194 : :
2195 : : // We first build PI_0:
2196 : : Node laDiseqOr = nm->mkNode(
2197 : : Kind::SEXPR,
2198 : 568 : d_cl,
2199 : 1704 : nm->mkNode(Kind::OR, res, leq.notNode(), leqInverted.notNode()));
2200 : : Node laDiseqCl = nm->mkNode(
2201 : 3976 : Kind::SEXPR, {d_cl, res, leq.notNode(), leqInverted.notNode()});
2202 : 568 : success &=
2203 [ + - ][ + - ]: 568 : addAletheStep(AletheRule::LA_DISEQUALITY,
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
2204 : : laDiseqOr,
2205 : : laDiseqOr,
2206 : : {},
2207 : : {},
2208 : : *cdp)
2209 : 1704 : && addAletheStep(
2210 [ + - ]: 1136 : AletheRule::OR, laDiseqCl, laDiseqCl, {laDiseqOr}, {}, *cdp);
2211 : : // Now we build PI_1:
2212 : 1136 : Node compSimp = geq.eqNode(leqInverted);
2213 : 1704 : Node compSimpCl = nm->mkNode(Kind::SEXPR, d_cl, compSimp);
2214 : 1136 : success &= addAletheStep(
2215 : 568 : AletheRule::COMP_SIMPLIFY, compSimpCl, compSimpCl, {}, {}, *cdp);
2216 : : Node equivPos2Cl = nm->mkNode(
2217 : : Kind::SEXPR,
2218 : 3976 : {d_cl, compSimp.notNode(), geq.notNode(), leqInverted});
2219 : 1136 : success &= addAletheStep(
2220 : 568 : AletheRule::EQUIV_POS2, equivPos2Cl, equivPos2Cl, {}, {}, *cdp);
2221 : 1704 : Node resPi1Conc = nm->mkNode(Kind::SEXPR, d_cl, leqInverted);
2222 [ + + ][ - - ]: 2840 : success &= addAletheStep(
2223 : : AletheRule::RESOLUTION,
2224 : : resPi1Conc,
2225 : : resPi1Conc,
2226 : : {compSimpCl, equivPos2Cl, geq},
2227 : 1136 : d_resPivots ? std::vector<Node>{compSimp, d_true, geq, d_false}
2228 : : : std::vector<Node>(),
2229 : 2272 : *cdp);
2230 : : // Now we build the final resultion
2231 [ + + ][ - - ]: 3408 : success &= addAletheStep(
2232 : : AletheRule::RESOLUTION,
2233 : : res,
2234 : 1136 : nm->mkNode(Kind::SEXPR, d_cl, res),
2235 : : {leq, laDiseqCl, resPi1Conc},
2236 : 1136 : d_resPivots ? std::vector<Node>{leq, d_true, leqInverted, d_false}
2237 : : : std::vector<Node>(),
2238 : 2272 : *cdp);
2239 : 568 : break;
2240 : : }
2241 : 210 : case Kind::GT:
2242 : : {
2243 [ + - ]: 210 : Trace("alethe-proof") << "..case GT\n";
2244 : 420 : Node geq, notEq;
2245 : 210 : Kind kc0 = children[0].getKind();
2246 : 210 : if (kc0 == Kind::GEQ
2247 [ + + ][ + - ]: 210 : || (kc0 == Kind::NOT && children[0][0].getKind() == Kind::LT))
[ + + ][ + + ]
[ + + ][ - - ]
2248 : : {
2249 : 151 : geq = children[0];
2250 : 151 : notEq = children[1];
2251 : : }
2252 : : else
2253 : : {
2254 : 59 : geq = children[1];
2255 : 59 : notEq = children[0];
2256 : : }
2257 : 630 : Node leq = nm->mkNode(Kind::LEQ, x, c);
2258 : 630 : Node leqInverted = nm->mkNode(Kind::LEQ, c, x);
2259 : 420 : Assert(notEq.getKind() == Kind::NOT
2260 : : && notEq[0].getKind() == Kind::EQUAL);
2261 : : // it may be that the premise supposed to be (>= x c) is actually the
2262 : : // literal (not (< x c)). In this case we use that premise to deriv
2263 : : // (>= x c), so that the reconstruction below remains the same
2264 [ + + ]: 210 : if (geq.getKind() != Kind::GEQ)
2265 : : {
2266 : 92 : Assert(geq.getKind() == Kind::NOT && geq[0].getKind() == Kind::LT);
2267 : 92 : Node notLt = geq;
2268 : 46 : geq = nm->mkNode(Kind::GEQ, x, c);
2269 : : // @pa: (= (< x c) (not (<= c x)))
2270 : : // @pb: (< x c)
2271 : : // @pc: (<= c x)
2272 : : // notLT : (not @pb)
2273 : : //
2274 : : // PI_a:
2275 : : //
2276 : : // --- comp_simplify --------------------- equiv_pos1 ----- notLT
2277 : : // @pa (cl (not @pa) @pb (not (not @pc))) (not @pb)
2278 : : // ------------------------------------------------------ resolution
2279 : : // (cl (not (not @pc)))
2280 : : //
2281 : : //
2282 : : // PI_b:
2283 : : //
2284 : : // ------------------------------ NOT_NOT -------------------- PI_a
2285 : : // (cl (not (not (not @pc))) @pc) (cl (not (not @pc)))
2286 : : // ------------------------------------------------------ resolution
2287 : : // @pc
2288 : : //
2289 : : // PI_c:
2290 : : //
2291 : : // @pd: (= (>= x c) (<= c x))
2292 : : //
2293 : : // --- comp_simplify -------------------------- equiv_pos1 --- PI_b
2294 : : // @pd (cl (not @pd) (>= x c) (not @pc)) @pc
2295 : : // ------------------------------------------------------ resolution
2296 : : // (cl (>= x c))
2297 : : //
2298 : 92 : Node pb = notLt[0];
2299 : 92 : Node pc = leqInverted;
2300 : 92 : Node pa = pb.eqNode(pc.notNode());
2301 : : // We first build PI_a:
2302 : 138 : Node compSimpCl = nm->mkNode(Kind::SEXPR, d_cl, pa);
2303 : 92 : success &= addAletheStep(AletheRule::COMP_SIMPLIFY,
2304 : : compSimpCl,
2305 : : compSimpCl,
2306 : : {},
2307 : : {},
2308 : 46 : *cdp);
2309 : : Node equivPos1Cl = nm->mkNode(
2310 : 322 : Kind::SEXPR, {d_cl, pa.notNode(), pb, pc.notNode().notNode()});
2311 : 92 : success &= addAletheStep(
2312 : 46 : AletheRule::EQUIV_POS1, equivPos1Cl, equivPos1Cl, {}, {}, *cdp);
2313 : : Node resPiAConc =
2314 : 138 : nm->mkNode(Kind::SEXPR, d_cl, pc.notNode().notNode());
2315 [ + + ][ - - ]: 230 : success &= addAletheStep(
2316 : : AletheRule::RESOLUTION,
2317 : : resPiAConc,
2318 : : resPiAConc,
2319 : : {compSimpCl, equivPos1Cl, pb.notNode()},
2320 : 92 : d_resPivots ? std::vector<Node>{pa, d_true, pb, d_true}
2321 : : : std::vector<Node>(),
2322 : 184 : *cdp);
2323 : : // We then build PI_b:
2324 : 138 : Node notNot = pc.notNode().notNode().notNode();
2325 : : Node notNotCl =
2326 : 138 : nm->mkNode(Kind::SEXPR, d_cl, pc.notNode().notNode().notNode());
2327 : 92 : success &= addAletheStep(
2328 : 46 : AletheRule::NOT_NOT, notNotCl, notNotCl, {}, {}, *cdp);
2329 : : Node resPiBConc =
2330 : 138 : nm->mkNode(Kind::SEXPR, d_cl, pc.notNode().notNode());
2331 [ + + ][ - - ]: 184 : success &= addAletheStep(
2332 : : AletheRule::RESOLUTION,
2333 : : resPiBConc,
2334 : : resPiBConc,
2335 : : {notNotCl, resPiAConc},
2336 : 92 : d_resPivots ? std::vector<Node>{pc.notNode().notNode(), d_false}
2337 : : : std::vector<Node>(),
2338 : 138 : *cdp);
2339 : : // Now we conclude, building PI_c
2340 : 46 : Node pd = geq.eqNode(pc);
2341 : 46 : compSimpCl = nm->mkNode(Kind::SEXPR, d_cl, pd);
2342 : 92 : success &= addAletheStep(AletheRule::COMP_SIMPLIFY,
2343 : : compSimpCl,
2344 : : compSimpCl,
2345 : : {},
2346 : : {},
2347 : 46 : *cdp);
2348 [ + + ][ - - ]: 322 : equivPos1Cl = nm->mkNode(Kind::SEXPR,
2349 : 368 : {d_cl, pd.notNode(), geq, pc.notNode()});
2350 : 92 : success &= addAletheStep(
2351 : 46 : AletheRule::EQUIV_POS1, equivPos1Cl, equivPos1Cl, {}, {}, *cdp);
2352 [ + + ][ - - ]: 276 : success &= addAletheStep(
2353 : : AletheRule::RESOLUTION,
2354 : : geq,
2355 : 92 : nm->mkNode(Kind::SEXPR, d_cl, geq),
2356 : : {compSimpCl, equivPos1Cl, resPiBConc},
2357 : 92 : d_resPivots ? std::vector<Node>{pd, d_true, pc, d_false}
2358 : : : std::vector<Node>(),
2359 : 184 : *cdp);
2360 : : }
2361 : : // The subproof built here uses the PI_1 defined in the case above,
2362 : : // where the premise for "geq" is used to conclude leqInverted. Here
2363 : : // @p4 is "res", @p5 is "leq". The goal of PI_2 is to conclude (not
2364 : : // (not @p5)), which can remove the element from the conclusion of
2365 : : // PI_0 that is (not @p5). The conclusion of PI_1 and notEq exclude
2366 : : // the other elements, such that only @p4 will remain, the expected
2367 : : // conclusion.
2368 : : //
2369 : : // PI_2:
2370 : : // with @p3: (= (> x c) (not (<= x c)))
2371 : : // with @p4: (> x c)
2372 : : // with @p5: (<= x c)
2373 : : //
2374 : : // ----- comp_simplify ----------------------------------- equiv_pos1
2375 : : // @p3 (cl (not @p3) @p4 (not (not @p5)))
2376 : : // ------------------------------------------------------- resolution
2377 : : // (cl @p4 (not (not @p5)))
2378 : : //
2379 : : // Then we combine the proofs PI_0, the premise for "notEq", and
2380 : : // PI_1 and PI_2:
2381 : : //
2382 : : // --------- notEq
2383 : : // PI_0 (not (= x c)) PI_1 PI_2
2384 : : // ------------------------------------- resolution
2385 : : // (> x c)
2386 : : //
2387 : : // where (= x c) is the expected result
2388 : :
2389 : : // We first build PI_0:
2390 : : Node laDiseqOr = nm->mkNode(
2391 : : Kind::SEXPR,
2392 : 210 : d_cl,
2393 : 840 : nm->mkNode(
2394 : 1260 : Kind::OR, notEq[0], leq.notNode(), leqInverted.notNode()));
2395 : : Node laDiseqCl = nm->mkNode(
2396 : : Kind::SEXPR,
2397 : 1470 : {d_cl, notEq[0], leq.notNode(), leqInverted.notNode()});
2398 : 210 : success &=
2399 [ + - ][ + - ]: 210 : addAletheStep(AletheRule::LA_DISEQUALITY,
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
2400 : : laDiseqOr,
2401 : : laDiseqOr,
2402 : : {},
2403 : : {},
2404 : : *cdp)
2405 : 630 : && addAletheStep(
2406 [ + - ]: 420 : AletheRule::OR, laDiseqCl, laDiseqCl, {laDiseqOr}, {}, *cdp);
2407 : : // Now we build PI_1:
2408 : 420 : Node compSimp = geq.eqNode(leqInverted);
2409 : 630 : Node compSimpCl = nm->mkNode(Kind::SEXPR, d_cl, compSimp);
2410 : 420 : success &= addAletheStep(
2411 : 210 : AletheRule::COMP_SIMPLIFY, compSimpCl, compSimpCl, {}, {}, *cdp);
2412 : : Node equivPos2Cl = nm->mkNode(
2413 : : Kind::SEXPR,
2414 : 1470 : {d_cl, compSimp.notNode(), geq.notNode(), leqInverted});
2415 : 420 : success &= addAletheStep(
2416 : 210 : AletheRule::EQUIV_POS2, equivPos2Cl, equivPos2Cl, {}, {}, *cdp);
2417 : 630 : Node resPi1Conc = nm->mkNode(Kind::SEXPR, d_cl, leqInverted);
2418 [ + + ][ - - ]: 1050 : success &= addAletheStep(
2419 : : AletheRule::RESOLUTION,
2420 : : resPi1Conc,
2421 : : resPi1Conc,
2422 : : {compSimpCl, equivPos2Cl, geq},
2423 : 420 : d_resPivots ? std::vector<Node>{compSimp, d_true, geq, d_false}
2424 : : : std::vector<Node>(),
2425 : 840 : *cdp);
2426 : : // Now we build PI_2
2427 : 420 : Node compSimp2 = res.eqNode(leq.notNode());
2428 : 630 : Node compSimp2Cl = nm->mkNode(Kind::SEXPR, d_cl, compSimp2);
2429 : 420 : success &= addAletheStep(AletheRule::COMP_SIMPLIFY,
2430 : : compSimp2Cl,
2431 : : compSimp2Cl,
2432 : : {},
2433 : : {},
2434 : 210 : *cdp);
2435 : : Node equivPos1Cl = nm->mkNode(
2436 : : Kind::SEXPR,
2437 : 1470 : {d_cl, compSimp2.notNode(), res, leq.notNode().notNode()});
2438 : 420 : success &= addAletheStep(
2439 : 210 : AletheRule::EQUIV_POS1, equivPos1Cl, equivPos1Cl, {}, {}, *cdp);
2440 : : Node resPi2Conc =
2441 : 630 : nm->mkNode(Kind::SEXPR, d_cl, res, leq.notNode().notNode());
2442 : 210 : success &=
2443 [ + + ][ - - ]: 840 : addAletheStep(AletheRule::RESOLUTION,
2444 : : resPi2Conc,
2445 : : resPi2Conc,
2446 : : {compSimp2Cl, equivPos1Cl},
2447 : 420 : d_resPivots ? std::vector<Node>{compSimp2, d_true}
2448 : : : std::vector<Node>(),
2449 : 630 : *cdp);
2450 : : // Now we build the final resolution
2451 : 210 : success &=
2452 [ + + ][ - - ]: 1470 : addAletheStep(AletheRule::RESOLUTION,
2453 : : res,
2454 : 420 : nm->mkNode(Kind::SEXPR, d_cl, res),
2455 : : {notEq, laDiseqCl, resPi1Conc, resPi2Conc},
2456 : 420 : d_resPivots ? std::vector<Node>{notEq[0],
2457 : 0 : d_false,
2458 : : leqInverted,
2459 : 0 : d_false,
2460 : : leq.notNode(),
2461 : 0 : d_true}
2462 : : : std::vector<Node>(),
2463 : 1050 : *cdp);
2464 : 210 : break;
2465 : : }
2466 : 96 : case Kind::LT:
2467 : : {
2468 [ + - ]: 96 : Trace("alethe-proof") << "..case LT\n";
2469 : 192 : Node leq, notEq;
2470 : 96 : Kind kc0 = children[0].getKind();
2471 : 96 : if (kc0 == Kind::LEQ
2472 [ + + ][ + - ]: 96 : || (kc0 == Kind::NOT && children[0][0].getKind() == Kind::LT))
[ - + ][ + + ]
[ + + ][ - - ]
2473 : : {
2474 : 39 : leq = children[0];
2475 : 39 : notEq = children[1];
2476 : : }
2477 : : else
2478 : : {
2479 : 57 : leq = children[1];
2480 : 57 : notEq = children[0];
2481 : : }
2482 : 192 : Assert(notEq.getKind() == Kind::NOT
2483 : : && notEq[0].getKind() == Kind::EQUAL);
2484 [ - + ][ - + ]: 96 : Assert(leq.getKind() == Kind::LEQ);
[ - - ]
2485 : 288 : Node leqInverted = nm->mkNode(Kind::LEQ, c, x);
2486 : : // The subproof built here uses the PI_0 defined in the case
2487 : : // above. Note that @p7 is res and @p8 is leqInverted.
2488 : : //
2489 : : // PI_3:
2490 : : // with @p6: (= (< x c) (not (<= c x)))
2491 : : // with @p7: (< x c)
2492 : : // with @p8: (<= c x)
2493 : : //
2494 : : // ----- comp_simplify ----------------------------------- equiv_pos1
2495 : : // @p6 (cl (not @p6) @p7 (not (not @p8)))
2496 : : // -------------------------------------------------------- resolution
2497 : : // (cl @p7 (not (not @p8)))
2498 : : //
2499 : : // Then we combine the proofs PI_0, the premise for "notEq", the
2500 : : // premise for "leq", and PI_3 above:
2501 : : //
2502 : : // ------- notEq -----leq ---------------------------- PI_3
2503 : : // PI_0 (not (= x c)) (<= x c) (cl (< x c) (not (not (<= c x))))
2504 : : // -------------------------------------------------------- resolution
2505 : : // (< x c)
2506 : : //
2507 : : // where (< x c) is the expected result
2508 : :
2509 : : // We first build PI_0:
2510 : : Node laDiseqOr = nm->mkNode(
2511 : : Kind::SEXPR,
2512 : 96 : d_cl,
2513 : 384 : nm->mkNode(
2514 : 576 : Kind::OR, notEq[0], leq.notNode(), leqInverted.notNode()));
2515 : : Node laDiseqCl = nm->mkNode(
2516 : : Kind::SEXPR,
2517 : 672 : {d_cl, notEq[0], leq.notNode(), leqInverted.notNode()});
2518 : 96 : success &=
2519 [ + - ][ + - ]: 96 : addAletheStep(AletheRule::LA_DISEQUALITY,
[ + - ][ - - ]
[ - - ][ - - ]
[ - - ]
2520 : : laDiseqOr,
2521 : : laDiseqOr,
2522 : : {},
2523 : : {},
2524 : : *cdp)
2525 : 288 : && addAletheStep(
2526 [ + - ]: 192 : AletheRule::OR, laDiseqCl, laDiseqCl, {laDiseqOr}, {}, *cdp);
2527 : : // Now we build PI_3:
2528 : 192 : Node compSimp = res.eqNode(leqInverted.notNode());
2529 : 288 : Node compSimpCl = nm->mkNode(Kind::SEXPR, d_cl, compSimp);
2530 : 192 : success &= addAletheStep(
2531 : 96 : AletheRule::COMP_SIMPLIFY, compSimpCl, compSimpCl, {}, {}, *cdp);
2532 : : Node equivPos1Cl = nm->mkNode(
2533 : : Kind::SEXPR,
2534 : 672 : {d_cl, compSimp.notNode(), res, leqInverted.notNode().notNode()});
2535 : 192 : success &= addAletheStep(
2536 : 96 : AletheRule::EQUIV_POS1, equivPos1Cl, equivPos1Cl, {}, {}, *cdp);
2537 : : // We do a single resolution step , inlining the one finishing PI_3
2538 : : // above, to build the final resolution
2539 [ + + ][ - - ]: 768 : success &= addAletheStep(
2540 : : AletheRule::RESOLUTION,
2541 : : res,
2542 : 192 : nm->mkNode(Kind::SEXPR, d_cl, res),
2543 : : {laDiseqCl, notEq, leq, equivPos1Cl, compSimpCl},
2544 : 192 : d_resPivots ? std::vector<Node>{notEq[0],
2545 : 0 : d_true,
2546 : : leq,
2547 : 0 : d_false,
2548 : : leqInverted.notNode(),
2549 : 0 : d_true,
2550 : : compSimp,
2551 : 0 : d_false}
2552 : : : std::vector<Node>(),
2553 : 576 : *cdp);
2554 : 96 : break;
2555 : : }
2556 : 0 : default:
2557 : : {
2558 : 0 : Unreachable() << "should not have gotten here";
2559 : : }
2560 : : }
2561 : 874 : return success;
2562 : : }
2563 : 120141 : default:
2564 : : {
2565 [ + - ]: 240282 : Trace("alethe-proof")
2566 : 0 : << "... rule not translated yet " << id << " / " << res << " "
2567 : 120141 : << children << " " << args << std::endl;
2568 : 240282 : std::stringstream ss;
2569 : 120141 : ss << "\"" << id << "\"";
2570 : : std::vector<Node> newArgs{
2571 : 480564 : NodeManager::mkRawSymbol(ss.str(), nm->sExprType())};
2572 : 120141 : newArgs.insert(newArgs.end(), args.begin(), args.end());
2573 : 240282 : return addAletheStep(AletheRule::HOLE,
2574 : : res,
2575 : 240282 : nm->mkNode(Kind::SEXPR, d_cl, res),
2576 : : children,
2577 : : newArgs,
2578 : 120141 : *cdp);
2579 : : }
2580 : : }
2581 : : Trace("alethe-proof") << "... error translating rule " << id << " / " << res
2582 : : << " " << children << " " << args << std::endl;
2583 : : return false;
2584 : : }
2585 : :
2586 : 14893 : bool AletheProofPostprocessCallback::maybeReplacePremiseProof(Node premise,
2587 : : CDProof* cdp)
2588 : : {
2589 : : // Test if the proof of premise concludes a non-singleton clause. Assumptions
2590 : : // always succeed the test.
2591 : 29786 : std::shared_ptr<ProofNode> premisePf = cdp->getProofFor(premise);
2592 [ + + ]: 14893 : if (premisePf->getRule() == ProofRule::ASSUME)
2593 : : {
2594 : 332 : return false;
2595 : : }
2596 : 29122 : Node premisePfConclusion = premisePf->getArguments()[2];
2597 : : // not a proof of a non-singleton clause
2598 : 43683 : if (premisePfConclusion.getNumChildren() <= 2
2599 [ + + ][ - + ]: 14561 : || premisePfConclusion[0] != d_cl)
[ + + ][ + + ]
[ - - ]
2600 : : {
2601 : 3657 : return false;
2602 : : }
2603 : : // If this resolution child is used as a singleton OR but the rule
2604 : : // justifying it concludes a clause, then we are often in this scenario:
2605 : : //
2606 : : // (or t1 ... tn)
2607 : : // -------------- OR
2608 : : // (cl t1 ... tn)
2609 : : // ---------------- FACTORING/REORDERING
2610 : : // (cl t1' ... tn')
2611 : : //
2612 : : // where what is used in the resolution is actually (or t1' ... tn').
2613 : : //
2614 : : // This happens when (or t1' ... tn') has been added to the SAT solver as
2615 : : // a literal as well, and its node clashed with the conclusion of the
2616 : : // FACTORING/REORDERING step.
2617 : : //
2618 : : // When this is happening at one level, as in the example above, a solution is
2619 : : // to *not* use FACTORING/REORDERING (which in Alethe operate on clauses) but
2620 : : // generate a proof to obtain the expected node (or t1' ... tn') from the
2621 : : // original node (or t1 ... tn).
2622 : : //
2623 : : // If the change is due to FACTORING, this can be easily obtained via
2624 : : // rewriting (with OR_SIMPLIFY), equivalence elimination, and resolution.
2625 : : //
2626 : : // Otherise we are either in the case of REORDERING or in a case where we
2627 : : // cannot easily access a proof of (or t1 ... tn). In both case we will derive
2628 : : // (cl (or t1' ... tn')) using n or_neg steps, as shown below.
2629 : 10904 : NodeManager* nm = nodeManager();
2630 [ + - ]: 10904 : Trace("alethe-proof") << "\n";
2631 : 10904 : AletheRule premiseProofRule = getAletheRule(premisePf->getArguments()[0]);
2632 : 10904 : if (premiseProofRule == AletheRule::CONTRACTION
2633 [ + + ][ - + ]: 10904 : && getAletheRule(premisePf->getChildren()[0]->getArguments()[0])
[ + + ][ - + ]
[ - - ]
2634 : : == AletheRule::OR)
2635 : : {
2636 : : // get great grand child
2637 : : std::shared_ptr<ProofNode> premiseChildPf =
2638 : 0 : premisePf->getChildren()[0]->getChildren()[0];
2639 : 0 : Node premiseChildConclusion = premiseChildPf->getResult();
2640 : : // Note that we need to add this proof node explicitly to cdp because it
2641 : : // does not have a step for premiseChildConclusion. Rather it is only
2642 : : // present in cdp as a descendant of premisePf (which is in cdp), so if
2643 : : // premisePf is to be lost, then so will premiseChildPf. By adding
2644 : : // premiseChildPf explicitly, it can be retrieved to justify
2645 : : // premiseChildConclusion when requested.
2646 : 0 : cdp->addProof(premiseChildPf);
2647 : : // equate it to what we expect. If the premise rule is CONTRACTION, we can
2648 : : // justify it via OR_SIMPLIFY. Otherwise...
2649 : 0 : Node equiv = premiseChildConclusion.eqNode(premise);
2650 : 0 : bool success = true;
2651 [ - - ]: 0 : if (premiseProofRule == AletheRule::CONTRACTION)
2652 : : {
2653 : 0 : success &= addAletheStep(AletheRule::OR_SIMPLIFY,
2654 : : equiv,
2655 : 0 : nm->mkNode(Kind::SEXPR, d_cl, equiv),
2656 : : {},
2657 : : {},
2658 : 0 : *cdp);
2659 : : Node equivElim = nm->mkNode(
2660 : : Kind::SEXPR,
2661 : 0 : {d_cl, equiv.notNode(), premiseChildConclusion.notNode(), premise});
2662 : 0 : success &= addAletheStep(
2663 : 0 : AletheRule::EQUIV_POS2, equivElim, equivElim, {}, {}, *cdp);
2664 : 0 : Node newPremise = nm->mkNode(Kind::SEXPR, d_cl, premise);
2665 [ - - ]: 0 : Trace("alethe-proof")
2666 : 0 : << "Reverted handling as a clause for converting "
2667 : 0 : << premiseChildConclusion << " into " << premise << std::endl;
2668 : : return success
2669 : 0 : && addAletheStep(AletheRule::RESOLUTION,
2670 : : newPremise,
2671 : : newPremise,
2672 : : {equivElim, equiv, premiseChildConclusion},
2673 : 0 : d_resPivots
2674 : 0 : ? std::vector<Node>{equiv,
2675 : 0 : d_false,
2676 : : premiseChildConclusion,
2677 : 0 : d_false}
2678 : : : std::vector<Node>(),
2679 [ - - ]: 0 : *cdp);
2680 : : }
2681 : : }
2682 : : // Derive (cl (or t1' ... tn')) from (cl t1' ... tn') (i.e., the premise) with
2683 : : //
2684 : : // ----------------------- ... --------------------- or_neg
2685 : : // premise (cl premise, (not t1')) ... (cl premise, (not tn'))
2686 : : // ---------------------------- resolution
2687 : : // (cl premise ... premise)
2688 : : // ---------------------------- contraction
2689 : : // (cl premise)
2690 : 43616 : std::vector<Node> resPremises{premise};
2691 : 21808 : std::vector<Node> resArgs;
2692 : 43616 : std::vector<Node> contractionPremiseChildren{d_cl};
2693 : 10904 : bool success = true;
2694 : :
2695 [ + + ]: 71327 : for (size_t i = 0, size = premise.getNumChildren(); i < size; ++i)
2696 : : {
2697 : 120846 : Node nNeg = premise[i].notNode();
2698 : 60423 : resPremises.push_back(nm->mkNode(Kind::SEXPR, d_cl, premise, nNeg));
2699 : 241692 : success &= addAletheStep(AletheRule::OR_NEG,
2700 : 60423 : resPremises.back(),
2701 : 60423 : resPremises.back(),
2702 : : {},
2703 : 181269 : std::vector<Node>{nm->mkConstInt(i)},
2704 : 60423 : *cdp);
2705 : 60423 : resArgs.push_back(nNeg[0]);
2706 : 60423 : resArgs.push_back(d_true);
2707 : 60423 : contractionPremiseChildren.push_back(premise);
2708 : : }
2709 : 21808 : Node contractionPremise = nm->mkNode(Kind::SEXPR, contractionPremiseChildren);
2710 : 21808 : success &= addAletheStep(AletheRule::RESOLUTION,
2711 : : contractionPremise,
2712 : : contractionPremise,
2713 : : resPremises,
2714 [ + + ]: 21808 : d_resPivots ? resArgs : std::vector<Node>(),
2715 : 10904 : *cdp);
2716 : 21808 : Node newPremise = nm->mkNode(Kind::SEXPR, d_cl, premise);
2717 : : return success
2718 : 32712 : && addAletheStep(AletheRule::CONTRACTION,
2719 : : newPremise,
2720 : : newPremise,
2721 : : {contractionPremise},
2722 : : {},
2723 [ + - ]: 21808 : *cdp);
2724 : : }
2725 : :
2726 : 268962 : bool AletheProofPostprocessCallback::updatePost(
2727 : : Node res,
2728 : : CVC5_UNUSED ProofRule id,
2729 : : const std::vector<Node>& children,
2730 : : const std::vector<Node>& args,
2731 : : CDProof* cdp)
2732 : : {
2733 : 268962 : NodeManager* nm = nodeManager();
2734 : 268962 : AletheRule rule = getAletheRule(args[0]);
2735 [ + - ]: 537924 : Trace("alethe-proof") << "...Alethe post-update " << rule << " / " << res
2736 : 268962 : << " / args: " << args << std::endl;
2737 : 268962 : bool success = true;
2738 [ + + ][ - ]: 268962 : switch (rule)
2739 : : {
2740 : : // In the case of a resolution rule, the rule might originally have been a
2741 : : // cvc5 RESOLUTION or CHAIN_RESOLUTION rule, and it is possible that one of
2742 : : // the children was printed as (cl (or F1 ... Fn)) but it was actually used
2743 : : // as (cl F1 ... Fn). However, from the pivot of the resolution step for the
2744 : : // child we can determine if an additional OR step is necessary to convert
2745 : : // the clase (cl (or ...)) to (cl ...). This is done below.
2746 : 112860 : case AletheRule::RESOLUTION_OR:
2747 : : {
2748 : : // We need pivots to more easily do the computations here, so we require
2749 : : // them.
2750 [ - + ][ - + ]: 112860 : Assert(args.size() >= 4);
[ - - ]
2751 : 112860 : std::vector<Node> newChildren = children;
2752 : 112860 : bool hasUpdated = false;
2753 : :
2754 : : // Note that we will have inverted the order of polarity/pivot.
2755 : : size_t polIdx, pivIdx;
2756 : : // their starting positions in the arguments
2757 : 112860 : polIdx = 4;
2758 : 112860 : pivIdx = 3;
2759 : : // The first child is used as a non-singleton clause if it is not equal
2760 : : // to its pivot L_1. Since it's the first clause in the resolution it can
2761 : : // only be equal to the pivot in the case the polarity is true.
2762 : 112860 : if (children[0].getKind() == Kind::OR
2763 [ + + ][ + + ]: 112860 : && (args[polIdx] != d_true || args[pivIdx] != children[0]))
[ + - ][ + + ]
2764 : : {
2765 : 118582 : std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[0]);
2766 : 59291 : bool childPfIsAssume = childPf->getRule() == ProofRule::ASSUME;
2767 : : Node childConclusion =
2768 [ + + ]: 118582 : childPfIsAssume ? childPf->getResult() : childPf->getArguments()[2];
2769 : : // if child conclusion is of the form (sexpr cl (or ...)), then we need
2770 : : // to add an OR step, since this child must not be a singleton
2771 [ - + ]: 13284 : if ((childPfIsAssume && childConclusion.getKind() == Kind::OR)
2772 [ + + ][ + + ]: 74854 : || (childConclusion.getNumChildren() == 2
2773 [ + - ][ + + ]: 61570 : && childConclusion[0] == d_cl
[ - - ]
2774 [ + - ][ + + ]: 61570 : && childConclusion[1].getKind() == Kind::OR))
[ + + ][ - - ]
2775 : : {
2776 : 15563 : hasUpdated = true;
2777 : : // Add or step
2778 : 62252 : std::vector<Node> subterms{d_cl};
2779 [ + + ]: 15563 : if (childPfIsAssume)
2780 : : {
2781 : : subterms.insert(
2782 : 13284 : subterms.end(), childConclusion.begin(), childConclusion.end());
2783 : : }
2784 : : else
2785 : : {
2786 : 2279 : subterms.insert(subterms.end(),
2787 : : childConclusion[1].begin(),
2788 : 4558 : childConclusion[1].end());
2789 : : }
2790 : 15563 : Node newConclusion = nm->mkNode(Kind::SEXPR, subterms);
2791 : 62252 : success &= addAletheStep(AletheRule::OR,
2792 : : newConclusion,
2793 : : newConclusion,
2794 : 15563 : {children[0]},
2795 : : {},
2796 : 31126 : *cdp);
2797 : 15563 : newChildren[0] = newConclusion;
2798 [ + - ]: 31126 : Trace("alethe-proof") << "Added OR step for " << childConclusion
2799 : 15563 : << " / " << newConclusion << std::endl;
2800 : : }
2801 : : }
2802 : : // The premise is used a singleton clause. We must guarantee that its
2803 : : // proof indeed concludes a singleton clause.
2804 [ - + ]: 53569 : else if (children[0].getKind() == Kind::OR)
2805 : : {
2806 : 0 : Assert(args[polIdx] == d_true && args[pivIdx] == children[0]);
2807 [ - - ]: 0 : if (maybeReplacePremiseProof(children[0], cdp))
2808 : : {
2809 : 0 : hasUpdated = true;
2810 : 0 : newChildren[0] = nm->mkNode(Kind::SEXPR, d_cl, children[0]);
2811 : : }
2812 : : }
2813 : : // For all other children C_i the procedure is similar. There is however a
2814 : : // key difference in the choice of the pivot element which is now the
2815 : : // L_{i-1}, i.e. the pivot of the child with the result of the i-1
2816 : : // resolution steps between the children before it. Therefore, if the
2817 : : // policy id_{i-1} is true, the pivot has to appear negated in the child
2818 : : // in which case it should not be a (cl (or F1 ... Fn)) node. The same is
2819 : : // true if it isn't the pivot element.
2820 [ + + ]: 1865010 : for (std::size_t i = 1, size = children.size(); i < size; ++i)
2821 : : {
2822 : 1752150 : polIdx = 2 * (i - 1) + 3 + 1;
2823 : 1752150 : pivIdx = 2 * (i - 1) + 3;
2824 : 1752150 : if (children[i].getKind() == Kind::OR
2825 [ + + ][ + + ]: 4039840 : && (args[polIdx] != d_false
2826 : 2287700 : || d_anc.convert(args[pivIdx]) != d_anc.convert(children[i])))
2827 : : {
2828 : 1484180 : if (args[polIdx] == d_false
2829 : 2968370 : && d_anc.convert(args[pivIdx]) == d_anc.convert(children[i]))
2830 : : {
2831 : 0 : continue;
2832 : : }
2833 : 2968370 : std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[i]);
2834 : 1484180 : bool childPfIsAssume = childPf->getRule() == ProofRule::ASSUME;
2835 : : Node childConclusion = childPfIsAssume ? childPf->getResult()
2836 [ + + ]: 2968370 : : childPf->getArguments()[2];
2837 : : // Add or step
2838 [ - + ]: 166654 : if ((childPfIsAssume && childConclusion.getKind() == Kind::OR)
2839 [ + + ][ + + ]: 1709600 : || (childConclusion.getNumChildren() == 2
2840 [ + - ][ + + ]: 1542950 : && childConclusion[0] == d_cl
[ - - ]
2841 [ + - ][ + + ]: 1542950 : && childConclusion[1].getKind() == Kind::OR))
[ + + ][ - - ]
2842 : : {
2843 : 225420 : hasUpdated = true;
2844 : 901680 : std::vector<Node> lits{d_cl};
2845 [ + + ]: 225420 : if (childPfIsAssume)
2846 : : {
2847 : : lits.insert(
2848 : 166654 : lits.end(), childConclusion.begin(), childConclusion.end());
2849 : : }
2850 : : else
2851 : : {
2852 : 58766 : lits.insert(lits.end(),
2853 : : childConclusion[1].begin(),
2854 : 117532 : childConclusion[1].end());
2855 : : }
2856 : 225420 : Node conclusion = nm->mkNode(Kind::SEXPR, lits);
2857 : 901680 : success &= addAletheStep(AletheRule::OR,
2858 : : conclusion,
2859 : : conclusion,
2860 : 225420 : {children[i]},
2861 : : {},
2862 : 450840 : *cdp);
2863 : 225420 : newChildren[i] = conclusion;
2864 [ + - ]: 450840 : Trace("alethe-proof") << "Added OR step for " << childConclusion
2865 : 225420 : << " / " << conclusion << std::endl;
2866 : : }
2867 : : }
2868 : : // As for the first premise, we need to handle the case in which the
2869 : : // premise is a singleton but the rule concluding it yields a clause.
2870 [ + + ]: 267965 : else if (children[i].getKind() == Kind::OR)
2871 : : {
2872 : 29786 : Assert(args[polIdx] == d_false
2873 : : && d_anc.convert(args[pivIdx]) == d_anc.convert(children[i]));
2874 [ + + ]: 14893 : if (maybeReplacePremiseProof(children[i], cdp))
2875 : : {
2876 : 10904 : hasUpdated = true;
2877 : 10904 : newChildren[i] = nm->mkNode(Kind::SEXPR, d_cl, children[i]);
2878 : : }
2879 : : }
2880 : : }
2881 [ - + ]: 112860 : if (TraceIsOn("alethe-proof"))
2882 : : {
2883 [ - - ]: 0 : if (hasUpdated)
2884 : : {
2885 [ - - ]: 0 : Trace("alethe-proof")
2886 : 0 : << "... update alethe step in finalizer " << res << " "
2887 : 0 : << newChildren << " / " << args << std::endl;
2888 : : }
2889 : : else
2890 : : {
2891 [ - - ]: 0 : Trace("alethe-proof") << "... no update\n";
2892 : : }
2893 : : }
2894 : 225720 : success &= addAletheStep(
2895 : : AletheRule::RESOLUTION,
2896 : : res,
2897 : 112860 : args[2],
2898 : : newChildren,
2899 [ + + ][ + + ]: 225720 : d_resPivots ? std::vector<Node>{args.begin() + 3, args.end()}
[ - - ]
2900 : : : std::vector<Node>{},
2901 : 112860 : *cdp);
2902 : 112860 : return success;
2903 : : }
2904 : : // A application of the FACTORING rule:
2905 : : //
2906 : : // (or a a b)
2907 : : // ---------- FACTORING
2908 : : // (or a b)
2909 : : //
2910 : : // might be translated during pre-visit (update) to:
2911 : : //
2912 : : // (or (cl a a b))*
2913 : : // ---------------- CONTRACTION
2914 : : // (cl a b)**
2915 : : //
2916 : : // In this post-visit an additional OR step is added in that case:
2917 : : //
2918 : : // (cl (or a a b))*
2919 : : // ---------------- OR
2920 : : // (cl a a b)
2921 : : // ---------------- CONTRACTION
2922 : : // (cl a b)**
2923 : : //
2924 : : // * the corresponding proof node is (or a a b)
2925 : : // ** the corresponding proof node is (or a b)
2926 : : //
2927 : : // The process is analogous for REORDERING.
2928 : 156102 : case AletheRule::REORDERING:
2929 : : case AletheRule::CONTRACTION:
2930 : : {
2931 : 312204 : std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[0]);
2932 : 156102 : bool childPfIsAssume = childPf->getRule() == ProofRule::ASSUME;
2933 : : Node childConclusion =
2934 [ + + ]: 312204 : childPfIsAssume ? childPf->getResult() : childPf->getArguments()[2];
2935 [ - + ]: 2 : if ((childPfIsAssume && childConclusion.getKind() == Kind::OR)
2936 [ + + ][ + + ]: 165869 : || (childConclusion.getNumChildren() == 2
2937 [ + - ][ + + ]: 165867 : && childConclusion[0] == d_cl
[ - - ]
2938 [ + - ][ + + ]: 165867 : && childConclusion[1].getKind() == Kind::OR))
[ + + ][ - - ]
2939 : : {
2940 : : // Add or step for child
2941 : 39068 : std::vector<Node> subterms{d_cl};
2942 [ + + ]: 9767 : if (childPfIsAssume)
2943 : : {
2944 : : subterms.insert(
2945 : 2 : subterms.end(), childConclusion.begin(), childConclusion.end());
2946 : : }
2947 : : else
2948 : : {
2949 : 9765 : subterms.insert(subterms.end(),
2950 : : childConclusion[1].begin(),
2951 : 19530 : childConclusion[1].end());
2952 : : }
2953 : 9767 : Node newChild = nm->mkNode(Kind::SEXPR, subterms);
2954 : 39068 : success &= addAletheStep(
2955 : 29301 : AletheRule::OR, newChild, newChild, {children[0]}, {}, *cdp);
2956 [ + - ]: 19534 : Trace("alethe-proof")
2957 : 0 : << "Added OR step in finalizer to child " << childConclusion
2958 : 9767 : << " / " << newChild << std::endl;
2959 : : // update res step
2960 : 19534 : cdp->addStep(res, ProofRule::ALETHE_RULE, {newChild}, args);
2961 : 9767 : return success;
2962 : : }
2963 [ + - ]: 146335 : Trace("alethe-proof") << "... no update\n";
2964 : 146335 : return false;
2965 : : }
2966 : 0 : default:
2967 : : {
2968 [ - - ]: 0 : Trace("alethe-proof") << "... no update\n";
2969 : 0 : return false;
2970 : : }
2971 : : }
2972 : : Trace("alethe-proof") << "... no update\n";
2973 : : return false;
2974 : : }
2975 : :
2976 : 2043 : bool AletheProofPostprocessCallback::ensureFinalStep(
2977 : : Node res,
2978 : : CVC5_UNUSED ProofRule id,
2979 : : std::vector<Node>& children,
2980 : : const std::vector<Node>& args,
2981 : : CDProof* cdp)
2982 : : {
2983 : 2043 : bool success = true;
2984 : 2043 : NodeManager* nm = nodeManager();
2985 : 4086 : std::shared_ptr<ProofNode> childPf = cdp->getProofFor(children[0]);
2986 : :
2987 : : // convert inner proof, i.e., children[0], if its conclusion is (cl false) or
2988 : : // if it's a false assumption
2989 : : //
2990 : : // ...
2991 : : // ------------------- ---------------------- false
2992 : : // (cl false) (cl (not false))
2993 : : // -------------------------------------------------- resolution
2994 : : // (cl)
2995 : 2043 : if ((childPf->getRule() == ProofRule::ALETHE_RULE
2996 [ + + ]: 1552 : && childPf->getArguments()[2].getNumChildren() == 2
2997 [ - + ][ + + ]: 2491 : && childPf->getArguments()[2][1] == d_false)
[ - - ]
2998 [ + + ][ + + ]: 5651 : || (childPf->getRule() == ProofRule::ASSUME
2999 [ + - ][ + + ]: 2056 : && childPf->getResult() == d_false))
[ + + ][ - - ]
3000 : : {
3001 : : Node notFalse =
3002 : 1383 : nm->mkNode(Kind::SEXPR, d_cl, d_false.notNode()); // (cl (not false))
3003 : 922 : Node newChild = nm->mkNode(Kind::SEXPR, d_cl); // (cl)
3004 : :
3005 : 461 : success &=
3006 : 461 : addAletheStep(AletheRule::FALSE, notFalse, notFalse, {}, {}, *cdp);
3007 [ + + ][ - - ]: 1844 : success &= addAletheStep(
3008 : : AletheRule::RESOLUTION,
3009 : : newChild,
3010 : : newChild,
3011 : 461 : {children[0], notFalse},
3012 : 922 : d_resPivots ? std::vector<Node>{d_false, d_true} : std::vector<Node>(),
3013 : 1383 : *cdp);
3014 : 461 : children[0] = newChild;
3015 : : }
3016 : :
3017 : : // Sanitize original assumptions and create a double scope to hold them, where
3018 : : // the first scope is empty. This is needed because of the expected form a
3019 : : // proof node to be printed.
3020 : 4086 : std::vector<Node> sanitizedArgs;
3021 [ + + ]: 14593 : for (const Node& a : args)
3022 : : {
3023 : 12900 : Node conv = d_anc.maybeConvert(a, true);
3024 [ + + ]: 12900 : if (conv.isNull())
3025 : : {
3026 : 350 : d_reasonForConversionFailure = d_anc.getError();
3027 : 350 : success = false;
3028 : 350 : break;
3029 : : }
3030 : : // avoid repeated assumptions
3031 : 12550 : if (std::find(sanitizedArgs.begin(), sanitizedArgs.end(), conv)
3032 [ + + ]: 25100 : == sanitizedArgs.end())
3033 : : {
3034 : 12314 : sanitizedArgs.push_back(conv);
3035 : : }
3036 : : }
3037 : 2043 : Node placeHolder = nm->mkNode(Kind::SEXPR, res);
3038 : 2043 : cdp->addStep(placeHolder, ProofRule::SCOPE, children, sanitizedArgs);
3039 : 5779 : return success && cdp->addStep(res, ProofRule::SCOPE, {placeHolder}, {});
3040 : : }
3041 : :
3042 : 2070150 : bool AletheProofPostprocessCallback::addAletheStep(
3043 : : AletheRule rule,
3044 : : Node res,
3045 : : Node conclusion,
3046 : : const std::vector<Node>& children,
3047 : : const std::vector<Node>& args,
3048 : : CDProof& cdp)
3049 : : {
3050 : : std::vector<Node> newArgs{
3051 : 10350800 : nodeManager()->mkConstInt(Rational(static_cast<uint32_t>(rule)))};
3052 : 2070150 : newArgs.push_back(res);
3053 : 2070150 : conclusion = d_anc.maybeConvert(conclusion);
3054 [ + + ]: 2070150 : if (conclusion.isNull())
3055 : : {
3056 : 162 : d_reasonForConversionFailure = d_anc.getError();
3057 : 162 : return false;
3058 : : }
3059 : 2069990 : newArgs.push_back(conclusion);
3060 [ + + ]: 6597560 : for (const Node& arg : args)
3061 : : {
3062 : 4528020 : Node conv = d_anc.maybeConvert(arg);
3063 [ + + ]: 4528020 : if (conv.isNull())
3064 : : {
3065 : 443 : d_reasonForConversionFailure = d_anc.getError();
3066 : 443 : return false;
3067 : : }
3068 : 4527580 : newArgs.push_back(conv);
3069 : : }
3070 [ + - ]: 4139090 : Trace("alethe-proof") << "... add alethe step " << res << " / " << conclusion
3071 : 0 : << " " << rule << " " << children << " / " << newArgs
3072 : 2069550 : << std::endl;
3073 : 2069550 : return cdp.addStep(res, ProofRule::ALETHE_RULE, children, newArgs);
3074 : : }
3075 : :
3076 : 131788 : bool AletheProofPostprocessCallback::addAletheStepFromOr(
3077 : : AletheRule rule,
3078 : : Node res,
3079 : : const std::vector<Node>& children,
3080 : : const std::vector<Node>& args,
3081 : : CDProof& cdp)
3082 : : {
3083 [ - + ][ - + ]: 131788 : Assert(res.getKind() == Kind::OR);
[ - - ]
3084 : 527152 : std::vector<Node> subterms = {d_cl};
3085 : 131788 : subterms.insert(subterms.end(), res.begin(), res.end());
3086 : 131788 : Node conclusion = nodeManager()->mkNode(Kind::SEXPR, subterms);
3087 : 263576 : return addAletheStep(rule, res, conclusion, children, args, cdp);
3088 : : }
3089 : :
3090 : 2116 : AletheProofPostprocess::AletheProofPostprocess(Env& env,
3091 : 2116 : AletheNodeConverter& anc)
3092 : 2116 : : EnvObj(env), d_cb(env, anc, options().proof.proofAletheResPivots)
3093 : : {
3094 : 2116 : }
3095 : :
3096 : 2116 : AletheProofPostprocess::~AletheProofPostprocess() {}
3097 : :
3098 : 691 : const std::string& AletheProofPostprocess::getError()
3099 : : {
3100 : 691 : return d_reasonForConversionFailure;
3101 : : }
3102 : :
3103 : 2116 : bool AletheProofPostprocess::process(std::shared_ptr<ProofNode> pf)
3104 : : {
3105 [ + + ]: 2116 : if (logicInfo().isHigherOrder())
3106 : : {
3107 : 73 : std::stringstream ss;
3108 : 73 : ss << "\"Proof unsupported by Alethe: contains higher-order elements\"";
3109 : 73 : d_reasonForConversionFailure = ss.str();
3110 : 73 : return false;
3111 : : }
3112 : : // first two nodes are scopes for definitions and other assumptions. We
3113 : : // process only the internal proof node. And we merge these two scopes
3114 [ + - ][ + - ]: 4086 : Assert(pf->getRule() == ProofRule::SCOPE
[ - + ][ - - ]
3115 : : && pf->getChildren()[0]->getRule() == ProofRule::SCOPE);
3116 : 4086 : std::shared_ptr<ProofNode> definitionsScope = pf;
3117 : 4086 : std::shared_ptr<ProofNode> assumptionsScope = pf->getChildren()[0];
3118 : 4086 : std::shared_ptr<ProofNode> internalProof = assumptionsScope->getChildren()[0];
3119 : : // Translate proof node
3120 : 4086 : ProofNodeUpdater updater(d_env, d_cb, false, false);
3121 : 2043 : updater.process(internalProof);
3122 [ + - ]: 2043 : if (d_reasonForConversionFailure.empty())
3123 : : {
3124 : : // In the Alethe proof format the final step has to be (cl). However, after
3125 : : // the translation, the final step might still be (cl false). In that case
3126 : : // additional steps are required. The function has the additional purpose
3127 : : // of sanitizing the arguments of the outer SCOPEs
3128 : : CDProof cpf(d_env,
3129 : : nullptr,
3130 : : "AletheProofPostProcess::ensureFinalStep::CDProof",
3131 : 6129 : true);
3132 : 8172 : std::vector<Node> ccn{internalProof->getResult()};
3133 : 2043 : cpf.addProof(internalProof);
3134 : 2043 : std::vector<Node> args{definitionsScope->getArguments().begin(),
3135 : 4086 : definitionsScope->getArguments().end()};
3136 : 2043 : args.insert(args.end(),
3137 : 2043 : assumptionsScope->getArguments().begin(),
3138 : 6129 : assumptionsScope->getArguments().end());
3139 : 2043 : if (d_cb.ensureFinalStep(
3140 [ + + ]: 4086 : definitionsScope->getResult(), ProofRule::SCOPE, ccn, args, &cpf))
3141 : : {
3142 : : std::shared_ptr<ProofNode> npn =
3143 : 1693 : cpf.getProofFor(definitionsScope->getResult());
3144 : :
3145 : : // then, update the original proof node based on this one
3146 [ + - ]: 1693 : Trace("pf-process-debug") << "Update node..." << std::endl;
3147 : 1693 : d_env.getProofNodeManager()->updateNode(pf.get(), npn.get());
3148 [ + - ]: 1693 : Trace("pf-process-debug") << "...update node finished." << std::endl;
3149 : : }
3150 : : }
3151 : : // Since the final step may also lead to issues, need to test here again
3152 [ + + ]: 2043 : if (!d_cb.getError().empty())
3153 : : {
3154 : 618 : d_reasonForConversionFailure = d_cb.getError();
3155 : 618 : return false;
3156 : : }
3157 : 1425 : return true;
3158 : : }
3159 : :
3160 : : } // namespace proof
3161 : : } // namespace cvc5::internal
|