LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/preprocessing/passes - bv_gauss.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 343 383 89.6 %
Date: 2026-06-07 10:33:52 Functions: 8 8 100.0 %
Branches: 287 458 62.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                 :            :  * Gaussian Elimination preprocessing pass.
      11                 :            :  *
      12                 :            :  * Simplify a given equation system modulo a (prime) number via Gaussian
      13                 :            :  * Elimination if possible.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "preprocessing/passes/bv_gauss.h"
      17                 :            : 
      18                 :            : #include <unordered_map>
      19                 :            : #include <vector>
      20                 :            : 
      21                 :            : #include "expr/node.h"
      22                 :            : #include "preprocessing/assertion_pipeline.h"
      23                 :            : #include "preprocessing/preprocessing_pass_context.h"
      24                 :            : #include "theory/bv/theory_bv_rewrite_rules_normalization.h"
      25                 :            : #include "theory/bv/theory_bv_utils.h"
      26                 :            : #include "theory/rewriter.h"
      27                 :            : #include "util/bitvector.h"
      28                 :            : 
      29                 :            : using namespace cvc5::internal;
      30                 :            : using namespace cvc5::internal::theory;
      31                 :            : using namespace cvc5::internal::theory::bv;
      32                 :            : 
      33                 :            : namespace cvc5::internal {
      34                 :            : namespace preprocessing {
      35                 :            : namespace passes {
      36                 :            : 
      37                 :       2901 : bool BVGauss::is_bv_const(Node n)
      38                 :            : {
      39         [ +  + ]:       2901 :   if (n.isConst())
      40                 :            :   {
      41                 :        905 :     return true;
      42                 :            :   }
      43                 :       1996 :   return rewrite(n).getKind() == Kind::CONST_BITVECTOR;
      44                 :            : }
      45                 :            : 
      46                 :        451 : Node BVGauss::get_bv_const(Node n)
      47                 :            : {
      48 [ -  + ][ -  + ]:        451 :   Assert(is_bv_const(n));
                 [ -  - ]
      49                 :        451 :   return rewrite(n);
      50                 :            : }
      51                 :            : 
      52                 :        241 : Integer BVGauss::get_bv_const_value(Node n)
      53                 :            : {
      54 [ -  + ][ -  + ]:        241 :   Assert(is_bv_const(n));
                 [ -  - ]
      55                 :        482 :   return get_bv_const(n).getConst<BitVector>().getValue();
      56                 :            : }
      57                 :            : 
      58                 :            : /**
      59                 :            :  * Determines if an overflow may occur in given 'expr'.
      60                 :            :  *
      61                 :            :  * Returns 0 if an overflow may occur, and the minimum required
      62                 :            :  * bit-width such that no overflow occurs, otherwise.
      63                 :            :  *
      64                 :            :  * Note that it would suffice for this function to be Boolean.
      65                 :            :  * However, it is handy to determine the minimum required bit-width for
      66                 :            :  * debugging purposes.
      67                 :            :  *
      68                 :            :  * Note: getMinBwExpr assumes that 'expr' is rewritten.
      69                 :            :  *
      70                 :            :  * If not, all operators that are removed via rewriting (e.g., ror, rol, ...)
      71                 :            :  * will be handled via the default case, which is not incorrect but also not
      72                 :            :  * necessarily the minimum.
      73                 :            :  */
      74                 :        101 : uint32_t BVGauss::getMinBwExpr(Node expr)
      75                 :            : {
      76                 :        101 :   std::vector<Node> visit;
      77                 :            :   /* Maps visited nodes to the determined minimum bit-width required. */
      78                 :        101 :   std::unordered_map<Node, unsigned> visited;
      79                 :        101 :   std::unordered_map<Node, unsigned>::iterator it;
      80                 :            : 
      81                 :        101 :   visit.push_back(expr);
      82                 :        101 :   NodeManager* nm = nodeManager();
      83         [ +  + ]:       1459 :   while (!visit.empty())
      84                 :            :   {
      85                 :       1361 :     Node n = visit.back();
      86                 :       1361 :     visit.pop_back();
      87                 :       1361 :     it = visited.find(n);
      88         [ +  + ]:       1361 :     if (it == visited.end())
      89                 :            :     {
      90         [ +  + ]:        745 :       if (is_bv_const(n))
      91                 :            :       {
      92                 :            :         /* Rewrite const expr, overflows in consts are irrelevant. */
      93                 :        210 :         visited[n] = get_bv_const(n).getConst<BitVector>().getValue().length();
      94                 :            :       }
      95                 :            :       else
      96                 :            :       {
      97                 :        535 :         visited[n] = 0;
      98                 :        535 :         visit.push_back(n);
      99         [ +  + ]:       1263 :         for (const Node& nn : n)
     100                 :            :         {
     101                 :        728 :           visit.push_back(nn);
     102                 :        728 :         }
     103                 :            :       }
     104                 :            :     }
     105         [ +  + ]:        616 :     else if (it->second == 0)
     106                 :            :     {
     107                 :        533 :       Kind k = n.getKind();
     108 [ -  + ][ -  + ]:        533 :       Assert(k != Kind::CONST_BITVECTOR);
                 [ -  - ]
     109 [ -  + ][ -  + ]:        533 :       Assert(!is_bv_const(n));
                 [ -  - ]
     110 [ +  + ][ +  + ]:        533 :       switch (k)
         [ +  - ][ +  + ]
     111                 :            :       {
     112                 :         28 :         case Kind::BITVECTOR_EXTRACT:
     113                 :            :         {
     114                 :         28 :           const unsigned size = bv::utils::getSize(n);
     115                 :         28 :           const unsigned low = bv::utils::getExtractLow(n);
     116                 :         28 :           const unsigned child_min_width = visited[n[0]];
     117                 :         28 :           visited[n] = std::min(
     118         [ +  - ]:         28 :               size, child_min_width >= low ? child_min_width - low : 0u);
     119 [ -  + ][ -  + ]:         28 :           Assert(visited[n] <= visited[n[0]]);
                 [ -  - ]
     120                 :         28 :           break;
     121                 :            :         }
     122                 :            : 
     123                 :         36 :         case Kind::BITVECTOR_ZERO_EXTEND:
     124                 :            :         {
     125                 :         36 :           visited[n] = visited[n[0]];
     126                 :         36 :           break;
     127                 :            :         }
     128                 :            : 
     129                 :         69 :         case Kind::BITVECTOR_MULT:
     130                 :            :         {
     131                 :         69 :           Integer maxval = Integer(1);
     132         [ +  + ]:        213 :           for (const Node& nn : n)
     133                 :            :           {
     134         [ +  + ]:        144 :             if (is_bv_const(nn))
     135                 :            :             {
     136                 :         57 :               maxval *= get_bv_const_value(nn);
     137                 :            :             }
     138                 :            :             else
     139                 :            :             {
     140                 :         87 :               maxval *= BitVector::mkOnes(visited[nn]).getValue();
     141                 :            :             }
     142                 :        144 :           }
     143                 :         69 :           unsigned w = maxval.length();
     144         [ +  + ]:         69 :           if (w > bv::utils::getSize(n))
     145                 :            :           {
     146                 :          2 :             return 0;
     147                 :            :           } /* overflow */
     148                 :         67 :           visited[n] = w;
     149                 :         67 :           break;
     150         [ +  + ]:         69 :         }
     151                 :            : 
     152                 :        164 :         case Kind::BITVECTOR_CONCAT:
     153                 :            :         {
     154                 :            :           unsigned i, wnz, nc;
     155         [ +  + ]:        325 :           for (i = 0, wnz = 0, nc = n.getNumChildren() - 1; i < nc; ++i)
     156                 :            :           {
     157                 :        189 :             unsigned wni = bv::utils::getSize(n[i]);
     158         [ +  + ]:        189 :             if (n[i] != bv::utils::mkZero(nm, wni))
     159                 :            :             {
     160                 :         28 :               break;
     161                 :            :             }
     162                 :            :             /* sum of all bit-widths of leading zero concats */
     163                 :        161 :             wnz += wni;
     164                 :            :           }
     165                 :            :           /* Do not consider leading zero concats, i.e.,
     166                 :            :            * min bw of current concat is determined as
     167                 :            :            *   min bw of first non-zero term
     168                 :            :            *   plus actual bw of all subsequent terms */
     169                 :            :           // Use nSize to ensure deterministic node ID assignments
     170                 :        164 :           unsigned nSize = bv::utils::getSize(n);
     171                 :        164 :           visited[n] = nSize + visited[n[i]] - bv::utils::getSize(n[i]) - wnz;
     172                 :        164 :           break;
     173                 :            :         }
     174                 :            : 
     175                 :          3 :         case Kind::BITVECTOR_UREM:
     176                 :            :         case Kind::BITVECTOR_LSHR:
     177                 :            :         case Kind::BITVECTOR_ASHR:
     178                 :            :         {
     179                 :          3 :           visited[n] = visited[n[0]];
     180                 :          3 :           break;
     181                 :            :         }
     182                 :            : 
     183                 :          0 :         case Kind::BITVECTOR_OR:
     184                 :            :         case Kind::BITVECTOR_NOR:
     185                 :            :         case Kind::BITVECTOR_XOR:
     186                 :            :         case Kind::BITVECTOR_XNOR:
     187                 :            :         case Kind::BITVECTOR_AND:
     188                 :            :         case Kind::BITVECTOR_NAND:
     189                 :            :         {
     190                 :          0 :           unsigned wmax = 0;
     191         [ -  - ]:          0 :           for (const Node& nn : n)
     192                 :            :           {
     193         [ -  - ]:          0 :             if (visited[nn] > wmax)
     194                 :            :             {
     195                 :          0 :               wmax = visited[nn];
     196                 :            :             }
     197                 :          0 :           }
     198                 :          0 :           visited[n] = wmax;
     199                 :          0 :           break;
     200                 :            :         }
     201                 :            : 
     202                 :         52 :         case Kind::BITVECTOR_ADD:
     203                 :            :         {
     204                 :         52 :           Integer maxval = Integer(0);
     205         [ +  + ]:        182 :           for (const Node& nn : n)
     206                 :            :           {
     207         [ -  + ]:        130 :             if (is_bv_const(nn))
     208                 :            :             {
     209                 :          0 :               maxval += get_bv_const_value(nn);
     210                 :            :             }
     211                 :            :             else
     212                 :            :             {
     213                 :        130 :               maxval += BitVector::mkOnes(visited[nn]).getValue();
     214                 :            :             }
     215                 :        130 :           }
     216                 :         52 :           unsigned w = maxval.length();
     217         [ +  + ]:         52 :           if (w > bv::utils::getSize(n))
     218                 :            :           {
     219                 :          1 :             return 0;
     220                 :            :           } /* overflow */
     221                 :         51 :           visited[n] = w;
     222                 :         51 :           break;
     223         [ +  + ]:         52 :         }
     224                 :            : 
     225                 :        181 :         default:
     226                 :            :         {
     227                 :            :           /* BITVECTOR_UDIV (since x / 0 = -1)
     228                 :            :            * BITVECTOR_NOT
     229                 :            :            * BITVECTOR_NEG
     230                 :            :            * BITVECTOR_SHL */
     231                 :        181 :           visited[n] = bv::utils::getSize(n);
     232                 :            :         }
     233                 :            :       }
     234                 :            :     }
     235         [ +  + ]:       1361 :   }
     236 [ -  + ][ -  + ]:         98 :   Assert(visited.find(expr) != visited.end());
                 [ -  - ]
     237                 :         98 :   return visited[expr];
     238                 :        101 : }
     239                 :            : 
     240                 :            : /**
     241                 :            :  * Apply Gaussian Elimination modulo a (prime) number.
     242                 :            :  * The given equation system is represented as a matrix of Integers.
     243                 :            :  *
     244                 :            :  * Note that given 'prime' does not have to be prime but can be any
     245                 :            :  * arbitrary number. However, if 'prime' is indeed prime, GE is guaranteed
     246                 :            :  * to succeed, which is not the case, otherwise.
     247                 :            :  *
     248                 :            :  * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
     249                 :            :  * successful, and NONE, otherwise.
     250                 :            :  *
     251                 :            :  * Vectors 'rhs' and 'lhs' represent the right hand side and left hand side
     252                 :            :  * of the given matrix, respectively. The resulting matrix (in row echelon
     253                 :            :  * form) is stored in 'rhs' and 'lhs', i.e., the given matrix is overwritten
     254                 :            :  * with the resulting matrix.
     255                 :            :  */
     256                 :         70 : BVGauss::Result BVGauss::gaussElim(Integer prime,
     257                 :            :                                    std::vector<Integer>& rhs,
     258                 :            :                                    std::vector<std::vector<Integer>>& lhs)
     259                 :            : {
     260 [ -  + ][ -  + ]:         70 :   Assert(prime > 0);
                 [ -  - ]
     261 [ -  + ][ -  + ]:         70 :   Assert(lhs.size());
                 [ -  - ]
     262 [ -  + ][ -  + ]:         70 :   Assert(lhs.size() == rhs.size());
                 [ -  - ]
     263 [ -  + ][ -  + ]:         70 :   Assert(lhs.size() <= lhs[0].size());
                 [ -  - ]
     264                 :            : 
     265                 :            :   /* special case: zero ring */
     266         [ +  + ]:         70 :   if (prime == 1)
     267                 :            :   {
     268                 :          1 :     rhs = std::vector<Integer>(rhs.size(), Integer(0));
     269                 :          3 :     lhs = std::vector<std::vector<Integer>>(
     270                 :          3 :         lhs.size(), std::vector<Integer>(lhs[0].size(), Integer(0)));
     271                 :          1 :     return BVGauss::Result::UNIQUE;
     272                 :            :   }
     273                 :            : 
     274                 :         69 :   size_t nrows = lhs.size();
     275                 :         69 :   size_t ncols = lhs[0].size();
     276                 :            : 
     277                 :            : #ifdef CVC5_ASSERTIONS
     278 [ -  + ][ -  + ]:        195 :   for (size_t i = 1; i < nrows; ++i) Assert(lhs[i].size() == ncols);
         [ +  + ][ -  - ]
     279                 :            : #endif
     280                 :            :   /* (1) if element in pivot column is non-zero and != 1, divide row elements
     281                 :            :    *     by element in pivot column modulo prime, i.e., multiply row with
     282                 :            :    *     multiplicative inverse of element in pivot column modulo prime
     283                 :            :    *
     284                 :            :    * (2) subtract pivot row from all rows below pivot row
     285                 :            :    *
     286                 :            :    * (3) subtract (multiple of) current row from all rows above s.t. all
     287                 :            :    *     elements in current pivot column above current row become equal to one
     288                 :            :    *
     289                 :            :    * Note: we do not normalize the given matrix to values modulo prime
     290                 :            :    *       beforehand but on-the-fly. */
     291                 :            : 
     292                 :            :   /* pivot = lhs[pcol][pcol] */
     293 [ +  + ][ +  + ]:        234 :   for (size_t pcol = 0, prow = 0; pcol < ncols && prow < nrows; ++pcol, ++prow)
     294                 :            :   {
     295                 :            :     /* lhs[j][pcol]: element in pivot column */
     296         [ +  + ]:        510 :     for (size_t j = prow; j < nrows; ++j)
     297                 :            :     {
     298                 :            : #ifdef CVC5_ASSERTIONS
     299         [ +  + ]:        572 :       for (size_t k = 0; k < pcol; ++k)
     300                 :            :       {
     301 [ -  + ][ -  + ]:        227 :         Assert(lhs[j][k] == 0);
                 [ -  - ]
     302                 :            :       }
     303                 :            : #endif
     304                 :            :       /* normalize element in pivot column to modulo prime */
     305                 :        345 :       lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
     306                 :            :       /* exchange rows if pivot elem is 0 */
     307         [ +  + ]:        345 :       if (j == prow)
     308                 :            :       {
     309         [ +  + ]:        209 :         while (lhs[j][pcol] == 0)
     310                 :            :         {
     311         [ +  + ]:         85 :           for (size_t k = prow + 1; k < nrows; ++k)
     312                 :            :           {
     313                 :         53 :             lhs[k][pcol] = lhs[k][pcol].euclidianDivideRemainder(prime);
     314         [ +  + ]:         53 :             if (lhs[k][pcol] != 0)
     315                 :            :             {
     316                 :         27 :               std::swap(rhs[j], rhs[k]);
     317                 :         27 :               std::swap(lhs[j], lhs[k]);
     318                 :         27 :               break;
     319                 :            :             }
     320                 :            :           }
     321         [ +  + ]:         59 :           if (pcol >= ncols - 1) break;
     322         [ +  + ]:         38 :           if (lhs[j][pcol] == 0)
     323                 :            :           {
     324                 :         16 :             pcol += 1;
     325         [ +  + ]:         16 :             if (lhs[j][pcol] != 0)
     326                 :         10 :               lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
     327                 :            :           }
     328                 :            :         }
     329                 :            :       }
     330                 :            : 
     331         [ +  + ]:        345 :       if (lhs[j][pcol] != 0)
     332                 :            :       {
     333                 :            :         /* (1) */
     334         [ +  + ]:        264 :         if (lhs[j][pcol] != 1)
     335                 :            :         {
     336                 :        196 :           Integer inv = lhs[j][pcol].modInverse(prime);
     337         [ +  + ]:        196 :           if (inv == -1)
     338                 :            :           {
     339                 :          6 :             return BVGauss::Result::INVALID; /* not coprime */
     340                 :            :           }
     341         [ +  + ]:        626 :           for (size_t k = pcol; k < ncols; ++k)
     342                 :            :           {
     343                 :        436 :             lhs[j][k] = lhs[j][k].modMultiply(inv, prime);
     344         [ +  + ]:        436 :             if (j <= prow) continue; /* pivot */
     345                 :        246 :             lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
     346                 :            :           }
     347                 :        190 :           rhs[j] = rhs[j].modMultiply(inv, prime);
     348         [ +  + ]:        190 :           if (j > prow)
     349                 :            :           {
     350                 :         92 :             rhs[j] = rhs[j].modAdd(-rhs[prow], prime);
     351                 :            :           }
     352         [ +  + ]:        196 :         }
     353                 :            :         /* (2) */
     354         [ +  + ]:         68 :         else if (j != prow)
     355                 :            :         {
     356         [ +  + ]:         46 :           for (size_t k = pcol; k < ncols; ++k)
     357                 :            :           {
     358                 :         34 :             lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
     359                 :            :           }
     360                 :         12 :           rhs[j] = rhs[j].modAdd(-rhs[prow], prime);
     361                 :            :         }
     362                 :            :       }
     363                 :            :     }
     364                 :            :     /* (3) */
     365         [ +  + ]:        305 :     for (size_t j = 0; j < prow; ++j)
     366                 :            :     {
     367                 :        140 :       Integer mul = lhs[j][pcol];
     368         [ +  + ]:        140 :       if (mul != 0)
     369                 :            :       {
     370         [ +  + ]:        276 :         for (size_t k = pcol; k < ncols; ++k)
     371                 :            :         {
     372                 :        162 :           lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k] * mul, prime);
     373                 :            :         }
     374                 :        114 :         rhs[j] = rhs[j].modAdd(-rhs[prow] * mul, prime);
     375                 :            :       }
     376                 :        140 :     }
     377                 :            :   }
     378                 :            : 
     379                 :         63 :   bool ispart = false;
     380         [ +  + ]:        236 :   for (size_t i = 0; i < nrows; ++i)
     381                 :            :   {
     382                 :        177 :     size_t pcol = i;
     383 [ +  + ][ +  + ]:        229 :     while (pcol < ncols && lhs[i][pcol] == 0) ++pcol;
         [ +  + ][ +  + ]
                 [ -  - ]
     384         [ +  + ]:        177 :     if (pcol >= ncols)
     385                 :            :     {
     386                 :         29 :       rhs[i] = rhs[i].euclidianDivideRemainder(prime);
     387         [ +  + ]:         29 :       if (rhs[i] != 0)
     388                 :            :       {
     389                 :            :         /* no solution */
     390                 :          4 :         return BVGauss::Result::NONE;
     391                 :            :       }
     392                 :         25 :       continue;
     393                 :            :     }
     394         [ +  + ]:        488 :     for (size_t j = i; j < ncols; ++j)
     395                 :            :     {
     396 [ +  + ][ -  + ]:        340 :       if (lhs[i][j] >= prime || lhs[i][j] <= -prime)
         [ +  + ][ +  + ]
                 [ -  - ]
     397                 :            :       {
     398                 :          1 :         lhs[i][j] = lhs[i][j].euclidianDivideRemainder(prime);
     399                 :            :       }
     400 [ +  + ][ +  + ]:        340 :       if (j > pcol && lhs[i][j] != 0)
         [ +  + ][ +  + ]
                 [ -  - ]
     401                 :            :       {
     402                 :         36 :         ispart = true;
     403                 :            :       }
     404                 :            :     }
     405                 :            :   }
     406                 :            : 
     407         [ +  + ]:         59 :   if (ispart)
     408                 :            :   {
     409                 :         21 :     return BVGauss::Result::PARTIAL;
     410                 :            :   }
     411                 :            : 
     412                 :         38 :   return BVGauss::Result::UNIQUE;
     413                 :            : }
     414                 :            : 
     415                 :            : /**
     416                 :            :  * Apply Gaussian Elimination on a set of equations modulo some (prime)
     417                 :            :  * number given as bit-vector equations.
     418                 :            :  *
     419                 :            :  * IMPORTANT: Applying GE modulo some number (rather than modulo 2^bw)
     420                 :            :  * on a set of bit-vector equations is only sound if this set of equations
     421                 :            :  * has a solution that does not produce overflows. Consequently, we only
     422                 :            :  * apply GE if the given bit-width guarantees that no overflows can occur
     423                 :            :  * in the given set of equations.
     424                 :            :  *
     425                 :            :  * Note that the given set of equations does not have to be modulo a prime
     426                 :            :  * but can be modulo any arbitrary number. However, if it is indeed modulo
     427                 :            :  * prime, GE is guaranteed to succeed, which is not the case, otherwise.
     428                 :            :  *
     429                 :            :  * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
     430                 :            :  * successful, and NONE, otherwise.
     431                 :            :  *
     432                 :            :  * The resulting constraints are stored in 'res' as a mapping of unknown
     433                 :            :  * to result (modulo prime). These mapped results are added as constraints
     434                 :            :  * of the form 'unknown = mapped result' in applyInternal.
     435                 :            :  */
     436                 :         18 : BVGauss::Result BVGauss::gaussElimRewriteForUrem(
     437                 :            :     const std::vector<Node>& equations, std::unordered_map<Node, Node>& res)
     438                 :            : {
     439 [ -  + ][ -  + ]:         18 :   Assert(res.empty());
                 [ -  - ]
     440                 :            : 
     441                 :         18 :   Node prime;
     442                 :         18 :   Integer iprime;
     443                 :         18 :   std::unordered_map<Node, std::vector<Integer>> vars;
     444                 :         18 :   size_t neqs = equations.size();
     445                 :         18 :   std::vector<Integer> rhs;
     446                 :            :   std::vector<std::vector<Integer>> lhs =
     447                 :         36 :       std::vector<std::vector<Integer>>(neqs, std::vector<Integer>());
     448                 :            : 
     449                 :         18 :   res = std::unordered_map<Node, Node>();
     450                 :            : 
     451                 :         18 :   NodeManager* nm = nodeManager();
     452         [ +  + ]:         64 :   for (size_t i = 0; i < neqs; ++i)
     453                 :            :   {
     454                 :         46 :     Node eq = equations[i];
     455 [ -  + ][ -  + ]:         46 :     Assert(eq.getKind() == Kind::EQUAL);
                 [ -  - ]
     456                 :         46 :     Node urem, eqrhs;
     457                 :            : 
     458         [ +  - ]:         46 :     if (eq[0].getKind() == Kind::BITVECTOR_UREM)
     459                 :            :     {
     460                 :         46 :       urem = eq[0];
     461 [ -  + ][ -  + ]:         46 :       Assert(is_bv_const(eq[1]));
                 [ -  - ]
     462                 :         46 :       eqrhs = eq[1];
     463                 :            :     }
     464                 :            :     else
     465                 :            :     {
     466                 :          0 :       Assert(eq[1].getKind() == Kind::BITVECTOR_UREM);
     467                 :          0 :       urem = eq[1];
     468                 :          0 :       Assert(is_bv_const(eq[0]));
     469                 :          0 :       eqrhs = eq[0];
     470                 :            :     }
     471         [ -  + ]:         46 :     if (getMinBwExpr(rewrite(urem[0])) == 0)
     472                 :            :     {
     473         [ -  - ]:          0 :       Trace("bv-gauss-elim")
     474                 :            :           << "Minimum required bit-width exceeds given bit-width, "
     475                 :          0 :              "will not apply Gaussian Elimination."
     476                 :          0 :           << std::endl;
     477                 :          0 :       return BVGauss::Result::INVALID;
     478                 :            :     }
     479                 :         46 :     rhs.push_back(get_bv_const_value(eqrhs));
     480                 :            : 
     481 [ -  + ][ -  + ]:         46 :     Assert(is_bv_const(urem[1]));
                 [ -  - ]
     482                 :         46 :     Assert(i == 0 || get_bv_const_value(urem[1]) == iprime);
     483         [ +  + ]:         46 :     if (i == 0)
     484                 :            :     {
     485                 :         18 :       prime = urem[1];
     486                 :         18 :       iprime = get_bv_const_value(prime);
     487                 :            :     }
     488                 :            : 
     489                 :         46 :     std::unordered_map<Node, Integer> tmp;
     490                 :         46 :     std::vector<Node> stack;
     491                 :         46 :     stack.push_back(urem[0]);
     492         [ +  + ]:        198 :     while (!stack.empty())
     493                 :            :     {
     494                 :        152 :       Node n = stack.back();
     495                 :        152 :       stack.pop_back();
     496                 :            : 
     497                 :            :       /* Subtract from rhs if const */
     498         [ -  + ]:        152 :       if (is_bv_const(n))
     499                 :            :       {
     500                 :          0 :         Integer val = get_bv_const_value(n);
     501         [ -  - ]:          0 :         if (val > 0) rhs.back() -= val;
     502                 :          0 :         continue;
     503                 :          0 :       }
     504                 :            : 
     505                 :            :       /* Split into matrix columns */
     506                 :        152 :       Kind k = n.getKind();
     507         [ +  + ]:        152 :       if (k == Kind::BITVECTOR_ADD)
     508                 :            :       {
     509         [ +  + ]:        159 :         for (const Node& nn : n)
     510                 :            :         {
     511                 :        106 :           stack.push_back(nn);
     512                 :        106 :         }
     513                 :            :       }
     514         [ +  + ]:         99 :       else if (k == Kind::BITVECTOR_MULT)
     515                 :            :       {
     516                 :         92 :         Node n0, n1;
     517                 :            :         /* Flatten mult expression. */
     518                 :         92 :         n = RewriteRule<FlattenAssocCommut>::run<true>(n);
     519                 :            :         /* Split operands into consts and non-consts */
     520                 :         92 :         NodeBuilder nb_consts(nm, k);
     521                 :         92 :         NodeBuilder nb_nonconsts(nm, k);
     522         [ +  + ]:        284 :         for (const Node& nn : n)
     523                 :            :         {
     524                 :        192 :           Node nnrw = rewrite(nn);
     525         [ +  + ]:        192 :           if (is_bv_const(nnrw))
     526                 :            :           {
     527                 :         88 :             nb_consts << nnrw;
     528                 :            :           }
     529                 :            :           else
     530                 :            :           {
     531                 :        104 :             nb_nonconsts << nnrw;
     532                 :            :           }
     533                 :        192 :         }
     534 [ -  + ][ -  + ]:         92 :         Assert(nb_nonconsts.getNumChildren() > 0);
                 [ -  - ]
     535                 :            :         /* n0 is const */
     536                 :         92 :         unsigned nc = nb_consts.getNumChildren();
     537         [ -  + ]:         92 :         if (nc > 1)
     538                 :            :         {
     539                 :          0 :           n0 = rewrite(nb_consts.constructNode());
     540                 :            :         }
     541         [ +  + ]:         92 :         else if (nc == 1)
     542                 :            :         {
     543                 :         88 :           n0 = nb_consts[0];
     544                 :            :         }
     545                 :            :         else
     546                 :            :         {
     547                 :          4 :           n0 = bv::utils::mkOne(nm, bv::utils::getSize(n));
     548                 :            :         }
     549                 :            :         /* n1 is a mult with non-const operands */
     550         [ +  + ]:         92 :         if (nb_nonconsts.getNumChildren() > 1)
     551                 :            :         {
     552                 :         10 :           n1 = rewrite(nb_nonconsts.constructNode());
     553                 :            :         }
     554                 :            :         else
     555                 :            :         {
     556                 :         82 :           n1 = nb_nonconsts[0];
     557                 :            :         }
     558 [ -  + ][ -  + ]:         92 :         Assert(is_bv_const(n0));
                 [ -  - ]
     559 [ -  + ][ -  + ]:         92 :         Assert(!is_bv_const(n1));
                 [ -  - ]
     560                 :         92 :         tmp[n1] += get_bv_const_value(n0);
     561                 :         92 :       }
     562                 :            :       else
     563                 :            :       {
     564                 :          7 :         tmp[n] += Integer(1);
     565                 :            :       }
     566         [ +  - ]:        152 :     }
     567                 :            : 
     568                 :            :     /* Note: "var" is not necessarily a VARIABLE but can be an arbitrary expr */
     569                 :            : 
     570         [ +  + ]:        145 :     for (const auto& p : tmp)
     571                 :            :     {
     572                 :         99 :       Node var = p.first;
     573                 :         99 :       Integer val = p.second;
     574 [ +  + ][ +  + ]:         99 :       if (i > 0 && vars.find(var) == vars.end())
                 [ +  + ]
     575                 :            :       {
     576                 :            :         /* Add column and fill column elements of rows above with 0. */
     577                 :         14 :         vars[var].insert(vars[var].end(), i, Integer(0));
     578                 :            :       }
     579                 :         99 :       vars[var].push_back(val);
     580                 :         99 :     }
     581                 :            : 
     582         [ +  + ]:        166 :     for (const auto& p : vars)
     583                 :            :     {
     584         [ +  + ]:        120 :       if (tmp.find(p.first) == tmp.end())
     585                 :            :       {
     586                 :         21 :         vars[p.first].push_back(Integer(0));
     587                 :            :       }
     588                 :            :     }
     589 [ +  - ][ +  - ]:         46 :   }
                 [ +  - ]
     590                 :            : 
     591                 :         18 :   size_t nvars = vars.size();
     592         [ -  + ]:         18 :   if (nvars == 0)
     593                 :            :   {
     594                 :          0 :     return BVGauss::Result::INVALID;
     595                 :            :   }
     596                 :         18 :   size_t nrows = vars.begin()->second.size();
     597                 :            : #ifdef CVC5_ASSERTIONS
     598         [ +  + ]:         71 :   for (const auto& p : vars)
     599                 :            :   {
     600 [ -  + ][ -  + ]:         53 :     Assert(p.second.size() == nrows);
                 [ -  - ]
     601                 :            :   }
     602                 :            : #endif
     603                 :            : 
     604         [ -  + ]:         18 :   if (nrows < 1)
     605                 :            :   {
     606                 :          0 :     return BVGauss::Result::INVALID;
     607                 :            :   }
     608                 :            : 
     609         [ +  + ]:         64 :   for (size_t i = 0; i < nrows; ++i)
     610                 :            :   {
     611         [ +  + ]:        182 :     for (const auto& p : vars)
     612                 :            :     {
     613                 :        136 :       lhs[i].push_back(p.second[i]);
     614                 :            :     }
     615                 :            :   }
     616                 :            : 
     617                 :            : #ifdef CVC5_ASSERTIONS
     618         [ +  + ]:         64 :   for (const auto& row : lhs)
     619                 :            :   {
     620 [ -  + ][ -  + ]:         46 :     Assert(row.size() == nvars);
                 [ -  - ]
     621                 :            :   }
     622 [ -  + ][ -  + ]:         18 :   Assert(lhs.size() == rhs.size());
                 [ -  - ]
     623                 :            : #endif
     624                 :            : 
     625         [ +  + ]:         18 :   if (lhs.size() > lhs[0].size())
     626                 :            :   {
     627                 :          1 :     return BVGauss::Result::INVALID;
     628                 :            :   }
     629                 :            : 
     630         [ +  - ]:         17 :   Trace("bv-gauss-elim") << "Applying Gaussian Elimination..." << std::endl;
     631                 :         17 :   BVGauss::Result ret = gaussElim(iprime, rhs, lhs);
     632                 :            : 
     633 [ +  - ][ +  - ]:         17 :   if (ret != BVGauss::Result::NONE && ret != BVGauss::Result::INVALID)
     634                 :            :   {
     635                 :         17 :     std::vector<Node> vvars;
     636         [ +  + ]:         68 :     for (const auto& p : vars)
     637                 :            :     {
     638                 :         51 :       vvars.push_back(p.first);
     639                 :            :     }
     640 [ -  + ][ -  + ]:         17 :     Assert(nvars == vvars.size());
                 [ -  - ]
     641 [ -  + ][ -  + ]:         17 :     Assert(nrows == lhs.size());
                 [ -  - ]
     642 [ -  + ][ -  + ]:         17 :     Assert(nrows == rhs.size());
                 [ -  - ]
     643         [ +  + ]:         17 :     if (ret == BVGauss::Result::UNIQUE)
     644                 :            :     {
     645         [ +  + ]:         27 :       for (size_t i = 0; i < nvars; ++i)
     646                 :            :       {
     647                 :         40 :         res[vvars[i]] = nm->mkConst<BitVector>(
     648                 :         60 :             BitVector(bv::utils::getSize(vvars[i]), rhs[i]));
     649                 :            :       }
     650                 :            :     }
     651                 :            :     else
     652                 :            :     {
     653 [ -  + ][ -  + ]:         10 :       Assert(ret == BVGauss::Result::PARTIAL);
                 [ -  - ]
     654                 :            : 
     655 [ +  - ][ +  + ]:         30 :       for (size_t pcol = 0, prow = 0; pcol < nvars && prow < nrows;
     656                 :         20 :            ++pcol, ++prow)
     657                 :            :       {
     658                 :         22 :         Assert(lhs[prow][pcol] == 0 || lhs[prow][pcol] == 1);
     659 [ +  + ][ +  + ]:         25 :         while (pcol < nvars && lhs[prow][pcol] == 0) pcol += 1;
         [ +  + ][ +  + ]
                 [ -  - ]
     660         [ +  + ]:         22 :         if (pcol >= nvars)
     661                 :            :         {
     662 [ -  + ][ -  + ]:          2 :           Assert(rhs[prow] == 0);
                 [ -  - ]
     663                 :          2 :           break;
     664                 :            :         }
     665         [ -  + ]:         20 :         if (lhs[prow][pcol] == 0)
     666                 :            :         {
     667                 :          0 :           Assert(rhs[prow] == 0);
     668                 :          0 :           continue;
     669                 :            :         }
     670 [ -  + ][ -  + ]:         20 :         Assert(lhs[prow][pcol] == 1);
                 [ -  - ]
     671                 :         20 :         std::vector<Node> stack;
     672         [ +  + ]:         51 :         for (size_t i = pcol + 1; i < nvars; ++i)
     673                 :            :         {
     674         [ +  + ]:         31 :           if (lhs[prow][i] == 0) continue;
     675                 :            :           /* Normalize (no negative numbers, hence no subtraction)
     676                 :            :            * e.g., x = 4 - 2y  --> x = 4 + 9y (modulo 11) */
     677                 :         17 :           Integer m = iprime - lhs[prow][i];
     678                 :         17 :           Node bv = bv::utils::mkConst(nm, bv::utils::getSize(vvars[i]), m);
     679                 :         34 :           Node mult = nm->mkNode(Kind::BITVECTOR_MULT, vvars[i], bv);
     680                 :         17 :           stack.push_back(mult);
     681                 :         17 :         }
     682                 :            : 
     683         [ +  + ]:         20 :         if (stack.empty())
     684                 :            :         {
     685                 :          6 :           res[vvars[pcol]] = nm->mkConst<BitVector>(
     686                 :          9 :               BitVector(bv::utils::getSize(vvars[pcol]), rhs[prow]));
     687                 :            :         }
     688                 :            :         else
     689                 :            :         {
     690                 :         34 :           Node tmp = stack.size() == 1 ? stack[0]
     691         [ +  - ]:         34 :                                        : nm->mkNode(Kind::BITVECTOR_ADD, stack);
     692                 :            : 
     693         [ +  + ]:         17 :           if (rhs[prow] != 0)
     694                 :            :           {
     695                 :            :             tmp =
     696                 :         64 :                 nm->mkNode(Kind::BITVECTOR_ADD,
     697                 :         32 :                            bv::utils::mkConst(
     698                 :         16 :                                nm, bv::utils::getSize(vvars[pcol]), rhs[prow]),
     699                 :         16 :                            tmp);
     700                 :            :           }
     701 [ -  + ][ -  + ]:         17 :           Assert(!is_bv_const(tmp));
                 [ -  - ]
     702                 :         17 :           res[vvars[pcol]] = nm->mkNode(Kind::BITVECTOR_UREM, tmp, prime);
     703                 :         17 :         }
     704                 :         20 :       }
     705                 :            :     }
     706                 :         17 :   }
     707                 :         17 :   return ret;
     708                 :         18 : }
     709                 :            : 
     710                 :      51970 : BVGauss::BVGauss(PreprocessingPassContext* preprocContext,
     711                 :      51970 :                  const std::string& name)
     712                 :      51970 :     : PreprocessingPass(preprocContext, name)
     713                 :            : {
     714                 :      51970 : }
     715                 :            : 
     716                 :          3 : PreprocessingPassResult BVGauss::applyInternal(
     717                 :            :     AssertionPipeline* assertionsToPreprocess)
     718                 :            : {
     719                 :          3 :   std::vector<Node> assertions(assertionsToPreprocess->ref());
     720                 :          3 :   std::unordered_map<Node, std::vector<Node>> equations;
     721                 :            : 
     722         [ +  + ]:         13 :   while (!assertions.empty())
     723                 :            :   {
     724                 :         10 :     Node a = assertions.back();
     725                 :         10 :     assertions.pop_back();
     726                 :         10 :     cvc5::internal::Kind k = a.getKind();
     727                 :            : 
     728         [ -  + ]:         10 :     if (k == Kind::AND)
     729                 :            :     {
     730         [ -  - ]:          0 :       for (const Node& aa : a)
     731                 :            :       {
     732                 :          0 :         assertions.push_back(aa);
     733                 :          0 :       }
     734                 :            :     }
     735         [ +  - ]:         10 :     else if (k == Kind::EQUAL)
     736                 :            :     {
     737                 :         10 :       Node urem;
     738                 :            : 
     739                 :         10 :       if (is_bv_const(a[1]) && a[0].getKind() == Kind::BITVECTOR_UREM)
     740                 :            :       {
     741                 :         10 :         urem = a[0];
     742                 :            :       }
     743                 :          0 :       else if (is_bv_const(a[0]) && a[1].getKind() == Kind::BITVECTOR_UREM)
     744                 :            :       {
     745                 :          0 :         urem = a[1];
     746                 :            :       }
     747                 :            :       else
     748                 :            :       {
     749                 :          0 :         continue;
     750                 :            :       }
     751                 :            : 
     752                 :         10 :       if (urem[0].getKind() == Kind::BITVECTOR_ADD && is_bv_const(urem[1]))
     753                 :            :       {
     754                 :         10 :         equations[urem[1]].push_back(a);
     755                 :            :       }
     756         [ +  - ]:         10 :     }
     757         [ +  - ]:         10 :   }
     758                 :            : 
     759                 :          3 :   std::unordered_map<Node, Node> subst;
     760                 :            : 
     761                 :          3 :   NodeManager* nm = nodeManager();
     762         [ +  + ]:          7 :   for (const auto& eq : equations)
     763                 :            :   {
     764         [ -  + ]:          4 :     if (eq.second.size() <= 1)
     765                 :            :     {
     766                 :          0 :       continue;
     767                 :            :     }
     768                 :            : 
     769                 :          4 :     std::unordered_map<Node, Node> res;
     770                 :          4 :     BVGauss::Result ret = gaussElimRewriteForUrem(eq.second, res);
     771         [ +  - ]:          8 :     Trace("bv-gauss-elim") << "result: "
     772                 :            :                            << (ret == BVGauss::Result::INVALID
     773         [ -  - ]:          4 :                                    ? "INVALID"
     774                 :            :                                    : (ret == BVGauss::Result::UNIQUE
     775         [ -  - ]:          0 :                                           ? "UNIQUE"
     776                 :            :                                           : (ret == BVGauss::Result::PARTIAL
     777         [ -  - ]:          0 :                                                  ? "PARTIAL"
     778                 :          0 :                                                  : "NONE")))
     779                 :          4 :                            << std::endl;
     780         [ +  - ]:          4 :     if (ret != BVGauss::Result::INVALID)
     781                 :            :     {
     782         [ -  + ]:          4 :       if (ret == BVGauss::Result::NONE)
     783                 :            :       {
     784                 :          0 :         Node n = nm->mkConst<bool>(false);
     785                 :          0 :         assertionsToPreprocess->push_back(
     786                 :            :             n, false, nullptr, TrustId::PREPROCESS_BV_GUASS_LEMMA);
     787                 :          0 :         return PreprocessingPassResult::CONFLICT;
     788                 :          0 :       }
     789                 :            :       else
     790                 :            :       {
     791         [ +  + ]:         14 :         for (const Node& e : eq.second)
     792                 :            :         {
     793                 :         10 :           subst[e] = nm->mkConst<bool>(true);
     794                 :            :         }
     795                 :            :         /* add resulting constraints */
     796         [ +  + ]:         14 :         for (const auto& p : res)
     797                 :            :         {
     798                 :         20 :           Node a = nm->mkNode(Kind::EQUAL, p.first, p.second);
     799         [ +  - ]:         10 :           Trace("bv-gauss-elim") << "added assertion: " << a << std::endl;
     800                 :            :           // add new assertion
     801                 :         10 :           assertionsToPreprocess->push_back(
     802                 :            :               a, false, nullptr, TrustId::PREPROCESS_BV_GUASS_LEMMA);
     803                 :         10 :         }
     804                 :            :       }
     805                 :            :     }
     806         [ +  - ]:          4 :   }
     807                 :            : 
     808         [ +  - ]:          3 :   if (!subst.empty())
     809                 :            :   {
     810                 :            :     /* delete (= substitute with true) obsolete assertions */
     811                 :          3 :     const std::vector<Node>& aref = assertionsToPreprocess->ref();
     812         [ +  + ]:         23 :     for (size_t i = 0, asize = aref.size(); i < asize; ++i)
     813                 :            :     {
     814                 :         20 :       Node a = aref[i];
     815                 :         20 :       Node as = a.substitute(subst.begin(), subst.end());
     816                 :            :       // replace the assertion
     817                 :         20 :       assertionsToPreprocess->replace(
     818                 :            :           i, as, nullptr, TrustId::PREPROCESS_BV_GUASS);
     819                 :         20 :     }
     820                 :            :   }
     821                 :          3 :   return PreprocessingPassResult::NO_CONFLICT;
     822                 :          3 : }
     823                 :            : 
     824                 :            : }  // namespace passes
     825                 :            : }  // namespace preprocessing
     826                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14