LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/preprocessing - assertion_pipeline.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 134 139 96.4 %
Date: 2026-10-06 10:35:57 Functions: 17 18 94.4 %
Branches: 82 134 61.2 %

           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                 :            :  * AssertionPipeline stores a list of assertions modified by
      11                 :            :  * preprocessing passes.
      12                 :            :  */
      13                 :            : 
      14                 :            : #include "preprocessing/assertion_pipeline.h"
      15                 :            : 
      16                 :            : #include "expr/node_manager.h"
      17                 :            : #include "options/smt_options.h"
      18                 :            : #include "proof/lazy_proof.h"
      19                 :            : #include "smt/logic_exception.h"
      20                 :            : #include "smt/preprocess_proof_generator.h"
      21                 :            : #include "theory/builtin/proof_checker.h"
      22                 :            : #include "util/rational.h"
      23                 :            : 
      24                 :            : namespace cvc5::internal {
      25                 :            : namespace preprocessing {
      26                 :            : 
      27                 :      28208 : AssertionPipeline::AssertionPipeline(Env& env)
      28                 :            :     : EnvObj(env),
      29                 :      28208 :       d_storeSubstsInAsserts(false),
      30                 :      28208 :       d_pppg(nullptr),
      31                 :      28208 :       d_conflict(false),
      32                 :      28208 :       d_isRefutationUnsound(false),
      33                 :      28208 :       d_isModelUnsound(false),
      34                 :      28208 :       d_isNegated(false)
      35                 :            : {
      36                 :      28208 :   d_false = nodeManager()->mkConst(false);
      37                 :      28208 : }
      38                 :            : 
      39                 :      40239 : void AssertionPipeline::clear()
      40                 :            : {
      41                 :      40239 :   d_conflict = false;
      42                 :      40239 :   d_isRefutationUnsound = false;
      43                 :      40239 :   d_isModelUnsound = false;
      44                 :      40239 :   d_isNegated = false;
      45                 :      40239 :   d_nodes.clear();
      46                 :      40239 :   d_iteSkolemMap.clear();
      47                 :      40239 :   d_substsIndices.clear();
      48                 :      40239 : }
      49                 :            : 
      50                 :     541952 : void AssertionPipeline::push_back(
      51                 :            :     Node n, bool isInput, ProofGenerator* pgen, TrustId trustId, bool ensureRew)
      52                 :            : {
      53         [ +  + ]:     541952 :   if (d_conflict)
      54                 :            :   {
      55                 :            :     // if we are already in conflict, we skip. This is required to handle the
      56                 :            :     // case where "false" was already seen as an input assertion.
      57                 :        682 :     return;
      58                 :            :   }
      59                 :            :   // If proof enabled, notify the preprocess proof generator.
      60                 :            :   // Note that if n is (and F1 ... Fn), below we instead add the assertions
      61                 :            :   // F1 .... Fn whose proofs are AND_ELIM steps given a proof of n. We do not
      62                 :            :   // add n as an assertion. However, we also remember the proof for n itself.
      63                 :            :   // The reason is that in rare cases we may relearn n (say via rewriting
      64                 :            :   // another assumption) which may lead to a cyclic proof if that rewriting
      65                 :            :   // depended on one of F1 ... Fn.
      66         [ +  + ]:     541270 :   if (isProofEnabled())
      67                 :            :   {
      68         [ +  + ]:     301106 :     if (!isInput)
      69                 :            :     {
      70                 :            :       // notice this is always called, regardless of whether pgen is nullptr
      71                 :     214060 :       d_pppg->notifyNewAssert(n, pgen, trustId);
      72                 :            :     }
      73                 :            :     else
      74                 :            :     {
      75 [ -  + ][ -  + ]:      87046 :       Assert(pgen == nullptr);
                 [ -  - ]
      76                 :            :       // n is an input assertion, whose proof should be ASSUME.
      77                 :      87046 :       d_pppg->notifyInput(n);
      78                 :            :     }
      79                 :            :   }
      80         [ +  + ]:     541270 :   if (n == d_false)
      81                 :            :   {
      82                 :       2779 :     markConflict();
      83                 :            :   }
      84         [ +  + ]:     538491 :   else if (n.getKind() == Kind::AND)
      85                 :            :   {
      86                 :            :     // Immediately miniscope top-level AND, which is important for minimizing
      87                 :            :     // dependencies in proofs. We add each conjunct seperately, justifying
      88                 :            :     // each with an AND_ELIM step.
      89                 :      13131 :     std::vector<Node> conjs;
      90         [ +  + ]:      13131 :     if (isProofEnabled())
      91                 :            :     {
      92         [ +  + ]:       7867 :       if (!isInput)
      93                 :            :       {
      94 [ +  + ][ +  - ]:       1674 :         Assert(pgen != nullptr || trustId != TrustId::UNKNOWN_PREPROCESS_LEMMA);
         [ -  + ][ -  + ]
                 [ -  - ]
      95                 :       1674 :         d_andElimEpg->addLazyStep(n, pgen, trustId);
      96                 :            :       }
      97                 :            :     }
      98                 :      13131 :     std::vector<Node> toProcess;
      99                 :      13131 :     toProcess.emplace_back(n);
     100                 :            :     do
     101                 :            :     {
     102                 :     337688 :       Node nc = toProcess.back();
     103                 :     337688 :       toProcess.pop_back();
     104         [ +  + ]:     337688 :       if (nc.getKind() == Kind::AND)
     105                 :            :       {
     106         [ +  + ]:      54306 :         if (isProofEnabled())
     107                 :            :         {
     108                 :      24398 :           NodeManager* nm = nodeManager();
     109         [ +  + ]:     196385 :           for (size_t j = 0, nchild = nc.getNumChildren(); j < nchild; j++)
     110                 :            :           {
     111                 :     171987 :             size_t jj = (nchild - 1) - j;
     112                 :     171987 :             Node in = nm->mkConstInt(Rational(jj));
     113                 :            :             // Never overwrite here. This is because the assumption we would
     114                 :            :             // overwrite might be at a lower user context. Overwriting the
     115                 :            :             // assumption can lead to open proofs in incremental mode.
     116                 :     515961 :             d_andElimEpg->addStep(nc[jj],
     117                 :            :                                   ProofRule::AND_ELIM,
     118                 :            :                                   {nc},
     119                 :            :                                   {in},
     120                 :            :                                   false,
     121                 :            :                                   CDPOverwrite::NEVER);
     122                 :     171987 :             toProcess.emplace_back(nc[jj]);
     123                 :     171987 :           }
     124                 :            :         }
     125                 :            :         else
     126                 :            :         {
     127                 :      29908 :           toProcess.insert(toProcess.end(), nc.rbegin(), nc.rend());
     128                 :            :         }
     129                 :            :       }
     130                 :            :       else
     131                 :            :       {
     132                 :     283382 :         conjs.emplace_back(nc);
     133                 :            :       }
     134         [ +  + ]:     337688 :     } while (!toProcess.empty());
     135                 :            :     // add each conjunct
     136         [ +  + ]:     296513 :     for (const Node& nc : conjs)
     137                 :            :     {
     138         [ +  + ]:     283382 :       push_back(nc,
     139                 :            :                 false,
     140                 :     283382 :                 d_andElimEpg.get(),
     141                 :            :                 TrustId::UNKNOWN_PREPROCESS_LEMMA,
     142                 :            :                 ensureRew);
     143                 :            :     }
     144                 :      13131 :     return;
     145                 :      13131 :   }
     146                 :            :   else
     147                 :            :   {
     148                 :     525360 :     d_nodes.push_back(n);
     149         [ +  + ]:     525360 :     if (ensureRew)
     150                 :            :     {
     151                 :       7183 :       ensureRewritten(d_nodes.size() - 1);
     152                 :            :     }
     153                 :            :   }
     154         [ +  - ]:    1056278 :   Trace("assert-pipeline") << "Assertions: ...new assertion " << n
     155                 :     528139 :                            << ", isInput=" << isInput << std::endl;
     156                 :            : }
     157                 :            : 
     158                 :      32924 : void AssertionPipeline::pushBackTrusted(TrustNode trn,
     159                 :            :                                         TrustId trustId,
     160                 :            :                                         bool ensureRew)
     161                 :            : {
     162 [ -  + ][ -  + ]:      32924 :   Assert(trn.getKind() == TrustNodeKind::LEMMA);
                 [ -  - ]
     163                 :            :   // push back what was proven
     164                 :      32924 :   push_back(trn.getProven(), false, trn.getGenerator(), trustId, ensureRew);
     165                 :      32924 : }
     166                 :            : 
     167                 :    1274228 : void AssertionPipeline::replace(size_t i,
     168                 :            :                                 Node n,
     169                 :            :                                 ProofGenerator* pgen,
     170                 :            :                                 TrustId trustId)
     171                 :            : {
     172 [ -  + ][ -  + ]:    1274228 :   Assert(i < d_nodes.size());
                 [ -  - ]
     173         [ +  + ]:    1274228 :   if (n == d_nodes[i])
     174                 :            :   {
     175                 :            :     // no change, skip
     176                 :     937872 :     return;
     177                 :            :   }
     178         [ +  - ]:     672712 :   Trace("assert-pipeline") << "Assertions: Replace " << d_nodes[i] << " with "
     179                 :     336356 :                            << n << std::endl;
     180         [ +  + ]:     336356 :   if (isProofEnabled())
     181                 :            :   {
     182 [ +  + ][ +  - ]:     181571 :     Assert(pgen != nullptr || trustId != TrustId::UNKNOWN_PREPROCESS);
         [ -  + ][ -  + ]
                 [ -  - ]
     183                 :     181571 :     d_pppg->notifyPreprocessed(d_nodes[i], n, pgen, trustId);
     184                 :            :   }
     185         [ +  + ]:     336356 :   if (n == d_false)
     186                 :            :   {
     187                 :       3284 :     markConflict();
     188                 :            :   }
     189                 :            :   else
     190                 :            :   {
     191                 :     333072 :     d_nodes[i] = n;
     192                 :            :   }
     193                 :            : }
     194                 :            : 
     195                 :        460 : void AssertionPipeline::removeIteSkolem(TNode skolem)
     196                 :            : {
     197                 :        460 :   for (IteSkolemMap::iterator it = d_iteSkolemMap.begin();
     198         [ +  + ]:        470 :        it != d_iteSkolemMap.end();)
     199                 :            :   {
     200         [ +  + ]:         10 :     if (it->second == skolem)
     201                 :            :     {
     202                 :          6 :       it = d_iteSkolemMap.erase(it);
     203                 :            :     }
     204                 :            :     else
     205                 :            :     {
     206                 :          4 :       ++it;
     207                 :            :     }
     208                 :            :   }
     209                 :        460 : }
     210                 :            : 
     211                 :     620868 : void AssertionPipeline::replaceTrusted(size_t i, TrustNode trn, TrustId trustId)
     212                 :            : {
     213 [ -  + ][ -  + ]:     620868 :   Assert(i < d_nodes.size());
                 [ -  - ]
     214         [ +  + ]:     620868 :   if (trn.isNull())
     215                 :            :   {
     216                 :            :     // null trust node denotes no change, nothing to do
     217                 :     304787 :     return;
     218                 :            :   }
     219 [ -  + ][ -  + ]:     316081 :   Assert(trn.getKind() == TrustNodeKind::REWRITE);
                 [ -  - ]
     220 [ -  + ][ -  + ]:     316081 :   Assert(trn.getProven()[0] == d_nodes[i]);
                 [ -  - ]
     221                 :     316081 :   replace(i, trn.getNode(), trn.getGenerator(), trustId);
     222                 :            : }
     223                 :            : 
     224                 :      21368 : void AssertionPipeline::ensureRewritten(size_t i)
     225                 :            : {
     226 [ -  + ][ -  + ]:      21368 :   Assert(i < d_nodes.size());
                 [ -  - ]
     227         [ +  + ]:      21368 :   replace(i, rewrite(d_nodes[i]), d_rewpg.get());
     228                 :      21368 : }
     229                 :            : 
     230                 :      13902 : void AssertionPipeline::enableProofs(smt::PreprocessProofGenerator* pppg)
     231                 :            : {
     232                 :      13902 :   d_pppg = pppg;
     233         [ +  - ]:      13902 :   if (d_andElimEpg == nullptr)
     234                 :            :   {
     235                 :      27804 :     d_andElimEpg.reset(
     236                 :      27804 :         new LazyCDProof(d_env, nullptr, userContext(), "AssertionsAndElim"));
     237                 :            :   }
     238         [ +  - ]:      13902 :   if (d_rewpg == nullptr)
     239                 :            :   {
     240                 :      13902 :     d_rewpg.reset(new RewriteProofGenerator(d_env));
     241                 :            :   }
     242                 :      13902 : }
     243                 :            : 
     244                 :     945063 : bool AssertionPipeline::isProofEnabled() const { return d_pppg != nullptr; }
     245                 :            : 
     246                 :       3219 : void AssertionPipeline::enableStoreSubstsInAsserts()
     247                 :            : {
     248                 :       3219 :   d_storeSubstsInAsserts = true;
     249                 :       3219 :   d_nodes.push_back(nodeManager()->mkConst<bool>(true));
     250                 :       3219 : }
     251                 :            : 
     252                 :      27106 : void AssertionPipeline::disableStoreSubstsInAsserts()
     253                 :            : {
     254                 :      27106 :   d_storeSubstsInAsserts = false;
     255                 :      27106 : }
     256                 :            : 
     257                 :       1865 : void AssertionPipeline::addSubstitutionNode(Node n,
     258                 :            :                                             ProofGenerator* pg,
     259                 :            :                                             TrustId trustId)
     260                 :            : {
     261 [ -  + ][ -  + ]:       1865 :   Assert(d_storeSubstsInAsserts);
                 [ -  - ]
     262 [ -  + ][ -  + ]:       1865 :   Assert(n.getKind() == Kind::EQUAL);
                 [ -  - ]
     263                 :       1865 :   size_t prevNodeSize = d_nodes.size();
     264                 :            :   // ensure rewritten here
     265                 :       1865 :   push_back(n, false, pg, trustId, true);
     266                 :            :   // remember this is a substitution index
     267         [ +  + ]:       3730 :   for (size_t i = prevNodeSize, newSize = d_nodes.size(); i < newSize; i++)
     268                 :            :   {
     269                 :       1865 :     d_substsIndices.insert(i);
     270                 :            :   }
     271                 :       1865 : }
     272                 :            : 
     273                 :     917826 : bool AssertionPipeline::isSubstsIndex(size_t i) const
     274                 :            : {
     275                 :     917826 :   return d_storeSubstsInAsserts
     276 [ +  + ][ +  + ]:     917826 :          && d_substsIndices.find(i) != d_substsIndices.end();
     277                 :            : }
     278                 :            : 
     279                 :       6063 : void AssertionPipeline::markConflict()
     280                 :            : {
     281                 :       6063 :   d_conflict = true;
     282                 :       6063 :   d_nodes.clear();
     283                 :       6063 :   d_iteSkolemMap.clear();
     284                 :       6063 :   d_nodes.push_back(d_false);
     285                 :       6063 : }
     286                 :            : 
     287                 :         65 : void AssertionPipeline::markRefutationUnsound()
     288                 :            : {
     289                 :         65 :   d_isRefutationUnsound = true;
     290                 :         65 : }
     291                 :            : 
     292                 :          0 : void AssertionPipeline::markModelUnsound() { d_isModelUnsound = true; }
     293                 :            : 
     294                 :          2 : void AssertionPipeline::markNegated()
     295                 :            : {
     296 [ +  - ][ -  + ]:          2 :   if (d_isRefutationUnsound || d_isModelUnsound)
     297                 :            :   {
     298                 :            :     // disallow unintuitive uses of global negation.
     299                 :          0 :     std::stringstream ss;
     300                 :            :     ss << "Cannot negate the preprocessed assertions when already marked as "
     301                 :          0 :           "refutation or model unsound.";
     302                 :          0 :     throw LogicException(ss.str());
     303                 :          0 :   }
     304                 :          2 :   d_isNegated = true;
     305                 :          2 : }
     306                 :            : 
     307                 :            : }  // namespace preprocessing
     308                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14