LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/proof - lazy_proof_chain.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 141 200 70.5 %
Date: 2026-04-20 10:41:59 Functions: 8 12 66.7 %
Branches: 86 166 51.8 %

           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                 :            :  * Implementation of lazy proof utility.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "proof/lazy_proof_chain.h"
      14                 :            : 
      15                 :            : #include "options/proof_options.h"
      16                 :            : #include "proof/proof.h"
      17                 :            : #include "proof/proof_ensure_closed.h"
      18                 :            : #include "proof/proof_node.h"
      19                 :            : #include "proof/proof_node_algorithm.h"
      20                 :            : #include "proof/proof_node_manager.h"
      21                 :            : 
      22                 :            : namespace cvc5::internal {
      23                 :            : 
      24                 :      82059 : LazyCDProofChain::LazyCDProofChain(Env& env,
      25                 :            :                                    bool cyclic,
      26                 :            :                                    context::Context* c,
      27                 :            :                                    ProofGenerator* defGen,
      28                 :            :                                    bool defRec,
      29                 :      82059 :                                    const std::string& name)
      30                 :            :     : CDProof(env, c, name, false),
      31                 :      82059 :       d_cyclic(cyclic),
      32                 :      82059 :       d_defRec(defRec),
      33                 :      82059 :       d_context(),
      34         [ +  + ]:      82059 :       d_gens(c ? c : &d_context),
      35                 :      82059 :       d_defGen(defGen),
      36                 :     164118 :       d_name(name)
      37                 :            : {
      38                 :      82059 : }
      39                 :            : 
      40                 :     121359 : LazyCDProofChain::~LazyCDProofChain() {}
      41                 :            : 
      42                 :          0 : const std::map<Node, std::shared_ptr<ProofNode>> LazyCDProofChain::getLinks()
      43                 :            :     const
      44                 :            : {
      45                 :          0 :   std::map<Node, std::shared_ptr<ProofNode>> links;
      46         [ -  - ]:          0 :   for (const std::pair<const Node, ProofGenerator* const>& link : d_gens)
      47                 :            :   {
      48                 :          0 :     Assert(link.second);
      49                 :          0 :     std::shared_ptr<ProofNode> pfn = link.second->getProofFor(link.first);
      50                 :          0 :     Assert(pfn);
      51                 :          0 :     links[link.first] = pfn;
      52                 :          0 :   }
      53                 :          0 :   return links;
      54                 :          0 : }
      55                 :            : 
      56                 :     102100 : std::shared_ptr<ProofNode> LazyCDProofChain::getProofFor(Node fact)
      57                 :            : {
      58         [ +  - ]:     204200 :   Trace("lazy-cdproofchain")
      59                 :     102100 :       << "LazyCDProofChain::getProofFor of gen " << d_name << "\n";
      60         [ +  - ]:     204200 :   Trace("lazy-cdproofchain")
      61                 :     102100 :       << "LazyCDProofChain::getProofFor: " << fact << "\n";
      62                 :            :   // which facts have had proofs retrieved for. This is maintained to avoid
      63                 :            :   // cycles. It also saves the proof node of the fact
      64                 :     102100 :   std::unordered_map<Node, std::shared_ptr<ProofNode>> toConnect;
      65                 :            :   // leaves of proof node links in the chain, i.e. assumptions, yet to be
      66                 :            :   // expanded
      67                 :            :   std::unordered_map<Node, std::vector<std::shared_ptr<ProofNode>>>
      68                 :     102100 :       assumptionsToExpand;
      69                 :            :   // invariant of the loop below, the first iteration notwithstanding:
      70                 :            :   //   visit = domain(assumptionsToExpand) \ domain(toConnect)
      71                 :     306300 :   std::vector<Node> visit{fact};
      72                 :     102100 :   std::unordered_map<Node, bool> visited;
      73                 :     102100 :   Node cur;
      74                 :            :   do
      75                 :            :   {
      76                 :   10375817 :     cur = visit.back();
      77                 :   10375817 :     visit.pop_back();
      78                 :   10375817 :     auto itVisited = visited.find(cur);
      79                 :            :     // pre-traversal
      80         [ +  + ]:   10375817 :     if (itVisited == visited.end())
      81                 :            :     {
      82         [ +  - ]:    2825520 :       Trace("lazy-cdproofchain")
      83                 :    1412760 :           << "LazyCDProofChain::getProofFor: check " << cur << "\n";
      84 [ -  + ][ -  + ]:    1412760 :       Assert(toConnect.find(cur) == toConnect.end());
                 [ -  - ]
      85                 :            :       // The current fact may be justified by concrete steps added to this
      86                 :            :       // proof, in which case we do not use the generators.
      87                 :    1412760 :       bool rec = true;
      88                 :    1412760 :       std::shared_ptr<ProofNode> curPfn = getProofForInternal(cur, rec);
      89         [ +  + ]:    1412760 :       if (curPfn == nullptr)
      90                 :            :       {
      91         [ +  - ]:    2015194 :         Trace("lazy-cdproofchain")
      92                 :    1007597 :             << "LazyCDProofChain::getProofFor: No proof found, skip\n";
      93                 :    1007597 :         visited[cur] = true;
      94                 :    1007597 :         continue;
      95                 :            :       }
      96                 :            :       // map node whose proof node must be expanded to the respective poof
      97                 :            :       // node
      98                 :     405163 :       toConnect[cur] = curPfn;
      99                 :            :       // We may not want to recursively connect this proof so we skip.
     100         [ +  + ]:     405163 :       if (!rec)
     101                 :            :       {
     102                 :      58794 :         visited[cur] = true;
     103                 :      58794 :         continue;
     104                 :            :       }
     105         [ +  - ]:     692738 :       Trace("lazy-cdproofchain-debug")
     106                 :          0 :           << "LazyCDProofChain::getProofFor: stored proof: " << *curPfn.get()
     107                 :     346369 :           << "\n";
     108                 :            :       // retrieve free assumptions and their respective proof nodes
     109                 :     346369 :       std::map<Node, std::vector<std::shared_ptr<ProofNode>>> famap;
     110                 :     346369 :       expr::getFreeAssumptionsMap(curPfn, famap);
     111         [ +  - ]:     346369 :       if (d_cyclic)
     112                 :            :       {
     113                 :            :         // First check for a trivial cycle, which is when cur is a free
     114                 :            :         // assumption of curPfn. Note that in the special case in which curPfn
     115                 :            :         // has cur as an assumption and cur is actually the initial fact that
     116                 :            :         // getProofFor is called on, the general cycle detection below would
     117                 :            :         // prevent this method from generating a proof for cur, which would be
     118                 :            :         // wrong since there is a justification for it in curPfn.
     119                 :     346369 :         bool isCyclic = false;
     120         [ +  + ]:   10323436 :         for (const auto& fap : famap)
     121                 :            :         {
     122         [ +  + ]:   10026738 :           if (cur == fap.first)
     123                 :            :           {
     124                 :            :             // connect it to one of the assumption proof nodes
     125                 :      49671 :             toConnect[cur] = fap.second[0];
     126                 :      49671 :             isCyclic = true;
     127                 :      49671 :             break;
     128                 :            :           }
     129                 :            :         }
     130         [ +  + ]:     346369 :         if (isCyclic)
     131                 :            :         {
     132         [ +  - ]:      99342 :           Trace("lazy-cdproofchain")
     133                 :          0 :               << "LazyCDProofChain::getProofFor: trivial cycle detected for "
     134                 :      49671 :               << cur << ", abort\n";
     135                 :      49671 :           visited[cur] = true;
     136                 :      49671 :           continue;
     137                 :            :         }
     138         [ -  + ]:     296698 :         if (TraceIsOn("lazy-cdproofchain"))
     139                 :            :         {
     140                 :          0 :           unsigned alreadyToVisit = 0;
     141         [ -  - ]:          0 :           Trace("lazy-cdproofchain")
     142                 :          0 :               << "LazyCDProofChain::getProofFor: " << famap.size()
     143                 :          0 :               << " free assumptions:\n";
     144         [ -  - ]:          0 :           for (auto fap : famap)
     145                 :            :           {
     146         [ -  - ]:          0 :             Trace("lazy-cdproofchain")
     147                 :          0 :                 << "LazyCDProofChain::getProofFor:  - " << fap.first << "\n";
     148                 :          0 :             alreadyToVisit +=
     149                 :          0 :                 std::find(visit.begin(), visit.end(), fap.first) != visit.end()
     150         [ -  - ]:          0 :                     ? 1
     151                 :            :                     : 0;
     152                 :          0 :           }
     153         [ -  - ]:          0 :           Trace("lazy-cdproofchain")
     154                 :          0 :               << "LazyCDProofChain::getProofFor: " << alreadyToVisit
     155                 :          0 :               << " already to visit\n";
     156                 :            :         }
     157                 :            :         // If we are controlling cycle, check whether any of the assumptions of
     158                 :            :         // cur would provoke a cycle. In such we remove cur from toConnect and
     159                 :            :         // do not proceed to expand any of its assumptions.
     160         [ +  - ]:     593396 :         Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: marking "
     161                 :     296698 :                                    << cur << " for cycle check\n";
     162                 :            :         // enqueue free assumptions to process
     163         [ +  + ]:   10273729 :         for (const auto& fap : famap)
     164                 :            :         {
     165                 :            :           // A cycle is characterized by cur having an assumption being
     166                 :            :           // *currently* expanded that is seen again, i.e. in toConnect and not
     167                 :            :           // yet post-visited
     168                 :    9977043 :           auto itToConnect = toConnect.find(fap.first);
     169 [ +  + ][ +  + ]:    9977043 :           if (itToConnect != toConnect.end() && !visited[fap.first])
                 [ +  + ]
     170                 :            :           {
     171                 :            :             // Since we have a cycle with an assumption, we must:
     172                 :            :             // - remove that assumption from toConnect, since this class cannot
     173                 :            :             //   generate a well-formed proof to it.
     174                 :            :             // - remove curr from toConnect, since its proof will no longer be
     175                 :            :             //   needed. Note that nothing prevents cur from appearing as a
     176                 :            :             //   dependency in another chain and being then expanded,
     177                 :            :             //   podentially without a cycle.
     178                 :         12 :             isCyclic = true;
     179         [ +  - ]:         24 :             Trace("lazy-cdproofchain")
     180                 :          0 :                 << "LazyCDProofChain::getProofFor: cyclic assumption "
     181                 :         12 :                 << fap.first << "; removing it from toConnect\n";
     182                 :         12 :             toConnect.erase(itToConnect);
     183                 :         12 :             break;
     184                 :            :           }
     185                 :            :         }
     186         [ +  + ]:     296698 :         if (isCyclic)
     187                 :            :         {
     188         [ +  - ]:         24 :           Trace("lazy-cdproofchain")
     189                 :          0 :               << "LazyCDProofChain::getProofFor: Removing " << cur
     190                 :         12 :               << " from toConnect\n";
     191                 :         12 :           toConnect.erase(toConnect.find(cur));
     192                 :         12 :           continue;
     193                 :            :         }
     194                 :     296686 :         visit.push_back(cur);
     195                 :     296686 :         visited[cur] = false;
     196                 :            :       }
     197                 :            :       else
     198                 :            :       {
     199                 :          0 :         visited[cur] = true;
     200                 :            :       }
     201                 :            :       // enqueue free assumptions to process
     202         [ +  + ]:   10273717 :       for (const auto& fap : famap)
     203                 :            :       {
     204         [ +  - ]:   19954062 :         Trace("lazy-cdproofchain")
     205                 :          0 :             << "LazyCDProofChain::getProofFor: marking " << fap.first
     206                 :          0 :             << " for revisit and for expansion (curr: "
     207                 :          0 :             << assumptionsToExpand[fap.first].size()
     208                 :    9977031 :             << ", adding: " << fap.second.size() << ")\n";
     209                 :            :         // We always add assumptions to visit so that their last seen occurrence
     210                 :            :         // is expanded (rather than the first seen occurrence, if we were not
     211                 :            :         // adding assumptions, say, in assumptionsToExpand). This is so because
     212                 :            :         // in the case where we are checking cycles this is necessary (and
     213                 :            :         // harmless when we are not). For example the cycle
     214                 :            :         //
     215                 :            :         //                 a2
     216                 :            :         //                ...
     217                 :            :         //               ----
     218                 :            :         //                a1
     219                 :            :         //               ...
     220                 :            :         //             --------
     221                 :            :         //   a0   a1    a2
     222                 :            :         //       ...
     223                 :            :         //  ---------------
     224                 :            :         //       n
     225                 :            :         //
     226                 :            :         // in which a2 has a1 as an assumption, which has a2 an assumption,
     227                 :            :         // would not be captured if we did not revisit a1, which is an
     228                 :            :         // assumption of n and this already occur in assumptionsToExpand when
     229                 :            :         // it shows up again as an assumption of a2.
     230                 :    9977031 :         visit.push_back(fap.first);
     231                 :            :         // add assumption proof nodes to be updated
     232                 :   19954062 :         assumptionsToExpand[fap.first].insert(
     233                 :   19954062 :             assumptionsToExpand[fap.first].end(),
     234                 :            :             fap.second.begin(),
     235                 :            :             fap.second.end());
     236                 :            :       }
     237         [ +  - ]:     296686 :       if (d_cyclic)
     238                 :            :       {
     239         [ +  - ]:     296686 :         Trace("lazy-cdproofchain") << push;
     240         [ +  - ]:     296686 :         Trace("lazy-cdproofchain-debug") << push;
     241                 :            :       }
     242 [ +  + ][ +  + ]:    1462443 :     }
     243         [ +  + ]:    8963057 :     else if (!itVisited->second)
     244                 :            :     {
     245                 :     296686 :       visited[cur] = true;
     246         [ +  - ]:     296686 :       Trace("lazy-cdproofchain") << pop;
     247         [ +  - ]:     296686 :       Trace("lazy-cdproofchain-debug") << pop;
     248         [ +  - ]:     593372 :       Trace("lazy-cdproofchain")
     249                 :     296686 :           << "LazyCDProofChain::getProofFor: post-visited " << cur << "\n";
     250                 :            :     }
     251                 :            :     else
     252                 :            :     {
     253         [ +  - ]:   17332742 :       Trace("lazy-cdproofchain")
     254                 :          0 :           << "LazyCDProofChain::getProofFor: already fully processed " << cur
     255                 :    8666371 :           << "\n";
     256                 :            :     }
     257         [ +  + ]:   10375817 :   } while (!visit.empty());
     258                 :     102100 :   ProofNodeManager* pnm = getManager();
     259                 :            :   // expand all assumptions marked to be connected
     260                 :     102100 :   for (const std::pair<const Node, std::shared_ptr<ProofNode>>& npfn :
     261         [ +  + ]:     609339 :        toConnect)
     262                 :            :   {
     263                 :     405139 :     auto it = assumptionsToExpand.find(npfn.first);
     264         [ +  + ]:     405139 :     if (it == assumptionsToExpand.end())
     265                 :            :     {
     266 [ -  + ][ -  + ]:      99028 :       Assert(npfn.first == fact);
                 [ -  - ]
     267                 :      99028 :       continue;
     268                 :            :     }
     269 [ -  + ][ -  + ]:     306111 :     Assert(npfn.second);
                 [ -  - ]
     270         [ +  - ]:     612222 :     Trace("lazy-cdproofchain")
     271                 :          0 :         << "LazyCDProofChain::getProofFor: expand assumption " << npfn.first
     272                 :     306111 :         << "\n";
     273         [ +  - ]:     612222 :     Trace("lazy-cdproofchain-debug")
     274                 :          0 :         << "LazyCDProofChain::getProofFor: ...expand to " << *npfn.second.get()
     275                 :     306111 :         << "\n";
     276                 :            :     // update each assumption proof node
     277         [ +  + ]:    1672924 :     for (std::shared_ptr<ProofNode> pfn : it->second)
     278                 :            :     {
     279                 :    1366813 :       pnm->updateNode(pfn.get(), npfn.second.get());
     280                 :    1366813 :     }
     281                 :            :   }
     282         [ +  - ]:     102100 :   Trace("lazy-cdproofchain") << "===========\n";
     283                 :            :   // final proof of fact
     284                 :     102100 :   auto it = toConnect.find(fact);
     285         [ +  + ]:     102100 :   if (it == toConnect.end())
     286                 :            :   {
     287                 :       3072 :     return nullptr;
     288                 :            :   }
     289                 :      99028 :   return it->second;
     290                 :     102100 : }
     291                 :            : 
     292                 :     448655 : void LazyCDProofChain::addLazyStep(Node expected, ProofGenerator* pg)
     293                 :            : {
     294 [ -  + ][ -  + ]:     448655 :   Assert(pg != nullptr);
                 [ -  - ]
     295         [ +  - ]:     897310 :   Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected
     296 [ -  + ][ -  - ]:     448655 :                              << " set to generator " << pg->identify() << "\n";
     297                 :            :   // note this will replace the generator for expected, if any
     298                 :     448655 :   d_gens.insert(expected, pg);
     299                 :     448655 : }
     300                 :            : 
     301                 :          0 : void LazyCDProofChain::addLazyStep(Node expected,
     302                 :            :                                    ProofGenerator* pg,
     303                 :            :                                    const std::vector<Node>& assumptions,
     304                 :            :                                    const char* ctx)
     305                 :            : {
     306                 :          0 :   Assert(pg != nullptr);
     307         [ -  - ]:          0 :   Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected
     308                 :          0 :                              << " set to generator " << pg->identify() << "\n";
     309                 :            :   // note this will rewrite the generator for expected, if any
     310                 :          0 :   d_gens.insert(expected, pg);
     311                 :            :   // check if chain is closed if eager checking is on
     312         [ -  - ]:          0 :   if (options().proof.proofCheck == options::ProofCheckMode::EAGER)
     313                 :            :   {
     314         [ -  - ]:          0 :     Trace("lazy-cdproofchain")
     315                 :          0 :         << "LazyCDProofChain::addLazyStep: Checking closed proof...\n";
     316                 :          0 :     std::shared_ptr<ProofNode> pfn = pg->getProofFor(expected);
     317                 :          0 :     std::vector<Node> allowedLeaves{assumptions.begin(), assumptions.end()};
     318                 :            :     // add all current links in the chain
     319         [ -  - ]:          0 :     for (const std::pair<const Node, ProofGenerator* const>& link : d_gens)
     320                 :            :     {
     321                 :          0 :       allowedLeaves.push_back(link.first);
     322                 :            :     }
     323         [ -  - ]:          0 :     if (TraceIsOn("lazy-cdproofchain"))
     324                 :            :     {
     325         [ -  - ]:          0 :       Trace("lazy-cdproofchain") << "Checking relative to leaves...\n";
     326         [ -  - ]:          0 :       for (const Node& n : allowedLeaves)
     327                 :            :       {
     328         [ -  - ]:          0 :         Trace("lazy-cdproofchain") << "  - " << n << "\n";
     329                 :            :       }
     330         [ -  - ]:          0 :       Trace("lazy-cdproofchain") << "\n";
     331                 :            :     }
     332                 :          0 :     pfnEnsureClosedWrt(
     333                 :            :         options(), pfn.get(), allowedLeaves, "lazy-cdproofchain", ctx);
     334                 :          0 :   }
     335                 :          0 : }
     336                 :            : 
     337                 :     209907 : bool LazyCDProofChain::hasGenerator(Node fact) const
     338                 :            : {
     339                 :     209907 :   return d_gens.find(fact) != d_gens.end();
     340                 :            : }
     341                 :            : 
     342                 :          0 : ProofGenerator* LazyCDProofChain::getGeneratorFor(Node fact)
     343                 :            : {
     344                 :          0 :   bool rec = true;
     345                 :          0 :   return getGeneratorForInternal(fact, rec);
     346                 :            : }
     347                 :            : 
     348                 :    1412737 : ProofGenerator* LazyCDProofChain::getGeneratorForInternal(Node fact, bool& rec)
     349                 :            : {
     350                 :    1412737 :   auto it = d_gens.find(fact);
     351         [ +  + ]:    1412737 :   if (it != d_gens.end())
     352                 :            :   {
     353                 :     298482 :     return (*it).second;
     354                 :            :   }
     355                 :            :   // otherwise, if no explicit generators, we use the default one
     356         [ +  + ]:    1114255 :   if (d_defGen != nullptr)
     357                 :            :   {
     358                 :     116037 :     rec = d_defRec;
     359                 :     116037 :     return d_defGen;
     360                 :            :   }
     361                 :     998218 :   return nullptr;
     362                 :            : }
     363                 :            : 
     364                 :    1412760 : std::shared_ptr<ProofNode> LazyCDProofChain::getProofForInternal(Node fact,
     365                 :            :                                                                  bool& rec)
     366                 :            : {
     367                 :    1412760 :   std::shared_ptr<ProofNode> pfn = CDProof::getProofFor(fact);
     368 [ -  + ][ -  + ]:    1412760 :   Assert(pfn != nullptr);
                 [ -  - ]
     369                 :            :   // If concrete proof, save it, otherwise try generators.
     370         [ +  + ]:    1412760 :   if (pfn->getRule() != ProofRule::ASSUME)
     371                 :            :   {
     372                 :         23 :     return pfn;
     373                 :            :   }
     374                 :    1412737 :   ProofGenerator* pg = getGeneratorForInternal(fact, rec);
     375         [ +  + ]:    1412737 :   if (!pg)
     376                 :            :   {
     377                 :     998218 :     return nullptr;
     378                 :            :   }
     379         [ +  - ]:     829038 :   Trace("lazy-cdproofchain")
     380 [ -  + ][ -  - ]:     414519 :       << "LazyCDProofChain::getProofFor: Call generator " << pg->identify()
     381                 :     414519 :       << " for chain link " << fact << "\n";
     382                 :     414519 :   return pg->getProofFor(fact);
     383                 :    1412760 : }
     384                 :            : 
     385                 :          0 : std::string LazyCDProofChain::identify() const { return d_name; }
     386                 :            : 
     387                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14