LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/expr - aci_norm.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 177 181 97.8 %
Date: 2026-04-26 10:45:53 Functions: 9 9 100.0 %
Branches: 107 133 80.5 %

           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                 :            :  * Definition of ProofRule::ACI_NORM and ProofRule::ABSORB.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "expr/aci_norm.h"
      14                 :            : 
      15                 :            : #include "expr/attribute.h"
      16                 :            : #include "expr/skolem_manager.h"
      17                 :            : #include "theory/bv/theory_bv_utils.h"
      18                 :            : #include "theory/strings/word.h"
      19                 :            : #include "util/bitvector.h"
      20                 :            : #include "util/finite_field_value.h"
      21                 :            : #include "util/rational.h"
      22                 :            : #include "util/regexp.h"
      23                 :            : #include "util/string.h"
      24                 :            : 
      25                 :            : using namespace cvc5::internal::kind;
      26                 :            : 
      27                 :            : namespace cvc5::internal {
      28                 :            : namespace expr {
      29                 :            : 
      30                 :     866488 : Node getNullTerminator(NodeManager* nm, Kind k, TypeNode tn)
      31                 :            : {
      32                 :     866488 :   Node nullTerm;
      33 [ +  + ][ +  + ]:     866488 :   switch (k)
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
            [ +  + ][ + ]
      34                 :            :   {
      35                 :     360789 :     case Kind::OR: nullTerm = nm->mkConst(false); break;
      36                 :     276161 :     case Kind::AND:
      37                 :     276161 :     case Kind::SEP_STAR: nullTerm = nm->mkConst(true); break;
      38                 :          4 :     case Kind::ADD:
      39 [ +  - ][ -  - ]:         12 :       nullTerm = tn.isInteger() ? nm->mkConstInt(Rational(0))
      40 [ -  + ][ +  - ]:          8 :                                 : nm->mkConstReal(Rational(0));
                 [ -  - ]
      41                 :          4 :       break;
      42                 :          2 :     case Kind::MULT:
      43                 :            :     case Kind::NONLINEAR_MULT:
      44 [ +  - ][ -  - ]:          6 :       nullTerm = tn.isInteger() ? nm->mkConstInt(Rational(1))
      45 [ -  + ][ +  - ]:          4 :                                 : nm->mkConstReal(Rational(1));
                 [ -  - ]
      46                 :          2 :       break;
      47                 :      31703 :     case Kind::STRING_CONCAT:
      48                 :            :       // handles strings and sequences
      49         [ +  - ]:      31703 :       if (tn.isStringLike())
      50                 :            :       {
      51                 :      31703 :         nullTerm = theory::strings::Word::mkEmptyWord(tn);
      52                 :            :       }
      53                 :      31703 :       break;
      54                 :        246 :     case Kind::REGEXP_CONCAT:
      55                 :            :       // the language containing only the empty string
      56                 :        246 :       nullTerm = nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(String("")));
      57                 :        246 :       break;
      58                 :        207 :     case Kind::REGEXP_UNION:
      59                 :            :       // empty language
      60                 :        207 :       nullTerm = nm->mkNode(Kind::REGEXP_NONE);
      61                 :        207 :       break;
      62                 :        107 :     case Kind::REGEXP_INTER:
      63                 :            :       // universal language
      64                 :        107 :       nullTerm = nm->mkNode(Kind::REGEXP_ALL);
      65                 :        107 :       break;
      66                 :       3612 :     case Kind::BITVECTOR_AND:
      67                 :            :       // it may be the case that we are an abstract type, which we guard here
      68                 :            :       // and return the null node.
      69         [ +  - ]:       3612 :       if (tn.isBitVector())
      70                 :            :       {
      71                 :       3612 :         nullTerm = theory::bv::utils::mkOnes(nm, tn.getBitVectorSize());
      72                 :            :       }
      73                 :       3612 :       break;
      74                 :       7330 :     case Kind::BITVECTOR_OR:
      75                 :            :     case Kind::BITVECTOR_ADD:
      76                 :            :     case Kind::BITVECTOR_XOR:
      77         [ +  + ]:       7330 :       if (tn.isBitVector())
      78                 :            :       {
      79                 :       7146 :         nullTerm = theory::bv::utils::mkZero(nm, tn.getBitVectorSize());
      80                 :            :       }
      81                 :       7330 :       break;
      82                 :        711 :     case Kind::BITVECTOR_MULT:
      83         [ +  - ]:        711 :       if (tn.isBitVector())
      84                 :            :       {
      85                 :        711 :         nullTerm = theory::bv::utils::mkOne(nm, tn.getBitVectorSize());
      86                 :            :       }
      87                 :        711 :       break;
      88                 :       8818 :     case Kind::BITVECTOR_CONCAT:
      89                 :            :     {
      90                 :       8818 :       nullTerm = nm->getSkolemManager()->mkSkolemFunction(SkolemId::BV_EMPTY);
      91                 :            :     }
      92                 :       8818 :     break;
      93                 :        571 :     case Kind::FINITE_FIELD_ADD:
      94         [ +  - ]:        571 :       if (tn.isFiniteField())
      95                 :            :       {
      96                 :        571 :         nullTerm = nm->mkConst(FiniteFieldValue(Integer(0), tn.getFfSize()));
      97                 :            :       }
      98                 :        571 :       break;
      99                 :        645 :     case Kind::FINITE_FIELD_MULT:
     100         [ +  - ]:        645 :       if (tn.isFiniteField())
     101                 :            :       {
     102                 :        645 :         nullTerm = nm->mkConst(FiniteFieldValue(Integer(1), tn.getFfSize()));
     103                 :            :       }
     104                 :        645 :       break;
     105                 :     175582 :     default:
     106                 :            :       // not handled as null-terminated
     107                 :     175582 :       break;
     108                 :            :   }
     109                 :     866488 :   return nullTerm;
     110                 :          0 : }
     111                 :            : 
     112                 :    3297842 : bool isAssocCommIdem(Kind k)
     113                 :            : {
     114         [ +  + ]:    3297842 :   switch (k)
     115                 :            :   {
     116                 :     170914 :     case Kind::OR:
     117                 :            :     case Kind::AND:
     118                 :            :     case Kind::SEP_STAR:
     119                 :            :     case Kind::REGEXP_UNION:
     120                 :            :     case Kind::REGEXP_INTER:
     121                 :            :     case Kind::BITVECTOR_AND:
     122                 :            :     case Kind::BITVECTOR_OR:
     123                 :            :     case Kind::FINITE_FIELD_ADD:
     124                 :     170914 :     case Kind::FINITE_FIELD_MULT: return true;
     125                 :    3126928 :     default: break;
     126                 :            :   }
     127                 :    3126928 :   return false;
     128                 :            : }
     129                 :            : 
     130                 :     867235 : bool isAssocComm(Kind k) { return (k == Kind::BITVECTOR_XOR); }
     131                 :            : 
     132                 :    1588076 : bool isAssoc(Kind k)
     133                 :            : {
     134         [ +  + ]:    1588076 :   switch (k)
     135                 :            :   {
     136                 :      50924 :     case Kind::BITVECTOR_CONCAT:
     137                 :            :     case Kind::STRING_CONCAT:
     138                 :      50924 :     case Kind::REGEXP_CONCAT: return true;
     139                 :    1537152 :     default: break;
     140                 :            :   }
     141                 :            :   // also return true for the operators listed above
     142                 :    1537152 :   return isAssocCommIdem(k);
     143                 :            : }
     144                 :            : 
     145                 :            : struct NormalFormTag
     146                 :            : {
     147                 :            : };
     148                 :            : using NormalFormAttr = expr::Attribute<NormalFormTag, Node>;
     149                 :            : 
     150                 :    2314922 : Node getACINormalForm(Node a)
     151                 :            : {
     152                 :            :   NormalFormAttr nfa;
     153                 :    2314922 :   Node an = a.getAttribute(nfa);
     154         [ +  + ]:    2314922 :   if (!an.isNull())
     155                 :            :   {
     156                 :            :     // already computed
     157                 :    1447687 :     return an;
     158                 :            :   }
     159                 :     867235 :   Kind k = a.getKind();
     160                 :     867235 :   bool aci = isAssocCommIdem(k);
     161 [ +  + ][ +  + ]:     867235 :   bool ac = isAssocComm(k) || aci;
     162 [ +  + ][ +  + ]:     867235 :   if (!ac && !isAssoc(k))
                 [ +  + ]
     163                 :            :   {
     164                 :            :     // not associative, return self
     165                 :     724771 :     a.setAttribute(nfa, a);
     166                 :     724771 :     return a;
     167                 :            :   }
     168                 :     142464 :   TypeNode atn = a.getType();
     169                 :     142464 :   Node nt = getNullTerminator(a.getNodeManager(), k, atn);
     170         [ -  + ]:     142464 :   if (nt.isNull())
     171                 :            :   {
     172                 :            :     // no null terminator, likely abstract type, return self
     173                 :          0 :     a.setAttribute(nfa, a);
     174                 :          0 :     return a;
     175                 :            :   }
     176                 :     142464 :   std::vector<Node> toProcess;
     177                 :     142464 :   toProcess.insert(toProcess.end(), a.rbegin(), a.rend());
     178                 :     142464 :   std::vector<Node> children;
     179                 :     142464 :   Node cur;
     180                 :            :   do
     181                 :            :   {
     182                 :     742079 :     cur = toProcess.back();
     183                 :     742079 :     toProcess.pop_back();
     184         [ +  + ]:     742079 :     if (cur == nt)
     185                 :            :     {
     186                 :            :       // ignore null terminator (which is the neutral element)
     187                 :      23874 :       continue;
     188                 :            :     }
     189         [ +  + ]:     718205 :     else if (cur.getKind() == k)
     190                 :            :     {
     191                 :            :       // flatten
     192                 :      82600 :       toProcess.insert(toProcess.end(), cur.rbegin(), cur.rend());
     193                 :            :     }
     194                 :    1271210 :     else if (!aci
     195 [ +  + ][ +  + ]:    1750417 :              || std::find(children.begin(), children.end(), cur)
     196         [ +  + ]:    1750417 :                     == children.end())
     197                 :            :     {
     198                 :            :       // add to final children if not idempotent or if not a duplicate
     199                 :     630381 :       children.push_back(cur);
     200                 :            :     }
     201         [ +  + ]:     742079 :   } while (!toProcess.empty());
     202         [ +  + ]:     142464 :   if (ac)
     203                 :            :   {
     204                 :            :     // sort if commutative
     205                 :     119503 :     std::sort(children.begin(), children.end());
     206                 :            :   }
     207                 :     142464 :   an = children.empty()
     208 [ +  + ][ +  + ]:     440590 :            ? nt
     209                 :     155662 :            : (children.size() == 1 ? children[0]
     210                 :     142464 :                                    : a.getNodeManager()->mkNode(k, children));
     211                 :     142464 :   a.setAttribute(nfa, an);
     212                 :     142464 :   return an;
     213                 :    2314922 : }
     214                 :            : 
     215                 :    1082445 : bool isACINorm(Node a, Node b)
     216                 :            : {
     217                 :    1082445 :   Node an = getACINormalForm(a);
     218                 :    1082445 :   Node bn = getACINormalForm(b);
     219         [ +  + ]:    1082445 :   if (a.getKind() == b.getKind())
     220                 :            :   {
     221                 :            :     // if the kinds are equal, we compare their normal forms only, as the checks
     222                 :            :     // below are spurious.
     223                 :     260158 :     return (an == bn);
     224                 :            :   }
     225                 :            :   // note we compare three possibilities, to handle cases like
     226                 :            :   //   (or (and A B) false) == (and A B).
     227                 :            :   //
     228                 :            :   // Note that we do *not* succeed if an==bn here, since this depends on the
     229                 :            :   // chosen ordering. For example, if (or (and A B) false) == (and B A),
     230                 :            :   // we get a normal form of (and A B) for the LHS. The normal form of the
     231                 :            :   // RHS is either (and A B) or (and B A). If we succeeded when an==bn,
     232                 :            :   // then this would only be the case if the former was chosen as a normal
     233                 :            :   // form. Instead, both fail.
     234 [ +  + ][ +  + ]:     822287 :   return (a == bn) || (an == b);
     235                 :    1082445 : }
     236                 :            : 
     237                 :    1068691 : Node getZeroElement(NodeManager* nm, Kind k, TypeNode tn)
     238                 :            : {
     239                 :    1068691 :   Node zeroTerm;
     240 [ +  + ][ +  + ]:    1068691 :   switch (k)
         [ +  + ][ +  + ]
     241                 :            :   {
     242                 :      25566 :     case Kind::OR: zeroTerm = nm->mkConst(true); break;
     243                 :      84420 :     case Kind::AND:
     244                 :      84420 :     case Kind::SEP_STAR: zeroTerm = nm->mkConst(false); break;
     245                 :      13763 :     case Kind::MULT:
     246                 :            :     case Kind::NONLINEAR_MULT:
     247                 :            :       // Note that we ignore the type. This is safe since multiplication is
     248                 :            :       // permissive for subtypes.
     249                 :      13763 :       zeroTerm = nm->mkConstInt(Rational(0));
     250                 :      13763 :       break;
     251                 :         42 :     case Kind::REGEXP_UNION:
     252                 :            :       // universal language
     253                 :         42 :       zeroTerm = nm->mkNode(Kind::REGEXP_ALL);
     254                 :         42 :       break;
     255                 :        147 :     case Kind::REGEXP_INTER:
     256                 :            :     case Kind::REGEXP_CONCAT:
     257                 :            :       // empty language
     258                 :        147 :       zeroTerm = nm->mkNode(Kind::REGEXP_NONE);
     259                 :        147 :       break;
     260                 :       2265 :     case Kind::BITVECTOR_OR:
     261         [ +  - ]:       2265 :       if (tn.isBitVector())
     262                 :            :       {
     263                 :       2265 :         zeroTerm = theory::bv::utils::mkOnes(nm, tn.getBitVectorSize());
     264                 :            :       }
     265                 :       2265 :       break;
     266                 :       3388 :     case Kind::BITVECTOR_AND:
     267                 :            :     case Kind::BITVECTOR_MULT:
     268                 :            :       // it may be the case that we are an abstract type, which we guard here
     269                 :            :       // and return the null node.
     270         [ +  - ]:       3388 :       if (tn.isBitVector())
     271                 :            :       {
     272                 :       3388 :         zeroTerm = theory::bv::utils::mkZero(nm, tn.getBitVectorSize());
     273                 :            :       }
     274                 :       3388 :       break;
     275                 :     939100 :     default:
     276                 :            :       // no zero
     277                 :     939100 :       break;
     278                 :            :   }
     279                 :    1068691 :   return zeroTerm;
     280                 :          0 : }
     281                 :            : 
     282                 :            : struct AbsorbTag
     283                 :            : {
     284                 :            : };
     285                 :            : struct AbsorbComputedTag
     286                 :            : {
     287                 :            : };
     288                 :            : /**
     289                 :            :  * Attribute true for terms that can be absorbd. Note the same attribute
     290                 :            :  * is stored for all kinds.
     291                 :            :  */
     292                 :            : typedef expr::Attribute<AbsorbTag, bool> AbsorbAttr;
     293                 :            : typedef expr::Attribute<AbsorbComputedTag, bool> AbsorbComputedAttr;
     294                 :            : 
     295                 :      33439 : bool isAbsorb(Kind k)
     296                 :            : {
     297         [ +  + ]:      33439 :   switch (k)
     298                 :            :   {
     299                 :      30034 :     case Kind::OR:
     300                 :            :     case Kind::AND:
     301                 :            :     case Kind::REGEXP_UNION:
     302                 :            :     case Kind::REGEXP_INTER:
     303                 :            :     case Kind::REGEXP_CONCAT:
     304                 :            :     case Kind::BITVECTOR_AND:
     305                 :      30034 :     case Kind::BITVECTOR_OR: return true;
     306                 :       3405 :     default: break;
     307                 :            :   }
     308                 :       3405 :   return false;
     309                 :            : }
     310                 :            : 
     311                 :      33439 : bool isAbsorb(Node a, const Node& zero)
     312                 :            : {
     313                 :      33439 :   Kind k = a.getKind();
     314         [ +  + ]:      33439 :   if (!isAbsorb(k))
     315                 :            :   {
     316                 :       3405 :     return false;
     317                 :            :   }
     318                 :            :   AbsorbAttr aa;
     319                 :            :   AbsorbComputedAttr aca;
     320                 :      30034 :   std::unordered_set<TNode> visited;
     321                 :      30034 :   std::unordered_set<TNode>::iterator it;
     322                 :      30034 :   std::vector<TNode> visit;
     323                 :      30034 :   TNode cur;
     324                 :      30034 :   visit.push_back(a);
     325                 :            :   do
     326                 :            :   {
     327                 :      63230 :     cur = visit.back();
     328 [ -  + ][ -  + ]:      63230 :     Assert(cur.getKind() == k);
                 [ -  - ]
     329         [ +  + ]:      63230 :     if (cur.getAttribute(aca))
     330                 :            :     {
     331                 :      15594 :       visit.pop_back();
     332                 :      39412 :       continue;
     333                 :            :     }
     334                 :      47636 :     it = visited.find(cur);
     335         [ +  + ]:      47636 :     if (it == visited.end())
     336                 :            :     {
     337                 :      23818 :       visited.insert(cur);
     338         [ +  + ]:      95540 :       for (const Node& cc : cur)
     339                 :            :       {
     340         [ +  + ]:      71722 :         if (cc.getKind() == k)
     341                 :            :         {
     342                 :       9378 :           visit.push_back(cc);
     343                 :            :         }
     344                 :      71722 :       }
     345                 :      23818 :       continue;
     346                 :      23818 :     }
     347                 :      23818 :     visit.pop_back();
     348                 :      23818 :     bool isAnnil = false;
     349         [ +  + ]:      80757 :     for (const Node& cc : cur)
     350                 :            :     {
     351                 :            :       // only absorbs if the child is zero or has the same kind and
     352                 :            :       // absorbs
     353 [ +  + ][ +  + ]:      66646 :       if (cc == zero || (cc.getKind() == k && cc.getAttribute(aa)))
         [ +  + ][ +  + ]
     354                 :            :       {
     355                 :       9707 :         isAnnil = true;
     356                 :       9707 :         break;
     357                 :            :       }
     358         [ +  + ]:      66646 :     }
     359                 :      23818 :     cur.setAttribute(aa, isAnnil);
     360                 :      23818 :     cur.setAttribute(aca, true);
     361         [ +  + ]:      63230 :   } while (!visit.empty());
     362                 :      30034 :   return a.getAttribute(aa);
     363                 :      30034 : }
     364                 :            : 
     365                 :            : }  // namespace expr
     366                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14