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