GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 525 590 89.0 %
Date: 2021-09-30 Branches: 1067 2592 41.2 %

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