LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/arith/nl - iand_utils.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 101 107 94.4 %
Date: 2026-04-27 10:45:38 Functions: 12 13 92.3 %
Branches: 58 102 56.9 %

           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 to maintain finite tables that represent the value of iand.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/arith/nl/iand_utils.h"
      14                 :            : 
      15                 :            : #include <cmath>
      16                 :            : #include <limits>
      17                 :            : 
      18                 :            : #include "cvc5_private.h"
      19                 :            : #include "theory/arith/nl/nl_model.h"
      20                 :            : #include "theory/rewriter.h"
      21                 :            : #include "util/rational.h"
      22                 :            : 
      23                 :            : using namespace cvc5::internal::kind;
      24                 :            : 
      25                 :            : namespace cvc5::internal {
      26                 :            : namespace theory {
      27                 :            : namespace arith {
      28                 :            : namespace nl {
      29                 :            : 
      30                 :        470 : static Rational intpow2(uint32_t b)
      31                 :            : {
      32                 :            :   // b must be <= max-int to prevent a failure when using gmp.
      33 [ -  + ][ -  + ]:        470 :   Assert(b <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
                 [ -  - ]
      34                 :        940 :   return Rational(Integer(2).pow(b), Integer(1));
      35                 :            : }
      36                 :            : 
      37                 :        470 : Node pow2(NodeManager* nm, uint32_t k)
      38                 :            : {
      39                 :        940 :   return nm->mkConstInt(Rational(intpow2(k)));
      40                 :            : }
      41                 :            : 
      42 [ +  + ][ +  + ]:       6372 : bool oneBitAnd(bool a, bool b) { return (a && b); }
      43                 :            : 
      44                 :            : // computes (bv_to_int ((_ extract i+size-1 i) (int_to_bv x))))
      45                 :        188 : Node intExtract(Node x, uint32_t i, uint32_t size)
      46                 :            : {
      47                 :        188 :   NodeManager* nm = x.getNodeManager();
      48 [ -  + ][ -  + ]:        188 :   Assert(size > 0);
                 [ -  - ]
      49                 :            :   // extract definition in integers is:
      50                 :            :   // (mod (div a (two_to_the j)) (two_to_the (+ (- i j) 1))))
      51                 :        564 :   Node extract = NodeManager::mkNode(
      52                 :            :       Kind::INTS_MODULUS_TOTAL,
      53                 :        376 :       {NodeManager::mkNode(Kind::INTS_DIVISION_TOTAL, x, pow2(nm, i * size)),
      54                 :        376 :        pow2(nm, size)});
      55                 :        188 :   return extract;
      56                 :            : }
      57                 :            : 
      58                 :     119425 : IAndUtils::IAndUtils(NodeManager* nm) : d_nm(nm)
      59                 :            : {
      60                 :     119425 :   d_zero = nm->mkConstInt(Rational(0));
      61                 :     119425 :   d_one = nm->mkConstInt(Rational(1));
      62                 :     119425 :   d_two = nm->mkConstInt(Rational(2));
      63                 :     119425 : }
      64                 :            : 
      65                 :        161 : Node IAndUtils::createITEFromTable(
      66                 :            :     Node x,
      67                 :            :     Node y,
      68                 :            :     uint32_t granularity,
      69                 :            :     const std::map<std::pair<int64_t, int64_t>, uint64_t>& table)
      70                 :            : {
      71 [ -  + ][ -  + ]:        161 :   Assert(granularity <= 8);
                 [ -  - ]
      72                 :        161 :   uint64_t num_of_values = ((uint64_t)pow(2, granularity));
      73                 :            :   // The table represents a function from pairs of integers to integers, where
      74                 :            :   // all integers are between 0 (inclusive) and num_of_values (exclusive).
      75                 :            :   // additionally, there is a default value (-1, -1).
      76 [ -  + ][ -  + ]:        161 :   Assert(table.size() == 1 + ((uint64_t)(num_of_values * num_of_values)));
                 [ -  - ]
      77                 :            :   // start with the default, most common value.
      78                 :            :   // this value is represented in the table by (-1, -1).
      79                 :        161 :   Node ite = d_nm->mkConstInt(Rational(table.at(std::make_pair(-1, -1))));
      80         [ +  + ]:        571 :   for (uint64_t i = 0; i < num_of_values; i++)
      81                 :            :   {
      82         [ +  + ]:       2590 :     for (uint64_t j = 0; j < num_of_values; j++)
      83                 :            :     {
      84                 :            :       // skip the most common value, as it was already stored.
      85         [ +  + ]:       2180 :       if (table.at(std::make_pair(i, j)) == table.at(std::make_pair(-1, -1)))
      86                 :            :       {
      87                 :        963 :         continue;
      88                 :            :       }
      89                 :            :       // append the current value to the ite.
      90 [ +  + ][ -  - ]:       7302 :       ite = NodeManager::mkNode(
      91                 :            :           Kind::ITE,
      92 [ +  + ][ -  - ]:       6085 :           {NodeManager::mkNode(
      93                 :            :                Kind::AND,
      94                 :       2434 :                {NodeManager::mkNode(
      95                 :       2434 :                     Kind::EQUAL, x, d_nm->mkConstInt(Rational(i))),
      96                 :       2434 :                 NodeManager::mkNode(
      97                 :       2434 :                     Kind::EQUAL, y, d_nm->mkConstInt(Rational(j)))}),
      98                 :       2434 :            d_nm->mkConstInt(Rational(table.at(std::make_pair(i, j)))),
      99                 :       1217 :            ite});
     100                 :            :     }
     101                 :            :   }
     102                 :        161 :   return ite;
     103                 :          0 : }
     104                 :            : 
     105                 :         27 : Node IAndUtils::createSumNode(Node x,
     106                 :            :                               Node y,
     107                 :            :                               uint32_t bvsize,
     108                 :            :                               uint32_t granularity)
     109                 :            : {
     110 [ +  - ][ +  - ]:         27 :   Assert(0 < granularity && granularity <= 8);
         [ -  + ][ -  + ]
                 [ -  - ]
     111                 :            :   // Standardize granularity.
     112                 :            :   // If it is greater than bvsize, it is set to bvsize.
     113                 :            :   // Otherwise, it is set to the closest (going down)  divider of bvsize.
     114         [ -  + ]:         27 :   if (granularity > bvsize)
     115                 :            :   {
     116                 :          0 :     granularity = bvsize;
     117                 :            :   }
     118                 :            :   else
     119                 :            :   {
     120         [ -  + ]:         27 :     while (bvsize % granularity != 0)
     121                 :            :     {
     122                 :          0 :       granularity = granularity - 1;
     123                 :            :     }
     124                 :            :   }
     125                 :            : 
     126                 :            :   // Create the sum.
     127                 :            :   // For granularity 1, the sum has bvsize elements.
     128                 :            :   // In contrast, if bvsize = granularity, sum has one element.
     129                 :            :   // Each element in the sum is an ite that corresponds to the generated table,
     130                 :            :   // multiplied by the appropriate power of two.
     131                 :            :   // More details are in bv_to_int.h .
     132                 :            : 
     133                 :            :   // number of elements in the sum expression
     134                 :         27 :   uint32_t sumSize = bvsize / granularity;
     135                 :            :   // initialize the sum
     136                 :         27 :   Node sumNode = d_nm->mkConstInt(Rational(0));
     137                 :            :   // compute the table for the current granularity if needed
     138         [ +  + ]:         27 :   if (d_bvandTable.find(granularity) == d_bvandTable.end())
     139                 :            :   {
     140                 :         20 :     computeAndTable(granularity);
     141                 :            :   }
     142                 :            :   const std::map<std::pair<int64_t, int64_t>, uint64_t>& table =
     143                 :         27 :       d_bvandTable[granularity];
     144         [ +  + ]:        121 :   for (uint32_t i = 0; i < sumSize; i++)
     145                 :            :   {
     146                 :            :     // compute the current blocks of x and y
     147                 :         94 :     Node xExtract = intExtract(x, i, granularity);
     148                 :         94 :     Node yExtract = intExtract(y, i, granularity);
     149                 :            :     // compute the ite for this part
     150                 :        188 :     Node sumPart = createITEFromTable(xExtract, yExtract, granularity, table);
     151                 :            :     // append the current block to the sum
     152                 :        188 :     sumNode = NodeManager::mkNode(
     153                 :            :         Kind::ADD,
     154                 :            :         sumNode,
     155                 :        282 :         NodeManager::mkNode(Kind::MULT, pow2(d_nm, i * granularity), sumPart));
     156                 :         94 :   }
     157                 :         27 :   return sumNode;
     158                 :          0 : }
     159                 :            : 
     160                 :         67 : Node IAndUtils::createBitwiseIAndNode(Node x,
     161                 :            :                                       Node y,
     162                 :            :                                       uint32_t high,
     163                 :            :                                       uint32_t low)
     164                 :            : {
     165                 :         67 :   uint32_t granularity = high - low + 1;
     166 [ -  + ][ -  + ]:         67 :   Assert(granularity <= 8);
                 [ -  - ]
     167                 :            :   // compute the table for the current granularity if needed
     168         [ +  + ]:         67 :   if (d_bvandTable.find(granularity) == d_bvandTable.end())
     169                 :            :   {
     170                 :         29 :     computeAndTable(granularity);
     171                 :            :   }
     172                 :            :   const std::map<std::pair<int64_t, int64_t>, uint64_t>& table =
     173                 :         67 :       d_bvandTable[granularity];
     174                 :            :   // Use iextractX and iextractY to ensure deterministic node ID assignments
     175                 :         67 :   Node iextractX = iextract(high, low, x);
     176                 :         67 :   Node iextractY = iextract(high, low, y);
     177                 :        134 :   return createITEFromTable(iextractX, iextractY, granularity, table);
     178                 :         67 : }
     179                 :            : 
     180                 :        201 : Node IAndUtils::iextract(uint32_t i, uint32_t j, Node n) const
     181                 :            : {
     182                 :            :   //  ((_ extract i j) n) is n / 2^j mod 2^{i-j+1}
     183                 :        402 :   Node n2j = NodeManager::mkNode(Kind::INTS_DIVISION_TOTAL, n, twoToK(j));
     184                 :        402 :   return NodeManager::mkNode(Kind::INTS_MODULUS_TOTAL, n2j, twoToK(i - j + 1));
     185                 :        201 : }
     186                 :            : 
     187                 :         49 : void IAndUtils::computeAndTable(uint32_t granularity)
     188                 :            : {
     189 [ -  + ][ -  + ]:         49 :   Assert(d_bvandTable.find(granularity) == d_bvandTable.end());
                 [ -  - ]
     190                 :            :   // the table was not yet computed
     191                 :         49 :   std::map<std::pair<int64_t, int64_t>, uint64_t> table;
     192                 :         49 :   uint64_t num_of_values = ((uint64_t)pow(2, granularity));
     193                 :            :   // populate the table with all the values
     194         [ +  + ]:        235 :   for (uint64_t i = 0; i < num_of_values; i++)
     195                 :            :   {
     196         [ +  + ]:       1918 :     for (uint64_t j = 0; j < num_of_values; j++)
     197                 :            :     {
     198                 :            :       // compute
     199                 :            :       // (bv_to_int (bvand ((int_to_bv granularity) i) ((int_to_bv granularity)
     200                 :            :       // j)))
     201                 :       1732 :       int64_t sum = 0;
     202         [ +  + ]:       8104 :       for (uint64_t n = 0; n < granularity; n++)
     203                 :            :       {
     204                 :            :         // b is the result of f on the current bit
     205                 :       6372 :         bool b = oneBitAnd((((i >> n) & 1) == 1), (((j >> n) & 1) == 1));
     206                 :            :         // add the corresponding power of 2 only if the result is 1
     207         [ +  + ]:       6372 :         if (b)
     208                 :            :         {
     209                 :       1593 :           sum += 1 << n;
     210                 :            :         }
     211                 :            :       }
     212                 :       1732 :       table[std::make_pair(i, j)] = sum;
     213                 :            :     }
     214                 :            :   }
     215                 :            :   // optimize the table by identifying and adding the default value
     216                 :         49 :   addDefaultValue(table, num_of_values);
     217 [ -  + ][ -  + ]:         49 :   Assert(table.size()
                 [ -  - ]
     218                 :            :          == 1 + (static_cast<uint64_t>(num_of_values * num_of_values)));
     219                 :            :   // store the table in the cache and return it
     220                 :         49 :   d_bvandTable[granularity] = table;
     221                 :         49 : }
     222                 :            : 
     223                 :         49 : void IAndUtils::addDefaultValue(
     224                 :            :     std::map<std::pair<int64_t, int64_t>, uint64_t>& table,
     225                 :            :     uint64_t num_of_values)
     226                 :            : {
     227                 :            :   // map each result to the number of times it occurs
     228                 :         49 :   std::map<uint64_t, uint64_t> counters;
     229         [ +  + ]:        284 :   for (uint64_t i = 0; i <= num_of_values; i++)
     230                 :            :   {
     231                 :        235 :     counters[i] = 0;
     232                 :            :   }
     233         [ +  + ]:       1781 :   for (const auto& element : table)
     234                 :            :   {
     235                 :       1732 :     uint64_t result = element.second;
     236                 :       1732 :     counters[result]++;
     237                 :            :   }
     238                 :            : 
     239                 :            :   // compute the most common result
     240                 :         49 :   uint64_t most_common_result = 0;
     241                 :         49 :   uint64_t max_num_of_occ = 0;
     242         [ +  + ]:        284 :   for (uint64_t i = 0; i <= num_of_values; i++)
     243                 :            :   {
     244         [ +  + ]:        235 :     if (counters[i] >= max_num_of_occ)
     245                 :            :     {
     246                 :         49 :       max_num_of_occ = counters[i];
     247                 :         49 :       most_common_result = i;
     248                 :            :     }
     249                 :            :   }
     250                 :            :   // sanity check: some value appears at least once.
     251 [ -  + ][ -  + ]:         49 :   Assert(max_num_of_occ != 0);
                 [ -  - ]
     252                 :            : 
     253                 :            :   // -1 is the default case of the table.
     254                 :            :   // add it to the table
     255                 :         49 :   table[std::make_pair(-1, -1)] = most_common_result;
     256                 :         49 : }
     257                 :            : 
     258                 :        569 : Node IAndUtils::twoToK(unsigned k) const
     259                 :            : {
     260                 :            :   // could be faster
     261                 :        569 :   return d_nm->mkNode(Kind::POW, d_two, d_nm->mkConstInt(Rational(k)));
     262                 :            : }
     263                 :            : 
     264                 :          0 : Node IAndUtils::twoToKMinusOne(unsigned k) const
     265                 :            : {
     266                 :            :   // could be faster
     267                 :          0 :   return NodeManager::mkNode(Kind::SUB, twoToK(k), d_one);
     268                 :            : }
     269                 :            : 
     270                 :            : }  // namespace nl
     271                 :            : }  // namespace arith
     272                 :            : }  // namespace theory
     273                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14