GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/synth_conjecture.cpp Lines: 486 539 90.2 %
Date: 2021-11-07 Branches: 997 2366 42.1 %

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