GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 524 589 89.0 %
Date: 2021-09-07 Branches: 1047 2556 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
1191
SynthConjecture::SynthConjecture(QuantifiersState& qs,
49
                                 QuantifiersInferenceManager& qim,
50
                                 QuantifiersRegistry& qr,
51
                                 TermRegistry& tr,
52
1191
                                 SygusStatistics& s)
53
    : d_qstate(qs),
54
      d_qim(qim),
55
      d_qreg(qr),
56
      d_treg(tr),
57
      d_stats(s),
58
1191
      d_tds(tr.getTermDatabaseSygus()),
59
      d_verify(qs.options(), qs.getLogicInfo(), d_tds),
60
      d_hasSolution(false),
61
2382
      d_ceg_si(new CegSingleInv(qs.getEnv(), tr, s)),
62
1191
      d_templInfer(new SygusTemplateInfer),
63
1191
      d_ceg_proc(new SynthConjectureProcess),
64
1191
      d_ceg_gc(new CegGrammarConstructor(d_tds, this)),
65
2382
      d_sygus_rconst(new SygusRepairConst(qs.getEnv(), d_tds)),
66
1191
      d_exampleInfer(new ExampleInfer(d_tds)),
67
1191
      d_ceg_pbe(new SygusPbe(qs, qim, d_tds, this)),
68
1191
      d_ceg_cegis(new Cegis(qs, qim, d_tds, this)),
69
1191
      d_ceg_cegisUnif(new CegisUnif(qs, qim, d_tds, this)),
70
1191
      d_sygus_ccore(new CegisCoreConnective(qs, qim, d_tds, this)),
71
      d_master(nullptr),
72
      d_set_ce_sk_vars(false),
73
      d_repair_index(0),
74
16674
      d_guarded_stream_exc(false)
