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