LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers/sygus - synth_verify.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 88 83.0 %
Date: 2026-01-30 12:59:30 Functions: 5 6 83.3 %
Branches: 49 94 52.1 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Abdalrhman Mohamed, 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                 :            :  * Class for verifying queries for synthesis solutions
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/quantifiers/sygus/synth_verify.h"
      17                 :            : 
      18                 :            : #include "expr/node_algorithm.h"
      19                 :            : #include "options/arith_options.h"
      20                 :            : #include "options/base_options.h"
      21                 :            : #include "options/datatypes_options.h"
      22                 :            : #include "options/quantifiers_options.h"
      23                 :            : #include "smt/set_defaults.h"
      24                 :            : #include "theory/quantifiers/first_order_model.h"
      25                 :            : #include "theory/quantifiers/sygus/term_database_sygus.h"
      26                 :            : #include "theory/rewriter.h"
      27                 :            : #include "theory/smt_engine_subsolver.h"
      28                 :            : 
      29                 :            : using namespace cvc5::internal::kind;
      30                 :            : using namespace std;
      31                 :            : 
      32                 :            : namespace cvc5::internal {
      33                 :            : namespace theory {
      34                 :            : namespace quantifiers {
      35                 :            : 
      36                 :       6664 : SynthVerify::SynthVerify(Env& env, TermDbSygus* tds)
      37                 :       6664 :     : EnvObj(env), d_tds(tds), d_subLogicInfo(logicInfo())
      38                 :            : {
      39                 :            :   // determine the options to use for the verification subsolvers we spawn
      40                 :            :   // we start with the provided options
      41                 :       6664 :   d_subOptions.copyValues(options());
      42                 :            :   // limit the number of instantiation rounds on subcalls
      43                 :      13328 :   d_subOptions.write_quantifiers().instMaxRounds =
      44                 :       6664 :       d_subOptions.quantifiers.sygusVerifyInstMaxRounds;
      45                 :            :   // Disable sygus on the subsolver. This is particularly important since it
      46                 :            :   // ensures that recursive function definitions have the standard ownership
      47                 :            :   // instead of being claimed by sygus in the subsolver.
      48                 :       6664 :   d_subOptions.write_base().inputLanguage = Language::LANG_SMTLIB_V2_6;
      49                 :       6664 :   d_subOptions.write_quantifiers().sygus = false;
      50                 :            :   // use tangent planes by default, since we want to put effort into
      51                 :            :   // the verification step for sygus queries with non-linear arithmetic
      52         [ +  + ]:       6664 :   if (!d_subOptions.arith.nlExtTangentPlanesWasSetByUser)
      53                 :            :   {
      54                 :       6652 :     d_subOptions.write_arith().nlExtTangentPlanes = true;
      55                 :            :   }
      56                 :            :   // we must use the same setting for datatype selectors, since shared selectors
      57                 :            :   // can appear in solutions
      58                 :      13328 :   d_subOptions.write_datatypes().dtSharedSelectors =
      59                 :       6664 :       options().datatypes.dtSharedSelectors;
      60                 :       6664 :   d_subOptions.write_datatypes().dtSharedSelectorsWasSetByUser = true;
      61                 :            :   // disable checking
      62                 :       6664 :   smt::SetDefaults::disableChecking(d_subOptions);
      63                 :       6664 : }
      64                 :            : 
      65                 :       6518 : SynthVerify::~SynthVerify() {}
      66                 :            : 
      67                 :       5991 : Result SynthVerify::verify(Node query,
      68                 :            :                            const std::vector<Node>& vars,
      69                 :            :                            std::vector<Node>& mvs)
      70                 :            : {
      71                 :      11982 :   Node queryp = preprocessQueryInternal(query);
      72                 :            :   bool finished;
      73                 :      11982 :   Result r;
      74                 :          0 :   do
      75                 :            :   {
      76         [ +  - ]:       5991 :     Trace("sygus-engine") << "  *** Verify with subcall..." << std::endl;
      77         [ +  + ]:       5991 :     if (queryp.isConst())
      78                 :            :     {
      79         [ +  + ]:       2626 :       if (!queryp.getConst<bool>())
      80                 :            :       {
      81                 :       1231 :         return Result(Result::UNSAT);
      82                 :            :       }
      83         [ +  + ]:       1395 :       else if (vars.empty())
      84                 :            :       {
      85                 :       1365 :         return Result(Result::SAT);
      86                 :            :       }
      87                 :            :       // sat, but we need to get arbtirary model values below
      88                 :            :     }
      89                 :       3395 :     SubsolverSetupInfo ssi(d_subOptions,
      90                 :            :                            d_subLogicInfo,
      91                 :       6790 :                            d_env.getSepLocType(),
      92                 :      10185 :                            d_env.getSepDataType());
      93                 :       6790 :     r = checkWithSubsolver(queryp,
      94                 :            :                            vars,
      95                 :            :                            mvs,
      96                 :            :                            ssi,
      97                 :       3395 :                            options().quantifiers.sygusVerifyTimeout != 0,
      98                 :       6790 :                            options().quantifiers.sygusVerifyTimeout);
      99                 :       3395 :     finished = true;
     100         [ +  - ]:       3395 :     Trace("sygus-engine") << "  ...got " << r << std::endl;
     101                 :            :     // we try to learn models for "sat" and "unknown" here
     102         [ +  + ]:       3395 :     if (r.getStatus() != Result::UNSAT)
     103                 :            :     {
     104         [ -  + ]:       2279 :       if (TraceIsOn("sygus-engine"))
     105                 :            :       {
     106         [ -  - ]:          0 :         Trace("sygus-engine") << "  * Verification lemma failed for:\n   ";
     107         [ -  - ]:          0 :         for (unsigned i = 0, size = vars.size(); i < size; i++)
     108                 :            :         {
     109         [ -  - ]:          0 :           Trace("sygus-engine") << vars[i] << " -> " << mvs[i] << " ";
     110                 :            :         }
     111         [ -  - ]:          0 :         Trace("sygus-engine") << std::endl;
     112                 :            :       }
     113                 :            :       // check whether the query is satisfied by the model
     114 [ +  - ][ +  - ]:       2279 :       if (options().quantifiers.oracles || Configuration::isAssertionBuild())
                 [ +  - ]
     115                 :            :       {
     116 [ -  + ][ -  + ]:       2279 :         Assert(vars.size() == mvs.size());
                 [ -  - ]
     117                 :            :         // the values for the query should be a complete model
     118                 :            :         Node squery =
     119                 :       4558 :             query.substitute(vars.begin(), vars.end(), mvs.begin(), mvs.end());
     120         [ +  - ]:       2279 :         Trace("cegqi-debug") << "...squery : " << squery << std::endl;
     121                 :            :         // Rewrite the node. Notice that if squery contains oracle function
     122                 :            :         // symbols, then this may trigger new calls to oracles.
     123                 :       2279 :         squery = d_tds->rewriteNode(squery);
     124         [ +  - ]:       2279 :         Trace("cegqi-debug") << "...rewrites to : " << squery << std::endl;
     125 [ +  + ][ +  + ]:       2279 :         if (!squery.isConst() || !squery.getConst<bool>())
                 [ +  + ]
     126                 :            :         {
     127                 :            :           // If the query did not simplify to true, then it may be that the
     128                 :            :           // value for an oracle function was not what we expected.
     129         [ -  + ]:       1134 :           if (options().quantifiers.oracles)
     130                 :            :           {
     131                 :            :             // In this case, we reconstruct the query, which may include more
     132                 :            :             // information about oracles than we had previously, due to the
     133                 :            :             // call to rewriteNode above. We rerun the satisfiability check
     134                 :            :             // above, which now may conjoin more I/O pairs to the preprocessed
     135                 :            :             // query.
     136                 :          0 :             Node nextQueryp = preprocessQueryInternal(query);
     137         [ -  - ]:          0 :             if (nextQueryp != queryp)
     138                 :            :             {
     139                 :          0 :               queryp = nextQueryp;
     140                 :          0 :               finished = false;
     141                 :            :             }
     142                 :            :           }
     143         [ +  + ]:       1134 :           else if (squery.isConst())
     144                 :            :           {
     145                 :            :             // simplified to false, the result should have been unknown, or
     146                 :            :             // else this indicates a check-model failure. We check this only
     147                 :            :             // if oracles are disabled.
     148 [ -  + ][ -  + ]:        114 :             Assert(r.getStatus() == Result::UNKNOWN)
                 [ -  - ]
     149                 :          0 :                 << "Expected model from verification step to satisfy query";
     150                 :            :           }
     151                 :            :         }
     152                 :            :       }
     153                 :            :     }
     154         [ -  + ]:       3395 :   } while (!finished);
     155                 :       3395 :   return r;
     156                 :            : }
     157                 :            : 
     158                 :       1870 : Result SynthVerify::verify(Node query)
     159                 :            : {
     160                 :       3740 :   std::vector<Node> vars;
     161                 :       1870 :   std::vector<Node> mvs;
     162                 :       3740 :   return verify(query, vars, mvs);
     163                 :            : }
     164                 :            : 
     165                 :       5991 : Node SynthVerify::preprocessQueryInternal(Node query)
     166                 :            : {
     167                 :       5991 :   NodeManager* nm = nodeManager();
     168         [ +  - ]:       5991 :   Trace("cegqi-debug") << "pre-rewritten query : " << query << std::endl;
     169                 :            :   // simplify the lemma based on the term database sygus utility
     170                 :       5991 :   query = d_tds->rewriteNode(query);
     171                 :            :   // eagerly unfold applications of evaluation function
     172         [ +  - ]:       5991 :   Trace("cegqi-debug") << "post-rewritten query : " << query << std::endl;
     173         [ +  + ]:       5991 :   if (!query.isConst())
     174                 :            :   {
     175                 :            :     // if non-constant, we may need to add recursive function definitions
     176                 :       3365 :     FunDefEvaluator* feval = d_tds->getFunDefEvaluator();
     177                 :       3365 :     OracleChecker* ochecker = d_env.getOracleChecker();
     178                 :       3365 :     const std::vector<Node>& fdefs = feval->getDefinitions();
     179 [ +  + ][ -  + ]:       3365 :     if (!fdefs.empty() || (ochecker != nullptr && ochecker->hasOracles()))
         [ -  - ][ +  + ]
     180                 :            :     {
     181                 :            :       // Get the relevant definitions based on the symbols in the query.
     182                 :            :       // Notice in some cases, this may have the effect of making the subcall
     183                 :            :       // involve no recursive function definitions at all, in which case the
     184                 :            :       // subcall to verification may be decidable, in which case the call
     185                 :            :       // below is guaranteed to generate a new counterexample point.
     186                 :        248 :       std::unordered_set<Node> syms;
     187                 :        124 :       expr::getSymbols(query, syms);
     188                 :        124 :       std::vector<Node> qconj;
     189                 :        124 :       qconj.push_back(query);
     190         [ +  + ]:        739 :       for (const Node& f : syms)
     191                 :            :       {
     192                 :            :         // get the function definition
     193                 :       1230 :         Node q = feval->getDefinitionFor(f);
     194         [ +  + ]:        615 :         if (!q.isNull())
     195                 :            :         {
     196                 :        126 :           qconj.push_back(q);
     197                 :            :         }
     198                 :            :         // Get the relevant cached oracle calls.
     199                 :            :         // In contrast to the presentation in Polgreen et al VMCAI 2022,
     200                 :            :         // we do not use SMTO as a subsolver for SyMO here. Instead, we
     201                 :            :         // conjoin the set of I/O pairs known about each oracle function
     202                 :            :         // to the query.
     203 [ -  + ][ -  - ]:        615 :         if (ochecker != nullptr && ochecker->hasOracleCalls(f))
         [ -  + ][ -  + ]
                 [ -  - ]
     204                 :            :         {
     205                 :            :           const std::map<Node, std::vector<Node>>& ocalls =
     206                 :          0 :               ochecker->getOracleCalls(f);
     207         [ -  - ]:          0 :           for (const std::pair<const Node, std::vector<Node>>& oc : ocalls)
     208                 :            :           {
     209                 :            :             // we ignore calls that had a size other than one
     210         [ -  - ]:          0 :             if (oc.second.size() == 1)
     211                 :            :             {
     212                 :          0 :               qconj.push_back(oc.first.eqNode(oc.second[0]));
     213                 :            :             }
     214                 :            :           }
     215                 :            :         }
     216                 :            :       }
     217                 :        124 :       query = nm->mkAnd(qconj);
     218         [ +  - ]:        248 :       Trace("cegqi-debug")
     219                 :          0 :           << "after function definitions + oracle calls, query is " << query
     220                 :        124 :           << std::endl;
     221                 :            :     }
     222                 :            :   }
     223                 :       5991 :   return query;
     224                 :            : }
     225                 :            : 
     226                 :            : }  // namespace quantifiers
     227                 :            : }  // namespace theory
     228                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14