75
{
76
1191
  if (options::sygusSymBreakPbe() || options::sygusUnifPbe())
77
  {
78
1191
    d_modules.push_back(d_ceg_pbe.get());
79
  }
80
1191
  if (options::sygusUnifPi() != options::SygusUnifPiMode::NONE)
81
  {
82
21
    d_modules.push_back(d_ceg_cegisUnif.get());
83
  }
84
1191
  if (options::sygusCoreConnective())
85
  {
86
67
    d_modules.push_back(d_sygus_ccore.get());
87
  }
88
1191
  d_modules.push_back(d_ceg_cegis.get());
89
1191
}
90
91
1189
SynthConjecture::~SynthConjecture() {}
92
93
1173
void SynthConjecture::presolve()
94
{
95
  // we don't have a solution yet
96
1173
  d_hasSolution = false;
97
1173
}
98
99
330
void SynthConjecture::assign(Node q)
100
{
101
330
  Assert(d_embed_quant.isNull());
102
330
  Assert(q.getKind() == FORALL);
103
330
  Trace("cegqi") << "SynthConjecture : assign : " << q << std::endl;
104
330
  d_quant = q;
105
330
  NodeManager* nm = NodeManager::currentNM();
106
330
  SkolemManager* sm = nm->getSkolemManager();
107
108
  // initialize the guard
109
330
  d_feasible_guard = sm->mkDummySkolem("G", nm->booleanType());
110
330
  d_feasible_guard = Rewriter::rewrite(d_feasible_guard);
111
330
  d_feasible_guard = d_qstate.getValuation().ensureLiteral(d_feasible_guard);
112
330
  AlwaysAssert(!d_feasible_guard.isNull());
113
114
  // pre-simplify the quantified formula based on the process utility
115
330
  d_simp_quant = d_ceg_proc->preSimplify(d_quant);
116
117
  // compute its attributes
118
658
  QAttributes qa;
119
330
  QuantAttributes::computeQuantAttributes(q, qa);
120
121
658
  std::map<Node, Node> templates;
122
658
  std::map<Node, Node> templates_arg;
123
  // register with single invocation if applicable
124
330
  if (qa.d_sygus)
125
  {
126
330
    d_ceg_si->initialize(d_simp_quant);
127
330
    d_simp_quant = d_ceg_si->getSimplifiedConjecture();
128
330
    if (!d_ceg_si->isSingleInvocation())
129
    {
130
330
      d_templInfer->initialize(d_simp_quant);
131
    }
132
    // carry the templates
133
833
    for (const Node& v : q[0])
134
    {
135
1006
      Node templ = d_templInfer->getTemplate(v);
136
503
      if (!templ.isNull())
137
      {
138
31
        templates[v] = templ;
139
31
        templates_arg[v] = d_templInfer->getTemplateArg(v);
140
      }
141
    }
142
  }
143
144
  // post-simplify the quantified formula based on the process utility
145
330
  d_simp_quant = d_ceg_proc->postSimplify(d_simp_quant);
146
147
  // finished simplifying the quantified formula at this point
148
149
  // convert to deep embedding and finalize single invocation here
150
330
  d_embed_quant = d_ceg_gc->process(d_simp_quant, templates, templates_arg);
151
660
  Trace("cegqi") << "SynthConjecture : converted to embedding : "
152
330
                 << d_embed_quant << std::endl;
153
154
658
  Node sc = qa.d_sygusSideCondition;
155
330
  if (!sc.isNull())
156
  {
157
    // convert to deep embedding
158
16
    d_embedSideCondition = d_ceg_gc->convertToEmbedding(sc);
159
32
    Trace("cegqi") << "SynthConjecture : side condition : "
160
16
                   << d_embedSideCondition << std::endl;
161
  }
162
163
  // we now finalize the single invocation module, based on the syntax
164
  // restrictions
165
330
  if (qa.d_sygus)
166
  {
167
330
    d_ceg_si->finishInit(d_ceg_gc->isSyntaxRestricted());
168
  }
169
170
330
  Assert(d_candidates.empty());
171
658
  std::vector<Node> vars;
172
833
  for (unsigned i = 0; i < d_embed_quant[0].getNumChildren(); i++)
173
  {
174
503
    vars.push_back(d_embed_quant[0][i]);
175
1006
    Node e = sm->mkDummySkolem("e", d_embed_quant[0][i].getType());
176
503
    d_candidates.push_back(e);
177
  }
178
660
  Trace("cegqi") << "Base quantified formula is : " << d_embed_quant
179
330
                 << std::endl;
180
  // construct base instantiation
181
660
  d_base_inst = Rewriter::rewrite(d_qim.getInstantiate()->getInstantiation(
182
330
      d_embed_quant, vars, d_candidates));
183
330
  if (!d_embedSideCondition.isNull() && !vars.empty())
184
  {
185
32
    d_embedSideCondition = d_embedSideCondition.substitute(
186
16
        vars.begin(), vars.end(), d_candidates.begin(), d_candidates.end());
187
  }
188
330
  Trace("cegqi") << "Base instantiation is :      " << d_base_inst << std::endl;
189
190
  // initialize the sygus constant repair utility
191
330
  if (options::sygusRepairConst())
192
  {
193
8
    d_sygus_rconst->initialize(d_base_inst.negate(), d_candidates);
194
8
    if (options::sygusConstRepairAbort())
195
    {
196
      if (!d_sygus_rconst->isActive())
197
      {
198
        // no constant repair is possible: abort
199
        std::stringstream ss;
200
        ss << "Grammar does not allow repair constants." << std::endl;
201
        throw LogicException(ss.str());
202
      }
203
    }
204
  }
205
  // initialize the example inference utility
206
330
  if (!d_exampleInfer->initialize(d_base_inst, d_candidates))
207
  {
208
    // there is a contradictory example pair, the conjecture is infeasible.
209
4
    Node infLem = d_feasible_guard.negate();
210
2
    d_qim.lemma(infLem, InferenceId::QUANTIFIERS_SYGUS_EXAMPLE_INFER_CONTRA);
211
    // we don't need to continue initialization in this case
212
2
    return;
213
  }
214
215
  // register this term with sygus database and other utilities that impact
216
  // the enumerative sygus search
217
328
  if (!isSingleInvocation())
218
  {
219
241
    d_ceg_proc->initialize(d_base_inst, d_candidates);
220
440
    for (unsigned i = 0, size = d_modules.size(); i < size; i++)
221
    {
222
440
      if (d_modules[i]->initialize(d_simp_quant, d_base_inst, d_candidates))
223
      {
224
241
        d_master = d_modules[i];
225
241
        break;
226
      }
227
    }
228
241
    Assert(d_master != nullptr);
229
  }
230
231
328
  Assert(d_qreg.getQuantAttributes().isSygus(q));
232
  // if the base instantiation is an existential, store its variables
233
328
  if (d_base_inst.getKind() == NOT && d_base_inst[0].getKind() == FORALL)
234
  {
235
979
    for (const Node& v : d_base_inst[0][0])
236
    {
237
795
      d_inner_vars.push_back(v);
238
    }
239
  }
240
241
  // register the strategy
242
984
  d_feasible_strategy.reset(
243
      new DecisionStrategySingleton("sygus_feasible",
244
                                    d_feasible_guard,
245
328
                                    d_qstate.getSatContext(),
246
656
                                    d_qstate.getValuation()));
247
328
  d_qim.getDecisionManager()->registerStrategy(
248
      DecisionManager::STRAT_QUANT_SYGUS_FEASIBLE, d_feasible_strategy.get());
249
  // this must be called, both to ensure that the feasible guard is
250
  // decided on with true polariy, but also to ensure that output channel
251
  // has been used on this call to check.
252
328
  d_qim.requirePhase(d_feasible_guard, true);
253
254
656
  Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation()
255
328
                 << std::endl;
256
}
257
258
4135
Node SynthConjecture::getGuard() const { return d_feasible_guard; }
259
260
41198
bool SynthConjecture::isSingleInvocation() const
261
{
262
41198
  return d_ceg_si->isSingleInvocation();
263
}
264
265
4797
bool SynthConjecture::needsCheck()
266
{
267
  bool value;
268
4797
  Assert(!d_feasible_guard.isNull());
269
  // non or fully single invocation : look at guard only
270
4797
  if (d_qstate.getValuation().hasSatValue(d_feasible_guard, value))
271
  {
272
4797
    if (!value)
273
    {
274
8
      Trace("sygus-engine-debug") << "Conjecture is infeasible." << std::endl;
275
8
      Warning() << "Warning : the SyGuS conjecture may be infeasible"
276
                << std::endl;
277
8
      return false;
278
    }
279
    else
280
    {
281
9578
      Trace("sygus-engine-debug") << "Feasible guard " << d_feasible_guard
282
4789
                                  << " assigned true." << std::endl;
283
    }
284
  }
285
  else
286
  {
287
    Trace("cegqi-warn") << "WARNING: Guard " << d_feasible_guard
288
                        << " is not assigned!" << std::endl;
289
    Assert(false);
290
  }
291
4789
  return true;
292
}
293
294
110321
bool SynthConjecture::needsRefinement() const { return d_set_ce_sk_vars; }
295
39096
bool SynthConjecture::doCheck()
296
{
297
39096
  if (isSingleInvocation())
298
  {
299
    // We now try to solve with the single invocation solver, which may or may
300
    // not succeed in solving the conjecture. In either case,  we are done and
301
    // return true.
302
79
    if (d_ceg_si->solve())
303
    {
304
71
      d_hasSolution = true;
305
      // the conjecture has a solution, so its negation holds
306
142
      Node qn = d_quant.negate();
307
71
      d_qim.addPendingLemma(qn, InferenceId::QUANTIFIERS_SYGUS_SI_SOLVED);
308
    }
309
79
    return true;
310
  }
311
39017
  Assert(d_master != nullptr);
312
39017
  Assert(!d_hasSolution);
313
314
  // get the list of terms that the master strategy is interested in
315
78034
  std::vector<Node> terms;
316
39017
  d_master->getTermList(d_candidates, terms);
317
318
39017
  Assert(!d_candidates.empty());
319
320
78034
  Trace("cegqi-check") << "CegConjuncture : check, build candidates..."
321
39017
                       << std::endl;
322
78034
  std::vector<Node> candidate_values;
323
39017
  bool constructed_cand = false;
324
325
  // If a module is not trying to repair constants in solutions and the option
326
  // sygusRepairConst  is true, we use a default scheme for trying to repair
327
  // constants here.
328
  bool doRepairConst =
329
39017
      options::sygusRepairConst() && !d_master->usingRepairConst();
330
39017
  if (doRepairConst)
331
  {
332
    // have we tried to repair the previous solution?
333
    // if not, call the repair constant utility
334
    unsigned ninst = d_cinfo[d_candidates[0]].d_inst.size();
335
    if (d_repair_index < ninst)
336
    {
337
      std::vector<Node> fail_cvs;
338
      for (const Node& cprog : d_candidates)
339
      {
340
        Assert(d_repair_index < d_cinfo[cprog].d_inst.size());
341
        fail_cvs.push_back(d_cinfo[cprog].d_inst[d_repair_index]);
342
      }
343
      if (Trace.isOn("sygus-engine"))
344
      {
345
        Trace("sygus-engine") << "CegConjuncture : repair previous solution ";
346
        for (const Node& fc : fail_cvs)
347
        {
348
          std::stringstream ss;
349
          TermDbSygus::toStreamSygus(ss, fc);
350
          Trace("sygus-engine") << ss.str() << " ";
351
        }
352
        Trace("sygus-engine") << std::endl;
353
      }
354
      d_repair_index++;
355
      if (d_sygus_rconst->repairSolution(
356
              d_candidates, fail_cvs, candidate_values, true))
357
      {
358
        constructed_cand = true;
359
      }
360
    }
361
  }
362
363
39017
  bool printDebug = Output.isOn(options::OutputTag::SYGUS);
364
39017
  if (!constructed_cand)
365
  {
366
    // get the model value of the relevant terms from the master module
367
55800
    std::vector<Node> enum_values;
368
39017
    bool activeIncomplete = false;
369
39017
    bool fullModel = getEnumeratedValues(terms, enum_values, activeIncomplete);
370
371
    // if the master requires a full model and the model is partial, we fail
372
39008
    if (!d_master->allowPartialModel() && !fullModel)
373
    {
374
      // we retain the values in d_ev_active_gen_waiting
375
19006
      Trace("sygus-engine-debug") << "...partial model, fail." << std::endl;
376
      // if we are partial due to an active enumerator, we may still succeed
377
      // on the next call
378
19006
      return !activeIncomplete;
379
    }
380
    // determine if we had at least one value for an enumerator
381
20002
    Assert(terms.size() == enum_values.size());
382
20002
    bool modelSuccess = false;
383
45234
    for (unsigned i = 0, size = terms.size(); i < size; i++)
384
    {
385
25232
      if (!enum_values[i].isNull())
386
      {
387
21562
        modelSuccess = true;
388
      }
389
    }
390
20002
    if (modelSuccess)
391
    {
392
      // Must separately compute whether trace is on due to compilation of
393
      // Trace.isOn.
394
16774
      bool traceIsOn = Trace.isOn("sygus-engine");
395
16774
      if (printDebug || traceIsOn)
396
      {
397
2
        Trace("sygus-engine") << "  * Value is : ";
398
4
        std::stringstream sygusEnumOut;
399
2
        FirstOrderModel* m = d_treg.getModel();
400
4
        for (unsigned i = 0, size = terms.size(); i < size; i++)
401
        {
402
4
          Node nv = enum_values[i];
403
4
          Node onv = nv.isNull() ? m->getValue(terms[i]) : nv;
404
4
          TypeNode tn = onv.getType();
405
4
          std::stringstream ss;
406
2
          TermDbSygus::toStreamSygus(ss, onv);
407
2
          if (printDebug)
408
          {
409
2
            sygusEnumOut << " " << ss.str();
410
          }
411
2
          Trace("sygus-engine") << terms[i] << " -> ";
412
2
          if (nv.isNull())
413
          {
414
            Trace("sygus-engine") << "[EXC: " << ss.str() << "] ";
415
          }
416
          else
417
          {
418
2
            Trace("sygus-engine") << ss.str() << " ";
419
2
            if (Trace.isOn("sygus-engine-rr"))
420
            {
421
              Node bv = d_tds->sygusToBuiltin(nv, tn);
422
              bv = Rewriter::rewrite(bv);
423
              Trace("sygus-engine-rr") << " -> " << bv << std::endl;
424
            }
425
          }
426
        }
427
2
        Trace("sygus-engine") << std::endl;
428
4
        Output(options::OutputTag::SYGUS)
429
2
            << "(sygus-enum" << sygusEnumOut.str() << ")" << std::endl;
430
      }
431
16774
      Assert(candidate_values.empty());
432
33548
      constructed_cand = d_master->constructCandidates(
433
16774
          terms, enum_values, d_candidates, candidate_values);
434
    }
435
    // notify the enumerator managers of the status of the candidate
436
25652
    for (std::pair<const Node, std::unique_ptr<EnumValueManager>>& ecp :
437
20002
         d_enumManager)
438
    {
439
25652
      ecp.second->notifyCandidate(modelSuccess);
440
    }
441
    // if we did not generate a candidate, return now
442
20002
    if (!modelSuccess)
443
    {
444
3228
      Trace("sygus-engine-debug") << "...empty model, fail." << std::endl;
445
3228
      return !activeIncomplete;
446
    }
447
  }
448
449
16774
  NodeManager* nm = NodeManager::currentNM();
450
16774
  SkolemManager* sm = nm->getSkolemManager();
451
452
  // check the side condition if we constructed a candidate
453
16774
  if (constructed_cand)
454
  {
455
1790
    if (!checkSideCondition(candidate_values))
456
    {
457
70
      excludeCurrentSolution(terms, candidate_values);
458
70
      Trace("sygus-engine") << "...failed side condition" << std::endl;
459
70
      return false;
460
    }
461
  }
462
463
  // must get a counterexample to the value of the current candidate
464
33408
  Node inst;
465
16704
  if (constructed_cand)
466
  {
467
1720
    if (Trace.isOn("cegqi-check"))
468
    {
469
      Trace("cegqi-check") << "CegConjuncture : check candidate : "
470
                           << std::endl;
471
      for (unsigned i = 0, size = candidate_values.size(); i < size; i++)
472
      {
473
        Trace("cegqi-check") << "  " << i << " : " << d_candidates[i] << " -> "
474
                             << candidate_values[i] << std::endl;
475
      }
476
    }
477
1720
    Assert(candidate_values.size() == d_candidates.size());
478
1720
    inst = d_base_inst.substitute(d_candidates.begin(),
479
                                  d_candidates.end(),
480
                                  candidate_values.begin(),
481
                                  candidate_values.end());
482
  }
483
  else
484
  {
485
14984
    inst = d_base_inst;
486
  }
487
488
16704
  if (!constructed_cand)
489
  {
490
14984
    return false;
491
  }
492
493
  // if we trust the sampling we ran, we terminate now
494
1720
  if (options::cegisSample() == options::CegisSampleMode::TRUST)
495
  {
496
    // we have that the current candidate passed a sample test
497
    // since we trust sampling in this mode, we assert there is no
498
    // counterexample to the conjecture here.
499
2
    Node qn = d_quant.negate();
500
1
    d_qim.addPendingLemma(qn,
501
                          InferenceId::QUANTIFIERS_SYGUS_SAMPLE_TRUST_SOLVED);
502
1
    recordSolution(candidate_values);
503
1
    return true;
504
  }
505
1719
  Assert(!d_set_ce_sk_vars);
506
507
  // immediately skolemize inner existentials
508
3438
  Node query;
509
  // introduce the skolem variables
510
3438
  std::vector<Node> sks;
511
3438
  std::vector<Node> vars;
512
1719
  if (constructed_cand)
513
  {
514
1719
    if (printDebug)
515
    {
516
2
      const Options& sopts = d_qstate.options();
517
2
      std::ostream& out = *sopts.base.out;
518
2
      out << "(sygus-candidate ";
519
2
      Assert(d_quant[0].getNumChildren() == candidate_values.size());
520
4
      for (unsigned i = 0, ncands = candidate_values.size(); i < ncands; i++)
521
      {
522
4
        Node v = candidate_values[i];
523
4
        std::stringstream ss;
524
2
        TermDbSygus::toStreamSygus(ss, v);
525
2
        out << "(" << d_quant[0][i] << " " << ss.str() << ")";
526
      }
527
2
      out << ")" << std::endl;
528
    }
529
1719
    if (inst.getKind() == NOT && inst[0].getKind() == FORALL)
530
    {
531
3166
      for (const Node& v : inst[0][0])
532
      {
533
5406
        Node sk = sm->mkDummySkolem("rsk", v.getType());
534
2703
        sks.push_back(sk);
535
2703
        vars.push_back(v);
536
5406
        Trace("cegqi-check-debug")
537
2703
            << "  introduce skolem " << sk << " for " << v << "\n";
538
      }
539
463
      query = inst[0][1].substitute(
540
          vars.begin(), vars.end(), sks.begin(), sks.end());
541
463
      query = query.negate();
542
    }
543
    else
544
    {
545
      // use the instance itself
546
1256
      query = inst;
547
    }
548
  }
549
1719
  d_ce_sk_vars.insert(d_ce_sk_vars.end(), sks.begin(), sks.end());
550
1719
  d_set_ce_sk_vars = true;
551
552
1719
  if (query.isNull())
553
  {
554
    // no lemma to check
555
    return false;
556
  }
557
558
  // Record the solution, which may be falsified below. We require recording
559
  // here since the result of the satisfiability test may be unknown.
560
1719
  recordSolution(candidate_values);
561
562
3438
  Result r = d_verify.verify(query, d_ce_sk_vars, d_ce_sk_var_mvs);
563
564
1719
  if (r.asSatisfiabilityResult().isSat() == Result::SAT)
565
  {
566
    // we have a counterexample
567
424
    return false;
568
  }
569
1295
  else if (r.asSatisfiabilityResult().isSat() != Result::UNSAT)
570
  {
571
    // In the rare case that the subcall is unknown, we simply exclude the
572
    // solution, without adding a counterexample point. This should only
573
    // happen if the quantifier free logic is undecidable.
574
36
    excludeCurrentSolution(terms, candidate_values);
575
    // We should set incomplete, since a "sat" answer should not be
576
    // interpreted as "infeasible", which would make a difference in the rare
577
    // case where e.g. we had a finite grammar and exhausted the grammar.
578
36
    d_qim.setIncomplete(IncompleteId::QUANTIFIERS_SYGUS_NO_VERIFY);
579
36
    return false;
580
  }
581
  // otherwise we are unsat, and we will process the solution below
582
583
  // now mark that we have a solution
584
1259
  d_hasSolution = true;
585
1259
  if (options::sygusStream())
586
  {
587
    // immediately print the current solution
588
1068
    printAndContinueStream(terms, candidate_values);
589
    // streaming means now we immediately are looking for a new solution
590
1068
    d_hasSolution = false;
591
1068
    return false;
592
  }
593
  // Use lemma to terminate with "unsat", this is justified by the verification
594
  // check above, which confirms the synthesis conjecture is solved.
595
382
  Node qn = d_quant.negate();
596
191
  d_qim.addPendingLemma(qn, InferenceId::QUANTIFIERS_SYGUS_VERIFY_SOLVED);
597
191
  return true;
598
}
599
600
1790
bool SynthConjecture::checkSideCondition(const std::vector<Node>& cvals) const
601
{
602
1790
  if (!d_embedSideCondition.isNull())
603
  {
604
244
    Node sc = d_embedSideCondition;
605
157
    if (!cvals.empty())
606
    {
607
157
      sc = sc.substitute(
608
        d_candidates.begin(), d_candidates.end(), cvals.begin(), cvals.end());
609
    }
610
157
    Trace("sygus-engine") << "Check side condition..." << std::endl;
611
157
    Trace("cegqi-debug") << "Check side condition : " << sc << std::endl;
612
157
    Env& env = d_qstate.getEnv();
613
244
    Result r = checkWithSubsolver(sc, env.getOptions(), env.getLogicInfo());
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
1720
  return true;
622
}
623
624
424
bool SynthConjecture::doRefine()
625
{
626
424
  Assert(d_set_ce_sk_vars);
627
628
  // first, make skolem substitution
629
848
  Trace("cegqi-refine") << "doRefine : construct skolem substitution..."
630
424
                        << std::endl;
631
848
  std::vector<Node> sk_vars;
632
848
  std::vector<Node> sk_subs;
633
  // collect the substitution over all disjuncts
634
424
  if (!d_ce_sk_vars.empty())
635
  {
636
273
    Trace("cegqi-refine") << "Get model values for skolems..." << std::endl;
637
273
    Assert(d_inner_vars.size() == d_ce_sk_vars.size());
638
273
    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
273
      Assert(d_ce_sk_var_mvs.size() == d_ce_sk_vars.size());
652
273
      sk_subs.insert(
653
546
          sk_subs.end(), d_ce_sk_var_mvs.begin(), d_ce_sk_var_mvs.end());
654
    }
655
273
    sk_vars.insert(sk_vars.end(), d_inner_vars.begin(), d_inner_vars.end());
656
  }
657
  else
658
  {
659
151
    Assert(d_inner_vars.empty());
660
  }
661
662
848
  Trace("cegqi-refine") << "doRefine : Construct refinement lemma..."
663
424
                        << std::endl;
664
848
  Trace("cegqi-refine-debug")
665
424
      << "  For counterexample skolems : " << d_ce_sk_vars << std::endl;
666
848
  Node base_lem;
667
424
  if (d_base_inst.getKind() == NOT && d_base_inst[0].getKind() == FORALL)
668
  {
669
273
    base_lem = d_base_inst[0][1];
670
  }
671
  else
672
  {
673
151
    base_lem = d_base_inst.negate();
674
  }
675
676
424
  Assert(sk_vars.size() == sk_subs.size());
677
678
424
  Trace("cegqi-refine") << "doRefine : substitute..." << std::endl;
679
424
  base_lem = base_lem.substitute(
680
      sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end());
681
424
  Trace("cegqi-refine") << "doRefine : rewrite..." << std::endl;
682
424
  base_lem = d_tds->rewriteNode(base_lem);
683
848
  Trace("cegqi-refine") << "doRefine : register refinement lemma " << base_lem
684
424
                        << "..." << std::endl;
685
424
  size_t prevPending = d_qim.numPendingLemmas();
686
424
  d_master->registerRefinementLemma(sk_vars, base_lem);
687
424
  Trace("cegqi-refine") << "doRefine : finished" << std::endl;
688
424
  d_set_ce_sk_vars = false;
689
424
  d_ce_sk_vars.clear();
690
424
  d_ce_sk_var_mvs.clear();
691
692
  // check if we added a lemma
693
424
  bool addedLemma = d_qim.numPendingLemmas() > prevPending;
694
424
  if (addedLemma)
695
  {
696
267
    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
848
  return addedLemma;
716
}
717
718
332
void SynthConjecture::preregisterConjecture(Node q)
719
{
720
332
  d_ceg_si->preregisterConjecture(q);
721
332
}
722
723
39017
bool SynthConjecture::getEnumeratedValues(std::vector<Node>& n,
724
                                          std::vector<Node>& v,
725
                                          bool& activeIncomplete)
726
{
727
78034
  std::vector<Node> ncheck = n;
728
39017
  n.clear();
729
39017
  bool ret = true;
730
84936
  for (unsigned i = 0, size = ncheck.size(); i < size; i++)
731
  {
732
91840
    Node e = ncheck[i];
733
    // if it is not active, we return null
734
91840
    Node g = d_tds->getActiveGuardForEnumerator(e);
735
45928
    if (!g.isNull())
736
    {
737
70214
      Node gstatus = d_qstate.getValuation().getSatValue(g);
738
35131
      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
45912
    EnumValueManager* eman = getEnumValueManagerFor(e);
746
91815
    Node nv = eman->getEnumeratedValue(activeIncomplete);
747
45903
    n.push_back(e);
748
45903
    v.push_back(nv);
749
45903
    ret = ret && !nv.isNull();
750
  }
751
78016
  return ret;
752
}
753
754
64476
EnumValueManager* SynthConjecture::getEnumValueManagerFor(Node e)
755
{
756
  std::map<Node, std::unique_ptr<EnumValueManager>>::iterator it =
757
64476
      d_enumManager.find(e);
758
64476
  if (it != d_enumManager.end())
759
  {
760
64132
    return it->second.get();
761
  }
762
  // otherwise, allocate it
763
688
  Node f = d_tds->getSynthFunForEnumerator(e);
764
1032
  bool hasExamples = (d_exampleInfer->hasExamples(f)
765
1032
                      && d_exampleInfer->getNumExamples(f) != 0);
766
688
  d_enumManager[e].reset(
767
344
      new EnumValueManager(e, d_qstate, d_qim, d_treg, d_stats, hasExamples));
768
344
  EnumValueManager* eman = d_enumManager[e].get();
769
  // set up the examples
770
344
  if (hasExamples)
771
  {
772
128
    ExampleEvalCache* eec = eman->getExampleEvalCache();
773
128
    Assert(eec != nullptr);
774
698
    for (unsigned i = 0, nex = d_exampleInfer->getNumExamples(f); i < nex; i++)
775
    {
776
1140
      std::vector<Node> input;
777
570
      d_exampleInfer->getExample(f, i, input);
778
570
      eec->addExample(input);
779
    }
780
  }
781
344
  return eman;
782
}
783
784
1412
Node SynthConjecture::getModelValue(Node n)
785
{
786
1412
  Trace("cegqi-mv") << "getModelValue for : " << n << std::endl;
787
1412
  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
  const Options& sopts = d_qstate.options();
804
1068
  printSynthSolutionInternal(*sopts.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_qstate.getEnv()));
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
631
  for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
991
  {
992
756
    Node sol = sols[i];
993
378
    int8_t status = statuses[i];
994
756
    Trace("cegqi-debug") << "...got " << i << ": " << sol
995
378
                         << ", status=" << status << std::endl;
996
    // get the builtin solution
997
756
    Node bsol = sol;
998
378
    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
229
      bsol = datatypes::utils::sygusToBuiltin(sol, true);
1004
    }
1005
    // convert to lambda
1006
756
    TypeNode tn = d_embed_quant[0][i].getType();
1007
378
    const DType& dt = tn.getDType();
1008
756
    Node fvar = d_quant[0][i];
1009
756
    Node bvl = dt.getSygusVarList();
1010
378
    if (!bvl.isNull())
1011
    {
1012
      // since we don't have function subtyping, this assertion should only
1013
      // check the return type
1014
294
      Assert(fvar.getType().isFunction());
1015
294
      Assert(fvar.getType().getRangeType().isComparableTo(bsol.getType()));
1016
294
      bsol = nm->mkNode(LAMBDA, bvl, bsol);
1017
    }
1018
    else
1019
    {
1020
84
      Assert(fvar.getType().isComparableTo(bsol.getType()));
1021
    }
1022
    // store in map
1023
378
    smc[fvar] = bsol;
1024
378
    Trace("cegqi-debug") << "...return " << bsol << std::endl;
1025
  }
1026
253
  return true;
1027
}
1028
1029
1720
void SynthConjecture::recordSolution(std::vector<Node>& vs)
1030
{
1031
1720
  Assert(vs.size() == d_candidates.size());
1032
1720
  d_cinfo.clear();
1033
3572
  for (unsigned i = 0; i < vs.size(); i++)
1034
  {
1035
1852
    d_cinfo[d_candidates[i]].d_inst.push_back(vs[i]);
1036
  }
1037
1720
}
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
2767
  for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
