1 /********************* */
2 /*! \file synth_conjecture.cpp
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
12 ** \brief Implementation of class that encapsulates techniques for a single
13 ** (SyGuS) synthesis conjecture.
15 #include "theory/quantifiers/sygus/synth_conjecture.h"
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"
36 using namespace CVC4::kind
;
41 namespace quantifiers
{
43 SynthConjecture::SynthConjecture(QuantifiersEngine
* qe
,
49 d_tds(qe
->getTermDatabaseSygus()),
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)),
62 d_set_ce_sk_vars(false),
65 d_guarded_stream_exc(false)
67 if (options::sygusSymBreakPbe() || options::sygusUnifPbe())
69 d_modules
.push_back(d_ceg_pbe
.get());
71 if (options::sygusUnifPi() != options::SygusUnifPiMode::NONE
)
73 d_modules
.push_back(d_ceg_cegisUnif
.get());
75 if (options::sygusCoreConnective())
77 d_modules
.push_back(d_sygus_ccore
.get());
79 d_modules
.push_back(d_ceg_cegis
.get());
82 SynthConjecture::~SynthConjecture() {}
84 void SynthConjecture::presolve()
86 // we don't have a solution yet
87 d_hasSolution
= false;
90 void SynthConjecture::assign(Node q
)
92 Assert(d_embed_quant
.isNull());
93 Assert(q
.getKind() == FORALL
);
94 Trace("cegqi") << "SynthConjecture : assign : " << q
<< std::endl
;
96 NodeManager
* nm
= NodeManager::currentNM();
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());
104 // pre-simplify the quantified formula based on the process utility
105 d_simp_quant
= d_ceg_proc
->preSimplify(d_quant
);
107 // compute its attributes
109 QuantAttributes::computeQuantAttributes(q
, qa
);
111 std::map
<Node
, Node
> templates
;
112 std::map
<Node
, Node
> templates_arg
;
113 // register with single invocation if applicable
116 d_ceg_si
->initialize(d_simp_quant
);
117 d_simp_quant
= d_ceg_si
->getSimplifiedConjecture();
118 if (!d_ceg_si
->isSingleInvocation())
120 d_templInfer
->initialize(d_simp_quant
);
122 // carry the templates
123 for (const Node
& v
: q
[0])
125 Node templ
= d_templInfer
->getTemplate(v
);
128 templates
[v
] = templ
;
129 templates_arg
[v
] = d_templInfer
->getTemplateArg(v
);
134 // post-simplify the quantified formula based on the process utility
135 d_simp_quant
= d_ceg_proc
->postSimplify(d_simp_quant
);
137 // finished simplifying the quantified formula at this point
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
;
144 Node sc
= qa
.d_sygusSideCondition
;
147 // convert to deep embedding
148 d_embedSideCondition
= d_ceg_gc
->convertToEmbedding(sc
);
149 Trace("cegqi") << "SynthConjecture : side condition : "
150 << d_embedSideCondition
<< std::endl
;
153 // we now finalize the single invocation module, based on the syntax
157 d_ceg_si
->finishInit(d_ceg_gc
->isSyntaxRestricted());
160 Assert(d_candidates
.empty());
161 std::vector
<Node
> vars
;
162 for (unsigned i
= 0; i
< d_embed_quant
[0].getNumChildren(); i
++)
164 vars
.push_back(d_embed_quant
[0][i
]);
166 NodeManager::currentNM()->mkSkolem("e", d_embed_quant
[0][i
].getType());
167 d_candidates
.push_back(e
);
169 Trace("cegqi") << "Base quantified formula is : " << d_embed_quant
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())
176 d_embedSideCondition
= d_embedSideCondition
.substitute(
177 vars
.begin(), vars
.end(), d_candidates
.begin(), d_candidates
.end());
179 Trace("cegqi") << "Base instantiation is : " << d_base_inst
<< std::endl
;
181 // initialize the sygus constant repair utility
182 if (options::sygusRepairConst())
184 d_sygus_rconst
->initialize(d_base_inst
.negate(), d_candidates
);
185 if (options::sygusConstRepairAbort())
187 if (!d_sygus_rconst
->isActive())
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());
196 // initialize the example inference utility
197 if (!d_exampleInfer
->initialize(d_base_inst
, d_candidates
))
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
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())
211 d_ceg_proc
->initialize(d_base_inst
, d_candidates
);
212 for (unsigned i
= 0, size
= d_modules
.size(); i
< size
; i
++)
214 if (d_modules
[i
]->initialize(
215 d_simp_quant
, d_base_inst
, d_candidates
, guarded_lemmas
))
217 d_master
= d_modules
[i
];
221 Assert(d_master
!= nullptr);
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
)
228 for (const Node
& v
: d_base_inst
[0][0])
230 d_inner_vars
.push_back(v
);
234 // register the strategy
235 d_feasible_strategy
.reset(
236 new DecisionStrategySingleton("sygus_feasible",
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);
247 Node gneg
= d_feasible_guard
.negate();
248 for (unsigned i
= 0; i
< guarded_lemmas
.size(); i
++)
250 Node lem
= nm
->mkNode(OR
, gneg
, guarded_lemmas
[i
]);
251 Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem
253 d_qe
->getOutputChannel().lemma(lem
);
256 Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation()
260 Node
SynthConjecture::getGuard() const { return d_feasible_guard
; }
262 bool SynthConjecture::isSingleInvocation() const
264 return d_ceg_si
->isSingleInvocation();
267 bool SynthConjecture::needsCheck()
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
))
276 Trace("sygus-engine-debug") << "Conjecture is infeasible." << std::endl
;
281 Trace("sygus-engine-debug") << "Feasible guard " << d_feasible_guard
282 << " assigned true." << std::endl
;
287 Trace("cegqi-warn") << "WARNING: Guard " << d_feasible_guard
288 << " is not assigned!" << std::endl
;
294 bool SynthConjecture::needsRefinement() const { return d_set_ce_sk_vars
; }
295 bool SynthConjecture::doCheck(std::vector
<Node
>& lems
)
297 if (isSingleInvocation())
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
302 if (d_ceg_si
->solve())
304 d_hasSolution
= true;
305 // the conjecture has a solution, so its negation holds
306 lems
.push_back(d_quant
.negate());
310 Assert(d_master
!= nullptr);
311 Assert(!d_hasSolution
);
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
);
317 Assert(!d_candidates
.empty());
319 Trace("cegqi-check") << "CegConjuncture : check, build candidates..."
321 std::vector
<Node
> candidate_values
;
322 bool constructed_cand
= false;
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
328 options::sygusRepairConst() && !d_master
->usingRepairConst();
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
)
336 std::vector
<Node
> fail_cvs
;
337 for (const Node
& cprog
: d_candidates
)
339 Assert(d_repair_index
< d_cinfo
[cprog
].d_inst
.size());
340 fail_cvs
.push_back(d_cinfo
[cprog
].d_inst
[d_repair_index
]);
342 if (Trace
.isOn("sygus-engine"))
344 Trace("sygus-engine") << "CegConjuncture : repair previous solution ";
345 for (const Node
& fc
: fail_cvs
)
347 std::stringstream ss
;
348 TermDbSygus::toStreamSygus(ss
, fc
);
349 Trace("sygus-engine") << ss
.str() << " ";
351 Trace("sygus-engine") << std::endl
;
354 if (d_sygus_rconst
->repairSolution(
355 d_candidates
, fail_cvs
, candidate_values
, true))
357 constructed_cand
= true;
362 bool printDebug
= options::debugSygus();
363 if (!constructed_cand
)
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
);
370 // if the master requires a full model and the model is partial, we fail
371 if (!d_master
->allowPartialModel() && !fullModel
)
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
377 return !activeIncomplete
;
379 // the waiting values are passed to the module below, clear
380 d_ev_active_gen_waiting
.clear();
382 Assert(terms
.size() == enum_values
.size());
383 bool emptyModel
= true;
384 for (unsigned i
= 0, size
= terms
.size(); i
< size
; i
++)
386 if (!enum_values
[i
].isNull())
393 Trace("sygus-engine-debug") << "...empty model, fail." << std::endl
;
394 return !activeIncomplete
;
396 // Must separately compute whether trace is on due to compilation of
398 bool traceIsOn
= Trace
.isOn("sygus-engine");
399 if (printDebug
|| traceIsOn
)
401 Trace("sygus-engine") << " * Value is : ";
402 std::stringstream sygusEnumOut
;
403 for (unsigned i
= 0, size
= terms
.size(); i
< size
; i
++)
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
);
412 sygusEnumOut
<< " " << ss
.str();
414 Trace("sygus-engine") << terms
[i
] << " -> ";
417 Trace("sygus-engine") << "[EXC: " << ss
.str() << "] ";
421 Trace("sygus-engine") << ss
.str() << " ";
422 if (Trace
.isOn("sygus-engine-rr"))
424 Node bv
= d_tds
->sygusToBuiltin(nv
, tn
);
425 bv
= Rewriter::rewrite(bv
);
426 Trace("sygus-engine-rr") << " -> " << bv
<< std::endl
;
430 Trace("sygus-engine") << std::endl
;
433 Options
& sopts
= smt::currentSmtEngine()->getOptions();
434 std::ostream
& out
= *sopts
.getOut();
435 out
<< "(sygus-enum" << sygusEnumOut
.str() << ")" << std::endl
;
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
:
445 ExampleEvalCache
* eec
= ecp
.second
.get();
448 eec
->clearEvaluationAll();
453 NodeManager
* nm
= NodeManager::currentNM();
455 // check the side condition if we constructed a candidate
456 if (constructed_cand
)
458 if (!checkSideCondition(candidate_values
))
460 excludeCurrentSolution(terms
, candidate_values
);
461 Trace("sygus-engine") << "...failed side condition" << std::endl
;
466 // must get a counterexample to the value of the current candidate
468 if (constructed_cand
)
470 if (Trace
.isOn("cegqi-check"))
472 Trace("cegqi-check") << "CegConjuncture : check candidate : "
474 for (unsigned i
= 0, size
= candidate_values
.size(); i
< size
; i
++)
476 Trace("cegqi-check") << " " << i
<< " : " << d_candidates
[i
] << " -> "
477 << candidate_values
[i
] << std::endl
;
480 Assert(candidate_values
.size() == d_candidates
.size());
481 inst
= d_base_inst
.substitute(d_candidates
.begin(),
483 candidate_values
.begin(),
484 candidate_values
.end());
491 if (!constructed_cand
)
496 // if we trust the sampling we ran, we terminate now
497 if (options::cegisSample() == options::CegisSampleMode::TRUST
)
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
);
506 Assert(!d_set_ce_sk_vars
);
508 // immediately skolemize inner existentials
510 // introduce the skolem variables
511 std::vector
<Node
> sks
;
512 std::vector
<Node
> vars
;
513 if (constructed_cand
)
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
++)
523 Node v
= candidate_values
[i
];
524 std::stringstream ss
;
525 TermDbSygus::toStreamSygus(ss
, v
);
526 out
<< "(" << d_quant
[0][i
] << " " << ss
.str() << ")";
528 out
<< ")" << std::endl
;
530 if (inst
.getKind() == NOT
&& inst
[0].getKind() == FORALL
)
532 for (const Node
& v
: inst
[0][0])
534 Node sk
= nm
->mkSkolem("rsk", v
.getType());
537 Trace("cegqi-check-debug")
538 << " introduce skolem " << sk
<< " for " << v
<< "\n";
540 query
= inst
[0][1].substitute(
541 vars
.begin(), vars
.end(), sks
.begin(), sks
.end());
542 query
= query
.negate();
546 // use the instance itself
550 d_ce_sk_vars
.insert(d_ce_sk_vars
.end(), sks
.begin(), sks
.end());
551 d_set_ce_sk_vars
= true;
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
);
567 if (!query
.isConst() || query
.getConst
<bool>())
569 Trace("sygus-engine") << " *** Verify with subcall..." << std::endl
;
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
)
575 if (Trace
.isOn("sygus-engine"))
577 Trace("sygus-engine") << " * Verification lemma failed for:\n ";
578 for (unsigned i
= 0, size
= d_ce_sk_vars
.size(); i
< size
; i
++)
580 Trace("sygus-engine")
581 << d_ce_sk_vars
[i
] << " -> " << d_ce_sk_var_mvs
[i
] << " ";
583 Trace("sygus-engine") << std::endl
;
585 if (Configuration::isAssertionBuild())
587 // the values for the query should be a complete model
588 Node squery
= query
.substitute(d_ce_sk_vars
.begin(),
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>()));
600 else if (r
.asSatisfiabilityResult().isSat() != Result::UNSAT
)
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();
612 // otherwise we are unsat, and we will process the solution below
614 // now mark that we have a solution
615 d_hasSolution
= true;
616 if (options::sygusStream())
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;
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());
630 bool SynthConjecture::checkSideCondition(const std::vector
<Node
>& cvals
) const
632 if (!d_embedSideCondition
.isNull())
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
)
644 Trace("sygus-engine") << "...passed side condition" << std::endl
;
649 bool SynthConjecture::doRefine()
651 std::vector
<Node
> lems
;
652 Assert(d_set_ce_sk_vars
);
654 // first, make skolem substitution
655 Trace("cegqi-refine") << "doRefine : construct skolem substitution..."
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())
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())
666 std::vector
<Node
> model_values
;
667 for (const Node
& v
: d_ce_sk_vars
)
669 Node mv
= getModelValue(v
);
670 Trace("cegqi-refine") << " " << v
<< " -> " << mv
<< std::endl
;
671 model_values
.push_back(mv
);
673 sk_subs
.insert(sk_subs
.end(), model_values
.begin(), model_values
.end());
677 Assert(d_ce_sk_var_mvs
.size() == d_ce_sk_vars
.size());
679 sk_subs
.end(), d_ce_sk_var_mvs
.begin(), d_ce_sk_var_mvs
.end());
681 sk_vars
.insert(sk_vars
.end(), d_inner_vars
.begin(), d_inner_vars
.end());
685 Assert(d_inner_vars
.empty());
688 std::vector
<Node
> lem_c
;
689 Trace("cegqi-refine") << "doRefine : Construct refinement lemma..."
691 Trace("cegqi-refine-debug")
692 << " For counterexample skolems : " << d_ce_sk_vars
<< std::endl
;
694 if (d_base_inst
.getKind() == NOT
&& d_base_inst
[0].getKind() == FORALL
)
696 base_lem
= d_base_inst
[0][1];
700 base_lem
= d_base_inst
.negate();
703 Assert(sk_vars
.size() == sk_subs
.size());
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();
718 // now send the lemmas
719 bool addedLemma
= false;
720 for (const Node
& lem
: lems
)
722 Trace("cegqi-lemma") << "Cegqi::Lemma : candidate refinement : " << lem
724 bool res
= d_qe
->addLemma(lem
);
727 ++(d_stats
.d_cegqi_lemmas_refine
);
733 Trace("cegqi-warn") << " ...FAILED to add refinement!" << std::endl
;
738 Trace("sygus-engine-debug") << " ...refine candidate." << std::endl
;
742 Trace("sygus-engine-debug") << " ...(warning) failed to refine candidate, "
743 "manually exclude candidate."
745 std::vector
<Node
> cvals
;
746 for (const Node
& c
: d_candidates
)
748 cvals
.push_back(d_cinfo
[c
].d_inst
.back());
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.
760 void SynthConjecture::preregisterConjecture(Node q
)
762 d_ceg_si
->preregisterConjecture(q
);
765 bool SynthConjecture::getEnumeratedValues(std::vector
<Node
>& n
,
766 std::vector
<Node
>& v
,
767 bool& activeIncomplete
)
769 std::vector
<Node
> ncheck
= n
;
772 for (unsigned i
= 0, size
= ncheck
.size(); i
< size
; i
++)
775 // if it is not active, we return null
776 Node g
= d_tds
->getActiveGuardForEnumerator(e
);
779 Node gstatus
= d_qe
->getValuation().getSatValue(g
);
780 if (gstatus
.isNull() || !gstatus
.getConst
<bool>())
782 Trace("sygus-engine-debug")
783 << "Enumerator " << e
<< " is inactive." << std::endl
;
787 Node nv
= getEnumeratedValue(e
, activeIncomplete
);
790 ret
= ret
&& !nv
.isNull();
795 Node
SynthConjecture::getEnumeratedValue(Node e
, bool& activeIncomplete
)
797 bool isEnum
= d_tds
->isEnumerator(e
);
799 if (isEnum
&& !e
.getAttribute(SygusSymBreakOkAttribute()))
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."
810 if (!isEnum
|| d_tds
->isPassiveEnumerator(e
))
812 return getModelValue(e
);
815 // management of actively generated enumerators goes here
817 // initialize the enumerated value generator for e
818 std::map
<Node
, std::unique_ptr
<EnumValGenerator
> >::iterator iteg
=
820 if (iteg
== d_evg
.end())
822 if (d_tds
->isVariableAgnosticEnumerator(e
))
824 d_evg
[e
].reset(new EnumStreamConcrete(d_tds
));
828 // Actively-generated enumerators are currently either variable agnostic
829 // or basic. The auto mode always prefers the optimized enumerator over
831 Assert(d_tds
->isBasicEnumerator(e
));
832 if (options::sygusActiveGenMode()
833 == options::SygusActiveGenMode::ENUM_BASIC
)
835 d_evg
[e
].reset(new EnumValGeneratorBasic(d_tds
, e
.getType()));
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
));
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
;
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())
857 Trace("sygus-active-gen-debug")
858 << "Active-gen: return waiting " << itw
->second
<< std::endl
;
861 // Check if there is an (abstract) value absE we were actively generating
863 Node absE
= d_ev_curr_active_gen
[e
];
864 bool firstTime
= false;
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"))
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
;
877 d_ev_curr_active_gen
[e
] = absE
;
878 iteg
->second
->addValue(absE
);
884 inc
= iteg
->second
->increment();
889 v
= iteg
->second
->getCurrent();
891 Trace("sygus-active-gen-debug") << "...generated " << v
892 << ", with increment success : " << inc
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
903 if (!d_tds
->isBasicEnumerator(e
))
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
++)
909 exp
[i
] = exp
[i
].negate();
912 Node g
= d_tds
->getActiveGuardForEnumerator(e
);
915 if (d_ev_active_gen_first_val
.find(e
) == d_ev_active_gen_first_val
.end())
917 exp
.push_back(g
.negate());
918 d_ev_active_gen_first_val
[e
] = absE
;
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 : "
929 if (Trace
.isOn("sygus-active-gen-debug"))
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
;
935 d_qe
->getOutputChannel().lemma(lem
);
939 // We are waiting to send e -> v to the module that requested it.
942 activeIncomplete
= true;
946 d_ev_active_gen_waiting
[e
] = v
;
948 if (Trace
.isOn("sygus-active-gen"))
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
;
961 Node
SynthConjecture::getModelValue(Node n
)
963 Trace("cegqi-mv") << "getModelValue for : " << n
<< std::endl
;
964 return d_qe
->getModel()->getValue(n
);
967 void SynthConjecture::debugPrint(const char* c
)
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
;
974 void SynthConjecture::printAndContinueStream(const std::vector
<Node
>& enums
,
975 const std::vector
<Node
>& values
)
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
);
985 void SynthConjecture::excludeCurrentSolution(const std::vector
<Node
>& enums
,
986 const std::vector
<Node
>& values
)
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
++)
1001 Node cprog
= enums
[i
];
1002 Assert(d_tds
->isEnumerator(cprog
));
1003 if (d_tds
->isPassiveEnumerator(cprog
))
1005 Node cval
= values
[i
];
1006 // add to explanation of exclusion
1007 d_tds
->getExplain()->getExplanationForEquality(cprog
, cval
, exp
);
1012 if (!d_guarded_stream_exc
)
1014 d_guarded_stream_exc
= true;
1015 exp
.push_back(d_feasible_guard
);
1017 Node exc_lem
= exp
.size() == 1
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
);
1027 void SynthConjecture::printSynthSolution(std::ostream
& out
)
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
))
1037 NodeManager
* nm
= NodeManager::currentNM();
1038 for (unsigned i
= 0, size
= d_embed_quant
[0].getNumChildren(); i
< size
; i
++)
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
;
1049 std::string
f(ss
.str());
1051 ++(d_stats
.d_solutions
);
1053 bool is_unique_term
= true;
1056 && (options::sygusRewSynth() || options::sygusQueryGen()
1057 || options::sygusFilterSolMode()
1058 != options::SygusFilterSolMode::NONE
))
1060 Trace("cegqi-sol-debug") << "Run expression mining..." << std::endl
;
1061 std::map
<Node
, ExpressionMinerManager
>::iterator its
=
1063 if (its
== d_exprm
.end())
1065 d_exprm
[prog
].initializeSygus(
1066 d_qe
, d_candidates
[i
], options::sygusSamples(), true);
1067 if (options::sygusRewSynth())
1069 d_exprm
[prog
].enableRewriteRuleSynth();
1071 if (options::sygusQueryGen())
1073 d_exprm
[prog
].enableQueryGeneration(options::sygusQueryGenThresh());
1075 if (options::sygusFilterSolMode()
1076 != options::SygusFilterSolMode::NONE
)
1078 if (options::sygusFilterSolMode()
1079 == options::SygusFilterSolMode::STRONG
)
1081 d_exprm
[prog
].enableFilterStrongSolutions();
1083 else if (options::sygusFilterSolMode()
1084 == options::SygusFilterSolMode::WEAK
)
1086 d_exprm
[prog
].enableFilterWeakSolutions();
1089 its
= d_exprm
.find(prog
);
1091 bool rew_print
= false;
1092 is_unique_term
= d_exprm
[prog
].addTerm(sol
, out
, rew_print
);
1095 ++(d_stats
.d_candidate_rewrites_print
);
1097 if (!is_unique_term
)
1099 ++(d_stats
.d_filtered_solutions
);
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
1112 std::vector
<Node
> pvs
;
1113 Node vl
= dt
.getSygusVarList();
1116 Assert(vl
.getKind() == BOUND_VAR_LIST
);
1117 SygusVarToTermAttribute sta
;
1118 for (const Node
& v
: vl
)
1120 if (!v
.hasAttribute(sta
))
1132 vl
= nm
->mkNode(BOUND_VAR_LIST
, pvs
);
1135 out
<< dt
.getSygusType() << " ";
1142 Node bsol
= datatypes::utils::sygusToBuiltin(sol
, true);
1145 out
<< ")" << std::endl
;
1151 bool SynthConjecture::getSynthSolutions(
1152 std::map
<Node
, std::map
<Node
, Node
> >& sol_map
)
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
))
1160 Trace("cegqi-debug") << "...failed internal" << std::endl
;
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
++)
1168 int status
= statuses
[i
];
1169 Trace("cegqi-debug") << "...got " << i
<< ": " << sol
1170 << ", status=" << status
<< std::endl
;
1171 // get the builtin solution
1175 // Convert sygus to builtin here.
1176 // We must use the external representation to ensure bsol matches the
1178 bsol
= datatypes::utils::sygusToBuiltin(sol
, true);
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();
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
);
1195 Assert(fvar
.getType().isComparableTo(bsol
.getType()));
1199 Trace("cegqi-debug") << "...return " << bsol
<< std::endl
;
1204 void SynthConjecture::recordSolution(std::vector
<Node
>& vs
)
1206 Assert(vs
.size() == d_candidates
.size());
1208 for (unsigned i
= 0; i
< vs
.size(); i
++)
1210 d_cinfo
[d_candidates
[i
]].d_inst
.push_back(vs
[i
]);
1214 bool SynthConjecture::getSynthSolutionsInternal(std::vector
<Node
>& sols
,
1215 std::vector
<int>& statuses
)
1221 for (unsigned i
= 0, size
= d_embed_quant
[0].getNumChildren(); i
< size
; i
++)
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());
1230 if (isSingleInvocation())
1232 Assert(d_ceg_si
!= NULL
);
1233 sol
= d_ceg_si
->getSolution(i
, tn
, status
, true);
1238 sol
= sol
.getKind() == LAMBDA
? sol
[1] : sol
;
1242 Node cprog
= d_candidates
[i
];
1243 if (!d_cinfo
[cprog
].d_inst
.empty())
1245 // the solution is just the last instantiated term
1246 sol
= d_cinfo
[cprog
].d_inst
.back();
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())
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())
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
;
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
;
1277 Trace("cegqi-inv-debug")
1278 << "...was embedding into grammar." << std::endl
;
1283 Trace("cegqi-inv-debug")
1284 << sf
<< " did not use template" << std::endl
;
1289 Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
1290 "syntax-guided solution!"
1294 sols
.push_back(sol
);
1295 statuses
.push_back(status
);
1300 Node
SynthConjecture::getSymmetryBreakingPredicate(
1301 Node x
, Node e
, TypeNode tn
, unsigned tindex
, unsigned depth
)
1303 std::vector
<Node
> sb_lemmas
;
1305 // based on simple preprocessing
1307 d_ceg_proc
->getSymmetryBreakingPredicate(x
, e
, tn
, tindex
, depth
);
1308 if (!ppred
.isNull())
1310 sb_lemmas
.push_back(ppred
);
1313 // other static conjecture-dependent symmetry breaking goes here
1315 if (!sb_lemmas
.empty())
1317 return sb_lemmas
.size() == 1
1319 : NodeManager::currentNM()->mkNode(kind::AND
, sb_lemmas
);
1323 return Node::null();
1327 ExampleEvalCache
* SynthConjecture::getExampleEvalCache(Node e
)
1329 std::map
<Node
, std::unique_ptr
<ExampleEvalCache
> >::iterator it
=
1330 d_exampleEvalCache
.find(e
);
1331 if (it
!= d_exampleEvalCache
.end())
1333 return it
->second
.get();
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)
1339 d_exampleEvalCache
[e
].reset(nullptr);
1342 d_exampleEvalCache
[e
].reset(new ExampleEvalCache(d_tds
, this, f
, e
));
1343 return d_exampleEvalCache
[e
].get();
1346 } // namespace quantifiers
1347 } // namespace theory
1348 } /* namespace CVC4 */