1 /*********************                                                        */
2 /*! \file synth_conjecture.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Andrew Reynolds, Haniel Barbosa, Tim King
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 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.\endverbatim
11  **
12  ** \brief Implementation of class that encapsulates techniques for a single
13  ** (SyGuS) synthesis conjecture.
14  **/
15 #include "theory/quantifiers/sygus/synth_conjecture.h"
16 
17 #include "expr/datatype.h"
18 #include "options/base_options.h"
19 #include "options/datatypes_options.h"
20 #include "options/quantifiers_options.h"
21 #include "printer/printer.h"
22 #include "prop/prop_engine.h"
23 #include "smt/smt_engine.h"
24 #include "smt/smt_engine_scope.h"
25 #include "smt/smt_statistics_registry.h"
26 #include "theory/datatypes/datatypes_rewriter.h"
27 #include "theory/quantifiers/first_order_model.h"
28 #include "theory/quantifiers/instantiate.h"
29 #include "theory/quantifiers/quantifiers_attributes.h"
30 #include "theory/quantifiers/sygus/enum_stream_substitution.h"
31 #include "theory/quantifiers/sygus/sygus_enumerator.h"
32 #include "theory/quantifiers/sygus/synth_engine.h"
33 #include "theory/quantifiers/sygus/term_database_sygus.h"
34 #include "theory/quantifiers/term_util.h"
35 #include "theory/theory_engine.h"
36 
37 using namespace CVC4::kind;
38 using namespace std;
39 
40 namespace CVC4 {
41 namespace theory {
42 namespace quantifiers {
43 
SynthConjecture(QuantifiersEngine * qe)44 SynthConjecture::SynthConjecture(QuantifiersEngine* qe)
45     : d_qe(qe),
46       d_tds(qe->getTermDatabaseSygus()),
47       d_ceg_si(new CegSingleInv(qe, this)),
48       d_ceg_proc(new SynthConjectureProcess(qe)),
49       d_ceg_gc(new CegGrammarConstructor(qe, this)),
50       d_sygus_rconst(new SygusRepairConst(qe)),
51       d_ceg_pbe(new SygusPbe(qe, this)),
52       d_ceg_cegis(new Cegis(qe, this)),
53       d_ceg_cegisUnif(new CegisUnif(qe, this)),
54       d_master(nullptr),
55       d_set_ce_sk_vars(false),
56       d_repair_index(0),
57       d_refine_count(0),
58       d_guarded_stream_exc(false)
59 {
60   if (options::sygusSymBreakPbe() || options::sygusUnifPbe())
61   {
62     d_modules.push_back(d_ceg_pbe.get());
63   }
64   if (options::sygusUnif())
65   {
66     d_modules.push_back(d_ceg_cegisUnif.get());
67   }
68   d_modules.push_back(d_ceg_cegis.get());
69 }
70 
~SynthConjecture()71 SynthConjecture::~SynthConjecture() {}
72 
assign(Node q)73 void SynthConjecture::assign(Node q)
74 {
75   Assert(d_embed_quant.isNull());
76   Assert(q.getKind() == FORALL);
77   Trace("cegqi") << "SynthConjecture : assign : " << q << std::endl;
78   d_quant = q;
79   NodeManager* nm = NodeManager::currentNM();
80 
81   // initialize the guard
82   d_feasible_guard = nm->mkSkolem("G", nm->booleanType());
83   d_feasible_guard = Rewriter::rewrite(d_feasible_guard);
84   d_feasible_guard = d_qe->getValuation().ensureLiteral(d_feasible_guard);
85   AlwaysAssert(!d_feasible_guard.isNull());
86 
87   // pre-simplify the quantified formula based on the process utility
88   d_simp_quant = d_ceg_proc->preSimplify(d_quant);
89 
90   // compute its attributes
91   QAttributes qa;
92   QuantAttributes::computeQuantAttributes(q, qa);
93 
94   std::map<Node, Node> templates;
95   std::map<Node, Node> templates_arg;
96   // register with single invocation if applicable
97   if (qa.d_sygus)
98   {
99     d_ceg_si->initialize(d_simp_quant);
100     d_simp_quant = d_ceg_si->getSimplifiedConjecture();
101     // carry the templates
102     for (unsigned i = 0; i < q[0].getNumChildren(); i++)
103     {
104       Node v = q[0][i];
105       Node templ = d_ceg_si->getTemplate(v);
106       if (!templ.isNull())
107       {
108         templates[v] = templ;
109         templates_arg[v] = d_ceg_si->getTemplateArg(v);
110       }
111     }
112   }
113 
114   // post-simplify the quantified formula based on the process utility
115   d_simp_quant = d_ceg_proc->postSimplify(d_simp_quant);
116 
117   // finished simplifying the quantified formula at this point
118 
119   // convert to deep embedding and finalize single invocation here
120   d_embed_quant = d_ceg_gc->process(d_simp_quant, templates, templates_arg);
121   Trace("cegqi") << "SynthConjecture : converted to embedding : "
122                  << d_embed_quant << std::endl;
123 
124   Node sc = qa.d_sygusSideCondition;
125   if (!sc.isNull())
126   {
127     // convert to deep embedding
128     d_embedSideCondition = d_ceg_gc->convertToEmbedding(sc);
129     Trace("cegqi") << "SynthConjecture : side condition : "
130                    << d_embedSideCondition << std::endl;
131   }
132 
133   // we now finalize the single invocation module, based on the syntax
134   // restrictions
135   if (qa.d_sygus)
136   {
137     d_ceg_si->finishInit(d_ceg_gc->isSyntaxRestricted());
138   }
139 
140   Assert(d_candidates.empty());
141   std::vector<Node> vars;
142   for (unsigned i = 0; i < d_embed_quant[0].getNumChildren(); i++)
143   {
144     vars.push_back(d_embed_quant[0][i]);
145     Node e =
146         NodeManager::currentNM()->mkSkolem("e", d_embed_quant[0][i].getType());
147     d_candidates.push_back(e);
148   }
149   Trace("cegqi") << "Base quantified formula is : " << d_embed_quant
150                  << std::endl;
151   // construct base instantiation
152   d_base_inst = Rewriter::rewrite(d_qe->getInstantiate()->getInstantiation(
153       d_embed_quant, vars, d_candidates));
154   if (!d_embedSideCondition.isNull())
155   {
156     d_embedSideCondition = d_embedSideCondition.substitute(
157         vars.begin(), vars.end(), d_candidates.begin(), d_candidates.end());
158   }
159   Trace("cegqi") << "Base instantiation is :      " << d_base_inst << std::endl;
160 
161   // initialize the sygus constant repair utility
162   if (options::sygusRepairConst())
163   {
164     d_sygus_rconst->initialize(d_base_inst.negate(), d_candidates);
165     if (options::sygusConstRepairAbort())
166     {
167       if (!d_sygus_rconst->isActive())
168       {
169         // no constant repair is possible: abort
170         std::stringstream ss;
171         ss << "Grammar does not allow repair constants." << std::endl;
172         throw LogicException(ss.str());
173       }
174     }
175   }
176 
177   // register this term with sygus database and other utilities that impact
178   // the enumerative sygus search
179   std::vector<Node> guarded_lemmas;
180   if (!isSingleInvocation())
181   {
182     d_ceg_proc->initialize(d_base_inst, d_candidates);
183     for (unsigned i = 0, size = d_modules.size(); i < size; i++)
184     {
185       if (d_modules[i]->initialize(d_base_inst, d_candidates, guarded_lemmas))
186       {
187         d_master = d_modules[i];
188         break;
189       }
190     }
191     Assert(d_master != nullptr);
192   }
193 
194   Assert(d_qe->getQuantAttributes()->isSygus(q));
195   // if the base instantiation is an existential, store its variables
196   if (d_base_inst.getKind() == NOT && d_base_inst[0].getKind() == FORALL)
197   {
198     for (const Node& v : d_base_inst[0][0])
199     {
200       d_inner_vars.push_back(v);
201     }
202   }
203 
204   // register the strategy
205   d_feasible_strategy.reset(
206       new DecisionStrategySingleton("sygus_feasible",
207                                     d_feasible_guard,
208                                     d_qe->getSatContext(),
209                                     d_qe->getValuation()));
210   d_qe->getTheoryEngine()->getDecisionManager()->registerStrategy(
211       DecisionManager::STRAT_QUANT_SYGUS_FEASIBLE, d_feasible_strategy.get());
212   // this must be called, both to ensure that the feasible guard is
213   // decided on with true polariy, but also to ensure that output channel
214   // has been used on this call to check.
215   d_qe->getOutputChannel().requirePhase(d_feasible_guard, true);
216 
217   if (isSingleInvocation())
218   {
219     std::vector<Node> lems;
220     d_ceg_si->getInitialSingleInvLemma(d_feasible_guard, lems);
221     for (unsigned i = 0; i < lems.size(); i++)
222     {
223       Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation " << i << " : "
224                            << lems[i] << std::endl;
225       d_qe->getOutputChannel().lemma(lems[i]);
226       if (Trace.isOn("cegqi-debug"))
227       {
228         Node rlem = Rewriter::rewrite(lems[i]);
229         Trace("cegqi-debug") << "...rewritten : " << rlem << std::endl;
230       }
231     }
232   }
233   Node gneg = d_feasible_guard.negate();
234   for (unsigned i = 0; i < guarded_lemmas.size(); i++)
235   {
236     Node lem = nm->mkNode(OR, gneg, guarded_lemmas[i]);
237     Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem
238                          << std::endl;
239     d_qe->getOutputChannel().lemma(lem);
240   }
241 
242   if (options::sygusStream())
243   {
244     d_stream_strategy.reset(new SygusStreamDecisionStrategy(
245         d_qe->getSatContext(), d_qe->getValuation()));
246     d_qe->getTheoryEngine()->getDecisionManager()->registerStrategy(
247         DecisionManager::STRAT_QUANT_SYGUS_STREAM_FEASIBLE,
248         d_stream_strategy.get());
249     d_current_stream_guard = d_stream_strategy->getLiteral(0);
250   }
251   Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation()
252                  << std::endl;
253 }
254 
getGuard() const255 Node SynthConjecture::getGuard() const { return d_feasible_guard; }
256 
isSingleInvocation() const257 bool SynthConjecture::isSingleInvocation() const
258 {
259   return d_ceg_si->isSingleInvocation();
260 }
261 
needsCheck()262 bool SynthConjecture::needsCheck()
263 {
264   bool value;
265   Assert(!d_feasible_guard.isNull());
266   // non or fully single invocation : look at guard only
267   if (d_qe->getValuation().hasSatValue(d_feasible_guard, value))
268   {
269     if (!value)
270     {
271       Trace("cegqi-engine-debug") << "Conjecture is infeasible." << std::endl;
272       return false;
273     }
274     else
275     {
276       Trace("cegqi-engine-debug") << "Feasible guard " << d_feasible_guard
277                                   << " assigned true." << std::endl;
278     }
279   }
280   else
281   {
282     Trace("cegqi-warn") << "WARNING: Guard " << d_feasible_guard
283                         << " is not assigned!" << std::endl;
284     Assert(false);
285   }
286   return true;
287 }
288 
doSingleInvCheck(std::vector<Node> & lems)289 void SynthConjecture::doSingleInvCheck(std::vector<Node>& lems)
290 {
291   if (d_ceg_si != NULL)
292   {
293     d_ceg_si->check(lems);
294   }
295 }
296 
needsRefinement() const297 bool SynthConjecture::needsRefinement() const { return d_set_ce_sk_vars; }
doCheck(std::vector<Node> & lems)298 bool SynthConjecture::doCheck(std::vector<Node>& lems)
299 {
300   Assert(d_master != nullptr);
301 
302   // process the sygus streaming guard
303   if (options::sygusStream())
304   {
305     Assert(!isSingleInvocation());
306     // it may be the case that we have a new solution now
307     Node currGuard = getCurrentStreamGuard();
308     if (currGuard != d_current_stream_guard)
309     {
310       // we have a new guard, print and continue the stream
311       printAndContinueStream();
312       d_current_stream_guard = currGuard;
313       return true;
314     }
315   }
316 
317   // get the list of terms that the master strategy is interested in
318   std::vector<Node> terms;
319   d_master->getTermList(d_candidates, terms);
320 
321   Assert(!d_candidates.empty());
322 
323   Trace("cegqi-check") << "CegConjuncture : check, build candidates..."
324                        << std::endl;
325   std::vector<Node> candidate_values;
326   bool constructed_cand = false;
327 
328   // If a module is not trying to repair constants in solutions and the option
329   // sygusRepairConst  is true, we use a default scheme for trying to repair
330   // constants here.
331   if (options::sygusRepairConst() && !d_master->usingRepairConst())
332   {
333     // have we tried to repair the previous solution?
334     // if not, call the repair constant utility
335     unsigned ninst = d_cinfo[d_candidates[0]].d_inst.size();
336     if (d_repair_index < ninst)
337     {
338       std::vector<Node> fail_cvs;
339       for (const Node& cprog : d_candidates)
340       {
341         Assert(d_repair_index < d_cinfo[cprog].d_inst.size());
342         fail_cvs.push_back(d_cinfo[cprog].d_inst[d_repair_index]);
343       }
344       if (Trace.isOn("cegqi-engine"))
345       {
346         Trace("cegqi-engine") << "CegConjuncture : repair previous solution ";
347         for (const Node& fc : fail_cvs)
348         {
349           std::stringstream ss;
350           Printer::getPrinter(options::outputLanguage())->toStreamSygus(ss, fc);
351           Trace("cegqi-engine") << ss.str() << " ";
352         }
353         Trace("cegqi-engine") << std::endl;
354       }
355       d_repair_index++;
356       if (d_sygus_rconst->repairSolution(
357               d_candidates, fail_cvs, candidate_values, true))
358       {
359         constructed_cand = true;
360       }
361     }
362   }
363 
364   if (!constructed_cand)
365   {
366     // get the model value of the relevant terms from the master module
367     std::vector<Node> enum_values;
368     bool activeIncomplete = false;
369     bool fullModel = getEnumeratedValues(terms, enum_values, activeIncomplete);
370 
371     // if the master requires a full model and the model is partial, we fail
372     if (!d_master->allowPartialModel() && !fullModel)
373     {
374       // we retain the values in d_ev_active_gen_waiting
375       Trace("cegqi-engine-debug") << "...partial model, fail." << std::endl;
376       // if we are partial due to an active enumerator, we may still succeed
377       // on the next call
378       return !activeIncomplete;
379     }
380     // the waiting values are passed to the module below, clear
381     d_ev_active_gen_waiting.clear();
382 
383     Assert(terms.size() == enum_values.size());
384     bool emptyModel = true;
385     for (unsigned i = 0, size = terms.size(); i < size; i++)
386     {
387       if (!enum_values[i].isNull())
388       {
389         emptyModel = false;
390       }
391     }
392     if (emptyModel)
393     {
394       Trace("cegqi-engine-debug") << "...empty model, fail." << std::endl;
395       return !activeIncomplete;
396     }
397     // debug print
398     if (Trace.isOn("cegqi-engine"))
399     {
400       Trace("cegqi-engine") << "  * Value is : ";
401       for (unsigned i = 0, size = terms.size(); i < size; i++)
402       {
403         Node nv = enum_values[i];
404         Node onv = nv.isNull() ? d_qe->getModel()->getValue(terms[i]) : nv;
405         TypeNode tn = onv.getType();
406         std::stringstream ss;
407         Printer::getPrinter(options::outputLanguage())->toStreamSygus(ss, onv);
408         Trace("cegqi-engine") << terms[i] << " -> ";
409         if (nv.isNull())
410         {
411           Trace("cegqi-engine") << "[EXC: " << ss.str() << "] ";
412         }
413         else
414         {
415           Trace("cegqi-engine") << ss.str() << " ";
416           if (Trace.isOn("cegqi-engine-rr"))
417           {
418             Node bv = d_tds->sygusToBuiltin(nv, tn);
419             bv = Rewriter::rewrite(bv);
420             Trace("cegqi-engine-rr") << " -> " << bv << std::endl;
421           }
422         }
423       }
424       Trace("cegqi-engine") << std::endl;
425     }
426     Assert(candidate_values.empty());
427     constructed_cand = d_master->constructCandidates(
428         terms, enum_values, d_candidates, candidate_values, lems);
429   }
430 
431   NodeManager* nm = NodeManager::currentNM();
432 
433   // must get a counterexample to the value of the current candidate
434   Node inst;
435   if (constructed_cand)
436   {
437     if (Trace.isOn("cegqi-check"))
438     {
439       Trace("cegqi-check") << "CegConjuncture : check candidate : "
440                            << std::endl;
441       for (unsigned i = 0, size = candidate_values.size(); i < size; i++)
442       {
443         Trace("cegqi-check") << "  " << i << " : " << d_candidates[i] << " -> "
444                              << candidate_values[i] << std::endl;
445       }
446     }
447     Assert(candidate_values.size() == d_candidates.size());
448     inst = d_base_inst.substitute(d_candidates.begin(),
449                                   d_candidates.end(),
450                                   candidate_values.begin(),
451                                   candidate_values.end());
452   }
453   else
454   {
455     inst = d_base_inst;
456   }
457 
458   // check whether we will run CEGIS on inner skolem variables
459   bool sk_refine = (!isGround() || d_refine_count == 0) && constructed_cand;
460   if (sk_refine)
461   {
462     if (options::cegisSample() == CEGIS_SAMPLE_TRUST)
463     {
464       // we have that the current candidate passed a sample test
465       // since we trust sampling in this mode, we assert there is no
466       // counterexample to the conjecture here.
467       Node lem = nm->mkNode(OR, d_quant.negate(), nm->mkConst(false));
468       lem = getStreamGuardedLemma(lem);
469       lems.push_back(lem);
470       recordInstantiation(candidate_values);
471       return true;
472     }
473     Assert(!d_set_ce_sk_vars);
474   }
475   else
476   {
477     if (!constructed_cand)
478     {
479       return false;
480     }
481   }
482 
483   // immediately skolemize inner existentials
484   Node lem;
485   // introduce the skolem variables
486   std::vector<Node> sks;
487   std::vector<Node> vars;
488   if (constructed_cand)
489   {
490     if (inst.getKind() == NOT && inst[0].getKind() == FORALL)
491     {
492       for (const Node& v : inst[0][0])
493       {
494         Node sk = nm->mkSkolem("rsk", v.getType());
495         sks.push_back(sk);
496         vars.push_back(v);
497         Trace("cegqi-check-debug")
498             << "  introduce skolem " << sk << " for " << v << "\n";
499       }
500       lem = inst[0][1].substitute(
501           vars.begin(), vars.end(), sks.begin(), sks.end());
502       lem = lem.negate();
503     }
504     else
505     {
506       // use the instance itself
507       lem = inst;
508     }
509   }
510   if (sk_refine)
511   {
512     d_ce_sk_vars.insert(d_ce_sk_vars.end(), sks.begin(), sks.end());
513     d_set_ce_sk_vars = true;
514   }
515 
516   if (lem.isNull())
517   {
518     // no lemma to check
519     return false;
520   }
521 
522   lem = Rewriter::rewrite(lem);
523   // eagerly unfold applications of evaluation function
524   Trace("cegqi-debug") << "pre-unfold counterexample : " << lem << std::endl;
525   std::map<Node, Node> visited_n;
526   lem = d_tds->getEagerUnfold(lem, visited_n);
527   // record the instantiation
528   // this is used for remembering the solution
529   recordInstantiation(candidate_values);
530 
531   // check the side condition
532   Node sc;
533   if (!d_embedSideCondition.isNull() && constructed_cand)
534   {
535     sc = d_embedSideCondition.substitute(d_candidates.begin(),
536                                          d_candidates.end(),
537                                          candidate_values.begin(),
538                                          candidate_values.end());
539     sc = Rewriter::rewrite(sc);
540     Trace("cegqi-engine") << "Check side condition..." << std::endl;
541     Trace("cegqi-debug") << "Check side condition : " << sc << std::endl;
542     SmtEngine scSmt(nm->toExprManager());
543     scSmt.setIsInternalSubsolver();
544     scSmt.setLogic(smt::currentSmtEngine()->getLogicInfo());
545     scSmt.assertFormula(sc.toExpr());
546     Result r = scSmt.checkSat();
547     Trace("cegqi-debug") << "...got side condition : " << r << std::endl;
548     if (r == Result::UNSAT)
549     {
550       // exclude the current solution TODO
551       excludeCurrentSolution();
552       Trace("cegqi-engine") << "...failed side condition" << std::endl;
553       return false;
554     }
555     Trace("cegqi-engine") << "...passed side condition" << std::endl;
556   }
557 
558   Node query = lem;
559   bool success = false;
560   if (query.isConst() && !query.getConst<bool>())
561   {
562     // short circuit the check
563     lem = d_quant.negate();
564     success = true;
565   }
566   else
567   {
568     // This is the "verification lemma", which states
569     // either this conjecture does not have a solution, or candidate_values
570     // is a solution for this conjecture.
571     lem = nm->mkNode(OR, d_quant.negate(), query);
572     if (options::sygusVerifySubcall())
573     {
574       Trace("cegqi-engine") << "  *** Verify with subcall..." << std::endl;
575       SmtEngine verifySmt(nm->toExprManager());
576       verifySmt.setIsInternalSubsolver();
577       verifySmt.setLogic(smt::currentSmtEngine()->getLogicInfo());
578       verifySmt.assertFormula(query.toExpr());
579       Result r = verifySmt.checkSat();
580       Trace("cegqi-engine") << "  ...got " << r << std::endl;
581       if (r.asSatisfiabilityResult().isSat() == Result::SAT)
582       {
583         Trace("cegqi-engine") << "  * Verification lemma failed for:\n   ";
584         // do not send out
585         for (unsigned i = 0, size = d_ce_sk_vars.size(); i < size; i++)
586         {
587           Node v = d_ce_sk_vars[i];
588           Node mv = Node::fromExpr(verifySmt.getValue(v.toExpr()));
589           Trace("cegqi-engine") << vars[i] << " -> " << mv << " ";
590           d_ce_sk_var_mvs.push_back(mv);
591         }
592         Trace("cegqi-engine") << std::endl;
593 #ifdef CVC4_ASSERTIONS
594         // the values for the query should be a complete model
595         Node squery = query.substitute(d_ce_sk_vars.begin(),
596                                        d_ce_sk_vars.end(),
597                                        d_ce_sk_var_mvs.begin(),
598                                        d_ce_sk_var_mvs.end());
599         Trace("cegqi-debug") << "...squery : " << squery << std::endl;
600         squery = Rewriter::rewrite(squery);
601         Trace("cegqi-debug") << "...rewrites to : " << squery << std::endl;
602         Assert(squery.isConst() && squery.getConst<bool>());
603 #endif
604         return false;
605       }
606       else if (r.asSatisfiabilityResult().isSat() == Result::UNSAT)
607       {
608         // if the result in the subcall was unsatisfiable, we avoid
609         // rechecking, hence we drop "query" from the verification lemma
610         lem = d_quant.negate();
611         // we can short circuit adding the lemma (for sygus stream)
612         success = true;
613       }
614       // In the rare case that the subcall is unknown, we add the verification
615       // lemma in the main solver. This should only happen if the quantifier
616       // free logic is undecidable.
617     }
618   }
619   if (success && options::sygusStream())
620   {
621     // if we were successful, we immediately print the current solution.
622     // this saves us from introducing a verification lemma and a new guard.
623     printAndContinueStream();
624     return false;
625   }
626   lem = getStreamGuardedLemma(lem);
627   lems.push_back(lem);
628   return true;
629 }
630 
doRefine(std::vector<Node> & lems)631 void SynthConjecture::doRefine(std::vector<Node>& lems)
632 {
633   Assert(lems.empty());
634   Assert(d_set_ce_sk_vars);
635 
636   // first, make skolem substitution
637   Trace("cegqi-refine") << "doRefine : construct skolem substitution..."
638                         << std::endl;
639   std::vector<Node> sk_vars;
640   std::vector<Node> sk_subs;
641   // collect the substitution over all disjuncts
642   if (!d_ce_sk_vars.empty())
643   {
644     Trace("cegqi-refine") << "Get model values for skolems..." << std::endl;
645     Assert(d_inner_vars.size() == d_ce_sk_vars.size());
646     if (d_ce_sk_var_mvs.empty())
647     {
648       std::vector<Node> model_values;
649       for (const Node& v : d_ce_sk_vars)
650       {
651         Node mv = getModelValue(v);
652         Trace("cegqi-refine") << "  " << v << " -> " << mv << std::endl;
653         model_values.push_back(mv);
654       }
655       sk_subs.insert(sk_subs.end(), model_values.begin(), model_values.end());
656     }
657     else
658     {
659       Assert(d_ce_sk_var_mvs.size() == d_ce_sk_vars.size());
660       sk_subs.insert(
661           sk_subs.end(), d_ce_sk_var_mvs.begin(), d_ce_sk_var_mvs.end());
662     }
663     sk_vars.insert(sk_vars.end(), d_inner_vars.begin(), d_inner_vars.end());
664   }
665   else
666   {
667     Assert(d_inner_vars.empty());
668   }
669 
670   std::vector<Node> lem_c;
671   Trace("cegqi-refine") << "doRefine : Construct refinement lemma..."
672                         << std::endl;
673   Trace("cegqi-refine-debug")
674       << "  For counterexample skolems : " << d_ce_sk_vars << std::endl;
675   Node base_lem;
676   if (d_base_inst.getKind() == NOT && d_base_inst[0].getKind() == FORALL)
677   {
678     base_lem = d_base_inst[0][1];
679   }
680   else
681   {
682     base_lem = d_base_inst.negate();
683   }
684 
685   Assert(sk_vars.size() == sk_subs.size());
686 
687   Trace("cegqi-refine") << "doRefine : substitute..." << std::endl;
688   base_lem = base_lem.substitute(
689       sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end());
690   Trace("cegqi-refine") << "doRefine : rewrite..." << std::endl;
691   base_lem = Rewriter::rewrite(base_lem);
692   Trace("cegqi-refine") << "doRefine : register refinement lemma " << base_lem
693                         << "..." << std::endl;
694   d_master->registerRefinementLemma(sk_vars, base_lem, lems);
695   Trace("cegqi-refine") << "doRefine : finished" << std::endl;
696   d_set_ce_sk_vars = false;
697   d_ce_sk_vars.clear();
698   d_ce_sk_var_mvs.clear();
699 }
700 
preregisterConjecture(Node q)701 void SynthConjecture::preregisterConjecture(Node q)
702 {
703   d_ceg_si->preregisterConjecture(q);
704 }
705 
getEnumeratedValues(std::vector<Node> & n,std::vector<Node> & v,bool & activeIncomplete)706 bool SynthConjecture::getEnumeratedValues(std::vector<Node>& n,
707                                           std::vector<Node>& v,
708                                           bool& activeIncomplete)
709 {
710   std::vector<Node> ncheck = n;
711   n.clear();
712   bool ret = true;
713   for (unsigned i = 0, size = ncheck.size(); i < size; i++)
714   {
715     Node e = ncheck[i];
716     // if it is not active, we return null
717     Node g = d_tds->getActiveGuardForEnumerator(e);
718     if (!g.isNull())
719     {
720       Node gstatus = d_qe->getValuation().getSatValue(g);
721       if (gstatus.isNull() || !gstatus.getConst<bool>())
722       {
723         Trace("cegqi-engine-debug")
724             << "Enumerator " << e << " is inactive." << std::endl;
725         continue;
726       }
727     }
728     Node nv = getEnumeratedValue(e, activeIncomplete);
729     n.push_back(e);
730     v.push_back(nv);
731     ret = ret && !nv.isNull();
732   }
733   return ret;
734 }
735 
736 /** A basic sygus value generator
737  *
738  * This class is a "naive" term generator for sygus conjectures, which invokes
739  * the type enumerator to generate a stream of (all) sygus terms of a given
740  * type.
741  */
742 class EnumValGeneratorBasic : public EnumValGenerator
743 {
744  public:
EnumValGeneratorBasic(TermDbSygus * tds,TypeNode tn)745   EnumValGeneratorBasic(TermDbSygus* tds, TypeNode tn) : d_tds(tds), d_te(tn) {}
~EnumValGeneratorBasic()746   ~EnumValGeneratorBasic() {}
747   /** initialize (do nothing) */
initialize(Node e)748   void initialize(Node e) override {}
749   /** initialize (do nothing) */
addValue(Node v)750   void addValue(Node v) override { d_currTerm = *d_te; }
751   /**
752    * Get next returns the next (T-rewriter-unique) value based on the type
753    * enumerator.
754    */
increment()755   bool increment() override
756   {
757     ++d_te;
758     if (d_te.isFinished())
759     {
760       d_currTerm = Node::null();
761       return false;
762     }
763     d_currTerm = *d_te;
764     if (options::sygusSymBreakDynamic())
765     {
766       Node nextb = d_tds->sygusToBuiltin(d_currTerm);
767       nextb = d_tds->getExtRewriter()->extendedRewrite(nextb);
768       if (d_cache.find(nextb) == d_cache.end())
769       {
770         d_cache.insert(nextb);
771         // only return the current term if not unique
772       }
773       else
774       {
775         d_currTerm = Node::null();
776       }
777     }
778     return true;
779   }
780   /** get the current term */
getCurrent()781   Node getCurrent() override { return d_currTerm; }
782  private:
783   /** pointer to term database sygus */
784   TermDbSygus* d_tds;
785   /** the type enumerator */
786   TypeEnumerator d_te;
787   /** the current term */
788   Node d_currTerm;
789   /** cache of (enumerated) builtin values we have enumerated so far */
790   std::unordered_set<Node, NodeHashFunction> d_cache;
791 };
792 
getEnumeratedValue(Node e,bool & activeIncomplete)793 Node SynthConjecture::getEnumeratedValue(Node e, bool& activeIncomplete)
794 {
795   bool isEnum = d_tds->isEnumerator(e);
796 
797   if (isEnum && !e.getAttribute(SygusSymBreakOkAttribute()))
798   {
799     // if the current model value of e was not registered by the datatypes
800     // sygus solver, or was excluded by symmetry breaking, then it does not
801     // have a proper model value that we should consider, thus we return null.
802     Trace("cegqi-engine-debug")
803         << "Enumerator " << e << " does not have proper model value."
804         << std::endl;
805     return Node::null();
806   }
807 
808   if (!isEnum || d_tds->isPassiveEnumerator(e))
809   {
810     return getModelValue(e);
811   }
812 
813   // management of actively generated enumerators goes here
814 
815   // initialize the enumerated value generator for e
816   std::map<Node, std::unique_ptr<EnumValGenerator> >::iterator iteg =
817       d_evg.find(e);
818   if (iteg == d_evg.end())
819   {
820     if (d_tds->isVariableAgnosticEnumerator(e))
821     {
822       d_evg[e].reset(new EnumStreamConcrete(d_tds));
823     }
824     else
825     {
826       // Actively-generated enumerators are currently either variable agnostic
827       // or basic. The auto mode always prefers the optimized enumerator over
828       // the basic one.
829       Assert(d_tds->isBasicEnumerator(e));
830       if (options::sygusActiveGenMode() == SYGUS_ACTIVE_GEN_ENUM_BASIC)
831       {
832         d_evg[e].reset(new EnumValGeneratorBasic(d_tds, e.getType()));
833       }
834       else
835       {
836         Assert(options::sygusActiveGenMode() == SYGUS_ACTIVE_GEN_ENUM
837                || options::sygusActiveGenMode() == SYGUS_ACTIVE_GEN_AUTO);
838         d_evg[e].reset(new SygusEnumerator(d_tds, this));
839       }
840     }
841     Trace("sygus-active-gen")
842         << "Active-gen: initialize for " << e << std::endl;
843     d_evg[e]->initialize(e);
844     d_ev_curr_active_gen[e] = Node::null();
845     iteg = d_evg.find(e);
846     Trace("sygus-active-gen-debug") << "...finish" << std::endl;
847   }
848   // if we have a waiting value, return it
849   std::map<Node, Node>::iterator itw = d_ev_active_gen_waiting.find(e);
850   if (itw != d_ev_active_gen_waiting.end())
851   {
852     Trace("sygus-active-gen-debug")
853         << "Active-gen: return waiting " << itw->second << std::endl;
854     return itw->second;
855   }
856   // Check if there is an (abstract) value absE we were actively generating
857   // values based on.
858   Node absE = d_ev_curr_active_gen[e];
859   bool firstTime = false;
860   if (absE.isNull())
861   {
862     // None currently exist. The next abstract value is the model value for e.
863     absE = getModelValue(e);
864     if (Trace.isOn("sygus-active-gen"))
865     {
866       Trace("sygus-active-gen") << "Active-gen: new abstract value : ";
867       TermDbSygus::toStreamSygus("sygus-active-gen", e);
868       Trace("sygus-active-gen") << " -> ";
869       TermDbSygus::toStreamSygus("sygus-active-gen", absE);
870       Trace("sygus-active-gen") << std::endl;
871     }
872     d_ev_curr_active_gen[e] = absE;
873     iteg->second->addValue(absE);
874     firstTime = true;
875   }
876   bool inc = true;
877   if (!firstTime)
878   {
879     inc = iteg->second->increment();
880   }
881   Node v;
882   if (inc)
883   {
884     v = iteg->second->getCurrent();
885   }
886   Trace("sygus-active-gen-debug") << "...generated " << v
887                                   << ", with increment success : " << inc
888                                   << std::endl;
889   if (!inc)
890   {
891     // No more concrete values generated from absE.
892     NodeManager* nm = NodeManager::currentNM();
893     d_ev_curr_active_gen[e] = Node::null();
894     std::vector<Node> exp;
895     // If we are a basic enumerator, a single abstract value maps to *all*
896     // concrete values of its type, thus we don't depend on the current
897     // solution.
898     if (!d_tds->isBasicEnumerator(e))
899     {
900       // We must block e = absE
901       d_tds->getExplain()->getExplanationForEquality(e, absE, exp);
902       for (unsigned i = 0, size = exp.size(); i < size; i++)
903       {
904         exp[i] = exp[i].negate();
905       }
906     }
907     Node g = d_tds->getActiveGuardForEnumerator(e);
908     if (!g.isNull())
909     {
910       if (d_ev_active_gen_first_val.find(e) == d_ev_active_gen_first_val.end())
911       {
912         exp.push_back(g.negate());
913         d_ev_active_gen_first_val[e] = absE;
914       }
915     }
916     else
917     {
918       Assert(false);
919     }
920     Node lem = exp.size() == 1 ? exp[0] : nm->mkNode(OR, exp);
921     Trace("cegqi-lemma") << "Cegqi::Lemma : actively-generated enumerator "
922                             "exclude current solution : "
923                          << lem << std::endl;
924     if (Trace.isOn("sygus-active-gen-debug"))
925     {
926       Trace("sygus-active-gen-debug") << "Active-gen: block ";
927       TermDbSygus::toStreamSygus("sygus-active-gen-debug", absE);
928       Trace("sygus-active-gen-debug") << std::endl;
929     }
930     d_qe->getOutputChannel().lemma(lem);
931   }
932   else
933   {
934     // We are waiting to send e -> v to the module that requested it.
935     if (v.isNull())
936     {
937       activeIncomplete = true;
938     }
939     else
940     {
941       d_ev_active_gen_waiting[e] = v;
942     }
943     if (Trace.isOn("sygus-active-gen"))
944     {
945       Trace("sygus-active-gen") << "Active-gen : " << e << " : ";
946       TermDbSygus::toStreamSygus("sygus-active-gen", absE);
947       Trace("sygus-active-gen") << " -> ";
948       TermDbSygus::toStreamSygus("sygus-active-gen", v);
949       Trace("sygus-active-gen") << std::endl;
950     }
951   }
952 
953   return v;
954 }
955 
getModelValue(Node n)956 Node SynthConjecture::getModelValue(Node n)
957 {
958   Trace("cegqi-mv") << "getModelValue for : " << n << std::endl;
959   return d_qe->getModel()->getValue(n);
960 }
961 
debugPrint(const char * c)962 void SynthConjecture::debugPrint(const char* c)
963 {
964   Trace(c) << "Synthesis conjecture : " << d_embed_quant << std::endl;
965   Trace(c) << "  * Candidate programs : " << d_candidates << std::endl;
966   Trace(c) << "  * Counterexample skolems : " << d_ce_sk_vars << std::endl;
967 }
968 
getCurrentStreamGuard() const969 Node SynthConjecture::getCurrentStreamGuard() const
970 {
971   if (d_stream_strategy != nullptr)
972   {
973     // the stream guard is the current asserted literal of the stream strategy
974     Node lit = d_stream_strategy->getAssertedLiteral();
975     if (lit.isNull())
976     {
977       // if none exist, get the first
978       lit = d_stream_strategy->getLiteral(0);
979     }
980     return lit;
981   }
982   return Node::null();
983 }
984 
getStreamGuardedLemma(Node n) const985 Node SynthConjecture::getStreamGuardedLemma(Node n) const
986 {
987   if (options::sygusStream())
988   {
989     // if we are in streaming mode, we guard with the current stream guard
990     Node csg = getCurrentStreamGuard();
991     Assert(!csg.isNull());
992     return NodeManager::currentNM()->mkNode(kind::OR, csg.negate(), n);
993   }
994   return n;
995 }
996 
SygusStreamDecisionStrategy(context::Context * satContext,Valuation valuation)997 SynthConjecture::SygusStreamDecisionStrategy::SygusStreamDecisionStrategy(
998     context::Context* satContext, Valuation valuation)
999     : DecisionStrategyFmf(satContext, valuation)
1000 {
1001 }
1002 
mkLiteral(unsigned i)1003 Node SynthConjecture::SygusStreamDecisionStrategy::mkLiteral(unsigned i)
1004 {
1005   NodeManager* nm = NodeManager::currentNM();
1006   Node curr_stream_guard = nm->mkSkolem("G_Stream", nm->booleanType());
1007   return curr_stream_guard;
1008 }
1009 
printAndContinueStream()1010 void SynthConjecture::printAndContinueStream()
1011 {
1012   Assert(d_master != nullptr);
1013   // we have generated a solution, print it
1014   // get the current output stream
1015   // this output stream should coincide with wherever --dump-synth is output on
1016   Options& nodeManagerOptions = NodeManager::currentNM()->getOptions();
1017   printSynthSolution(*nodeManagerOptions.getOut());
1018   excludeCurrentSolution();
1019 }
1020 
excludeCurrentSolution()1021 void SynthConjecture::excludeCurrentSolution()
1022 {
1023   // We will not refine the current candidate solution since it is a solution
1024   // thus, we clear information regarding the current refinement
1025   d_set_ce_sk_vars = false;
1026   d_ce_sk_vars.clear();
1027   d_ce_sk_var_mvs.clear();
1028   // However, we need to exclude the current solution using an explicit
1029   // blocking clause, so that we proceed to the next solution. We do this only
1030   // for passively-generated enumerators (TermDbSygus::isPassiveEnumerator).
1031   std::vector<Node> terms;
1032   d_master->getTermList(d_candidates, terms);
1033   std::vector<Node> exp;
1034   for (const Node& cprog : terms)
1035   {
1036     Assert(d_tds->isEnumerator(cprog));
1037     if (d_tds->isPassiveEnumerator(cprog))
1038     {
1039       Node sol = cprog;
1040       if (!d_cinfo[cprog].d_inst.empty())
1041       {
1042         sol = d_cinfo[cprog].d_inst.back();
1043         // add to explanation of exclusion
1044         d_tds->getExplain()->getExplanationForEquality(cprog, sol, exp);
1045       }
1046     }
1047   }
1048   if (!exp.empty())
1049   {
1050     if (!d_guarded_stream_exc)
1051     {
1052       d_guarded_stream_exc = true;
1053       exp.push_back(d_feasible_guard);
1054     }
1055     Node exc_lem = exp.size() == 1
1056                        ? exp[0]
1057                        : NodeManager::currentNM()->mkNode(kind::AND, exp);
1058     exc_lem = exc_lem.negate();
1059     Trace("cegqi-lemma") << "Cegqi::Lemma : stream exclude current solution : "
1060                          << exc_lem << std::endl;
1061     d_qe->getOutputChannel().lemma(exc_lem);
1062   }
1063 }
1064 
printSynthSolution(std::ostream & out)1065 void SynthConjecture::printSynthSolution(std::ostream& out)
1066 {
1067   Trace("cegqi-sol-debug") << "Printing synth solution..." << std::endl;
1068   Assert(d_quant[0].getNumChildren() == d_embed_quant[0].getNumChildren());
1069   std::vector<Node> sols;
1070   std::vector<int> statuses;
1071   if (!getSynthSolutionsInternal(sols, statuses))
1072   {
1073     return;
1074   }
1075   for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
1076   {
1077     Node sol = sols[i];
1078     if (!sol.isNull())
1079     {
1080       Node prog = d_embed_quant[0][i];
1081       int status = statuses[i];
1082       TypeNode tn = prog.getType();
1083       const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
1084       std::stringstream ss;
1085       ss << prog;
1086       std::string f(ss.str());
1087       f.erase(f.begin());
1088       SynthEngine* cei = d_qe->getSynthEngine();
1089       ++(cei->d_statistics.d_solutions);
1090 
1091       bool is_unique_term = true;
1092 
1093       if (status != 0
1094           && (options::sygusRewSynth() || options::sygusQueryGen()
1095               || options::sygusFilterSolMode() != SYGUS_FILTER_SOL_NONE))
1096       {
1097         Trace("cegqi-sol-debug") << "Run expression mining..." << std::endl;
1098         std::map<Node, ExpressionMinerManager>::iterator its =
1099             d_exprm.find(prog);
1100         if (its == d_exprm.end())
1101         {
1102           d_exprm[prog].initializeSygus(
1103               d_qe, d_candidates[i], options::sygusSamples(), true);
1104           if (options::sygusRewSynth())
1105           {
1106             d_exprm[prog].enableRewriteRuleSynth();
1107           }
1108           if (options::sygusQueryGen())
1109           {
1110             d_exprm[prog].enableQueryGeneration(options::sygusQueryGenThresh());
1111           }
1112           if (options::sygusFilterSolMode() != SYGUS_FILTER_SOL_NONE)
1113           {
1114             if (options::sygusFilterSolMode() == SYGUS_FILTER_SOL_STRONG)
1115             {
1116               d_exprm[prog].enableFilterStrongSolutions();
1117             }
1118             else if (options::sygusFilterSolMode() == SYGUS_FILTER_SOL_WEAK)
1119             {
1120               d_exprm[prog].enableFilterWeakSolutions();
1121             }
1122           }
1123           its = d_exprm.find(prog);
1124         }
1125         bool rew_print = false;
1126         is_unique_term = d_exprm[prog].addTerm(sol, out, rew_print);
1127         if (rew_print)
1128         {
1129           ++(cei->d_statistics.d_candidate_rewrites_print);
1130         }
1131         if (!is_unique_term)
1132         {
1133           ++(cei->d_statistics.d_filtered_solutions);
1134         }
1135       }
1136       if (is_unique_term)
1137       {
1138         out << "(define-fun " << f << " ";
1139         if (dt.getSygusVarList().isNull())
1140         {
1141           out << "() ";
1142         }
1143         else
1144         {
1145           out << dt.getSygusVarList() << " ";
1146         }
1147         out << dt.getSygusType() << " ";
1148         if (status == 0)
1149         {
1150           out << sol;
1151         }
1152         else
1153         {
1154           Printer::getPrinter(options::outputLanguage())
1155               ->toStreamSygus(out, sol);
1156         }
1157         out << ")" << std::endl;
1158       }
1159     }
1160   }
1161 }
1162 
getSynthSolutions(std::map<Node,Node> & sol_map)1163 void SynthConjecture::getSynthSolutions(std::map<Node, Node>& sol_map)
1164 {
1165   NodeManager* nm = NodeManager::currentNM();
1166   std::vector<Node> sols;
1167   std::vector<int> statuses;
1168   if (!getSynthSolutionsInternal(sols, statuses))
1169   {
1170     return;
1171   }
1172   for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
1173   {
1174     Node sol = sols[i];
1175     int status = statuses[i];
1176     // get the builtin solution
1177     Node bsol = sol;
1178     if (status != 0)
1179     {
1180       // convert sygus to builtin here
1181       bsol = d_tds->sygusToBuiltin(sol, sol.getType());
1182     }
1183     // convert to lambda
1184     TypeNode tn = d_embed_quant[0][i].getType();
1185     const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
1186     Node bvl = Node::fromExpr(dt.getSygusVarList());
1187     if (!bvl.isNull())
1188     {
1189       bsol = nm->mkNode(LAMBDA, bvl, bsol);
1190     }
1191     // store in map
1192     Node fvar = d_quant[0][i];
1193     Assert(fvar.getType() == bsol.getType());
1194     sol_map[fvar] = bsol;
1195   }
1196 }
1197 
getSynthSolutionsInternal(std::vector<Node> & sols,std::vector<int> & statuses)1198 bool SynthConjecture::getSynthSolutionsInternal(std::vector<Node>& sols,
1199                                                 std::vector<int>& statuses)
1200 {
1201   for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
1202   {
1203     Node prog = d_embed_quant[0][i];
1204     Trace("cegqi-debug") << "  get solution for " << prog << std::endl;
1205     TypeNode tn = prog.getType();
1206     Assert(tn.isDatatype());
1207     // get the solution
1208     Node sol;
1209     int status = -1;
1210     if (isSingleInvocation())
1211     {
1212       Assert(d_ceg_si != NULL);
1213       sol = d_ceg_si->getSolution(i, tn, status, true);
1214       if (sol.isNull())
1215       {
1216         return false;
1217       }
1218       sol = sol.getKind() == LAMBDA ? sol[1] : sol;
1219     }
1220     else
1221     {
1222       Node cprog = getCandidate(i);
1223       if (!d_cinfo[cprog].d_inst.empty())
1224       {
1225         // the solution is just the last instantiated term
1226         sol = d_cinfo[cprog].d_inst.back();
1227         status = 1;
1228 
1229         // check if there was a template
1230         Node sf = d_quant[0][i];
1231         Node templ = d_ceg_si->getTemplate(sf);
1232         if (!templ.isNull())
1233         {
1234           Trace("cegqi-inv-debug")
1235               << sf << " used template : " << templ << std::endl;
1236           // if it was not embedded into the grammar
1237           if (!options::sygusTemplEmbedGrammar())
1238           {
1239             TNode templa = d_ceg_si->getTemplateArg(sf);
1240             // make the builtin version of the full solution
1241             sol = d_tds->sygusToBuiltin(sol, sol.getType());
1242             Trace("cegqi-inv") << "Builtin version of solution is : " << sol
1243                                << ", type : " << sol.getType() << std::endl;
1244             TNode tsol = sol;
1245             sol = templ.substitute(templa, tsol);
1246             Trace("cegqi-inv-debug") << "With template : " << sol << std::endl;
1247             sol = Rewriter::rewrite(sol);
1248             Trace("cegqi-inv-debug") << "Simplified : " << sol << std::endl;
1249             // now, reconstruct to the syntax
1250             sol = d_ceg_si->reconstructToSyntax(sol, tn, status, true);
1251             sol = sol.getKind() == LAMBDA ? sol[1] : sol;
1252             Trace("cegqi-inv-debug")
1253                 << "Reconstructed to syntax : " << sol << std::endl;
1254           }
1255           else
1256           {
1257             Trace("cegqi-inv-debug")
1258                 << "...was embedding into grammar." << std::endl;
1259           }
1260         }
1261         else
1262         {
1263           Trace("cegqi-inv-debug")
1264               << sf << " did not use template" << std::endl;
1265         }
1266       }
1267       else
1268       {
1269         Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
1270                                "syntax-guided solution!"
1271                             << std::endl;
1272       }
1273     }
1274     sols.push_back(sol);
1275     statuses.push_back(status);
1276   }
1277   return true;
1278 }
1279 
getSymmetryBreakingPredicate(Node x,Node e,TypeNode tn,unsigned tindex,unsigned depth)1280 Node SynthConjecture::getSymmetryBreakingPredicate(
1281     Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
1282 {
1283   std::vector<Node> sb_lemmas;
1284 
1285   // based on simple preprocessing
1286   Node ppred =
1287       d_ceg_proc->getSymmetryBreakingPredicate(x, e, tn, tindex, depth);
1288   if (!ppred.isNull())
1289   {
1290     sb_lemmas.push_back(ppred);
1291   }
1292 
1293   // other static conjecture-dependent symmetry breaking goes here
1294 
1295   if (!sb_lemmas.empty())
1296   {
1297     return sb_lemmas.size() == 1
1298                ? sb_lemmas[0]
1299                : NodeManager::currentNM()->mkNode(kind::AND, sb_lemmas);
1300   }
1301   else
1302   {
1303     return Node::null();
1304   }
1305 }
1306 
1307 }  // namespace quantifiers
1308 }  // namespace theory
1309 } /* namespace CVC4 */
1310