LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/proof/lfsc - lfsc_post_processor.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 257 279 92.1 %
Date: 2026-04-16 10:42:20 Functions: 10 11 90.9 %
Branches: 142 234 60.7 %

           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 the Lfsc post processor
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "proof/lfsc/lfsc_post_processor.h"
      14                 :            : 
      15                 :            : #include "options/proof_options.h"
      16                 :            : #include "proof/lazy_proof.h"
      17                 :            : #include "proof/lfsc/lfsc_printer.h"
      18                 :            : #include "proof/proof_checker.h"
      19                 :            : #include "proof/proof_node_algorithm.h"
      20                 :            : #include "proof/proof_node_manager.h"
      21                 :            : #include "proof/proof_node_updater.h"
      22                 :            : #include "rewriter/rewrites.h"
      23                 :            : #include "smt/env.h"
      24                 :            : #include "theory/strings/theory_strings_utils.h"
      25                 :            : 
      26                 :            : using namespace cvc5::internal::kind;
      27                 :            : 
      28                 :            : namespace cvc5::internal {
      29                 :            : namespace proof {
      30                 :            : 
      31                 :       1722 : LfscProofPostprocessCallback::LfscProofPostprocessCallback(
      32                 :       1722 :     Env& env, LfscNodeConverter& ltp)
      33                 :            :     : EnvObj(env),
      34                 :       1722 :       d_pc(env.getProofNodeManager()->getChecker()),
      35                 :       1722 :       d_tproc(ltp),
      36                 :       3444 :       d_numIgnoredScopes(0)
      37                 :            : {
      38                 :       1722 : }
      39                 :            : 
      40                 :       1722 : void LfscProofPostprocessCallback::initializeUpdate()
      41                 :            : {
      42                 :       1722 :   d_numIgnoredScopes = 0;
      43                 :       1722 : }
      44                 :            : 
      45                 :    3772437 : bool LfscProofPostprocessCallback::shouldUpdate(
      46                 :            :     std::shared_ptr<ProofNode> pn,
      47                 :            :     CVC5_UNUSED const std::vector<Node>& fa,
      48                 :            :     CVC5_UNUSED bool& continueUpdate)
      49                 :            : {
      50                 :    3772437 :   return pn->getRule() != ProofRule::LFSC_RULE;
      51                 :            : }
      52                 :            : 
      53                 :    2262012 : bool LfscProofPostprocessCallback::update(Node res,
      54                 :            :                                           ProofRule id,
      55                 :            :                                           const std::vector<Node>& children,
      56                 :            :                                           const std::vector<Node>& args,
      57                 :            :                                           CDProof* cdp,
      58                 :            :                                           CVC5_UNUSED bool& continueUpdate)
      59                 :            : {
      60         [ +  - ]:    4524024 :   Trace("lfsc-pp") << "LfscProofPostprocessCallback::update: " << id
      61                 :    2262012 :                    << std::endl;
      62         [ +  - ]:    2262012 :   Trace("lfsc-pp-debug") << "...proves " << res << std::endl;
      63                 :    2262012 :   NodeManager* nm = nodeManager();
      64 [ -  + ][ -  + ]:    2262012 :   Assert(id != ProofRule::LFSC_RULE);
                 [ -  - ]
      65                 :            : 
      66 [ +  + ][ +  + ]:    2262012 :   switch (id)
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
      67                 :            :   {
      68                 :     142377 :     case ProofRule::ASSUME:
      69                 :            :     {
      70         [ +  + ]:     142377 :       if (d_defs.find(res) != d_defs.cend())
      71                 :            :       {
      72                 :        301 :         addLfscRule(cdp, res, children, LfscRule::DEFINITION, args);
      73                 :        301 :         return true;
      74                 :            :       }
      75                 :     142076 :       return false;
      76                 :            :     }
      77                 :            :     break;
      78                 :      45804 :     case ProofRule::SCOPE:
      79                 :            :     {
      80                 :            :       // On the first two calls to update, the proof node is the outermost
      81                 :            :       // scopes of the proof. These scopes should not be printed in the LFSC
      82                 :            :       // proof. Instead, the LFSC proof printer will print the proper scopes
      83                 :            :       // around the proof, which e.g. involves an LFSC "check" command.
      84         [ +  + ]:      45804 :       if (d_numIgnoredScopes < 2)
      85                 :            :       {
      86                 :            :         // The arguments of the outer scope are definitions.
      87         [ +  + ]:       3444 :         if (d_numIgnoredScopes == 0)
      88                 :            :         {
      89         [ +  + ]:       2280 :           for (const Node& arg : args)
      90                 :            :           {
      91                 :        558 :             d_defs.insert(arg);
      92                 :            :             // Notes:
      93                 :            :             // - Some declarations only appear inside definitions and don't show
      94                 :            :             // up in assertions. To ensure that those declarations are printed,
      95                 :            :             // we need to process the definitions.
      96                 :            :             // - We process the definitions here before the rest of the proof to
      97                 :            :             // keep the indices of bound variables consistant between different
      98                 :            :             // queries that share the same definitions (e.g., incremental mode).
      99                 :            :             // Otherwise, bound variables will be assigned indices according to
     100                 :            :             // the order in which they appear in the proof.
     101                 :        558 :             d_tproc.convert(arg);
     102                 :            :           }
     103                 :            :         }
     104                 :       3444 :         d_numIgnoredScopes++;
     105                 :            :         // Note that we do not want to modify the top-most SCOPEs.
     106                 :       3444 :         return false;
     107                 :            :       }
     108 [ -  + ][ -  + ]:      42360 :       Assert(children.size() == 1);
                 [ -  - ]
     109                 :            :       // (SCOPE P :args (F1 ... Fn))
     110                 :            :       // becomes
     111                 :            :       // (scope _ _ (\ X1 ... (scope _ _ (\ Xn P)) ... ))
     112                 :      42360 :       Node curr = children[0];
     113         [ +  + ]:     249575 :       for (size_t i = 0, nargs = args.size(); i < nargs; i++)
     114                 :            :       {
     115                 :     207215 :         size_t ii = (nargs - 1) - i;
     116                 :            :         // Use a dummy conclusion for what LAMBDA proves, since there is no
     117                 :            :         // FOL representation for its type.
     118                 :     207215 :         Node fconc = mkDummyPredicate(nm);
     119                 :     621645 :         addLfscRule(cdp, fconc, {curr}, LfscRule::LAMBDA, {args[ii]});
     120                 :            :         // we use a chained implication (=> F1 ... (=> Fn C)) which avoids
     121                 :            :         // aliasing.
     122                 :     414430 :         Node next = nm->mkNode(Kind::OR, args[ii].notNode(), curr);
     123                 :     621645 :         addLfscRule(cdp, next, {fconc}, LfscRule::SCOPE, {args[ii]});
     124                 :     207215 :         curr = next;
     125                 :     207215 :       }
     126                 :            :       // In LFSC, we have now proved:
     127                 :            :       //  (or (not F1) (or (not F2) ... (or (not Fn) C) ... ))
     128                 :            :       // We now must convert this to one of two cases
     129         [ +  + ]:      42360 :       if (res.getKind() == Kind::NOT)
     130                 :            :       {
     131                 :            :         // we have C = false,
     132                 :            :         // convert to (not (and F1 (and F2 ... (and Fn true) ... )))
     133                 :            :         // this also handles the case where the conclusion is simply F1,
     134                 :            :         // when n=1.
     135                 :      18534 :         addLfscRule(cdp, res, {curr}, LfscRule::NOT_AND_REV, {});
     136                 :            :       }
     137                 :            :       else
     138                 :            :       {
     139                 :            :         // we have that C != false
     140                 :            :         // convert to (=> (and F1 (and F2 ... (and Fn true) ... )) C)
     141                 :      99279 :         addLfscRule(cdp, res, {curr}, LfscRule::PROCESS_SCOPE, {children[0]});
     142                 :            :       }
     143                 :      42360 :     }
     144                 :      42360 :     break;
     145                 :      98035 :     case ProofRule::CHAIN_RESOLUTION:
     146                 :            :     {
     147                 :            :       // turn into binary resolution
     148                 :      98035 :       Node cur = children[0];
     149         [ +  + ]:     564644 :       for (size_t i = 1, size = children.size(); i < size; i++)
     150                 :            :       {
     151                 :    1866436 :         std::vector<Node> newChildren{cur, children[i]};
     152                 :    1866436 :         std::vector<Node> newArgs{args[0][i - 1], args[1][i - 1]};
     153                 :     466609 :         cur = d_pc->checkDebug(ProofRule::RESOLUTION, newChildren, newArgs);
     154                 :     466609 :         cdp->addStep(cur, ProofRule::RESOLUTION, newChildren, newArgs);
     155                 :     466609 :       }
     156                 :      98035 :     }
     157                 :      98035 :     break;
     158                 :      55969 :     case ProofRule::SYMM:
     159                 :            :     {
     160         [ +  + ]:      55969 :       if (res.getKind() != Kind::NOT)
     161                 :            :       {
     162                 :            :         // no need to convert (positive) equality symmetry
     163                 :      55589 :         return false;
     164                 :            :       }
     165                 :            :       // must use alternate SYMM rule for disequality
     166                 :        760 :       addLfscRule(cdp, res, {children[0]}, LfscRule::NEG_SYMM, {});
     167                 :            :     }
     168                 :        380 :     break;
     169                 :     234519 :     case ProofRule::TRANS:
     170                 :            :     {
     171         [ +  + ]:     234519 :       if (children.size() <= 2)
     172                 :            :       {
     173                 :            :         // no need to change
     174                 :     212835 :         return false;
     175                 :            :       }
     176                 :            :       // turn into binary
     177                 :      21684 :       Node cur = children[0];
     178                 :      21684 :       std::unordered_set<Node> processed;
     179                 :      21684 :       processed.insert(children.begin(), children.end());
     180         [ +  + ]:      92088 :       for (size_t i = 1, size = children.size(); i < size; i++)
     181                 :            :       {
     182                 :     281616 :         std::vector<Node> newChildren{cur, children[i]};
     183                 :      70404 :         cur = d_pc->checkDebug(ProofRule::TRANS, newChildren, {});
     184         [ +  + ]:      70404 :         if (processed.find(cur) != processed.end())
     185                 :            :         {
     186                 :         14 :           continue;
     187                 :            :         }
     188                 :      70390 :         processed.insert(cur);
     189                 :      70390 :         cdp->addStep(cur, ProofRule::TRANS, newChildren, {});
     190         [ +  + ]:      70404 :       }
     191                 :      21684 :     }
     192                 :      21684 :     break;
     193                 :     258867 :     case ProofRule::CONG:
     194                 :            :     case ProofRule::NARY_CONG:
     195                 :            :     {
     196 [ -  + ][ -  + ]:     258867 :       Assert(res.getKind() == Kind::EQUAL);
                 [ -  - ]
     197 [ -  + ][ -  + ]:     258867 :       Assert(res[0].getOperator() == res[1].getOperator());
                 [ -  - ]
     198         [ +  - ]:     517734 :       Trace("lfsc-pp-cong") << "Processing congruence for " << res << " "
     199 [ -  + ][ -  - ]:     258867 :                             << res[0].getKind() << std::endl;
     200                 :            :       // different for closures
     201         [ +  + ]:     258867 :       if (res[0].isClosure())
     202                 :            :       {
     203         [ -  + ]:       4204 :         if (res[0][0] != res[1][0])
     204                 :            :         {
     205                 :            :           // cannot convert congruence with different variables currently
     206                 :          0 :           return false;
     207                 :            :         }
     208                 :       4204 :         Node cop = d_tproc.getOperatorOfClosure(res[0]);
     209                 :       4204 :         Node pcop = d_tproc.getOperatorOfClosure(res[0], false, true);
     210         [ +  - ]:       4204 :         Trace("lfsc-pp-qcong") << "Operator for closure " << cop << std::endl;
     211                 :            :         // start with base case body = body'
     212                 :       4204 :         Node curL = children[0][0];
     213                 :       4204 :         Node curR = children[0][1];
     214                 :       4204 :         Node currEq = children[0];
     215         [ +  - ]:       4204 :         Trace("lfsc-pp-qcong") << "Base congruence " << currEq << std::endl;
     216         [ +  + ]:      12776 :         for (size_t i = 0, nvars = res[0][0].getNumChildren(); i < nvars; i++)
     217                 :            :         {
     218                 :       8572 :           size_t ii = (nvars - 1) - i;
     219         [ +  - ]:       8572 :           Trace("lfsc-pp-qcong") << "Process child " << i << std::endl;
     220                 :            :           // CONG rules for each variable
     221                 :      17144 :           Node v = res[0][0][ii];
     222                 :            :           // Use partial version for each argument except the last one. This
     223                 :            :           // avoids type errors in internal representation of LFSC terms.
     224         [ +  + ]:      17144 :           Node vop = d_tproc.getOperatorOfBoundVar(ii == 0 ? cop : pcop, v);
     225                 :       8572 :           Node vopEq = vop.eqNode(vop);
     226                 :      17144 :           cdp->addStep(vopEq, ProofRule::REFL, {}, {vop});
     227                 :       8572 :           Node nextEq;
     228         [ +  + ]:       8572 :           if (i + 1 == nvars)
     229                 :            :           {
     230                 :            :             // if we are at the end, we prove the final equality
     231                 :       4204 :             nextEq = res;
     232                 :            :           }
     233                 :            :           else
     234                 :            :           {
     235                 :       4368 :             curL = nm->mkNode(Kind::HO_APPLY, vop, curL);
     236                 :       4368 :             curR = nm->mkNode(Kind::HO_APPLY, vop, curR);
     237                 :       4368 :             nextEq = curL.eqNode(curR);
     238                 :            :           }
     239 [ +  + ][ -  - ]:      25716 :           addLfscRule(cdp, nextEq, {vopEq, currEq}, LfscRule::CONG, {});
     240                 :       8572 :           currEq = nextEq;
     241                 :       8572 :         }
     242                 :       4204 :         return true;
     243                 :       4204 :       }
     244                 :     254663 :       Kind k = res[0].getKind();
     245         [ -  + ]:     254663 :       if (k == Kind::HO_APPLY)
     246                 :            :       {
     247                 :            :         // HO_APPLY congruence is a single application of LFSC congruence
     248                 :          0 :         addLfscRule(cdp, res, children, LfscRule::CONG, {});
     249                 :          0 :         return true;
     250                 :            :       }
     251                 :            :       // We are proving f(t1, ..., tn) = f(s1, ..., sn), nested.
     252                 :            :       // First, get the operator, which will be used for printing the base
     253                 :            :       // REFL step. Notice this may be for interpreted or uninterpreted
     254                 :            :       // function symbols.
     255                 :     254663 :       Node op = d_tproc.getOperatorOfTerm(res[0]);
     256         [ +  - ]:     509326 :       Trace("lfsc-pp-cong") << "Processing cong for op " << op << " "
     257 [ -  + ][ -  - ]:     254663 :                             << op.getType() << std::endl;
     258 [ -  + ][ -  + ]:     254663 :       Assert(!op.isNull());
                 [ -  - ]
     259                 :            :       // initial base step is REFL
     260                 :     254663 :       Node opEq = op.eqNode(op);
     261                 :     509326 :       cdp->addStep(opEq, ProofRule::REFL, {}, {op});
     262                 :     254663 :       size_t nchildren = children.size();
     263                 :     509326 :       Node nullTerm = d_tproc.getNullTerminator(nm, k, res[0].getType());
     264                 :            :       // Are we doing congruence of an n-ary operator? If so, notice that op
     265                 :            :       // is a binary operator and we must apply congruence in a special way.
     266                 :            :       // Note we use the first block of code if we have more than 2 children,
     267                 :            :       // or if we have a null terminator.
     268                 :            :       // special case: constructors and apply uf are not treated as n-ary; these
     269                 :            :       // symbols have function types that expect n arguments.
     270         [ +  + ]:     399169 :       bool isNary = NodeManager::isNAryKind(k) && k != Kind::APPLY_CONSTRUCTOR
     271 [ +  + ][ +  + ]:     399169 :                     && k != Kind::APPLY_UF;
     272 [ +  + ][ +  + ]:     254663 :       if (isNary && (nchildren > 2 || !nullTerm.isNull()))
         [ +  + ][ +  + ]
     273                 :            :       {
     274                 :            :         // get the null terminator for the kind, which may mean we are doing
     275                 :            :         // a special kind of congruence for n-ary kinds whose base is a REFL
     276                 :            :         // step for the null terminator.
     277                 :      79227 :         Node currEq;
     278         [ +  - ]:      79227 :         if (!nullTerm.isNull())
     279                 :            :         {
     280                 :      79227 :           currEq = nullTerm.eqNode(nullTerm);
     281                 :            :           // if we have a null terminator, we do a final REFL step to add
     282                 :            :           // the null terminator to both sides.
     283                 :     158454 :           cdp->addStep(currEq, ProofRule::REFL, {}, {nullTerm});
     284                 :            :         }
     285                 :            :         else
     286                 :            :         {
     287                 :            :           // Otherwise, start with the last argument.
     288                 :          0 :           currEq = children[nchildren - 1];
     289                 :            :         }
     290         [ +  + ]:     384246 :         for (size_t i = 0; i < nchildren; i++)
     291                 :            :         {
     292                 :     305019 :           size_t ii = (nchildren - 1) - i;
     293         [ +  - ]:     305019 :           Trace("lfsc-pp-cong") << "Process child " << ii << std::endl;
     294                 :     305019 :           Node uop = op;
     295                 :            :           // special case: applications of the following kinds in the chain may
     296                 :            :           // have a different type, so remake the operator here.
     297 [ +  + ][ +  + ]:     305019 :           if (k == Kind::BITVECTOR_CONCAT || k == Kind::ADD || k == Kind::MULT
                 [ +  + ]
     298         [ +  + ]:     235345 :               || k == Kind::NONLINEAR_MULT)
     299                 :            :           {
     300                 :            :             // we get the operator of the next argument concatenated with the
     301                 :            :             // current accumulated remainder.
     302                 :     140522 :             Node currApp = nm->mkNode(k, children[ii][0], currEq[0]);
     303                 :      70261 :             uop = d_tproc.getOperatorOfTerm(currApp);
     304                 :      70261 :           }
     305 [ +  - ][ -  - ]:     610038 :           Trace("lfsc-pp-cong") << "Apply " << uop << " to " << children[ii][0]
     306 [ -  + ][ -  + ]:     305019 :                                 << " and " << children[ii][1] << std::endl;
                 [ -  - ]
     307                 :            :           Node argAppEq =
     308                 :     610038 :               nm->mkNode(Kind::HO_APPLY, uop, children[ii][0])
     309                 :     610038 :                   .eqNode(nm->mkNode(Kind::HO_APPLY, uop, children[ii][1]));
     310 [ +  + ][ -  - ]:     915057 :           addLfscRule(cdp, argAppEq, {opEq, children[ii]}, LfscRule::CONG, {});
     311                 :            :           // now, congruence to the current equality
     312                 :     305019 :           Node nextEq;
     313         [ +  + ]:     305019 :           if (ii == 0)
     314                 :            :           {
     315                 :            :             // use final conclusion
     316                 :      79227 :             nextEq = res;
     317                 :            :           }
     318                 :            :           else
     319                 :            :           {
     320                 :            :             // otherwise continue to apply
     321                 :            :             nextEq =
     322                 :     451584 :                 nm->mkNode(Kind::HO_APPLY, argAppEq[0], currEq[0])
     323                 :     225792 :                     .eqNode(nm->mkNode(Kind::HO_APPLY, argAppEq[1], currEq[1]));
     324                 :            :           }
     325 [ +  + ][ -  - ]:     915057 :           addLfscRule(cdp, nextEq, {argAppEq, currEq}, LfscRule::CONG, {});
     326                 :     305019 :           currEq = nextEq;
     327                 :     305019 :         }
     328                 :      79227 :       }
     329                 :            :       else
     330                 :            :       {
     331                 :            :         // non n-ary kinds do not have null terminators
     332 [ -  + ][ -  + ]:     175436 :         Assert(nullTerm.isNull());
                 [ -  - ]
     333                 :     175436 :         updateCong(res, children, cdp, op);
     334                 :            :       }
     335                 :     254663 :     }
     336                 :     254663 :     break;
     337                 :        388 :     case ProofRule::HO_CONG:
     338                 :            :     {
     339                 :            :       // converted to chain of CONG, with no base operator
     340                 :        388 :       updateCong(res, children, cdp, Node::null());
     341                 :            :     }
     342                 :        388 :     break;
     343                 :      31253 :     case ProofRule::AND_INTRO:
     344                 :            :     {
     345                 :      31253 :       Node cur = d_tproc.getNullTerminator(nm, Kind::AND);
     346                 :      31253 :       size_t nchildren = children.size();
     347         [ +  + ]:     129106 :       for (size_t j = 0; j < nchildren; j++)
     348                 :            :       {
     349                 :      97853 :         size_t jj = (nchildren - 1) - j;
     350                 :            :         // conclude the final conclusion if we are finished
     351                 :     164453 :         Node next = jj == 0 ? res : nm->mkNode(Kind::AND, children[jj], cur);
     352         [ +  + ]:      97853 :         if (j == 0)
     353                 :            :         {
     354                 :      62506 :           addLfscRule(cdp, next, {children[jj]}, LfscRule::AND_INTRO1, {});
     355                 :            :         }
     356                 :            :         else
     357                 :            :         {
     358 [ +  + ][ -  - ]:     199800 :           addLfscRule(cdp, next, {children[jj], cur}, LfscRule::AND_INTRO2, {});
     359                 :            :         }
     360                 :      97853 :         cur = next;
     361                 :      97853 :       }
     362                 :      31253 :     }
     363                 :      31253 :     break;
     364                 :       5282 :     case ProofRule::ARITH_SUM_UB:
     365                 :            :     {
     366                 :            :       // proof of null terminator base 0 = 0
     367                 :      10564 :       Node zero = d_tproc.getNullTerminator(nm, Kind::ADD, res[0].getType());
     368                 :       5282 :       Node cur = zero.eqNode(zero);
     369                 :      10564 :       cdp->addStep(cur, ProofRule::REFL, {}, {zero});
     370         [ +  + ]:      32839 :       for (size_t i = 0, size = children.size(); i < size; i++)
     371                 :            :       {
     372                 :      27557 :         size_t ii = (children.size() - 1) - i;
     373                 :     110228 :         std::vector<Node> newChildren{children[ii], cur};
     374         [ +  + ]:      27557 :         if (ii == 0)
     375                 :            :         {
     376                 :            :           // final rule must be the real conclusion
     377                 :       5282 :           addLfscRule(cdp, res, newChildren, LfscRule::ARITH_SUM_UB, {});
     378                 :            :         }
     379                 :            :         else
     380                 :            :         {
     381                 :            :           // rules build an n-ary chain of + on both sides
     382                 :      22275 :           cur = d_pc->checkDebug(ProofRule::ARITH_SUM_UB, newChildren, {});
     383                 :      22275 :           addLfscRule(cdp, cur, newChildren, LfscRule::ARITH_SUM_UB, {});
     384                 :            :         }
     385                 :      27557 :       }
     386                 :       5282 :     }
     387                 :       5282 :     break;
     388                 :       1373 :     case ProofRule::INSTANTIATE:
     389                 :            :     {
     390                 :       1373 :       Node q = children[0];
     391 [ -  + ][ -  + ]:       1373 :       Assert(q.getKind() == Kind::FORALL);
                 [ -  - ]
     392                 :       1373 :       std::vector<Node> terms;
     393                 :       2746 :       std::vector<Node> qvars(q[0].begin(), q[0].end());
     394                 :       1373 :       Node conc = q;
     395         [ +  + ]:       4878 :       for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
     396                 :            :       {
     397 [ -  + ][ -  + ]:       3505 :         Assert(conc.getKind() == Kind::FORALL);
                 [ -  - ]
     398                 :       3505 :         Node prevConc = conc;
     399         [ +  + ]:       3505 :         if (i + 1 == nvars)
     400                 :            :         {
     401                 :       1373 :           conc = res;
     402                 :            :         }
     403                 :            :         else
     404                 :            :         {
     405 [ -  + ][ -  + ]:       2132 :           Assert(i + 1 < qvars.size());
                 [ -  - ]
     406                 :       2132 :           std::vector<Node> qvarsNew(qvars.begin() + i + 1, qvars.end());
     407 [ -  + ][ -  + ]:       2132 :           Assert(!qvarsNew.empty());
                 [ -  - ]
     408 [ -  + ][ -  + ]:       8528 :           AssertEqual(qvars[i].getType(), args[0][i].getType());
                 [ -  - ]
     409                 :       2132 :           std::vector<Node> qchildren;
     410                 :       2132 :           TNode v = qvars[i];
     411                 :       2132 :           TNode subs = args[0][i];
     412                 :       2132 :           qchildren.push_back(nm->mkNode(Kind::BOUND_VAR_LIST, qvarsNew));
     413                 :       2132 :           qchildren.push_back(conc[1].substitute(v, subs));
     414                 :       2132 :           conc = nm->mkNode(Kind::FORALL, qchildren);
     415                 :       2132 :         }
     416                 :      10515 :         addLfscRule(cdp, conc, {prevConc}, LfscRule::INSTANTIATE, {args[0][i]});
     417                 :       3505 :       }
     418                 :       1373 :     }
     419                 :       1373 :     break;
     420                 :        246 :     case ProofRule::THEORY_REWRITE:
     421                 :            :     {
     422 [ -  + ][ -  + ]:        246 :       Assert(args.size() >= 2);
                 [ -  - ]
     423                 :            :       ProofRewriteRule idr;
     424         [ -  + ]:        246 :       if (!rewriter::getRewriteRule(args[0], idr))
     425                 :            :       {
     426                 :        246 :         return false;
     427                 :            :       }
     428         [ -  + ]:        246 :       if (idr == ProofRewriteRule::BETA_REDUCE)
     429                 :            :       {
     430                 :            :         // get the term to beta-reduce
     431                 :          0 :         Node termToReduce = nm->mkNode(Kind::APPLY_UF, args[1][0]);
     432                 :          0 :         addLfscRule(cdp, res, {}, LfscRule::BETA_REDUCE, {termToReduce});
     433                 :          0 :       }
     434                 :            :       else
     435                 :            :       {
     436                 :        246 :         return false;
     437                 :            :       }
     438                 :            :     }
     439                 :          0 :     break;
     440                 :    1387899 :     default: return false; break;
     441                 :            :   }
     442 [ -  + ][ -  + ]:     455418 :   AlwaysAssert(cdp->getProofFor(res)->getRule() != ProofRule::ASSUME);
                 [ -  - ]
     443                 :     455418 :   return true;
     444                 :            : }
     445                 :            : 
     446                 :     175824 : void LfscProofPostprocessCallback::updateCong(Node res,
     447                 :            :                                               const std::vector<Node>& children,
     448                 :            :                                               CDProof* cdp,
     449                 :            :                                               Node startOp)
     450                 :            : {
     451                 :     175824 :   Node currEq;
     452                 :     175824 :   size_t i = 0;
     453                 :     175824 :   size_t nchildren = children.size();
     454         [ +  + ]:     175824 :   if (!startOp.isNull())
     455                 :            :   {
     456                 :            :     // start with reflexive equality on operator
     457                 :     175436 :     currEq = startOp.eqNode(startOp);
     458                 :            :   }
     459                 :            :   else
     460                 :            :   {
     461                 :            :     // first child specifies (higher-order) operator equality
     462                 :        388 :     currEq = children[0];
     463                 :        388 :     i++;
     464                 :            :   }
     465                 :     175824 :   Node curL = currEq[0];
     466                 :     175824 :   Node curR = currEq[1];
     467                 :     175824 :   NodeManager* nm = nodeManager();
     468         [ +  + ]:     490877 :   for (; i < nchildren; i++)
     469                 :            :   {
     470                 :            :     // CONG rules for each child
     471                 :     315053 :     Node nextEq;
     472         [ +  + ]:     315053 :     if (i + 1 == nchildren)
     473                 :            :     {
     474                 :            :       // if we are at the end, we prove the final equality
     475                 :     175824 :       nextEq = res;
     476                 :            :     }
     477                 :            :     else
     478                 :            :     {
     479                 :     139229 :       curL = nm->mkNode(Kind::HO_APPLY, curL, children[i][0]);
     480                 :     139229 :       curR = nm->mkNode(Kind::HO_APPLY, curR, children[i][1]);
     481                 :     139229 :       nextEq = curL.eqNode(curR);
     482                 :            :     }
     483 [ +  + ][ -  - ]:     945159 :     addLfscRule(cdp, nextEq, {currEq, children[i]}, LfscRule::CONG, {});
     484                 :     315053 :     currEq = nextEq;
     485                 :     315053 :   }
     486                 :     175824 : }
     487                 :            : 
     488                 :    1520049 : void LfscProofPostprocessCallback::addLfscRule(
     489                 :            :     CDProof* cdp,
     490                 :            :     Node conc,
     491                 :            :     const std::vector<Node>& children,
     492                 :            :     LfscRule lr,
     493                 :            :     const std::vector<Node>& args)
     494                 :            : {
     495                 :    1520049 :   std::vector<Node> largs;
     496                 :    1520049 :   largs.push_back(mkLfscRuleNode(nodeManager(), lr));
     497                 :    1520049 :   largs.push_back(conc);
     498                 :    1520049 :   largs.insert(largs.end(), args.begin(), args.end());
     499                 :    1520049 :   cdp->addStep(conc, ProofRule::LFSC_RULE, children, largs);
     500                 :    1520049 : }
     501                 :            : 
     502                 :          0 : Node LfscProofPostprocessCallback::mkChain(Kind k,
     503                 :            :                                            const std::vector<Node>& children)
     504                 :            : {
     505                 :          0 :   Assert(!children.empty());
     506                 :          0 :   NodeManager* nm = nodeManager();
     507                 :          0 :   size_t nchildren = children.size();
     508                 :          0 :   size_t i = 0;
     509                 :            :   // do we have a null terminator? If so, we start with it.
     510                 :          0 :   Node ret = d_tproc.getNullTerminator(nm, k, children[0].getType());
     511         [ -  - ]:          0 :   if (ret.isNull())
     512                 :            :   {
     513                 :          0 :     ret = children[nchildren - 1];
     514                 :          0 :     i = 1;
     515                 :            :   }
     516         [ -  - ]:          0 :   while (i < nchildren)
     517                 :            :   {
     518                 :          0 :     ret = nm->mkNode(k, children[(nchildren - 1) - i], ret);
     519                 :          0 :     i++;
     520                 :            :   }
     521                 :          0 :   return ret;
     522                 :          0 : }
     523                 :            : 
     524                 :     207215 : Node LfscProofPostprocessCallback::mkDummyPredicate(NodeManager* nm)
     525                 :            : {
     526                 :     414430 :   return NodeManager::mkBoundVar(nm->booleanType());
     527                 :            : }
     528                 :            : 
     529                 :       1722 : LfscProofPostprocess::LfscProofPostprocess(Env& env, LfscNodeConverter& ltp)
     530                 :       1722 :     : EnvObj(env), d_cb(new proof::LfscProofPostprocessCallback(env, ltp))
     531                 :            : {
     532                 :       1722 : }
     533                 :            : 
     534                 :       1722 : void LfscProofPostprocess::process(std::shared_ptr<ProofNode> pf)
     535                 :            : {
     536                 :       1722 :   d_cb->initializeUpdate();
     537                 :            :   // do not automatically add symmetry steps, since this leads to
     538                 :            :   // non-termination for example on policy_variable.smt2
     539                 :       1722 :   ProofNodeUpdater updater(d_env, *(d_cb.get()), false, false);
     540                 :       1722 :   updater.process(pf);
     541                 :       1722 : }
     542                 :            : 
     543                 :            : }  // namespace proof
     544                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14