LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory - substitutions.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 114 146 78.1 %
Date: 2025-02-19 11:37:34 Functions: 8 12 66.7 %
Branches: 82 120 68.3 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Dejan Jovanovic, Clark Barrett
       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                 :            :  * A substitution mapping for theory simplification.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/substitutions.h"
      17                 :            : #include "expr/node_algorithm.h"
      18                 :            : #include "theory/rewriter.h"
      19                 :            : 
      20                 :            : using namespace std;
      21                 :            : 
      22                 :            : namespace cvc5::internal {
      23                 :            : namespace theory {
      24                 :            : 
      25                 :     467997 : SubstitutionMap::SubstitutionMap(context::Context* context, bool compress)
      26                 :            :     : d_context(),
      27                 :            :       d_substitutions(context ? context : &d_context),
      28                 :            :       d_substitutionCache(),
      29                 :            :       d_cacheInvalidated(false),
      30                 :            :       d_compress(compress),
      31 [ +  + ][ +  + ]:     467997 :       d_cacheInvalidator(context ? context : &d_context, d_cacheInvalidated)
      32                 :            : {
      33                 :     467997 : }
      34                 :            : 
      35                 :      39326 : std::unordered_map<Node, Node> SubstitutionMap::getSubstitutions() const
      36                 :            : {
      37                 :      39326 :   std::unordered_map<Node, Node> subs;
      38         [ +  + ]:      96445 :   for (const auto& sub : d_substitutions)
      39                 :            :   {
      40                 :      57119 :     subs.emplace(sub.first, sub.second);
      41                 :            :   }
      42                 :      39326 :   return subs;
      43                 :            : }
      44                 :            : 
      45                 :          3 : Node SubstitutionMap::toFormula(NodeManager* nm) const
      46                 :            : {
      47                 :          6 :   std::vector<Node> conj;
      48         [ +  + ]:         13 :   for (const auto& sub : d_substitutions)
      49                 :            :   {
      50                 :         10 :     conj.emplace_back(nm->mkNode(Kind::EQUAL, sub.first, sub.second));
      51                 :            :   }
      52                 :          6 :   return nm->mkAnd(conj);
      53                 :            : }
      54                 :            : 
      55                 :            : struct substitution_stack_element {
      56                 :            :   TNode d_node;
      57                 :            :   bool d_children_added;
      58                 :    6538470 :   substitution_stack_element(TNode node) : d_node(node), d_children_added(false)
      59                 :            :   {
      60                 :    6538470 :   }
      61                 :            : };/* struct substitution_stack_element */
      62                 :            : 
      63                 :    4230720 : Node SubstitutionMap::internalSubstitute(TNode t,
      64                 :            :                                          NodeCache& cache,
      65                 :            :                                          std::set<TNode>* tracker,
      66                 :            :                                          const ShouldTraverseCallback* stc)
      67                 :            : {
      68         [ +  - ]:    4230720 :   Trace("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << ")" << endl;
      69                 :            : 
      70         [ +  + ]:    4230720 :   if (d_substitutions.empty()) {
      71                 :    2670940 :     return t;
      72                 :            :   }
      73                 :            : 
      74                 :            :   // Do a topological sort of the subexpressions and substitute them
      75                 :    1559780 :   vector<substitution_stack_element> toVisit;
      76                 :    1559780 :   toVisit.push_back((TNode) t);
      77                 :            : 
      78         [ +  + ]:   12296200 :   while (!toVisit.empty())
      79                 :            :   {
      80                 :            :     // The current node we are processing
      81                 :   10736400 :     substitution_stack_element& stackHead = toVisit.back();
      82                 :   10736400 :     TNode current = stackHead.d_node;
      83                 :            : 
      84         [ +  - ]:   10736400 :     Trace("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): processing " << current << endl;
      85                 :            : 
      86                 :            :     // If node already in the cache we're done, pop from the stack
      87                 :   10736400 :     NodeCache::iterator find = cache.find(current);
      88         [ +  + ]:   10736400 :     if (find != cache.end()) {
      89                 :     876040 :       toVisit.pop_back();
      90                 :     876040 :       continue;
      91                 :            :     }
      92                 :            : 
      93                 :    9860380 :     NodeMap::iterator find2 = d_substitutions.find(current);
      94         [ +  + ]:    9860380 :     if (find2 != d_substitutions.end()) {
      95                 :     545813 :       Node rhs = (*find2).second;
      96                 :            :       // if the substitution changes the current term
      97         [ +  - ]:     545813 :       if (rhs != current)
      98                 :            :       {
      99                 :     545813 :         find = cache.find(rhs);
     100         [ +  + ]:     545813 :         if (find == cache.end())
     101                 :            :         {
     102                 :            :           // visit the RHS
     103                 :     200124 :           toVisit.push_back((TNode)rhs);
     104                 :     200124 :           continue;
     105                 :            :         }
     106                 :            :         // update the substitution map if using compression
     107 [ +  + ][ +  + ]:     345689 :         if (tracker == nullptr && d_compress)
     108                 :            :         {
     109                 :     343495 :           d_substitutions[current] = cache[rhs];
     110                 :            :         }
     111                 :     345689 :         cache[current] = cache[rhs];
     112                 :     345689 :         toVisit.pop_back();
     113         [ +  + ]:     345689 :         if (tracker != nullptr)
     114                 :            :         {
     115                 :       2170 :           tracker->insert(current);
     116                 :            :         }
     117                 :     345689 :         continue;
     118                 :            :       }
     119                 :            :     }
     120                 :            : 
     121                 :            :     // Not yet substituted, so process
     122         [ +  + ]:    9314570 :     if (stackHead.d_children_added)
     123                 :            :     {
     124                 :            :       // Children have been processed, so substitute
     125                 :    3997830 :       NodeBuilder builder(current.getNodeManager(), current.getKind());
     126         [ +  + ]:    3997830 :       if (current.getMetaKind() == kind::metakind::PARAMETERIZED)
     127                 :            :       {
     128                 :     650095 :         builder << Node(cache[current.getOperator()]);
     129                 :            :       }
     130         [ +  + ]:   12391800 :       for (const Node& nc : current)
     131                 :            :       {
     132 [ -  + ][ -  + ]:    8394000 :         Assert(cache.find(nc) != cache.end());
                 [ -  - ]
     133                 :    8394000 :         builder << Node(cache[nc]);
     134                 :            :       }
     135                 :            :       // Mark the substitution and continue
     136                 :    3997830 :       Node result = builder;
     137         [ +  + ]:    3997830 :       if (result != current) {
     138                 :    1226630 :         find = cache.find(result);
     139         [ +  + ]:    1226630 :         if (find != cache.end()) {
     140                 :      17648 :           result = find->second;
     141                 :            :         }
     142                 :            :         else {
     143                 :    1208980 :           find2 = d_substitutions.find(result);
     144         [ -  + ]:    1208980 :           if (find2 != d_substitutions.end()) {
     145                 :          0 :             Node rhs = (*find2).second;
     146         [ -  - ]:          0 :             if (rhs != result)
     147                 :            :             {
     148                 :          0 :               find = cache.find(rhs);
     149         [ -  - ]:          0 :               if (find == cache.end())
     150                 :            :               {
     151                 :            :                 // visit the RHS
     152                 :          0 :                 toVisit.push_back((TNode)rhs);
     153                 :          0 :                 continue;
     154                 :            :               }
     155                 :            :               // update the substitution map if using compression
     156         [ -  - ]:          0 :               if (d_compress)
     157                 :            :               {
     158                 :          0 :                 d_substitutions[result] = cache[rhs];
     159                 :            :               }
     160                 :          0 :               cache[result] = cache[rhs];
     161         [ -  - ]:          0 :               if (tracker != nullptr)
     162                 :            :               {
     163                 :          0 :                 tracker->insert(result);
     164                 :            :               }
     165                 :          0 :               result = cache[rhs];
     166                 :            :             }
     167                 :            :           }
     168                 :            :         }
     169                 :            :       }
     170         [ +  - ]:    3997830 :       Trace("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << result << endl;
     171                 :    3997830 :       cache[current] = result;
     172                 :    3997830 :       toVisit.pop_back();
     173                 :            :     }
     174                 :            :     else
     175                 :            :     {
     176                 :            :       // Mark that we have added the children if any
     177 [ +  + ][ +  + ]:    5316740 :       bool recurse = (stc == nullptr || (*stc)(current));
         [ +  + ][ -  - ]
     178                 :    5316740 :       if (recurse
     179 [ +  + ][ +  + ]:    6637440 :           && (current.getNumChildren() > 0
                 [ +  + ]
     180         [ +  + ]:    1320700 :               || current.getMetaKind() == kind::metakind::PARAMETERIZED))
     181                 :            :       {
     182                 :    3997830 :         stackHead.d_children_added = true;
     183                 :            :         // We need to add the operator, if any
     184         [ +  + ]:    3997830 :         if(current.getMetaKind() == kind::metakind::PARAMETERIZED) {
     185                 :    1300190 :           TNode opNode = current.getOperator();
     186                 :     650095 :           NodeCache::iterator opFind = cache.find(opNode);
     187         [ +  + ]:     650095 :           if (opFind == cache.end()) {
     188                 :     205703 :             toVisit.push_back(opNode);
     189                 :            :           }
     190                 :            :         }
     191                 :            :         // We need to add the children
     192         [ +  + ]:   12391800 :         for(TNode::iterator child_it = current.begin(); child_it != current.end(); ++ child_it) {
     193                 :   16788000 :           TNode childNode = *child_it;
     194                 :    8394000 :           NodeCache::iterator childFind = cache.find(childNode);
     195         [ +  + ]:    8394000 :           if (childFind == cache.end()) {
     196                 :    4572860 :             toVisit.push_back(childNode);
     197                 :            :           }
     198                 :            :         }
     199                 :            :       }
     200                 :            :       else
     201                 :            :       {
     202                 :            :         // No children, so we're done
     203         [ +  - ]:    1318910 :         Trace("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << current << endl;
     204                 :    1318910 :         cache[current] = current;
     205                 :    1318910 :         toVisit.pop_back();
     206                 :            :       }
     207                 :            :     }
     208                 :            :   }
     209                 :            : 
     210                 :            :   // Return the substituted version
     211                 :    1559780 :   return cache[t];
     212                 :            : } /* SubstitutionMap::internalSubstitute() */
     213                 :            : 
     214                 :    6911050 : void SubstitutionMap::addSubstitution(TNode x, TNode t, bool invalidateCache)
     215                 :            : {
     216                 :            :   // don't check type equal here, since this utility may be used in conversions
     217                 :            :   // that change the types of terms
     218         [ +  - ]:    6911050 :   Trace("substitution") << "SubstitutionMap::addSubstitution(" << x << ", " << t << ")" << endl;
     219                 :            :   // Shouldn't use compression if replacing a current substitution, or otherwise
     220                 :            :   // the range of other d_substitutions can be out of sync.
     221                 :            :   // For example, if we have substitution { y -> f(x) } and then later add
     222                 :            :   // { x -> a } the substitution may be updated to { y -> f(a), x -> a }. If
     223                 :            :   // we later erase x, then we'd have { y -> f(a) }, but x no longer maps to a.
     224                 :    6911050 :   Assert(d_substitutions.find(x) == d_substitutions.end() || !d_compress);
     225                 :            : 
     226                 :            :   // this causes a later assert-fail (the rhs != current one, above) anyway
     227                 :            :   // putting it here is easier to diagnose
     228 [ -  + ][ -  + ]:    6911050 :   Assert(x != t) << "cannot substitute a term for itself";
                 [ -  - ]
     229                 :            : 
     230                 :    6911050 :   d_substitutions[x] = t;
     231                 :            : 
     232                 :            :   // Also invalidate the cache if necessary
     233         [ +  + ]:    6911050 :   if (invalidateCache) {
     234                 :    6910920 :     d_cacheInvalidated = true;
     235                 :            :   }
     236                 :            :   else {
     237                 :        125 :     d_substitutionCache[x] = d_substitutions[x];
     238                 :            :   }
     239                 :    6911050 : }
     240                 :            : 
     241                 :            : 
     242                 :      21495 : void SubstitutionMap::addSubstitutions(SubstitutionMap& subMap, bool invalidateCache)
     243                 :            : {
     244                 :      21495 :   NodeMap::const_iterator it = subMap.begin();
     245                 :      21495 :   NodeMap::const_iterator it_end = subMap.end();
     246         [ +  + ]:      44808 :   for (; it != it_end; ++ it) {
     247 [ -  + ][ -  + ]:      23313 :     Assert(d_substitutions.find((*it).first) == d_substitutions.end());
                 [ -  - ]
     248                 :      23313 :     d_substitutions[(*it).first] = (*it).second;
     249         [ -  + ]:      23313 :     if (!invalidateCache) {
     250                 :          0 :       d_substitutionCache[(*it).first] = d_substitutions[(*it).first];
     251                 :            :     }
     252                 :            :   }
     253         [ +  - ]:      21495 :   if (invalidateCache) {
     254                 :      21495 :     d_cacheInvalidated = true;
     255                 :            :   }
     256                 :      21495 : }
     257                 :            : 
     258                 :          0 : void SubstitutionMap::eraseSubstitution(TNode x, bool invalidateCache)
     259                 :            : {
     260                 :            :   // shouldn't use compression
     261                 :          0 :   Assert(!d_compress);
     262                 :          0 :   Assert(d_substitutions.find(x) != d_substitutions.end());
     263                 :          0 :   d_substitutions[x] = x;
     264         [ -  - ]:          0 :   if (invalidateCache)
     265                 :            :   {
     266                 :          0 :     d_cacheInvalidated = true;
     267                 :            :   }
     268                 :          0 : }
     269                 :            : 
     270                 :    4230720 : Node SubstitutionMap::apply(TNode t,
     271                 :            :                             Rewriter* r,
     272                 :            :                             std::set<TNode>* tracker,
     273                 :            :                             const ShouldTraverseCallback* stc)
     274                 :            : {
     275         [ +  - ]:    4230720 :   Trace("substitution") << "SubstitutionMap::apply(" << t << ")" << endl;
     276                 :            : 
     277                 :            :   // Setup the cache
     278         [ +  + ]:    4230720 :   if (d_cacheInvalidated) {
     279                 :     170413 :     d_substitutionCache.clear();
     280                 :     170413 :     d_cacheInvalidated = false;
     281         [ +  - ]:     170413 :     Trace("substitution") << "-- reset the cache" << endl;
     282                 :            :   }
     283                 :            : 
     284                 :            :   // Perform the substitution
     285                 :    4230720 :   Node result = internalSubstitute(t, d_substitutionCache, tracker, stc);
     286         [ +  - ]:    4230720 :   Trace("substitution") << "SubstitutionMap::apply(" << t << ") => " << result << endl;
     287                 :            : 
     288         [ +  + ]:    4230720 :   if (r != nullptr)
     289                 :            :   {
     290                 :    2497560 :     Node orig = result;
     291                 :    2497560 :     result = r->rewrite(result);
     292                 :    4995110 :     Assert(r->rewrite(result) == result)
     293                 :    2497560 :         << "Non-idempotent rewrite: " << orig << " --> " << result << " --> "
     294                 :    2497560 :         << r->rewrite(result);
     295                 :            :   }
     296                 :            : 
     297                 :    4230710 :   return result;
     298                 :            : }
     299                 :            : 
     300                 :          0 : void SubstitutionMap::print(ostream& out) const {
     301                 :          0 :   NodeMap::const_iterator it = d_substitutions.begin();
     302                 :          0 :   NodeMap::const_iterator it_end = d_substitutions.end();
     303         [ -  - ]:          0 :   for (; it != it_end; ++ it) {
     304                 :          0 :     out << (*it).first << " -> " << (*it).second << endl;
     305                 :            :   }
     306                 :          0 : }
     307                 :            : 
     308                 :          0 : std::string SubstitutionMap::toString() const
     309                 :            : {
     310                 :          0 :   std::stringstream ss;
     311                 :          0 :   print(ss);
     312                 :          0 :   return ss.str();
     313                 :            : }
     314                 :            : 
     315                 :            : }  // namespace theory
     316                 :            : 
     317                 :          0 : std::ostream& operator<<(std::ostream& out, const theory::SubstitutionMap::iterator& i) {
     318                 :          0 :   return out << "[CDMap-iterator]";
     319                 :            : }
     320                 :            : 
     321                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14