LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers/fmf - bounded_integers.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 556 623 89.2 %
Date: 2026-07-30 10:35:56 Functions: 29 29 100.0 %
Branches: 368 560 65.7 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Bounded integers module
      11                 :            :  *
      12                 :            :  * This class manages integer bounds for quantifiers.
      13                 :            :  */
      14                 :            : 
      15                 :            : #include "theory/quantifiers/fmf/bounded_integers.h"
      16                 :            : 
      17                 :            : #include "expr/dtype_cons.h"
      18                 :            : #include "expr/emptyset.h"
      19                 :            : #include "expr/node_algorithm.h"
      20                 :            : #include "expr/skolem_manager.h"
      21                 :            : #include "options/datatypes_options.h"
      22                 :            : #include "options/quantifiers_options.h"
      23                 :            : #include "theory/arith/arith_msum.h"
      24                 :            : #include "theory/datatypes/theory_datatypes_utils.h"
      25                 :            : #include "theory/decision_manager.h"
      26                 :            : #include "theory/quantifiers/first_order_model.h"
      27                 :            : #include "theory/quantifiers/fmf/model_engine.h"
      28                 :            : #include "theory/quantifiers/term_enumeration.h"
      29                 :            : #include "theory/quantifiers/term_util.h"
      30                 :            : #include "theory/rep_set_iterator.h"
      31                 :            : #include "theory/rewriter.h"
      32                 :            : #include "theory/sets/normal_form.h"
      33                 :            : #include "util/rational.h"
      34                 :            : 
      35                 :            : using namespace cvc5::internal::kind;
      36                 :            : 
      37                 :            : namespace cvc5::internal {
      38                 :            : namespace theory {
      39                 :            : namespace quantifiers {
      40                 :            : 
      41                 :       1130 : BoundedIntegers::IntRangeDecisionHeuristic::IntRangeDecisionHeuristic(
      42                 :       1130 :     Env& env, Node r, Valuation valuation, bool isProxy)
      43                 :            :     : DecisionStrategyFmf(env, valuation),
      44                 :       1130 :       d_range(r),
      45                 :       1130 :       d_ranges_proxied(userContext())
      46                 :            : {
      47                 :            :   // we require a proxy if the term is set.card
      48 [ +  + ][ +  + ]:       1130 :   if (options().quantifiers.fmfBoundLazy || r.getKind() == Kind::SET_CARD)
                 [ +  + ]
      49                 :            :   {
      50                 :            :     d_proxy_range =
      51                 :         64 :         isProxy ? r : NodeManager::mkDummySkolem("pbir", r.getType());
      52                 :            :   }
      53                 :            :   else
      54                 :            :   {
      55                 :       1066 :     d_proxy_range = r;
      56                 :            :   }
      57         [ +  + ]:       1130 :   if (!isProxy)
      58                 :            :   {
      59         [ +  - ]:       2006 :     Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for "
      60                 :       1003 :                        << d_range << std::endl;
      61                 :            :   }
      62                 :       1130 : }
      63                 :       3184 : Node BoundedIntegers::IntRangeDecisionHeuristic::mkLiteral(unsigned n)
      64                 :            : {
      65                 :       3184 :   NodeManager* nm = nodeManager();
      66         [ +  + ]:       3184 :   Node cn = nm->mkConstInt(Rational(n == 0 ? 0 : n - 1));
      67         [ +  + ]:       6368 :   return nm->mkNode(n == 0 ? Kind::LT : Kind::LEQ, d_proxy_range, cn);
      68                 :       3184 : }
      69                 :            : 
      70                 :       3048 : Node BoundedIntegers::IntRangeDecisionHeuristic::proxyCurrentRangeLemma()
      71                 :            : {
      72         [ +  + ]:       3048 :   if (d_range == d_proxy_range)
      73                 :            :   {
      74                 :       2675 :     return Node::null();
      75                 :            :   }
      76                 :        373 :   unsigned curr = 0;
      77         [ +  + ]:        373 :   if (!getAssertedLiteralIndex(curr))
      78                 :            :   {
      79                 :          2 :     return Node::null();
      80                 :            :   }
      81         [ +  + ]:        371 :   if (d_ranges_proxied.find(curr) != d_ranges_proxied.end())
      82                 :            :   {
      83                 :        154 :     return Node::null();
      84                 :            :   }
      85                 :        217 :   d_ranges_proxied[curr] = true;
      86                 :        217 :   NodeManager* nm = nodeManager();
      87                 :        217 :   Node currLit = getLiteral(curr);
      88                 :        217 :   Node lit;
      89         [ +  + ]:        217 :   if (d_range.getKind() == Kind::SET_CARD)
      90                 :            :   {
      91                 :            :     // Instead of introducing (set.card s) < n, we introduce the literal
      92                 :            :     // s = characteristicSet(s, n-1) for n>0 and false for n=0. We do this
      93                 :            :     // to avoid introducing set.card.
      94         [ +  + ]:        210 :     if (curr == 0)
      95                 :            :     {
      96                 :         57 :       lit = nodeManager()->mkConst(false);
      97                 :            :     }
      98                 :            :     else
      99                 :            :     {
     100                 :            :       Node cset = sets::NormalForm::getCharacteristicSet(
     101                 :        153 :           nodeManager(), d_range[0], curr - 1);
     102                 :        153 :       lit = d_range[0].eqNode(cset);
     103                 :        153 :     }
     104                 :            :   }
     105                 :            :   else
     106                 :            :   {
     107         [ +  - ]:         21 :     lit = nm->mkNode(curr == 0 ? Kind::LT : Kind::LEQ,
     108                 :          7 :                      d_range,
     109         [ -  + ]:         21 :                      nm->mkConstInt(Rational(curr == 0 ? 0 : curr - 1)));
     110                 :            :   }
     111                 :        434 :   Node lem = nm->mkNode(Kind::EQUAL, currLit, lit);
     112                 :        217 :   return lem;
     113                 :        217 : }
     114                 :            : 
     115                 :      13990 : BoundedIntegers::BoundedIntegers(Env& env,
     116                 :            :                                  QuantifiersState& qs,
     117                 :            :                                  QuantifiersInferenceManager& qim,
     118                 :            :                                  QuantifiersRegistry& qr,
     119                 :      13990 :                                  TermRegistry& tr)
     120         [ +  + ]:      41970 :     : QuantifiersModule(env, qs, qim, qr, tr)
     121                 :            : {
     122                 :      13990 : }
     123                 :            : 
     124 [ +  - ][ +  + ]:      55936 : BoundedIntegers::~BoundedIntegers() {}
     125                 :            : 
     126                 :      14004 : void BoundedIntegers::presolve() { d_bnd_it.clear(); }
     127                 :            : 
     128                 :      25323 : bool BoundedIntegers::hasNonBoundVar(Node f,
     129                 :            :                                      Node b,
     130                 :            :                                      std::map<Node, bool>& visited)
     131                 :            : {
     132         [ +  + ]:      25323 :   if (visited.find(b) == visited.end())
     133                 :            :   {
     134                 :      21354 :     visited[b] = true;
     135         [ +  + ]:      21354 :     if (b.getKind() == Kind::BOUND_VARIABLE)
     136                 :            :     {
     137         [ +  + ]:       1685 :       if (!isBound(f, b))
     138                 :            :       {
     139                 :        874 :         return true;
     140                 :            :       }
     141                 :            :     }
     142                 :            :     else
     143                 :            :     {
     144         [ +  + ]:      38408 :       for (unsigned i = 0; i < b.getNumChildren(); i++)
     145                 :            :       {
     146         [ +  + ]:      20248 :         if (hasNonBoundVar(f, b[i], visited))
     147                 :            :         {
     148                 :       1509 :           return true;
     149                 :            :         }
     150                 :            :       }
     151                 :            :     }
     152                 :            :   }
     153                 :      22940 :   return false;
     154                 :            : }
     155                 :       5075 : bool BoundedIntegers::hasNonBoundVar(Node f, Node b)
     156                 :            : {
     157                 :       5075 :   std::map<Node, bool> visited;
     158                 :      10150 :   return hasNonBoundVar(f, b, visited);
     159                 :       5075 : }
     160                 :            : 
     161                 :       1036 : bool BoundedIntegers::processEqDisjunct(Node q,
     162                 :            :                                         Node n,
     163                 :            :                                         Node& v,
     164                 :            :                                         std::vector<Node>& v_cases)
     165                 :            : {
     166         [ +  + ]:       1036 :   if (n.getKind() == Kind::EQUAL)
     167                 :            :   {
     168         [ +  + ]:       3006 :     for (unsigned i = 0; i < 2; i++)
     169                 :            :     {
     170                 :       2028 :       Node t = n[i];
     171         [ +  + ]:       2028 :       if (!hasNonBoundVar(q, n[1 - i]))
     172                 :            :       {
     173         [ +  + ]:       1399 :         if (t == v)
     174                 :            :         {
     175                 :         24 :           v_cases.push_back(n[1 - i]);
     176                 :         24 :           return true;
     177                 :            :         }
     178 [ +  + ][ +  + ]:       1375 :         else if (v.isNull() && t.getKind() == Kind::BOUND_VARIABLE)
                 [ +  + ]
     179                 :            :         {
     180                 :         26 :           v = t;
     181                 :         26 :           v_cases.push_back(n[1 - i]);
     182                 :         26 :           return true;
     183                 :            :         }
     184                 :            :       }
     185         [ +  + ]:       2028 :     }
     186                 :            :   }
     187                 :        986 :   return false;
     188                 :            : }
     189                 :            : 
     190                 :        400 : void BoundedIntegers::processMatchBoundVars(Node q,
     191                 :            :                                             Node n,
     192                 :            :                                             std::vector<Node>& bvs,
     193                 :            :                                             std::map<Node, bool>& visited)
     194                 :            : {
     195         [ +  - ]:        400 :   if (visited.find(n) == visited.end())
     196                 :            :   {
     197                 :        400 :     visited[n] = true;
     198                 :        400 :     if (n.getKind() == Kind::BOUND_VARIABLE && !isBound(q, n))
     199                 :            :     {
     200                 :        125 :       bvs.push_back(n);
     201                 :            :       // injective operators
     202                 :            :     }
     203         [ +  + ]:        275 :     else if (n.getKind() == Kind::APPLY_CONSTRUCTOR)
     204                 :            :     {
     205         [ +  + ]:        308 :       for (unsigned i = 0; i < n.getNumChildren(); i++)
     206                 :            :       {
     207                 :        172 :         processMatchBoundVars(q, n[i], bvs, visited);
     208                 :            :       }
     209                 :            :     }
     210                 :            :   }
     211                 :        400 : }
     212                 :            : 
     213                 :      16620 : void BoundedIntegers::process(
     214                 :            :     Node q,
     215                 :            :     Node n,
     216                 :            :     bool pol,
     217                 :            :     std::map<Node, unsigned>& bound_lit_type_map,
     218                 :            :     std::map<int, std::map<Node, Node> >& bound_lit_map,
     219                 :            :     std::map<int, std::map<Node, bool> >& bound_lit_pol_map,
     220                 :            :     std::map<int, std::map<Node, Node> >& bound_int_range_term,
     221                 :            :     std::map<Node, std::vector<Node> >& bound_fixed_set)
     222                 :            : {
     223 [ +  + ][ +  + ]:      16620 :   if (n.getKind() == Kind::OR || n.getKind() == Kind::AND)
                 [ +  + ]
     224                 :            :   {
     225         [ +  + ]:       3517 :     if ((n.getKind() == Kind::OR) == pol)
     226                 :            :     {
     227         [ +  + ]:      11554 :       for (unsigned i = 0; i < n.getNumChildren(); i++)
     228                 :            :       {
     229                 :       8995 :         process(q,
     230                 :            :                 n[i],
     231                 :            :                 pol,
     232                 :            :                 bound_lit_type_map,
     233                 :            :                 bound_lit_map,
     234                 :            :                 bound_lit_pol_map,
     235                 :            :                 bound_int_range_term,
     236                 :            :                 bound_fixed_set);
     237                 :            :       }
     238                 :            :     }
     239                 :            :     else
     240                 :            :     {
     241                 :            :       // if we are ( x != t1 ^ ...^ x != tn ), then x can be bound to { t1...tn
     242                 :            :       // }
     243                 :        958 :       Node conj = n;
     244         [ -  + ]:        958 :       if (!pol)
     245                 :            :       {
     246                 :          0 :         conj = TermUtil::simpleNegate(conj);
     247                 :            :       }
     248         [ +  - ]:       1916 :       Trace("bound-int-debug")
     249                 :          0 :           << "Process possible finite disequality conjunction : " << conj
     250                 :        958 :           << std::endl;
     251 [ -  + ][ -  + ]:        958 :       Assert(conj.getKind() == Kind::AND);
                 [ -  - ]
     252                 :        958 :       Node v;
     253                 :        958 :       std::vector<Node> v_cases;
     254                 :        958 :       bool success = true;
     255         [ +  + ]:       1008 :       for (unsigned i = 0; i < conj.getNumChildren(); i++)
     256                 :            :       {
     257 [ +  + ][ -  - ]:        984 :         if (conj[i].getKind() == Kind::NOT
     258                 :        984 :             && processEqDisjunct(q, conj[i][0], v, v_cases))
     259                 :            :         {
     260                 :            :           // continue
     261                 :            :         }
     262                 :            :         else
     263                 :            :         {
     264         [ +  - ]:       1868 :           Trace("bound-int-debug")
     265 [ -  + ][ -  - ]:        934 :               << "...failed due to " << conj[i] << std::endl;
     266                 :        934 :           success = false;
     267                 :        934 :           break;
     268                 :            :         }
     269                 :            :       }
     270                 :        958 :       if (success && !isBound(q, v))
     271                 :            :       {
     272         [ +  - ]:         10 :         Trace("bound-int-debug") << "Success with variable " << v << std::endl;
     273                 :         10 :         bound_lit_type_map[v] = BOUND_FIXED_SET;
     274                 :         10 :         bound_lit_map[3][v] = n;
     275                 :         10 :         bound_lit_pol_map[3][v] = pol;
     276                 :         10 :         bound_fixed_set[v].clear();
     277                 :         20 :         bound_fixed_set[v].insert(
     278                 :         20 :             bound_fixed_set[v].end(), v_cases.begin(), v_cases.end());
     279                 :            :       }
     280                 :        958 :     }
     281                 :            :   }
     282         [ +  + ]:      13103 :   else if (n.getKind() == Kind::EQUAL)
     283                 :            :   {
     284         [ +  + ]:       1622 :     if (!pol)
     285                 :            :     {
     286                 :            :       // non-applied DER on x != t, x can be bound to { t }
     287                 :        750 :       Node v;
     288                 :        750 :       std::vector<Node> v_cases;
     289         [ -  + ]:        750 :       if (processEqDisjunct(q, n, v, v_cases))
     290                 :            :       {
     291         [ -  - ]:          0 :         if (!isBound(q, v))
     292                 :            :         {
     293                 :          0 :           bound_lit_type_map[v] = BOUND_FIXED_SET;
     294                 :          0 :           bound_lit_map[3][v] = n;
     295                 :          0 :           bound_lit_pol_map[3][v] = pol;
     296                 :          0 :           Assert(v_cases.size() == 1);
     297                 :          0 :           bound_fixed_set[v].clear();
     298                 :          0 :           bound_fixed_set[v].push_back(v_cases[0]);
     299                 :            :         }
     300                 :            :       }
     301                 :        750 :     }
     302                 :            :   }
     303         [ +  + ]:      11481 :   else if (n.getKind() == Kind::NOT)
     304                 :            :   {
     305                 :       4800 :     process(q,
     306                 :            :             n[0],
     307                 :       4800 :             !pol,
     308                 :            :             bound_lit_type_map,
     309                 :            :             bound_lit_map,
     310                 :            :             bound_lit_pol_map,
     311                 :            :             bound_int_range_term,
     312                 :            :             bound_fixed_set);
     313                 :            :   }
     314         [ +  + ]:       6681 :   else if (n.getKind() == Kind::GEQ)
     315                 :            :   {
     316         [ +  + ]:       5562 :     if (n[0].getType().isInteger())
     317                 :            :     {
     318                 :       5557 :       std::map<Node, Node> msum;
     319         [ +  - ]:       5557 :       if (ArithMSum::getMonomialSumLit(n, msum))
     320                 :            :       {
     321                 :       5557 :         NodeManager* nm = nodeManager();
     322         [ +  - ]:      11114 :         Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n
     323                 :       5557 :                                  << " is monomial sum : " << std::endl;
     324                 :       5557 :         ArithMSum::debugPrintMonomialSum(msum, "bound-int-debug");
     325         [ +  + ]:      16773 :         for (std::map<Node, Node>::iterator it = msum.begin(); it != msum.end();
     326                 :      11216 :              ++it)
     327                 :            :         {
     328         [ +  + ]:      20273 :           if (!it->first.isNull() && it->first.getKind() == Kind::BOUND_VARIABLE
     329                 :      31489 :               && !isBound(q, it->first))
     330                 :            :           {
     331                 :            :             // if not bound in another way
     332                 :       2847 :             if (bound_lit_type_map.find(it->first) == bound_lit_type_map.end()
     333 [ +  + ][ +  + ]:       2847 :                 || bound_lit_type_map[it->first] == BOUND_INT_RANGE)
                 [ +  + ]
     334                 :            :             {
     335                 :       2812 :               Node veq;
     336         [ +  - ]:       2812 :               if (ArithMSum::isolate(it->first, msum, veq, Kind::GEQ) != 0)
     337                 :            :               {
     338                 :       2812 :                 Node n1 = veq[0];
     339                 :       2812 :                 Node n2 = veq[1];
     340         [ +  + ]:       2812 :                 if (pol)
     341                 :            :                 {
     342                 :            :                   // flip
     343                 :       1230 :                   n1 = veq[1];
     344                 :       1230 :                   n2 = veq[0];
     345         [ +  + ]:       1230 :                   if (n1.getKind() == Kind::BOUND_VARIABLE)
     346                 :            :                   {
     347                 :          2 :                     n2 = nm->mkNode(Kind::ADD, n2, nm->mkConstInt(Rational(1)));
     348                 :            :                   }
     349                 :            :                   else
     350                 :            :                   {
     351                 :            :                     n1 =
     352                 :       1228 :                         nm->mkNode(Kind::ADD, n1, nm->mkConstInt(Rational(-1)));
     353                 :            :                   }
     354                 :       1230 :                   veq = nm->mkNode(Kind::GEQ, n1, n2);
     355                 :            :                 }
     356         [ +  - ]:       5624 :                 Trace("bound-int-debug")
     357                 :          0 :                     << "Isolated for " << it->first << " : (" << n1
     358                 :       2812 :                     << " >= " << n2 << ")" << std::endl;
     359         [ +  + ]:       2812 :                 Node t = n1 == it->first ? n2 : n1;
     360         [ +  + ]:       2812 :                 if (!hasNonBoundVar(q, t))
     361                 :            :                 {
     362         [ +  - ]:       5148 :                   Trace("bound-int-debug")
     363                 :       2574 :                       << "The bound is relevant." << std::endl;
     364         [ +  + ]:       2574 :                   int loru = n1 == it->first ? 0 : 1;
     365                 :       2574 :                   bound_lit_type_map[it->first] = BOUND_INT_RANGE;
     366                 :       2574 :                   bound_int_range_term[loru][it->first] = t;
     367                 :       2574 :                   bound_lit_map[loru][it->first] = n;
     368                 :       2574 :                   bound_lit_pol_map[loru][it->first] = pol;
     369                 :            :                 }
     370                 :            :                 else
     371                 :            :                 {
     372         [ +  - ]:        476 :                   Trace("bound-int-debug")
     373                 :          0 :                       << "The term " << t << " has non-bound variable."
     374                 :        238 :                       << std::endl;
     375                 :            :                 }
     376                 :       2812 :               }
     377                 :       2812 :             }
     378                 :            :           }
     379                 :            :         }
     380                 :            :       }
     381                 :       5557 :     }
     382                 :            :   }
     383         [ +  + ]:       1119 :   else if (n.getKind() == Kind::SET_MEMBER)
     384                 :            :   {
     385                 :            :     // Note this is incomplete when combined with cardinality constraints,
     386                 :            :     // since we may introduce slack elements during model construction.
     387                 :            :     // Here, fmfBound should be enabled, otherwise the incompleteness check
     388                 :            :     // in the theory of sets is out of sync.
     389 [ -  + ][ -  + ]:        235 :     Assert(options().quantifiers.fmfBound);
                 [ -  - ]
     390                 :        235 :     if (!pol && !hasNonBoundVar(q, n[1]))
     391                 :            :     {
     392                 :        228 :       std::vector<Node> bound_vars;
     393                 :        228 :       std::map<Node, bool> visited;
     394                 :        228 :       processMatchBoundVars(q, n[0], bound_vars, visited);
     395         [ +  + ]:        353 :       for (unsigned i = 0; i < bound_vars.size(); i++)
     396                 :            :       {
     397                 :        125 :         Node v = bound_vars[i];
     398         [ +  - ]:        250 :         Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n
     399                 :        125 :                                  << " is membership." << std::endl;
     400                 :        125 :         bound_lit_type_map[v] = BOUND_SET_MEMBER;
     401                 :        125 :         bound_lit_map[2][v] = n;
     402                 :        125 :         bound_lit_pol_map[2][v] = pol;
     403                 :        125 :       }
     404                 :        228 :     }
     405                 :            :   }
     406                 :            :   else
     407                 :            :   {
     408 [ +  - ][ +  - ]:        884 :     Assert(n.getKind() != Kind::LEQ && n.getKind() != Kind::LT
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
     409                 :            :            && n.getKind() != Kind::GT);
     410                 :            :   }
     411                 :      16620 : }
     412                 :            : 
     413                 :      76723 : bool BoundedIntegers::needsCheck(Theory::Effort e)
     414                 :            : {
     415                 :      76723 :   return e == Theory::EFFORT_LAST_CALL;
     416                 :            : }
     417                 :            : 
     418                 :      25817 : void BoundedIntegers::check(CVC5_UNUSED Theory::Effort e, QEffort quant_e)
     419                 :            : {
     420         [ +  + ]:      25817 :   if (quant_e != QEFFORT_STANDARD)
     421                 :            :   {
     422                 :      16841 :     return;
     423                 :            :   }
     424         [ +  - ]:       8976 :   Trace("bint-engine") << "---Bounded Integers---" << std::endl;
     425                 :       8976 :   bool addedLemma = false;
     426                 :            :   // make sure proxies are up-to-date with range
     427         [ +  + ]:      12024 :   for (const Node& r : d_ranges)
     428                 :            :   {
     429                 :       3048 :     Node prangeLem = d_rms[r]->proxyCurrentRangeLemma();
     430         [ +  + ]:       3048 :     if (!prangeLem.isNull())
     431                 :            :     {
     432         [ +  - ]:        434 :       Trace("bound-int-lemma")
     433                 :        217 :           << "*** bound int : proxy lemma : " << prangeLem << std::endl;
     434                 :        217 :       d_qim.addPendingLemma(prangeLem, InferenceId::QUANTIFIERS_BINT_PROXY);
     435                 :        217 :       addedLemma = true;
     436                 :            :     }
     437                 :       3048 :   }
     438         [ +  - ]:       8976 :   Trace("bint-engine") << "   addedLemma = " << addedLemma << std::endl;
     439                 :            : }
     440                 :       1495 : void BoundedIntegers::setBoundedVar(Node q, Node v, BoundVarType bound_type)
     441                 :            : {
     442                 :       1495 :   d_bound_type[q][v] = bound_type;
     443                 :       1495 :   d_set_nums[q][v] = d_set[q].size();
     444                 :       1495 :   d_set[q].push_back(v);
     445         [ +  - ]:       2990 :   Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v
     446                 :       1495 :                          << std::endl;
     447                 :       1495 : }
     448                 :            : 
     449                 :      24149 : void BoundedIntegers::checkOwnership(Node f)
     450                 :            : {
     451                 :            :   // this needs to be done at preregister since it affects e.g. QuantDSplit's
     452                 :            :   // preregister
     453         [ +  - ]:      24149 :   Trace("bound-int") << "check ownership quantifier " << f << std::endl;
     454                 :            : 
     455                 :            :   // determine if we should look at the quantified formula at all
     456         [ +  + ]:      24149 :   if (!options().quantifiers.fmfBound)
     457                 :            :   {
     458                 :            :     // only applying it to internal quantifiers
     459                 :      23727 :     QuantAttributes& qattr = d_qreg.getQuantAttributes();
     460         [ +  + ]:      23727 :     if (!qattr.isQuantBounded(f))
     461                 :            :     {
     462         [ +  - ]:      22794 :       Trace("bound-int") << "...not bounded, skip" << std::endl;
     463                 :      22794 :       return;
     464                 :            :     }
     465                 :            :   }
     466                 :            : 
     467                 :       1355 :   NodeManager* nm = nodeManager();
     468                 :            : 
     469                 :            :   bool success;
     470         [ +  + ]:       2825 :   do
     471                 :            :   {
     472                 :       2825 :     std::map<Node, unsigned> bound_lit_type_map;
     473                 :       2825 :     std::map<int, std::map<Node, Node> > bound_lit_map;
     474                 :       2825 :     std::map<int, std::map<Node, bool> > bound_lit_pol_map;
     475                 :       2825 :     std::map<int, std::map<Node, Node> > bound_int_range_term;
     476                 :       2825 :     std::map<Node, std::vector<Node> > bound_fixed_set;
     477                 :       2825 :     success = false;
     478                 :       2825 :     process(f,
     479                 :            :             f[1],
     480                 :            :             true,
     481                 :            :             bound_lit_type_map,
     482                 :            :             bound_lit_map,
     483                 :            :             bound_lit_pol_map,
     484                 :            :             bound_int_range_term,
     485                 :            :             bound_fixed_set);
     486                 :            :     // for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it !=
     487                 :            :     // d_bounds[0][f].end(); ++it ){
     488                 :       2825 :     for (std::map<Node, unsigned>::iterator it = bound_lit_type_map.begin();
     489         [ +  + ]:       4256 :          it != bound_lit_type_map.end();
     490                 :       1431 :          ++it)
     491                 :            :     {
     492                 :       1431 :       Node v = it->first;
     493         [ +  - ]:       1431 :       if (!isBound(f, v))
     494                 :            :       {
     495                 :       1431 :         bool setBoundVar = false;
     496         [ +  + ]:       1431 :         if (it->second == BOUND_INT_RANGE)
     497                 :            :         {
     498                 :            :           // must have both
     499                 :       1308 :           std::map<Node, Node>& blm0 = bound_lit_map[0];
     500                 :       1308 :           std::map<Node, Node>& blm1 = bound_lit_map[1];
     501 [ +  + ][ +  + ]:       1308 :           if (blm0.find(v) != blm0.end() && blm1.find(v) != blm1.end())
                 [ +  + ]
     502                 :            :           {
     503                 :       1181 :             setBoundedVar(f, v, BOUND_INT_RANGE);
     504                 :       1181 :             setBoundVar = true;
     505         [ +  + ]:       3543 :             for (unsigned b = 0; b < 2; b++)
     506                 :            :             {
     507                 :            :               // set the bounds
     508 [ -  + ][ -  + ]:       2362 :               Assert(bound_int_range_term[b].find(v)
                 [ -  - ]
     509                 :            :                      != bound_int_range_term[b].end());
     510                 :       2362 :               d_bounds[b][f][v] = bound_int_range_term[b][v];
     511                 :            :             }
     512                 :            :             Node r =
     513                 :       2362 :                 nm->mkNode(Kind::SUB, d_bounds[1][f][v], d_bounds[0][f][v]);
     514                 :       1181 :             d_range[f][v] = rewrite(r);
     515         [ +  - ]:       2362 :             Trace("bound-int") << "Variable " << v
     516                 :          0 :                                << " is bound because of int range literals "
     517                 :          0 :                                << bound_lit_map[0][v] << " and "
     518                 :       1181 :                                << bound_lit_map[1][v] << std::endl;
     519                 :       1181 :           }
     520                 :            :         }
     521         [ +  + ]:        123 :         else if (it->second == BOUND_SET_MEMBER)
     522                 :            :         {
     523                 :        113 :           setBoundedVar(f, v, BOUND_SET_MEMBER);
     524                 :        113 :           setBoundVar = true;
     525                 :        113 :           d_setm_range[f][v] = bound_lit_map[2][v][1];
     526                 :        113 :           d_setm_range_lit[f][v] = bound_lit_map[2][v];
     527                 :        113 :           Node cardTerm = nm->mkNode(Kind::SET_CARD, d_setm_range[f][v]);
     528                 :            :           // Note that we avoid reasoning about cardinality by eagerly
     529                 :            :           // eliminating set.card for literals as they are introduced.
     530                 :        113 :           d_range[f][v] = cardTerm;
     531         [ +  - ]:        226 :           Trace("bound-int") << "Variable " << v
     532                 :          0 :                              << " is bound because of set membership literal "
     533                 :        113 :                              << bound_lit_map[2][v] << std::endl;
     534                 :        113 :         }
     535         [ +  - ]:         10 :         else if (it->second == BOUND_FIXED_SET)
     536                 :            :         {
     537                 :         10 :           setBoundedVar(f, v, BOUND_FIXED_SET);
     538                 :         10 :           setBoundVar = true;
     539         [ +  + ]:         30 :           for (unsigned i = 0; i < bound_fixed_set[v].size(); i++)
     540                 :            :           {
     541                 :         20 :             Node t = bound_fixed_set[v][i];
     542         [ +  + ]:         20 :             if (expr::hasBoundVar(t))
     543                 :            :             {
     544                 :          6 :               d_fixed_set_ngr_range[f][v].push_back(t);
     545                 :            :             }
     546                 :            :             else
     547                 :            :             {
     548                 :         14 :               d_fixed_set_gr_range[f][v].push_back(t);
     549                 :            :             }
     550                 :         20 :           }
     551         [ +  - ]:         20 :           Trace("bound-int") << "Variable " << v
     552                 :          0 :                              << " is bound because of disequality conjunction "
     553                 :         10 :                              << bound_lit_map[3][v] << std::endl;
     554                 :            :         }
     555         [ +  + ]:       1431 :         if (setBoundVar)
     556                 :            :         {
     557                 :       1304 :           success = true;
     558                 :            :           // set Attributes on literals
     559         [ +  + ]:       3912 :           for (unsigned b = 0; b < 2; b++)
     560                 :            :           {
     561                 :       2608 :             std::map<Node, Node>& blm = bound_lit_map[b];
     562         [ +  + ]:       2608 :             if (blm.find(v) != blm.end())
     563                 :            :             {
     564                 :       2369 :               std::map<Node, bool>& blmp = bound_lit_pol_map[b];
     565                 :            :               // WARNING_CANDIDATE:
     566                 :            :               // This assertion may fail. We intentionally do not enable this in
     567                 :            :               // production as it is considered safe for this to fail. We fail
     568                 :            :               // the assertion in debug mode to have this instance raised to
     569                 :            :               // our attention.
     570 [ -  + ][ -  + ]:       2369 :               Assert(blmp.find(v) != blmp.end());
                 [ -  - ]
     571                 :            :               BoundIntLitAttribute bila;
     572         [ +  + ]:       2369 :               bound_lit_map[b][v].setAttribute(bila, blmp[v] ? 1 : 0);
     573                 :            :             }
     574                 :            :             else
     575                 :            :             {
     576 [ -  + ][ -  + ]:        239 :               Assert(it->second != BOUND_INT_RANGE);
                 [ -  - ]
     577                 :            :             }
     578                 :            :           }
     579                 :            :         }
     580                 :            :       }
     581                 :       1431 :     }
     582         [ +  + ]:       2825 :     if (!success)
     583                 :            :     {
     584                 :            :       // resort to setting a finite bound on a variable
     585         [ +  + ]:       3178 :       for (unsigned i = 0; i < f[0].getNumChildren(); i++)
     586                 :            :       {
     587         [ +  + ]:       1823 :         if (d_bound_type[f].find(f[0][i]) == d_bound_type[f].end())
     588                 :            :         {
     589                 :        566 :           TypeNode tn = f[0][i].getType();
     590 [ +  + ][ +  + ]:        332 :           if ((tn.isUninterpretedSort() && d_env.isFiniteType(tn))
                 [ -  - ]
     591 [ +  + ][ +  + ]:        332 :               || d_qreg.getQuantifiersBoundInference().mayComplete(tn))
         [ +  + ][ +  + ]
                 [ -  - ]
     592                 :            :           {
     593                 :        191 :             success = true;
     594                 :        191 :             setBoundedVar(f, f[0][i], BOUND_FINITE);
     595                 :        191 :             break;
     596                 :            :           }
     597         [ +  + ]:        283 :         }
     598                 :            :       }
     599                 :            :     }
     600                 :       2825 :   } while (success);
     601                 :            : 
     602         [ -  + ]:       1355 :   if (TraceIsOn("bound-int"))
     603                 :            :   {
     604         [ -  - ]:          0 :     Trace("bound-int") << "Bounds are : " << std::endl;
     605         [ -  - ]:          0 :     for (unsigned i = 0; i < f[0].getNumChildren(); i++)
     606                 :            :     {
     607                 :          0 :       Node v = f[0][i];
     608         [ -  - ]:          0 :       if (std::find(d_set[f].begin(), d_set[f].end(), v) != d_set[f].end())
     609                 :            :       {
     610                 :          0 :         Assert(d_bound_type[f].find(v) != d_bound_type[f].end());
     611         [ -  - ]:          0 :         if (d_bound_type[f][v] == BOUND_INT_RANGE)
     612                 :            :         {
     613         [ -  - ]:          0 :           Trace("bound-int") << "  " << d_bounds[0][f][v] << " <= " << v
     614                 :          0 :                              << " <= " << d_bounds[1][f][v] << " (range is "
     615                 :          0 :                              << d_range[f][v] << ")" << std::endl;
     616                 :            :         }
     617         [ -  - ]:          0 :         else if (d_bound_type[f][v] == BOUND_SET_MEMBER)
     618                 :            :         {
     619         [ -  - ]:          0 :           if (d_setm_range_lit[f][v][0] == v)
     620                 :            :           {
     621         [ -  - ]:          0 :             Trace("bound-int")
     622                 :          0 :                 << "  " << v << " in " << d_setm_range[f][v] << std::endl;
     623                 :            :           }
     624                 :            :           else
     625                 :            :           {
     626         [ -  - ]:          0 :             Trace("bound-int") << "  " << v << " unifiable in "
     627                 :          0 :                                << d_setm_range_lit[f][v] << std::endl;
     628                 :            :           }
     629                 :            :         }
     630         [ -  - ]:          0 :         else if (d_bound_type[f][v] == BOUND_FIXED_SET)
     631                 :            :         {
     632         [ -  - ]:          0 :           Trace("bound-int") << "  " << v << " in { ";
     633         [ -  - ]:          0 :           for (TNode fnr : d_fixed_set_ngr_range[f][v])
     634                 :            :           {
     635         [ -  - ]:          0 :             Trace("bound-int") << fnr << " ";
     636                 :          0 :           }
     637         [ -  - ]:          0 :           for (TNode fgr : d_fixed_set_gr_range[f][v])
     638                 :            :           {
     639         [ -  - ]:          0 :             Trace("bound-int") << fgr << " ";
     640                 :          0 :           }
     641         [ -  - ]:          0 :           Trace("bound-int") << "}" << std::endl;
     642                 :            :         }
     643         [ -  - ]:          0 :         else if (d_bound_type[f][v] == BOUND_FINITE)
     644                 :            :         {
     645         [ -  - ]:          0 :           Trace("bound-int")
     646                 :          0 :               << "  " << v << " has small finite type." << std::endl;
     647                 :            :         }
     648                 :            :         else
     649                 :            :         {
     650         [ -  - ]:          0 :           Trace("bound-int") << "  " << v << " has unknown bound." << std::endl;
     651                 :          0 :           DebugUnhandled();
     652                 :            :         }
     653                 :            :       }
     654                 :            :       else
     655                 :            :       {
     656         [ -  - ]:          0 :         Trace("bound-int") << "  " << "*** " << v << " is unbounded."
     657                 :          0 :                            << std::endl;
     658                 :            :       }
     659                 :          0 :     }
     660                 :            :   }
     661                 :            : 
     662                 :       1355 :   bool bound_success = true;
     663         [ +  + ]:       2848 :   for (unsigned i = 0; i < f[0].getNumChildren(); i++)
     664                 :            :   {
     665         [ +  + ]:       1561 :     if (d_bound_type[f].find(f[0][i]) == d_bound_type[f].end())
     666                 :            :     {
     667         [ +  - ]:        136 :       Trace("bound-int-warn")
     668                 :         68 :           << "Warning : Bounded Integers : Due to quantification on " << f[0][i]
     669                 :         68 :           << ", could not find bounds for " << f << std::endl;
     670                 :         68 :       bound_success = false;
     671                 :         68 :       break;
     672                 :            :     }
     673                 :            :   }
     674                 :            : 
     675         [ +  + ]:       1355 :   if (bound_success)
     676                 :            :   {
     677                 :       1287 :     d_bound_quants.push_back(f);
     678                 :       1287 :     DecisionManager* dm = d_qim.getDecisionManager();
     679         [ +  + ]:       2777 :     for (unsigned i = 0; i < d_set[f].size(); i++)
     680                 :            :     {
     681                 :       1490 :       Node v = d_set[f][i];
     682                 :       1490 :       std::map<Node, Node>::iterator itr = d_range[f].find(v);
     683         [ +  + ]:       1490 :       if (itr != d_range[f].end())
     684                 :            :       {
     685                 :       1292 :         Node r = itr->second;
     686 [ -  + ][ -  + ]:       1292 :         Assert(!r.isNull());
                 [ -  - ]
     687                 :       1292 :         bool isProxy = false;
     688         [ +  + ]:       1292 :         if (expr::hasBoundVar(r))
     689                 :            :         {
     690                 :            :           // introduce a new bound
     691                 :        254 :           Node new_range = NodeManager::mkDummySkolem("bir", r.getType());
     692                 :        127 :           d_nground_range[f][v] = r;
     693                 :        127 :           d_range[f][v] = new_range;
     694                 :        127 :           r = new_range;
     695                 :        127 :           isProxy = true;
     696                 :        127 :         }
     697         [ +  + ]:       1292 :         if (!r.isConst())
     698                 :            :         {
     699         [ +  + ]:       1235 :           if (d_rms.find(r) == d_rms.end())
     700                 :            :           {
     701         [ +  - ]:       2260 :             Trace("bound-int")
     702                 :          0 :                 << "For " << v
     703                 :          0 :                 << ", bounded Integer Module will try to minimize : " << r
     704                 :       1130 :                 << std::endl;
     705                 :       1130 :             d_ranges.push_back(r);
     706                 :       2260 :             d_rms[r].reset(new IntRangeDecisionHeuristic(
     707                 :       1130 :                 d_env, r, d_qstate.getValuation(), isProxy));
     708                 :       1130 :             dm->registerStrategy(DecisionManager::STRAT_QUANT_BOUND_INT_SIZE,
     709                 :       1130 :                                  d_rms[r].get());
     710                 :            :           }
     711                 :            :         }
     712                 :       1292 :       }
     713                 :       1490 :     }
     714                 :            :   }
     715                 :            : }
     716                 :            : 
     717                 :      16408 : bool BoundedIntegers::isBound(Node q, Node v) const
     718                 :            : {
     719                 :      16408 :   std::map<Node, std::vector<Node> >::const_iterator its = d_set.find(q);
     720         [ +  + ]:      16408 :   if (its == d_set.end())
     721                 :            :   {
     722                 :      10853 :     return false;
     723                 :            :   }
     724                 :       5555 :   return std::find(its->second.begin(), its->second.end(), v)
     725                 :      11110 :          != its->second.end();
     726                 :            : }
     727                 :            : 
     728                 :      10113 : BoundVarType BoundedIntegers::getBoundVarType(Node q, Node v) const
     729                 :            : {
     730                 :            :   std::map<Node, std::map<Node, BoundVarType> >::const_iterator itb =
     731                 :      10113 :       d_bound_type.find(q);
     732         [ +  + ]:      10113 :   if (itb == d_bound_type.end())
     733                 :            :   {
     734                 :       1858 :     return BOUND_NONE;
     735                 :            :   }
     736                 :       8255 :   std::map<Node, BoundVarType>::const_iterator it = itb->second.find(v);
     737         [ +  + ]:       8255 :   if (it == itb->second.end())
     738                 :            :   {
     739                 :        354 :     return BOUND_NONE;
     740                 :            :   }
     741                 :       7901 :   return it->second;
     742                 :            : }
     743                 :            : 
     744                 :       8744 : void BoundedIntegers::getBoundVarIndices(Node q,
     745                 :            :                                          std::vector<size_t>& indices) const
     746                 :            : {
     747                 :       8744 :   std::map<Node, std::vector<Node> >::const_iterator it = d_set.find(q);
     748         [ +  + ]:       8744 :   if (it != d_set.end())
     749                 :            :   {
     750         [ +  + ]:      11682 :     for (const Node& v : it->second)
     751                 :            :     {
     752                 :       6644 :       indices.push_back(TermUtil::getVariableNum(q, v));
     753                 :            :     }
     754                 :            :   }
     755                 :       8744 : }
     756                 :            : 
     757                 :       5159 : void BoundedIntegers::getBounds(
     758                 :            :     Node f, Node v, RepSetIterator* rsi, Node& l, Node& u)
     759                 :            : {
     760                 :       5159 :   l = d_bounds[0][f][v];
     761                 :       5159 :   u = d_bounds[1][f][v];
     762         [ +  + ]:       5159 :   if (d_nground_range[f].find(v) != d_nground_range[f].end())
     763                 :            :   {
     764                 :            :     // get the substitution
     765                 :       1427 :     std::vector<Node> vars;
     766                 :       1427 :     std::vector<Node> subs;
     767         [ +  + ]:       1427 :     if (getRsiSubsitution(f, v, vars, subs, rsi))
     768                 :            :     {
     769                 :       1346 :       u = u.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
     770                 :       1346 :       l = l.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
     771                 :            :     }
     772                 :            :     else
     773                 :            :     {
     774                 :         81 :       u = Node::null();
     775                 :         81 :       l = Node::null();
     776                 :            :     }
     777                 :       1427 :   }
     778                 :       5159 : }
     779                 :            : 
     780                 :       2620 : void BoundedIntegers::getBoundValues(
     781                 :            :     Node f, Node v, RepSetIterator* rsi, Node& l, Node& u)
     782                 :            : {
     783                 :       2620 :   getBounds(f, v, rsi, l, u);
     784         [ +  - ]:       5240 :   Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u
     785                 :       2620 :                          << std::endl;
     786         [ +  + ]:       2620 :   if (!l.isNull())
     787                 :            :   {
     788                 :       2539 :     l = d_treg.getModel()->getValue(l);
     789                 :            :   }
     790         [ +  + ]:       2620 :   if (!u.isNull())
     791                 :            :   {
     792                 :       2539 :     u = d_treg.getModel()->getValue(u);
     793                 :            :   }
     794         [ +  - ]:       2620 :   Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl;
     795                 :       2620 :   return;
     796                 :            : }
     797                 :            : 
     798                 :        834 : bool BoundedIntegers::isGroundRange(Node q, Node v)
     799                 :            : {
     800         [ +  - ]:        834 :   if (isBound(q, v))
     801                 :            :   {
     802         [ +  + ]:        834 :     if (d_bound_type[q][v] == BOUND_INT_RANGE)
     803                 :            :     {
     804                 :       1300 :       return !expr::hasBoundVar(getLowerBound(q, v))
     805                 :       1300 :              && !expr::hasBoundVar(getUpperBound(q, v));
     806                 :            :     }
     807         [ +  + ]:        184 :     else if (d_bound_type[q][v] == BOUND_SET_MEMBER)
     808                 :            :     {
     809                 :         52 :       return !expr::hasBoundVar(d_setm_range[q][v]);
     810                 :            :     }
     811         [ +  - ]:        132 :     else if (d_bound_type[q][v] == BOUND_FIXED_SET)
     812                 :            :     {
     813                 :        132 :       return !d_fixed_set_ngr_range[q][v].empty();
     814                 :            :     }
     815                 :            :   }
     816                 :          0 :   return false;
     817                 :            : }
     818                 :            : 
     819                 :        239 : Node BoundedIntegers::getSetRange(Node q, Node v, RepSetIterator* rsi)
     820                 :            : {
     821                 :        239 :   Node sr = d_setm_range[q][v];
     822         [ +  + ]:        239 :   if (d_nground_range[q].find(v) != d_nground_range[q].end())
     823                 :            :   {
     824         [ +  - ]:         28 :     Trace("bound-int-rsi-debug")
     825                 :         14 :         << sr << " is non-ground, apply substitution..." << std::endl;
     826                 :            :     // get the substitution
     827                 :         14 :     std::vector<Node> vars;
     828                 :         14 :     std::vector<Node> subs;
     829         [ +  + ]:         14 :     if (getRsiSubsitution(q, v, vars, subs, rsi))
     830                 :            :     {
     831         [ +  - ]:         14 :       Trace("bound-int-rsi-debug")
     832                 :          7 :           << "  apply " << vars << " -> " << subs << std::endl;
     833                 :          7 :       sr = sr.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
     834                 :            :     }
     835                 :            :     else
     836                 :            :     {
     837                 :          7 :       sr = Node::null();
     838                 :            :     }
     839                 :         14 :   }
     840                 :        239 :   return sr;
     841                 :          0 : }
     842                 :            : 
     843                 :        239 : Node BoundedIntegers::getSetRangeValue(Node q, Node v, RepSetIterator* rsi)
     844                 :            : {
     845                 :        478 :   Node sr = getSetRange(q, v, rsi);
     846         [ +  + ]:        239 :   if (sr.isNull())
     847                 :            :   {
     848                 :          7 :     return sr;
     849                 :            :   }
     850         [ +  - ]:        232 :   Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl;
     851 [ -  + ][ -  + ]:        232 :   Assert(!expr::hasFreeVar(sr));
                 [ -  - ]
     852                 :        232 :   Node sro = sr;
     853                 :        232 :   sr = d_treg.getModel()->getValue(sr);
     854                 :            :   // if non-constant, then sr does not occur in the model, we fail
     855         [ -  + ]:        232 :   if (!sr.isConst())
     856                 :            :   {
     857                 :          0 :     return Node::null();
     858                 :            :   }
     859         [ +  - ]:        232 :   Trace("bound-int-rsi") << "Value is " << sr << std::endl;
     860         [ +  + ]:        232 :   if (sr.getKind() == Kind::SET_EMPTY)
     861                 :            :   {
     862                 :          4 :     return sr;
     863                 :            :   }
     864                 :            :   // we can use choice functions for canonical symbolic instantiations
     865                 :        228 :   unsigned srCard = 0;
     866         [ +  + ]:        445 :   while (sr.getKind() == Kind::SET_UNION)
     867                 :            :   {
     868 [ -  + ][ -  + ]:        217 :     Assert(sr[0].getKind() == Kind::SET_SINGLETON);
                 [ -  - ]
     869                 :        217 :     srCard++;
     870                 :        217 :     sr = sr[1];
     871                 :            :   }
     872 [ -  + ][ -  + ]:        228 :   Assert(sr.getKind() == Kind::SET_SINGLETON);
                 [ -  - ]
     873                 :        228 :   srCard++;
     874         [ +  - ]:        228 :   Trace("bound-int-rsi") << "...cardinality is " << srCard << std::endl;
     875                 :            :   // get the characteristic set
     876                 :        228 :   Node nsr = sets::NormalForm::getCharacteristicSet(nodeManager(), sro, srCard);
     877                 :            :   // turns the concrete set value of sro into a canonical representation
     878                 :            :   //   e.g.
     879                 :            :   // singleton(0) union singleton(1)
     880                 :            :   //   becomes
     881                 :            :   // C1 union (set.singleton (set.choose (set.minus S C1)))
     882                 :            :   // where C1 = (set.singleton (set.choose S)).
     883         [ +  - ]:        228 :   Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl;
     884                 :        228 :   return nsr;
     885                 :        239 : }
     886                 :            : 
     887                 :       1493 : bool BoundedIntegers::getRsiSubsitution(Node q,
     888                 :            :                                         Node v,
     889                 :            :                                         std::vector<Node>& vars,
     890                 :            :                                         std::vector<Node>& subs,
     891                 :            :                                         RepSetIterator* rsi)
     892                 :            : {
     893         [ +  - ]:       2986 :   Trace("bound-int-rsi") << "Get bound value in model of variable " << v
     894                 :       1493 :                          << std::endl;
     895 [ -  + ][ -  + ]:       1493 :   Assert(d_set_nums[q].find(v) != d_set_nums[q].end());
                 [ -  - ]
     896                 :       1493 :   int vindex = d_set_nums[q][v];
     897 [ -  + ][ -  + ]:       1493 :   Assert(d_set_nums[q][v] == vindex);
                 [ -  - ]
     898         [ +  - ]:       1493 :   Trace("bound-int-rsi-debug") << "  index order is " << vindex << std::endl;
     899                 :            :   // must take substitution for all variables that are iterating at higher level
     900         [ +  + ]:       3266 :   for (int i = 0; i < vindex; i++)
     901                 :            :   {
     902 [ -  + ][ -  + ]:       1773 :     Assert(d_set_nums[q][d_set[q][i]] == i);
                 [ -  - ]
     903         [ +  - ]:       3546 :     Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " "
     904                 :       1773 :                            << i << std::endl;
     905                 :       1773 :     int vo = rsi->getVariableOrder(i);
     906 [ -  + ][ -  + ]:       1773 :     Assert(q[0][vo] == d_set[q][i]);
                 [ -  - ]
     907                 :       1773 :     TypeNode tn = d_set[q][i].getType();
     908                 :            :     // If the type of tn is not closed enumerable, we must map the value back
     909                 :            :     // to a term that appears in the same equivalence class as the constant.
     910                 :            :     // Notice that this is to ensure that unhandled values (e.g. uninterpreted
     911                 :            :     // constants, datatype values) do not enter instantiations/lemmas, which
     912                 :            :     // can lead to refutation unsoundness. However, it is important that we
     913                 :            :     // conversely do *not* map terms to values in other cases. In particular,
     914                 :            :     // replacing a constant c with a term t can lead to solution unsoundness
     915                 :            :     // if we are instantiating a quantified formula that corresponds to a
     916                 :            :     // reduction for t, since then the reduction is using circular reasoning:
     917                 :            :     // the current value of t is being used to reason about the range of
     918                 :            :     // its axiomatization. This is limited to reductions in the theory of
     919                 :            :     // strings, which use quantification on integers only. Note this
     920                 :            :     // impacts only quantified formulas with 2+ dimensions and dependencies
     921                 :            :     // between dimensions, e.g. str.indexof_re reduction.
     922                 :       1773 :     Node t = rsi->getCurrentTerm(vo, !tn.isClosedEnumerable());
     923         [ +  - ]:       1773 :     Trace("bound-int-rsi") << "term : " << t << std::endl;
     924                 :       1773 :     vars.push_back(d_set[q][i]);
     925                 :       1773 :     subs.push_back(t);
     926                 :       1773 :   }
     927                 :            : 
     928                 :            :   // check if it has been instantiated
     929 [ +  - ][ +  + ]:       1493 :   if (!vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs))
                 [ +  + ]
     930                 :            :   {
     931                 :         96 :     if (d_bound_type[q][v] == BOUND_INT_RANGE
     932 [ +  + ][ +  + ]:         96 :         || d_bound_type[q][v] == BOUND_SET_MEMBER)
                 [ +  + ]
     933                 :            :     {
     934                 :            :       // must add the lemma
     935                 :         88 :       Node nn = d_nground_range[q][v];
     936                 :         88 :       nn = nn.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
     937                 :        176 :       Node lem = nodeManager()->mkNode(Kind::LEQ, nn, d_range[q][v]);
     938         [ +  - ]:        176 :       Trace("bound-int-lemma")
     939                 :          0 :           << "*** Add lemma to minimize instantiated non-ground term " << lem
     940                 :         88 :           << std::endl;
     941                 :         88 :       d_qim.lemma(lem, InferenceId::QUANTIFIERS_BINT_MIN_NG);
     942                 :         88 :     }
     943                 :         96 :     return false;
     944                 :            :   }
     945                 :            :   else
     946                 :            :   {
     947                 :       1397 :     return true;
     948                 :            :   }
     949                 :            : }
     950                 :            : 
     951                 :        680 : Node BoundedIntegers::matchBoundVar(Node v, Node t, Node e)
     952                 :            : {
     953         [ +  + ]:        680 :   if (t == v)
     954                 :            :   {
     955                 :        302 :     return e;
     956                 :            :   }
     957         [ +  + ]:        378 :   else if (t.getKind() == Kind::APPLY_CONSTRUCTOR)
     958                 :            :   {
     959         [ -  + ]:        302 :     if (e.getKind() == Kind::APPLY_CONSTRUCTOR)
     960                 :            :     {
     961         [ -  - ]:          0 :       if (t.getOperator() != e.getOperator())
     962                 :            :       {
     963                 :          0 :         return Node::null();
     964                 :            :       }
     965                 :            :     }
     966                 :        302 :     const DType& dt = datatypes::utils::datatypeOf(t.getOperator());
     967                 :        302 :     unsigned index = datatypes::utils::indexOf(t.getOperator());
     968                 :        302 :     bool sharedSel = options().datatypes.dtSharedSelectors;
     969         [ +  - ]:        378 :     for (unsigned i = 0; i < t.getNumChildren(); i++)
     970                 :            :     {
     971                 :        378 :       Node u;
     972         [ -  + ]:        378 :       if (e.getKind() == Kind::APPLY_CONSTRUCTOR)
     973                 :            :       {
     974                 :          0 :         u = matchBoundVar(v, t[i], e[i]);
     975                 :            :       }
     976                 :            :       else
     977                 :            :       {
     978                 :        378 :         Node se = datatypes::utils::applySelector(dt[index], i, sharedSel, e);
     979                 :        378 :         u = matchBoundVar(v, t[i], se);
     980                 :        378 :       }
     981         [ +  + ]:        378 :       if (!u.isNull())
     982                 :            :       {
     983                 :        302 :         return u;
     984                 :            :       }
     985         [ +  + ]:        378 :     }
     986                 :            :   }
     987                 :         76 :   return Node::null();
     988                 :            : }
     989                 :            : 
     990                 :       3437 : bool BoundedIntegers::getBoundElements(RepSetIterator* rsi,
     991                 :            :                                        bool initial,
     992                 :            :                                        Node q,
     993                 :            :                                        Node v,
     994                 :            :                                        std::vector<Node>& elements)
     995                 :            : {
     996                 :       3437 :   if (initial || !isGroundRange(q, v))
     997                 :            :   {
     998                 :       3127 :     elements.clear();
     999                 :       3127 :     BoundVarType bvt = getBoundVarType(q, v);
    1000         [ +  + ]:       3127 :     if (bvt == BOUND_INT_RANGE)
    1001                 :            :     {
    1002                 :       2620 :       Node l, u;
    1003                 :       2620 :       getBoundValues(q, v, rsi, l, u);
    1004 [ +  + ][ -  + ]:       2620 :       if (l.isNull() || u.isNull())
                 [ +  + ]
    1005                 :            :       {
    1006         [ +  - ]:        162 :         Trace("bound-int-warn")
    1007                 :          0 :             << "WARNING: Could not find integer bounds in model for " << v
    1008                 :         81 :             << " in " << q << std::endl;
    1009                 :            :         // failed, abort the iterator
    1010                 :         81 :         return false;
    1011                 :            :       }
    1012                 :            :       else
    1013                 :            :       {
    1014                 :       2539 :         NodeManager* nm = nodeManager();
    1015         [ +  - ]:       5078 :         Trace("bound-int-rsi") << "Can limit bounds of " << v << " to " << l
    1016                 :       2539 :                                << "..." << u << std::endl;
    1017                 :       5078 :         Node range = rewrite(nm->mkNode(Kind::SUB, u, l));
    1018         [ -  + ]:       2539 :         if (!range.isConst())
    1019                 :            :         {
    1020         [ -  - ]:          0 :           Trace("fmf-incomplete") << "Incomplete because of integer "
    1021                 :          0 :                                      "quantification, bounds are unknown for "
    1022                 :          0 :                                   << v << "." << std::endl;
    1023                 :          0 :           return false;
    1024                 :            :         }
    1025                 :       2539 :         Rational rat = range.getConst<Rational>();
    1026                 :            :         // 9999 is an arbitrary range past which we do not do exhaustive
    1027                 :            :         // bounded instantation, based on the check below.
    1028                 :       2539 :         Node tl = l;
    1029                 :       2539 :         Node tu = u;
    1030                 :       2539 :         getBounds(q, v, rsi, tl, tu);
    1031 [ +  - ][ +  - ]:       2539 :         Assert(!tl.isNull() && !tu.isNull());
         [ -  + ][ -  + ]
                 [ -  - ]
    1032         [ +  - ]:       2539 :         if (rat < Rational(9999))
    1033                 :            :         {
    1034                 :            :           // if negative, elements are empty
    1035         [ +  + ]:       2539 :           if (rat.sgn() >= 0)
    1036                 :            :           {
    1037                 :       2205 :             long rr = rat.getNumerator().getLong() + 1;
    1038         [ +  - ]:       4410 :             Trace("bound-int-rsi")
    1039                 :       2205 :                 << "Actual bound range is " << rr << std::endl;
    1040         [ +  + ]:      10953 :             for (long k = 0; k < rr; k++)
    1041                 :            :             {
    1042                 :      17496 :               Node t = nm->mkNode(Kind::ADD, tl, nm->mkConstInt(Rational(k)));
    1043                 :       8748 :               t = rewrite(t);
    1044                 :       8748 :               elements.push_back(t);
    1045                 :       8748 :             }
    1046                 :            :           }
    1047                 :       2539 :           return true;
    1048                 :            :         }
    1049                 :            :         else
    1050                 :            :         {
    1051         [ -  - ]:          0 :           Trace("fmf-incomplete") << "Incomplete because of integer "
    1052                 :          0 :                                      "quantification, bounds are too big for "
    1053                 :          0 :                                   << v << "." << std::endl;
    1054                 :          0 :           return false;
    1055                 :            :         }
    1056                 :       2539 :       }
    1057                 :       2620 :     }
    1058         [ +  + ]:        507 :     else if (bvt == BOUND_SET_MEMBER)
    1059                 :            :     {
    1060                 :        478 :       Node srv = getSetRangeValue(q, v, rsi);
    1061         [ +  + ]:        239 :       if (srv.isNull())
    1062                 :            :       {
    1063         [ +  - ]:         14 :         Trace("bound-int-warn")
    1064                 :          0 :             << "WARNING: Could not find set bound in model for " << v << " in "
    1065                 :          7 :             << q << std::endl;
    1066                 :          7 :         return false;
    1067                 :            :       }
    1068                 :            :       else
    1069                 :            :       {
    1070         [ +  - ]:        464 :         Trace("bound-int-rsi")
    1071                 :        232 :             << "Bounded by set membership : " << srv << std::endl;
    1072         [ +  + ]:        232 :         if (srv.getKind() != Kind::SET_EMPTY)
    1073                 :            :         {
    1074                 :            :           // collect the elements
    1075         [ +  + ]:        445 :           while (srv.getKind() == Kind::SET_UNION)
    1076                 :            :           {
    1077 [ -  + ][ -  + ]:        217 :             Assert(srv[1].getKind() == Kind::SET_SINGLETON);
                 [ -  - ]
    1078                 :        217 :             elements.push_back(srv[1][0]);
    1079                 :        217 :             srv = srv[0];
    1080                 :            :           }
    1081 [ -  + ][ -  + ]:        228 :           Assert(srv.getKind() == Kind::SET_SINGLETON);
                 [ -  - ]
    1082                 :        228 :           elements.push_back(srv[0]);
    1083                 :            :           // check if we need to do matching, for literals like ( tuple( v ) in
    1084                 :            :           // S )
    1085                 :        228 :           Node t = d_setm_range_lit[q][v][0];
    1086         [ +  + ]:        228 :           if (t != v)
    1087                 :            :           {
    1088                 :        157 :             std::vector<Node> elements_tmp;
    1089                 :        314 :             elements_tmp.insert(
    1090                 :        157 :                 elements_tmp.end(), elements.begin(), elements.end());
    1091                 :        157 :             elements.clear();
    1092         [ +  + ]:        459 :             for (unsigned i = 0; i < elements_tmp.size(); i++)
    1093                 :            :             {
    1094                 :            :               // do matching to determine v -> u
    1095                 :        604 :               Node u = matchBoundVar(v, t, elements_tmp[i]);
    1096         [ +  - ]:        604 :               Trace("bound-int-rsi-debug")
    1097                 :          0 :                   << "  unification : " << elements_tmp[i] << " = " << t
    1098                 :        302 :                   << " yields " << v << " -> " << u << std::endl;
    1099         [ +  - ]:        302 :               if (!u.isNull())
    1100                 :            :               {
    1101                 :        302 :                 elements.push_back(u);
    1102                 :            :               }
    1103                 :        302 :             }
    1104                 :        157 :           }
    1105                 :        228 :         }
    1106                 :        232 :         return true;
    1107                 :            :       }
    1108                 :        239 :     }
    1109         [ +  + ]:        268 :     else if (bvt == BOUND_FIXED_SET)
    1110                 :            :     {
    1111                 :            :       std::map<Node, std::vector<Node> >::iterator it =
    1112                 :         82 :           d_fixed_set_gr_range[q].find(v);
    1113         [ +  + ]:         82 :       if (it != d_fixed_set_gr_range[q].end())
    1114                 :            :       {
    1115         [ +  + ]:        166 :         for (unsigned i = 0; i < it->second.size(); i++)
    1116                 :            :         {
    1117                 :        104 :           elements.push_back(it->second[i]);
    1118                 :            :         }
    1119                 :            :       }
    1120                 :         82 :       it = d_fixed_set_ngr_range[q].find(v);
    1121         [ +  + ]:         82 :       if (it != d_fixed_set_ngr_range[q].end())
    1122                 :            :       {
    1123                 :         52 :         std::vector<Node> vars;
    1124                 :         52 :         std::vector<Node> subs;
    1125         [ +  + ]:         52 :         if (getRsiSubsitution(q, v, vars, subs, rsi))
    1126                 :            :         {
    1127         [ +  + ]:         98 :           for (unsigned i = 0; i < it->second.size(); i++)
    1128                 :            :           {
    1129                 :         54 :             Node t = it->second[i].substitute(
    1130                 :         54 :                 vars.begin(), vars.end(), subs.begin(), subs.end());
    1131                 :         54 :             elements.push_back(t);
    1132                 :         54 :           }
    1133                 :         44 :           return true;
    1134                 :            :         }
    1135                 :            :         else
    1136                 :            :         {
    1137                 :          8 :           return false;
    1138                 :            :         }
    1139                 :         52 :       }
    1140                 :            :       else
    1141                 :            :       {
    1142                 :         30 :         return true;
    1143                 :            :       }
    1144                 :            :     }
    1145                 :            :     else
    1146                 :            :     {
    1147                 :        186 :       return false;
    1148                 :            :     }
    1149                 :            :   }
    1150                 :            :   else
    1151                 :            :   {
    1152                 :            :     // no change required
    1153                 :        310 :     return true;
    1154                 :            :   }
    1155                 :            : }
    1156                 :            : 
    1157                 :            : /**
    1158                 :            :  * Attribute true for quantifiers that have been internally generated and
    1159                 :            :  * should be processed with the bounded integers module, e.g. quantified
    1160                 :            :  * formulas from reductions of string operators.
    1161                 :            :  *
    1162                 :            :  * Currently, this attribute is used for indicating that E-matching should
    1163                 :            :  * not be applied, as E-matching should not be applied to quantifiers
    1164                 :            :  * generated internally.
    1165                 :            :  *
    1166                 :            :  * This attribute can potentially be generalized to an identifier indicating
    1167                 :            :  * the internal source of the quantified formula (of which strings reduction
    1168                 :            :  * is one possibility).
    1169                 :            :  */
    1170                 :            : struct BoundedQuantAttributeId
    1171                 :            : {
    1172                 :            : };
    1173                 :            : typedef expr::Attribute<BoundedQuantAttributeId, bool> BoundedQuantAttribute;
    1174                 :            : /**
    1175                 :            :  * Mapping to a dummy node for marking an attribute on internal quantified
    1176                 :            :  * formulas. This ensures that reductions are deterministic.
    1177                 :            :  */
    1178                 :            : struct QInternalVarAttributeId
    1179                 :            : {
    1180                 :            : };
    1181                 :            : typedef expr::Attribute<QInternalVarAttributeId, Node> QInternalVarAttribute;
    1182                 :            : 
    1183                 :       1364 : Node BoundedIntegers::mkBoundedForall(NodeManager* nm, Node bvl, Node body)
    1184                 :            : {
    1185                 :            :   QInternalVarAttribute qiva;
    1186                 :       1364 :   Node qvar;
    1187         [ +  + ]:       1364 :   if (bvl.hasAttribute(qiva))
    1188                 :            :   {
    1189                 :        430 :     qvar = bvl.getAttribute(qiva);
    1190                 :            :   }
    1191                 :            :   else
    1192                 :            :   {
    1193                 :        934 :     qvar = NodeManager::mkDummySkolem("qinternal", nm->booleanType());
    1194                 :            :     // this dummy variable marks that the quantified formula is internal
    1195                 :        934 :     qvar.setAttribute(BoundedQuantAttribute(), true);
    1196                 :            :     // remember the dummy variable
    1197                 :        934 :     bvl.setAttribute(qiva, qvar);
    1198                 :            :   }
    1199                 :            :   // make the internal attribute, and put it in a singleton list
    1200                 :       1364 :   Node ip = nm->mkNode(Kind::INST_ATTRIBUTE, qvar);
    1201                 :       1364 :   Node ipl = nm->mkNode(Kind::INST_PATTERN_LIST, ip);
    1202                 :            :   // make the overall formula
    1203                 :       2728 :   return nm->mkNode(Kind::FORALL, bvl, body, ipl);
    1204                 :       1364 : }
    1205                 :            : 
    1206                 :      25444 : bool BoundedIntegers::isBoundedForallAttribute(Node var)
    1207                 :            : {
    1208                 :      25444 :   return var.getAttribute(BoundedQuantAttribute());
    1209                 :            : }
    1210                 :            : 
    1211                 :            : }  // namespace quantifiers
    1212                 :            : }  // namespace theory
    1213                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14