Branch data Line data Source code
1 : : /******************************************************************************
2 : : * This file is part of the cvc5 project.
3 : : *
4 : : * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
5 : : * in the top-level source directory and their institutional affiliations.
6 : : * All rights reserved. See the file COPYING in the top-level source
7 : : * directory for licensing information.
8 : : * ****************************************************************************
9 : : *
10 : : * The printer for the Eunoia format.
11 : : */
12 : :
13 : : #include "proof/eo/eo_printer.h"
14 : :
15 : : #include <cctype>
16 : : #include <iostream>
17 : : #include <memory>
18 : : #include <ostream>
19 : : #include <sstream>
20 : :
21 : : #include "expr/aci_norm.h"
22 : : #include "expr/node_algorithm.h"
23 : : #include "expr/sequence.h"
24 : : #include "expr/subs.h"
25 : : #include "options/base_options.h"
26 : : #include "options/main_options.h"
27 : : #include "options/strings_options.h"
28 : : #include "printer/printer.h"
29 : : #include "printer/smt2/smt2_printer.h"
30 : : #include "proof/eo/eo_dependent_type_converter.h"
31 : : #include "proof/proof_node_to_sexpr.h"
32 : : #include "rewriter/rewrite_db.h"
33 : : #include "smt/print_benchmark.h"
34 : : #include "theory/builtin/generic_op.h"
35 : : #include "theory/strings/regexp_entail.h"
36 : : #include "theory/strings/theory_strings_utils.h"
37 : : #include "theory/strings/word.h"
38 : : #include "theory/theory.h"
39 : : #include "util/string.h"
40 : :
41 : : namespace cvc5::internal {
42 : :
43 : : namespace proof {
44 : :
45 : 1893 : EoPrinter::EoPrinter(Env& env,
46 : : BaseEoNodeConverter& atp,
47 : : rewriter::RewriteDb* rdb,
48 : 1893 : uint32_t letThresh)
49 : : : EnvObj(env),
50 : 1893 : d_tproc(atp),
51 : 1893 : d_pfIdCounter(0),
52 : 1893 : d_alreadyPrinted(&d_passumeCtx),
53 : 1893 : d_passumeMap(&d_passumeCtx),
54 : 1893 : d_termLetPrefix("@t"),
55 : 1893 : d_rdb(rdb),
56 : : // Use a let binding if proofDagGlobal is true. We can traverse binders
57 : : // due to the way we print global declare-var, since terms beneath
58 : : // binders will always have their variables in scope and hence can be
59 : : // printed in define commands. We additionally traverse skolems with this
60 : : // utility.
61 : 1893 : d_lbind(d_termLetPrefix, letThresh, true, true),
62 [ + - ]: 1893 : d_lbindUse(options().proof.proofDagGlobal ? &d_lbind : nullptr),
63 : 7572 : d_eletify(d_lbindUse)
64 : : {
65 : 1893 : d_pfType = nodeManager()->mkSort("proofType");
66 : 1893 : d_false = nodeManager()->mkConst(false);
67 : 1893 : d_absType = nodeManager()->mkAbstractType(Kind::ABSTRACT_TYPE);
68 : 1893 : }
69 : :
70 : 4193746 : bool EoPrinter::isHandled(const Options& opts, const ProofNode* pfn)
71 : : {
72 : 4193746 : const std::vector<Node> pargs = pfn->getArguments();
73 [ + + ][ + + ]: 4193746 : switch (pfn->getRule())
[ + + ][ + + ]
[ + + ]
74 : : {
75 : : // List of handled rules
76 : 4009706 : case ProofRule::ASSUME:
77 : : case ProofRule::SCOPE:
78 : : case ProofRule::REFL:
79 : : case ProofRule::SYMM:
80 : : case ProofRule::TRANS:
81 : : case ProofRule::CONG:
82 : : case ProofRule::NARY_CONG:
83 : : case ProofRule::PAIRWISE_CONG:
84 : : case ProofRule::HO_CONG:
85 : : case ProofRule::TRUE_INTRO:
86 : : case ProofRule::TRUE_ELIM:
87 : : case ProofRule::FALSE_INTRO:
88 : : case ProofRule::FALSE_ELIM:
89 : : case ProofRule::SPLIT:
90 : : case ProofRule::EQ_RESOLVE:
91 : : case ProofRule::MODUS_PONENS:
92 : : case ProofRule::NOT_NOT_ELIM:
93 : : case ProofRule::CONTRA:
94 : : case ProofRule::AND_ELIM:
95 : : case ProofRule::AND_INTRO:
96 : : case ProofRule::NOT_OR_ELIM:
97 : : case ProofRule::IMPLIES_ELIM:
98 : : case ProofRule::NOT_IMPLIES_ELIM1:
99 : : case ProofRule::NOT_IMPLIES_ELIM2:
100 : : case ProofRule::EQUIV_ELIM1:
101 : : case ProofRule::EQUIV_ELIM2:
102 : : case ProofRule::NOT_EQUIV_ELIM1:
103 : : case ProofRule::NOT_EQUIV_ELIM2:
104 : : case ProofRule::XOR_ELIM1:
105 : : case ProofRule::XOR_ELIM2:
106 : : case ProofRule::NOT_XOR_ELIM1:
107 : : case ProofRule::NOT_XOR_ELIM2:
108 : : case ProofRule::ITE_ELIM1:
109 : : case ProofRule::ITE_ELIM2:
110 : : case ProofRule::NOT_ITE_ELIM1:
111 : : case ProofRule::NOT_ITE_ELIM2:
112 : : case ProofRule::NOT_AND:
113 : : case ProofRule::CNF_AND_NEG:
114 : : case ProofRule::CNF_OR_POS:
115 : : case ProofRule::CNF_OR_NEG:
116 : : case ProofRule::CNF_IMPLIES_POS:
117 : : case ProofRule::CNF_IMPLIES_NEG1:
118 : : case ProofRule::CNF_IMPLIES_NEG2:
119 : : case ProofRule::CNF_EQUIV_POS1:
120 : : case ProofRule::CNF_EQUIV_POS2:
121 : : case ProofRule::CNF_EQUIV_NEG1:
122 : : case ProofRule::CNF_EQUIV_NEG2:
123 : : case ProofRule::CNF_XOR_POS1:
124 : : case ProofRule::CNF_XOR_POS2:
125 : : case ProofRule::CNF_XOR_NEG1:
126 : : case ProofRule::CNF_XOR_NEG2:
127 : : case ProofRule::CNF_ITE_POS1:
128 : : case ProofRule::CNF_ITE_POS2:
129 : : case ProofRule::CNF_ITE_POS3:
130 : : case ProofRule::CNF_ITE_NEG1:
131 : : case ProofRule::CNF_ITE_NEG2:
132 : : case ProofRule::CNF_ITE_NEG3:
133 : : case ProofRule::CNF_AND_POS:
134 : : case ProofRule::FACTORING:
135 : : case ProofRule::REORDERING:
136 : : case ProofRule::RESOLUTION:
137 : : case ProofRule::CHAIN_RESOLUTION:
138 : : case ProofRule::CHAIN_M_RESOLUTION:
139 : : case ProofRule::ARRAYS_READ_OVER_WRITE:
140 : : case ProofRule::ARRAYS_READ_OVER_WRITE_CONTRA:
141 : : case ProofRule::ARRAYS_READ_OVER_WRITE_1:
142 : : case ProofRule::ARRAYS_EXT:
143 : : case ProofRule::ARITH_SUM_UB:
144 : : case ProofRule::ARITH_MULT_POS:
145 : : case ProofRule::ARITH_MULT_NEG:
146 : : case ProofRule::ARITH_MULT_TANGENT:
147 : : case ProofRule::ARITH_MULT_SIGN:
148 : : case ProofRule::ARITH_MULT_ABS_COMPARISON:
149 : : case ProofRule::ARITH_TRICHOTOMY:
150 : : case ProofRule::INT_TIGHT_LB:
151 : : case ProofRule::INT_TIGHT_UB:
152 : : case ProofRule::SKOLEM_INTRO:
153 : : case ProofRule::SETS_SINGLETON_INJ:
154 : : case ProofRule::SETS_EXT:
155 : : case ProofRule::SETS_CHOOSE_MEMBER:
156 : : case ProofRule::CONCAT_EQ:
157 : : case ProofRule::CONCAT_UNIFY:
158 : : case ProofRule::CONCAT_CSPLIT:
159 : : case ProofRule::CONCAT_CPROP:
160 : : case ProofRule::CONCAT_SPLIT:
161 : : case ProofRule::CONCAT_LPROP:
162 : : case ProofRule::STRING_LENGTH_POS:
163 : : case ProofRule::STRING_LENGTH_NON_EMPTY:
164 : : case ProofRule::RE_INTER:
165 : : case ProofRule::RE_CONCAT:
166 : : case ProofRule::RE_UNFOLD_POS:
167 : : case ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED:
168 : : case ProofRule::RE_UNFOLD_NEG:
169 : : case ProofRule::STRING_CODE_INJ:
170 : : case ProofRule::STRING_SEQ_UNIT_INJ:
171 : : case ProofRule::STRING_DECOMPOSE:
172 : : case ProofRule::STRING_EXT:
173 : : case ProofRule::DT_SPLIT:
174 : : case ProofRule::ITE_EQ:
175 : : case ProofRule::INSTANTIATE:
176 : : case ProofRule::SKOLEMIZE:
177 : : case ProofRule::ALPHA_EQUIV:
178 : : case ProofRule::QUANT_VAR_REORDERING:
179 : : case ProofRule::ENCODE_EQ_INTRO:
180 : : case ProofRule::HO_APP_ENCODE:
181 : : case ProofRule::BV_EAGER_ATOM:
182 : : case ProofRule::ACI_NORM:
183 : : case ProofRule::ABSORB:
184 : : case ProofRule::ARITH_POLY_NORM:
185 : : case ProofRule::ARITH_POLY_NORM_REL:
186 : : case ProofRule::BV_POLY_NORM:
187 : : case ProofRule::BV_POLY_NORM_EQ:
188 : : case ProofRule::EXISTS_STRING_LENGTH:
189 : 4009706 : case ProofRule::DSL_REWRITE: return true;
190 : 16554 : case ProofRule::BV_BITBLAST_STEP:
191 : : {
192 : 16554 : return isHandledBitblastStep(pfn->getArguments()[0]);
193 : : }
194 : : break;
195 : 13180 : case ProofRule::THEORY_REWRITE:
196 : : {
197 : : ProofRewriteRule id;
198 : 13180 : rewriter::getRewriteRule(pfn->getArguments()[0], id);
199 : 13180 : return isHandledTheoryRewrite(opts, id, pfn->getArguments()[1]);
200 : : }
201 : : break;
202 : 912 : case ProofRule::ARITH_REDUCTION:
203 : : {
204 : 912 : Kind k = pargs[0].getKind();
205 [ + + ]: 892 : return k == Kind::TO_INTEGER || k == Kind::IS_INTEGER
206 [ + + ][ + + ]: 846 : || k == Kind::DIVISION || k == Kind::DIVISION_TOTAL
207 [ + + ][ + + ]: 752 : || k == Kind::INTS_DIVISION || k == Kind::INTS_DIVISION_TOTAL
208 [ + + ][ + + ]: 474 : || k == Kind::INTS_MODULUS || k == Kind::INTS_MODULUS_TOTAL
209 [ + + ][ + + ]: 1804 : || k == Kind::ABS || k == Kind::INTS_LOG2;
[ + + ]
210 : : }
211 : : break;
212 : 668 : case ProofRule::STRING_REDUCTION:
213 : : {
214 : : // depends on the operator
215 [ - + ][ - + ]: 668 : Assert(!pargs.empty());
[ - - ]
216 : 668 : Kind k = pargs[0].getKind();
217 [ + - ]: 668 : switch (k)
218 : : {
219 : 668 : case Kind::STRING_CONTAINS:
220 : : case Kind::STRING_SUBSTR:
221 : : case Kind::STRING_INDEXOF:
222 : : case Kind::STRING_INDEXOF_RE:
223 : : case Kind::STRING_REPLACE:
224 : : case Kind::STRING_REPLACE_ALL:
225 : : case Kind::STRING_REPLACE_RE:
226 : : case Kind::STRING_REPLACE_RE_ALL:
227 : : case Kind::STRING_STOI:
228 : : case Kind::STRING_ITOS:
229 : : case Kind::SEQ_NTH:
230 : : case Kind::STRING_UPDATE:
231 : : case Kind::STRING_LEQ:
232 : : case Kind::STRING_REV:
233 : : case Kind::STRING_TO_LOWER:
234 : 668 : case Kind::STRING_TO_UPPER: return true;
235 : 0 : default: break;
236 : : }
237 [ - - ]: 0 : Trace("eo-printer-debug") << "Cannot STRING_REDUCTION " << k << std::endl;
238 : 0 : return false;
239 : : }
240 : : break;
241 : 289 : case ProofRule::STRING_EAGER_REDUCTION:
242 : : {
243 : : // depends on the operator
244 [ - + ][ - + ]: 289 : Assert(!pargs.empty());
[ - - ]
245 : 289 : Kind k = pargs[0].getKind();
246 [ + + ][ + + ]: 289 : if (k == Kind::STRING_TO_CODE || k == Kind::STRING_FROM_CODE)
247 : : {
248 : : // must use standard alphabet size
249 : 110 : return opts.strings.stringsAlphaCard == String::num_codes();
250 : : }
251 [ + + ]: 70 : return k == Kind::STRING_CONTAINS || k == Kind::STRING_INDEXOF
252 [ + + ][ + + ]: 30 : || k == Kind::STRING_INDEXOF_RE || k == Kind::STRING_IN_REGEXP
253 [ + + ][ + - ]: 249 : || k == Kind::STRING_STOI;
254 : : }
255 : : break;
256 : : //
257 : 148277 : case ProofRule::EVALUATE:
258 : : {
259 [ + - ]: 148277 : if (canEvaluate(pargs[0]))
260 : : {
261 [ + - ]: 148277 : Trace("eo-printer-debug") << "Can evaluate " << pargs[0] << std::endl;
262 : 148277 : return true;
263 : : }
264 : : }
265 : 0 : break;
266 : 140 : case ProofRule::DISTINCT_VALUES:
267 : : {
268 : 140 : if (isHandledDistinctValues(pargs[0])
269 [ + + ][ + - ]: 140 : && isHandledDistinctValues(pargs[1]))
[ + + ]
270 : : {
271 [ + - ]: 84 : Trace("eo-printer-debug") << "Can distinguish values " << pargs[0]
272 : 42 : << " " << pargs[1] << std::endl;
273 : 42 : return true;
274 : : }
275 : : }
276 : 98 : break;
277 : 86 : case ProofRule::ARITH_TRANS_EXP_NEG:
278 : : case ProofRule::ARITH_TRANS_EXP_POSITIVITY:
279 : : case ProofRule::ARITH_TRANS_EXP_SUPER_LIN:
280 : : case ProofRule::ARITH_TRANS_EXP_ZERO:
281 : : case ProofRule::ARITH_TRANS_SINE_BOUNDS:
282 : : case ProofRule::ARITH_TRANS_SINE_SYMMETRY:
283 : : case ProofRule::ARITH_TRANS_SINE_TANGENT_ZERO:
284 : : case ProofRule::ARITH_TRANS_SINE_TANGENT_PI:
285 : : case ProofRule::SETS_FILTER_UP:
286 : : case ProofRule::SETS_FILTER_DOWN:
287 : : {
288 : : // only supported in unrestricted builds
289 [ + - ]: 86 : if (opts.base.safeMode == options::SafeMode::UNRESTRICTED)
290 : : {
291 : 86 : return true;
292 : : }
293 : : }
294 : 0 : break;
295 : : // otherwise not handled
296 : 3934 : default: break;
297 : : }
298 : 4032 : return false;
299 : 4193746 : }
300 : :
301 : 13180 : bool EoPrinter::isHandledTheoryRewrite(const Options& opts,
302 : : ProofRewriteRule id,
303 : : const Node& n)
304 : : {
305 [ + + ][ + + ]: 13180 : switch (id)
306 : : {
307 : 12879 : case ProofRewriteRule::DISTINCT_ELIM:
308 : : case ProofRewriteRule::DISTINCT_CARD_CONFLICT:
309 : : case ProofRewriteRule::DISTINCT_TRUE:
310 : : case ProofRewriteRule::DISTINCT_FALSE:
311 : : case ProofRewriteRule::BETA_REDUCE:
312 : : case ProofRewriteRule::UBV_TO_INT_ELIM:
313 : : case ProofRewriteRule::INT_TO_BV_ELIM:
314 : : case ProofRewriteRule::ARITH_STRING_PRED_ENTAIL:
315 : : case ProofRewriteRule::ARITH_STRING_PRED_SAFE_APPROX:
316 : : case ProofRewriteRule::EXISTS_ELIM:
317 : : case ProofRewriteRule::QUANT_UNUSED_VARS:
318 : : case ProofRewriteRule::DT_INST:
319 : : case ProofRewriteRule::DT_COLLAPSE_SELECTOR:
320 : : case ProofRewriteRule::DT_COLLAPSE_TESTER:
321 : : case ProofRewriteRule::DT_COLLAPSE_TESTER_SINGLETON:
322 : : case ProofRewriteRule::DT_CONS_EQ:
323 : : case ProofRewriteRule::DT_CONS_EQ_CLASH:
324 : : case ProofRewriteRule::DT_CYCLE:
325 : : case ProofRewriteRule::DT_COLLAPSE_UPDATER:
326 : : case ProofRewriteRule::DT_UPDATER_ELIM:
327 : : case ProofRewriteRule::QUANT_MERGE_PRENEX:
328 : : case ProofRewriteRule::QUANT_MINISCOPE_AND:
329 : : case ProofRewriteRule::QUANT_MINISCOPE_OR:
330 : : case ProofRewriteRule::QUANT_MINISCOPE_ITE:
331 : : case ProofRewriteRule::QUANT_VAR_ELIM_EQ:
332 : : case ProofRewriteRule::QUANT_DT_SPLIT:
333 : : case ProofRewriteRule::RE_LOOP_ELIM:
334 : : case ProofRewriteRule::RE_EQ_ELIM:
335 : : case ProofRewriteRule::SETS_EVAL_OP:
336 : : case ProofRewriteRule::STR_IN_RE_CONCAT_STAR_CHAR:
337 : : case ProofRewriteRule::STR_IN_RE_SIGMA:
338 : : case ProofRewriteRule::STR_IN_RE_SIGMA_STAR:
339 : : case ProofRewriteRule::STR_IN_RE_CONSUME:
340 : : case ProofRewriteRule::STR_INDEXOF_RE_EVAL:
341 : : case ProofRewriteRule::STR_REPLACE_RE_EVAL:
342 : : case ProofRewriteRule::STR_REPLACE_RE_ALL_EVAL:
343 : : case ProofRewriteRule::RE_INTER_INCLUSION:
344 : : case ProofRewriteRule::RE_UNION_INCLUSION:
345 : : case ProofRewriteRule::BV_SMULO_ELIM:
346 : : case ProofRewriteRule::BV_UMULO_ELIM:
347 : : case ProofRewriteRule::BV_REPEAT_ELIM:
348 : : case ProofRewriteRule::BV_BITWISE_SLICING:
349 : : case ProofRewriteRule::STR_OVERLAP_SPLIT_CTN:
350 : : case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_CTN:
351 : : case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_INDEXOF:
352 : : case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_REPLACE:
353 : : case ProofRewriteRule::STR_CTN_MULTISET_SUBSET:
354 : 12879 : case ProofRewriteRule::SEQ_EVAL_OP: return true;
355 : 229 : case ProofRewriteRule::STR_IN_RE_EVAL:
356 : : {
357 : 229 : Assert(n[0].getKind() == Kind::STRING_IN_REGEXP && n[0][0].isConst());
358 [ + + ]: 229 : if (theory::strings::Word::isEmpty(n[0][0]))
359 : : {
360 : : // If the string is empty, the signature only requires determining
361 : : // whether the regular expression is nullable, which does not require
362 : : // it to be evaluatable.
363 : : bool res;
364 : 65 : return theory::strings::RegExpEntail::isNullable(n[0][1], res);
365 : : }
366 : 164 : return canEvaluateRegExp(n[0][1]);
367 : : }
368 : 44 : case ProofRewriteRule::ARITH_POW_ELIM:
369 : : case ProofRewriteRule::ARRAYS_SELECT_CONST:
370 : : case ProofRewriteRule::LAMBDA_ELIM:
371 : : // only supported in unrestricted builds
372 [ + - ]: 44 : if (opts.base.safeMode == options::SafeMode::UNRESTRICTED)
373 : : {
374 : 44 : return true;
375 : : }
376 : 0 : break;
377 : 28 : default: break;
378 : : }
379 : 28 : return false;
380 : : }
381 : :
382 : 16554 : bool EoPrinter::isHandledBitblastStep(const Node& eq)
383 : : {
384 [ - + ][ - + ]: 16554 : Assert(eq.getKind() == Kind::EQUAL);
[ - - ]
385 [ + + ]: 16554 : if (theory::Theory::isLeafOf(eq[0], theory::THEORY_BV))
386 : : {
387 : 3566 : return true;
388 : : }
389 [ + - ]: 12988 : switch (eq[0].getKind())
390 : : {
391 : 12988 : case Kind::CONST_BITVECTOR:
392 : : case Kind::BITVECTOR_EXTRACT:
393 : : case Kind::BITVECTOR_CONCAT:
394 : : case Kind::BITVECTOR_AND:
395 : : case Kind::BITVECTOR_OR:
396 : : case Kind::BITVECTOR_XOR:
397 : : case Kind::BITVECTOR_XNOR:
398 : : case Kind::BITVECTOR_NOT:
399 : : case Kind::BITVECTOR_ADD:
400 : : case Kind::BITVECTOR_SUB:
401 : : case Kind::BITVECTOR_NEG:
402 : : case Kind::BITVECTOR_MULT:
403 : : case Kind::BITVECTOR_SIGN_EXTEND:
404 : : case Kind::BITVECTOR_SHL:
405 : : case Kind::BITVECTOR_ASHR:
406 : : case Kind::BITVECTOR_LSHR:
407 : : case Kind::BITVECTOR_UDIV:
408 : : case Kind::BITVECTOR_UREM:
409 : : case Kind::EQUAL:
410 : : case Kind::BITVECTOR_SLT:
411 : : case Kind::BITVECTOR_SLE:
412 : : case Kind::BITVECTOR_ULT:
413 : : case Kind::BITVECTOR_ULE:
414 : : case Kind::BITVECTOR_ITE:
415 : : case Kind::BITVECTOR_COMP:
416 : : case Kind::BITVECTOR_ULTBV:
417 : 12988 : case Kind::BITVECTOR_SLTBV: return true;
418 : 0 : default:
419 : 0 : Trace("eo-printer-debug") << "Cannot bitblast " << eq[0] << std::endl;
420 : 0 : break;
421 : : }
422 : 0 : return false;
423 : : }
424 : :
425 : 148503 : bool EoPrinter::canEvaluate(Node n)
426 : : {
427 : 148503 : std::unordered_set<TNode> visited;
428 : 148503 : std::vector<TNode> visit;
429 : 148503 : TNode cur;
430 : 148503 : visit.push_back(n);
431 : : do
432 : : {
433 : 496997 : cur = visit.back();
434 : 496997 : visit.pop_back();
435 [ + + ]: 496997 : if (visited.find(cur) == visited.end())
436 : : {
437 : 420814 : visited.insert(cur);
438 : 420814 : Kind k = cur.getKind();
439 [ - + ]: 420814 : if (k == Kind::APPLY_INDEXED_SYMBOLIC)
440 : : {
441 : 0 : k = cur.getOperator().getConst<GenericOp>().getKind();
442 : : }
443 [ + + ][ - ]: 420814 : switch (k)
444 : : {
445 : 417332 : case Kind::ITE:
446 : : case Kind::NOT:
447 : : case Kind::AND:
448 : : case Kind::OR:
449 : : case Kind::IMPLIES:
450 : : case Kind::XOR:
451 : : case Kind::CONST_BOOLEAN:
452 : : case Kind::CONST_INTEGER:
453 : : case Kind::CONST_RATIONAL:
454 : : case Kind::CONST_STRING:
455 : : case Kind::CONST_BITVECTOR:
456 : : case Kind::ADD:
457 : : case Kind::SUB:
458 : : case Kind::NEG:
459 : : case Kind::LT:
460 : : case Kind::GT:
461 : : case Kind::GEQ:
462 : : case Kind::LEQ:
463 : : case Kind::MULT:
464 : : case Kind::NONLINEAR_MULT:
465 : : case Kind::INTS_MODULUS:
466 : : case Kind::INTS_MODULUS_TOTAL:
467 : : case Kind::DIVISION:
468 : : case Kind::DIVISION_TOTAL:
469 : : case Kind::INTS_DIVISION:
470 : : case Kind::INTS_DIVISION_TOTAL:
471 : : case Kind::INTS_ISPOW2:
472 : : case Kind::INTS_LOG2:
473 : : case Kind::POW2:
474 : : case Kind::TO_REAL:
475 : : case Kind::TO_INTEGER:
476 : : case Kind::IS_INTEGER:
477 : : case Kind::ABS:
478 : : case Kind::STRING_CONCAT:
479 : : case Kind::STRING_SUBSTR:
480 : : case Kind::STRING_LENGTH:
481 : : case Kind::STRING_CONTAINS:
482 : : case Kind::STRING_REPLACE:
483 : : case Kind::STRING_REPLACE_ALL:
484 : : case Kind::STRING_INDEXOF:
485 : : case Kind::STRING_TO_CODE:
486 : : case Kind::STRING_FROM_CODE:
487 : : case Kind::STRING_PREFIX:
488 : : case Kind::STRING_SUFFIX:
489 : : case Kind::STRING_ITOS:
490 : : case Kind::STRING_STOI:
491 : : case Kind::STRING_TO_LOWER:
492 : : case Kind::STRING_TO_UPPER:
493 : : case Kind::STRING_REV:
494 : : case Kind::STRING_CHARAT:
495 : : case Kind::STRING_UPDATE:
496 : : case Kind::STRING_LEQ:
497 : : case Kind::BITVECTOR_EXTRACT:
498 : : case Kind::BITVECTOR_CONCAT:
499 : : case Kind::BITVECTOR_ADD:
500 : : case Kind::BITVECTOR_SUB:
501 : : case Kind::BITVECTOR_NEG:
502 : : case Kind::BITVECTOR_NOT:
503 : : case Kind::BITVECTOR_MULT:
504 : : case Kind::BITVECTOR_UDIV:
505 : : case Kind::BITVECTOR_UREM:
506 : : case Kind::BITVECTOR_SHL:
507 : : case Kind::BITVECTOR_LSHR:
508 : : case Kind::BITVECTOR_ASHR:
509 : : case Kind::BITVECTOR_AND:
510 : : case Kind::BITVECTOR_OR:
511 : : case Kind::BITVECTOR_XOR:
512 : : case Kind::BITVECTOR_ULT:
513 : : case Kind::BITVECTOR_ULE:
514 : : case Kind::BITVECTOR_UGT:
515 : : case Kind::BITVECTOR_UGE:
516 : : case Kind::BITVECTOR_SLT:
517 : : case Kind::BITVECTOR_SLE:
518 : : case Kind::BITVECTOR_SGT:
519 : : case Kind::BITVECTOR_SGE:
520 : : case Kind::BITVECTOR_REPEAT:
521 : : case Kind::BITVECTOR_SIGN_EXTEND:
522 : : case Kind::BITVECTOR_ZERO_EXTEND:
523 : : case Kind::CONST_BITVECTOR_SYMBOLIC:
524 : : case Kind::BITVECTOR_UBV_TO_INT:
525 : : case Kind::BITVECTOR_SBV_TO_INT:
526 : : case Kind::INT_TO_BITVECTOR:
527 : 417332 : case Kind::EQUAL: break; // note that equality falls through
528 : 3482 : case Kind::BITVECTOR_SIZE:
529 : : // special case, evaluates no matter what is inside
530 : 3482 : continue;
531 : 0 : default:
532 [ - - ]: 0 : Trace("eo-printer-debug")
533 : 0 : << "Cannot evaluate " << cur.getKind() << std::endl;
534 : 0 : return false;
535 : : }
536 [ + + ]: 765826 : for (const Node& cn : cur)
537 : : {
538 : 348494 : visit.push_back(cn);
539 : 348494 : }
540 : : }
541 [ + + ]: 496997 : } while (!visit.empty());
542 : 148503 : return true;
543 : 148503 : }
544 : :
545 : 182 : bool EoPrinter::isHandledDistinctValues(const Node& n)
546 : : {
547 : 182 : std::unordered_set<TNode> visited;
548 : 182 : std::vector<TNode> visit;
549 : 182 : TNode cur;
550 : 182 : visit.push_back(n);
551 : : do
552 : : {
553 : 296 : cur = visit.back();
554 : 296 : visit.pop_back();
555 [ + + ]: 296 : if (visited.find(cur) == visited.end())
556 : : {
557 : 292 : visited.insert(cur);
558 : : // Note we don't currently handle constants in expert theories
559 : : // or array constants.
560 [ + + ][ + ]: 292 : switch (cur.getKind())
561 : : {
562 : 170 : case Kind::CONST_BOOLEAN:
563 : : case Kind::CONST_INTEGER:
564 : : case Kind::CONST_RATIONAL:
565 : : case Kind::CONST_STRING:
566 : : case Kind::CONST_BITVECTOR:
567 : : case Kind::SET_SINGLETON:
568 : : case Kind::SET_UNION:
569 : : case Kind::SET_EMPTY:
570 : : case Kind::APPLY_CONSTRUCTOR:
571 : 170 : case Kind::SEQ_UNIT: break;
572 : 24 : case Kind::CONST_SEQUENCE:
573 [ + + ]: 24 : if (!cur.getConst<Sequence>().empty())
574 : : {
575 : : // must traverse on component values
576 : 20 : cur = theory::strings::utils::mkConcatForConstSequence(cur);
577 : : }
578 : 24 : break;
579 : 98 : default:
580 [ + - ]: 196 : Trace("eo-printer-debug")
581 : 98 : << "Cannot distinct values " << cur.getKind() << std::endl;
582 : 98 : return false;
583 : : }
584 [ + + ]: 308 : for (const Node& cn : cur)
585 : : {
586 : 114 : visit.push_back(cn);
587 : 114 : }
588 : : }
589 [ + + ]: 198 : } while (!visit.empty());
590 : 84 : return true;
591 : 182 : }
592 : :
593 : 164 : bool EoPrinter::canEvaluateRegExp(Node r)
594 : : {
595 [ - + ][ - + ]: 164 : Assert(r.getType().isRegExp());
[ - - ]
596 [ + - ]: 164 : Trace("eo-printer-debug") << "canEvaluateRegExp? " << r << std::endl;
597 : 164 : std::unordered_set<TNode> visited;
598 : 164 : std::vector<TNode> visit;
599 : 164 : TNode cur;
600 : 164 : visit.push_back(r);
601 : : do
602 : : {
603 : 992 : cur = visit.back();
604 : 992 : visit.pop_back();
605 [ + + ]: 992 : if (visited.find(cur) == visited.end())
606 : : {
607 : 578 : visited.insert(cur);
608 [ + + ][ + - ]: 578 : switch (cur.getKind())
609 : : {
610 : 320 : case Kind::REGEXP_ALL:
611 : : case Kind::REGEXP_ALLCHAR:
612 : : case Kind::REGEXP_COMPLEMENT:
613 : : case Kind::REGEXP_NONE:
614 : : case Kind::REGEXP_UNION:
615 : : case Kind::REGEXP_INTER:
616 : : case Kind::REGEXP_CONCAT:
617 : 320 : case Kind::REGEXP_STAR: break;
618 : 32 : case Kind::REGEXP_RANGE:
619 [ - + ]: 32 : if (!theory::strings::utils::isCharacterRange(cur))
620 : : {
621 [ - - ]: 0 : Trace("eo-printer-debug") << "Non-char range" << std::endl;
622 : 0 : return false;
623 : : }
624 : 32 : continue;
625 : 226 : case Kind::STRING_TO_REGEXP:
626 [ - + ]: 226 : if (!canEvaluate(cur[0]))
627 : : {
628 [ - - ]: 0 : Trace("eo-printer-debug") << "Non-evaluatable string" << std::endl;
629 : 0 : return false;
630 : : }
631 : 226 : continue;
632 : 0 : default:
633 [ - - ]: 0 : Trace("eo-printer-debug") << "Cannot evaluate " << cur.getKind()
634 : 0 : << " in regular expressions" << std::endl;
635 : 0 : return false;
636 : : }
637 [ + + ]: 1148 : for (const Node& cn : cur)
638 : : {
639 : 828 : visit.push_back(cn);
640 : 828 : }
641 : : }
642 [ + + ]: 992 : } while (!visit.empty());
643 : 164 : return true;
644 : 164 : }
645 : :
646 : 4189666 : std::string EoPrinter::getRuleName(const ProofNode* pfn) const
647 : : {
648 : 4189666 : ProofRule r = pfn->getRule();
649 [ + + ]: 4189666 : if (r == ProofRule::DSL_REWRITE)
650 : : {
651 : : ProofRewriteRule id;
652 : 238418 : rewriter::getRewriteRule(pfn->getArguments()[0], id);
653 : 238418 : std::stringstream ss;
654 : 238418 : ss << id;
655 : 238418 : return ss.str();
656 : 238418 : }
657 [ + + ]: 3951248 : else if (r == ProofRule::THEORY_REWRITE)
658 : : {
659 : : ProofRewriteRule id;
660 : 13152 : rewriter::getRewriteRule(pfn->getArguments()[0], id);
661 : 13152 : std::stringstream ss;
662 : 13152 : ss << id;
663 : 13152 : return ss.str();
664 : 13152 : }
665 [ + + ][ + + ]: 3938096 : else if (r == ProofRule::ENCODE_EQ_INTRO || r == ProofRule::HO_APP_ENCODE
666 [ + + ]: 3936866 : || r == ProofRule::BV_EAGER_ATOM)
667 : : {
668 : : // ENCODE_EQ_INTRO proves (= t (convert t)) from argument t,
669 : : // where (convert t) is indistinguishable from t according to the proof.
670 : : // Similarly, HO_APP_ENCODE proves an equality between a term of kind
671 : : // Kind::HO_APPLY and Kind::APPLY_UF, which denotes the same term in Eunoia.
672 : : // BV_EAGER_ATOM also is indistinguishable as the eager atom predicate is
673 : : // ignored in the printer.
674 : 1238 : return "refl";
675 : : }
676 [ + + ]: 3936858 : else if (r == ProofRule::ACI_NORM)
677 : : {
678 : 54556 : Node eq = pfn->getArguments()[0];
679 [ - + ][ - + ]: 54556 : Assert(eq.getKind() == Kind::EQUAL);
[ - - ]
680 : : // may have to use the "expert" version.
681 : : Kind k;
682 : 109112 : if (eq[0].getKind() == eq[1].getKind()
683 : 109112 : || expr::getACINormalForm(eq[0]) == eq[1])
684 : : {
685 : 54376 : k = eq[0].getKind();
686 : : }
687 : : else
688 : : {
689 : 180 : k = eq[1].getKind();
690 : : }
691 : 54556 : std::stringstream ss;
692 : 54556 : ss << "aci_norm";
693 [ + + ]: 54556 : switch (k)
694 : : {
695 : 200 : case Kind::SEP_STAR:
696 : : case Kind::FINITE_FIELD_ADD:
697 : 200 : case Kind::FINITE_FIELD_MULT: ss << "_expert"; break;
698 : 54356 : default: break;
699 : : }
700 : 54556 : return ss.str();
701 : 54556 : }
702 : 7764604 : std::string name = toString(r);
703 : 3882302 : std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) {
704 : 36175112 : return std::tolower(c);
705 : : });
706 : 3882302 : return name;
707 : : }
708 : :
709 : 0 : void EoPrinter::printDslRule(std::ostream& out, ProofRewriteRule r)
710 : : {
711 : 0 : options::ioutils::applyPrintArithLitToken(out, true);
712 : 0 : options::ioutils::applyPrintSkolemDefinitions(out, true);
713 : 0 : const rewriter::RewriteProofRule& rpr = d_rdb->getRule(r);
714 : 0 : const std::vector<Node>& varList = rpr.getVarList();
715 : 0 : const std::vector<Node>& uvarList = rpr.getUserVarList();
716 : 0 : const std::vector<Node>& conds = rpr.getConditions();
717 : 0 : Node conc = rpr.getConclusion(true);
718 : : // We must map variables of the rule to internal symbols (via
719 : : // mkInternalSymbol) so that the Eunoia node converter will not treat the
720 : : // BOUND_VARIABLE of this rule as user provided variables. The substitution
721 : : // su stores this mapping.
722 : 0 : Subs su;
723 : 0 : out << "(declare-rule " << r << " (";
724 : 0 : EoDependentTypeConverter adtc(nodeManager(), d_tproc);
725 : 0 : std::stringstream ssExplicit;
726 : 0 : std::map<std::string, size_t> nameCount;
727 : 0 : std::vector<Node> uviList;
728 : 0 : std::map<Node, Node> adtcConvMap;
729 [ - - ]: 0 : for (size_t i = 0, nvars = uvarList.size(); i < nvars; i++)
730 : : {
731 [ - - ]: 0 : if (i > 0)
732 : : {
733 : 0 : ssExplicit << " ";
734 : : }
735 : 0 : const Node& uv = uvarList[i];
736 : 0 : std::stringstream sss;
737 : 0 : sss << uv;
738 : : // Use a consistent variable name, which e.g. ensures that minor changes
739 : : // to the RARE rules do not induce major changes in the CPC definition.
740 : : // Below, we have a variable when the user has named x (which itself may
741 : : // contain digits), and the cvc5 RARE parser has renamed to xN where N is
742 : : // <numeral>+. We rename this to xM where M is the number of times we have
743 : : // seen a variable with prefix M. For example, the variable `x1s2` may be
744 : : // renamed to `x1s2123`, which will be renamed to `x1s1` here.
745 : 0 : std::string str = sss.str();
746 : 0 : size_t index = str.find_last_not_of("0123456789");
747 : 0 : std::string result = str.substr(0, index + 1);
748 : 0 : sss.str("");
749 : 0 : nameCount[result]++;
750 : 0 : sss << result << nameCount[result];
751 : 0 : Node uvi = d_tproc.mkInternalSymbol(sss.str(), uv.getType());
752 : 0 : uviList.emplace_back(uvi);
753 : 0 : su.add(varList[i], uvi);
754 : 0 : ssExplicit << "(" << sss.str() << " ";
755 : 0 : TypeNode uvt = uv.getType();
756 : 0 : Node uvtp = adtc.process(uvt);
757 : 0 : adtcConvMap[uvi] = uvtp;
758 : 0 : ssExplicit << uvtp;
759 [ - - ]: 0 : if (expr::isListVar(uv))
760 : : {
761 : : // carry over whether it is a list variable
762 : 0 : expr::markListVar(uvi);
763 : 0 : ssExplicit << " :list";
764 : : }
765 : 0 : ssExplicit << ")";
766 : 0 : }
767 : : // print implicit parameters introduced in dependent type conversion
768 : 0 : const std::vector<Node>& params = adtc.getFreeParameters();
769 [ - - ]: 0 : for (const Node& p : params)
770 : : {
771 : 0 : out << "(" << p << " " << p.getType() << ") ";
772 : : }
773 : : // carry the mapping from symbols to their types, which is used when
774 : : // eliminating internal-only operators for representing empty set and sequence
775 : 0 : EoListNodeConverter ltproc(nodeManager(), d_tproc, adtcConvMap);
776 : : // now print variables of the proof rule
777 : 0 : out << ssExplicit.str();
778 : 0 : out << ")" << std::endl;
779 [ - - ]: 0 : if (!conds.empty())
780 : : {
781 : 0 : out << " :premises (";
782 : 0 : bool firstTime = true;
783 [ - - ]: 0 : for (const Node& c : conds)
784 : : {
785 [ - - ]: 0 : if (firstTime)
786 : : {
787 : 0 : firstTime = false;
788 : : }
789 : : else
790 : : {
791 : 0 : out << " ";
792 : : }
793 : : // note we apply list conversion to premises as well.
794 : 0 : Node cc = d_tproc.convert(su.apply(c));
795 : 0 : cc = ltproc.convert(cc);
796 : 0 : out << cc;
797 : 0 : }
798 : 0 : out << ")" << std::endl;
799 : : }
800 : 0 : out << " :args (";
801 : 0 : bool printedArg = false;
802 [ - - ]: 0 : for (const Node& v : uviList)
803 : : {
804 [ - - ]: 0 : out << (printedArg ? " " : "");
805 : 0 : printedArg = true;
806 : 0 : out << v;
807 : : }
808 : : // Special case: must print explicit types.
809 : : // This is to handle rules where Kind::TYPE_OF appears in the conclusion
810 : : // or in the premises. Since RARE rules do not take types as arguments,
811 : : // we must add them here. The printer for proof steps will add them in
812 : : // a similar manner.
813 : 0 : std::vector<Node> explictTypeOf = rpr.getExplicitTypeOfList();
814 : 0 : std::map<Node, Node>::iterator itet;
815 [ - - ]: 0 : for (const Node& et : explictTypeOf)
816 : : {
817 [ - - ]: 0 : out << (printedArg ? " " : "");
818 : 0 : printedArg = true;
819 : 0 : Assert(et.getKind() == Kind::TYPE_OF);
820 : 0 : Node v = su.apply(et[0]);
821 : 0 : itet = adtcConvMap.find(v);
822 : 0 : Assert(itet != adtcConvMap.end());
823 : 0 : out << itet->second;
824 : 0 : }
825 : 0 : out << ")" << std::endl;
826 : 0 : Node sconc = d_tproc.convert(su.apply(conc));
827 : 0 : Node rhs = ltproc.convert(sconc[1]);
828 : : // do not apply singleton elimination to head
829 : 0 : EoListNodeConverter ltprocNse(nodeManager(), d_tproc, adtcConvMap, false);
830 : 0 : Node lhs = ltprocNse.convert(sconc[0]);
831 : 0 : Assert(sconc.getKind() == Kind::EQUAL);
832 : 0 : out << " :conclusion (= " << lhs << " " << rhs << ")" << std::endl;
833 : 0 : out << ")" << std::endl;
834 : 0 : }
835 : :
836 : 0 : LetBinding* EoPrinter::getLetBinding() { return d_lbindUse; }
837 : :
838 : 1893 : void EoPrinter::printLetList(std::ostream& out, LetBinding& lbind)
839 : : {
840 : 1893 : std::vector<Node> letList;
841 : 1893 : lbind.letify(letList);
842 : 1893 : std::map<Node, size_t>::const_iterator it;
843 [ + + ]: 1439529 : for (size_t i = 0, nlets = letList.size(); i < nlets; i++)
844 : : {
845 : 1437636 : Node n = letList[i];
846 : : // use define command which does not invoke type checking
847 : 1437636 : out << "(define " << d_termLetPrefix << lbind.getId(n);
848 : 1437636 : out << " () ";
849 : 1437636 : Printer::getPrinter(out)->toStream(out, n, &lbind, false);
850 : 1437636 : out << ")" << std::endl;
851 : 1437636 : }
852 : 1893 : }
853 : :
854 : 1893 : void EoPrinter::print(std::ostream& out,
855 : : std::shared_ptr<ProofNode> pfn,
856 : : ProofScopeMode psm)
857 : : {
858 : : // ensures options are set once and for all
859 : 1893 : options::ioutils::applyOutputLanguage(out, Language::LANG_SMTLIB_V2_6);
860 : 1893 : options::ioutils::applyPrintArithLitToken(out, true);
861 : 1893 : options::ioutils::applyPrintSkolemDefinitions(out, true);
862 : : // allocate a print channel
863 : 1893 : EoPrintChannelOut aprint(out, d_lbindUse, d_termLetPrefix, true);
864 : 1893 : print(aprint, pfn, psm);
865 : 1893 : }
866 : :
867 : 1893 : void EoPrinter::print(EoPrintChannelOut& aout,
868 : : std::shared_ptr<ProofNode> pfn,
869 : : ProofScopeMode psm)
870 : : {
871 : 1893 : std::ostream& out = aout.getOStream();
872 [ - + ][ - + ]: 1893 : Assert(d_pletMap.empty());
[ - - ]
873 : 1893 : d_pfIdCounter = 0;
874 : :
875 : 1893 : const ProofNode* ascope = nullptr;
876 : 1893 : const ProofNode* dscope = nullptr;
877 : 1893 : const ProofNode* pnBody = nullptr;
878 [ - + ]: 1893 : if (psm == ProofScopeMode::NONE)
879 : : {
880 : 0 : pnBody = pfn.get();
881 : : }
882 [ - + ]: 1893 : else if (psm == ProofScopeMode::UNIFIED)
883 : : {
884 : 0 : ascope = pfn.get();
885 : 0 : Assert(ascope->getRule() == ProofRule::SCOPE);
886 : 0 : pnBody = pfn->getChildren()[0].get();
887 : : }
888 [ + - ]: 1893 : else if (psm == ProofScopeMode::DEFINITIONS_AND_ASSERTIONS)
889 : : {
890 : 1893 : dscope = pfn.get();
891 [ - + ][ - + ]: 1893 : Assert(dscope->getRule() == ProofRule::SCOPE);
[ - - ]
892 : 1893 : ascope = pfn->getChildren()[0].get();
893 [ - + ][ - + ]: 1893 : Assert(ascope->getRule() == ProofRule::SCOPE);
[ - - ]
894 : 1893 : pnBody = pfn->getChildren()[0]->getChildren()[0].get();
895 : : }
896 : :
897 : : // Get the definitions and assertions and print the declarations from them
898 : : const std::vector<Node>& definitions =
899 [ + - ]: 1893 : dscope != nullptr ? dscope->getArguments() : d_emptyVec;
900 : : const std::vector<Node>& assertions =
901 [ + - ]: 1893 : ascope != nullptr ? ascope->getArguments() : d_emptyVec;
902 : :
903 : : bool wasAlloc;
904 [ + + ]: 5679 : for (size_t i = 0; i < 2; i++)
905 : : {
906 : : EoPrintChannel* ao;
907 [ + + ]: 3786 : if (i == 0)
908 : : {
909 : 1893 : ao = &d_eletify;
910 : : }
911 : : else
912 : : {
913 : 1893 : ao = &aout;
914 : : }
915 [ + + ]: 3786 : if (i == 1)
916 : : {
917 : : // do not need to print DSL rules
918 [ + - ]: 1893 : if (!options().proof.proofPrintReference)
919 : : {
920 : : // [1] print the declarations
921 : 1893 : printer::smt2::Smt2Printer eprinter(printer::smt2::Variant::eo_variant);
922 : : // we do not print declarations in a sorted manner to reduce overhead
923 : 1893 : smt::PrintBenchmark pb(nodeManager(), &eprinter, false, &d_tproc);
924 : 1893 : std::stringstream outDecl;
925 : 1893 : std::stringstream outDef;
926 : 1893 : options::ioutils::applyPrintArithLitToken(outDef, true);
927 : 1893 : pb.printDeclarationsFrom(outDecl, outDef, definitions, assertions);
928 : 1893 : out << outDecl.str();
929 : : // [2] print the definitions
930 : 1893 : out << outDef.str();
931 : 1893 : }
932 : : // [3] print proof-level term bindings
933 : 1893 : printLetList(out, d_lbind);
934 : : }
935 : : // [4] print (unique) assumptions, including definitions
936 : 3786 : std::unordered_set<Node> processed;
937 [ + + ]: 34158 : for (const Node& n : assertions)
938 : : {
939 [ + + ]: 30372 : if (processed.find(n) != processed.end())
940 : : {
941 : 694 : continue;
942 : : }
943 : 29678 : processed.insert(n);
944 : 29678 : size_t id = allocateAssumeId(n, wasAlloc);
945 : 29678 : Node nc = d_tproc.convert(n);
946 : 29678 : ao->printAssume(nc, id, false);
947 : 29678 : }
948 [ + + ]: 5000 : for (const Node& n : definitions)
949 : : {
950 [ - + ]: 1214 : if (n.getKind() != Kind::EQUAL)
951 : : {
952 : : // skip define-fun-rec?
953 : 0 : continue;
954 : : }
955 [ - + ]: 1214 : if (processed.find(n) != processed.end())
956 : : {
957 : 0 : continue;
958 : : }
959 : 1214 : processed.insert(n);
960 : : // define-fun are HO equalities that can be proven by refl
961 : 1214 : size_t id = allocateAssumeId(n, wasAlloc);
962 : 1214 : Node f = d_tproc.convert(n[0]);
963 : 1214 : Node lam = d_tproc.convert(n[1]);
964 : 2428 : ao->printStep("refl", f.eqNode(lam), id, {}, {lam});
965 : 1214 : }
966 : : // [5] print proof body
967 : 3786 : printProofInternal(ao, pnBody, i == 1);
968 : 3786 : }
969 : : // [6] If the body of the proof is an assumption, then no step was printed
970 : : // for it above and the proof would end with an assume command. We print a
971 : : // dummy step here so that the proof always ends with a step.
972 [ + + ]: 1893 : if (pnBody->getRule() == ProofRule::ASSUME)
973 : : {
974 : 20 : printAssumeBodyStep(aout, pnBody);
975 : : }
976 : 1893 : }
977 : :
978 : 20 : void EoPrinter::printAssumeBodyStep(EoPrintChannelOut& aout,
979 : : const ProofNode* pn)
980 : : {
981 [ - + ][ - + ]: 20 : Assert(pn->getRule() == ProofRule::ASSUME);
[ - - ]
982 : : // The body of the proof is an assumption. This is the case e.g. if false is
983 : : // one of the input assertions, in which case the proof of false is the
984 : : // assumption of false itself. Since we require that proofs end with a step
985 : : // and not an assume command, we print a dummy derivation of the assumed
986 : : // formula F from the assumption of F:
987 : : //
988 : : // ------------- refl
989 : : // @p_a: F @p_r: (= F F)
990 : : // ------------------------------------------ eq_resolve
991 : : // @p_c: F
992 : 20 : Node f = d_tproc.convert(pn->getResult());
993 : 20 : bool wasAlloc = false;
994 : 20 : size_t aid = allocateAssumeId(pn->getResult(), wasAlloc);
995 [ - + ]: 20 : if (wasAlloc)
996 : : {
997 : : // Print the assumption if it was not printed above, which should only
998 : : // happen if we are not printing the proof within a scope.
999 : 0 : aout.printAssume(f, aid, false);
1000 : : }
1001 : 20 : d_pfIdCounter++;
1002 : 20 : size_t rid = d_pfIdCounter;
1003 : 40 : aout.printStep("refl", f.eqNode(f), rid, {}, {f});
1004 : 20 : d_pfIdCounter++;
1005 : 20 : aout.printStep("eq_resolve", f, d_pfIdCounter, {aid, rid}, {});
1006 : : // Note that F is not necessarily false here, since this method applies to
1007 : : // any proof whose body is an assumption, e.g. the preprocessed input proof
1008 : : // printed when proof logging. The dummy step is unnecessary in that case,
1009 : : // but harmless.
1010 : 20 : }
1011 : :
1012 : 0 : void EoPrinter::printNext(EoPrintChannelOut& aout,
1013 : : std::shared_ptr<ProofNode> pfn)
1014 : : {
1015 : 0 : const ProofNode* pnBody = pfn.get();
1016 : : // print with letification
1017 : 0 : printProofInternal(&d_eletify, pnBody, false);
1018 : : // print the new let bindings
1019 : 0 : std::ostream& out = aout.getOStream();
1020 : : // Print new terms from the let binding. note that this should print only
1021 : : // the terms we have yet to see so far.
1022 : 0 : printLetList(out, d_lbind);
1023 : : // print the proof
1024 : 0 : printProofInternal(&aout, pnBody, true);
1025 : 0 : }
1026 : :
1027 : 3786 : void EoPrinter::printProofInternal(EoPrintChannel* out,
1028 : : const ProofNode* pn,
1029 : : bool addToCache)
1030 : : {
1031 : : // the stack
1032 : 3786 : std::vector<const ProofNode*> visit;
1033 : : // Whether we have to process children.
1034 : : // This map is dependent on the proof assumption context, e.g. subproofs of
1035 : : // SCOPE are reprocessed if they happen to occur in different proof scopes.
1036 : 3786 : context::CDHashMap<const ProofNode*, bool> processingChildren(&d_passumeCtx);
1037 : : // helper iterators
1038 : 3786 : context::CDHashMap<const ProofNode*, bool>::iterator pit;
1039 : : const ProofNode* cur;
1040 : 3786 : visit.push_back(pn);
1041 : : do
1042 : : {
1043 : 18091330 : cur = visit.back();
1044 [ + + ]: 18091330 : if (d_alreadyPrinted.find(cur) != d_alreadyPrinted.end())
1045 : : {
1046 : 2466838 : visit.pop_back();
1047 : 2466838 : continue;
1048 : : }
1049 : 15624492 : pit = processingChildren.find(cur);
1050 [ + + ]: 15624492 : if (pit == processingChildren.end())
1051 : : {
1052 : 8963588 : ProofRule r = cur->getRule();
1053 [ + + ]: 8963588 : if (r == ProofRule::ASSUME)
1054 : : {
1055 : : // ignore
1056 : 4769842 : visit.pop_back();
1057 : 4769842 : continue;
1058 : : }
1059 : : // print preorder traversal
1060 : 4193746 : printStepPre(out, cur);
1061 : 4193746 : processingChildren[cur] = true;
1062 : : // will revisit this proof node
1063 : 4193746 : std::vector<std::shared_ptr<ProofNode>> children;
1064 : 4193746 : getChildrenFromProofRule(cur, children);
1065 : : // visit each child
1066 [ + + ]: 18087544 : for (const std::shared_ptr<ProofNode>& c : children)
1067 : : {
1068 : 13893798 : visit.push_back(c.get());
1069 : : }
1070 : 4193746 : continue;
1071 : 4193746 : }
1072 : 6660904 : visit.pop_back();
1073 [ + + ]: 6660904 : if (pit->second)
1074 : : {
1075 : 4193746 : processingChildren[cur] = false;
1076 : : // print postorder traversal
1077 : 4193746 : printStepPost(out, cur);
1078 [ + + ]: 4193746 : if (addToCache)
1079 : : {
1080 : 2094397 : d_alreadyPrinted.insert(cur);
1081 : : }
1082 : : }
1083 [ + + ]: 18091330 : } while (!visit.empty());
1084 : 3786 : }
1085 : :
1086 : 4193746 : void EoPrinter::printStepPre(EoPrintChannel* out, const ProofNode* pn)
1087 : : {
1088 : : // if we haven't yet allocated a proof id, do it now
1089 : 4193746 : ProofRule r = pn->getRule();
1090 [ + + ]: 4193746 : if (r == ProofRule::SCOPE)
1091 : : {
1092 : : // The assumptions only are valid within the body of the SCOPE, thus
1093 : : // we push a context scope.
1094 : 110135 : d_passumeCtx.push();
1095 : 110135 : const std::vector<Node>& args = pn->getArguments();
1096 [ + + ]: 671812 : for (const Node& a : args)
1097 : : {
1098 : 561677 : size_t aid = allocateAssumePushId(pn, a);
1099 : 561677 : Node aa = d_tproc.convert(a);
1100 : : // print a push
1101 : 561677 : out->printAssume(aa, aid, true);
1102 : 561677 : }
1103 : : }
1104 : 4193746 : }
1105 : :
1106 : 8387492 : void EoPrinter::getChildrenFromProofRule(
1107 : : const ProofNode* pn, std::vector<std::shared_ptr<ProofNode>>& children)
1108 : : {
1109 : 8387492 : const std::vector<std::shared_ptr<ProofNode>>& cc = pn->getChildren();
1110 [ + + ]: 8387492 : switch (pn->getRule())
1111 : : {
1112 : 975572 : case ProofRule::CONG:
1113 : : {
1114 : : // Ignore prefix of premises that are just REFL. Moreover this is required
1115 : : // to ensure CONG over APPLY_INDEXED_SYMBOLIC do not include premises
1116 : : // stating equality over indices to indexed operators, which cong does
1117 : : // not handle.
1118 : 975572 : size_t start = 0;
1119 : 975572 : while (start < cc.size()
1120 : 1261910 : && cc[start]->getResult()[0] == cc[start]->getResult()[1])
1121 : : {
1122 : 286338 : start++;
1123 : : }
1124 : 975572 : Node res = pn->getResult();
1125 [ + + ]: 975572 : if (res[0].isClosure())
1126 : : {
1127 : : // Ignore the children after the required arguments.
1128 : : // This ensures that we ignore e.g. equalities between patterns
1129 : : // which can appear in term conversion proofs.
1130 : 26316 : size_t arity = kind::metakind::getMinArityForKind(res[0].getKind());
1131 : 78948 : children.insert(
1132 : 78948 : children.end(), cc.begin() + start, cc.begin() + arity - 1);
1133 : 26316 : return;
1134 : : }
1135 [ + + ]: 949256 : else if (start > 0)
1136 : : {
1137 : 272954 : children.insert(children.end(), cc.begin() + start, cc.end());
1138 : 272954 : return;
1139 : : }
1140 [ + + ]: 975572 : }
1141 : 676302 : break;
1142 : 7411920 : default: break;
1143 : : }
1144 : 8088222 : children.insert(children.end(), cc.begin(), cc.end());
1145 : : }
1146 : :
1147 : 4189666 : void EoPrinter::getArgsFromProofRule(const ProofNode* pn,
1148 : : std::vector<Node>& args)
1149 : : {
1150 : 4189666 : Node res = pn->getResult();
1151 : 4189666 : const std::vector<Node> pargs = pn->getArguments();
1152 : 4189666 : ProofRule r = pn->getRule();
1153 [ + + ][ + + ]: 4189666 : switch (r)
[ + ]
1154 : : {
1155 : 1128 : case ProofRule::HO_CONG:
1156 : : {
1157 : : // argument is ignored
1158 : 1128 : return;
1159 : : }
1160 : 3684 : case ProofRule::INSTANTIATE:
1161 : : {
1162 : : // ignore arguments past the term vector
1163 : 3684 : Node ts = d_tproc.convert(pargs[0]);
1164 : 3684 : args.push_back(ts);
1165 : 3684 : return;
1166 : 3684 : }
1167 : 238418 : case ProofRule::DSL_REWRITE:
1168 : : {
1169 : : ProofRewriteRule dr;
1170 [ - + ]: 238418 : if (!rewriter::getRewriteRule(pargs[0], dr))
1171 : : {
1172 : 0 : Unhandled() << "Failed to get DSL proof rule";
1173 : : }
1174 [ + - ]: 238418 : Trace("eo-printer-debug") << "Get args for " << dr << std::endl;
1175 : 238418 : const rewriter::RewriteProofRule& rpr = d_rdb->getRule(dr);
1176 : 238418 : std::vector<Node> ss(pargs.begin() + 1, pargs.end());
1177 : 238418 : std::vector<std::pair<Kind, std::vector<Node>>> witnessTerms;
1178 : 238418 : rpr.getConclusionFor(ss, witnessTerms);
1179 : : // the arguments are the computed witness terms
1180 [ + + ]: 688298 : for (const std::pair<Kind, std::vector<Node>>& w : witnessTerms)
1181 : : {
1182 [ + + ]: 449880 : if (w.first == Kind::UNDEFINED_KIND)
1183 : : {
1184 [ - + ][ - + ]: 442154 : Assert(w.second.size() == 1);
[ - - ]
1185 : 442154 : args.push_back(d_tproc.convert(w.second[0]));
1186 : : }
1187 : : else
1188 : : {
1189 : 7726 : std::vector<Node> wargs;
1190 [ + + ]: 272808 : for (const Node& wc : w.second)
1191 : : {
1192 : 265082 : wargs.push_back(d_tproc.convert(wc));
1193 : : }
1194 : 7726 : args.push_back(d_tproc.mkInternalApp(
1195 : 15452 : printer::smt2::Smt2Printer::smtKindString(w.first),
1196 : : wargs,
1197 : 7726 : d_absType));
1198 : 7726 : }
1199 : : }
1200 : : // special case: explicit type-of terms, which require explicit type
1201 : : // arguments
1202 : : std::map<ProofRewriteRule, std::vector<Node>>::iterator it =
1203 : 238418 : d_explicitTypeOf.find(dr);
1204 [ + + ]: 238418 : if (it == d_explicitTypeOf.end())
1205 : : {
1206 : 9514 : d_explicitTypeOf[dr] = rpr.getExplicitTypeOfList();
1207 : 9514 : it = d_explicitTypeOf.find(dr);
1208 : : }
1209 [ + + ]: 238418 : if (!it->second.empty())
1210 : : {
1211 : 520 : const std::vector<Node>& fvs = rpr.getVarList();
1212 [ - + ][ - + ]: 520 : AlwaysAssert(fvs.size() == ss.size());
[ - - ]
1213 [ + + ]: 1040 : for (const Node& t : it->second)
1214 : : {
1215 [ - + ][ - + ]: 520 : Assert(t.getKind() == Kind::TYPE_OF);
[ - - ]
1216 : : Node tts =
1217 : 520 : t[0].substitute(fvs.begin(), fvs.end(), ss.begin(), ss.end());
1218 : 520 : args.push_back(d_tproc.typeAsNode(tts.getType()));
1219 : 520 : }
1220 : : }
1221 : 238418 : return;
1222 : 238418 : }
1223 : 13152 : case ProofRule::THEORY_REWRITE:
1224 : : {
1225 : : // ignore the identifier
1226 [ - + ][ - + ]: 13152 : Assert(pargs.size() == 2);
[ - - ]
1227 : 13152 : args.push_back(d_tproc.convert(pargs[1]));
1228 : 13152 : return;
1229 : : }
1230 : : break;
1231 : 3933284 : default: break;
1232 : : }
1233 [ + + ]: 8195516 : for (size_t i = 0, nargs = pargs.size(); i < nargs; i++)
1234 : : {
1235 : 4262232 : Node av = d_tproc.convert(pargs[i]);
1236 : 4262232 : args.push_back(av);
1237 : 4262232 : }
1238 [ + + ][ + + ]: 4446048 : }
1239 : :
1240 : 4193746 : void EoPrinter::printStepPost(EoPrintChannel* out, const ProofNode* pn)
1241 : : {
1242 [ - + ][ - + ]: 4193746 : Assert(pn->getRule() != ProofRule::ASSUME);
[ - - ]
1243 : : // if we have yet to allocate a proof id, do it now
1244 : 4193746 : bool wasAlloc = false;
1245 : 8387492 : TNode conclusion = d_tproc.convert(pn->getResult());
1246 : 4193746 : TNode conclusionPrint;
1247 : : // print conclusion only if option is set, or this is false
1248 [ + + ][ + + ]: 4193746 : if (options().proof.proofPrintConclusion || conclusion == d_false)
[ + + ]
1249 : : {
1250 : 4192168 : conclusionPrint = conclusion;
1251 : : }
1252 : 4193746 : ProofRule r = pn->getRule();
1253 : 4193746 : std::vector<std::shared_ptr<ProofNode>> children;
1254 : 4193746 : getChildrenFromProofRule(pn, children);
1255 : 4193746 : std::vector<Node> args;
1256 : 4193746 : bool handled = isHandled(options(), pn);
1257 [ + + ]: 4193746 : if (handled)
1258 : : {
1259 : 4189666 : getArgsFromProofRule(pn, args);
1260 : : }
1261 : 4193746 : size_t id = allocateProofId(pn, wasAlloc);
1262 : 4193746 : std::vector<size_t> premises;
1263 : : // get the premises
1264 : 4193746 : context::CDHashMap<Node, size_t>::iterator ita;
1265 : 4193746 : std::map<const ProofNode*, size_t>::iterator itp;
1266 [ + + ]: 18087544 : for (const std::shared_ptr<ProofNode>& c : children)
1267 : : {
1268 : : size_t pid;
1269 : : // if assume, lookup in passumeMap
1270 [ + + ]: 13893798 : if (c->getRule() == ProofRule::ASSUME)
1271 : : {
1272 : 4769802 : ita = d_passumeMap.find(c->getResult());
1273 [ - + ][ - + ]: 4769802 : Assert(ita != d_passumeMap.end());
[ - - ]
1274 : 4769802 : pid = ita->second;
1275 : : }
1276 : : else
1277 : : {
1278 : 9123996 : itp = d_pletMap.find(c.get());
1279 [ - + ][ - + ]: 9123996 : Assert(itp != d_pletMap.end());
[ - - ]
1280 : 9123996 : pid = itp->second;
1281 : : }
1282 : 13893798 : premises.push_back(pid);
1283 : : }
1284 : : // if we don't handle the rule, print trust
1285 [ + + ]: 4193746 : if (!handled)
1286 : : {
1287 [ - + ]: 4080 : if (!options().proof.proofAllowTrust)
1288 : : {
1289 : 0 : std::stringstream ss;
1290 : 0 : ss << pn->getRule();
1291 [ - - ]: 0 : if (pn->getRule() == ProofRule::THEORY_REWRITE)
1292 : : {
1293 : : ProofRewriteRule prid;
1294 : 0 : rewriter::getRewriteRule(pn->getArguments()[0], prid);
1295 : 0 : ss << " (" << prid << ")";
1296 : : }
1297 [ - - ]: 0 : else if (pn->getRule() == ProofRule::TRUST)
1298 : : {
1299 : : TrustId tid;
1300 : 0 : getTrustId(pn->getArguments()[0], tid);
1301 : 0 : ss << " (" << tid << ")";
1302 : : }
1303 : 0 : Trace("eo-pf-hole") << "Proof rule " << ss.str() << ": "
1304 : 0 : << pn->getResult() << std::endl;
1305 : 0 : Unreachable() << "A Eunoia proof requires a trust step for " << ss.str()
1306 : : << ", but --" << options::proof::longName::proofAllowTrust
1307 : 0 : << " is false" << std::endl;
1308 : 0 : }
1309 : 4080 : out->printTrustStep(pn->getRule(),
1310 : : conclusionPrint,
1311 : : id,
1312 : : premises,
1313 : : pn->getArguments(),
1314 : : conclusion);
1315 : 4080 : return;
1316 : : }
1317 : 4189666 : std::string rname = getRuleName(pn);
1318 [ + + ]: 4189666 : if (r == ProofRule::SCOPE)
1319 : : {
1320 [ - + ]: 110135 : if (args.empty())
1321 : : {
1322 : : // If there are no premises, any reference to this proof can just refer to
1323 : : // the body.
1324 : 0 : d_pletMap[pn] = premises[0];
1325 : : }
1326 : : else
1327 : : {
1328 : : // Assuming the body of the scope has identifier id_0, the following
1329 : : // prints: (step-pop id_1 :rule scope :premises (id_0))
1330 : : // ...
1331 : : // (step-pop id_n :rule scope :premises (id_{n-1}))
1332 : : // (step id :rule process_scope :premises (id_n) :args (C))
1333 : : size_t tmpId;
1334 [ + + ]: 671812 : for (size_t i = 0, nargs = args.size(); i < nargs; i++)
1335 : : {
1336 : : // Manually increment proof id counter and premises. Note they will only
1337 : : // be used locally here to chain together the pops mentioned above.
1338 : 561677 : d_pfIdCounter++;
1339 : 561677 : tmpId = d_pfIdCounter;
1340 : 561677 : out->printStep(rname, Node::null(), tmpId, premises, {}, true);
1341 : : // The current id is the premises of the next.
1342 : 561677 : premises.clear();
1343 : 561677 : premises.push_back(tmpId);
1344 : : }
1345 : : // Finish with the process scope step.
1346 : 110135 : std::vector<Node> pargs;
1347 : 110135 : pargs.push_back(d_tproc.convert(children[0]->getResult()));
1348 : 110135 : out->printStep("process_scope", conclusionPrint, id, premises, pargs);
1349 : 110135 : }
1350 : : // We are done with the assumptions in scope, pop a context.
1351 : 110135 : d_passumeCtx.pop();
1352 : : }
1353 : : else
1354 : : {
1355 : 4079531 : out->printStep(rname, conclusionPrint, id, premises, args);
1356 : : }
1357 [ + + ][ + + ]: 4210066 : }
[ + + ][ + + ]
[ + + ]
1358 : :
1359 : 561677 : size_t EoPrinter::allocateAssumePushId(const ProofNode* pn, const Node& a)
1360 : : {
1361 : 561677 : std::pair<const ProofNode*, Node> key(pn, a);
1362 : :
1363 : 561677 : bool wasAlloc = false;
1364 : 561677 : size_t aid = allocateAssumeId(a, wasAlloc);
1365 : : // if we assigned an id to the assumption
1366 [ + + ]: 561677 : if (!wasAlloc)
1367 : : {
1368 : : // otherwise we shadow, just use a dummy
1369 : 151050 : d_pfIdCounter++;
1370 : 151050 : aid = d_pfIdCounter;
1371 : : }
1372 : 561677 : return aid;
1373 : 561677 : }
1374 : :
1375 : 592589 : size_t EoPrinter::allocateAssumeId(const Node& n, bool& wasAlloc)
1376 : : {
1377 : 592589 : context::CDHashMap<Node, size_t>::iterator it = d_passumeMap.find(n);
1378 [ + + ]: 592589 : if (it != d_passumeMap.end())
1379 : : {
1380 : 166516 : wasAlloc = false;
1381 : 166516 : return it->second;
1382 : : }
1383 : 426073 : wasAlloc = true;
1384 : 426073 : d_pfIdCounter++;
1385 : 426073 : d_passumeMap[n] = d_pfIdCounter;
1386 : 426073 : return d_pfIdCounter;
1387 : : }
1388 : :
1389 : 4193746 : size_t EoPrinter::allocateProofId(const ProofNode* pn, bool& wasAlloc)
1390 : : {
1391 : 4193746 : std::map<const ProofNode*, size_t>::iterator it = d_pletMap.find(pn);
1392 [ + + ]: 4193746 : if (it != d_pletMap.end())
1393 : : {
1394 : 2387143 : wasAlloc = false;
1395 : 2387143 : return it->second;
1396 : : }
1397 : 1806603 : wasAlloc = true;
1398 : 1806603 : d_pfIdCounter++;
1399 : 1806603 : d_pletMap[pn] = d_pfIdCounter;
1400 : 1806603 : return d_pfIdCounter;
1401 : : }
1402 : :
1403 : : } // namespace proof
1404 : : } // namespace cvc5::internal
|