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