LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/proof/alethe - alethe_printer.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 191 202 94.6 %
Date: 2026-08-05 10:35:17 Functions: 10 10 100.0 %
Branches: 99 172 57.6 %

           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 module for printing Alethe proof nodes.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "proof/alethe/alethe_printer.h"
      14                 :            : 
      15                 :            : #include <iostream>
      16                 :            : #include <sstream>
      17                 :            : #include <unordered_map>
      18                 :            : 
      19                 :            : #include "options/printer_options.h"
      20                 :            : #include "options/proof_options.h"
      21                 :            : #include "proof/alethe/alethe_proof_rule.h"
      22                 :            : #include "util/smt2_quote_string.h"
      23                 :            : 
      24                 :            : namespace cvc5::internal {
      25                 :            : 
      26                 :            : namespace proof {
      27                 :            : 
      28                 :        476 : LetUpdaterPfCallback::LetUpdaterPfCallback(AletheLetBinding& lbind)
      29                 :        476 :     : d_lbind(lbind)
      30                 :            : {
      31                 :        476 : }
      32                 :            : 
      33                 :        952 : LetUpdaterPfCallback::~LetUpdaterPfCallback() {}
      34                 :            : 
      35                 :    1568648 : bool LetUpdaterPfCallback::shouldUpdate(std::shared_ptr<ProofNode> pn,
      36                 :            :                                         CVC5_UNUSED const std::vector<Node>& fa,
      37                 :            :                                         CVC5_UNUSED bool& continueUpdate)
      38                 :            : {
      39                 :    1568648 :   ProofRule r = pn->getRule();
      40         [ +  + ]:    1568648 :   if (r == ProofRule::ASSUME)
      41                 :            :   {
      42                 :     114736 :     d_lbind.process(pn->getResult());
      43                 :     114736 :     return false;
      44                 :            :   }
      45                 :    1453912 :   const std::vector<Node>& args = pn->getArguments();
      46         [ +  + ]:    1453912 :   if (r == ProofRule::SCOPE)
      47                 :            :   {
      48         [ +  + ]:       9543 :     for (size_t i = 0, size = args.size(); i < size; ++i)
      49                 :            :     {
      50                 :       9073 :       d_lbind.process(args[i]);
      51                 :            :     }
      52                 :        470 :     return false;
      53                 :            :   }
      54                 :            :   // Letification done on the converted terms (thus from the converted
      55                 :            :   // conclusion) and potentially on arguments, which means to ignore the first
      56                 :            :   // two arguments (which are the Alethe rule and the original conclusion).
      57 [ -  + ][ -  - ]:    1453442 :   AlwaysAssert(args.size() > 2)
      58                 :    1453442 :       << "res: " << pn->getResult() << "\nid: " << pn->getRule();
      59         [ +  + ]:    3409216 :   for (size_t i = 2, size = args.size(); i < size; ++i)
      60                 :            :   {
      61         [ +  - ]:    1955774 :     Trace("alethe-printer") << "Process " << args[i] << std::endl;
      62                 :            :     // We do not share s-expressions, but rather their children
      63         [ +  + ]:    1955774 :     if (args[i].getKind() == Kind::SEXPR)
      64                 :            :     {
      65         [ +  + ]:    6564223 :       for (const auto& arg : args[i])
      66                 :            :       {
      67                 :    5110508 :         d_lbind.process(arg);
      68                 :    5110508 :       }
      69                 :    1453715 :       continue;
      70                 :    1453715 :     }
      71                 :     502059 :     d_lbind.process(args[i]);
      72                 :            :   }
      73                 :    1453442 :   return false;
      74                 :            : }
      75                 :            : 
      76                 :        476 : AletheProofPrinter::AletheProofPrinter(Env& env, AletheNodeConverter& anc)
      77                 :            :     : EnvObj(env),
      78                 :        476 :       d_context(),
      79                 :        476 :       d_assumptionsMap(&d_context),
      80                 :        476 :       d_pfMap(&d_context),
      81         [ +  + ]:        476 :       d_lbind(options().printer.dagThresh ? options().printer.dagThresh + 1
      82                 :            :                                           : 0),
      83                 :        476 :       d_anc(anc),
      84                 :        952 :       d_cb(new LetUpdaterPfCallback(d_lbind))
      85                 :            : {
      86                 :        476 : }
      87                 :            : 
      88                 :    2076127 : void AletheProofPrinter::printStep(
      89                 :            :     std::ostream& out,
      90                 :            :     const std::string& stepId,
      91                 :            :     AletheRule arule,
      92                 :            :     const std::vector<Node>& pfArgs,
      93                 :            :     const std::vector<std::shared_ptr<ProofNode>>& pfChildren)
      94                 :            : {
      95                 :    2076127 :   out << "(step " << stepId << " ";
      96                 :            :   // print the conclusion and the rule
      97                 :    2076127 :   printTerm(out, pfArgs[2]);
      98                 :    2076127 :   out << " :rule " << arule;
      99         [ +  + ]:    2076127 :   if (!pfChildren.empty())
     100                 :            :   {
     101                 :    1414690 :     out << " :premises (";
     102                 :    1414690 :     bool first = true;
     103         [ +  + ]:    4991779 :     for (const std::shared_ptr<ProofNode>& pfChild : pfChildren)
     104                 :            :     {
     105         [ +  + ]:    3577089 :       out << (first ? "" : " ");
     106                 :    3577089 :       first = false;
     107                 :    3577089 :       printStepId(out, pfChild);
     108                 :            :     }
     109                 :    1414690 :     out << ")";
     110                 :            :   }
     111         [ +  + ]:    2076127 :   if (pfArgs.size() > 3)
     112                 :            :   {
     113                 :     315832 :     out << " :args (";
     114         [ +  + ]:     803507 :     for (size_t i = 3, size = pfArgs.size(); i < size; i++)
     115                 :            :     {
     116                 :     487675 :       printTerm(out, pfArgs[i]);
     117         [ +  + ]:     487675 :       out << (i < pfArgs.size() - 1 ? " " : "");
     118                 :            :     }
     119                 :     315832 :     out << ")";
     120                 :            :   }
     121                 :    2076127 :   out << ")" << std::endl;
     122                 :    2076127 : }
     123                 :            : 
     124                 :    3577089 : void AletheProofPrinter::printStepId(std::ostream& out,
     125                 :            :                                      std::shared_ptr<ProofNode> pfn)
     126                 :            : {
     127         [ +  + ]:    3577089 :   if (pfn->getRule() == ProofRule::ASSUME)
     128                 :            :   {
     129                 :     343109 :     Node res = d_anc.convert(pfn->getResult());
     130 [ -  + ][ -  + ]:     343109 :     Assert(!res.isNull());
                 [ -  - ]
     131         [ +  - ]:     343109 :     Trace("alethe-printer") << "... reached assumption " << res << std::endl;
     132                 :     343109 :     auto it = d_assumptionsMap.find(res);
     133 [ -  + ][ -  - ]:     343109 :     Assert(it != d_assumptionsMap.end())
     134 [ -  + ][ -  + ]:     343109 :         << "Assumption has not been printed yet! " << res << std::endl;
                 [ -  - ]
     135                 :     343109 :     out << it->second;
     136                 :     343109 :     return;
     137                 :     343109 :   }
     138 [ -  + ][ -  - ]:    3233980 :   Assert(d_pfMap.find(pfn.get()) != d_pfMap.end())
     139                 :    3233980 :       << "Cannot find pf of " << pfn->getResult() << std::endl;
     140                 :    3233980 :   out << d_pfMap.find(pfn.get())->second;
     141                 :            : }
     142                 :            : 
     143                 :    2803335 : void AletheProofPrinter::printTerm(std::ostream& out, TNode n)
     144                 :            : {
     145                 :    2803335 :   std::stringstream ss;
     146                 :    2803335 :   options::ioutils::applyOutputLanguage(ss, Language::LANG_SMTLIB_V2_6);
     147                 :            :   // We print lambda applications in non-curried manner
     148                 :    2803335 :   options::ioutils::applyFlattenHOChains(ss, true);
     149                 :            :   // Make sure we do not introduce "let" for sharing, since names will not have
     150                 :            :   // been introduced under binders.
     151                 :    2803335 :   options::ioutils::applyDagThresh(ss, 0);
     152                 :            :   // Guarantee we print reals as expected
     153                 :    2803335 :   options::ioutils::applyPrintArithLitToken(ss, true);
     154                 :    2803335 :   ss << d_lbind.convert(nodeManager(), n, "@p_");
     155                 :    2803335 :   out << ss.str();
     156                 :    2803335 : }
     157                 :            : 
     158                 :        476 : void AletheProofPrinter::print(
     159                 :            :     std::ostream& out,
     160                 :            :     std::shared_ptr<ProofNode> pfn,
     161                 :            :     const std::map<Node, std::string>& assertionNames)
     162                 :            : {
     163         [ +  - ]:        476 :   Trace("alethe-printer") << "- Print proof in Alethe format." << std::endl;
     164                 :            :   // ignore outer scope
     165                 :        476 :   pfn = pfn->getChildren()[0];
     166                 :        476 :   std::shared_ptr<ProofNode> innerPf = pfn->getChildren()[0];
     167 [ -  + ][ -  + ]:        476 :   Assert(innerPf);
                 [ -  - ]
     168                 :            : 
     169                 :            :   // print quantifier Skolems, if they are being defined
     170         [ -  + ]:        476 :   if (options().proof.proofAletheDefineSkolems)
     171                 :            :   {
     172                 :          0 :     const std::map<Node, Node>& skolemDefs = d_anc.getSkolemDefinitions();
     173                 :          0 :     const std::vector<Node>& skolemList = d_anc.getSkolemList();
     174         [ -  - ]:          0 :     for (const auto& skolem : skolemList)
     175                 :            :     {
     176                 :          0 :       Assert(skolemDefs.find(skolem) != skolemDefs.end());
     177                 :          0 :       out << "(define-fun " << skolem << " () " << skolem.getType() << " ";
     178                 :          0 :       printTerm(out, skolemDefs.at(skolem));
     179                 :          0 :       out << ")" << std::endl;
     180                 :            :     }
     181                 :            :   }
     182         [ +  + ]:        476 :   if (options().printer.dagThresh)
     183                 :            :   {
     184                 :            :     // Traverse the proof node to letify the (converted) conclusions of proof
     185                 :            :     // steps. Note that we traverse the original proof node because assumptions
     186                 :            :     // may apper just in them (if they are not used in the rest of the proof).
     187                 :            :     // Otherwise repeated terms *only* in assumptions would not be letified.
     188                 :        470 :     ProofNodeUpdater updater(d_env, *(d_cb.get()), false, false);
     189         [ +  - ]:        470 :     Trace("alethe-printer") << "- letify." << std::endl;
     190                 :        470 :     updater.process(pfn);
     191                 :            : 
     192                 :        470 :     std::vector<Node> letList;
     193                 :        470 :     d_lbind.letify(letList);
     194         [ -  + ]:        470 :     if (TraceIsOn("alethe-printer"))
     195                 :            :     {
     196         [ -  - ]:          0 :       for (TNode n : letList)
     197                 :            :       {
     198         [ -  - ]:          0 :         Trace("alethe-printer")
     199                 :          0 :             << "Term " << n << " has id " << d_lbind.getId(n) << std::endl;
     200                 :          0 :       }
     201                 :            :     }
     202                 :        470 :   }
     203         [ +  - ]:        476 :   Trace("alethe-printer") << "- Print assumptions." << std::endl;
     204                 :        476 :   const std::vector<Node>& args = pfn->getArguments();
     205                 :            :   // Special handling for the first scope. Print assumptions and add them to the
     206                 :            :   // list but do not print anchor.
     207 [ -  + ][ -  + ]:        476 :   Assert(!args.empty());
                 [ -  - ]
     208         [ +  + ]:       9573 :   for (size_t i = 0, size = args.size(); i < size; i++)
     209                 :            :   {
     210                 :            :     // search name with original assumption rather than its conversion
     211 [ -  + ][ -  + ]:       9097 :     Assert(!d_anc.getOriginalAssumption(args[i]).isNull());
                 [ -  - ]
     212                 :       9097 :     Node original = d_anc.getOriginalAssumption(args[i]);
     213                 :       9097 :     auto it = assertionNames.find(original);
     214         [ +  + ]:       9097 :     if (it != assertionNames.end())
     215                 :            :     {
     216                 :            :       // Since names can be strings that were originally quoted, we must see if
     217                 :            :       // the quotes need to be added back.
     218                 :         16 :       std::string quotedName = quoteSymbol(it->second);
     219                 :         16 :       out << "(assume " << quotedName << " ";
     220                 :         16 :       d_assumptionsMap[args[i]] = quotedName;
     221                 :         16 :     }
     222                 :            :     else
     223                 :            :     {
     224                 :       9081 :       out << "(assume a" << i << " ";
     225                 :       9081 :       d_assumptionsMap[args[i]] = "a" + std::to_string(i);
     226                 :            :     }
     227                 :       9097 :     printTerm(out, args[i]);
     228                 :       9097 :     out << ")" << std::endl;
     229                 :       9097 :   }
     230                 :            :   // Then, print the rest of the proof node
     231                 :        476 :   size_t id = 0;
     232                 :        476 :   printInternal(out, "", id, pfn->getChildren()[0]);
     233                 :        476 : }
     234                 :            : 
     235                 :    3620538 : void AletheProofPrinter::printInternal(std::ostream& out,
     236                 :            :                                        const std::string& prefix,
     237                 :            :                                        size_t& id,
     238                 :            :                                        std::shared_ptr<ProofNode> pfn)
     239                 :            : {
     240                 :            :   // assumptions are not printed when reached here because in Alethe they are
     241                 :            :   // always printed beforehand, i.e., from the scope introducing them, or being
     242                 :            :   // the initial assumptions.
     243         [ +  + ]:    3620538 :   if (pfn->getRule() == ProofRule::ASSUME)
     244                 :            :   {
     245                 :    1544420 :     return;
     246                 :            :   }
     247                 :            :   context::CDHashMap<ProofNode*, std::string>::const_iterator pfIt =
     248                 :    3277429 :       d_pfMap.find(pfn.get());
     249         [ +  + ]:    3277429 :   if (pfIt != d_pfMap.end())
     250                 :            :   {
     251         [ +  - ]:    2316628 :     Trace("alethe-printer") << "... step is already printed t" << pfIt->second
     252         [ -  - ]:    1158314 :                             << " " << pfn->getResult() << " "
     253 [ -  + ][ -  + ]:    1158314 :                             << getAletheRule(pfn->getArguments()[0]) << "\n";
                 [ -  - ]
     254                 :    1158314 :     return;
     255                 :            :   }
     256                 :    2119115 :   const std::vector<Node>& args = pfn->getArguments();
     257                 :            :   const std::vector<std::shared_ptr<ProofNode>>& pfChildren =
     258                 :    2119115 :       pfn->getChildren();
     259                 :            :   // Get the alethe proof rule
     260                 :    2119115 :   AletheRule arule = getAletheRule(args[0]);
     261         [ +  - ]:    4238230 :   Trace("alethe-printer") << "... print step " << arule << " : " << args[2]
     262                 :    2119115 :                           << std::endl;
     263                 :            :   // We special case printing anchors
     264                 :    2119115 :   if (arule >= AletheRule::ANCHOR_SUBPROOF
     265         [ +  + ]:    2119115 :       && arule <= AletheRule::ANCHOR_ONEPOINT)
     266                 :            :   {
     267         [ +  - ]:      42997 :     Trace("alethe-printer") << push;
     268 [ -  + ][ -  + ]:      42997 :     Assert(pfChildren.size() == 1);
                 [ -  - ]
     269                 :      42997 :     out << "(anchor :step " << prefix << "t" << id;
     270                 :      85994 :     std::string subproofPrefix = prefix + "t" + std::to_string(id) + ".";
     271                 :            :     // create a new context for the subproof
     272                 :      42997 :     d_context.push();
     273                 :      42997 :     std::vector<std::string> dischargeIds;
     274                 :            :     // if subproof, print assumptions, otherwise print arguments
     275         [ +  + ]:      42997 :     if (arule == AletheRule::ANCHOR_SUBPROOF)
     276                 :            :     {
     277                 :      28777 :       out << ")" << std::endl;
     278 [ -  + ][ -  + ]:      28777 :       Assert(args.size() >= 3);
                 [ -  - ]
     279         [ +  + ]:     191148 :       for (size_t i = 3, size = args.size(); i < size; ++i)
     280                 :            :       {
     281         [ +  - ]:     324742 :         Trace("alethe-printer")
     282                 :     162371 :             << "... print assumption " << args[i] << std::endl;
     283                 :     324742 :         std::string assumptionId = subproofPrefix + "a" + std::to_string(i - 3);
     284                 :     162371 :         out << "(assume " << assumptionId << " ";
     285                 :     162371 :         printTerm(out, args[i]);
     286                 :     162371 :         out << ")" << std::endl;
     287                 :     162371 :         d_assumptionsMap[args[i]] = assumptionId;
     288                 :     162371 :         dischargeIds.push_back(assumptionId);
     289                 :     162371 :       }
     290                 :            :     }
     291                 :            :     else
     292                 :            :     {
     293 [ +  - ][ +  - ]:      14220 :       Assert(arule >= AletheRule::ANCHOR_BIND
         [ -  + ][ -  + ]
                 [ -  - ]
     294                 :            :              && arule <= AletheRule::ANCHOR_ONEPOINT);
     295                 :      14220 :       out << " :args (";
     296         [ +  + ]:      63995 :       for (size_t i = 3, size = args.size(); i < size; ++i)
     297                 :            :       {
     298         [ +  + ]:      49775 :         if (args[i].getKind() == Kind::EQUAL)
     299                 :            :         {
     300 [ -  + ][ -  + ]:      25068 :           Assert(args[i][0].getKind() == Kind::BOUND_VARIABLE);
                 [ -  - ]
     301                 :      25068 :           out << "(:= (" << args[i][0] << " " << args[i][0].getType() << ") ";
     302                 :      25068 :           printTerm(out, args[i][1]);
     303         [ +  + ]:      25068 :           out << ")" << (i != args.size() - 1 ? " " : "");
     304                 :      25068 :           continue;
     305                 :            :         }
     306                 :      24707 :         Assert(args[i].getKind() == Kind::BOUND_VARIABLE) << args[i];
     307                 :      24707 :         out << "(" << args[i] << " " << args[i].getType() << ") ";
     308                 :            :       }
     309                 :      14220 :       out << "))" << std::endl;
     310                 :            :     }
     311                 :            :     // since the subproof shape relies on having at least one step inside it, if
     312                 :            :     // the step relative to children[0] is already d_pfMap, we should just print
     313                 :            :     // the step and be done
     314                 :      42997 :     auto it = d_pfMap.find(pfChildren[0].get());
     315         [ +  + ]:      42997 :     if (it != d_pfMap.end())
     316                 :            :     {
     317                 :         18 :       std::string childStepId = prefix + "t" + std::to_string(id) + ".t0";
     318                 :          9 :       const std::vector<Node>& childArgs = pfChildren[0]->getArguments();
     319                 :            :       const std::vector<std::shared_ptr<ProofNode>>& childPfChildren =
     320                 :          9 :           pfChildren[0]->getChildren();
     321                 :          9 :       AletheRule childArule = getAletheRule(childArgs[0]);
     322                 :          9 :       printStep(out, childStepId, childArule, childArgs, childPfChildren);
     323                 :          9 :     }
     324                 :            :     else
     325                 :            :     {
     326                 :      42988 :       size_t subproofId = 0;
     327                 :      42988 :       printInternal(out, subproofPrefix, subproofId, pfChildren[0]);
     328                 :            :     }
     329                 :      42997 :     d_context.pop();
     330         [ +  - ]:      42997 :     Trace("alethe-printer") << pop;
     331                 :      85994 :     std::string stepId = prefix + "t" + std::to_string(id++);
     332                 :      42997 :     out << "(step " << stepId << " ";
     333                 :      42997 :     printTerm(out, args[2]);
     334                 :      42997 :     out << " :rule " << arule;
     335                 :            :     // Discharge assumptions in the case of subproof
     336         [ +  + ]:      42997 :     if (arule == AletheRule::ANCHOR_SUBPROOF)
     337                 :            :     {
     338                 :      28777 :       out << " :discharge (";
     339         [ +  + ]:     191148 :       for (size_t i = 3, size = args.size(); i < size; ++i)
     340                 :            :       {
     341         [ +  + ]:     162371 :         out << dischargeIds[i - 3] << (i < args.size() - 1 ? " " : "");
     342                 :            :       }
     343                 :      28777 :       out << ")";
     344                 :            :     }
     345                 :      42997 :     out << ")" << std::endl;
     346                 :      42997 :     d_pfMap[pfn.get()] = stepId;
     347                 :      42997 :     return;
     348                 :      42997 :   }
     349                 :            :   // Print the steps for children to guarantee we will have ids for them in the
     350                 :            :   // premises of this step
     351         [ +  + ]:    5653192 :   for (const std::shared_ptr<ProofNode>& pfChild : pfChildren)
     352                 :            :   {
     353         [ +  - ]:    3577074 :     Trace("alethe-printer") << push;
     354                 :    3577074 :     printInternal(out, prefix, id, pfChild);
     355         [ +  - ]:    3577074 :     Trace("alethe-printer") << pop;
     356                 :            :   }
     357                 :            :   // Print this step
     358                 :    4152236 :   std::string stepId = prefix + "t" + std::to_string(id++);
     359                 :    2076118 :   printStep(out, stepId, arule, args, pfChildren);
     360                 :    2076118 :   d_pfMap[pfn.get()] = stepId;
     361                 :    2076118 : }
     362                 :            : 
     363                 :            : }  // namespace proof
     364                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14