LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/proof - resolution_proofs_util.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 264 303 87.1 %
Date: 2026-02-26 11:40:56 Functions: 4 5 80.0 %
Branches: 221 352 62.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                 :            :  * Utilities for resolution proofs.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "proof/resolution_proofs_util.h"
      14                 :            : 
      15                 :            : #include "proof/proof.h"
      16                 :            : #include "proof/proof_checker.h"
      17                 :            : #include "proof/proof_node_manager.h"
      18                 :            : 
      19                 :            : namespace cvc5::internal {
      20                 :            : namespace proof {
      21                 :            : 
      22                 :            : /** The information relevant for converting CHAIN_M_RESOLUTION steps into
      23                 :            :  * CHAIN_RESOLUTION+FACTORING steps when "crowding literals" are present. See
      24                 :            :  * `ProofPostprocessCallback::eliminateCrowdingLits` for more information. */
      25                 :            : struct CrowdingLitInfo
      26                 :            : {
      27                 :     417160 :   CrowdingLitInfo(size_t lastInclusion = static_cast<size_t>(-1),
      28                 :            :                   size_t eliminator = static_cast<size_t>(-1),
      29                 :            :                   bool onlyCrowdAndConcLitsInElim = false,
      30                 :            :                   size_t maxSafeMovePosition = static_cast<size_t>(-1))
      31                 :     417160 :       : d_lastInclusion(lastInclusion),
      32                 :     417160 :         d_eliminator(eliminator),
      33                 :     417160 :         d_onlyCrowdAndConcLitsInElim(onlyCrowdAndConcLitsInElim),
      34                 :     417160 :         d_maxSafeMovePosition(maxSafeMovePosition)
      35                 :            :   {
      36                 :     417160 :   }
      37                 :            :   /* Index of last (left-to-right) premise to introduce this crowding literal */
      38                 :            :   size_t d_lastInclusion;
      39                 :            :   /* Index of last premise to eliminate this crowding literal */
      40                 :            :   size_t d_eliminator;
      41                 :            :   /* Whether there are only crowding/conclusion literals in the eliminator. */
      42                 :            :   bool d_onlyCrowdAndConcLitsInElim;
      43                 :            :   /* Maximum position to which the eliminator premise can be moved without
      44                 :            :    * changing the behavior of the resolution chain. Note this only applies when
      45                 :            :    * d_onlyCrowdAndConcLitsInElim is true. */
      46                 :            :   size_t d_maxSafeMovePosition;
      47                 :            : };
      48                 :            : 
      49                 :          0 : std::ostream& operator<<(std::ostream& out, CrowdingLitInfo info)
      50                 :            : {
      51                 :          0 :   out << "{" << info.d_lastInclusion << ", " << info.d_eliminator << ", ";
      52         [ -  - ]:          0 :   if (info.d_onlyCrowdAndConcLitsInElim)
      53                 :            :   {
      54                 :          0 :     out << "true, " << info.d_maxSafeMovePosition;
      55                 :            :   }
      56                 :            :   else
      57                 :            :   {
      58                 :          0 :     out << "false";
      59                 :            :   }
      60                 :          0 :   out << "}";
      61                 :          0 :   return out;
      62                 :            : }
      63                 :            : 
      64                 :      20549 : Node eliminateCrowdingLits(NodeManager* nm,
      65                 :            :                            bool reorderPremises,
      66                 :            :                            const std::vector<Node>& clauseLits,
      67                 :            :                            const std::vector<Node>& targetClauseLits,
      68                 :            :                            const std::vector<Node>& children,
      69                 :            :                            const std::vector<Node>& args,
      70                 :            :                            CDProof* cdp,
      71                 :            :                            ProofNodeManager* pnm)
      72                 :            : {
      73         [ +  - ]:      20549 :   Trace("crowding-lits") << push;
      74         [ +  - ]:      20549 :   Trace("crowding-lits") << "Clause lits: " << clauseLits << "\n";
      75         [ +  - ]:      20549 :   Trace("crowding-lits") << "Target lits: " << targetClauseLits << "\n\n";
      76                 :      20549 :   std::vector<Node> newChildren{children}, newArgs{args};
      77                 :      20549 :   Node trueNode = nm->mkConst(true);
      78                 :            :   // get crowding lits and the position of the last clause that includes
      79                 :            :   // them. The factoring step must be added after the last inclusion and before
      80                 :            :   // its elimination.
      81                 :      20549 :   std::unordered_set<TNode> crowding;
      82                 :      20549 :   size_t childrenSize = children.size(), numCrowding = 0;
      83                 :      20549 :   std::vector<std::pair<Node, size_t>> lastInclusion;
      84                 :            :   // for each crowding lit, the information that is used to eliminate it
      85                 :      20549 :   std::map<Node, CrowdingLitInfo> crowdLitsInfo;
      86                 :            :   // positions of eliminators of crowding literals, which are the positions of
      87                 :            :   // the clauses that eliminate crowding literals *after* their last inclusion
      88                 :      20549 :   std::vector<size_t> eliminators;
      89         [ +  + ]:    1754691 :   for (size_t i = 0, size = clauseLits.size(); i < size; ++i)
      90                 :            :   {
      91                 :            :     // repeated crowding literal or conclusion literal
      92 [ +  + ][ -  - ]:    3468284 :     if (crowding.count(clauseLits[i])
      93 [ +  + ][ +  + ]:    6472534 :         || std::find(
      94                 :    1502125 :                targetClauseLits.begin(), targetClauseLits.end(), clauseLits[i])
      95         [ +  - ]:    4738392 :                != targetClauseLits.end())
      96                 :            :     {
      97                 :    1525562 :       continue;
      98                 :            :     }
      99                 :     208580 :     Node crowdLit = clauseLits[i];
     100                 :     208580 :     crowding.insert(crowdLit);
     101         [ +  - ]:     208580 :     Trace("crowding-lits3") << "crowding lit " << crowdLit << "\n";
     102                 :            :     // found crowding lit, now get its last inclusion position, which is the
     103                 :            :     // position of the last resolution link that introduces the crowding
     104                 :            :     // literal. Note that this position has to be *before* the last link, as a
     105                 :            :     // link *after* the last inclusion must eliminate the crowding literal.
     106                 :            :     size_t j;
     107         [ +  - ]:    3452649 :     for (j = childrenSize - 1; j > 0; --j)
     108                 :            :     {
     109                 :            :       // notice that only non-singleton clauses may be introducing the
     110                 :            :       // crowding literal, so we only care about non-singleton OR nodes. We
     111                 :            :       // check then against the kind and whether the whole OR node occurs as a
     112                 :            :       // pivot of the respective resolution
     113         [ +  + ]:    3452649 :       if (newChildren[j - 1].getKind() != Kind::OR)
     114                 :            :       {
     115                 :     303570 :         continue;
     116                 :            :       }
     117                 :    3149079 :       uint64_t pivotIndex = 2 * (j - 1);
     118                 :    3149079 :       if (newArgs[pivotIndex] == newChildren[j - 1]
     119 [ +  + ][ -  + ]:    6298158 :           || newArgs[pivotIndex].notNode() == newChildren[j - 1])
         [ +  + ][ +  + ]
                 [ -  - ]
     120                 :            :       {
     121                 :       2374 :         continue;
     122                 :            :       }
     123                 :    6293410 :       if (std::find(
     124                 :    6293410 :               newChildren[j - 1].begin(), newChildren[j - 1].end(), crowdLit)
     125         [ +  + ]:    6293410 :           != newChildren[j - 1].end())
     126                 :            :       {
     127                 :     208580 :         break;
     128                 :            :       }
     129                 :            :     }
     130 [ -  + ][ -  + ]:     208580 :     Assert(j > 0);
                 [ -  - ]
     131                 :     208580 :     lastInclusion.emplace_back(crowdLit, j - 1);
     132                 :     208580 :     CrowdingLitInfo info;
     133                 :     208580 :     info.d_lastInclusion = j - 1;
     134                 :            : 
     135         [ +  - ]:     208580 :     Trace("crowding-lits3") << "last inc " << j - 1 << "\n";
     136                 :            :     // get elimination position, starting from the following link as the last
     137                 :            :     // inclusion one. The result is the last (in the chain, but first from
     138                 :            :     // this point on) resolution link that eliminates the crowding literal. A
     139                 :            :     // literal l is eliminated by a link if it contains a literal l' with
     140                 :            :     // opposite polarity to l.
     141         [ +  - ]:     601739 :     for (; j < childrenSize; ++j)
     142                 :            :     {
     143                 :     601739 :       bool posFirst = newArgs[(2 * j) - 1] == trueNode;
     144                 :     601739 :       Node pivot = newArgs[(2 * j)];
     145         [ +  - ]:    1203478 :       Trace("crowding-lits3")
     146                 :     601739 :           << "\tcheck w/ args " << posFirst << " / " << pivot << "\n";
     147                 :            :       // To eliminate the crowding literal (crowdLit), the clause must contain
     148                 :            :       // it with opposite polarity. There are three successful cases,
     149                 :            :       // according to the pivot and its sign
     150                 :            :       //
     151                 :            :       // - crowdLit is the same as the pivot and posFirst is true, which means
     152                 :            :       //   that the clause contains its negation and eliminates it
     153                 :            :       //
     154                 :            :       // - crowdLit is the negation of the pivot and posFirst is false, so the
     155                 :            :       //   clause contains the node whose negation is crowdLit. Note that this
     156                 :            :       //   case may either be crowdLit.notNode() == pivot or crowdLit ==
     157                 :            :       //   pivot.notNode().
     158         [ -  + ]:     126610 :       if ((crowdLit == pivot && posFirst)
     159 [ -  + ][ -  - ]:    1076868 :           || (crowdLit.notNode() == pivot && !posFirst)
         [ +  + ][ -  - ]
     160 [ +  + ][ +  + ]:    1203478 :           || (pivot.notNode() == crowdLit && !posFirst))
         [ +  - ][ +  + ]
         [ +  + ][ -  - ]
     161                 :            :       {
     162         [ +  - ]:     208580 :         Trace("crowding-lits3") << "\t\tfound it!\n";
     163                 :     208580 :         eliminators.push_back(j);
     164                 :     208580 :         break;
     165                 :            :       }
     166         [ +  + ]:     601739 :     }
     167                 :     208580 :     info.d_eliminator = eliminators.back();
     168                 :     208580 :     crowdLitsInfo[crowdLit] = info;
     169         [ +  - ]:     417160 :     Trace("crowding-lits3") << "last inc " << info.d_lastInclusion << ", elim "
     170                 :     208580 :                             << info.d_eliminator << "\n";
     171 [ -  + ][ -  + ]:     208580 :     AlwaysAssert(j < childrenSize);
                 [ -  - ]
     172                 :     208580 :   }
     173 [ -  + ][ -  + ]:      20549 :   Assert(!lastInclusion.empty());
                 [ -  - ]
     174         [ +  - ]:      41098 :   Trace("crowding-lits") << "..total of " << lastInclusion.size()
     175                 :      20549 :                          << " crowding literals\n";
     176                 :      20549 :   numCrowding = lastInclusion.size();
     177                 :            :   // order map so that we process crowding literals in the order of the clauses
     178                 :            :   // that last introduce them
     179                 :     832700 :   auto cmp = [](std::pair<Node, size_t>& a, std::pair<Node, size_t>& b) {
     180                 :     832700 :     return a.second < b.second;
     181                 :            :   };
     182                 :      20549 :   std::sort(lastInclusion.begin(), lastInclusion.end(), cmp);
     183                 :            :   // order eliminators
     184                 :      20549 :   std::sort(eliminators.begin(), eliminators.end());
     185         [ +  - ]:      20549 :   if (reorderPremises)
     186                 :            :   {
     187                 :            :     // compute extra information. We are trying to determine if we can move
     188                 :            :     // crowding literal eliminators further to the right in the resolution
     189                 :            :     // chain. This will reduce the number of divisions to the chain to be made
     190                 :            :     // in the expansion below. Currently we determine this moving worthwhile
     191                 :            :     // when the eliminator has no non-crowding, non-conclusion, non-pivot
     192                 :            :     // literals, since they would necessarily be eliminated somewhere down the
     193                 :            :     // road, and moving the premise beyond that point would break the resolution
     194                 :            :     // chain. While this may also happen for conclusion and other crowding
     195                 :            :     // literals, we can compute the safe point to move relatively cheaply.
     196         [ +  + ]:     229129 :     for (size_t i = 0; i < numCrowding; ++i)
     197                 :            :     {
     198                 :     208580 :       Node crowdingLit = lastInclusion[i].first;
     199                 :     208580 :       size_t elim = crowdLitsInfo[crowdingLit].d_eliminator;
     200                 :            :       // Since this primise is an eliminator, if it's an OR it can only be a
     201                 :            :       // singleton if the crowding literal is its negation.
     202                 :     208580 :       size_t maxSafeMove = childrenSize,
     203                 :     208580 :              numLits = (newChildren[elim].getKind() != Kind::OR
     204         [ +  + ]:     202230 :                         || (crowdingLit.getKind() == Kind::NOT
     205 [ +  + ][ -  - ]:     153630 :                             && crowdingLit[0] == newChildren[elim]))
     206         [ +  + ]:     417162 :                            ? 1
     207         [ +  + ]:     208580 :                            : newChildren[elim].getNumChildren();
     208                 :            :       // unit eliminators can be moved to the end
     209         [ +  + ]:     208580 :       if (numLits == 1)
     210                 :            :       {
     211         [ +  - ]:      12704 :         Trace("crowding-lits2") << "....elim " << elim << " is unit (of " << i
     212                 :       6352 :                                 << "-th crowding lit)\n";
     213                 :       6352 :         crowdLitsInfo[lastInclusion[i].first].d_onlyCrowdAndConcLitsInElim =
     214                 :            :             true;
     215                 :       6352 :         crowdLitsInfo[lastInclusion[i].first].d_maxSafeMovePosition =
     216                 :            :             maxSafeMove;
     217                 :       6352 :         continue;
     218                 :            :       }
     219                 :            :       // how many non-crowding non-conclusion lits
     220                 :     202228 :       uint32_t regularLits = 0;
     221                 :            :       // only search while it's possible (no non-crowding, non-conclusion,
     222                 :            :       // non-pivot literals) or worthwhile (would move at least one
     223                 :            :       // position). Note that there will always be at least one "regular"
     224                 :            :       // literal, i.e., the pivot that eliminates the crowding literal.
     225                 :     888309 :       for (size_t j = 0;
     226 [ +  + ][ +  + ]:     888309 :            j < numLits && regularLits <= 1 && (maxSafeMove - elim) > 1;
                 [ +  + ]
     227                 :            :            ++j)
     228                 :            :       {
     229                 :     686081 :         bool isCrowdingLit = crowding.count(newChildren[elim][j]);
     230                 :            :         // if not crowding and not in conclusion it's a regular literal
     231                 :    1571338 :         if (!isCrowdingLit
     232 [ +  + ][ +  + ]:    1716279 :             && std::find(targetClauseLits.begin(),
         [ +  + ][ -  - ]
     233                 :            :                          targetClauseLits.end(),
     234                 :     515099 :                          newChildren[elim][j])
     235         [ +  + ]:    1716279 :                    == targetClauseLits.end())
     236                 :            :         {
     237                 :     199176 :           regularLits++;
     238                 :     199176 :           continue;
     239                 :            :         }
     240                 :            :         // If it's a crowding literal we know it's unsafe to move this premise
     241                 :            :         // beyond its last inclusion
     242         [ +  + ]:     486905 :         if (isCrowdingLit)
     243                 :            :         {
     244 [ -  + ][ -  + ]:     170982 :           Assert(crowdLitsInfo.find(newChildren[elim][j])
                 [ -  - ]
     245                 :            :                  != crowdLitsInfo.end());
     246                 :     170982 :           size_t lastInc = crowdLitsInfo[newChildren[elim][j]].d_lastInclusion;
     247         [ +  + ]:     170982 :           maxSafeMove = lastInc < maxSafeMove ? lastInc : maxSafeMove;
     248                 :            :         }
     249                 :            :         // We check the pivots of the steps until the current limit
     250                 :            :         // (maxSafeMove). If this literal is a pivot in any such step then it is
     251                 :            :         // unsafe to move the eliminator beyond it. That then becomes the new
     252                 :            :         // limit. The literal is such a pivot if it matches a lhs pivot, i.e.,
     253                 :            :         // the same as a (pivot, +pol) or the negation of a (pivot, -pol).
     254                 :     486905 :         for (size_t k = 2 * (elim + 1) - 1, checkLim = (2 * maxSafeMove) - 1;
     255         [ +  + ]:    3516853 :              k < checkLim;
     256                 :    3029948 :              k = k + 2)
     257                 :            :         {
     258                 :    3030153 :           bool posPivot = newArgs[k] == trueNode;
     259 [ -  + ][ +  + ]:    6060458 :           if ((newChildren[elim][j] == newArgs[k + 1] && posPivot)
                 [ -  - ]
     260                 :    6060511 :               || (newChildren[elim][j] == newArgs[k + 1].notNode()
     261         [ +  - ]:         53 :                   && !posPivot))
     262                 :            :           {
     263                 :        205 :             maxSafeMove = (k + 1) / 2;
     264                 :        205 :             break;
     265                 :            :           }
     266                 :            :         }
     267                 :            :       }
     268                 :            :       // If there is a single regular literal, all other are either crowiding or
     269                 :            :       // conclusion literals.
     270 [ +  + ][ +  + ]:     202228 :       if (regularLits == 1 && maxSafeMove > elim && maxSafeMove - elim > 1)
                 [ +  + ]
     271                 :            :       {
     272         [ +  - ]:     139948 :         Trace("crowding-lits2")
     273                 :          0 :             << "....elim " << elim << " has only crowd/conc lits (of " << i
     274                 :      69974 :             << "-th crowding lit). MaxSafeMove: " << maxSafeMove << "\n";
     275                 :      69974 :         crowdLitsInfo[lastInclusion[i].first].d_onlyCrowdAndConcLitsInElim =
     276                 :            :             true;
     277                 :      69974 :         crowdLitsInfo[lastInclusion[i].first].d_maxSafeMovePosition =
     278                 :            :             maxSafeMove;
     279                 :            :       }
     280         [ +  + ]:     208580 :     }
     281         [ -  + ]:      20549 :     if (TraceIsOn("crowding-lits"))
     282                 :            :     {
     283         [ -  - ]:          0 :       Trace("crowding-lits") << "crowding lits last inclusion:\n";
     284         [ -  - ]:          0 :       for (size_t i = 0, size = lastInclusion.size(); i < size; ++i)
     285                 :            :       {
     286                 :          0 :         Node crowdingLit = lastInclusion[i].first;
     287         [ -  - ]:          0 :         Trace("crowding-lits")
     288                 :          0 :             << "\t- [" << crowdLitsInfo[crowdingLit].d_lastInclusion << "] : {"
     289                 :          0 :             << crowdLitsInfo[crowdingLit].d_eliminator << "} " << crowdingLit
     290                 :          0 :             << "\n";
     291         [ -  - ]:          0 :         if (crowdLitsInfo[crowdingLit].d_onlyCrowdAndConcLitsInElim)
     292                 :            :         {
     293         [ -  - ]:          0 :           Trace("crowding-lits")
     294                 :          0 :               << "\t\t- onlyCrowdAndConcLitsInElim; maxSafeMove: "
     295                 :          0 :               << crowdLitsInfo[crowdingLit].d_maxSafeMovePosition << "\n";
     296                 :            :         }
     297                 :          0 :       }
     298                 :            :     }
     299                 :            :     // Every eliminator can be moved, without loss of generality, up to the
     300                 :            :     // immediate position before a clause that eliminates one its non-pivot
     301                 :            :     // literals. We also add restrict the move to be up to the minimal last
     302                 :            :     // inclusion of a crowding literal, as moving beyond that would require
     303                 :            :     // recomputing the information of that crowding literal. So for example, an
     304                 :            :     // eliminator in position i, which has only crowding/conclusion literals
     305                 :            :     // that are not eliminated before a minimun last inclusion position i+1000,
     306                 :            :     // can be moved anywhere in the premises between i and i+999. This is the
     307                 :            :     // case since all the literals that are introduced by the eliminator are
     308                 :            :     // going to be killed (or preserved for conclusion) only starting at i+1000.
     309                 :            :     //
     310                 :            :     // Given the above we proceed, based on the crowding lits information, to
     311                 :            :     // move up to maximum all eliminators.
     312                 :            :     //
     313                 :            :     // We will use std::rotate to do the moving of the premises, which moves to
     314                 :            :     // target position (first argument) an interval [l, u), where l and u are
     315                 :            :     // the second and third arguments. For each eliminator in position i that
     316                 :            :     // has a minimum crowding last inclusion k bigger than i + 1, we do the
     317                 :            :     // moving with arguments (i, i+1, k). As a consequence newChildren[i] will
     318                 :            :     // move to position k-1. Since we are moving premises we also need to move
     319                 :            :     // arguments. The polarity/pivot of the eliminator in position i are (2*i)
     320                 :            :     // -1 and 2*i. So the rotation in the arguments is with (2*i-1, 2*i+1,
     321                 :            :     // 2*k-1).
     322         [ +  - ]:      20549 :     Trace("smt-proof-pp-debug") << "Moving eliminators\n" << push;
     323                 :            :     // This guys will be used to search for the position of the eliminator
     324                 :            :     // within newChildren after the moves below. This is necessary because an
     325                 :            :     // eliminator originally moved to position k can end up in some position
     326                 :            :     // before k if any other eliminator is moved ahead of k
     327                 :      20549 :     uint32_t counterMoved = 0;
     328         [ +  + ]:     229129 :     for (size_t i = 0; i < numCrowding; ++i)
     329                 :            :     {
     330                 :     208580 :       Node crowdingLit = lastInclusion[i].first;
     331                 :     208580 :       size_t elim = crowdLitsInfo[crowdingLit].d_eliminator;
     332                 :     208580 :       size_t maxSafeMove = crowdLitsInfo[crowdingLit].d_maxSafeMovePosition;
     333                 :     208580 :       Assert(maxSafeMove >= elim) << i << "-th crowding lit " << crowdingLit
     334                 :          0 :                                   << " has info " << crowdLitsInfo[crowdingLit];
     335                 :     208580 :       if (!crowdLitsInfo[crowdingLit].d_onlyCrowdAndConcLitsInElim
     336 [ +  + ][ +  + ]:     208580 :           || maxSafeMove - elim <= 1)
                 [ +  + ]
     337                 :            :       {
     338                 :     132470 :         continue;
     339                 :            :       }
     340                 :      76110 :       ++counterMoved;
     341         [ +  - ]:     152220 :       Trace("crowding-lits")
     342                 :      76110 :           << "..move elim " << elim << " to " << maxSafeMove - 1 << "\n";
     343                 :     152220 :       std::rotate(newChildren.begin() + elim,
     344                 :      76110 :                   newChildren.begin() + elim + 1,
     345                 :      76110 :                   newChildren.begin() + maxSafeMove);
     346                 :     152220 :       std::rotate(newArgs.begin() + (2 * elim) - 1,
     347                 :      76110 :                   newArgs.begin() + (2 * elim) + 1,
     348                 :      76110 :                   newArgs.begin() + (2 * maxSafeMove) - 1);
     349                 :            :       // Being pedantic here we should assert that the rotated
     350                 :            :       // newChildren/newArgs still yield the same conclusion with a
     351                 :            :       // CHAIN_M_RESOLUTION step. However this can be very expensive to check,
     352                 :            :       // so we don't do this. Only if one is debugging this code this test
     353                 :            :       // should be added.
     354                 :            : 
     355                 :            :       // Now we need to update the indices, since we have changed newChildren.
     356                 :            :       // For every crowding literal whose information indices are in the
     357                 :            :       // interval [elim+1, maxSafeMove), these indices should be decremented
     358                 :            :       // by 1.
     359         [ +  + ]:    1522704 :       for (std::pair<const Node, CrowdingLitInfo>& p : crowdLitsInfo)
     360                 :            :       {
     361                 :    1446594 :         bool updated = false;
     362                 :            :         // guarantee we update who we just moved...
     363         [ +  + ]:    1446594 :         if (p.first == crowdingLit)
     364                 :            :         {
     365                 :      76110 :           p.second.d_eliminator = maxSafeMove - 1;
     366                 :      76110 :           continue;
     367                 :            :         }
     368                 :            :         // can update lastInclusion, eliminator and maxSafeMovePosition
     369         [ +  + ]:    1370484 :         if (p.second.d_lastInclusion >= elim + 1
     370         [ +  + ]:     549424 :             && p.second.d_lastInclusion < maxSafeMove)
     371                 :            :         {
     372                 :      28544 :           --p.second.d_lastInclusion;
     373                 :      28544 :           updated = true;
     374                 :            :         }
     375         [ +  + ]:    1370484 :         if (p.second.d_eliminator >= elim + 1
     376         [ +  + ]:     738434 :             && p.second.d_eliminator < maxSafeMove)
     377                 :            :         {
     378                 :     198361 :           --p.second.d_eliminator;
     379                 :     198361 :           updated = true;
     380                 :            :         }
     381         [ +  + ]:    1370484 :         if (p.second.d_onlyCrowdAndConcLitsInElim
     382         [ +  + ]:     607926 :             && p.second.d_maxSafeMovePosition >= elim + 1
     383         [ +  + ]:     375846 :             && p.second.d_maxSafeMovePosition < maxSafeMove)
     384                 :            :         {
     385                 :       2314 :           --p.second.d_maxSafeMovePosition;
     386                 :       2314 :           updated = true;
     387                 :            :         }
     388         [ +  + ]:    1370484 :         if (updated)
     389                 :            :         {
     390         [ +  - ]:     398414 :           Trace("crowding-lits")
     391                 :          0 :               << "\tUpdated other crowding lit " << p.first << " info to "
     392                 :     199207 :               << crowdLitsInfo[p.first] << "\n";
     393                 :            :         }
     394                 :            :       }
     395         [ +  + ]:     208580 :     }
     396         [ +  - ]:      20549 :     Trace("smt-proof-pp-debug") << pop;
     397         [ +  + ]:      20549 :     if (counterMoved)
     398                 :            :     {
     399         [ +  - ]:      30888 :       Trace("crowding-lits")
     400                 :      15444 :           << "..moved up " << counterMoved << " eliminators\n";
     401         [ +  - ]:      30888 :       Trace("smt-proof-pp-debug")
     402                 :      15444 :           << "Updating bookkeeping of lastInclusion/eliminators vectors\n";
     403         [ +  - ]:      15444 :       Trace("crowding-lits") << "New premises " << newChildren << "\n";
     404         [ +  - ]:      15444 :       Trace("crowding-lits") << "New args " << newArgs << "\n";
     405                 :            :       // lastInclusion order will not have changed, so we just traverse in the
     406                 :            :       // same way and update
     407         [ +  + ]:     210109 :       for (auto& p : lastInclusion)
     408                 :            :       {
     409                 :     194665 :         p.second = crowdLitsInfo[p.first].d_lastInclusion;
     410                 :            :       }
     411                 :            :       // since eliminators may have changed drastically, we fully recompute
     412                 :      15444 :       eliminators.clear();
     413         [ +  + ]:     210109 :       for (const std::pair<const Node, CrowdingLitInfo>& p : crowdLitsInfo)
     414                 :            :       {
     415                 :     194665 :         eliminators.push_back(p.second.d_eliminator);
     416                 :            :       }
     417                 :      15444 :       std::sort(eliminators.begin(), eliminators.end());
     418                 :            :     }
     419                 :            :   }
     420         [ -  + ]:      20549 :   if (TraceIsOn("crowding-lits"))
     421                 :            :   {
     422         [ -  - ]:          0 :     Trace("crowding-lits") << "crowding lits last inclusion:\n";
     423         [ -  - ]:          0 :     for (size_t i = 0, size = lastInclusion.size(); i < size; ++i)
     424                 :            :     {
     425                 :          0 :       Node crowdingLit = lastInclusion[i].first;
     426         [ -  - ]:          0 :       Trace("crowding-lits")
     427                 :          0 :           << "\t- [" << crowdLitsInfo[crowdingLit].d_lastInclusion << "] : {"
     428                 :          0 :           << crowdLitsInfo[crowdingLit].d_eliminator << "} " << crowdingLit
     429                 :          0 :           << "\n";
     430         [ -  - ]:          0 :       if (crowdLitsInfo[crowdingLit].d_onlyCrowdAndConcLitsInElim)
     431                 :            :       {
     432         [ -  - ]:          0 :         Trace("crowding-lits")
     433                 :          0 :             << "\t\t- onlyCrowdingInElim; maxSafeMove: "
     434                 :          0 :             << crowdLitsInfo[lastInclusion[i].first].d_maxSafeMovePosition
     435                 :          0 :             << "\n";
     436                 :            :       }
     437                 :          0 :     }
     438                 :            :   }
     439                 :            :   // TODO (cvc4-wishues/issues/77): implement also simpler version and compare
     440                 :            :   //
     441                 :            :   // We now start to break the chain, one step at a time. Naively this breaking
     442                 :            :   // down would be one resolution/factoring to each crowding literal, but we can
     443                 :            :   // merge some of the cases. We do the following:
     444                 :            :   //
     445                 :            :   //
     446                 :            :   // lastClause   children[start] ... children[end]
     447                 :            :   // ---------------------------------------------- CHAIN_RES
     448                 :            :   //         C
     449                 :            :   //    ----------- FACTORING
     450                 :            :   //    lastClause'                children[start'] ... children[end']
     451                 :            :   //    -------------------------------------------------------------- CHAIN_RES
     452                 :            :   //                                    ...
     453                 :            :   //
     454                 :            :   // where
     455                 :            :   //   lastClause_0 = children[0]
     456                 :            :   //   start_0 = 1
     457                 :            :   //   end_0 = eliminators[0] - 1
     458                 :            :   //   start_i+1 = nextGuardedElimPos - 1
     459                 :            :   //
     460                 :            :   // The important point is how end_i+1 is computed. It is based on what we call
     461                 :            :   // the "nextGuardedElimPos", i.e., the next elimination position that requires
     462                 :            :   // removal of duplicates. The intuition is that a factoring step may eliminate
     463                 :            :   // the duplicates of crowding literals l1 and l2. If the last inclusion of l2
     464                 :            :   // is before the elimination of l1, then we can go ahead and also perform the
     465                 :            :   // elimination of l2 without another factoring. However if another literal l3
     466                 :            :   // has its last inclusion after the elimination of l2, then the elimination of
     467                 :            :   // l3 is the next guarded elimination.
     468                 :            :   //
     469                 :            :   // To do the above computation then we determine, after a resolution/factoring
     470                 :            :   // step, the first crowded literal to have its last inclusion after "end". The
     471                 :            :   // first elimination position to be bigger than the position of that crowded
     472                 :            :   // literal is the next guarded elimination position.
     473                 :      20549 :   size_t lastElim = 0;
     474                 :      20549 :   Node lastClause = newChildren[0];
     475                 :      20549 :   std::vector<Node> childrenRes;
     476                 :      20549 :   std::vector<Node> childrenResArgs;
     477                 :      20549 :   Node resPlaceHolder;
     478                 :      20549 :   size_t nextGuardedElimPos = eliminators[0];
     479                 :            :   do
     480                 :            :   {
     481                 :     138585 :     size_t start = lastElim + 1;
     482                 :     138585 :     size_t end = nextGuardedElimPos - 1;
     483         [ +  - ]:     277170 :     Trace("crowding-lits") << "res with:\n\t\tlastClause: " << lastClause
     484                 :          0 :                            << "\n\t\tstart: " << start << "\n\t\tend: " << end
     485                 :     138585 :                            << "\n";
     486                 :     138585 :     childrenRes.push_back(lastClause);
     487                 :            :     // note that the interval of insert is exclusive in the end, so we add 1
     488                 :     277170 :     childrenRes.insert(childrenRes.end(),
     489                 :     138585 :                        newChildren.begin() + start,
     490                 :     138585 :                        newChildren.begin() + end + 1);
     491                 :     277170 :     childrenResArgs.insert(childrenResArgs.end(),
     492                 :     138585 :                            newArgs.begin() + (2 * start) - 1,
     493                 :     138585 :                            newArgs.begin() + (2 * end) + 1);
     494                 :     138585 :     std::vector<Node> cpols;
     495                 :     138585 :     std::vector<Node> clits;
     496         [ +  + ]:     650985 :     for (size_t i = 0, ncargs = childrenResArgs.size(); i < ncargs; i = i + 2)
     497                 :            :     {
     498                 :     512400 :       cpols.push_back(childrenResArgs[i]);
     499                 :     512400 :       clits.push_back(childrenResArgs[i + 1]);
     500                 :            :     }
     501                 :     138585 :     std::vector<Node> cargs;
     502                 :     138585 :     cargs.push_back(nm->mkNode(Kind::SEXPR, cpols));
     503                 :     138585 :     cargs.push_back(nm->mkNode(Kind::SEXPR, clits));
     504         [ +  - ]:     138585 :     Trace("crowding-lits") << "\tres children: " << childrenRes << "\n";
     505         [ +  - ]:     138585 :     Trace("crowding-lits") << "\tres args: " << childrenResArgs << "\n";
     506                 :     415755 :     resPlaceHolder = pnm->getChecker()->checkDebug(
     507                 :     415755 :         ProofRule::CHAIN_RESOLUTION, childrenRes, cargs, Node::null(), "");
     508         [ +  - ]:     138585 :     Trace("crowding-lits") << "resPlaceHorder: " << resPlaceHolder << "\n";
     509         [ +  - ]:     138585 :     Trace("crowding-lits") << "-------\n";
     510                 :     138585 :     cdp->addStep(
     511                 :            :         resPlaceHolder, ProofRule::CHAIN_RESOLUTION, childrenRes, cargs);
     512                 :            :     // I need to add factoring if end < children.size(). Otherwise, this is
     513                 :            :     // to be handled by the caller
     514         [ +  + ]:     138585 :     if (end < childrenSize - 1)
     515                 :            :     {
     516                 :     472144 :       lastClause = pnm->getChecker()->checkDebug(
     517                 :     354108 :           ProofRule::FACTORING, {resPlaceHolder}, {}, Node::null(), "");
     518         [ +  - ]:     118036 :       if (!lastClause.isNull())
     519                 :            :       {
     520                 :     236072 :         cdp->addStep(lastClause, ProofRule::FACTORING, {resPlaceHolder}, {});
     521         [ +  - ]:     118036 :         Trace("crowding-lits") << "Apply factoring.\n";
     522                 :            :       }
     523                 :            :       else
     524                 :            :       {
     525                 :          0 :         lastClause = resPlaceHolder;
     526                 :            :       }
     527         [ +  - ]:     118036 :       Trace("crowding-lits") << "lastClause: " << lastClause << "\n";
     528                 :            :     }
     529                 :            :     else
     530                 :            :     {
     531                 :      20549 :       lastClause = resPlaceHolder;
     532                 :      20549 :       break;
     533                 :            :     }
     534                 :            :     // update for next round
     535                 :     118036 :     childrenRes.clear();
     536                 :     118036 :     childrenResArgs.clear();
     537                 :     118036 :     lastElim = end;
     538                 :            : 
     539                 :            :     // find the position of the last inclusion of the next crowded literal
     540                 :     118036 :     size_t nextCrowdedInclusionPos = numCrowding;
     541         [ +  + ]:    1113463 :     for (size_t i = 0; i < numCrowding; ++i)
     542                 :            :     {
     543         [ +  + ]:    1092914 :       if (lastInclusion[i].second > lastElim)
     544                 :            :       {
     545                 :      97487 :         nextCrowdedInclusionPos = i;
     546                 :      97487 :         break;
     547                 :            :       }
     548                 :            :     }
     549         [ +  - ]:     236072 :     Trace("crowding-lits") << "nextCrowdedInclusion/Pos: "
     550                 :          0 :                            << lastInclusion[nextCrowdedInclusionPos].second
     551                 :     118036 :                            << "/" << nextCrowdedInclusionPos << "\n";
     552                 :            :     // if there is none, then the remaining literals will be used in the next
     553                 :            :     // round
     554                 :     118036 :     nextGuardedElimPos = childrenSize;
     555         [ +  + ]:     118036 :     if (nextCrowdedInclusionPos != numCrowding)
     556                 :            :     {
     557                 :      97487 :       nextGuardedElimPos = newChildren.size();
     558         [ +  - ]:     844867 :       for (size_t i = 0; i < numCrowding; ++i)
     559                 :            :       {
     560                 :            :         //  nextGuardedElimPos is the largest element of
     561                 :            :         // eliminators bigger the next crowded literal's last inclusion
     562         [ +  + ]:     844867 :         if (eliminators[i] > lastInclusion[nextCrowdedInclusionPos].second)
     563                 :            :         {
     564                 :      97487 :           nextGuardedElimPos = eliminators[i];
     565                 :      97487 :           break;
     566                 :            :         }
     567                 :            :       }
     568 [ -  + ][ -  + ]:      97487 :       Assert(nextGuardedElimPos < childrenSize);
                 [ -  - ]
     569                 :            :     }
     570         [ +  - ]:     236072 :     Trace("crowding-lits") << "nextGuardedElimPos: " << nextGuardedElimPos
     571                 :     118036 :                            << "\n";
     572 [ +  + ][ +  + ]:     297719 :   } while (true);
                 [ +  + ]
     573         [ +  - ]:      20549 :   Trace("crowding-lits") << pop;
     574                 :      41098 :   return lastClause;
     575                 :      20549 : }
     576                 :            : 
     577                 :      83761 : bool isSingletonClause(TNode res,
     578                 :            :                        const std::vector<Node>& children,
     579                 :            :                        const std::vector<Node>& args)
     580                 :            : {
     581         [ +  + ]:      83761 :   if (res.getKind() != Kind::OR)
     582                 :            :   {
     583                 :      16714 :     return true;
     584                 :            :   }
     585                 :            :   size_t i;
     586                 :      67047 :   Node trueNode = res.getNodeManager()->mkConst(true);
     587                 :            :   // Find out the last child to introduced res, if any. We only need to
     588                 :            :   // look at the last one because any previous introduction would have
     589                 :            :   // been eliminated.
     590                 :            :   //
     591                 :            :   // After the loop finishes i is the index of the child C_i that
     592                 :            :   // introduced res. If i=0 none of the children introduced res as a
     593                 :            :   // subterm and therefore it cannot be a singleton clause.
     594         [ +  + ]:    2256738 :   for (i = children.size(); i > 0; --i)
     595                 :            :   {
     596                 :            :     // only non-singleton clauses may be introducing
     597                 :            :     // res, so we only care about non-singleton or nodes. We check then
     598                 :            :     // against the kind and whether the whole or node occurs as a pivot of
     599                 :            :     // the respective resolution
     600         [ +  + ]:    2190371 :     if (children[i - 1].getKind() != Kind::OR)
     601                 :            :     {
     602                 :     216319 :       continue;
     603                 :            :     }
     604         [ +  + ]:    1974052 :     size_t pivotIndex = (i != 1) ? 2 * (i - 1) - 1 : 1;
     605                 :    1974052 :     if (args[pivotIndex] == children[i - 1]
     606 [ +  + ][ -  + ]:    3948104 :         || args[pivotIndex].notNode() == children[i - 1])
         [ +  + ][ +  + ]
                 [ -  - ]
     607                 :            :     {
     608                 :       2018 :       continue;
     609                 :            :     }
     610                 :            :     // if res occurs as a subterm of a non-singleton premise
     611                 :    1972034 :     if (std::find(children[i - 1].begin(), children[i - 1].end(), res)
     612         [ +  + ]:    3944068 :         != children[i - 1].end())
     613                 :            :     {
     614                 :        680 :       break;
     615                 :            :     }
     616                 :            :   }
     617                 :            : 
     618                 :            :   // If res is a subterm of one of the children we still need to check if
     619                 :            :   // that subterm is eliminated
     620         [ +  + ]:      67047 :   if (i > 0)
     621                 :            :   {
     622         [ +  + ]:        680 :     bool posFirst = (i == 1) ? (args[0] == trueNode)
     623                 :         90 :                              : (args[(2 * (i - 1)) - 2] == trueNode);
     624         [ +  + ]:        680 :     Node pivot = (i == 1) ? args[1] : args[(2 * (i - 1)) - 1];
     625                 :            : 
     626                 :            :     // Check if it is eliminated by the previous resolution step
     627 [ -  - ][ -  + ]:       1360 :     if ((res == pivot && !posFirst) || (res.notNode() == pivot && posFirst)
         [ -  - ][ -  + ]
                 [ -  - ]
     628 [ -  + ][ -  + ]:       1360 :         || (pivot.notNode() == res && posFirst))
         [ -  - ][ +  - ]
         [ +  - ][ -  - ]
     629                 :            :     {
     630                 :            :       // We decrease i by one, since it could have been the case that i
     631                 :            :       // was equal to children.size(), so that we return false in the end
     632                 :          0 :       --i;
     633                 :            :     }
     634                 :            :     else
     635                 :            :     {
     636                 :            :       // Otherwise check if any subsequent premise eliminates it
     637         [ +  + ]:       1327 :       for (; i < children.size(); ++i)
     638                 :            :       {
     639                 :        653 :         posFirst = args[(2 * i) - 2] == trueNode;
     640                 :        653 :         pivot = args[(2 * i) - 1];
     641                 :            :         // To eliminate res, the clause must contain it with opposite
     642                 :            :         // polarity. There are three successful cases, according to the
     643                 :            :         // pivot and its sign
     644                 :            :         //
     645                 :            :         // - res is the same as the pivot and posFirst is true, which
     646                 :            :         // means that the clause contains its negation and eliminates it
     647                 :            :         //
     648                 :            :         // - res is the negation of the pivot and posFirst is false, so
     649                 :            :         // the clause contains the node whose negation is res. Note that
     650                 :            :         // this case may either be res.notNode() == pivot or res ==
     651                 :            :         // pivot.notNode().
     652 [ -  + ][ -  + ]:       1306 :         if ((res == pivot && posFirst) || (res.notNode() == pivot && !posFirst)
         [ -  - ][ +  + ]
                 [ -  - ]
     653 [ +  + ][ -  + ]:       1306 :             || (pivot.notNode() == res && !posFirst))
         [ -  - ][ +  + ]
         [ +  + ][ -  - ]
     654                 :            :         {
     655                 :          6 :           break;
     656                 :            :         }
     657                 :            :       }
     658                 :            :     }
     659                 :        680 :   }
     660                 :            :   // if not eliminated (loop went to the end), then it's a singleton
     661                 :            :   // clause
     662                 :      67047 :   return i == children.size();
     663                 :      67047 : }
     664                 :            : 
     665                 :            : }  // namespace proof
     666                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14