1047
  {
1048
2892
    Node prog = d_embed_quant[0][i];
1049
1446
    Trace("cegqi-debug") << "  get solution for " << prog << std::endl;
1050
2892
    TypeNode tn = prog.getType();
1051
1446
    Assert(tn.isDatatype());
1052
    // get the solution
1053
2892
    Node sol;
1054
1446
    int8_t status = -1;
1055
1446
    if (isSingleInvocation())
1056
    {
1057
156
      Assert(d_ceg_si != NULL);
1058
156
      sol = d_ceg_si->getSolution(i, tn, status, true);
1059
156
      if (sol.isNull())
1060
      {
1061
        return false;
1062
      }
1063
156
      sol = sol.getKind() == LAMBDA ? sol[1] : sol;
1064
    }
1065
    else
1066
    {
1067
2580
      Node cprog = d_candidates[i];
1068
1290
      if (!d_cinfo[cprog].d_inst.empty())
1069
      {
1070
        // the solution is just the last instantiated term
1071
1290
        sol = d_cinfo[cprog].d_inst.back();
1072
1290
        status = 1;
1073
1074
        // check if there was a template
1075
2580
        Node sf = d_quant[0][i];
1076
2580
        Node templ = d_templInfer->getTemplate(sf);
1077
1290
        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
2546
          Trace("cegqi-inv-debug")
1109
1273
              << 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
1446
    sols.push_back(sol);
1120
1446
    statuses.push_back(status);
1121
  }
1122
1321
  return true;
1123
}
1124
1125
9256
Node SynthConjecture::getSymmetryBreakingPredicate(
1126
    Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
1127
{
1128
18512
  std::vector<Node> sb_lemmas;
1129
1130
  // based on simple preprocessing
1131
  Node ppred =
1132
18512
      d_ceg_proc->getSymmetryBreakingPredicate(x, e, tn, tindex, depth);
1133
9256
  if (!ppred.isNull())
1134
  {
1135
    sb_lemmas.push_back(ppred);
1136
  }
1137
1138
  // other static conjecture-dependent symmetry breaking goes here
1139
1140
9256
  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
9256
    return Node::null();
1149
  }
1150
}
1151
1152
18564
ExampleEvalCache* SynthConjecture::getExampleEvalCache(Node e)
1153
{
1154
18564
  EnumValueManager* eman = getEnumValueManagerFor(e);
1155
18564
  return eman->getExampleEvalCache();
1156
}
1157
1158
}  // namespace quantifiers
1159
}  // namespace theory
1160
29502
}  // namespace cvc5