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