GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 594 677 87.7 %
Date: 2021-08-01 Branches: 1233 3020 40.8 %

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