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: 139 144 96.5 %
Date: 2025-01-25 12:38:51 Functions: 11 12 91.7 %
Branches: 76 108 70.4 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Mathias Preiner, Gereon Kremer
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Implementation of dynamic quantifiers splitting.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/quantifiers/quant_split.h"
      17                 :            : 
      18                 :            : #include "expr/bound_var_manager.h"
      19                 :            : #include "expr/dtype.h"
      20                 :            : #include "expr/dtype_cons.h"
      21                 :            : #include "options/quantifiers_options.h"
      22                 :            : #include "options/smt_options.h"
      23                 :            : #include "proof/proof.h"
      24                 :            : #include "proof/proof_generator.h"
      25                 :            : #include "theory/datatypes/theory_datatypes_utils.h"
      26                 :            : #include "theory/quantifiers/first_order_model.h"
      27                 :            : #include "theory/quantifiers/term_database.h"
      28                 :            : #include "util/rational.h"
      29                 :            : 
      30                 :            : using namespace cvc5::internal::kind;
      31                 :            : 
      32                 :            : namespace cvc5::internal {
      33                 :            : namespace theory {
      34                 :            : namespace quantifiers {
      35                 :            : 
      36                 :            : /**
      37                 :            :  * A proof generator for quantifiers splitting inferences
      38                 :            :  */
      39                 :            : class QuantDSplitProofGenerator : protected EnvObj, public ProofGenerator
      40                 :            : {
      41                 :            :  public:
      42                 :      13079 :   QuantDSplitProofGenerator(Env& env) : EnvObj(env), d_index(userContext()) {}
      43                 :      26130 :   virtual ~QuantDSplitProofGenerator() {}
      44                 :            :   /**
      45                 :            :    * Get proof for fact. This expects facts of the form
      46                 :            :    *    q = QuantDSplit::split(nm, q, n)
      47                 :            :    * We prove this by:
      48                 :            :    *    ------ QUANT_VAR_REORDERING ---------------------------- QUANT_DT_SPLIT
      49                 :            :    *    q = q'                      q' = QuantDSplit::split(nm, q', 0)
      50                 :            :    *    --------------------------------------------------------------- TRANS
      51                 :            :    *    q = QuantDSplit::split(nm, q, n)
      52                 :            :    *
      53                 :            :    * where the variables in q' is reordered from q such that the variable to
      54                 :            :    * split comes first.
      55                 :            :    */
      56                 :         34 :   std::shared_ptr<ProofNode> getProofFor(Node fact) override
      57                 :            :   {
      58                 :        102 :     CDProof cdp(d_env);
      59                 :            :     // find the index of the variable that was split for this lemma
      60                 :         34 :     context::CDHashMap<Node, size_t>::iterator it = d_index.find(fact);
      61         [ -  + ]:         34 :     if (it == d_index.end())
      62                 :            :     {
      63                 :          0 :       Assert(false) << "QuantDSplitProofGenerator failed to get proof";
      64                 :            :       return nullptr;
      65                 :            :     }
      66                 :         68 :     Assert(fact.getKind() == Kind::EQUAL && fact[0].getKind() == Kind::FORALL);
      67                 :         68 :     Node q = fact[0];
      68                 :         68 :     std::vector<Node> transEq;
      69         [ +  + ]:         34 :     if (it->second != 0)
      70                 :            :     {
      71                 :            :       // must reorder variables
      72                 :         20 :       std::vector<Node> newVars;
      73                 :         10 :       newVars.push_back(q[0][it->second]);
      74         [ +  + ]:         35 :       for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
      75                 :            :       {
      76         [ +  + ]:         25 :         if (i != it->second)
      77                 :            :         {
      78                 :         15 :           newVars.emplace_back(q[0][i]);
      79                 :            :         }
      80                 :            :       }
      81                 :         20 :       std::vector<Node> qc(q.begin(), q.end());
      82                 :         10 :       NodeManager* nm = nodeManager();
      83                 :         10 :       qc[0] = nm->mkNode(Kind::BOUND_VAR_LIST, newVars);
      84                 :         20 :       Node qn = nm->mkNode(Kind::FORALL, qc);
      85                 :         20 :       Node eqq = q.eqNode(qn);
      86                 :         20 :       cdp.addStep(eqq, ProofRule::QUANT_VAR_REORDERING, {}, {eqq});
      87                 :         10 :       transEq.emplace_back(eqq);
      88                 :         10 :       q = qn;
      89                 :            :     }
      90                 :         34 :     Node eqq2 = q.eqNode(fact[1]);
      91                 :         34 :     cdp.addTheoryRewriteStep(eqq2, ProofRewriteRule::QUANT_DT_SPLIT);
      92         [ +  + ]:         34 :     if (!transEq.empty())
      93                 :            :     {
      94                 :         10 :       transEq.emplace_back(eqq2);
      95                 :         10 :       cdp.addStep(fact, ProofRule::TRANS, transEq, {});
      96                 :            :     }
      97                 :         34 :     return cdp.getProofFor(fact);
      98                 :            :   }
      99                 :            :   /** identify */
     100                 :          0 :   std::string identify() const override { return "QuantDSplitProofGenerator"; }
     101                 :            :   /**
     102                 :            :    * Notify that the given lemma used the given variable index to split. We
     103                 :            :    * store this in d_index and use it to guide proof reconstruction above.
     104                 :            :    */
     105                 :       1393 :   void notifyLemma(const Node& lem, size_t index) { d_index[lem] = index; }
     106                 :            : 
     107                 :            :  private:
     108                 :            :   /** Mapping from lemmas to their notified index */
     109                 :            :   context::CDHashMap<Node, size_t> d_index;
     110                 :            : };
     111                 :            : 
     112                 :            : /**
     113                 :            :  * Attributes used for constructing bound variables in a canonical way. These
     114                 :            :  * are attributes that map to bound variable, introduced for the following
     115                 :            :  * purpose:
     116                 :            :  * - QDSplitVarAttribute: cached on (q, v, i) where QuantDSplit::split is called
     117                 :            :  * to split the variable v of q. We introduce bound variables, where the i^th
     118                 :            :  * variable created in that method is cached based on i.
     119                 :            :  */
     120                 :            : struct QDSplitVarAttributeId
     121                 :            : {
     122                 :            : };
     123                 :            : using QDSplitVarAttribute = expr::Attribute<QDSplitVarAttributeId, Node>;
     124                 :            : 
     125                 :      40410 : QuantDSplit::QuantDSplit(Env& env,
     126                 :            :                          QuantifiersState& qs,
     127                 :            :                          QuantifiersInferenceManager& qim,
     128                 :            :                          QuantifiersRegistry& qr,
     129                 :      40410 :                          TermRegistry& tr)
     130                 :            :     : QuantifiersModule(env, qs, qim, qr, tr),
     131                 :      80820 :       d_quant_to_reduce(userContext()),
     132                 :      80820 :       d_added_split(userContext()),
     133                 :      13079 :       d_pfgen(options().smt.produceProofs ? new QuantDSplitProofGenerator(d_env)
     134         [ +  + ]:      53489 :                                           : nullptr)
     135                 :            : {
     136                 :      40410 : }
     137                 :            : 
     138                 :      33252 : void QuantDSplit::checkOwnership(Node q)
     139                 :            : {
     140                 :            :   // If q is non-standard (marked as sygus, quantifier elimination, etc.), then
     141                 :            :   // do no split it.
     142                 :      33252 :   QAttributes qa;
     143                 :      33252 :   QuantAttributes::computeQuantAttributes(q, qa);
     144         [ +  + ]:      33252 :   if (!qa.isStandard())
     145                 :            :   {
     146                 :       3562 :     return;
     147                 :            :   }
     148                 :            :   // do not split if there is a trigger
     149         [ +  + ]:      29690 :   if (qa.d_hasPattern)
     150                 :            :   {
     151                 :       2576 :     return;
     152                 :            :   }
     153                 :      27114 :   bool takeOwnership = false;
     154                 :      27114 :   bool doSplit = false;
     155                 :      27114 :   QuantifiersBoundInference& qbi = d_qreg.getQuantifiersBoundInference();
     156         [ +  - ]:      27114 :   Trace("quant-dsplit-debug") << "Check split quantified formula : " << q << std::endl;
     157         [ +  + ]:      88301 :   for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
     158                 :            :   {
     159                 :     125832 :     TypeNode tn = q[0][i].getType();
     160         [ +  + ]:      62916 :     if( tn.isDatatype() ){
     161                 :       8040 :       bool isFinite = d_env.isFiniteType(tn);
     162                 :       8040 :       const DType& dt = tn.getDType();
     163         [ +  + ]:       8040 :       if (dt.isRecursiveSingleton(tn))
     164                 :            :       {
     165 [ +  - ][ -  + ]:        386 :         Trace("quant-dsplit-debug") << "Datatype " << dt.getName() << " is recursive singleton." << std::endl;
                 [ -  - ]
     166                 :            :       }
     167                 :            :       else
     168                 :            :       {
     169                 :       7654 :         if (options().quantifiers.quantDynamicSplit
     170         [ -  + ]:       7654 :             == options::QuantDSplitMode::AGG)
     171                 :            :         {
     172                 :            :           // split if it is a finite datatype
     173                 :          0 :           doSplit = isFinite;
     174                 :            :         }
     175                 :       7654 :         else if (options().quantifiers.quantDynamicSplit
     176         [ +  - ]:       7654 :                  == options::QuantDSplitMode::DEFAULT)
     177                 :            :         {
     178         [ +  + ]:       7654 :           if (!qbi.isFiniteBound(q, q[0][i]))
     179                 :            :           {
     180         [ +  + ]:       7469 :             if (isFinite)
     181                 :            :             {
     182                 :            :               // split if goes from being unhandled -> handled by finite
     183                 :            :               // instantiation. An example is datatypes with uninterpreted sort
     184                 :            :               // fields which are "interpreted finite" but not "finite".
     185                 :         48 :               doSplit = true;
     186                 :            :               // we additionally take ownership of this formula, in other words,
     187                 :            :               // we mark it reduced.
     188                 :         48 :               takeOwnership = true;
     189                 :            :             }
     190 [ +  + ][ +  - ]:       7421 :             else if (dt.getNumConstructors() == 1 && !dt.isCodatatype())
                 [ +  + ]
     191                 :            :             {
     192                 :            :               // split if only one constructor
     193                 :       1681 :               doSplit = true;
     194                 :            :             }
     195                 :            :           }
     196                 :            :         }
     197         [ +  + ]:       7654 :         if (doSplit)
     198                 :            :         {
     199                 :            :           // store the index to split
     200                 :       1729 :           d_quant_to_reduce[q] = i;
     201         [ +  - ]:       3458 :           Trace("quant-dsplit-debug")
     202 [ -  + ][ -  - ]:       1729 :               << "Split at index " << i << " based on datatype " << dt.getName()
     203                 :       1729 :               << std::endl;
     204                 :       1729 :           break;
     205                 :            :         }
     206         [ +  - ]:      11850 :         Trace("quant-dsplit-debug")
     207 [ -  + ][ -  - ]:       5925 :             << "Do not split based on datatype " << dt.getName() << std::endl;
     208                 :            :       }
     209                 :            :     }
     210                 :            :   }
     211                 :            : 
     212         [ +  + ]:      27114 :   if (takeOwnership)
     213                 :            :   {
     214         [ +  - ]:         48 :     Trace("quant-dsplit-debug") << "Will take ownership." << std::endl;
     215                 :         48 :     d_qreg.setOwner(q, this);
     216                 :            :   }
     217                 :            :   // Notice we may not take ownership in some cases, meaning that both the
     218                 :            :   // original quantified formula and the split one are generated. This may
     219                 :            :   // increase our ability to answer "unsat", since quantifier instantiation
     220                 :            :   // heuristics may be more effective for one or the other (see issues #993
     221                 :            :   // and 3481).
     222                 :            : }
     223                 :            : 
     224                 :            : /* whether this module needs to check this round */
     225                 :     106221 : bool QuantDSplit::needsCheck( Theory::Effort e ) {
     226 [ +  + ][ +  + ]:     106221 :   return e>=Theory::EFFORT_FULL && !d_quant_to_reduce.empty();
     227                 :            : }
     228                 :            : 
     229                 :        762 : bool QuantDSplit::checkCompleteFor( Node q ) {
     230                 :            :   // true if we split q
     231                 :        762 :   return d_added_split.find( q )!=d_added_split.end();
     232                 :            : }
     233                 :            : 
     234                 :            : /* Call during quantifier engine's check */
     235                 :       3990 : void QuantDSplit::check(Theory::Effort e, QEffort quant_e)
     236                 :            : {
     237                 :            :   //add lemmas ASAP (they are a reduction)
     238         [ +  + ]:       3990 :   if (quant_e != QEFFORT_CONFLICT)
     239                 :            :   {
     240                 :       2546 :     return;
     241                 :            :   }
     242         [ +  - ]:       1444 :   Trace("quant-dsplit") << "QuantDSplit::check" << std::endl;
     243                 :       1444 :   FirstOrderModel* m = d_treg.getModel();
     244                 :      27590 :   for (NodeIntMap::iterator it = d_quant_to_reduce.begin();
     245         [ +  + ]:      27590 :        it != d_quant_to_reduce.end();
     246                 :      26146 :        ++it)
     247                 :            :   {
     248                 :      26146 :     Node q = it->first;
     249         [ +  - ]:      26146 :     Trace("quant-dsplit") << "- Split quantifier " << q << std::endl;
     250         [ +  + ]:      26146 :     if (d_added_split.find(q) != d_added_split.end())
     251                 :            :     {
     252                 :      24255 :       continue;
     253                 :            :     }
     254                 :       1891 :     if (m->isQuantifierAsserted(q) && m->isQuantifierActive(q))
     255                 :            :     {
     256                 :       1682 :       d_added_split.insert(q);
     257                 :       3364 :       Node qsplit = split(nodeManager(), q, it->second);
     258                 :       1682 :       Node lem = q.eqNode(qsplit);
     259                 :            :       // must remember the variable index if proofs are enabled
     260         [ +  + ]:       1682 :       if (d_pfgen != nullptr)
     261                 :            :       {
     262                 :       1393 :         d_pfgen->notifyLemma(lem, it->second);
     263                 :            :       }
     264         [ +  - ]:       1682 :       Trace("quant-dsplit") << "QuantDSplit lemma : " << lem << std::endl;
     265         [ +  + ]:       1682 :       d_qim.addPendingLemma(lem,
     266                 :            :                             InferenceId::QUANTIFIERS_DSPLIT,
     267                 :            :                             LemmaProperty::NONE,
     268                 :       1682 :                             d_pfgen.get());
     269                 :            :     }
     270                 :            :   }
     271         [ +  - ]:       1444 :   Trace("quant-dsplit") << "QuantDSplit::check finished" << std::endl;
     272                 :            : }
     273                 :            : 
     274                 :       1703 : Node QuantDSplit::split(NodeManager* nm, const Node& q, size_t index)
     275                 :            : {
     276                 :       3406 :   std::vector<Node> bvs;
     277         [ +  + ]:       6817 :   for (size_t i = 0, nvars = q[0].getNumChildren(); i < nvars; i++)
     278                 :            :   {
     279         [ +  + ]:       5114 :     if (i != index)
     280                 :            :     {
     281                 :       3411 :       bvs.push_back(q[0][i]);
     282                 :            :     }
     283                 :            :   }
     284                 :       5109 :   TNode svar = q[0][index];
     285                 :       3406 :   TypeNode tn = svar.getType();
     286 [ -  + ][ -  + ]:       1703 :   Assert(tn.isDatatype());
                 [ -  - ]
     287                 :       3406 :   std::vector<Node> cons;
     288                 :       1703 :   const DType& dt = tn.getDType();
     289                 :       1703 :   BoundVarManager* bvm = nm->getBoundVarManager();
     290                 :       1703 :   size_t varCount = 0;
     291         [ +  + ]:       3422 :   for (size_t j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
     292                 :            :   {
     293                 :       3438 :     std::vector<Node> vars;
     294                 :       3438 :     TypeNode dtjtn = dt[j].getInstantiatedConstructorType(tn);
     295 [ -  + ][ -  + ]:       1719 :     Assert(dtjtn.getNumChildren() == dt[j].getNumArgs() + 1);
                 [ -  - ]
     296         [ +  + ]:       5095 :     for (size_t k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
     297                 :            :     {
     298                 :       6752 :       TypeNode tns = dtjtn[k];
     299                 :      10128 :       Node cacheVal = bvm->getCacheValue(q[1], q[0][index], varCount);
     300                 :       3376 :       varCount++;
     301                 :      10128 :       Node v = bvm->mkBoundVar<QDSplitVarAttribute>(cacheVal, tns);
     302                 :       3376 :       vars.push_back(v);
     303                 :            :     }
     304                 :       3438 :     std::vector<Node> bvs_cmb;
     305                 :       1719 :     bvs_cmb.insert(bvs_cmb.end(), bvs.begin(), bvs.end());
     306                 :       1719 :     bvs_cmb.insert(bvs_cmb.end(), vars.begin(), vars.end());
     307                 :       3438 :     Node c = datatypes::utils::mkApplyCons(tn, dt, j, vars);
     308                 :       3438 :     TNode ct = c;
     309                 :       5157 :     Node body = q[1].substitute(svar, ct);
     310         [ +  - ]:       1719 :     if (!bvs_cmb.empty())
     311                 :            :     {
     312                 :       3438 :       Node bvl = nm->mkNode(Kind::BOUND_VAR_LIST, bvs_cmb);
     313                 :       1719 :       std::vector<Node> children;
     314                 :       1719 :       children.push_back(bvl);
     315                 :       1719 :       children.push_back(body);
     316         [ -  + ]:       1719 :       if (q.getNumChildren() == 3)
     317                 :            :       {
     318                 :          0 :         Node ipls = q[2].substitute(svar, ct);
     319                 :          0 :         children.push_back(ipls);
     320                 :            :       }
     321                 :       1719 :       body = nm->mkNode(Kind::FORALL, children);
     322                 :            :     }
     323                 :       1719 :     cons.push_back(body);
     324                 :            :   }
     325                 :       3406 :   return nm->mkAnd(cons);
     326                 :            : }
     327                 :            : 
     328                 :            : }  // namespace quantifiers
     329                 :            : }  // namespace theory
     330                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14