GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 522 587 88.9 %
Date: 2021-09-29 Branches: 1052 2566 41.0 %

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