LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers/sygus - sygus_pbe.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 134 142 94.4 %
Date: 2026-10-06 10:35:57 Functions: 7 7 100.0 %
Branches: 83 136 61.0 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Utility for processing programming by examples synthesis conjectures.
      11                 :            :  */
      12                 :            : #include "theory/quantifiers/sygus/sygus_pbe.h"
      13                 :            : 
      14                 :            : #include "options/quantifiers_options.h"
      15                 :            : #include "theory/datatypes/sygus_datatype_utils.h"
      16                 :            : #include "theory/quantifiers/sygus/example_infer.h"
      17                 :            : #include "theory/quantifiers/sygus/sygus_unif_io.h"
      18                 :            : #include "theory/quantifiers/sygus/synth_conjecture.h"
      19                 :            : #include "theory/quantifiers/sygus/term_database_sygus.h"
      20                 :            : #include "theory/quantifiers/term_util.h"
      21                 :            : #include "util/random.h"
      22                 :            : 
      23                 :            : using namespace cvc5::internal;
      24                 :            : using namespace cvc5::internal::kind;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace theory {
      28                 :            : namespace quantifiers {
      29                 :            : 
      30                 :       3755 : SygusPbe::SygusPbe(Env& env,
      31                 :            :                    QuantifiersState& qs,
      32                 :            :                    QuantifiersInferenceManager& qim,
      33                 :            :                    TermDbSygus* tds,
      34                 :       3755 :                    SynthConjecture* p)
      35                 :       3755 :     : SygusModule(env, qs, qim, tds, p)
      36                 :            : {
      37                 :       3755 :   d_true = nodeManager()->mkConst(true);
      38                 :       3755 :   d_false = nodeManager()->mkConst(false);
      39                 :       3755 :   d_is_pbe = false;
      40                 :       3755 : }
      41                 :            : 
      42                 :       7496 : SygusPbe::~SygusPbe() {}
      43                 :            : 
      44                 :        485 : bool SygusPbe::initialize(CVC5_UNUSED Node conj,
      45                 :            :                           Node n,
      46                 :            :                           const std::vector<Node>& candidates)
      47                 :            : {
      48         [ +  - ]:        485 :   Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl;
      49                 :        485 :   NodeManager* nm = nodeManager();
      50                 :            : 
      51         [ +  + ]:        485 :   if (!options().quantifiers.sygusUnifPbe)
      52                 :            :   {
      53                 :            :     // we are not doing unification
      54                 :        129 :     return false;
      55                 :            :   }
      56                 :            : 
      57                 :            :   // PBE does not repair symbolic any-constant constructors in candidate
      58                 :            :   // solutions. Let CEGIS handle these grammars so SygusRepairConst can repair
      59                 :            :   // the concrete values for any-constant holes.
      60         [ +  + ]:        782 :   for (const Node& c : candidates)
      61                 :            :   {
      62                 :        454 :     TypeNode tn = c.getType();
      63                 :        454 :     d_tds->registerSygusType(tn);
      64         [ +  + ]:        454 :     if (d_tds->getTypeInfo(tn).hasSubtermSymbolicCons())
      65                 :            :     {
      66                 :         28 :       return false;
      67                 :            :     }
      68         [ +  + ]:        454 :   }
      69                 :            : 
      70                 :            :   // check if all candidates are valid examples
      71                 :        328 :   ExampleInfer* ei = d_parent->getExampleInfer();
      72                 :        328 :   d_is_pbe = true;
      73         [ +  + ]:        415 :   for (const Node& c : candidates)
      74                 :            :   {
      75                 :            :     // if it has no examples or the output of the examples is invalid
      76                 :        334 :     if (ei->getNumExamples(c) == 0 || !ei->hasExamplesOut(c))
      77                 :            :     {
      78                 :        247 :       d_is_pbe = false;
      79                 :        247 :       return false;
      80                 :            :     }
      81                 :            :   }
      82         [ +  + ]:        164 :   for (const Node& c : candidates)
      83                 :            :   {
      84 [ -  + ][ -  + ]:         83 :     Assert(ei->hasExamples(c));
                 [ -  - ]
      85                 :         83 :     d_sygus_unif[c].reset(new SygusUnifIo(d_env, d_parent));
      86         [ +  - ]:        166 :     Trace("sygus-pbe") << "Initialize unif utility for " << c << "..."
      87                 :         83 :                        << std::endl;
      88                 :         83 :     std::map<Node, std::vector<Node>> strategy_lemmas;
      89                 :        166 :     d_sygus_unif[c]->initializeCandidate(
      90                 :         83 :         d_tds, c, d_candidate_to_enum[c], strategy_lemmas);
      91 [ -  + ][ -  + ]:         83 :     Assert(!d_candidate_to_enum[c].empty());
                 [ -  - ]
      92         [ +  - ]:        166 :     Trace("sygus-pbe") << "Initialize " << d_candidate_to_enum[c].size()
      93                 :         83 :                        << " enumerators for " << c << "..." << std::endl;
      94                 :            :     // collect list per type of strategy points with strategy lemmas
      95                 :         83 :     std::map<TypeNode, std::vector<Node>> tn_to_strategy_pt;
      96         [ +  + ]:        179 :     for (const std::pair<const Node, std::vector<Node>>& p : strategy_lemmas)
      97                 :            :     {
      98                 :         96 :       TypeNode tnsp = p.first.getType();
      99                 :         96 :       tn_to_strategy_pt[tnsp].push_back(p.first);
     100                 :         96 :     }
     101                 :            :     // initialize the enumerators
     102         [ +  + ]:        202 :     for (const Node& e : d_candidate_to_enum[c])
     103                 :            :     {
     104                 :        119 :       TypeNode etn = e.getType();
     105                 :        119 :       d_tds->registerEnumerator(e, c, d_parent, ROLE_ENUM_POOL);
     106                 :        119 :       d_enum_to_candidate[e] = c;
     107                 :        119 :       TNode te = e;
     108                 :            :       // initialize static symmetry breaking lemmas for it
     109                 :            :       // we register only one "master" enumerator per type
     110                 :            :       // thus, the strategy lemmas (which are for individual strategy points)
     111                 :            :       // are applicable (disjunctively) to the master enumerator
     112                 :            :       std::map<TypeNode, std::vector<Node>>::iterator itt =
     113                 :        119 :           tn_to_strategy_pt.find(etn);
     114         [ +  + ]:        119 :       if (itt != tn_to_strategy_pt.end())
     115                 :            :       {
     116                 :         64 :         std::vector<Node> disj;
     117         [ +  + ]:        156 :         for (const Node& sp : itt->second)
     118                 :            :         {
     119                 :            :           std::map<Node, std::vector<Node>>::iterator itsl =
     120                 :         92 :               strategy_lemmas.find(sp);
     121 [ -  + ][ -  + ]:         92 :           Assert(itsl != strategy_lemmas.end());
                 [ -  - ]
     122         [ +  - ]:         92 :           if (!itsl->second.empty())
     123                 :            :           {
     124                 :         92 :             TNode tsp = sp;
     125                 :         92 :             Node lem = itsl->second.size() == 1
     126                 :         78 :                            ? itsl->second[0]
     127         [ +  + ]:        170 :                            : nm->mkNode(Kind::AND, itsl->second);
     128         [ +  + ]:         92 :             if (tsp != te)
     129                 :            :             {
     130                 :         28 :               lem = lem.substitute(tsp, te);
     131                 :            :             }
     132         [ +  + ]:         92 :             if (std::find(disj.begin(), disj.end(), lem) == disj.end())
     133                 :            :             {
     134                 :         68 :               disj.push_back(lem);
     135                 :            :             }
     136                 :         92 :           }
     137                 :            :         }
     138                 :            :         // add its active guard
     139                 :         64 :         Node ag = d_tds->getActiveGuardForEnumerator(e);
     140 [ -  + ][ -  + ]:         64 :         Assert(!ag.isNull());
                 [ -  - ]
     141                 :         64 :         disj.push_back(ag.negate());
     142         [ -  + ]:         64 :         Node lem = disj.size() == 1 ? disj[0] : nm->mkNode(Kind::OR, disj);
     143                 :            :         // Apply extended rewriting on the lemma. This helps utilities like
     144                 :            :         // SygusEnumerator more easily recognize the shape of this lemma, e.g.
     145                 :            :         // ( ~is-ite(x) or ( ~is-ite(x) ^ P ) ) --> ~is-ite(x).
     146                 :         64 :         lem = extendedRewrite(lem);
     147         [ +  - ]:        128 :         Trace("sygus-pbe") << "  static redundant op lemma : " << lem
     148                 :         64 :                            << std::endl;
     149                 :            :         // Register as a symmetry breaking lemma with the term database.
     150                 :            :         // This will either be processed via a lemma on the output channel
     151                 :            :         // of the sygus extension of the datatypes solver, or internally
     152                 :            :         // encoded as a constraint to an active enumerator.
     153                 :         64 :         d_tds->registerSymBreakLemma(e, lem, etn, 0, false);
     154                 :         64 :       }
     155                 :        119 :     }
     156                 :         83 :   }
     157                 :         81 :   return true;
     158                 :            : }
     159                 :            : 
     160                 :            : // ------------------------------------------- solution construction from
     161                 :            : // enumeration
     162                 :            : 
     163                 :       9992 : void SygusPbe::getTermList(const std::vector<Node>& candidates,
     164                 :            :                            std::vector<Node>& terms)
     165                 :            : {
     166         [ +  + ]:      20066 :   for (unsigned i = 0; i < candidates.size(); i++)
     167                 :            :   {
     168                 :      10074 :     Node v = candidates[i];
     169                 :            :     std::map<Node, std::vector<Node>>::iterator it =
     170                 :      10074 :         d_candidate_to_enum.find(v);
     171         [ +  - ]:      10074 :     if (it != d_candidate_to_enum.end())
     172                 :            :     {
     173                 :      10074 :       terms.insert(terms.end(), it->second.begin(), it->second.end());
     174                 :            :     }
     175                 :      10074 :   }
     176                 :       9992 : }
     177                 :            : 
     178                 :       9992 : bool SygusPbe::allowPartialModel()
     179                 :            : {
     180                 :       9992 :   return !options().quantifiers.sygusPbeMultiFair;
     181                 :            : }
     182                 :            : 
     183                 :        761 : bool SygusPbe::constructCandidates(const std::vector<Node>& enums,
     184                 :            :                                    const std::vector<Node>& enum_values,
     185                 :            :                                    const std::vector<Node>& candidates,
     186                 :            :                                    std::vector<Node>& candidate_values)
     187                 :            : {
     188 [ -  + ][ -  + ]:        761 :   Assert(enums.size() == enum_values.size());
                 [ -  - ]
     189         [ +  - ]:        761 :   if (!enums.empty())
     190                 :            :   {
     191                 :        761 :     unsigned min_term_size = 0;
     192         [ +  - ]:        761 :     Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl;
     193                 :        761 :     std::vector<unsigned> szs;
     194         [ +  + ]:       1924 :     for (unsigned i = 0, esize = enums.size(); i < esize; i++)
     195                 :            :     {
     196         [ +  - ]:       1163 :       Trace("sygus-pbe-enum") << "  " << enums[i] << " -> ";
     197                 :       1163 :       TermDbSygus::toStreamSygus("sygus-pbe-enum", enum_values[i]);
     198         [ +  - ]:       1163 :       Trace("sygus-pbe-enum") << std::endl;
     199         [ +  + ]:       1163 :       if (!enum_values[i].isNull())
     200                 :            :       {
     201                 :        851 :         unsigned sz = datatypes::utils::getSygusTermSize(enum_values[i]);
     202                 :        851 :         szs.push_back(sz);
     203 [ +  + ][ -  + ]:        851 :         if (i == 0 || sz < min_term_size)
     204                 :            :         {
     205                 :        681 :           min_term_size = sz;
     206                 :            :         }
     207                 :            :       }
     208                 :            :       else
     209                 :            :       {
     210                 :        312 :         szs.push_back(0);
     211                 :            :       }
     212                 :            :     }
     213                 :            :     // Assume two enumerators of types T1 and T2.
     214                 :            :     // If the sygusPbeMultiFair option is true,
     215                 :            :     // we ensure that all values of type T1 and size n are enumerated before
     216                 :            :     // any term of type T2 of size n+d, and vice versa, where d is
     217                 :            :     // set by the sygusPbeMultiFairDiff option. If d is zero, then our
     218                 :            :     // enumeration is such that all terms of T1 or T2 of size n are considered
     219                 :            :     // before any term of size n+1.
     220                 :        761 :     int diffAllow = options().quantifiers.sygusPbeMultiFairDiff;
     221                 :        761 :     std::vector<unsigned> enum_consider;
     222         [ +  + ]:       1924 :     for (unsigned i = 0, esize = enums.size(); i < esize; i++)
     223                 :            :     {
     224         [ +  + ]:       1163 :       if (!enum_values[i].isNull())
     225                 :            :       {
     226 [ -  + ][ -  + ]:        851 :         Assert(szs[i] >= min_term_size);
                 [ -  - ]
     227                 :        851 :         int diff = szs[i] - min_term_size;
     228 [ -  + ][ -  - ]:        851 :         if (!options().quantifiers.sygusPbeMultiFair || diff <= diffAllow)
                 [ +  - ]
     229                 :            :         {
     230                 :        851 :           enum_consider.push_back(i);
     231                 :            :         }
     232                 :            :       }
     233                 :            :     }
     234                 :            : 
     235                 :            :     // only consider the enumerators that are at minimum size (for fairness)
     236         [ +  - ]:       1522 :     Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / "
     237                 :        761 :                             << enums.size() << std::endl;
     238                 :        761 :     NodeManager* nm = nodeManager();
     239         [ +  + ]:       1612 :     for (unsigned i = 0, ecsize = enum_consider.size(); i < ecsize; i++)
     240                 :            :     {
     241                 :        851 :       unsigned j = enum_consider[i];
     242                 :        851 :       Node e = enums[j];
     243                 :        851 :       Node v = enum_values[j];
     244 [ -  + ][ -  + ]:        851 :       Assert(d_enum_to_candidate.find(e) != d_enum_to_candidate.end());
                 [ -  - ]
     245                 :        851 :       Node c = d_enum_to_candidate[e];
     246                 :        851 :       std::vector<Node> enum_lems;
     247                 :        851 :       d_sygus_unif[c]->notifyEnumeration(e, v, enum_lems);
     248         [ -  + ]:        851 :       if (!enum_lems.empty())
     249                 :            :       {
     250                 :            :         // the lemmas must be guarded by the active guard of the enumerator
     251                 :          0 :         Node g = d_tds->getActiveGuardForEnumerator(e);
     252                 :          0 :         Assert(!g.isNull());
     253         [ -  - ]:          0 :         for (unsigned k = 0, size = enum_lems.size(); k < size; k++)
     254                 :            :         {
     255                 :          0 :           Node lem = nm->mkNode(Kind::OR, g.negate(), enum_lems[k]);
     256                 :          0 :           d_qim.addPendingLemma(lem,
     257                 :            :                                 InferenceId::QUANTIFIERS_SYGUS_PBE_EXCLUDE);
     258                 :          0 :         }
     259                 :          0 :       }
     260                 :        851 :     }
     261                 :        761 :   }
     262         [ +  + ]:        844 :   for (unsigned i = 0; i < candidates.size(); i++)
     263                 :            :   {
     264                 :        763 :     Node c = candidates[i];
     265                 :            :     // build decision tree for candidate
     266                 :        763 :     std::vector<Node> sol;
     267                 :        763 :     std::vector<Node> lems;
     268                 :        763 :     bool solSuccess = d_sygus_unif[c]->constructSolution(sol, lems);
     269         [ -  + ]:        763 :     for (const Node& lem : lems)
     270                 :            :     {
     271                 :          0 :       d_qim.addPendingLemma(lem,
     272                 :            :                             InferenceId::QUANTIFIERS_SYGUS_PBE_CONSTRUCT_SOL);
     273                 :            :     }
     274         [ +  + ]:        763 :     if (solSuccess)
     275                 :            :     {
     276 [ -  + ][ -  + ]:         83 :       Assert(sol.size() == 1);
                 [ -  - ]
     277                 :         83 :       candidate_values.push_back(sol[0]);
     278                 :            :     }
     279                 :            :     else
     280                 :            :     {
     281                 :        680 :       return false;
     282                 :            :     }
     283 [ +  + ][ +  + ]:       2123 :   }
                 [ +  + ]
     284                 :         81 :   return true;
     285                 :            : }
     286                 :            : 
     287                 :            : }  // namespace quantifiers
     288                 :            : }  // namespace theory
     289                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14