LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/arith/nl - iand_solver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 129 165 78.2 %
Date: 2026-02-15 11:43:36 Functions: 8 13 61.5 %
Branches: 54 106 50.9 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Makai Mann, Aina Niemetz
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Implementation of integer and (IAND) solver.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/arith/nl/iand_solver.h"
      17                 :            : 
      18                 :            : #include "options/arith_options.h"
      19                 :            : #include "options/smt_options.h"
      20                 :            : #include "preprocessing/passes/bv_to_int.h"
      21                 :            : #include "theory/arith/arith_msum.h"
      22                 :            : #include "theory/arith/arith_utilities.h"
      23                 :            : #include "theory/arith/inference_manager.h"
      24                 :            : #include "theory/arith/nl/nl_model.h"
      25                 :            : #include "theory/rewriter.h"
      26                 :            : #include "util/bitvector.h"
      27                 :            : #include "util/iand.h"
      28                 :            : 
      29                 :            : using namespace cvc5::internal::kind;
      30                 :            : 
      31                 :            : namespace cvc5::internal {
      32                 :            : namespace theory {
      33                 :            : namespace arith {
      34                 :            : namespace nl {
      35                 :            : 
      36                 :      32702 : IAndSolver::IAndSolver(Env& env, InferenceManager& im, NlModel& model)
      37                 :            :     : EnvObj(env),
      38                 :            :       d_im(im),
      39                 :            :       d_model(model),
      40                 :            :       d_iandUtils(env.getNodeManager()),
      41                 :      32702 :       d_initRefine(userContext())
      42                 :            : {
      43                 :      32702 :   NodeManager* nm = nodeManager();
      44                 :      32702 :   d_false = nm->mkConst(false);
      45                 :      32702 :   d_true = nm->mkConst(true);
      46                 :      32702 :   d_zero = nm->mkConstInt(Rational(0));
      47                 :      32702 :   d_one = nm->mkConstInt(Rational(1));
      48                 :      32702 :   d_two = nm->mkConstInt(Rational(2));
      49                 :      32702 : }
      50                 :            : 
      51                 :      32418 : IAndSolver::~IAndSolver() {}
      52                 :            : 
      53                 :      11139 : void IAndSolver::initLastCall(const std::vector<Node>& xts)
      54                 :            : {
      55                 :      11139 :   d_iands.clear();
      56                 :            : 
      57         [ +  - ]:      11139 :   Trace("iand-mv") << "IAND terms : " << std::endl;
      58         [ +  + ]:      71170 :   for (const Node& a : xts)
      59                 :            :   {
      60                 :      60031 :     Kind ak = a.getKind();
      61         [ +  + ]:      60031 :     if (ak != Kind::IAND)
      62                 :            :     {
      63                 :            :       // don't care about other terms
      64                 :      59246 :       continue;
      65                 :            :     }
      66                 :        785 :     size_t bsize = a.getOperator().getConst<IntAnd>().d_size;
      67                 :        785 :     d_iands[bsize].push_back(a);
      68         [ +  - ]:        785 :     Trace("iand-mv") << "- " << a << std::endl;
      69                 :            :   }
      70                 :      11139 : }
      71                 :            : 
      72                 :      11139 : void IAndSolver::checkInitialRefine()
      73                 :            : {
      74         [ +  - ]:      11139 :   Trace("iand-check") << "IAndSolver::checkInitialRefine" << std::endl;
      75                 :      11139 :   NodeManager* nm = nodeManager();
      76         [ +  + ]:      11876 :   for (const std::pair<const unsigned, std::vector<Node> >& is : d_iands)
      77                 :            :   {
      78                 :            :     // the reference bitwidth
      79                 :        737 :     unsigned k = is.first;
      80         [ +  + ]:       1522 :     for (const Node& i : is.second)
      81                 :            :     {
      82         [ +  + ]:        785 :       if (d_initRefine.find(i) != d_initRefine.end())
      83                 :            :       {
      84                 :            :         // already sent initial axioms for i in this user context
      85                 :        630 :         continue;
      86                 :            :       }
      87                 :        155 :       d_initRefine.insert(i);
      88                 :        310 :       Node op = i.getOperator();
      89                 :        155 :       uint32_t bsize = op.getConst<IntAnd>().d_size;
      90                 :        465 :       Node twok = nm->mkConstInt(Rational(Integer(2).pow(bsize)));
      91                 :        465 :       Node arg0Mod = nm->mkNode(Kind::INTS_MODULUS, i[0], twok);
      92                 :        465 :       Node arg1Mod = nm->mkNode(Kind::INTS_MODULUS, i[1], twok);
      93                 :            :       // initial refinement lemmas
      94                 :        310 :       std::vector<Node> conj;
      95                 :            :       // iand(x,y)=iand(y,x) is guaranteed by rewriting
      96 [ -  + ][ -  + ]:        155 :       Assert(i[0] <= i[1]);
                 [ -  - ]
      97                 :            :       // conj.push_back(i.eqNode(nm->mkNode(IAND, op, i[1], i[0])));
      98                 :            :       // 0 <= iand(x,y) < 2^k
      99                 :        155 :       conj.push_back(nm->mkNode(Kind::LEQ, d_zero, i));
     100                 :        155 :       conj.push_back(nm->mkNode(Kind::LT, i, rewrite(d_iandUtils.twoToK(k))));
     101                 :            :       // iand(x,y)<=mod(x, 2^k)
     102                 :        155 :       conj.push_back(nm->mkNode(Kind::LEQ, i, arg0Mod));
     103                 :            :       // iand(x,y)<=mod(y, 2^k)
     104                 :        155 :       conj.push_back(nm->mkNode(Kind::LEQ, i, arg1Mod));
     105                 :            :       // x=y => iand(x,y)=mod(x, 2^k)
     106                 :        155 :       conj.push_back(
     107                 :        310 :           nm->mkNode(Kind::IMPLIES, i[0].eqNode(i[1]), i.eqNode(arg0Mod)));
     108         [ -  + ]:        310 :       Node lem = conj.size() == 1 ? conj[0] : nm->mkNode(Kind::AND, conj);
     109         [ +  - ]:        310 :       Trace("iand-lemma") << "IAndSolver::Lemma: " << lem << " ; INIT_REFINE"
     110                 :        155 :                           << std::endl;
     111                 :        155 :       d_im.addPendingLemma(lem, InferenceId::ARITH_NL_IAND_INIT_REFINE);
     112                 :            :     }
     113                 :            :   }
     114                 :      11139 : }
     115                 :            : 
     116                 :       2167 : void IAndSolver::checkFullRefine()
     117                 :            : {
     118         [ +  - ]:       2167 :   Trace("iand-check") << "IAndSolver::checkFullRefine";
     119         [ +  - ]:       2167 :   Trace("iand-check") << "IAND terms: " << std::endl;
     120         [ +  + ]:       2661 :   for (const std::pair<const unsigned, std::vector<Node> >& is : d_iands)
     121                 :            :   {
     122                 :            :     // the reference bitwidth
     123                 :        494 :     unsigned k = is.first;
     124         [ +  + ]:       1027 :     for (const Node& i : is.second)
     125                 :            :     {
     126                 :        533 :       Node valAndXY = d_model.computeAbstractModelValue(i);
     127                 :        533 :       Node valAndXYC = d_model.computeConcreteModelValue(i);
     128         [ -  + ]:        533 :       if (TraceIsOn("iand-check"))
     129                 :            :       {
     130                 :          0 :         Node x = i[0];
     131                 :          0 :         Node y = i[1];
     132                 :            : 
     133                 :          0 :         Node valX = d_model.computeConcreteModelValue(x);
     134                 :          0 :         Node valY = d_model.computeConcreteModelValue(y);
     135                 :            : 
     136         [ -  - ]:          0 :         Trace("iand-check")
     137                 :          0 :             << "* " << i << ", value = " << valAndXY << std::endl;
     138         [ -  - ]:          0 :         Trace("iand-check") << "  actual (" << valX << ", " << valY
     139                 :          0 :                             << ") = " << valAndXYC << std::endl;
     140                 :            :         // print the bit-vector versions
     141                 :          0 :         Node bvalX = convertToBvK(k, valX);
     142                 :          0 :         Node bvalY = convertToBvK(k, valY);
     143                 :          0 :         Node bvalAndXY = convertToBvK(k, valAndXY);
     144                 :          0 :         Node bvalAndXYC = convertToBvK(k, valAndXYC);
     145                 :            : 
     146         [ -  - ]:          0 :         Trace("iand-check") << "  bv-value = " << bvalAndXY << std::endl;
     147         [ -  - ]:          0 :         Trace("iand-check") << "  bv-actual (" << bvalX << ", " << bvalY
     148                 :          0 :                             << ") = " << bvalAndXYC << std::endl;
     149                 :            :       }
     150         [ +  + ]:        533 :       if (valAndXY == valAndXYC)
     151                 :            :       {
     152         [ +  - ]:         39 :         Trace("iand-check") << "...already correct" << std::endl;
     153                 :         39 :         continue;
     154                 :            :       }
     155                 :            : 
     156                 :            :       // ************* additional lemma schemas go here
     157         [ +  + ]:        494 :       if (options().smt.iandMode == options::IandMode::SUM)
     158                 :            :       {
     159                 :         30 :         Node lem = sumBasedLemma(i);  // add lemmas based on sum mode
     160         [ +  - ]:         30 :         Trace("iand-lemma")
     161                 :         15 :             << "IAndSolver::Lemma: " << lem << " ; SUM_REFINE" << std::endl;
     162                 :            :         // note that lemma can contain div/mod, and will be preprocessed in the
     163                 :            :         // prop engine
     164                 :         15 :         d_im.addPendingLemma(
     165                 :            :             lem, InferenceId::ARITH_NL_IAND_SUM_REFINE, nullptr, true);
     166                 :            :       }
     167         [ +  + ]:        479 :       else if (options().smt.iandMode == options::IandMode::BITWISE)
     168                 :            :       {
     169                 :         54 :         Node lem = bitwiseLemma(i);  // check for violated bitwise axioms
     170         [ +  - ]:         54 :         Trace("iand-lemma")
     171                 :         27 :             << "IAndSolver::Lemma: " << lem << " ; BITWISE_REFINE" << std::endl;
     172                 :            :         // note that lemma can contain div/mod, and will be preprocessed in the
     173                 :            :         // prop engine
     174                 :         27 :         d_im.addPendingLemma(
     175                 :            :             lem, InferenceId::ARITH_NL_IAND_BITWISE_REFINE, nullptr, true);
     176                 :            :       }
     177                 :            :       else
     178                 :            :       {
     179                 :            :         // this is the most naive model-based schema based on model values
     180                 :        904 :         Node lem = valueBasedLemma(i);
     181         [ +  - ]:        904 :         Trace("iand-lemma")
     182                 :        452 :             << "IAndSolver::Lemma: " << lem << " ; VALUE_REFINE" << std::endl;
     183                 :            :         // send the value lemma
     184                 :        452 :         d_im.addPendingLemma(lem,
     185                 :            :                              InferenceId::ARITH_NL_IAND_VALUE_REFINE,
     186                 :            :                              nullptr,
     187                 :            :                              true);
     188                 :            :       }
     189                 :            :     }
     190                 :            :   }
     191                 :       2167 : }
     192                 :            : 
     193                 :          0 : Node IAndSolver::convertToBvK(unsigned k, Node n) const
     194                 :            : {
     195                 :          0 :   Assert(n.isConst() && n.getType().isInteger());
     196                 :          0 :   NodeManager* nm = nodeManager();
     197                 :          0 :   Node iToBvOp = nm->mkConst(IntToBitVector(k));
     198                 :          0 :   Node bn = nm->mkNode(Kind::INT_TO_BITVECTOR, iToBvOp, n);
     199                 :          0 :   return rewrite(bn);
     200                 :            : }
     201                 :            : 
     202                 :          0 : Node IAndSolver::mkIAnd(unsigned k, Node x, Node y) const
     203                 :            : {
     204                 :          0 :   NodeManager* nm = nodeManager();
     205                 :          0 :   Node iAndOp = nm->mkConst(IntAnd(k));
     206                 :          0 :   Node ret = nm->mkNode(Kind::IAND, iAndOp, x, y);
     207                 :          0 :   ret = rewrite(ret);
     208                 :          0 :   return ret;
     209                 :            : }
     210                 :            : 
     211                 :          0 : Node IAndSolver::mkIOr(unsigned k, Node x, Node y) const
     212                 :            : {
     213                 :          0 :   Node ret = mkINot(k, mkIAnd(k, mkINot(k, x), mkINot(k, y)));
     214                 :          0 :   ret = rewrite(ret);
     215                 :          0 :   return ret;
     216                 :            : }
     217                 :            : 
     218                 :          0 : Node IAndSolver::mkINot(unsigned k, Node x) const
     219                 :            : {
     220                 :          0 :   NodeManager* nm = nodeManager();
     221                 :          0 :   Node ret = nm->mkNode(Kind::SUB, d_iandUtils.twoToKMinusOne(k), x);
     222                 :          0 :   ret = rewrite(ret);
     223                 :          0 :   return ret;
     224                 :            : }
     225                 :            : 
     226                 :        452 : Node IAndSolver::valueBasedLemma(Node i)
     227                 :            : {
     228                 :        452 :   NodeManager* nm = nodeManager();
     229 [ -  + ][ -  + ]:        452 :   Assert(i.getKind() == Kind::IAND);
                 [ -  - ]
     230                 :        904 :   Node x = i[0];
     231                 :        904 :   Node y = i[1];
     232                 :            : 
     233                 :        452 :   uint32_t bvsize = i.getOperator().getConst<IntAnd>().d_size;
     234                 :       1356 :   Node twok = nm->mkConstInt(Rational(Integer(2).pow(bvsize)));
     235                 :        904 :   Node valX = d_model.computeConcreteModelValue(x);
     236                 :        904 :   Node valY = d_model.computeConcreteModelValue(y);
     237                 :        452 :   valX = nm->mkNode(Kind::INTS_MODULUS, valX, twok);
     238                 :        452 :   valY = nm->mkNode(Kind::INTS_MODULUS, valY, twok);
     239                 :            : 
     240                 :       1356 :   Node valC = nm->mkNode(Kind::IAND, i.getOperator(), valX, valY);
     241                 :        452 :   valC = rewrite(valC);
     242                 :            : 
     243                 :       1356 :   Node xm = nm->mkNode(Kind::INTS_MODULUS, x, twok);
     244                 :       1356 :   Node ym = nm->mkNode(Kind::INTS_MODULUS, y, twok);
     245                 :            : 
     246                 :            :   // (=>
     247                 :            :   //   (and (= (mod x 2^n) (mod c1 2^n)) (= (mod y 2^n) (mod c2 2^n)))
     248                 :            :   //   (= ((_ iand n) x y) rewrite(((_ iand n) (mod c1 2^n) (mod c2 2^n))))
     249                 :            :   // Note we use mod above since it ensures the the set of possible literals
     250                 :            :   // introduced is finite, since there are finitely many values mod 2^n.
     251                 :            :   Node lem = nm->mkNode(Kind::IMPLIES,
     252                 :        904 :                         nm->mkNode(Kind::AND, xm.eqNode(valX), ym.eqNode(valY)),
     253                 :       1808 :                         i.eqNode(valC));
     254                 :        904 :   return lem;
     255                 :            : }
     256                 :            : 
     257                 :         15 : Node IAndSolver::sumBasedLemma(Node i)
     258                 :            : {
     259 [ -  + ][ -  + ]:         15 :   Assert(i.getKind() == Kind::IAND);
                 [ -  - ]
     260                 :         30 :   Node x = i[0];
     261                 :         30 :   Node y = i[1];
     262                 :         15 :   uint32_t bvsize = i.getOperator().getConst<IntAnd>().d_size;
     263 [ -  + ][ -  + ]:         15 :   Assert(options().smt.BVAndIntegerGranularity <= 8);
                 [ -  - ]
     264                 :         15 :   uint32_t granularity = static_cast<uint32_t>(options().smt.BVAndIntegerGranularity);
     265                 :         15 :   NodeManager* nm = nodeManager();
     266                 :            :   Node lem = nm->mkNode(
     267                 :         30 :       Kind::EQUAL, i, d_iandUtils.createSumNode(x, y, bvsize, granularity));
     268                 :         30 :   return lem;
     269                 :            : }
     270                 :            : 
     271                 :         27 : Node IAndSolver::bitwiseLemma(Node i)
     272                 :            : {
     273 [ -  + ][ -  + ]:         27 :   Assert(i.getKind() == Kind::IAND);
                 [ -  - ]
     274                 :         54 :   Node x = i[0];
     275                 :         54 :   Node y = i[1];
     276                 :            : 
     277                 :         27 :   unsigned bvsize = i.getOperator().getConst<IntAnd>().d_size;
     278 [ -  + ][ -  + ]:         27 :   Assert(options().smt.BVAndIntegerGranularity <= 8);
                 [ -  - ]
     279                 :         27 :   uint32_t granularity = static_cast<uint32_t>(options().smt.BVAndIntegerGranularity);
     280                 :            : 
     281                 :         81 :   Rational absI = d_model.computeAbstractModelValue(i).getConst<Rational>();
     282                 :         81 :   Rational concI = d_model.computeConcreteModelValue(i).getConst<Rational>();
     283                 :            : 
     284 [ -  + ][ -  + ]:         27 :   Assert(absI.isIntegral());
                 [ -  - ]
     285 [ -  + ][ -  + ]:         27 :   Assert(concI.isIntegral());
                 [ -  - ]
     286                 :            : 
     287                 :         54 :   BitVector bvAbsI = BitVector(bvsize, absI.getNumerator());
     288                 :         54 :   BitVector bvConcI = BitVector(bvsize, concI.getNumerator());
     289                 :            : 
     290                 :         27 :   NodeManager* nm = nodeManager();
     291                 :         27 :   Node lem = d_true;
     292                 :            : 
     293                 :            :   // compare each bit to bvI
     294                 :         54 :   Node cond;
     295                 :         54 :   Node bitIAnd;
     296                 :            :   uint32_t high_bit;
     297         [ +  + ]:        105 :   for (uint32_t j = 0; j < bvsize; j += granularity)
     298                 :            :   {
     299                 :         78 :     high_bit = j + granularity - 1;
     300                 :            :     // don't let high_bit pass bvsize
     301         [ +  + ]:         78 :     if (high_bit >= bvsize)
     302                 :            :     {
     303                 :          4 :       high_bit = bvsize - 1;
     304                 :            :     }
     305                 :            : 
     306                 :            :     // check if the abstraction differs from the concrete one on these bits
     307         [ +  + ]:         78 :     if (bvAbsI.extract(high_bit, j) != bvConcI.extract(high_bit, j))
     308                 :            :     {
     309                 :         31 :       bitIAnd = d_iandUtils.createBitwiseIAndNode(x, y, high_bit, j);
     310                 :            :       // enforce bitwise equality
     311                 :         62 :       lem = nm->mkNode(
     312                 :            :           Kind::AND,
     313                 :            :           lem,
     314                 :         93 :           rewrite(d_iandUtils.iextract(high_bit, j, i)).eqNode(bitIAnd));
     315                 :            :     }
     316                 :            :   }
     317                 :         54 :   return lem;
     318                 :            : }
     319                 :            : 
     320                 :            : }  // namespace nl
     321                 :            : }  // namespace arith
     322                 :            : }  // namespace theory
     323                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14