LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers - quant_split.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 159 166 95.8 %
Date: 2026-04-18 10:29:03 Functions: 12 13 92.3 %
Branches: 84 116 72.4 %

           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                 :            :  * Implementation of dynamic quantifiers splitting.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/quantifiers/quant_split.h"
      14                 :            : 
      15                 :            : #include "expr/bound_var_manager.h"
      16                 :            : #include "expr/dtype.h"
      17                 :            : #include "expr/dtype_cons.h"
      18                 :            : #include "options/quantifiers_options.h"
      19                 :            : #include "options/smt_options.h"
      20                 :            : #include "proof/proof.h"
      21                 :            : #include "proof/proof_generator.h"
      22                 :            : #include "theory/datatypes/theory_datatypes_utils.h"
      23                 :            : #include "theory/quantifiers/first_order_model.h"
      24                 :            : #include "theory/quantifiers/term_database.h"
      25                 :            : #include "util/rational.h"
      26                 :            : 
      27                 :            : using namespace cvc5::internal::kind;
      28                 :            : 
      29                 :            : namespace cvc5::internal {
      30                 :            : namespace theory {
      31                 :            : namespace quantifiers {
      32                 :            : 
      33                 :            : /**
      34                 :            :  * A proof generator for quantifiers splitting inferences
      35                 :            :  */
      36                 :            : class QuantDSplitProofGenerator : protected EnvObj, public ProofGenerator
      37                 :            : {
      38                 :            :  public:
      39                 :      13596 :   QuantDSplitProofGenerator(Env& env) : EnvObj(env), d_index(userContext()) {}
      40                 :      27151 :   virtual ~QuantDSplitProofGenerator() {}
      41                 :            :   /**
      42                 :            :    * Get proof for fact. This expects facts of the form
      43                 :            :    *    q = QuantDSplit::split(nm, q, n)
      44                 :            :    * We prove this by:
      45                 :            :    *    ------ QUANT_VAR_REORDERING ---------------------------- QUANT_DT_SPLIT
      46                 :            :    *    q = q'                      q' = QuantDSplit::split(nm, q', 0)
      47                 :            :    *    --------------------------------------------------------------- TRANS
      48                 :            :    *    q = QuantDSplit::split(nm, q, n)
      49                 :            :    *
      50                 :            :    * where the variables in q' is reordered from q such that the variable to
      51                 :            :    * split comes first.
      52                 :            :    *
      53                 :            :    * Note that this elaboration relies on the fact that the variables
      54                 :            :    * introduced for QuantDSplit::split(nm, q', 0) are the same as those for
      55                 :            :    * QuantDSplit::split(nm, q, n), since q and q' have the same body
      56                 :            :    * and these two splits refer to the same variables, which is the basis
      57                 :            :    * for the bound variables introduced (QDSplitVarAttribute).
      58                 :            :    */
      59                 :         40 :   std::shared_ptr<ProofNode> getProofFor(Node fact) override
      60                 :            :   {
      61                 :         80 :     CDProof cdp(d_env);
      62                 :            :     // find the index of the variable that was split for this lemma
      63                 :         40 :     context::CDHashMap<Node, size_t>::iterator it = d_index.find(fact);
      64         [ -  + ]:         40 :     if (it == d_index.end())
      65                 :            :     {
      66                 :          0 :       DebugUnhandled() << "QuantDSplitProofGenerator failed to get proof";
      67                 :            :       return nullptr;
      68                 :            :     }
      69                 :         40 :     Assert(fact.getKind() == Kind::EQUAL && fact[0].getKind() == Kind::FORALL);
      70                 :         40 :     Node q = fact[0];
      71                 :         40 :     std::vector<Node> transEq;
      72         [ +  + ]:         40 :     if (it->second != 0)
      73                 :            :     {
      74                 :            :       // must reorder variables
      75                 :         10 :       std::vector<Node> newVars;
      76                 :         10 :       newVars.push_back(q[0][it->second]);
      77         [ +  + ]:         34 :       for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
      78                 :            :       {
      79         [ +  + ]:         24 :         if (i != it->second)
      80                 :            :         {
      81                 :         14 :           newVars.emplace_back(q[0][i]);
      82                 :            :         }
      83                 :            :       }
      84                 :         10 :       std::vector<Node> qc(q.begin(), q.end());
      85                 :         10 :       NodeManager* nm = nodeManager();
      86                 :         10 :       qc[0] = nm->mkNode(Kind::BOUND_VAR_LIST, newVars);
      87                 :         10 :       Node qn = nm->mkNode(Kind::FORALL, qc);
      88                 :         10 :       Node eqq = q.eqNode(qn);
      89                 :         20 :       cdp.addStep(eqq, ProofRule::QUANT_VAR_REORDERING, {}, {eqq});
      90                 :         10 :       transEq.emplace_back(eqq);
      91                 :         10 :       q = qn;
      92                 :         10 :     }
      93                 :         40 :     Node eqq2 = q.eqNode(fact[1]);
      94                 :         40 :     cdp.addTheoryRewriteStep(eqq2, ProofRewriteRule::QUANT_DT_SPLIT);
      95         [ +  + ]:         40 :     if (!transEq.empty())
      96                 :            :     {
      97                 :         10 :       transEq.emplace_back(eqq2);
      98                 :         10 :       cdp.addStep(fact, ProofRule::TRANS, transEq, {});
      99                 :            :     }
     100                 :         40 :     return cdp.getProofFor(fact);
     101                 :         40 :   }
     102                 :            :   /** identify */
     103                 :          0 :   std::string identify() const override { return "QuantDSplitProofGenerator"; }
     104                 :            :   /**
     105                 :            :    * Notify that the given lemma used the given variable index to split. We
     106                 :            :    * store this in d_index and use it to guide proof reconstruction above.
     107                 :            :    */
     108                 :       1149 :   void notifyLemma(const Node& lem, size_t index) { d_index[lem] = index; }
     109                 :            : 
     110                 :            :  private:
     111                 :            :   /** Mapping from lemmas to their notified index */
     112                 :            :   context::CDHashMap<Node, size_t> d_index;
     113                 :            : };
     114                 :            : 
     115                 :      41109 : QuantDSplit::QuantDSplit(Env& env,
     116                 :            :                          QuantifiersState& qs,
     117                 :            :                          QuantifiersInferenceManager& qim,
     118                 :            :                          QuantifiersRegistry& qr,
     119                 :      41109 :                          TermRegistry& tr)
     120                 :            :     : QuantifiersModule(env, qs, qim, qr, tr),
     121                 :      41109 :       d_quant_to_reduce(userContext()),
     122                 :      41109 :       d_added_split(userContext()),
     123         [ +  + ]:      41109 :       d_pfgen(options().smt.produceProofs ? new QuantDSplitProofGenerator(d_env)
     124                 :      41109 :                                           : nullptr)
     125                 :            : {
     126                 :      41109 : }
     127                 :            : 
     128                 :      31297 : void QuantDSplit::checkOwnership(Node q)
     129                 :            : {
     130                 :            :   // If q is non-standard (marked as sygus, quantifier elimination, etc.), then
     131                 :            :   // do no split it.
     132                 :      31297 :   QAttributes qa;
     133                 :      31297 :   QuantAttributes::computeQuantAttributes(q, qa);
     134         [ +  + ]:      31297 :   if (!qa.isStandard())
     135                 :            :   {
     136                 :       3647 :     return;
     137                 :            :   }
     138                 :            :   // do not split if there is a trigger
     139         [ +  + ]:      27650 :   if (qa.d_hasPattern)
     140                 :            :   {
     141                 :       2270 :     return;
     142                 :            :   }
     143                 :      25380 :   bool takeOwnership = false;
     144                 :      25380 :   bool doSplit = false;
     145                 :      25380 :   QuantifiersBoundInference& qbi = d_qreg.getQuantifiersBoundInference();
     146         [ +  - ]:      50760 :   Trace("quant-dsplit-debug")
     147                 :      25380 :       << "Check split quantified formula : " << q << std::endl;
     148         [ +  + ]:      81019 :   for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
     149                 :            :   {
     150                 :     114210 :     TypeNode tn = q[0][i].getType();
     151         [ +  + ]:      57105 :     if (tn.isDatatype())
     152                 :            :     {
     153                 :       7701 :       bool isFinite = d_env.isFiniteType(tn);
     154                 :       7701 :       const DType& dt = tn.getDType();
     155         [ +  + ]:       7701 :       if (dt.isRecursiveSingleton(tn))
     156                 :            :       {
     157 [ +  - ][ -  + ]:        644 :         Trace("quant-dsplit-debug") << "Datatype " << dt.getName()
                 [ -  - ]
     158                 :        322 :                                     << " is recursive singleton." << std::endl;
     159                 :            :       }
     160                 :            :       else
     161                 :            :       {
     162                 :       7379 :         if (options().quantifiers.quantDynamicSplit
     163         [ -  + ]:       7379 :             == options::QuantDSplitMode::AGG)
     164                 :            :         {
     165                 :            :           // split if it is a finite datatype
     166                 :          0 :           doSplit = isFinite;
     167                 :            :         }
     168                 :       7379 :         else if (options().quantifiers.quantDynamicSplit
     169         [ +  - ]:       7379 :                  == options::QuantDSplitMode::DEFAULT)
     170                 :            :         {
     171         [ +  + ]:       7379 :           if (!qbi.isFiniteBound(q, q[0][i]))
     172                 :            :           {
     173         [ +  + ]:       7197 :             if (isFinite)
     174                 :            :             {
     175                 :            :               // split if goes from being unhandled -> handled by finite
     176                 :            :               // instantiation. An example is datatypes with uninterpreted sort
     177                 :            :               // fields which are "interpreted finite" but not "finite".
     178                 :         46 :               doSplit = true;
     179                 :            :               // we additionally take ownership of this formula, in other words,
     180                 :            :               // we mark it reduced.
     181                 :         46 :               takeOwnership = true;
     182                 :            :             }
     183 [ +  + ][ +  - ]:       7151 :             else if (dt.getNumConstructors() == 1 && !dt.isCodatatype())
                 [ +  + ]
     184                 :            :             {
     185                 :            :               // split if only one constructor
     186                 :       1420 :               doSplit = true;
     187                 :            :             }
     188                 :            :           }
     189                 :            :         }
     190         [ +  + ]:       7379 :         if (doSplit)
     191                 :            :         {
     192                 :            :           // store the index to split
     193                 :       1466 :           d_quant_to_reduce[q] = i;
     194         [ +  - ]:       2932 :           Trace("quant-dsplit-debug")
     195 [ -  + ][ -  - ]:       1466 :               << "Split at index " << i << " based on datatype " << dt.getName()
     196                 :       1466 :               << std::endl;
     197                 :       1466 :           break;
     198                 :            :         }
     199         [ +  - ]:      11826 :         Trace("quant-dsplit-debug")
     200 [ -  + ][ -  - ]:       5913 :             << "Do not split based on datatype " << dt.getName() << std::endl;
     201                 :            :       }
     202                 :            :     }
     203         [ +  + ]:      57105 :   }
     204                 :            : 
     205         [ +  + ]:      25380 :   if (takeOwnership)
     206                 :            :   {
     207         [ +  - ]:         46 :     Trace("quant-dsplit-debug") << "Will take ownership." << std::endl;
     208                 :         46 :     d_qreg.setOwner(q, this);
     209                 :            :   }
     210                 :            :   // Notice we may not take ownership in some cases, meaning that both the
     211                 :            :   // original quantified formula and the split one are generated. This may
     212                 :            :   // increase our ability to answer "unsat", since quantifier instantiation
     213                 :            :   // heuristics may be more effective for one or the other (see issues #993
     214                 :            :   // and 3481).
     215         [ +  + ]:      31297 : }
     216                 :            : 
     217                 :            : /* whether this module needs to check this round */
     218                 :     113042 : bool QuantDSplit::needsCheck(Theory::Effort e)
     219                 :            : {
     220 [ +  + ][ +  + ]:     113042 :   return e >= Theory::EFFORT_FULL && !d_quant_to_reduce.empty();
     221                 :            : }
     222                 :            : 
     223                 :        786 : bool QuantDSplit::checkCompleteFor(Node q)
     224                 :            : {
     225                 :            :   // true if we split q
     226                 :        786 :   return d_added_split.find(q) != d_added_split.end();
     227                 :            : }
     228                 :            : 
     229                 :            : /* Call during quantifier engine's check */
     230                 :       3463 : void QuantDSplit::check(CVC5_UNUSED Theory::Effort e, QEffort quant_e)
     231                 :            : {
     232                 :            :   // add lemmas ASAP (they are a reduction)
     233         [ +  + ]:       3463 :   if (quant_e != QEFFORT_CONFLICT)
     234                 :            :   {
     235                 :       2211 :     return;
     236                 :            :   }
     237         [ +  - ]:       1252 :   Trace("quant-dsplit") << "QuantDSplit::check" << std::endl;
     238                 :       1252 :   FirstOrderModel* m = d_treg.getModel();
     239                 :       1252 :   for (NodeIntMap::iterator it = d_quant_to_reduce.begin();
     240         [ +  + ]:      23277 :        it != d_quant_to_reduce.end();
     241                 :      22025 :        ++it)
     242                 :            :   {
     243                 :      22025 :     Node q = it->first;
     244         [ +  - ]:      22025 :     Trace("quant-dsplit") << "- Split quantifier " << q << std::endl;
     245         [ +  + ]:      22025 :     if (d_added_split.find(q) != d_added_split.end())
     246                 :            :     {
     247                 :      20420 :       continue;
     248                 :            :     }
     249                 :       1605 :     if (m->isQuantifierAsserted(q) && m->isQuantifierActive(q))
     250                 :            :     {
     251                 :       1425 :       d_added_split.insert(q);
     252                 :       1425 :       Node qsplit = split(nodeManager(), q, it->second);
     253                 :       1425 :       Node lem = q.eqNode(qsplit);
     254                 :            :       // must remember the variable index if proofs are enabled
     255         [ +  + ]:       1425 :       if (d_pfgen != nullptr)
     256                 :            :       {
     257                 :       1136 :         d_pfgen->notifyLemma(lem, it->second);
     258                 :            :       }
     259         [ +  - ]:       1425 :       Trace("quant-dsplit") << "QuantDSplit lemma : " << lem << std::endl;
     260         [ +  + ]:       1425 :       d_qim.addPendingLemma(lem,
     261                 :            :                             InferenceId::QUANTIFIERS_DSPLIT,
     262                 :            :                             LemmaProperty::NONE,
     263                 :       1425 :                             d_pfgen.get());
     264                 :       1425 :     }
     265         [ +  + ]:      22025 :   }
     266         [ +  - ]:       1252 :   Trace("quant-dsplit") << "QuantDSplit::check finished" << std::endl;
     267                 :            : }
     268                 :            : 
     269                 :       1608 : Node QuantDSplit::split(NodeManager* nm, const Node& q, size_t index)
     270                 :            : {
     271                 :       1608 :   std::vector<Node> bvs;
     272         [ +  + ]:       6838 :   for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
     273                 :            :   {
     274         [ +  + ]:       5230 :     if (i != index)
     275                 :            :     {
     276                 :       3622 :       bvs.push_back(q[0][i]);
     277                 :            :     }
     278                 :            :   }
     279                 :       3216 :   TNode svar = q[0][index];
     280                 :       1608 :   TypeNode tn = svar.getType();
     281 [ -  + ][ -  + ]:       1608 :   Assert(tn.isDatatype());
                 [ -  - ]
     282                 :       1608 :   std::vector<Node> cons;
     283                 :       1608 :   const DType& dt = tn.getDType();
     284                 :       1608 :   BoundVarManager* bvm = nm->getBoundVarManager();
     285                 :       1608 :   size_t varCount = 0;
     286         [ +  + ]:       3634 :   for (size_t j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
     287                 :            :   {
     288                 :       2026 :     std::vector<Node> vars;
     289                 :       2026 :     TypeNode dtjtn = dt[j].getInstantiatedConstructorType(tn);
     290 [ -  + ][ -  + ]:       2026 :     Assert(dtjtn.getNumChildren() == dt[j].getNumArgs() + 1);
                 [ -  - ]
     291         [ +  + ]:       5690 :     for (size_t k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
     292                 :            :     {
     293                 :       3664 :       TypeNode tns = dtjtn[k];
     294                 :       7328 :       Node cacheVal = bvm->getCacheValue(q[1], q[0][index], varCount);
     295                 :       3664 :       varCount++;
     296                 :       7328 :       Node v = bvm->mkBoundVar(BoundVarId::QUANT_DT_SPLIT, cacheVal, tns);
     297                 :       3664 :       vars.push_back(v);
     298                 :       3664 :     }
     299                 :       2026 :     std::vector<Node> bvs_cmb;
     300                 :       2026 :     bvs_cmb.insert(bvs_cmb.end(), bvs.begin(), bvs.end());
     301                 :       2026 :     bvs_cmb.insert(bvs_cmb.end(), vars.begin(), vars.end());
     302                 :       2026 :     Node c = datatypes::utils::mkApplyCons(tn, dt, j, vars);
     303                 :       2026 :     TNode ct = c;
     304                 :       4052 :     Node body = q[1].substitute(svar, ct);
     305         [ +  + ]:       2026 :     if (!bvs_cmb.empty())
     306                 :            :     {
     307                 :       1981 :       Node bvl = nm->mkNode(Kind::BOUND_VAR_LIST, bvs_cmb);
     308                 :       1981 :       std::vector<Node> children;
     309                 :       1981 :       children.push_back(bvl);
     310                 :       1981 :       children.push_back(body);
     311         [ -  + ]:       1981 :       if (q.getNumChildren() == 3)
     312                 :            :       {
     313                 :          0 :         Node ipls = q[2].substitute(svar, ct);
     314                 :          0 :         children.push_back(ipls);
     315                 :          0 :       }
     316                 :       1981 :       body = nm->mkNode(Kind::FORALL, children);
     317                 :       1981 :     }
     318                 :       2026 :     cons.push_back(body);
     319                 :       2026 :   }
     320                 :       3216 :   return nm->mkAnd(cons);
     321                 :       1608 : }
     322                 :            : 
     323                 :         13 : std::shared_ptr<ProofNode> QuantDSplit::getQuantDtSplitProof(Env& env,
     324                 :            :                                                              const Node& q,
     325                 :            :                                                              size_t index)
     326                 :            : {
     327                 :         13 :   Node qs = split(env.getNodeManager(), q, index);
     328         [ -  + ]:         13 :   if (qs.isNull())
     329                 :            :   {
     330                 :          0 :     return nullptr;
     331                 :            :   }
     332                 :         13 :   Node eq = q.eqNode(qs);
     333                 :         13 :   QuantDSplitProofGenerator pg(env);
     334                 :         13 :   pg.notifyLemma(eq, index);
     335                 :         13 :   return pg.getProofFor(eq);
     336                 :         13 : }
     337                 :            : 
     338                 :            : }  // namespace quantifiers
     339                 :            : }  // namespace theory
     340                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14