GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/cegis_unif.cpp Lines: 304 350 86.9 %
Date: 2021-09-15 Branches: 510 1384 36.8 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Haniel Barbosa, Mathias Preiner
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 for cegis with unification techniques.
14
 */
15
16
#include "theory/quantifiers/sygus/cegis_unif.h"
17
18
#include "expr/skolem_manager.h"
19
#include "expr/sygus_datatype.h"
20
#include "options/quantifiers_options.h"
21
#include "printer/printer.h"
22
#include "theory/datatypes/sygus_datatype_utils.h"
23
#include "theory/quantifiers/sygus/sygus_unif_rl.h"
24
#include "theory/quantifiers/sygus/synth_conjecture.h"
25
#include "theory/quantifiers/sygus/term_database_sygus.h"
26
27
using namespace cvc5::kind;
28
29
namespace cvc5 {
30
namespace theory {
31
namespace quantifiers {
32
33
1233
CegisUnif::CegisUnif(Env& env,
34
                     QuantifiersState& qs,
35
                     QuantifiersInferenceManager& qim,
36
                     TermDbSygus* tds,
37
1233
                     SynthConjecture* p)
38
    : Cegis(env, qs, qim, tds, p),
39
      d_sygus_unif(env, p),
40
1233
      d_u_enum_manager(env, qs, qim, tds, p)
41
{
42
1233
}
43
44
2462
CegisUnif::~CegisUnif() {}
45
11
bool CegisUnif::processInitialize(Node conj,
46
                                  Node n,
47
                                  const std::vector<Node>& candidates)
48
{
49
  // list of strategy points for unification candidates
50
22
  std::vector<Node> unif_candidate_pts;
51
  // map from strategy points to their conditions
52
22
  std::map<Node, Node> pt_to_cond;
53
  // strategy lemmas for each strategy point
54
22
  std::map<Node, std::vector<Node>> strategy_lemmas;
55
  // Initialize strategies for all functions-to-synthesize
56
  // The role of non-unification enumerators is to be either the single solution
57
  // or part of a solution involving multiple enumerators.
58
11
  EnumeratorRole eroleNonUnif = candidates.size() == 1
59
11
                                    ? ROLE_ENUM_SINGLE_SOLUTION
60
11
                                    : ROLE_ENUM_MULTI_SOLUTION;
61
25
  for (const Node& f : candidates)
62
  {
63
    // Init UNIF util for this candidate
64
28
    d_sygus_unif.initializeCandidate(
65
14
        d_tds, f, d_cand_to_strat_pt[f], strategy_lemmas);
66
14
    if (!d_sygus_unif.usingUnif(f))
67
    {
68
3
      Trace("cegis-unif") << "* non-unification candidate : " << f << std::endl;
69
3
      d_tds->registerEnumerator(f, f, d_parent, eroleNonUnif);
70
3
      d_non_unif_candidates.push_back(f);
71
    }
72
    else
73
    {
74
11
      d_unif_candidates.push_back(f);
75
22
      Trace("cegis-unif") << "* unification candidate : " << f
76
11
                          << " with strategy points:" << std::endl;
77
11
      std::vector<Node>& enums = d_cand_to_strat_pt[f];
78
11
      unif_candidate_pts.insert(
79
22
          unif_candidate_pts.end(), enums.begin(), enums.end());
80
      // map strategy point to its condition in pt_to_cond
81
22
      for (const Node& e : enums)
82
      {
83
22
        Node cond = d_sygus_unif.getConditionForEvaluationPoint(e);
84
11
        Assert(!cond.isNull());
85
22
        Trace("cegis-unif")
86
11
            << "  " << e << " with condition : " << cond << std::endl;
87
11
        pt_to_cond[e] = cond;
88
      }
89
    }
90
  }
91
  // initialize the enumeration manager
92
11
  d_u_enum_manager.initialize(unif_candidate_pts, pt_to_cond, strategy_lemmas);
93
22
  return true;
94
}
95
96
323
void CegisUnif::getTermList(const std::vector<Node>& candidates,
97
                            std::vector<Node>& enums)
98
{
99
  // Non-unif candidate are themselves the enumerators
100
323
  enums.insert(
101
646
      enums.end(), d_non_unif_candidates.begin(), d_non_unif_candidates.end());
102
646
  for (const Node& c : d_unif_candidates)
103
  {
104
    // Collect heads of candidates
105
1789
    for (const Node& hd : d_sygus_unif.getEvalPointHeads(c))
106
    {
107
2932
      Trace("cegis-unif-enum-debug")
108
1466
          << "......cand " << c << " with enum hd " << hd << "\n";
109
1466
      enums.push_back(hd);
110
    }
111
    // get unification enumerators
112
646
    for (const Node& e : d_cand_to_strat_pt[c])
113
    {
114
969
      for (unsigned index = 0; index < 2; index++)
115
      {
116
1292
        std::vector<Node> uenums;
117
        // get the current unification enumerators
118
646
        d_u_enum_manager.getEnumeratorsForStrategyPt(e, uenums, index);
119
        // get the model value of each enumerator
120
646
        enums.insert(enums.end(), uenums.begin(), uenums.end());
121
      }
122
    }
123
  }
124
323
}
125
126
280
bool CegisUnif::getEnumValues(const std::vector<Node>& enums,
127
                              const std::vector<Node>& enum_values,
128
                              std::map<Node, std::vector<Node>>& unif_cenums,
129
                              std::map<Node, std::vector<Node>>& unif_cvalues)
130
{
131
280
  NodeManager* nm = NodeManager::currentNM();
132
560
  Node cost_lit = d_u_enum_manager.getAssertedLiteral();
133
560
  std::map<Node, std::vector<Node>> unif_renums, unif_rvalues;
134
  // build model value map
135
560
  std::map<Node, Node> mvMap;
136
2537
  for (unsigned i = 0, size = enums.size(); i < size; i++)
137
  {
138
2257
    mvMap[enums[i]] = enum_values[i];
139
  }
140
280
  bool addedUnifEnumSymBreakLemma = false;
141
  // populate maps between unification enumerators and their model values
142
560
  for (const Node& c : d_unif_candidates)
143
  {
144
    // for each decision tree strategy allocated for c (these are referenced
145
    // by strategy points in d_cand_to_strat_pt[c])
146
560
    for (const Node& e : d_cand_to_strat_pt[c])
147
    {
148
840
      for (unsigned index = 0; index < 2; index++)
149
      {
150
1120
        std::vector<Node> es, vs;
151
1120
        Trace("cegis-unif")
152
1120
            << "  " << (index == 0 ? "Return values" : "Conditions") << " for "
153
560
            << e << ":\n";
154
        // get the current unification enumerators
155
560
        d_u_enum_manager.getEnumeratorsForStrategyPt(e, es, index);
156
        // set enums for condition enumerators
157
560
        if (index == 1)
158
        {
159
280
          if (usingConditionPool())
160
          {
161
            Assert(es.size() == 1);
162
            // whether valueus exhausted
163
            if (mvMap.find(es[0]) == mvMap.end())
164
            {
165
              Trace("cegis-unif") << "    " << es[0] << " -> N/A\n";
166
              es.clear();
167
            }
168
          }
169
280
          unif_cenums[e] = es;
170
        }
171
        // get the model value of each enumerator
172
1510
        for (const Node& eu : es)
173
        {
174
950
          Assert(mvMap.find(eu) != mvMap.end());
175
1900
          Node m_eu = mvMap[eu];
176
950
          if (Trace.isOn("cegis-unif"))
177
          {
178
            Trace("cegis-unif") << "    " << eu << " -> ";
179
            TermDbSygus::toStreamSygus("cegis-unif", m_eu);
180
            Trace("cegis-unif") << "\n";
181
          }
182
950
          vs.push_back(m_eu);
183
        }
184
        // set values for condition enumerators of e
185
560
        if (index == 1)
186
        {
187
280
          unif_cvalues[e] = vs;
188
        }
189
        // inter-enumerator symmetry breaking for return values
190
        else
191
        {
192
          // given a pool of unification enumerators eu_1, ..., eu_n,
193
          // CegisUnifEnumDecisionStrategy insists that size(eu_1) <= ... <=
194
          // size(eu_n). We additionally insist that M(eu_i) < M(eu_{i+1}) when
195
          // size(eu_i) = size(eu_{i+1}), where < is pointer comparison.
196
          // We enforce this below by adding symmetry breaking lemmas of the
197
          // form ~( eu_i = M(eu_i) ^ eu_{i+1} = M(eu_{i+1} ) )
198
          // when applicable.
199
          // we only do this for return value enumerators, since condition
200
          // enumerators cannot be ordered (their order is based on the
201
          // seperation resolution scheme during model construction).
202
594
          for (unsigned j = 1, nenum = vs.size(); j < nenum; j++)
203
          {
204
646
            Node prev_val = vs[j - 1];
205
646
            Node curr_val = vs[j];
206
            // compare the node values
207
332
            if (curr_val < prev_val)
208
            {
209
              // must have the same size
210
18
              unsigned prev_size = datatypes::utils::getSygusTermSize(prev_val);
211
18
              unsigned curr_size = datatypes::utils::getSygusTermSize(curr_val);
212
18
              Assert(prev_size <= curr_size);
213
18
              if (curr_size == prev_size)
214
              {
215
                Node slem =
216
72
                    nm->mkNode(
217
72
                          AND, es[j - 1].eqNode(vs[j - 1]), es[j].eqNode(vs[j]))
218
36
                        .negate();
219
36
                Trace("cegis-unif")
220
                    << "CegisUnif::lemma, inter-unif-enumerator "
221
18
                       "symmetry breaking lemma : "
222
18
                    << slem << "\n";
223
18
                d_qim.lemma(
224
                    slem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_INTER_ENUM_SB);
225
18
                addedUnifEnumSymBreakLemma = true;
226
18
                break;
227
              }
228
            }
229
          }
230
        }
231
      }
232
    }
233
  }
234
560
  return !addedUnifEnumSymBreakLemma;
235
}
236
237
684
bool CegisUnif::usingConditionPool() const
238
{
239
684
  return d_sygus_unif.usingConditionPool();
240
}
241
242
147
void CegisUnif::setConditions(
243
    const std::map<Node, std::vector<Node>>& unif_cenums,
244
    const std::map<Node, std::vector<Node>>& unif_cvalues)
245
{
246
294
  Node cost_lit = d_u_enum_manager.getAssertedLiteral();
247
147
  NodeManager* nm = NodeManager::currentNM();
248
  // set the conditions
249
294
  for (const Node& c : d_unif_candidates)
250
  {
251
294
    for (const Node& e : d_cand_to_strat_pt[c])
252
    {
253
147
      Assert(unif_cenums.find(e) != unif_cenums.end());
254
147
      Assert(unif_cvalues.find(e) != unif_cvalues.end());
255
      std::map<Node, std::vector<Node>>::const_iterator itc =
256
147
          unif_cenums.find(e);
257
      std::map<Node, std::vector<Node>>::const_iterator itv =
258
147
          unif_cvalues.find(e);
259
147
      d_sygus_unif.setConditions(e, cost_lit, itc->second, itv->second);
260
      // d_sygus_unif.setConditions(e, cost_lit, unif_cenums[e],
261
      // unif_cvalues[e]); if condition enumerator had value and it is being
262
      // passively generated, exclude this value
263
147
      if (usingConditionPool() && !itc->second.empty())
264
      {
265
        Node eu = itc->second[0];
266
        Assert(d_tds->isEnumerator(eu));
267
        Assert(!itv->second.empty());
268
        if (d_tds->isPassiveEnumerator(eu))
269
        {
270
          Node g = d_tds->getActiveGuardForEnumerator(eu);
271
          Node exp_exc = d_tds->getExplain()
272
                             ->getExplanationForEquality(eu, itv->second[0])
273
                             .negate();
274
          Node lem = nm->mkNode(OR, g.negate(), exp_exc);
275
          d_qim.addPendingLemma(
276
              lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_COND_EXCLUDE);
277
        }
278
      }
279
    }
280
  }
281
147
}
282
283
280
bool CegisUnif::processConstructCandidates(const std::vector<Node>& enums,
284
                                           const std::vector<Node>& enum_values,
285
                                           const std::vector<Node>& candidates,
286
                                           std::vector<Node>& candidate_values,
287
                                           bool satisfiedRl)
288
{
289
280
  if (d_unif_candidates.empty())
290
  {
291
    Assert(d_non_unif_candidates.size() == candidates.size());
292
    return Cegis::processConstructCandidates(
293
        enums, enum_values, candidates, candidate_values, satisfiedRl);
294
  }
295
280
  if (Trace.isOn("cegis-unif"))
296
  {
297
    for (const Node& c : d_unif_candidates)
298
    {
299
      // Collect heads of candidates
300
      Trace("cegis-unif") << "  Evaluation heads for " << c << " :\n";
301
      for (const Node& hd : d_sygus_unif.getEvalPointHeads(c))
302
      {
303
        bool isUnit = false;
304
        // d_rl_eval_hds accumulates eval apps, so need to look at operators
305
        for (const Node& hd_unit : d_rl_eval_hds)
306
        {
307
          if (hd == hd_unit[0])
308
          {
309
            isUnit = true;
310
            break;
311
          }
312
        }
313
        Trace("cegis-unif") << "    " << hd << (isUnit ? "*" : "") << " -> ";
314
        Assert(std::find(enums.begin(), enums.end(), hd) != enums.end());
315
        unsigned i = std::distance(enums.begin(),
316
                                   std::find(enums.begin(), enums.end(), hd));
317
        Assert(i >= 0 && i < enum_values.size());
318
        TermDbSygus::toStreamSygus("cegis-unif", enum_values[i]);
319
        Trace("cegis-unif") << "\n";
320
      }
321
    }
322
  }
323
  // the unification enumerators for conditions and their model values
324
560
  std::map<Node, std::vector<Node>> unif_cenums;
325
560
  std::map<Node, std::vector<Node>> unif_cvalues;
326
  // we only proceed to solution building if we are not introducing symmetry
327
  // breaking lemmas between return values and if we have not previously
328
  // introduced return values refinement lemmas
329
560
  if (!getEnumValues(enums, enum_values, unif_cenums, unif_cvalues)
330
280
      || !satisfiedRl)
331
  {
332
    // if condition values are being indepedently enumerated, they should be
333
    // communicated to the decision tree strategies indepedently of we
334
    // proceeding to attempt solution building
335
133
    if (usingConditionPool())
336
    {
337
      setConditions(unif_cenums, unif_cvalues);
338
    }
339
266
    Trace("cegis-unif") << (!satisfiedRl
340
266
                                ? "..added refinement lemmas"
341
133
                                : "..added unif enum symmetry breaking")
342
133
                        << "\n---CegisUnif Engine---\n";
343
    // if we didn't satisfy the specification, there is no way to repair
344
133
    return false;
345
  }
346
147
  setConditions(unif_cenums, unif_cvalues);
347
  // build solutions (for unif candidates a divide-and-conquer approach is used)
348
294
  std::vector<Node> sols;
349
294
  std::vector<Node> lemmas;
350
147
  if (d_sygus_unif.constructSolution(sols, lemmas))
351
  {
352
23
    candidate_values.insert(candidate_values.end(), sols.begin(), sols.end());
353
23
    if (Trace.isOn("cegis-unif"))
354
    {
355
      Trace("cegis-unif") << "* Candidate solutions are:\n";
356
      for (const Node& sol : sols)
357
      {
358
        Trace("cegis-unif")
359
            << "... " << d_tds->sygusToBuiltin(sol, sol.getType()) << "\n";
360
      }
361
      Trace("cegis-unif") << "---CegisUnif Engine---\n";
362
    }
363
23
    return true;
364
  }
365
366
  // TODO tie this to the lemma for getting a new condition value
367
124
  Assert(usingConditionPool() || !lemmas.empty());
368
248
  for (const Node& lem : lemmas)
369
  {
370
248
    Trace("cegis-unif-lemma")
371
124
        << "CegisUnif::lemma, separation lemma : " << lem << "\n";
372
124
    d_qim.lemma(lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_SEPARATION);
373
  }
374
124
  Trace("cegis-unif") << "..failed to separate heads\n---CegisUnif Engine---\n";
375
124
  return false;
376
}
377
378
15
void CegisUnif::registerRefinementLemma(const std::vector<Node>& vars, Node lem)
379
{
380
  // Notify lemma to unification utility and get its purified form
381
30
  std::map<Node, std::vector<Node>> eval_pts;
382
30
  Node plem = d_sygus_unif.addRefLemma(lem, eval_pts);
383
15
  addRefinementLemma(plem);
384
30
  Trace("cegis-unif-lemma")
385
15
      << "CegisUnif::lemma, refinement lemma : " << plem << "\n";
386
  // Notify the enumeration manager if there are new evaluation points
387
30
  for (const std::pair<const Node, std::vector<Node>>& ep : eval_pts)
388
  {
389
15
    Assert(d_cand_to_strat_pt.find(ep.first) != d_cand_to_strat_pt.end());
390
    // Notify each strategy point of the respective candidate
391
30
    for (const Node& n : d_cand_to_strat_pt[ep.first])
392
    {
393
15
      d_u_enum_manager.registerEvalPts(ep.second, n);
394
    }
395
  }
396
  // Make the refinement lemma and add it to lems. This lemma is guarded by the
397
  // parent's guard, which has the semantics "this conjecture has a solution",
398
  // hence this lemma states: if the parent conjecture has a solution, it
399
  // satisfies the specification for the given concrete point.
400
  Node rlem =
401
30
      NodeManager::currentNM()->mkNode(OR, d_parent->getGuard().negate(), plem);
402
15
  d_qim.addPendingLemma(rlem,
403
                        InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_REFINEMENT);
404
15
}
405
406
1233
CegisUnifEnumDecisionStrategy::CegisUnifEnumDecisionStrategy(
407
    Env& env,
408
    QuantifiersState& qs,
409
    QuantifiersInferenceManager& qim,
410
    TermDbSygus* tds,
411
1233
    SynthConjecture* parent)
412
1233
    : DecisionStrategyFmf(env, qs.getValuation()),
413
      d_qim(qim),
414
      d_tds(tds),
415
2466
      d_parent(parent)
416
{
417
1233
  d_initialized = false;
418
1233
  options::SygusUnifPiMode mode = options::sygusUnifPi();
419
1233
  d_useCondPool = mode == options::SygusUnifPiMode::CENUM
420
1233
                  || mode == options::SygusUnifPiMode::CENUM_IGAIN;
421
1233
}
422
423
17
Node CegisUnifEnumDecisionStrategy::mkLiteral(unsigned n)
424
{
425
17
  NodeManager* nm = NodeManager::currentNM();
426
17
  SkolemManager* sm = nm->getSkolemManager();
427
17
  Node newLit = sm->mkDummySkolem("G_cost", nm->booleanType());
428
17
  unsigned new_size = n + 1;
429
430
  // allocate an enumerator for each candidate
431
34
  for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info)
432
  {
433
34
    Node c = ci.first;
434
34
    TypeNode ct = c.getType();
435
34
    Node eu = sm->mkDummySkolem("eu", ct);
436
34
    Node ceu;
437
17
    if (!d_useCondPool && !ci.second.d_enums[0].empty())
438
    {
439
      // make a new conditional enumerator as well, starting the
440
      // second type around
441
6
      ceu = sm->mkDummySkolem("cu", ci.second.d_ce_type);
442
    }
443
    // register the new enumerators
444
51
    for (unsigned index = 0; index < 2; index++)
445
    {
446
57
      Node e = index == 0 ? eu : ceu;
447
34
      if (e.isNull())
448
      {
449
11
        continue;
450
      }
451
23
      setUpEnumerator(e, ci.second, index);
452
    }
453
  }
454
  // register the evaluation points at the new value
455
34
  for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info)
456
  {
457
34
    Node c = ci.first;
458
35
    for (const Node& ei : ci.second.d_eval_points)
459
    {
460
36
      Trace("cegis-unif-enum") << "...increasing enum number for hd " << ei
461
18
                               << " to new size " << new_size << "\n";
462
18
      registerEvalPtAtSize(c, ei, newLit, new_size);
463
    }
464
  }
465
  // enforce fairness between number of enumerators and enumerator size
466
17
  if (new_size > 1)
467
  {
468
    // construct the "virtual enumerator"
469
6
    if (d_virtual_enum.isNull())
470
    {
471
      // we construct the default integer grammar with no variables, e.g.:
472
      //   A -> 1 | A+A
473
10
      TypeNode intTn = nm->integerType();
474
      // use a null variable list
475
10
      Node bvl;
476
10
      std::string veName("_virtual_enum_grammar");
477
10
      SygusDatatype sdt(veName);
478
10
      TypeNode u = nm->mkSort(veName, NodeManager::SORT_FLAG_PLACEHOLDER);
479
10
      std::set<TypeNode> unresolvedTypes;
480
5
      unresolvedTypes.insert(u);
481
10
      std::vector<TypeNode> cargsEmpty;
482
10
      Node cr = nm->mkConst(Rational(1));
483
5
      sdt.addConstructor(cr, "1", cargsEmpty);
484
10
      std::vector<TypeNode> cargsPlus;
485
5
      cargsPlus.push_back(u);
486
5
      cargsPlus.push_back(u);
487
5
      sdt.addConstructor(PLUS, cargsPlus);
488
5
      sdt.initializeDatatype(nm->integerType(), bvl, false, false);
489
10
      std::vector<DType> datatypes;
490
5
      datatypes.push_back(sdt.getDatatype());
491
      std::vector<TypeNode> dtypes = nm->mkMutualDatatypeTypes(
492
10
          datatypes, unresolvedTypes, NodeManager::DATATYPE_FLAG_PLACEHOLDER);
493
5
      d_virtual_enum = sm->mkDummySkolem("_ve", dtypes[0]);
494
10
      d_tds->registerEnumerator(
495
10
          d_virtual_enum, Node::null(), d_parent, ROLE_ENUM_CONSTRAINED);
496
    }
497
    // if new_size is a power of two, then isPow2 returns log2(new_size)+1
498
    // otherwise, this returns 0. In the case it returns 0, we don't care
499
    // since the floor( log2( i ) ) = floor( log2( i - 1 ) ) and we do not
500
    // increase our size bound.
501
6
    unsigned pow_two = Integer(new_size).isPow2();
502
6
    if (pow_two > 0)
503
    {
504
10
      Node size_ve = nm->mkNode(DT_SIZE, d_virtual_enum);
505
      Node fair_lemma =
506
10
          nm->mkNode(GEQ, size_ve, nm->mkConst(Rational(pow_two - 1)));
507
5
      fair_lemma = nm->mkNode(OR, newLit, fair_lemma);
508
10
      Trace("cegis-unif-enum-lemma")
509
5
          << "CegisUnifEnum::lemma, fairness size:" << fair_lemma << "\n";
510
      // this lemma relates the number of conditions we enumerate and the
511
      // maximum size of a term that is part of our solution. It is of the
512
      // form:
513
      //   G_uq_i => size(ve) >= log_2( i-1 )
514
      // In other words, if we use i conditions, then we allow terms in our
515
      // solution whose size is at most log_2(i-1).
516
5
      d_qim.lemma(fair_lemma, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_FAIR_SIZE);
517
    }
518
  }
519
520
17
  return newLit;
521
}
522
523
11
void CegisUnifEnumDecisionStrategy::initialize(
524
    const std::vector<Node>& es,
525
    const std::map<Node, Node>& e_to_cond,
526
    const std::map<Node, std::vector<Node>>& strategy_lemmas)
527
{
528
11
  Assert(!d_initialized);
529
11
  d_initialized = true;
530
11
  if (es.empty())
531
  {
532
    return;
533
  }
534
  // initialize type information for candidates
535
11
  NodeManager* nm = NodeManager::currentNM();
536
11
  SkolemManager* sm = nm->getSkolemManager();
537
22
  for (const Node& e : es)
538
  {
539
11
    Trace("cegis-unif-enum-debug") << "...adding strategy point " << e << "\n";
540
    // currently, we allocate the same enumerators for candidates of the same
541
    // type
542
11
    d_ce_info[e].d_pt = e;
543
11
    std::map<Node, Node>::const_iterator itcc = e_to_cond.find(e);
544
11
    Assert(itcc != e_to_cond.end());
545
22
    Node cond = itcc->second;
546
22
    Trace("cegis-unif-enum-debug")
547
11
        << "...its condition strategy point is " << cond << "\n";
548
11
    d_ce_info[e].d_ce_type = cond.getType();
549
    // initialize the symmetry breaking lemma templates
550
33
    for (unsigned index = 0; index < 2; index++)
551
    {
552
22
      Assert(d_ce_info[e].d_sbt_lemma_tmpl[index].first.isNull());
553
42
      Node sp = index == 0 ? e : cond;
554
      std::map<Node, std::vector<Node>>::const_iterator it =
555
22
          strategy_lemmas.find(sp);
556
22
      if (it == strategy_lemmas.end())
557
      {
558
2
        continue;
559
      }
560
      // collect lemmas for removing redundant ops for this candidate's type
561
      Node d_sbt_lemma =
562
40
          it->second.size() == 1 ? it->second[0] : nm->mkNode(AND, it->second);
563
40
      Trace("cegis-unif-enum-debug")
564
20
          << "...adding lemma template to remove redundant operators for " << sp
565
20
          << " --> lambda " << sp << ". " << d_sbt_lemma << "\n";
566
40
      d_ce_info[e].d_sbt_lemma_tmpl[index] =
567
60
          std::pair<Node, Node>(d_sbt_lemma, sp);
568
    }
569
  }
570
571
  // register this strategy
572
11
  d_qim.getDecisionManager()->registerStrategy(
573
      DecisionManager::STRAT_QUANT_CEGIS_UNIF_NUM_ENUMS, this);
574
575
  // create single condition enumerator for each decision tree strategy
576
11
  if (d_useCondPool)
577
  {
578
    // allocate a condition enumerator for each candidate
579
    for (std::pair<const Node, StrategyPtInfo>& ci : d_ce_info)
580
    {
581
      Node ceu = sm->mkDummySkolem("cu", ci.second.d_ce_type);
582
      setUpEnumerator(ceu, ci.second, 1);
583
    }
584
  }
585
}
586
587
1206
void CegisUnifEnumDecisionStrategy::getEnumeratorsForStrategyPt(
588
    Node e, std::vector<Node>& es, unsigned index) const
589
{
590
  // the number of active enumerators is related to the current cost value
591
1206
  unsigned num_enums = 0;
592
1206
  bool has_num_enums = getAssertedLiteralIndex(num_enums);
593
1206
  AlwaysAssert(has_num_enums);
594
1206
  num_enums = num_enums + 1;
595
1206
  if (index == 1)
596
  {
597
    // we always use (cost-1) conditions, or 1 if in the indepedent case
598
603
    num_enums = !d_useCondPool ? num_enums - 1 : 1;
599
  }
600
1206
  if (num_enums > 0)
601
  {
602
1057
    std::map<Node, StrategyPtInfo>::const_iterator itc = d_ce_info.find(e);
603
1057
    Assert(itc != d_ce_info.end());
604
1057
    Assert(num_enums <= itc->second.d_enums[index].size());
605
3171
    es.insert(es.end(),
606
1057
              itc->second.d_enums[index].begin(),
607
5285
              itc->second.d_enums[index].begin() + num_enums);
608
  }
609
1206
}
610
611
23
void CegisUnifEnumDecisionStrategy::setUpEnumerator(Node e,
612
                                                    StrategyPtInfo& si,
613
                                                    unsigned index)
614
{
615
23
  NodeManager* nm = NodeManager::currentNM();
616
  // instantiate template for removing redundant operators
617
23
  if (!si.d_sbt_lemma_tmpl[index].first.isNull())
618
  {
619
46
    Node templ = si.d_sbt_lemma_tmpl[index].first;
620
46
    TNode templ_var = si.d_sbt_lemma_tmpl[index].second;
621
46
    Node sym_break_red_ops = templ.substitute(templ_var, e);
622
46
    Trace("cegis-unif-enum-lemma")
623
23
        << "CegisUnifEnum::lemma, remove redundant ops of " << e << " : "
624
23
        << sym_break_red_ops << "\n";
625
23
    d_qim.lemma(sym_break_red_ops,
626
                InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_REM_OPS);
627
  }
628
  // symmetry breaking between enumerators
629
23
  if (!si.d_enums[index].empty() && index == 0)
630
  {
631
12
    Node e_prev = si.d_enums[index].back();
632
12
    Node size_e = nm->mkNode(DT_SIZE, e);
633
12
    Node size_e_prev = nm->mkNode(DT_SIZE, e_prev);
634
12
    Node sym_break = nm->mkNode(GEQ, size_e, size_e_prev);
635
12
    Trace("cegis-unif-enum-lemma")
636
6
        << "CegisUnifEnum::lemma, enum sym break:" << sym_break << "\n";
637
6
    d_qim.lemma(sym_break, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_ENUM_SB);
638
  }
639
  // register the enumerator
640
23
  si.d_enums[index].push_back(e);
641
23
  EnumeratorRole erole = ROLE_ENUM_CONSTRAINED;
642
  // if we are using a single independent enumerator for conditions, then we
643
  // allocate an active guard, and are eligible to use variable-agnostic
644
  // enumeration.
645
23
  if (d_useCondPool && index == 1)
646
  {
647
    erole = ROLE_ENUM_POOL;
648
  }
649
46
  Trace("cegis-unif-enum") << "* Registering new enumerator " << e
650
23
                           << " to strategy point " << si.d_pt << "\n";
651
23
  d_tds->registerEnumerator(e, si.d_pt, d_parent, erole);
652
23
}
653
654
15
void CegisUnifEnumDecisionStrategy::registerEvalPts(
655
    const std::vector<Node>& eis, Node e)
656
{
657
  // candidates of the same type are managed
658
15
  std::map<Node, StrategyPtInfo>::iterator it = d_ce_info.find(e);
659
15
  Assert(it != d_ce_info.end());
660
30
  it->second.d_eval_points.insert(
661
30
      it->second.d_eval_points.end(), eis.begin(), eis.end());
662
  // register at all already allocated sizes
663
41
  for (const Node& ei : eis)
664
  {
665
26
    Assert(ei.getType() == e.getType());
666
63
    for (unsigned j = 0, size = d_literals.size(); j < size; j++)
667
    {
668
74
      Trace("cegis-unif-enum") << "...for cand " << e << " adding hd " << ei
669
37
                               << " at size " << j << "\n";
670
37
      registerEvalPtAtSize(e, ei, d_literals[j], j + 1);
671
    }
672
  }
673
15
}
674
675
55
void CegisUnifEnumDecisionStrategy::registerEvalPtAtSize(Node e,
676
                                                         Node ei,
677
                                                         Node guq_lit,
678
                                                         unsigned n)
679
{
680
  // must be equal to one of the first n enums
681
55
  std::map<Node, StrategyPtInfo>::iterator itc = d_ce_info.find(e);
682
55
  Assert(itc != d_ce_info.end());
683
55
  Assert(itc->second.d_enums[0].size() >= n);
684
110
  std::vector<Node> disj;
685
55
  disj.push_back(guq_lit.negate());
686
145
  for (unsigned i = 0; i < n; i++)
687
  {
688
90
    disj.push_back(ei.eqNode(itc->second.d_enums[0][i]));
689
  }
690
110
  Node lem = NodeManager::currentNM()->mkNode(OR, disj);
691
110
  Trace("cegis-unif-enum-lemma")
692
55
      << "CegisUnifEnum::lemma, domain:" << lem << "\n";
693
55
  d_qim.lemma(lem, InferenceId::QUANTIFIERS_SYGUS_UNIF_PI_DOMAIN);
694
55
}
695
696
}  // namespace quantifiers
697
}  // namespace theory
698
29577
}  // namespace cvc5