LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/smt - quant_elim_solver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 78 82 95.1 %
Date: 2026-01-21 12:59:26 Functions: 4 4 100.0 %
Branches: 53 80 66.2 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Aina Niemetz, Mathias Preiner
       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                 :            :  * The solver for quantifier elimination queries.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "smt/quant_elim_solver.h"
      17                 :            : 
      18                 :            : #include "base/modal_exception.h"
      19                 :            : #include "expr/node_algorithm.h"
      20                 :            : #include "expr/skolem_manager.h"
      21                 :            : #include "expr/subs.h"
      22                 :            : #include "expr/subtype_elim_node_converter.h"
      23                 :            : #include "smt/smt_driver.h"
      24                 :            : #include "smt/smt_solver.h"
      25                 :            : #include "theory/quantifiers/cegqi/nested_qe.h"
      26                 :            : #include "theory/quantifiers_engine.h"
      27                 :            : #include "theory/theory_engine.h"
      28                 :            : #include "util/string.h"
      29                 :            : 
      30                 :            : using namespace cvc5::internal::theory;
      31                 :            : using namespace cvc5::internal::kind;
      32                 :            : 
      33                 :            : namespace cvc5::internal {
      34                 :            : namespace smt {
      35                 :            : 
      36                 :      74702 : QuantElimSolver::QuantElimSolver(Env& env, SmtSolver& sms, ContextManager* ctx)
      37                 :      74702 :     : EnvObj(env), d_smtSolver(sms), d_ctx(ctx)
      38                 :            : {
      39                 :      74702 : }
      40                 :            : 
      41                 :     139508 : QuantElimSolver::~QuantElimSolver() {}
      42                 :            : 
      43                 :        415 : Node QuantElimSolver::getQuantifierElimination(Node q,
      44                 :            :                                                bool doFull,
      45                 :            :                                                bool isInternalSubsolver)
      46                 :            : {
      47         [ +  - ]:        415 :   Trace("smt-qe") << "QuantElimSolver: get qe : " << q << std::endl;
      48 [ +  + ][ +  + ]:        415 :   if (q.getKind() != Kind::EXISTS && q.getKind() != Kind::FORALL)
                 [ +  + ]
      49                 :            :   {
      50                 :            :     throw ModalException(
      51                 :          4 :         "Expecting a quantified formula as argument to get-qe.");
      52                 :            :   }
      53                 :        411 :   NodeManager* nm = nodeManager();
      54                 :            :   // ensure the body is rewritten
      55                 :        411 :   q = nm->mkNode(q.getKind(), q[0], rewrite(q[1]));
      56                 :            :   // do nested quantifier elimination if necessary
      57                 :        411 :   q = quantifiers::NestedQe::doNestedQe(d_env, q, true);
      58         [ +  - ]:        822 :   Trace("smt-qe") << "QuantElimSolver: after nested quantifier elimination : "
      59                 :        411 :                   << q << std::endl;
      60                 :            :   Node keyword =
      61         [ +  + ]:        822 :       nm->mkConst(String(doFull ? "quant-elim" : "quant-elim-partial"));
      62                 :        822 :   Node n_attr = nm->mkNode(Kind::INST_ATTRIBUTE, keyword);
      63                 :            :   // Polarity is the Boolean constant we return if the subquery is "sat".
      64                 :        411 :   bool polarity = (q.getKind() != Kind::EXISTS);
      65                 :        822 :   Node ne;
      66                 :            :   // Special case: if we have no free symbols, just use a quantifier-free
      67                 :            :   // query. This ensures we don't depend on quantifier instantiation in
      68                 :            :   // these cases, which is especially important if the theory does not admit
      69                 :            :   // QE, e.g. strings.
      70                 :        822 :   std::unordered_set<Node> syms;
      71                 :        411 :   expr::getSymbols(q, syms);
      72                 :        411 :   bool closed = false;
      73         [ +  + ]:        411 :   if (syms.empty())
      74                 :            :   {
      75                 :            :     // Partial elimination is irrelevant since we have a closed formula, so we
      76                 :            :     // assume we are doing full elimination.
      77                 :        368 :     doFull = true;
      78                 :        368 :     closed = true;
      79                 :        736 :     Subs sq;
      80                 :        368 :     SkolemManager* sm = nm->getSkolemManager();
      81         [ +  + ]:        756 :     for (const Node& v : q[0])
      82                 :            :     {
      83                 :            :       Node k = sm->mkInternalSkolemFunction(
      84                 :       1552 :           InternalSkolemId::QE_CLOSED_INPUT, v.getType(), {v});
      85                 :        388 :       sq.add(v, k);
      86                 :            :     }
      87                 :        368 :     ne = sq.apply(q[1]);
      88                 :            :     // We are skolemizing, so we flip the polarity
      89                 :        368 :     polarity = !polarity;
      90         [ +  + ]:        368 :     if (q.getKind() == Kind::EXISTS)
      91                 :            :     {
      92                 :          5 :       ne = ne.negate();
      93                 :            :     }
      94                 :            :   }
      95                 :            :   else
      96                 :            :   {
      97                 :            :     // tag the quantified formula with the quant-elim attribute
      98                 :         86 :     TypeNode t = nm->booleanType();
      99                 :         43 :     n_attr = nm->mkNode(Kind::INST_PATTERN_LIST, n_attr);
     100                 :         43 :     std::vector<Node> children;
     101                 :         43 :     children.push_back(q[0]);
     102 [ +  + ][ +  + ]:         43 :     children.push_back(q.getKind() == Kind::EXISTS ? q[1] : q[1].negate());
                 [ -  - ]
     103                 :         43 :     children.push_back(n_attr);
     104                 :         43 :     ne = nm->mkNode(Kind::EXISTS, children);
     105 [ -  + ][ -  + ]:         43 :     Assert(ne.getNumChildren() == 3);
                 [ -  - ]
     106                 :            :   }
     107         [ +  - ]:        822 :   Trace("smt-qe-debug") << "Query for quantifier elimination : " << ne
     108                 :        411 :                         << std::endl;
     109                 :            :   // use a single call driver
     110                 :        822 :   SmtDriverSingleCall sdsc(d_env, d_smtSolver, d_ctx);
     111                 :       1644 :   Result r = sdsc.checkSat(std::vector<Node>{ne.notNode()});
     112         [ +  - ]:        411 :   Trace("smt-qe") << "Query returned " << r << std::endl;
     113         [ +  + ]:        411 :   if (r.getStatus() != Result::UNSAT)
     114                 :            :   {
     115 [ +  + ][ -  + ]:         36 :     if (r.getStatus() != Result::SAT && doFull)
                 [ -  + ]
     116                 :            :     {
     117                 :          0 :       verbose(1)
     118                 :          0 :           << "While performing quantifier elimination, unexpected result : "
     119                 :          0 :           << r << " for query." << std::endl;
     120                 :            :       // failed, return original
     121                 :          0 :       return q;
     122                 :            :     }
     123                 :            :     // must use original quantified formula to compute QE, which ensures that
     124                 :            :     // e.g. term formula removal is not run on the body. Notice that we assume
     125                 :            :     // that the (single) quantified formula is preprocessed, rewritten
     126                 :            :     // version of the input quantified formula q.
     127                 :         72 :     std::vector<Node> inst_qs;
     128                 :         72 :     Node topq;
     129                 :         72 :     Node ret;
     130         [ +  + ]:         36 :     if (!closed)
     131                 :            :     {
     132                 :         32 :       TheoryEngine* te = d_smtSolver.getTheoryEngine();
     133 [ -  + ][ -  + ]:         32 :       Assert(te != nullptr);
                 [ -  - ]
     134                 :         32 :       QuantifiersEngine* qe = te->getQuantifiersEngine();
     135                 :         32 :       qe->getInstantiatedQuantifiedFormulas(inst_qs);
     136                 :            :       // Find the quantified formula corresponding to the quantifier elimination
     137         [ +  - ]:         33 :       for (const Node& qinst : inst_qs)
     138                 :            :       {
     139                 :            :         // Should have the same attribute mark as above
     140 [ +  + ][ +  - ]:         33 :         if (qinst.getNumChildren() == 3 && qinst[2] == n_attr)
         [ +  + ][ +  + ]
                 [ -  - ]
     141                 :            :         {
     142                 :         32 :           topq = qinst;
     143                 :         32 :           break;
     144                 :            :         }
     145                 :            :       }
     146         [ +  - ]:         32 :       if (!topq.isNull())
     147                 :            :       {
     148 [ -  + ][ -  + ]:         32 :         Assert(topq.getKind() == Kind::FORALL);
                 [ -  - ]
     149         [ +  - ]:         64 :         Trace("smt-qe") << "Get qe based on preprocessed quantified formula "
     150                 :         32 :                         << topq << std::endl;
     151                 :         64 :         std::vector<Node> insts;
     152                 :         32 :         qe->getInstantiations(topq, insts);
     153                 :            :         // note we do not convert to witness form here, since we could be
     154                 :            :         // an internal subsolver (SolverEngine::isInternalSubsolver).
     155                 :         32 :         ret = nm->mkAnd(insts);
     156         [ +  - ]:         32 :         Trace("smt-qe") << "QuantElimSolver returned : " << ret << std::endl;
     157         [ +  + ]:         32 :         if (q.getKind() == Kind::EXISTS)
     158                 :            :         {
     159                 :         27 :           ret = rewrite(ret.negate());
     160                 :            :         }
     161                 :            :         // do extended rewrite to minimize the size of the formula aggressively
     162                 :         32 :         ret = extendedRewrite(ret);
     163                 :            :         // if we are not an internal subsolver, convert to witness form, since
     164                 :            :         // internally generated skolems should not escape
     165         [ +  + ]:         32 :         if (!isInternalSubsolver)
     166                 :            :         {
     167                 :         12 :           ret = SkolemManager::getOriginalForm(ret);
     168                 :            :         }
     169                 :            :         // make so that the returned formula does not involve arithmetic
     170                 :            :         // subtyping
     171                 :         32 :         SubtypeElimNodeConverter senc(nodeManager());
     172                 :         32 :         ret = senc.convert(ret);
     173                 :            :       }
     174                 :            :     }
     175                 :            :     // If we are closed, or the quantified formula was not instantiated in the
     176                 :            :     // subsolver, then we are a constant.
     177         [ +  + ]:         36 :     if (ret.isNull())
     178                 :            :     {
     179                 :          4 :       ret = nm->mkConst(polarity);
     180                 :            :     }
     181                 :         36 :     return ret;
     182                 :            :   }
     183                 :            :   // otherwise, just true/false
     184                 :        750 :   return nm->mkConst(!polarity);
     185                 :            : }
     186                 :            : 
     187                 :            : }  // namespace smt
     188                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14