GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 579 662 87.5 %
Date: 2021-08-06 Branches: 1210 2980 40.6 %

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