LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers/sygus - cegis.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 371 394 94.2 %
Date: 2026-01-30 12:59:30 Functions: 15 15 100.0 %
Branches: 259 386 67.1 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Aina Niemetz, Haniel Barbosa
       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 cegis.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/quantifiers/sygus/cegis.h"
      17                 :            : 
      18                 :            : #include "expr/node_algorithm.h"
      19                 :            : #include "options/base_options.h"
      20                 :            : #include "options/quantifiers_options.h"
      21                 :            : #include "printer/printer.h"
      22                 :            : #include "theory/quantifiers/sygus/example_min_eval.h"
      23                 :            : #include "theory/quantifiers/sygus/synth_conjecture.h"
      24                 :            : #include "theory/quantifiers/sygus/term_database_sygus.h"
      25                 :            : #include "theory/rewriter.h"
      26                 :            : 
      27                 :            : using namespace std;
      28                 :            : using namespace cvc5::internal::kind;
      29                 :            : using namespace cvc5::context;
      30                 :            : 
      31                 :            : namespace cvc5::internal {
      32                 :            : namespace theory {
      33                 :            : namespace quantifiers {
      34                 :            : 
      35                 :      19992 : Cegis::Cegis(Env& env,
      36                 :            :              QuantifiersState& qs,
      37                 :            :              QuantifiersInferenceManager& qim,
      38                 :            :              TermDbSygus* tds,
      39                 :      19992 :              SynthConjecture* p)
      40                 :            :     : SygusModule(env, qs, qim, tds, p),
      41                 :      19992 :       d_eval_unfold(tds->getEvalUnfold()),
      42                 :            :       d_cexClosedEnum(false),
      43                 :            :       d_cegis_sampler(env),
      44                 :            :       d_usingSymCons(false),
      45                 :      19992 :       d_doEvalUnfold(false)
      46                 :            : {
      47                 :      19992 : }
      48                 :            : 
      49                 :       2059 : bool Cegis::initialize(Node conj, Node n, const std::vector<Node>& candidates)
      50                 :            : {
      51                 :       2059 :   d_base_body = n;
      52                 :       2059 :   d_cexClosedEnum = true;
      53                 :       6177 :   if (d_base_body.getKind() == Kind::NOT
      54 [ +  + ][ +  + ]:       2059 :       && d_base_body[0].getKind() == Kind::FORALL)
         [ +  + ][ +  + ]
                 [ -  - ]
      55                 :            :   {
      56         [ +  + ]:       4752 :     for (const Node& v : d_base_body[0][0])
      57                 :            :     {
      58                 :       3536 :       d_base_vars.push_back(v);
      59         [ +  + ]:       3536 :       if (!v.getType().isClosedEnumerable())
      60                 :            :       {
      61                 :            :         // not closed enumerable, refinement lemmas cannot be sent to the
      62                 :            :         // quantifier-free datatype solver
      63                 :         75 :         d_cexClosedEnum = false;
      64                 :            :       }
      65                 :            :     }
      66                 :       1216 :     d_base_body = d_base_body[0][1];
      67                 :            :   }
      68                 :            : 
      69                 :            :   // assign the cegis sampler if applicable
      70         [ +  + ]:       2059 :   if (options().quantifiers.cegisSample != options::CegisSampleMode::NONE)
      71                 :            :   {
      72         [ +  - ]:         30 :     Trace("cegis-sample") << "Initialize sampler for " << d_base_body << "..."
      73                 :         15 :                           << std::endl;
      74                 :         15 :     TypeNode bt = d_base_body.getType();
      75                 :         15 :     d_cegis_sampler.initialize(
      76                 :         15 :         bt, d_base_vars, options().quantifiers.sygusSamples);
      77                 :            :   }
      78 [ -  + ][ -  + ]:       2059 :   Assert(conj.getKind() == Kind::FORALL);
                 [ -  - ]
      79 [ -  + ][ -  + ]:       2059 :   Assert(conj[0].getNumChildren() == candidates.size());
                 [ -  - ]
      80                 :            :   // construct the substitution d_euSubs if evaluation unfolding is enabled.
      81                 :       2059 :   if (options().quantifiers.sygusEvalUnfoldMode
      82         [ +  - ]:       2059 :       != options::SygusEvalUnfoldMode::NONE)
      83                 :            :   {
      84                 :       2059 :     d_doEvalUnfold = true;
      85                 :       2059 :     NodeManager* nm = nodeManager();
      86         [ +  + ]:       4296 :     for (size_t i = 0, nvars = conj[0].getNumChildren(); i < nvars; i++)
      87                 :            :     {
      88                 :       2245 :       TypeNode tn = candidates[i].getType();
      89                 :       2245 :       SygusTypeInfo& ti = d_tds->getTypeInfo(tn);
      90                 :       2245 :       const std::vector<Node>& vars = ti.getVarList();
      91                 :       2245 :       std::vector<Node> vs;
      92         [ +  + ]:       5432 :       for (const Node& v : vars)
      93                 :            :       {
      94                 :       3187 :         vs.push_back(NodeManager::mkBoundVar(v.getType()));
      95                 :            :       }
      96                 :       2245 :       std::vector<Node> eargs;
      97                 :       2245 :       eargs.push_back(candidates[i]);
      98                 :       2245 :       Node ret;
      99         [ +  + ]:       2245 :       if (!vs.empty())
     100                 :            :       {
     101                 :       1741 :         Node lvl = nm->mkNode(Kind::BOUND_VAR_LIST, vs);
     102                 :       1741 :         eargs.insert(eargs.end(), vs.begin(), vs.end());
     103                 :       3482 :         ret = nm->mkNode(
     104                 :       5223 :             Kind::LAMBDA, lvl, nm->mkNode(Kind::DT_SYGUS_EVAL, eargs));
     105                 :            :       }
     106                 :            :       else
     107                 :            :       {
     108                 :        504 :         ret = nm->mkNode(Kind::DT_SYGUS_EVAL, eargs);
     109                 :            :       }
     110                 :       2245 :       TypeNode rt = ret.getType();
     111 [ +  + ][ +  + ]:       2245 :       if (rt.isRegExp() || (rt.isFunction() && rt.getRangeType().isRegExp()))
         [ -  + ][ +  + ]
         [ +  + ][ -  - ]
     112                 :            :       {
     113                 :            :         // cannot do evaluation unfolding for regular expressions
     114                 :          8 :         d_doEvalUnfold = false;
     115                 :          8 :         break;
     116                 :            :       }
     117                 :       2237 :       d_euSubs.add(conj[0][i], ret);
     118                 :            :     }
     119                 :            :   }
     120                 :       2059 :   return processInitialize(conj, n, candidates);
     121                 :            : }
     122                 :            : 
     123                 :       1130 : bool Cegis::processInitialize(Node conj,
     124                 :            :                               Node n,
     125                 :            :                               const std::vector<Node>& candidates)
     126                 :            : {
     127         [ +  - ]:       1130 :   Trace("cegis") << "Initialize cegis..." << std::endl;
     128                 :       1130 :   size_t csize = candidates.size();
     129                 :            :   // The role of enumerators is to be either the single solution or part of
     130                 :            :   // a solution involving multiple enumerators.
     131                 :       1130 :   EnumeratorRole erole =
     132         [ +  + ]:       1130 :       csize == 1 ? ROLE_ENUM_SINGLE_SOLUTION : ROLE_ENUM_MULTI_SOLUTION;
     133                 :            :   // initialize an enumerator for each candidate
     134                 :       1130 :   std::vector<Node> activeGuards;
     135         [ +  + ]:       2350 :   for (size_t i = 0; i < csize; i++)
     136                 :            :   {
     137         [ +  - ]:       1220 :     Trace("cegis") << "...register enumerator " << candidates[i];
     138                 :            :     // We use symbolic constants if we are doing repair constants or if the
     139                 :            :     // grammar construction was not simple.
     140                 :       1220 :     if (options().quantifiers.sygusRepairConst
     141 [ +  + ][ -  + ]:       1220 :         || options().quantifiers.sygusGrammarConsMode
                 [ +  + ]
     142                 :            :                != options::SygusGrammarConsMode::SIMPLE)
     143                 :            :     {
     144                 :       2424 :       TypeNode ctn = candidates[i].getType();
     145                 :       1212 :       d_tds->registerSygusType(ctn);
     146                 :       1212 :       SygusTypeInfo& cti = d_tds->getTypeInfo(ctn);
     147         [ +  + ]:       1212 :       if (cti.hasSubtermSymbolicCons())
     148                 :            :       {
     149                 :            :         // remember that we are using symbolic constructors
     150                 :         29 :         d_usingSymCons = true;
     151         [ +  - ]:         29 :         Trace("cegis") << " (using symbolic constructors)";
     152                 :            :       }
     153                 :            :     }
     154         [ +  - ]:       1220 :     Trace("cegis") << std::endl;
     155                 :       2440 :     Node e = candidates[i];
     156                 :       1220 :     d_tds->registerEnumerator(e, e, d_parent, erole);
     157                 :       2440 :     Node g = d_tds->getActiveGuardForEnumerator(e);
     158         [ +  + ]:       1220 :     if (!g.isNull())
     159                 :            :     {
     160                 :        996 :       activeGuards.push_back(g);
     161                 :            :     }
     162                 :            :   }
     163         [ +  + ]:       1130 :   if (!activeGuards.empty())
     164                 :            :   {
     165                 :            :     // This lemma has the semantics "if the conjecture holds, then there must
     166                 :            :     // be another value to enumerate for each function to synthesize". Note
     167                 :            :     // that active guards are only assigned for "actively generated"
     168                 :            :     // enumerators, e.g. when using sygus-enum=fast. Thus, this lemma is
     169                 :            :     // typically only added for single function conjectures.
     170                 :            :     // This lemma allows us to answer infeasible when we run out of values (for
     171                 :            :     // finite grammars).
     172                 :        996 :     NodeManager* nm = nodeManager();
     173                 :       1992 :     Node enumLem = nm->mkNode(Kind::IMPLIES, conj, nm->mkAnd(activeGuards));
     174                 :        996 :     d_qim.lemma(enumLem, InferenceId::QUANTIFIERS_SYGUS_COMPLETE_ENUM);
     175                 :            :   }
     176                 :       2260 :   return true;
     177                 :            : }
     178                 :            : 
     179                 :      98509 : void Cegis::getTermList(const std::vector<Node>& candidates,
     180                 :            :                         std::vector<Node>& enums)
     181                 :            : {
     182                 :      98509 :   enums.insert(enums.end(), candidates.begin(), candidates.end());
     183                 :      98509 : }
     184                 :            : 
     185                 :      45364 : bool Cegis::addEvalLemmas(const std::vector<Node>& candidates,
     186                 :            :                           const std::vector<Node>& candidate_values)
     187                 :            : {
     188                 :            :   // First, decide if this call will apply "conjecture-specific refinement".
     189                 :            :   // In other words, in some settings, the following method will identify and
     190                 :            :   // block a class of solutions {candidates -> S} that generalizes the current
     191                 :            :   // one (given by {candidates -> candidate_values}), such that for each
     192                 :            :   // candidate_values' in S, we have that {candidates -> candidate_values'} is
     193                 :            :   // also not a solution for the given conjecture. We may not
     194                 :            :   // apply this form of refinement if any (relevant) enumerator in candidates is
     195                 :            :   // "actively generated" (see TermDbSygs::isPassiveEnumerator), since its
     196                 :            :   // model values are themselves interpreted as classes of solutions.
     197                 :      45364 :   bool doGen = true;
     198         [ +  + ]:     130282 :   for (const Node& v : candidates)
     199                 :            :   {
     200                 :            :     // if it is relevant to refinement
     201         [ +  + ]:     120942 :     if (d_refinement_lemma_vars.find(v) != d_refinement_lemma_vars.end())
     202                 :            :     {
     203         [ +  + ]:     109288 :       if (!d_tds->isPassiveEnumerator(v))
     204                 :            :       {
     205                 :      36024 :         doGen = false;
     206                 :      36024 :         break;
     207                 :            :       }
     208                 :            :     }
     209                 :            :   }
     210                 :      45364 :   NodeManager* nm = nodeManager();
     211                 :      45364 :   bool addedEvalLemmas = false;
     212                 :            :   // Refinement evaluation should not be done for grammars with symbolic
     213                 :            :   // constructors.
     214         [ +  + ]:      45364 :   if (!d_usingSymCons)
     215                 :            :   {
     216         [ +  - ]:      90378 :     Trace("sygus-engine") << "  *** Do refinement lemma evaluation"
     217         [ -  - ]:      45189 :                           << (doGen ? " with conjecture-specific refinement"
     218                 :          0 :                                     : "")
     219                 :      45189 :                           << "..." << std::endl;
     220                 :            :     // see if any refinement lemma is refuted by evaluation
     221         [ +  + ]:      45189 :     if (doGen)
     222                 :            :     {
     223                 :      18614 :       std::vector<Node> cre_lems;
     224                 :       9307 :       getRefinementEvalLemmas(candidates, candidate_values, cre_lems);
     225         [ +  + ]:       9307 :       if (!cre_lems.empty())
     226                 :            :       {
     227         [ +  + ]:       8148 :         for (const Node& cl : cre_lems)
     228                 :            :         {
     229                 :       4791 :           d_qim.addPendingLemma(cl, InferenceId::QUANTIFIERS_SYGUS_REFINE_EVAL);
     230                 :            :         }
     231                 :       3357 :         addedEvalLemmas = true;
     232                 :            :         /* we could, but do not return here. experimentally, it is better to
     233                 :            :           add the lemmas below as well, in parallel. */
     234                 :            :       }
     235                 :            :     }
     236                 :            :     else
     237                 :            :     {
     238                 :            :       // just check whether the refinement lemmas are satisfied, fail if not
     239         [ +  + ]:      35882 :       if (checkRefinementEvalLemmas(candidates, candidate_values))
     240                 :            :       {
     241         [ +  - ]:      69330 :         Trace("sygus-engine") << "...(actively enumerated) candidate failed "
     242                 :          0 :                                  "refinement lemma evaluation."
     243                 :      34665 :                               << std::endl;
     244                 :      34665 :         return true;
     245                 :            :       }
     246                 :            :     }
     247                 :            :   }
     248                 :            :   // we only do evaluation unfolding for passive enumerators
     249 [ +  + ][ +  + ]:      10699 :   bool doEvalUnfold = (doGen && d_doEvalUnfold);
     250         [ +  + ]:      10699 :   if (doEvalUnfold)
     251                 :            :   {
     252         [ +  - ]:       9336 :     Trace("sygus-engine") << "  *** Do evaluation unfolding..." << std::endl;
     253                 :      18672 :     std::vector<Node> eager_terms, eager_vals, eager_exps;
     254         [ +  + ]:      94250 :     for (unsigned i = 0, size = candidates.size(); i < size; ++i)
     255                 :            :     {
     256         [ +  - ]:     169828 :       Trace("cegqi-debug") << "  register " << candidates[i] << " -> "
     257                 :      84914 :                            << candidate_values[i] << std::endl;
     258                 :      84914 :       d_eval_unfold->registerModelValue(candidates[i],
     259                 :      84914 :                                         candidate_values[i],
     260                 :            :                                         eager_terms,
     261                 :            :                                         eager_vals,
     262                 :            :                                         eager_exps);
     263                 :            :     }
     264         [ +  - ]:      18672 :     Trace("cegqi-debug") << "...produced " << eager_terms.size()
     265                 :       9336 :                          << " evaluation unfold lemmas.\n";
     266         [ +  + ]:      37122 :     for (unsigned i = 0, size = eager_terms.size(); i < size; ++i)
     267                 :            :     {
     268                 :            :       Node lem = nm->mkNode(Kind::OR,
     269                 :      55572 :                             eager_exps[i].negate(),
     270                 :     111144 :                             eager_terms[i].eqNode(eager_vals[i]));
     271                 :            :       // apply the substitution, which ensures that this lemma does not
     272                 :            :       // contain free variables (e.g. if using forward declarations).
     273                 :      27786 :       lem = d_euSubs.apply(lem);
     274                 :      27786 :       d_qim.addPendingLemma(lem, InferenceId::QUANTIFIERS_SYGUS_EVAL_UNFOLD);
     275                 :      27786 :       addedEvalLemmas = true;
     276         [ +  - ]:      55572 :       Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation unfold : " << lem
     277                 :      27786 :                            << std::endl;
     278                 :            :     }
     279                 :            :   }
     280                 :      10699 :   return addedEvalLemmas;
     281                 :            : }
     282                 :            : 
     283                 :        574 : Node Cegis::getRefinementLemmaFormula()
     284                 :            : {
     285                 :       1148 :   std::vector<Node> conj;
     286                 :            :   conj.insert(
     287                 :        574 :       conj.end(), d_refinement_lemmas.begin(), d_refinement_lemmas.end());
     288                 :            :   // get the propagated values
     289         [ +  + ]:       4002 :   for (unsigned i = 0, nprops = d_rl_eval_hds.size(); i < nprops; i++)
     290                 :            :   {
     291                 :       3428 :     conj.push_back(d_rl_eval_hds[i].eqNode(d_rl_vals[i]));
     292                 :            :   }
     293                 :            :   // make the formula
     294                 :        574 :   NodeManager* nm = nodeManager();
     295                 :        574 :   Node ret;
     296         [ +  + ]:        574 :   if (conj.empty())
     297                 :            :   {
     298                 :          8 :     ret = nm->mkConst(true);
     299                 :            :   }
     300                 :            :   else
     301                 :            :   {
     302         [ -  + ]:        566 :     ret = conj.size() == 1 ? conj[0] : nm->mkNode(Kind::AND, conj);
     303                 :            :   }
     304                 :       1148 :   return ret;
     305                 :            : }
     306                 :            : 
     307                 :      45962 : bool Cegis::constructCandidates(const std::vector<Node>& enums,
     308                 :            :                                 const std::vector<Node>& enum_values,
     309                 :            :                                 const std::vector<Node>& candidates,
     310                 :            :                                 std::vector<Node>& candidate_values)
     311                 :            : {
     312         [ -  + ]:      45962 :   if (TraceIsOn("cegis"))
     313                 :            :   {
     314         [ -  - ]:          0 :     Trace("cegis") << "  Enumerators :\n";
     315         [ -  - ]:          0 :     for (unsigned i = 0, size = enums.size(); i < size; ++i)
     316                 :            :     {
     317         [ -  - ]:          0 :       Trace("cegis") << "    " << enums[i] << " -> ";
     318                 :          0 :       TermDbSygus::toStreamSygus("cegis", enum_values[i]);
     319         [ -  - ]:          0 :       Trace("cegis") << "\n";
     320                 :            :     }
     321                 :            :   }
     322                 :            :   // if we are using grammar-based repair
     323 [ +  + ][ +  - ]:      45962 :   if (d_usingSymCons && options().quantifiers.sygusRepairConst)
                 [ +  + ]
     324                 :            :   {
     325                 :        773 :     SygusRepairConst* src = d_parent->getRepairConst();
     326 [ -  + ][ -  + ]:        773 :     Assert(src != nullptr);
                 [ -  - ]
     327                 :            :     // check if any enum_values have symbolic terms that must be repaired
     328                 :        773 :     bool mustRepair = false;
     329         [ +  + ]:        948 :     for (const Node& c : enum_values)
     330                 :            :     {
     331         [ +  + ]:        773 :       if (SygusRepairConst::mustRepair(c))
     332                 :            :       {
     333                 :        598 :         mustRepair = true;
     334                 :        598 :         break;
     335                 :            :       }
     336                 :            :     }
     337         [ +  - ]:        773 :     Trace("cegis-debug") << "must repair is: " << mustRepair << std::endl;
     338                 :            :     // if the solution contains a subterm that must be repaired
     339         [ +  + ]:        773 :     if (mustRepair)
     340                 :            :     {
     341                 :       1196 :       std::vector<Node> fail_cvs = enum_values;
     342 [ -  + ][ -  + ]:        598 :       Assert(candidates.size() == fail_cvs.size());
                 [ -  - ]
     343                 :            :       // try to solve entire problem?
     344         [ +  + ]:        598 :       if (src->repairSolution(candidates, fail_cvs, candidate_values))
     345                 :            :       {
     346         [ +  - ]:         24 :         Trace("cegis") << "...solution is repaired" << std::endl;
     347                 :         24 :         return true;
     348                 :            :       }
     349                 :       1148 :       Node rl = getRefinementLemmaFormula();
     350                 :            :       // try to solve for the refinement lemmas only
     351                 :            :       bool ret =
     352                 :        574 :           src->repairSolution(rl, candidates, fail_cvs, candidate_values);
     353                 :            :       // Even if ret is true, we will exclude the skeleton as well; this means
     354                 :            :       // that we have one chance to repair each skeleton. It is possible however
     355                 :            :       // that we might want to repair the same skeleton multiple times.
     356                 :        574 :       std::vector<Node> exp;
     357                 :        574 :       bool doExplain = true;
     358         [ +  + ]:        914 :       for (unsigned i = 0, size = enums.size(); i < size; i++)
     359                 :            :       {
     360         [ +  + ]:        574 :         if (!d_tds->isPassiveEnumerator(enums[i]))
     361                 :            :         {
     362                 :            :           // don't exclude active (fast) enumerators
     363                 :        234 :           doExplain = false;
     364                 :        234 :           break;
     365                 :            :         }
     366                 :       1020 :         d_tds->getExplain()->getExplanationForEquality(
     367                 :        680 :             enums[i], enum_values[i], exp);
     368                 :            :       }
     369         [ +  + ]:        574 :       if (doExplain)
     370                 :            :       {
     371 [ -  + ][ -  + ]:        340 :         Assert(!exp.empty());
                 [ -  - ]
     372                 :        340 :         NodeManager* nm = nodeManager();
     373         [ +  + ]:        340 :         Node expn = exp.size() == 1 ? exp[0] : nm->mkNode(Kind::AND, exp);
     374                 :            :         // must guard it
     375                 :       1020 :         expn = nm->mkNode(
     376                 :       1360 :             Kind::OR, d_parent->getConjecture().negate(), expn.negate());
     377                 :        340 :         d_qim.addPendingLemma(
     378                 :            :             expn, InferenceId::QUANTIFIERS_SYGUS_REPAIR_CONST_EXCLUDE);
     379                 :            :       }
     380         [ +  - ]:       1148 :       Trace("cegis") << "...solution was processed via repair, success = "
     381                 :        574 :                      << ret << std::endl;
     382                 :        574 :       return ret;
     383                 :            :     }
     384                 :            :   }
     385                 :            : 
     386                 :            :   // evaluate on refinement lemmas
     387                 :      45364 :   bool addedEvalLemmas = addEvalLemmas(enums, enum_values);
     388                 :            : 
     389                 :            :   // try to construct candidates
     390         [ +  + ]:      45364 :   if (!processConstructCandidates(
     391                 :      45364 :           enums, enum_values, candidates, candidate_values, !addedEvalLemmas))
     392                 :            :   {
     393         [ +  - ]:      41343 :     Trace("cegis") << "...construct candidates failed" << std::endl;
     394                 :      41343 :     return false;
     395                 :            :   }
     396                 :            : 
     397                 :       4021 :   if (options().quantifiers.cegisSample != options::CegisSampleMode::NONE
     398 [ +  + ][ +  + ]:       4021 :       && !addedEvalLemmas)
                 [ +  + ]
     399                 :            :   {
     400                 :            :     // if we didn't add a lemma, trying sampling to add a refinement lemma
     401                 :            :     // that immediately refutes the candidate we just constructed
     402         [ +  + ]:         79 :     if (sampleAddRefinementLemma(candidates, candidate_values))
     403                 :            :     {
     404                 :         10 :       candidate_values.clear();
     405                 :            :       // restart (should be guaranteed to add evaluation lemmas on this call)
     406                 :         10 :       return constructCandidates(
     407                 :         10 :           enums, enum_values, candidates, candidate_values);
     408                 :            :     }
     409                 :            :   }
     410         [ +  - ]:       4011 :   Trace("cegis") << "...success" << std::endl;
     411                 :       4011 :   return true;
     412                 :            : }
     413                 :            : 
     414                 :      40408 : bool Cegis::processConstructCandidates(const std::vector<Node>& enums,
     415                 :            :                                        const std::vector<Node>& enum_values,
     416                 :            :                                        const std::vector<Node>& candidates,
     417                 :            :                                        std::vector<Node>& candidate_values,
     418                 :            :                                        bool satisfiedRl)
     419                 :            : {
     420         [ +  + ]:      40408 :   if (satisfiedRl)
     421                 :            :   {
     422                 :            :     candidate_values.insert(
     423                 :       2677 :         candidate_values.end(), enum_values.begin(), enum_values.end());
     424                 :       2677 :     return true;
     425                 :            :   }
     426                 :      37731 :   return false;
     427                 :            : }
     428                 :            : 
     429                 :       1700 : void Cegis::addRefinementLemma(Node lem)
     430                 :            : {
     431         [ +  - ]:       1700 :   Trace("cegis-rl") << "Cegis::addRefinementLemma: " << lem << std::endl;
     432                 :       1700 :   d_refinement_lemmas.push_back(lem);
     433                 :            :   // apply existing substitution
     434                 :       3400 :   Node slem = lem;
     435         [ +  + ]:       1700 :   if (!d_rl_eval_hds.empty())
     436                 :            :   {
     437                 :       1740 :     slem = lem.substitute(d_rl_eval_hds.begin(),
     438                 :            :                           d_rl_eval_hds.end(),
     439                 :            :                           d_rl_vals.begin(),
     440                 :        870 :                           d_rl_vals.end());
     441                 :            :   }
     442                 :            :   // rewrite with extended rewriter
     443                 :       1700 :   slem = d_tds->rewriteNode(slem);
     444                 :            :   // collect all variables in slem
     445                 :       1700 :   expr::getSymbols(slem, d_refinement_lemma_vars);
     446                 :       3400 :   std::vector<Node> waiting;
     447                 :       1700 :   waiting.push_back(lem);
     448                 :       1700 :   unsigned wcounter = 0;
     449                 :            :   // while we are not done adding lemmas
     450         [ +  + ]:       4094 :   while (wcounter < waiting.size())
     451                 :            :   {
     452                 :            :     // add the conjunct, possibly propagating
     453                 :       2394 :     addRefinementLemmaConjunct(wcounter, waiting);
     454                 :       2394 :     wcounter++;
     455                 :            :   }
     456                 :       1700 : }
     457                 :            : 
     458                 :       2394 : void Cegis::addRefinementLemmaConjunct(unsigned wcounter,
     459                 :            :                                        std::vector<Node>& waiting)
     460                 :            : {
     461                 :       2394 :   Node lem = waiting[wcounter];
     462                 :       2394 :   lem = rewrite(lem);
     463                 :            :   // apply substitution and rewrite if applicable
     464         [ +  + ]:       2394 :   if (lem.isConst())
     465                 :            :   {
     466         [ -  + ]:         20 :     if (!lem.getConst<bool>())
     467                 :            :     {
     468                 :            :       // conjecture is infeasible
     469                 :            :     }
     470                 :            :     else
     471                 :            :     {
     472                 :          0 :       return;
     473                 :            :     }
     474                 :            :   }
     475                 :            :   // break into conjunctions
     476         [ +  + ]:       2394 :   if (lem.getKind() == Kind::AND)
     477                 :            :   {
     478         [ +  + ]:        887 :     for (const Node& lc : lem)
     479                 :            :     {
     480                 :        692 :       waiting.push_back(lc);
     481                 :            :     }
     482                 :        195 :     return;
     483                 :            :   }
     484                 :            :   // does this correspond to a substitution?
     485                 :       2199 :   NodeManager* nm = nodeManager();
     486                 :       2199 :   TNode term;
     487                 :       2199 :   TNode val;
     488         [ +  + ]:       2199 :   if (lem.getKind() == Kind::EQUAL)
     489                 :            :   {
     490         [ +  + ]:       1745 :     for (unsigned i = 0; i < 2; i++)
     491                 :            :     {
     492                 :       1422 :       if (lem[i].isConst() && d_tds->isEvaluationPoint(lem[1 - i]))
     493                 :            :       {
     494                 :        426 :         term = lem[1 - i];
     495                 :        426 :         val = lem[i];
     496                 :        426 :         break;
     497                 :            :       }
     498                 :            :     }
     499                 :            :   }
     500                 :            :   else
     501                 :            :   {
     502         [ +  + ]:       1450 :     term = lem.getKind() == Kind::NOT ? lem[0] : lem;
     503                 :            :     // predicate case: the conjunct is a (negated) evaluation point
     504         [ +  + ]:       1450 :     if (d_tds->isEvaluationPoint(term))
     505                 :            :     {
     506                 :        989 :       val = nm->mkConst(lem.getKind() != Kind::NOT);
     507                 :            :     }
     508                 :            :   }
     509         [ +  + ]:       2199 :   if (!val.isNull())
     510                 :            :   {
     511         [ +  + ]:       1415 :     if (d_refinement_lemma_unit.find(lem) != d_refinement_lemma_unit.end())
     512                 :            :     {
     513                 :            :       // already added
     514                 :        487 :       return;
     515                 :            :     }
     516         [ +  - ]:       1856 :     Trace("cegis-rl") << "* cegis-rl: propagate: " << term << " -> " << val
     517                 :        928 :                       << std::endl;
     518                 :        928 :     d_rl_eval_hds.push_back(term);
     519                 :        928 :     d_rl_vals.push_back(val);
     520                 :        928 :     d_refinement_lemma_unit.insert(lem);
     521                 :            : 
     522                 :            :     // apply to waiting lemmas beyond this one
     523         [ +  + ]:       1316 :     for (unsigned i = wcounter + 1, size = waiting.size(); i < size; i++)
     524                 :            :     {
     525                 :        388 :       waiting[i] = waiting[i].substitute(term, val);
     526                 :            :     }
     527                 :            :     // apply to all existing refinement lemmas
     528                 :       1856 :     std::vector<Node> to_rem;
     529         [ +  + ]:       1544 :     for (const Node& rl : d_refinement_lemma_conj)
     530                 :            :     {
     531                 :       1848 :       Node srl = rl.substitute(term, val);
     532         [ +  + ]:        616 :       if (srl != rl)
     533                 :            :       {
     534         [ +  - ]:          4 :         Trace("cegis-rl") << "* cegis-rl: replace: " << rl << " -> " << srl
     535                 :          2 :                           << std::endl;
     536                 :          2 :         waiting.push_back(srl);
     537                 :          2 :         to_rem.push_back(rl);
     538                 :            :       }
     539                 :            :     }
     540         [ +  + ]:        930 :     for (const Node& tr : to_rem)
     541                 :            :     {
     542                 :          2 :       d_refinement_lemma_conj.erase(tr);
     543                 :            :     }
     544                 :            :   }
     545                 :            :   else
     546                 :            :   {
     547         [ -  + ]:        784 :     if (TraceIsOn("cegis-rl"))
     548                 :            :     {
     549         [ -  - ]:          0 :       if (d_refinement_lemma_conj.find(lem) == d_refinement_lemma_conj.end())
     550                 :            :       {
     551         [ -  - ]:          0 :         Trace("cegis-rl") << "cegis-rl: add: " << lem << std::endl;
     552                 :            :       }
     553                 :            :     }
     554                 :        784 :     d_refinement_lemma_conj.insert(lem);
     555                 :            :   }
     556                 :            : }
     557                 :            : 
     558                 :       1528 : void Cegis::registerRefinementLemma(const std::vector<Node>& vars, Node lem)
     559                 :            : {
     560                 :       1528 :   addRefinementLemma(lem);
     561                 :            :   // must be closed enumerable
     562 [ +  + ][ +  + ]:       1528 :   if (d_cexClosedEnum && d_doEvalUnfold)
     563                 :            :   {
     564                 :            :     // Make the refinement lemma and add it to lems.
     565                 :            :     // This lemma is guarded by the parent's conjecture, which has the semantics
     566                 :            :     // "this conjecture has a solution", hence this lemma states:
     567                 :            :     // if the parent conjecture has a solution, it satisfies the specification
     568                 :            :     // for the given concrete point.
     569                 :       1481 :     Node rlem = nodeManager()->mkNode(
     570                 :       2962 :         Kind::OR, d_parent->getConjecture().negate(), lem);
     571                 :       1481 :     d_qim.addPendingLemma(rlem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE);
     572                 :            :   }
     573                 :       1528 : }
     574                 :            : 
     575                 :     101207 : bool Cegis::usingRepairConst() { return true; }
     576                 :       9307 : bool Cegis::getRefinementEvalLemmas(const std::vector<Node>& vs,
     577                 :            :                                     const std::vector<Node>& ms,
     578                 :            :                                     std::vector<Node>& lems)
     579                 :            : {
     580         [ +  - ]:      18614 :   Trace("sygus-cref-eval") << "Cref eval : conjecture has "
     581                 :          0 :                            << d_refinement_lemma_unit.size() << " unit and "
     582                 :       9307 :                            << d_refinement_lemma_conj.size()
     583                 :          0 :                            << " non-unit refinement lemma conjunctions."
     584                 :       9307 :                            << std::endl;
     585 [ -  + ][ -  + ]:       9307 :   Assert(vs.size() == ms.size());
                 [ -  - ]
     586                 :            : 
     587                 :       9307 :   NodeManager* nm = nodeManager();
     588                 :            : 
     589                 :      18614 :   Node nfalse = nm->mkConst(false);
     590                 :       9307 :   Node neg_guard = d_parent->getConjecture().negate();
     591                 :       9307 :   bool ret = false;
     592                 :            : 
     593         [ +  + ]:      23306 :   for (unsigned r = 0; r < 2; r++)
     594                 :            :   {
     595         [ +  + ]:      17356 :     std::unordered_set<Node>& rlemmas =
     596                 :            :         r == 0 ? d_refinement_lemma_unit : d_refinement_lemma_conj;
     597         [ +  + ]:      88553 :     for (const Node& lem : rlemmas)
     598                 :            :     {
     599 [ -  + ][ -  + ]:      71197 :       Assert(!lem.isNull());
                 [ -  - ]
     600                 :     142394 :       std::map<Node, Node> visited;
     601                 :     142394 :       std::map<Node, std::vector<Node> > exp;
     602                 :     142394 :       EvalSygusInvarianceTest vsit(d_env.getRewriter());
     603         [ +  - ]:     142394 :       Trace("sygus-cref-eval") << "Check refinement lemma conjunct " << lem
     604                 :      71197 :                                << " against current model." << std::endl;
     605         [ +  - ]:     142394 :       Trace("sygus-cref-eval2") << "Check refinement lemma conjunct " << lem
     606                 :      71197 :                                 << " against current model." << std::endl;
     607                 :     142394 :       Node cre_lem;
     608                 :     142394 :       Node lemcs = lem.substitute(vs.begin(), vs.end(), ms.begin(), ms.end());
     609         [ +  - ]:     142394 :       Trace("sygus-cref-eval2")
     610                 :      71197 :           << "...under substitution it is : " << lemcs << std::endl;
     611                 :     142394 :       Node lemcsu = d_tds->rewriteNode(lemcs);
     612         [ +  - ]:     142394 :       Trace("sygus-cref-eval2")
     613                 :      71197 :           << "...after unfolding is : " << lemcsu << std::endl;
     614 [ +  + ][ +  + ]:      71197 :       if (lemcsu.isConst() && !lemcsu.getConst<bool>())
                 [ +  + ]
     615                 :            :       {
     616                 :       7791 :         ret = true;
     617                 :      15582 :         std::vector<Node> msu;
     618                 :      15582 :         std::vector<Node> mexp;
     619                 :       7791 :         msu.insert(msu.end(), ms.begin(), ms.end());
     620                 :      15582 :         std::map<TypeNode, size_t> var_count;
     621         [ +  + ]:      52178 :         for (unsigned k = 0; k < vs.size(); k++)
     622                 :            :         {
     623                 :      44387 :           vsit.setUpdatedTerm(msu[k]);
     624                 :      44387 :           msu[k] = vs[k];
     625                 :            :           // substitute for everything except this
     626                 :            :           Node sconj =
     627                 :      88774 :               lem.substitute(vs.begin(), vs.end(), msu.begin(), msu.end());
     628                 :      44387 :           vsit.init(sconj, vs[k], nfalse);
     629                 :            :           // get minimal explanation for this
     630                 :      44387 :           Node ut = vsit.getUpdatedTerm();
     631         [ +  - ]:      88774 :           Trace("sygus-cref-eval2-debug")
     632                 :          0 :               << "  compute min explain of : " << vs[k] << " = " << ut
     633                 :      44387 :               << std::endl;
     634                 :      88774 :           d_tds->getExplain()->getExplanationFor(
     635                 :      44387 :               vs[k], ut, mexp, vsit, var_count, false);
     636         [ +  - ]:      44387 :           Trace("sygus-cref-eval2-debug") << "exp now: " << mexp << std::endl;
     637                 :      44387 :           msu[k] = vsit.getUpdatedTerm();
     638         [ +  - ]:      88774 :           Trace("sygus-cref-eval2-debug")
     639                 :      44387 :               << "updated term : " << msu[k] << std::endl;
     640                 :            :         }
     641         [ +  + ]:       7791 :         if (!mexp.empty())
     642                 :            :         {
     643         [ +  + ]:       7790 :           Node en = mexp.size() == 1 ? mexp[0] : nm->mkNode(Kind::AND, mexp);
     644                 :       7790 :           cre_lem = nm->mkNode(Kind::OR, en.negate(), neg_guard);
     645                 :            :         }
     646                 :            :         else
     647                 :            :         {
     648                 :          1 :           cre_lem = neg_guard;
     649                 :            :         }
     650         [ +  + ]:       7791 :         if (std::find(lems.begin(), lems.end(), cre_lem) == lems.end())
     651                 :            :         {
     652         [ +  - ]:       9582 :           Trace("sygus-cref-eval") << "...produced lemma : " << cre_lem
     653                 :       4791 :                                    << std::endl;
     654                 :       4791 :           lems.push_back(cre_lem);
     655                 :            :         }
     656                 :            :       }
     657                 :            :     }
     658         [ +  + ]:      17356 :     if (!lems.empty())
     659                 :            :     {
     660                 :       3357 :       break;
     661                 :            :     }
     662                 :            :   }
     663                 :      18614 :   return ret;
     664                 :            : }
     665                 :            : 
     666                 :      35882 : bool Cegis::checkRefinementEvalLemmas(const std::vector<Node>& vs,
     667                 :            :                                       const std::vector<Node>& ms)
     668                 :            : {
     669                 :            :   // Maybe we already evaluated some terms in refinement lemmas.
     670                 :            :   // In particular, the example eval cache for f may have some evaluations
     671                 :            :   // cached, which we add to evalVisited and pass to the evaluator below.
     672                 :      71764 :   std::unordered_map<Node, Node> evalVisited;
     673                 :      35882 :   ExampleInfer* ei = d_parent->getExampleInfer();
     674         [ +  + ]:      71764 :   for (unsigned i = 0, vsize = vs.size(); i < vsize; i++)
     675                 :            :   {
     676                 :      71764 :     Node f = vs[i];
     677                 :      35882 :     ExampleEvalCache* eec = d_parent->getExampleEvalCache(f);
     678         [ +  + ]:      35882 :     if (eec != nullptr)
     679                 :            :     {
     680                 :            :       // get the results we obtained through the example evaluation utility
     681                 :       4994 :       std::vector<Node> vsProc;
     682                 :       4994 :       std::vector<Node> msProc;
     683                 :       4994 :       Node bmsi = d_tds->sygusToBuiltin(ms[i]);
     684                 :       2497 :       ei->getExampleTerms(f, vsProc);
     685                 :       2497 :       eec->evaluateVec(bmsi, msProc);
     686 [ -  + ][ -  + ]:       2497 :       Assert(vsProc.size() == msProc.size());
                 [ -  - ]
     687         [ +  + ]:      23796 :       for (unsigned j = 0, psize = vsProc.size(); j < psize; j++)
     688                 :            :       {
     689                 :      21299 :         evalVisited[vsProc[j]] = msProc[j];
     690 [ -  + ][ -  + ]:      21299 :         Assert(vsProc[j].getType() == msProc[j].getType());
                 [ -  - ]
     691                 :            :       }
     692                 :            :     }
     693                 :            :   }
     694                 :            : 
     695         [ +  + ]:      38524 :   for (unsigned r = 0; r < 2; r++)
     696                 :            :   {
     697         [ +  + ]:      37307 :     std::unordered_set<Node>& rlemmas =
     698                 :            :         r == 0 ? d_refinement_lemma_unit : d_refinement_lemma_conj;
     699         [ +  + ]:      63464 :     for (const Node& lem : rlemmas)
     700                 :            :     {
     701                 :            :       // We may have computed the evaluation of some function applications
     702                 :            :       // via example-based symmetry breaking, stored in evalVisited.
     703                 :      60822 :       Node lemcsu = evaluate(lem, vs, ms, evalVisited);
     704 [ +  + ][ +  + ]:      60822 :       if (lemcsu.isConst() && !lemcsu.getConst<bool>())
                 [ +  + ]
     705                 :            :       {
     706                 :      34665 :         return true;
     707                 :            :       }
     708                 :            :     }
     709                 :            :   }
     710                 :       1217 :   return false;
     711                 :            : }
     712                 :            : 
     713                 :         79 : bool Cegis::sampleAddRefinementLemma(const std::vector<Node>& candidates,
     714                 :            :                                      const std::vector<Node>& vals)
     715                 :            : {
     716         [ +  - ]:         79 :   Trace("sygus-engine") << "  *** Do sample add refinement..." << std::endl;
     717         [ -  + ]:         79 :   if (TraceIsOn("cegis-sample"))
     718                 :            :   {
     719         [ -  - ]:          0 :     Trace("cegis-sample") << "Check sampling for candidate solution"
     720                 :          0 :                           << std::endl;
     721         [ -  - ]:          0 :     for (unsigned i = 0, size = vals.size(); i < size; i++)
     722                 :            :     {
     723         [ -  - ]:          0 :       Trace("cegis-sample") << "  " << candidates[i] << " -> " << vals[i]
     724                 :          0 :                             << std::endl;
     725                 :            :     }
     726                 :            :   }
     727 [ -  + ][ -  + ]:         79 :   Assert(vals.size() == candidates.size());
                 [ -  - ]
     728                 :            :   Node sbody = d_base_body.substitute(
     729                 :        158 :       candidates.begin(), candidates.end(), vals.begin(), vals.end());
     730         [ +  - ]:         79 :   Trace("cegis-sample-debug2") << "Sample " << sbody << std::endl;
     731                 :            :   // do eager rewriting
     732                 :         79 :   sbody = rewrite(sbody);
     733         [ +  - ]:         79 :   Trace("cegis-sample") << "Sample (after rewriting): " << sbody << std::endl;
     734                 :            : 
     735                 :         79 :   NodeManager* nm = nodeManager();
     736         [ +  + ]:      68746 :   for (size_t i = 0, size = d_cegis_sampler.getNumSamplePoints(); i < size; i++)
     737                 :            :   {
     738         [ +  + ]:      68677 :     if (d_cegis_sample_refine.find(i) == d_cegis_sample_refine.end())
     739                 :            :     {
     740                 :      68667 :       Node ev = d_cegis_sampler.evaluate(sbody, i);
     741         [ +  - ]:     137334 :       Trace("cegis-sample-debug") << "...evaluate point #" << i << " to " << ev
     742                 :      68667 :                                   << std::endl;
     743 [ -  + ][ -  + ]:      68667 :       Assert(ev.getType().isBoolean());
                 [ -  - ]
     744                 :            :       // if it evaluates to false
     745 [ +  + ][ +  + ]:      68667 :       if (ev.isConst() && !ev.getConst<bool>())
                 [ +  + ]
     746                 :            :       {
     747         [ +  - ]:         10 :         Trace("cegis-sample-debug") << "...false for point #" << i << std::endl;
     748                 :            :         // mark this as a CEGIS point (no longer sampled)
     749                 :         10 :         d_cegis_sample_refine.insert(i);
     750                 :         10 :         const std::vector<Node>& pt = d_cegis_sampler.getSamplePoint(i);
     751 [ -  + ][ -  + ]:         10 :         Assert(d_base_vars.size() == pt.size());
                 [ -  - ]
     752                 :            :         Node rlem = d_base_body.substitute(
     753                 :         10 :             d_base_vars.begin(), d_base_vars.end(), pt.begin(), pt.end());
     754                 :         10 :         rlem = rewrite(rlem);
     755                 :         10 :         if (std::find(
     756                 :         10 :                 d_refinement_lemmas.begin(), d_refinement_lemmas.end(), rlem)
     757         [ +  - ]:         20 :             == d_refinement_lemmas.end())
     758                 :            :         {
     759         [ -  + ]:         10 :           if (TraceIsOn("cegis-sample"))
     760                 :            :           {
     761         [ -  - ]:          0 :             Trace("cegis-sample") << "   false for point #" << i << " : ";
     762         [ -  - ]:          0 :             for (const Node& cn : pt)
     763                 :            :             {
     764         [ -  - ]:          0 :               Trace("cegis-sample") << cn << " ";
     765                 :            :             }
     766         [ -  - ]:          0 :             Trace("cegis-sample") << std::endl;
     767                 :            :           }
     768         [ +  - ]:         10 :           Trace("sygus-engine") << "  *** Refine by sampling" << std::endl;
     769                 :         10 :           addRefinementLemma(rlem);
     770                 :            :           // if trust, we are not interested in sending out refinement lemmas
     771                 :         10 :           if (options().quantifiers.cegisSample
     772         [ +  + ]:         10 :               != options::CegisSampleMode::TRUST)
     773                 :            :           {
     774                 :            :             Node lem =
     775                 :          8 :                 nm->mkNode(Kind::OR, d_parent->getConjecture().negate(), rlem);
     776                 :          4 :             d_qim.addPendingLemma(
     777                 :            :                 lem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE_SAMPLE);
     778                 :            :           }
     779                 :         10 :           return true;
     780                 :            :         }
     781                 :            :         else
     782                 :            :         {
     783         [ -  - ]:          0 :           Trace("cegis-sample-debug") << "...duplicate." << std::endl;
     784                 :            :         }
     785                 :            :       }
     786                 :            :     }
     787                 :            :   }
     788                 :         69 :   return false;
     789                 :            : }
     790                 :            : 
     791                 :            : }  // namespace quantifiers
     792                 :            : }  // namespace theory
     793                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14