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
,
45 QuantifiersInferenceManager
& qim
,
51 d_tds(qe
->getTermDatabaseSygus()),
53 d_ceg_si(new CegSingleInv(qe
)),
54 d_templInfer(new SygusTemplateInfer
),
55 d_ceg_proc(new SynthConjectureProcess(qe
)),
56 d_ceg_gc(new CegGrammarConstructor(qe
, this)),
57 d_sygus_rconst(new SygusRepairConst(qe
)),
58 d_exampleInfer(new ExampleInfer(d_tds
)),
59 d_ceg_pbe(new SygusPbe(qe
, this)),
60 d_ceg_cegis(new Cegis(qe
, this)),
61 d_ceg_cegisUnif(new CegisUnif(qe
, qs
, this)),
62 d_sygus_ccore(new CegisCoreConnective(qe
, this)),
64 d_set_ce_sk_vars(false),
67 d_guarded_stream_exc(false)
69 if (options::sygusSymBreakPbe() || options::sygusUnifPbe())
71 d_modules
.push_back(d_ceg_pbe
.get());
73 if (options::sygusUnifPi() != options::SygusUnifPiMode::NONE
)
75 d_modules
.push_back(d_ceg_cegisUnif
.get());
77 if (options::sygusCoreConnective())
79 d_modules
.push_back(d_sygus_ccore
.get());
81 d_modules
.push_back(d_ceg_cegis
.get());
84 SynthConjecture::~SynthConjecture() {}
86 void SynthConjecture::presolve()
88 // we don't have a solution yet
89 d_hasSolution
= false;
92 void SynthConjecture::assign(Node q
)
94 Assert(d_embed_quant
.isNull());
95 Assert(q
.getKind() == FORALL
);
96 Trace("cegqi") << "SynthConjecture : assign : " << q
<< std::endl
;
98 NodeManager
* nm
= NodeManager::currentNM();
100 // initialize the guard
101 d_feasible_guard
= nm
->mkSkolem("G", nm
->booleanType());
102 d_feasible_guard
= Rewriter::rewrite(d_feasible_guard
);
103 d_feasible_guard
= d_qe
->getValuation().ensureLiteral(d_feasible_guard
);
104 AlwaysAssert(!d_feasible_guard
.isNull());
106 // pre-simplify the quantified formula based on the process utility
107 d_simp_quant
= d_ceg_proc
->preSimplify(d_quant
);
109 // compute its attributes
111 QuantAttributes::computeQuantAttributes(q
, qa
);
113 std::map
<Node
, Node
> templates
;
114 std::map
<Node
, Node
> templates_arg
;
115 // register with single invocation if applicable
118 d_ceg_si
->initialize(d_simp_quant
);
119 d_simp_quant
= d_ceg_si
->getSimplifiedConjecture();
120 if (!d_ceg_si
->isSingleInvocation())
122 d_templInfer
->initialize(d_simp_quant
);
124 // carry the templates
125 for (const Node
& v
: q
[0])
127 Node templ
= d_templInfer
->getTemplate(v
);
130 templates
[v
] = templ
;
131 templates_arg
[v
] = d_templInfer
->getTemplateArg(v
);
136 // post-simplify the quantified formula based on the process utility
137 d_simp_quant
= d_ceg_proc
->postSimplify(d_simp_quant
);
139 // finished simplifying the quantified formula at this point
141 // convert to deep embedding and finalize single invocation here
142 d_embed_quant
= d_ceg_gc
->process(d_simp_quant
, templates
, templates_arg
);
143 Trace("cegqi") << "SynthConjecture : converted to embedding : "
144 << d_embed_quant
<< std::endl
;
146 Node sc
= qa
.d_sygusSideCondition
;
149 // convert to deep embedding
150 d_embedSideCondition
= d_ceg_gc
->convertToEmbedding(sc
);
151 Trace("cegqi") << "SynthConjecture : side condition : "
152 << d_embedSideCondition
<< std::endl
;
155 // we now finalize the single invocation module, based on the syntax
159 d_ceg_si
->finishInit(d_ceg_gc
->isSyntaxRestricted());
162 Assert(d_candidates
.empty());
163 std::vector
<Node
> vars
;
164 for (unsigned i
= 0; i
< d_embed_quant
[0].getNumChildren(); i
++)
166 vars
.push_back(d_embed_quant
[0][i
]);
168 NodeManager::currentNM()->mkSkolem("e", d_embed_quant
[0][i
].getType());
169 d_candidates
.push_back(e
);
171 Trace("cegqi") << "Base quantified formula is : " << d_embed_quant
173 // construct base instantiation
174 d_base_inst
= Rewriter::rewrite(d_qe
->getInstantiate()->getInstantiation(
175 d_embed_quant
, vars
, d_candidates
));
176 if (!d_embedSideCondition
.isNull())
178 d_embedSideCondition
= d_embedSideCondition
.substitute(
179 vars
.begin(), vars
.end(), d_candidates
.begin(), d_candidates
.end());
181 Trace("cegqi") << "Base instantiation is : " << d_base_inst
<< std::endl
;
183 // initialize the sygus constant repair utility
184 if (options::sygusRepairConst())
186 d_sygus_rconst
->initialize(d_base_inst
.negate(), d_candidates
);
187 if (options::sygusConstRepairAbort())
189 if (!d_sygus_rconst
->isActive())
191 // no constant repair is possible: abort
192 std::stringstream ss
;
193 ss
<< "Grammar does not allow repair constants." << std::endl
;
194 throw LogicException(ss
.str());
198 // initialize the example inference utility
199 if (!d_exampleInfer
->initialize(d_base_inst
, d_candidates
))
201 // there is a contradictory example pair, the conjecture is infeasible.
202 Node infLem
= d_feasible_guard
.negate();
203 d_qe
->getOutputChannel().lemma(infLem
);
204 // we don't need to continue initialization in this case
208 // register this term with sygus database and other utilities that impact
209 // the enumerative sygus search
210 std::vector
<Node
> guarded_lemmas
;
211 if (!isSingleInvocation())
213 d_ceg_proc
->initialize(d_base_inst
, d_candidates
);
214 for (unsigned i
= 0, size
= d_modules
.size(); i
< size
; i
++)
216 if (d_modules
[i
]->initialize(
217 d_simp_quant
, d_base_inst
, d_candidates
, guarded_lemmas
))
219 d_master
= d_modules
[i
];
223 Assert(d_master
!= nullptr);
226 Assert(d_qe
->getQuantAttributes()->isSygus(q
));
227 // if the base instantiation is an existential, store its variables
228 if (d_base_inst
.getKind() == NOT
&& d_base_inst
[0].getKind() == FORALL
)
230 for (const Node
& v
: d_base_inst
[0][0])
232 d_inner_vars
.push_back(v
);
236 // register the strategy
237 d_feasible_strategy
.reset(
238 new DecisionStrategySingleton("sygus_feasible",
240 d_qstate
.getSatContext(),
241 d_qe
->getValuation()));
242 d_qe
->getDecisionManager()->registerStrategy(
243 DecisionManager::STRAT_QUANT_SYGUS_FEASIBLE
, d_feasible_strategy
.get());
244 // this must be called, both to ensure that the feasible guard is
245 // decided on with true polariy, but also to ensure that output channel
246 // has been used on this call to check.
247 d_qe
->getOutputChannel().requirePhase(d_feasible_guard
, true);
249 Node gneg
= d_feasible_guard
.negate();
250 for (unsigned i
= 0; i
< guarded_lemmas
.size(); i
++)
252 Node lem
= nm
->mkNode(OR
, gneg
, guarded_lemmas
[i
]);
253 Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem
255 d_qe
->getOutputChannel().lemma(lem
);
258 Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation()
262 Node
SynthConjecture::getGuard() const { return d_feasible_guard
; }
264 bool SynthConjecture::isSingleInvocation() const
266 return d_ceg_si
->isSingleInvocation();
269 bool SynthConjecture::needsCheck()
272 Assert(!d_feasible_guard
.isNull());
273 // non or fully single invocation : look at guard only
274 if (d_qe
->getValuation().hasSatValue(d_feasible_guard
, value
))
278 Trace("sygus-engine-debug") << "Conjecture is infeasible." << std::endl
;
283 Trace("sygus-engine-debug") << "Feasible guard " << d_feasible_guard
284 << " assigned true." << std::endl
;
289 Trace("cegqi-warn") << "WARNING: Guard " << d_feasible_guard
290 << " is not assigned!" << std::endl
;
296 bool SynthConjecture::needsRefinement() const { return d_set_ce_sk_vars
; }
297 bool SynthConjecture::doCheck(std::vector
<Node
>& lems
)
299 if (isSingleInvocation())
301 // We now try to solve with the single invocation solver, which may or may
302 // not succeed in solving the conjecture. In either case, we are done and
304 if (d_ceg_si
->solve())
306 d_hasSolution
= true;
307 // the conjecture has a solution, so its negation holds
308 lems
.push_back(d_quant
.negate());
312 Assert(d_master
!= nullptr);
313 Assert(!d_hasSolution
);
315 // get the list of terms that the master strategy is interested in
316 std::vector
<Node
> terms
;
317 d_master
->getTermList(d_candidates
, terms
);
319 Assert(!d_candidates
.empty());
321 Trace("cegqi-check") << "CegConjuncture : check, build candidates..."
323 std::vector
<Node
> candidate_values
;
324 bool constructed_cand
= false;
326 // If a module is not trying to repair constants in solutions and the option
327 // sygusRepairConst is true, we use a default scheme for trying to repair
330 options::sygusRepairConst() && !d_master
->usingRepairConst();
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
)
338 std::vector
<Node
> fail_cvs
;
339 for (const Node
& cprog
: d_candidates
)
341 Assert(d_repair_index
< d_cinfo
[cprog
].d_inst
.size());
342 fail_cvs
.push_back(d_cinfo
[cprog
].d_inst
[d_repair_index
]);
344 if (Trace
.isOn("sygus-engine"))
346 Trace("sygus-engine") << "CegConjuncture : repair previous solution ";
347 for (const Node
& fc
: fail_cvs
)
349 std::stringstream ss
;
350 TermDbSygus::toStreamSygus(ss
, fc
);
351 Trace("sygus-engine") << ss
.str() << " ";
353 Trace("sygus-engine") << std::endl
;
356 if (d_sygus_rconst
->repairSolution(
357 d_candidates
, fail_cvs
, candidate_values
, true))
359 constructed_cand
= true;
364 bool printDebug
= options::debugSygus();
365 if (!constructed_cand
)
367 // get the model value of the relevant terms from the master module
368 std::vector
<Node
> enum_values
;
369 bool activeIncomplete
= false;
370 bool fullModel
= getEnumeratedValues(terms
, enum_values
, activeIncomplete
);
372 // if the master requires a full model and the model is partial, we fail
373 if (!d_master
->allowPartialModel() && !fullModel
)
375 // we retain the values in d_ev_active_gen_waiting
376 Trace("sygus-engine-debug") << "...partial model, fail." << std::endl
;
377 // if we are partial due to an active enumerator, we may still succeed
379 return !activeIncomplete
;
381 // the waiting values are passed to the module below, clear
382 d_ev_active_gen_waiting
.clear();
384 Assert(terms
.size() == enum_values
.size());
385 bool emptyModel
= true;
386 for (unsigned i
= 0, size
= terms
.size(); i
< size
; i
++)
388 if (!enum_values
[i
].isNull())
395 Trace("sygus-engine-debug") << "...empty model, fail." << std::endl
;
396 return !activeIncomplete
;
398 // Must separately compute whether trace is on due to compilation of
400 bool traceIsOn
= Trace
.isOn("sygus-engine");
401 if (printDebug
|| traceIsOn
)
403 Trace("sygus-engine") << " * Value is : ";
404 std::stringstream sygusEnumOut
;
405 for (unsigned i
= 0, size
= terms
.size(); i
< size
; i
++)
407 Node nv
= enum_values
[i
];
408 Node onv
= nv
.isNull() ? d_qe
->getModel()->getValue(terms
[i
]) : nv
;
409 TypeNode tn
= onv
.getType();
410 std::stringstream ss
;
411 TermDbSygus::toStreamSygus(ss
, onv
);
414 sygusEnumOut
<< " " << ss
.str();
416 Trace("sygus-engine") << terms
[i
] << " -> ";
419 Trace("sygus-engine") << "[EXC: " << ss
.str() << "] ";
423 Trace("sygus-engine") << ss
.str() << " ";
424 if (Trace
.isOn("sygus-engine-rr"))
426 Node bv
= d_tds
->sygusToBuiltin(nv
, tn
);
427 bv
= Rewriter::rewrite(bv
);
428 Trace("sygus-engine-rr") << " -> " << bv
<< std::endl
;
432 Trace("sygus-engine") << std::endl
;
435 Options
& sopts
= smt::currentSmtEngine()->getOptions();
436 std::ostream
& out
= *sopts
.getOut();
437 out
<< "(sygus-enum" << sygusEnumOut
.str() << ")" << std::endl
;
440 Assert(candidate_values
.empty());
441 constructed_cand
= d_master
->constructCandidates(
442 terms
, enum_values
, d_candidates
, candidate_values
, lems
);
443 // now clear the evaluation caches
444 for (std::pair
<const Node
, std::unique_ptr
<ExampleEvalCache
> >& ecp
:
447 ExampleEvalCache
* eec
= ecp
.second
.get();
450 eec
->clearEvaluationAll();
455 NodeManager
* nm
= NodeManager::currentNM();
457 // check the side condition if we constructed a candidate
458 if (constructed_cand
)
460 if (!checkSideCondition(candidate_values
))
462 excludeCurrentSolution(terms
, candidate_values
);
463 Trace("sygus-engine") << "...failed side condition" << std::endl
;
468 // must get a counterexample to the value of the current candidate
470 if (constructed_cand
)
472 if (Trace
.isOn("cegqi-check"))
474 Trace("cegqi-check") << "CegConjuncture : check candidate : "
476 for (unsigned i
= 0, size
= candidate_values
.size(); i
< size
; i
++)
478 Trace("cegqi-check") << " " << i
<< " : " << d_candidates
[i
] << " -> "
479 << candidate_values
[i
] << std::endl
;
482 Assert(candidate_values
.size() == d_candidates
.size());
483 inst
= d_base_inst
.substitute(d_candidates
.begin(),
485 candidate_values
.begin(),
486 candidate_values
.end());
493 if (!constructed_cand
)
498 // if we trust the sampling we ran, we terminate now
499 if (options::cegisSample() == options::CegisSampleMode::TRUST
)
501 // we have that the current candidate passed a sample test
502 // since we trust sampling in this mode, we assert there is no
503 // counterexample to the conjecture here.
504 lems
.push_back(d_quant
.negate());
505 recordSolution(candidate_values
);
508 Assert(!d_set_ce_sk_vars
);
510 // immediately skolemize inner existentials
512 // introduce the skolem variables
513 std::vector
<Node
> sks
;
514 std::vector
<Node
> vars
;
515 if (constructed_cand
)
519 Options
& sopts
= smt::currentSmtEngine()->getOptions();
520 std::ostream
& out
= *sopts
.getOut();
521 out
<< "(sygus-candidate ";
522 Assert(d_quant
[0].getNumChildren() == candidate_values
.size());
523 for (unsigned i
= 0, ncands
= candidate_values
.size(); i
< ncands
; i
++)
525 Node v
= candidate_values
[i
];
526 std::stringstream ss
;
527 TermDbSygus::toStreamSygus(ss
, v
);
528 out
<< "(" << d_quant
[0][i
] << " " << ss
.str() << ")";
530 out
<< ")" << std::endl
;
532 if (inst
.getKind() == NOT
&& inst
[0].getKind() == FORALL
)
534 for (const Node
& v
: inst
[0][0])
536 Node sk
= nm
->mkSkolem("rsk", v
.getType());
539 Trace("cegqi-check-debug")
540 << " introduce skolem " << sk
<< " for " << v
<< "\n";
542 query
= inst
[0][1].substitute(
543 vars
.begin(), vars
.end(), sks
.begin(), sks
.end());
544 query
= query
.negate();
548 // use the instance itself
552 d_ce_sk_vars
.insert(d_ce_sk_vars
.end(), sks
.begin(), sks
.end());
553 d_set_ce_sk_vars
= true;
561 // simplify the lemma based on the term database sygus utility
562 query
= d_tds
->rewriteNode(query
);
563 // eagerly unfold applications of evaluation function
564 Trace("cegqi-debug") << "pre-unfold counterexample : " << query
<< std::endl
;
565 // Record the solution, which may be falsified below. We require recording
566 // here since the result of the satisfiability test may be unknown.
567 recordSolution(candidate_values
);
569 if (!query
.isConst() || query
.getConst
<bool>())
571 Trace("sygus-engine") << " *** Verify with subcall..." << std::endl
;
573 checkWithSubsolver(query
.toExpr(), d_ce_sk_vars
, d_ce_sk_var_mvs
);
574 Trace("sygus-engine") << " ...got " << r
<< std::endl
;
575 if (r
.asSatisfiabilityResult().isSat() == Result::SAT
)
577 if (Trace
.isOn("sygus-engine"))
579 Trace("sygus-engine") << " * Verification lemma failed for:\n ";
580 for (unsigned i
= 0, size
= d_ce_sk_vars
.size(); i
< size
; i
++)
582 Trace("sygus-engine")
583 << d_ce_sk_vars
[i
] << " -> " << d_ce_sk_var_mvs
[i
] << " ";
585 Trace("sygus-engine") << std::endl
;
587 if (Configuration::isAssertionBuild())
589 // the values for the query should be a complete model
590 Node squery
= query
.substitute(d_ce_sk_vars
.begin(),
592 d_ce_sk_var_mvs
.begin(),
593 d_ce_sk_var_mvs
.end());
594 Trace("cegqi-debug") << "...squery : " << squery
<< std::endl
;
595 squery
= Rewriter::rewrite(squery
);
596 Trace("cegqi-debug") << "...rewrites to : " << squery
<< std::endl
;
597 Assert(options::sygusRecFun()
598 || (squery
.isConst() && squery
.getConst
<bool>()));
602 else if (r
.asSatisfiabilityResult().isSat() != Result::UNSAT
)
604 // In the rare case that the subcall is unknown, we simply exclude the
605 // solution, without adding a counterexample point. This should only
606 // happen if the quantifier free logic is undecidable.
607 excludeCurrentSolution(terms
, candidate_values
);
608 // We should set incomplete, since a "sat" answer should not be
609 // interpreted as "infeasible", which would make a difference in the rare
610 // case where e.g. we had a finite grammar and exhausted the grammar.
611 d_qe
->getOutputChannel().setIncomplete();
614 // otherwise we are unsat, and we will process the solution below
616 // now mark that we have a solution
617 d_hasSolution
= true;
618 if (options::sygusStream())
620 // immediately print the current solution
621 printAndContinueStream(terms
, candidate_values
);
622 // streaming means now we immediately are looking for a new solution
623 d_hasSolution
= false;
626 // Use lemma to terminate with "unsat", this is justified by the verification
627 // check above, which confirms the synthesis conjecture is solved.
628 lems
.push_back(d_quant
.negate());
632 bool SynthConjecture::checkSideCondition(const std::vector
<Node
>& cvals
) const
634 if (!d_embedSideCondition
.isNull())
636 Node sc
= d_embedSideCondition
.substitute(
637 d_candidates
.begin(), d_candidates
.end(), cvals
.begin(), cvals
.end());
638 Trace("sygus-engine") << "Check side condition..." << std::endl
;
639 Trace("cegqi-debug") << "Check side condition : " << sc
<< std::endl
;
640 Result r
= checkWithSubsolver(sc
);
641 Trace("cegqi-debug") << "...got side condition : " << r
<< std::endl
;
642 if (r
== Result::UNSAT
)
646 Trace("sygus-engine") << "...passed side condition" << std::endl
;
651 bool SynthConjecture::doRefine()
653 std::vector
<Node
> lems
;
654 Assert(d_set_ce_sk_vars
);
656 // first, make skolem substitution
657 Trace("cegqi-refine") << "doRefine : construct skolem substitution..."
659 std::vector
<Node
> sk_vars
;
660 std::vector
<Node
> sk_subs
;
661 // collect the substitution over all disjuncts
662 if (!d_ce_sk_vars
.empty())
664 Trace("cegqi-refine") << "Get model values for skolems..." << std::endl
;
665 Assert(d_inner_vars
.size() == d_ce_sk_vars
.size());
666 if (d_ce_sk_var_mvs
.empty())
668 std::vector
<Node
> model_values
;
669 for (const Node
& v
: d_ce_sk_vars
)
671 Node mv
= getModelValue(v
);
672 Trace("cegqi-refine") << " " << v
<< " -> " << mv
<< std::endl
;
673 model_values
.push_back(mv
);
675 sk_subs
.insert(sk_subs
.end(), model_values
.begin(), model_values
.end());
679 Assert(d_ce_sk_var_mvs
.size() == d_ce_sk_vars
.size());
681 sk_subs
.end(), d_ce_sk_var_mvs
.begin(), d_ce_sk_var_mvs
.end());
683 sk_vars
.insert(sk_vars
.end(), d_inner_vars
.begin(), d_inner_vars
.end());
687 Assert(d_inner_vars
.empty());
690 std::vector
<Node
> lem_c
;
691 Trace("cegqi-refine") << "doRefine : Construct refinement lemma..."
693 Trace("cegqi-refine-debug")
694 << " For counterexample skolems : " << d_ce_sk_vars
<< std::endl
;
696 if (d_base_inst
.getKind() == NOT
&& d_base_inst
[0].getKind() == FORALL
)
698 base_lem
= d_base_inst
[0][1];
702 base_lem
= d_base_inst
.negate();
705 Assert(sk_vars
.size() == sk_subs
.size());
707 Trace("cegqi-refine") << "doRefine : substitute..." << std::endl
;
708 base_lem
= base_lem
.substitute(
709 sk_vars
.begin(), sk_vars
.end(), sk_subs
.begin(), sk_subs
.end());
710 Trace("cegqi-refine") << "doRefine : rewrite..." << std::endl
;
711 base_lem
= d_tds
->rewriteNode(base_lem
);
712 Trace("cegqi-refine") << "doRefine : register refinement lemma " << base_lem
713 << "..." << std::endl
;
714 d_master
->registerRefinementLemma(sk_vars
, base_lem
, lems
);
715 Trace("cegqi-refine") << "doRefine : finished" << std::endl
;
716 d_set_ce_sk_vars
= false;
717 d_ce_sk_vars
.clear();
718 d_ce_sk_var_mvs
.clear();
720 // now send the lemmas
721 bool addedLemma
= false;
722 for (const Node
& lem
: lems
)
724 Trace("cegqi-lemma") << "Cegqi::Lemma : candidate refinement : " << lem
726 bool res
= d_qim
.addPendingLemma(lem
);
729 ++(d_stats
.d_cegqi_lemmas_refine
);
735 Trace("cegqi-warn") << " ...FAILED to add refinement!" << std::endl
;
740 Trace("sygus-engine-debug") << " ...refine candidate." << std::endl
;
744 Trace("sygus-engine-debug") << " ...(warning) failed to refine candidate, "
745 "manually exclude candidate."
747 std::vector
<Node
> cvals
;
748 for (const Node
& c
: d_candidates
)
750 cvals
.push_back(d_cinfo
[c
].d_inst
.back());
752 // something went wrong, exclude the current candidate
753 excludeCurrentSolution(d_candidates
, cvals
);
754 // Note this happens when evaluation is incapable of disproving a candidate
755 // for counterexample point c, but satisfiability checking happened to find
756 // the the same point c is indeed a true counterexample. It is sound
757 // to exclude the candidate in this case.
762 void SynthConjecture::preregisterConjecture(Node q
)
764 d_ceg_si
->preregisterConjecture(q
);
767 bool SynthConjecture::getEnumeratedValues(std::vector
<Node
>& n
,
768 std::vector
<Node
>& v
,
769 bool& activeIncomplete
)
771 std::vector
<Node
> ncheck
= n
;
774 for (unsigned i
= 0, size
= ncheck
.size(); i
< size
; i
++)
777 // if it is not active, we return null
778 Node g
= d_tds
->getActiveGuardForEnumerator(e
);
781 Node gstatus
= d_qe
->getValuation().getSatValue(g
);
782 if (gstatus
.isNull() || !gstatus
.getConst
<bool>())
784 Trace("sygus-engine-debug")
785 << "Enumerator " << e
<< " is inactive." << std::endl
;
789 Node nv
= getEnumeratedValue(e
, activeIncomplete
);
792 ret
= ret
&& !nv
.isNull();
797 Node
SynthConjecture::getEnumeratedValue(Node e
, bool& activeIncomplete
)
799 bool isEnum
= d_tds
->isEnumerator(e
);
801 if (isEnum
&& !e
.getAttribute(SygusSymBreakOkAttribute()))
803 // if the current model value of e was not registered by the datatypes
804 // sygus solver, or was excluded by symmetry breaking, then it does not
805 // have a proper model value that we should consider, thus we return null.
806 Trace("sygus-engine-debug")
807 << "Enumerator " << e
<< " does not have proper model value."
812 if (!isEnum
|| d_tds
->isPassiveEnumerator(e
))
814 return getModelValue(e
);
817 // management of actively generated enumerators goes here
819 // initialize the enumerated value generator for e
820 std::map
<Node
, std::unique_ptr
<EnumValGenerator
> >::iterator iteg
=
822 if (iteg
== d_evg
.end())
824 if (d_tds
->isVariableAgnosticEnumerator(e
))
826 d_evg
[e
].reset(new EnumStreamConcrete(d_tds
));
830 // Actively-generated enumerators are currently either variable agnostic
831 // or basic. The auto mode always prefers the optimized enumerator over
833 Assert(d_tds
->isBasicEnumerator(e
));
834 if (options::sygusActiveGenMode()
835 == options::SygusActiveGenMode::ENUM_BASIC
)
837 d_evg
[e
].reset(new EnumValGeneratorBasic(d_tds
, e
.getType()));
841 Assert(options::sygusActiveGenMode()
842 == options::SygusActiveGenMode::ENUM
843 || options::sygusActiveGenMode()
844 == options::SygusActiveGenMode::AUTO
);
845 d_evg
[e
].reset(new SygusEnumerator(d_tds
, this, d_stats
));
848 Trace("sygus-active-gen")
849 << "Active-gen: initialize for " << e
<< std::endl
;
850 d_evg
[e
]->initialize(e
);
851 d_ev_curr_active_gen
[e
] = Node::null();
852 iteg
= d_evg
.find(e
);
853 Trace("sygus-active-gen-debug") << "...finish" << std::endl
;
855 // if we have a waiting value, return it
856 std::map
<Node
, Node
>::iterator itw
= d_ev_active_gen_waiting
.find(e
);
857 if (itw
!= d_ev_active_gen_waiting
.end())
859 Trace("sygus-active-gen-debug")
860 << "Active-gen: return waiting " << itw
->second
<< std::endl
;
863 // Check if there is an (abstract) value absE we were actively generating
865 Node absE
= d_ev_curr_active_gen
[e
];
866 bool firstTime
= false;
869 // None currently exist. The next abstract value is the model value for e.
870 absE
= getModelValue(e
);
871 if (Trace
.isOn("sygus-active-gen"))
873 Trace("sygus-active-gen") << "Active-gen: new abstract value : ";
874 TermDbSygus::toStreamSygus("sygus-active-gen", e
);
875 Trace("sygus-active-gen") << " -> ";
876 TermDbSygus::toStreamSygus("sygus-active-gen", absE
);
877 Trace("sygus-active-gen") << std::endl
;
879 d_ev_curr_active_gen
[e
] = absE
;
880 iteg
->second
->addValue(absE
);
886 inc
= iteg
->second
->increment();
891 v
= iteg
->second
->getCurrent();
893 Trace("sygus-active-gen-debug") << "...generated " << v
894 << ", with increment success : " << inc
898 // No more concrete values generated from absE.
899 NodeManager
* nm
= NodeManager::currentNM();
900 d_ev_curr_active_gen
[e
] = Node::null();
901 std::vector
<Node
> exp
;
902 // If we are a basic enumerator, a single abstract value maps to *all*
903 // concrete values of its type, thus we don't depend on the current
905 if (!d_tds
->isBasicEnumerator(e
))
907 // We must block e = absE
908 d_tds
->getExplain()->getExplanationForEquality(e
, absE
, exp
);
909 for (unsigned i
= 0, size
= exp
.size(); i
< size
; i
++)
911 exp
[i
] = exp
[i
].negate();
914 Node g
= d_tds
->getActiveGuardForEnumerator(e
);
917 if (d_ev_active_gen_first_val
.find(e
) == d_ev_active_gen_first_val
.end())
919 exp
.push_back(g
.negate());
920 d_ev_active_gen_first_val
[e
] = absE
;
927 Node lem
= exp
.size() == 1 ? exp
[0] : nm
->mkNode(OR
, exp
);
928 Trace("cegqi-lemma") << "Cegqi::Lemma : actively-generated enumerator "
929 "exclude current solution : "
931 if (Trace
.isOn("sygus-active-gen-debug"))
933 Trace("sygus-active-gen-debug") << "Active-gen: block ";
934 TermDbSygus::toStreamSygus("sygus-active-gen-debug", absE
);
935 Trace("sygus-active-gen-debug") << std::endl
;
937 d_qe
->getOutputChannel().lemma(lem
);
941 // We are waiting to send e -> v to the module that requested it.
944 activeIncomplete
= true;
948 d_ev_active_gen_waiting
[e
] = v
;
950 if (Trace
.isOn("sygus-active-gen"))
952 Trace("sygus-active-gen") << "Active-gen : " << e
<< " : ";
953 TermDbSygus::toStreamSygus("sygus-active-gen", absE
);
954 Trace("sygus-active-gen") << " -> ";
955 TermDbSygus::toStreamSygus("sygus-active-gen", v
);
956 Trace("sygus-active-gen") << std::endl
;
963 Node
SynthConjecture::getModelValue(Node n
)
965 Trace("cegqi-mv") << "getModelValue for : " << n
<< std::endl
;
966 return d_qe
->getModel()->getValue(n
);
969 void SynthConjecture::debugPrint(const char* c
)
971 Trace(c
) << "Synthesis conjecture : " << d_embed_quant
<< std::endl
;
972 Trace(c
) << " * Candidate programs : " << d_candidates
<< std::endl
;
973 Trace(c
) << " * Counterexample skolems : " << d_ce_sk_vars
<< std::endl
;
976 void SynthConjecture::printAndContinueStream(const std::vector
<Node
>& enums
,
977 const std::vector
<Node
>& values
)
979 Assert(d_master
!= nullptr);
980 // we have generated a solution, print it
981 // get the current output stream
982 Options
& sopts
= smt::currentSmtEngine()->getOptions();
983 printSynthSolution(*sopts
.getOut());
984 excludeCurrentSolution(enums
, values
);
987 void SynthConjecture::excludeCurrentSolution(const std::vector
<Node
>& enums
,
988 const std::vector
<Node
>& values
)
990 Trace("cegqi-debug") << "Exclude current solution: " << enums
<< " / "
991 << values
<< std::endl
;
992 // We will not refine the current candidate solution since it is a solution
993 // thus, we clear information regarding the current refinement
994 d_set_ce_sk_vars
= false;
995 d_ce_sk_vars
.clear();
996 d_ce_sk_var_mvs
.clear();
997 // However, we need to exclude the current solution using an explicit
998 // blocking clause, so that we proceed to the next solution. We do this only
999 // for passively-generated enumerators (TermDbSygus::isPassiveEnumerator).
1000 std::vector
<Node
> exp
;
1001 for (unsigned i
= 0, tsize
= enums
.size(); i
< tsize
; i
++)
1003 Node cprog
= enums
[i
];
1004 Assert(d_tds
->isEnumerator(cprog
));
1005 if (d_tds
->isPassiveEnumerator(cprog
))
1007 Node cval
= values
[i
];
1008 // add to explanation of exclusion
1009 d_tds
->getExplain()->getExplanationForEquality(cprog
, cval
, exp
);
1014 if (!d_guarded_stream_exc
)
1016 d_guarded_stream_exc
= true;
1017 exp
.push_back(d_feasible_guard
);
1019 Node exc_lem
= exp
.size() == 1
1021 : NodeManager::currentNM()->mkNode(kind::AND
, exp
);
1022 exc_lem
= exc_lem
.negate();
1023 Trace("cegqi-lemma") << "Cegqi::Lemma : stream exclude current solution : "
1024 << exc_lem
<< std::endl
;
1025 d_qe
->getOutputChannel().lemma(exc_lem
);
1029 void SynthConjecture::printSynthSolution(std::ostream
& out
)
1031 Trace("cegqi-sol-debug") << "Printing synth solution..." << std::endl
;
1032 Assert(d_quant
[0].getNumChildren() == d_embed_quant
[0].getNumChildren());
1033 std::vector
<Node
> sols
;
1034 std::vector
<int> statuses
;
1035 if (!getSynthSolutionsInternal(sols
, statuses
))
1039 NodeManager
* nm
= NodeManager::currentNM();
1040 for (unsigned i
= 0, size
= d_embed_quant
[0].getNumChildren(); i
< size
; i
++)
1045 Node prog
= d_embed_quant
[0][i
];
1046 int status
= statuses
[i
];
1047 TypeNode tn
= prog
.getType();
1048 const DType
& dt
= tn
.getDType();
1049 std::stringstream ss
;
1051 std::string
f(ss
.str());
1053 ++(d_stats
.d_solutions
);
1055 bool is_unique_term
= true;
1058 && (options::sygusRewSynth() || options::sygusQueryGen()
1059 || options::sygusFilterSolMode()
1060 != options::SygusFilterSolMode::NONE
))
1062 Trace("cegqi-sol-debug") << "Run expression mining..." << std::endl
;
1063 std::map
<Node
, ExpressionMinerManager
>::iterator its
=
1065 if (its
== d_exprm
.end())
1067 d_exprm
[prog
].initializeSygus(
1068 d_qe
, d_candidates
[i
], options::sygusSamples(), true);
1069 if (options::sygusRewSynth())
1071 d_exprm
[prog
].enableRewriteRuleSynth();
1073 if (options::sygusQueryGen())
1075 d_exprm
[prog
].enableQueryGeneration(options::sygusQueryGenThresh());
1077 if (options::sygusFilterSolMode()
1078 != options::SygusFilterSolMode::NONE
)
1080 if (options::sygusFilterSolMode()
1081 == options::SygusFilterSolMode::STRONG
)
1083 d_exprm
[prog
].enableFilterStrongSolutions();
1085 else if (options::sygusFilterSolMode()
1086 == options::SygusFilterSolMode::WEAK
)
1088 d_exprm
[prog
].enableFilterWeakSolutions();
1091 its
= d_exprm
.find(prog
);
1093 bool rew_print
= false;
1094 is_unique_term
= d_exprm
[prog
].addTerm(sol
, out
, rew_print
);
1097 ++(d_stats
.d_candidate_rewrites_print
);
1099 if (!is_unique_term
)
1101 ++(d_stats
.d_filtered_solutions
);
1106 out
<< "(define-fun " << f
<< " ";
1107 // Only include variables that are truly bound variables of the
1108 // function-to-synthesize. This means we exclude variables that encode
1109 // external terms. This ensures that --sygus-stream prints
1110 // solutions with no arguments on the predicate for responses to
1111 // the get-abduct command.
1112 // pvs stores the variables that will be printed in the argument list
1114 std::vector
<Node
> pvs
;
1115 Node vl
= dt
.getSygusVarList();
1118 Assert(vl
.getKind() == BOUND_VAR_LIST
);
1119 SygusVarToTermAttribute sta
;
1120 for (const Node
& v
: vl
)
1122 if (!v
.hasAttribute(sta
))
1134 vl
= nm
->mkNode(BOUND_VAR_LIST
, pvs
);
1137 out
<< dt
.getSygusType() << " ";
1144 Node bsol
= datatypes::utils::sygusToBuiltin(sol
, true);
1147 out
<< ")" << std::endl
;
1153 bool SynthConjecture::getSynthSolutions(
1154 std::map
<Node
, std::map
<Node
, Node
> >& sol_map
)
1156 NodeManager
* nm
= NodeManager::currentNM();
1157 std::vector
<Node
> sols
;
1158 std::vector
<int> statuses
;
1159 Trace("cegqi-debug") << "getSynthSolutions..." << std::endl
;
1160 if (!getSynthSolutionsInternal(sols
, statuses
))
1162 Trace("cegqi-debug") << "...failed internal" << std::endl
;
1165 // we add it to the solution map, indexed by this conjecture
1166 std::map
<Node
, Node
>& smc
= sol_map
[d_quant
];
1167 for (unsigned i
= 0, size
= d_embed_quant
[0].getNumChildren(); i
< size
; i
++)
1170 int status
= statuses
[i
];
1171 Trace("cegqi-debug") << "...got " << i
<< ": " << sol
1172 << ", status=" << status
<< std::endl
;
1173 // get the builtin solution
1177 // Convert sygus to builtin here.
1178 // We must use the external representation to ensure bsol matches the
1180 bsol
= datatypes::utils::sygusToBuiltin(sol
, true);
1182 // convert to lambda
1183 TypeNode tn
= d_embed_quant
[0][i
].getType();
1184 const DType
& dt
= tn
.getDType();
1185 Node fvar
= d_quant
[0][i
];
1186 Node bvl
= dt
.getSygusVarList();
1189 // since we don't have function subtyping, this assertion should only
1190 // check the return type
1191 Assert(fvar
.getType().isFunction());
1192 Assert(fvar
.getType().getRangeType().isComparableTo(bsol
.getType()));
1193 bsol
= nm
->mkNode(LAMBDA
, bvl
, bsol
);
1197 Assert(fvar
.getType().isComparableTo(bsol
.getType()));
1201 Trace("cegqi-debug") << "...return " << bsol
<< std::endl
;
1206 void SynthConjecture::recordSolution(std::vector
<Node
>& vs
)
1208 Assert(vs
.size() == d_candidates
.size());
1210 for (unsigned i
= 0; i
< vs
.size(); i
++)
1212 d_cinfo
[d_candidates
[i
]].d_inst
.push_back(vs
[i
]);
1216 bool SynthConjecture::getSynthSolutionsInternal(std::vector
<Node
>& sols
,
1217 std::vector
<int>& statuses
)
1223 for (unsigned i
= 0, size
= d_embed_quant
[0].getNumChildren(); i
< size
; i
++)
1225 Node prog
= d_embed_quant
[0][i
];
1226 Trace("cegqi-debug") << " get solution for " << prog
<< std::endl
;
1227 TypeNode tn
= prog
.getType();
1228 Assert(tn
.isDatatype());
1232 if (isSingleInvocation())
1234 Assert(d_ceg_si
!= NULL
);
1235 sol
= d_ceg_si
->getSolution(i
, tn
, status
, true);
1240 sol
= sol
.getKind() == LAMBDA
? sol
[1] : sol
;
1244 Node cprog
= d_candidates
[i
];
1245 if (!d_cinfo
[cprog
].d_inst
.empty())
1247 // the solution is just the last instantiated term
1248 sol
= d_cinfo
[cprog
].d_inst
.back();
1251 // check if there was a template
1252 Node sf
= d_quant
[0][i
];
1253 Node templ
= d_templInfer
->getTemplate(sf
);
1254 if (!templ
.isNull())
1256 Trace("cegqi-inv-debug")
1257 << sf
<< " used template : " << templ
<< std::endl
;
1258 // if it was not embedded into the grammar
1259 if (!options::sygusTemplEmbedGrammar())
1261 TNode templa
= d_templInfer
->getTemplateArg(sf
);
1262 // make the builtin version of the full solution
1263 sol
= d_tds
->sygusToBuiltin(sol
, sol
.getType());
1264 Trace("cegqi-inv") << "Builtin version of solution is : " << sol
1265 << ", type : " << sol
.getType() << std::endl
;
1267 sol
= templ
.substitute(templa
, tsol
);
1268 Trace("cegqi-inv-debug") << "With template : " << sol
<< std::endl
;
1269 sol
= Rewriter::rewrite(sol
);
1270 Trace("cegqi-inv-debug") << "Simplified : " << sol
<< std::endl
;
1271 // now, reconstruct to the syntax
1272 sol
= d_ceg_si
->reconstructToSyntax(sol
, tn
, status
, true);
1273 sol
= sol
.getKind() == LAMBDA
? sol
[1] : sol
;
1274 Trace("cegqi-inv-debug")
1275 << "Reconstructed to syntax : " << sol
<< std::endl
;
1279 Trace("cegqi-inv-debug")
1280 << "...was embedding into grammar." << std::endl
;
1285 Trace("cegqi-inv-debug")
1286 << sf
<< " did not use template" << std::endl
;
1291 Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
1292 "syntax-guided solution!"
1296 sols
.push_back(sol
);
1297 statuses
.push_back(status
);
1302 Node
SynthConjecture::getSymmetryBreakingPredicate(
1303 Node x
, Node e
, TypeNode tn
, unsigned tindex
, unsigned depth
)
1305 std::vector
<Node
> sb_lemmas
;
1307 // based on simple preprocessing
1309 d_ceg_proc
->getSymmetryBreakingPredicate(x
, e
, tn
, tindex
, depth
);
1310 if (!ppred
.isNull())
1312 sb_lemmas
.push_back(ppred
);
1315 // other static conjecture-dependent symmetry breaking goes here
1317 if (!sb_lemmas
.empty())
1319 return sb_lemmas
.size() == 1
1321 : NodeManager::currentNM()->mkNode(kind::AND
, sb_lemmas
);
1325 return Node::null();
1329 ExampleEvalCache
* SynthConjecture::getExampleEvalCache(Node e
)
1331 std::map
<Node
, std::unique_ptr
<ExampleEvalCache
> >::iterator it
=
1332 d_exampleEvalCache
.find(e
);
1333 if (it
!= d_exampleEvalCache
.end())
1335 return it
->second
.get();
1337 Node f
= d_tds
->getSynthFunForEnumerator(e
);
1338 // if f does not have examples, we don't construct the utility
1339 if (!d_exampleInfer
->hasExamples(f
) || d_exampleInfer
->getNumExamples(f
) == 0)
1341 d_exampleEvalCache
[e
].reset(nullptr);
1344 d_exampleEvalCache
[e
].reset(new ExampleEvalCache(d_tds
, this, f
, e
));
1345 return d_exampleEvalCache
[e
].get();
1348 } // namespace quantifiers
1349 } // namespace theory
1350 } /* namespace CVC4 */