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