GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_unif_strat.cpp Lines: 479 571 83.9 %
Date: 2021-09-17 Branches: 933 2162 43.2 %

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 sygus_unif_strat.
14
 */
15
16
#include "theory/quantifiers/sygus/sygus_unif_strat.h"
17
18
#include "expr/dtype.h"
19
#include "expr/dtype_cons.h"
20
#include "expr/skolem_manager.h"
21
#include "theory/datatypes/theory_datatypes_utils.h"
22
#include "theory/quantifiers/sygus/sygus_eval_unfold.h"
23
#include "theory/quantifiers/sygus/sygus_unif.h"
24
#include "theory/quantifiers/sygus/term_database_sygus.h"
25
#include "theory/quantifiers/term_util.h"
26
27
using namespace std;
28
using namespace cvc5::kind;
29
30
namespace cvc5 {
31
namespace theory {
32
namespace quantifiers {
33
34
std::ostream& operator<<(std::ostream& os, EnumRole r)
35
{
36
  switch (r)
37
  {
38
    case enum_invalid: os << "INVALID"; break;
39
    case enum_io: os << "IO"; break;
40
    case enum_ite_condition: os << "CONDITION"; break;
41
    case enum_concat_term: os << "CTERM"; break;
42
    default: os << "enum_" << static_cast<unsigned>(r); break;
43
  }
44
  return os;
45
}
46
47
std::ostream& operator<<(std::ostream& os, NodeRole r)
48
{
49
  switch (r)
50
  {
51
    case role_equal: os << "equal"; break;
52
    case role_string_prefix: os << "string_prefix"; break;
53
    case role_string_suffix: os << "string_suffix"; break;
54
    case role_ite_condition: os << "ite_condition"; break;
55
    default: os << "role_" << static_cast<unsigned>(r); break;
56
  }
57
  return os;
58
}
59
60
366
EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
61
{
62
366
  switch (r)
63
  {
64
171
    case role_equal: return enum_io; break;
65
60
    case role_string_prefix: return enum_concat_term; break;
66
63
    case role_string_suffix: return enum_concat_term; break;
67
72
    case role_ite_condition: return enum_ite_condition; break;
68
    default: break;
69
  }
70
  return enum_invalid;
71
}
72
73
std::ostream& operator<<(std::ostream& os, StrategyType st)
74
{
75
  switch (st)
76
  {
77
    case strat_ITE: os << "ITE"; break;
78
    case strat_CONCAT_PREFIX: os << "CONCAT_PREFIX"; break;
79
    case strat_CONCAT_SUFFIX: os << "CONCAT_SUFFIX"; break;
80
    case strat_ID: os << "ID"; break;
81
    default: os << "strat_" << static_cast<unsigned>(st); break;
82
  }
83
  return os;
84
}
85
86
61
void SygusUnifStrategy::initialize(TermDbSygus* tds,
87
                                   Node f,
88
                                   std::vector<Node>& enums)
89
{
90
61
  Assert(d_candidate.isNull());
91
61
  d_candidate = f;
92
61
  d_root = f.getType();
93
61
  d_tds = tds;
94
95
  // collect the enumerator types and form the strategy
96
61
  buildStrategyGraph(d_root, role_equal);
97
  // add the enumerators
98
61
  enums.insert(enums.end(), d_esym_list.begin(), d_esym_list.end());
99
  // finish the initialization of the strategy
100
  // this computes if each node is conditional
101
122
  std::map<Node, std::map<NodeRole, bool> > visited;
102
61
  finishInit(getRootEnumerator(), role_equal, visited, false);
103
61
}
104
105
86
void SygusUnifStrategy::initializeType(TypeNode tn)
106
{
107
172
  Trace("sygus-unif") << "SygusUnifStrategy: initialize : " << tn << " for "
108
86
                      << d_candidate << std::endl;
109
86
  d_tinfo[tn].d_this_type = tn;
110
86
}
111
112
815
Node SygusUnifStrategy::getRootEnumerator() const
113
{
114
815
  std::map<TypeNode, EnumTypeInfo>::const_iterator itt = d_tinfo.find(d_root);
115
815
  Assert(itt != d_tinfo.end());
116
  std::map<EnumRole, Node>::const_iterator it =
117
815
      itt->second.d_enum.find(enum_io);
118
815
  Assert(it != itt->second.d_enum.end());
119
815
  return it->second;
120
}
121
122
6964
EnumInfo& SygusUnifStrategy::getEnumInfo(Node e)
123
{
124
6964
  std::map<Node, EnumInfo>::iterator it = d_einfo.find(e);
125
6964
  Assert(it != d_einfo.end());
126
6964
  return it->second;
127
}
128
129
4032
EnumTypeInfo& SygusUnifStrategy::getEnumTypeInfo(TypeNode tn)
130
{
131
8064
  Trace("sygus-unif") << "SygusUnifStrategy: get : " << tn << " for "
132
4032
                      << d_candidate << std::endl;
133
4032
  std::map<TypeNode, EnumTypeInfo>::iterator it = d_tinfo.find(tn);
134
4032
  Assert(it != d_tinfo.end());
135
4032
  return it->second;
136
}
137
// ----------------------------- establishing enumeration types
138
139
166
void SygusUnifStrategy::registerStrategyPoint(Node et,
140
                                           TypeNode tn,
141
                                           EnumRole enum_role,
142
                                           bool inSearch)
143
{
144
166
  if (d_einfo.find(et) == d_einfo.end())
145
  {
146
300
    Trace("sygus-unif-debug")
147
150
        << "...register " << et << " for " << tn.getDType().getName();
148
300
    Trace("sygus-unif-debug") << ", role = " << enum_role
149
150
                              << ", in search = " << inSearch << std::endl;
150
150
    d_einfo[et].initialize(enum_role);
151
    // if we are actually enumerating this (could be a compound node in the
152
    // strategy)
153
150
    if (inSearch)
154
    {
155
146
      std::map<TypeNode, Node>::iterator itn = d_master_enum.find(tn);
156
146
      if (itn == d_master_enum.end())
157
      {
158
        // use this for the search
159
82
        d_master_enum[tn] = et;
160
82
        d_esym_list.push_back(et);
161
82
        d_einfo[et].d_enum_slave.push_back(et);
162
      }
163
      else
164
      {
165
128
        Trace("sygus-unif-debug") << "Make " << et << " a slave of "
166
64
                                  << itn->second << std::endl;
167
64
        d_einfo[itn->second].d_enum_slave.push_back(et);
168
      }
169
    }
170
  }
171
166
}
172
173
289
void SygusUnifStrategy::buildStrategyGraph(TypeNode tn, NodeRole nrole)
174
{
175
289
  NodeManager* nm = NodeManager::currentNM();
176
289
  SkolemManager* sm = nm->getSkolemManager();
177
289
  if (d_tinfo.find(tn) == d_tinfo.end())
178
  {
179
    // register type
180
86
    Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl;
181
86
    initializeType(tn);
182
  }
183
289
  EnumTypeInfo& eti = d_tinfo[tn];
184
289
  std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
185
289
  if (itsn != eti.d_snodes.end())
186
  {
187
    // already initialized
188
337
    return;
189
  }
190
138
  StrategyNode& snode = eti.d_snodes[nrole];
191
192
  // get the enumerator for this
193
138
  EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
194
195
241
  Node ee;
196
138
  std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
197
138
  if (iten == eti.d_enum.end())
198
  {
199
122
    ee = sm->mkDummySkolem("ee", tn);
200
122
    eti.d_enum[erole] = ee;
201
244
    Trace("sygus-unif-debug")
202
244
        << "...enumerator " << ee << " for " << tn.getDType().getName()
203
122
        << ", role = " << erole << std::endl;
204
  }
205
  else
206
  {
207
16
    ee = iten->second;
208
  }
209
210
  // roles that we do not recurse on
211
138
  if (nrole == role_ite_condition)
212
  {
213
35
    Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
214
35
    registerStrategyPoint(ee, tn, erole, true);
215
35
    return;
216
  }
217
218
  // look at information on how we will construct solutions for this type
219
  // we know this is a sygus datatype since it is either the top-level type
220
  // in the strategy graph, or was recursed by a strategy we inferred.
221
103
  Assert(tn.isDatatype());
222
103
  const DType& dt = tn.getDType();
223
103
  Assert(dt.isSygus());
224
225
206
  std::map<Node, std::vector<StrategyType> > cop_to_strat;
226
206
  std::map<Node, unsigned> cop_to_cindex;
227
206
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ;
228
206
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ_arg;
229
206
  std::map<Node, std::vector<unsigned> > cop_to_carg_list;
230
206
  std::map<Node, std::vector<TypeNode> > cop_to_child_types;
231
206
  std::map<Node, std::vector<Node> > cop_to_sks;
232
233
  // whether we will enumerate the current type
234
103
  bool search_this = false;
235
786
  for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
236
  {
237
1366
    Node cop = dt[j].getConstructor();
238
1366
    Node op = dt[j].getSygusOp();
239
1366
    Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
240
683
                              << " with sygus op " << op << "..." << std::endl;
241
242
    // expand the evaluation to see if this constuctor induces a strategy
243
1366
    std::vector<Node> utchildren;
244
683
    utchildren.push_back(cop);
245
1366
    std::vector<Node> sks;
246
1366
    std::vector<TypeNode> sktns;
247
1286
    for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
248
    {
249
1206
      TypeNode ttn = dt[j][k].getRangeType();
250
1206
      Node kv = sm->mkDummySkolem("ut", ttn);
251
603
      sks.push_back(kv);
252
603
      cop_to_sks[cop].push_back(kv);
253
603
      sktns.push_back(ttn);
254
603
      utchildren.push_back(kv);
255
    }
256
1366
    Node ut = nm->mkNode(APPLY_CONSTRUCTOR, utchildren);
257
1366
    std::vector<Node> echildren;
258
683
    echildren.push_back(ut);
259
1366
    Node sbvl = dt.getSygusVarList();
260
3937
    for (const Node& sbv : sbvl)
261
    {
262
3254
      echildren.push_back(sbv);
263
    }
264
1366
    Node eut = nm->mkNode(DT_SYGUS_EVAL, echildren);
265
1366
    Trace("sygus-unif-debug2") << "  Test evaluation of " << eut << "..."
266
683
                               << std::endl;
267
683
    eut = d_tds->rewriteNode(eut);
268
683
    Trace("sygus-unif-debug2") << "  ...got " << eut;
269
683
    Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
270
271
    // candidate strategy
272
683
    if (eut.getKind() == ITE)
273
    {
274
40
      cop_to_strat[cop].push_back(strat_ITE);
275
    }
276
643
    else if (eut.getKind() == STRING_CONCAT)
277
    {
278
41
      if (nrole != role_string_suffix)
279
      {
280
27
        cop_to_strat[cop].push_back(strat_CONCAT_PREFIX);
281
      }
282
41
      if (nrole != role_string_prefix)
283
      {
284
28
        cop_to_strat[cop].push_back(strat_CONCAT_SUFFIX);
285
      }
286
    }
287
602
    else if (dt[j].isSygusIdFunc())
288
    {
289
3
      cop_to_strat[cop].push_back(strat_ID);
290
    }
291
292
    // the kinds for which there is a strategy
293
683
    if (cop_to_strat.find(cop) != cop_to_strat.end())
294
    {
295
      // infer an injection from the arguments of the datatype
296
168
      std::map<unsigned, unsigned> templ_injection;
297
168
      std::vector<Node> vs;
298
168
      std::vector<Node> ss;
299
168
      std::map<Node, unsigned> templ_var_index;
300
298
      for (unsigned k = 0, sksize = sks.size(); k < sksize; k++)
301
      {
302
214
        Assert(sks[k].getType().isDatatype());
303
214
        echildren[0] = sks[k];
304
428
        Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k]
305
214
                                   << std::endl;
306
428
        Node esk = nm->mkNode(DT_SYGUS_EVAL, echildren);
307
214
        vs.push_back(esk);
308
428
        Node tvar = sm->mkDummySkolem("templ", esk.getType());
309
214
        templ_var_index[tvar] = k;
310
428
        Trace("sygus-unif-debug2") << "* template inference : looking for "
311
214
                                   << tvar << " for arg " << k << std::endl;
312
214
        ss.push_back(tvar);
313
428
        Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar
314
214
                                   << std::endl;
315
      }
316
84
      eut = eut.substitute(vs.begin(), vs.end(), ss.begin(), ss.end());
317
168
      Trace("sygus-unif-debug2") << "Constructor " << j << ", base term is "
318
84
                                 << eut << std::endl;
319
168
      std::map<unsigned, Node> test_args;
320
84
      if (dt[j].isSygusIdFunc())
321
      {
322
3
        test_args[0] = eut;
323
      }
324
      else
325
      {
326
298
        for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
327
        {
328
217
          test_args[k] = eut[k];
329
        }
330
      }
331
332
      // TODO : prefix grouping prefix/suffix
333
84
      bool isAssoc = TermUtil::isAssoc(eut.getKind());
334
168
      Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
335
84
                                 << std::endl;
336
168
      std::map<unsigned, std::vector<unsigned> > assoc_combine;
337
168
      std::vector<unsigned> assoc_waiting;
338
84
      int assoc_last_valid_index = -1;
339
304
      for (std::pair<const unsigned, Node>& ta : test_args)
340
      {
341
220
        unsigned k = ta.first;
342
440
        Node eut_c = ta.second;
343
        // success if we can find a injection from args to sygus args
344
220
        if (!inferTemplate(k, eut_c, templ_var_index, templ_injection))
345
        {
346
          Trace("sygus-unif-debug")
347
              << "...fail: could not find injection (range)." << std::endl;
348
          cop_to_strat.erase(cop);
349
          break;
350
        }
351
220
        std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
352
220
        if (itti != templ_injection.end())
353
        {
354
          // if associative, combine arguments if it is the same variable
355
293
          if (isAssoc && assoc_last_valid_index >= 0
356
252
              && itti->second == templ_injection[assoc_last_valid_index])
357
          {
358
            templ_injection.erase(k);
359
            assoc_combine[assoc_last_valid_index].push_back(k);
360
          }
361
          else
362
          {
363
208
            assoc_last_valid_index = (int)k;
364
208
            if (!assoc_waiting.empty())
365
            {
366
              assoc_combine[k].insert(assoc_combine[k].end(),
367
                                      assoc_waiting.begin(),
368
                                      assoc_waiting.end());
369
              assoc_waiting.clear();
370
            }
371
208
            assoc_combine[k].push_back(k);
372
          }
373
        }
374
        else
375
        {
376
          // a ground argument
377
12
          if (!isAssoc)
378
          {
379
            Trace("sygus-unif-debug")
380
                << "...fail: could not find injection (functional)."
381
                << std::endl;
382
            cop_to_strat.erase(cop);
383
            break;
384
          }
385
          else
386
          {
387
12
            if (assoc_last_valid_index >= 0)
388
            {
389
12
              assoc_combine[assoc_last_valid_index].push_back(k);
390
            }
391
            else
392
            {
393
              assoc_waiting.push_back(k);
394
            }
395
          }
396
        }
397
      }
398
84
      if (cop_to_strat.find(cop) != cop_to_strat.end())
399
      {
400
        // construct the templates
401
84
        if (!assoc_waiting.empty())
402
        {
403
          // could not find a way to fit some arguments into injection
404
          cop_to_strat.erase(cop);
405
        }
406
        else
407
        {
408
298
          for (std::pair<const unsigned, Node>& ta : test_args)
409
          {
410
217
            unsigned k = ta.first;
411
434
            Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
412
217
                                       << std::endl;
413
217
            if (templ_injection.find(k) != templ_injection.end())
414
            {
415
205
              unsigned sk_index = templ_injection[k];
416
820
              if (std::find(cop_to_carg_list[cop].begin(),
417
205
                            cop_to_carg_list[cop].end(),
418
410
                            sk_index)
419
615
                  == cop_to_carg_list[cop].end())
420
              {
421
202
                cop_to_carg_list[cop].push_back(sk_index);
422
              }
423
              else
424
              {
425
6
                Trace("sygus-unif-debug") << "...fail: duplicate argument used"
426
3
                                          << std::endl;
427
3
                cop_to_strat.erase(cop);
428
3
                break;
429
              }
430
              // also store the template information, if necessary
431
404
              Node teut;
432
202
              if (isAssoc)
433
              {
434
85
                std::vector<unsigned>& ac = assoc_combine[k];
435
85
                Assert(!ac.empty());
436
170
                std::vector<Node> children;
437
182
                for (unsigned ack = 0, size_ac = ac.size(); ack < size_ac;
438
                     ack++)
439
                {
440
97
                  children.push_back(eut[ac[ack]]);
441
                }
442
170
                teut = children.size() == 1
443
170
                           ? children[0]
444
                           : nm->mkNode(eut.getKind(), children);
445
85
                teut = rewrite(teut);
446
              }
447
              else
448
              {
449
117
                teut = ta.second;
450
              }
451
452
202
              if (!teut.isVar())
453
              {
454
27
                cop_to_child_templ[cop][k] = teut;
455
27
                cop_to_child_templ_arg[cop][k] = ss[sk_index];
456
54
                Trace("sygus-unif-debug")
457
27
                    << "  Arg " << k << " (template : " << teut << " arg "
458
27
                    << ss[sk_index] << "), index " << sk_index << std::endl;
459
              }
460
              else
461
              {
462
350
                Trace("sygus-unif-debug") << "  Arg " << k << ", index "
463
175
                                          << sk_index << std::endl;
464
175
                Assert(teut == ss[sk_index]);
465
              }
466
            }
467
            else
468
            {
469
12
              Assert(isAssoc);
470
            }
471
          }
472
        }
473
      }
474
    }
475
476
683
    std::map<Node, std::vector<StrategyType> >::iterator itcs = cop_to_strat.find(cop);
477
683
    if (itcs != cop_to_strat.end())
478
    {
479
162
      Trace("sygus-unif") << "-> constructor " << cop
480
162
                          << " matches strategy for " << eut.getKind() << "..."
481
81
                          << std::endl;
482
      // collect children types
483
280
      for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
484
      {
485
398
        TypeNode ctn = sktns[cop_to_carg_list[cop][k]];
486
398
        Trace("sygus-unif-debug") << "   Child type " << k << " : "
487
199
                                  << ctn.getDType().getName() << std::endl;
488
199
        cop_to_child_types[cop].push_back(ctn);
489
      }
490
      // if there are checks on the consistency of child types wrt strategies,
491
      // these should be enforced here. We currently have none.
492
    }
493
683
    if (cop_to_strat.find(cop) == cop_to_strat.end())
494
    {
495
1204
      Trace("sygus-unif") << "...constructor " << cop
496
602
                          << " does not correspond to a strategy." << std::endl;
497
602
      search_this = true;
498
    }
499
  }
500
501
  // check whether we should also enumerate the current type
502
103
  Trace("sygus-unif-debug2") << "  register this strategy ..." << std::endl;
503
103
  registerStrategyPoint(ee, tn, erole, search_this);
504
505
103
  if (cop_to_strat.empty())
506
  {
507
56
    Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type"
508
28
                        << std::endl;
509
  }
510
  else
511
  {
512
156
    for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
513
    {
514
162
      Node cop = cstr.first;
515
162
      Trace("sygus-unif-debug") << "Constructor " << cop << " has "
516
162
                                << cstr.second.size() << " strategies..."
517
81
                                << std::endl;
518
176
      for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
519
      {
520
95
        EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
521
95
        StrategyType strat = cstr.second[s];
522
523
95
        cons_strat->d_this = strat;
524
95
        cons_strat->d_cons = cop;
525
190
        Trace("sygus-unif-debug") << "Process strategy #" << s
526
95
                                  << " for operator : " << cop << " : " << strat
527
95
                                  << std::endl;
528
95
        Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
529
95
        std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
530
95
        Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
531
95
        std::vector<unsigned>& cargList = cop_to_carg_list[cop];
532
533
190
        std::vector<Node> sol_templ_children;
534
95
        sol_templ_children.resize(cop_to_sks[cop].size());
535
536
323
        for (unsigned j = 0, csize = childTypes.size(); j < csize; j++)
537
        {
538
          // calculate if we should allocate a new enumerator : should be true
539
          // if we have a new role
540
228
          NodeRole nrole_c = nrole;
541
228
          if (strat == strat_ITE)
542
          {
543
111
            if (j == 0)
544
            {
545
37
              nrole_c = role_ite_condition;
546
            }
547
          }
548
117
          else if (strat == strat_CONCAT_PREFIX)
549
          {
550
56
            if ((j + 1) < childTypes.size())
551
            {
552
29
              nrole_c = role_string_prefix;
553
            }
554
          }
555
61
          else if (strat == strat_CONCAT_SUFFIX)
556
          {
557
58
            if (j > 0)
558
            {
559
30
              nrole_c = role_string_suffix;
560
            }
561
          }
562
          // in all other cases, role is same as parent
563
564
          // register the child type
565
456
          TypeNode ct = childTypes[j];
566
456
          Node csk = cop_to_sks[cop][cargList[j]];
567
228
          cons_strat->d_sol_templ_args.push_back(csk);
568
228
          sol_templ_children[cargList[j]] = csk;
569
570
228
          EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
571
          // make the enumerator
572
456
          Node et;
573
          // Build the strategy recursively, regardless of whether the
574
          // enumerator is templated.
575
228
          buildStrategyGraph(ct, nrole_c);
576
228
          if (cop_to_child_templ[cop].find(j) != cop_to_child_templ[cop].end())
577
          {
578
            // it is templated, allocate a fresh variable
579
28
            et = sm->mkDummySkolem("et", ct);
580
56
            Trace("sygus-unif-debug") << "...enumerate " << et << " of type "
581
28
                                      << ct.getDType().getName();
582
56
            Trace("sygus-unif-debug") << " for arg " << j << " of "
583
28
                                      << tn.getDType().getName() << std::endl;
584
28
            registerStrategyPoint(et, ct, erole_c, true);
585
28
            d_einfo[et].d_template = cop_to_child_templ[cop][j];
586
28
            d_einfo[et].d_template_arg = cop_to_child_templ_arg[cop][j];
587
28
            Assert(!d_einfo[et].d_template.isNull());
588
28
            Assert(!d_einfo[et].d_template_arg.isNull());
589
          }
590
          else
591
          {
592
400
            Trace("sygus-unif-debug")
593
400
                << "...child type enumerate " << ct.getDType().getName()
594
200
                << ", node role = " << nrole_c << std::endl;
595
            // otherwise use the previous
596
200
            Assert(d_tinfo[ct].d_enum.find(erole_c)
597
                   != d_tinfo[ct].d_enum.end());
598
200
            et = d_tinfo[ct].d_enum[erole_c];
599
          }
600
456
          Trace("sygus-unif-debug") << "Register child enumerator " << et
601
228
                                    << ", arg " << j << " of " << cop
602
228
                                    << ", role = " << erole_c << std::endl;
603
228
          Assert(!et.isNull());
604
228
          cons_strat->d_cenum.push_back(std::pair<Node, NodeRole>(et, nrole_c));
605
        }
606
        // children that are unused in the strategy can be arbitrary
607
339
        for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
608
             j++)
609
        {
610
244
          if (sol_templ_children[j].isNull())
611
          {
612
16
            sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
613
          }
614
        }
615
95
        sol_templ_children.insert(sol_templ_children.begin(), cop);
616
190
        cons_strat->d_sol_templ =
617
285
            nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
618
95
        if (strat == strat_CONCAT_SUFFIX)
619
        {
620
28
          std::reverse(cons_strat->d_cenum.begin(), cons_strat->d_cenum.end());
621
28
          std::reverse(cons_strat->d_sol_templ_args.begin(),
622
28
                       cons_strat->d_sol_templ_args.end());
623
        }
624
95
        if (Trace.isOn("sygus-unif"))
625
        {
626
          Trace("sygus-unif") << "Initialized strategy " << strat;
627
          Trace("sygus-unif")
628
              << " for " << tn.getDType().getName() << ", operator " << cop;
629
          Trace("sygus-unif") << ", #children = " << cons_strat->d_cenum.size()
630
                              << ", solution template = (lambda ( ";
631
          for (const Node& targ : cons_strat->d_sol_templ_args)
632
          {
633
            Trace("sygus-unif") << targ << " ";
634
          }
635
          Trace("sygus-unif") << ") " << cons_strat->d_sol_templ << ")";
636
          Trace("sygus-unif") << std::endl;
637
        }
638
        // make the strategy
639
95
        snode.d_strats.push_back(cons_strat);
640
      }
641
    }
642
  }
643
}
644
645
257
bool SygusUnifStrategy::inferTemplate(
646
    unsigned k,
647
    Node n,
648
    std::map<Node, unsigned>& templ_var_index,
649
    std::map<unsigned, unsigned>& templ_injection)
650
{
651
257
  if (n.getNumChildren() == 0)
652
  {
653
234
    std::map<Node, unsigned>::iterator itt = templ_var_index.find(n);
654
234
    if (itt != templ_var_index.end())
655
    {
656
208
      unsigned kk = itt->second;
657
208
      std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
658
208
      if (itti == templ_injection.end())
659
      {
660
416
        Trace("sygus-unif-debug") << "...set template injection " << k << " -> "
661
208
                                  << kk << std::endl;
662
208
        templ_injection[k] = kk;
663
      }
664
      else if (itti->second != kk)
665
      {
666
        // two distinct variables in this term, we fail
667
        return false;
668
      }
669
    }
670
234
    return true;
671
  }
672
  else
673
  {
674
60
    for (unsigned i = 0; i < n.getNumChildren(); i++)
675
    {
676
37
      if (!inferTemplate(k, n[i], templ_var_index, templ_injection))
677
      {
678
        return false;
679
      }
680
    }
681
  }
682
23
  return true;
683
}
684
685
47
void SygusUnifStrategy::staticLearnRedundantOps(
686
    std::map<Node, std::vector<Node>>& strategy_lemmas)
687
{
688
94
  StrategyRestrictions restrictions;
689
47
  staticLearnRedundantOps(strategy_lemmas, restrictions);
690
47
}
691
692
61
void SygusUnifStrategy::staticLearnRedundantOps(
693
    std::map<Node, std::vector<Node>>& strategy_lemmas,
694
    StrategyRestrictions& restrictions)
695
{
696
143
  for (unsigned i = 0; i < d_esym_list.size(); i++)
697
  {
698
164
    Node e = d_esym_list[i];
699
82
    std::map<Node, EnumInfo>::iterator itn = d_einfo.find(e);
700
82
    Assert(itn != d_einfo.end());
701
    // see if there is anything we can eliminate
702
164
    Trace("sygus-unif") << "* Search enumerator #" << i << " : type "
703
82
                        << e.getType().getDType().getName() << " : ";
704
164
    Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size()
705
82
                        << " slaves:" << std::endl;
706
228
    for (unsigned j = 0; j < itn->second.d_enum_slave.size(); j++)
707
    {
708
292
      Node es = itn->second.d_enum_slave[j];
709
146
      std::map<Node, EnumInfo>::iterator itns = d_einfo.find(es);
710
146
      Assert(itns != d_einfo.end());
711
292
      Trace("sygus-unif") << "  " << es << ", role = " << itns->second.getRole()
712
146
                          << std::endl;
713
    }
714
  }
715
61
  Trace("sygus-unif") << std::endl;
716
122
  Trace("sygus-unif") << "Strategy for candidate " << d_candidate
717
61
                      << " is : " << std::endl;
718
61
  debugPrint("sygus-unif");
719
122
  std::map<Node, std::map<NodeRole, bool> > visited;
720
122
  std::map<Node, std::map<unsigned, bool> > needs_cons;
721
61
  staticLearnRedundantOps(
722
122
      getRootEnumerator(), role_equal, visited, needs_cons, restrictions);
723
  // now, check the needs_cons map
724
169
  for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
725
  {
726
216
    Node em = nce.first;
727
108
    const DType& dt = em.getType().getDType();
728
216
    std::vector<Node> lemmas;
729
881
    for (std::pair<const unsigned, bool>& nc : nce.second)
730
    {
731
773
      Assert(nc.first < dt.getNumConstructors());
732
773
      if (!nc.second)
733
      {
734
442
        Node tst = datatypes::utils::mkTester(em, nc.first, dt).negate();
735
736
221
        if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
737
        {
738
442
          Trace("sygus-unif") << "...can exclude based on  : " << tst
739
221
                              << std::endl;
740
221
          lemmas.push_back(tst);
741
        }
742
      }
743
    }
744
108
    if (!lemmas.empty())
745
    {
746
74
      strategy_lemmas[em] = lemmas;
747
    }
748
  }
749
61
}
750
751
61
void SygusUnifStrategy::debugPrint(const char* c)
752
{
753
61
  if (Trace.isOn(c))
754
  {
755
    std::map<Node, std::map<NodeRole, bool> > visited;
756
    debugPrint(c, getRootEnumerator(), role_equal, visited, 0);
757
  }
758
61
}
759
760
279
void SygusUnifStrategy::staticLearnRedundantOps(
761
    Node e,
762
    NodeRole nrole,
763
    std::map<Node, std::map<NodeRole, bool>>& visited,
764
    std::map<Node, std::map<unsigned, bool>>& needs_cons,
765
    StrategyRestrictions& restrictions)
766
{
767
279
  if (visited[e].find(nrole) != visited[e].end())
768
  {
769
294
    return;
770
  }
771
288
  Trace("sygus-strat-slearn") << "Learn redundant operators " << e << " "
772
144
                              << nrole << "..." << std::endl;
773
144
  visited[e][nrole] = true;
774
144
  EnumInfo& ei = getEnumInfo(e);
775
144
  if (ei.isTemplated())
776
  {
777
24
    return;
778
  }
779
240
  TypeNode etn = e.getType();
780
120
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
781
120
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
782
  // the constructors of the current strategy point we need
783
240
  std::map<unsigned, bool> needs_cons_curr;
784
  // get the unused strategies
785
  std::map<Node, std::unordered_set<unsigned>>::iterator itus =
786
120
      restrictions.d_unused_strategies.find(e);
787
240
  std::unordered_set<unsigned> unused_strats;
788
120
  if (itus != restrictions.d_unused_strategies.end())
789
  {
790
2
    unused_strats.insert(itus->second.begin(), itus->second.end());
791
  }
792
211
  for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
793
  {
794
    // if we are not using this strategy, there is nothing to do
795
91
    if (unused_strats.find(j) != unused_strats.end())
796
    {
797
2
      continue;
798
    }
799
89
    EnumTypeInfoStrat* etis = snode.d_strats[j];
800
89
    unsigned cindex = datatypes::utils::indexOf(etis->d_cons);
801
    // constructors that correspond to strategies are not needed
802
    // the intuition is that the strategy itself is responsible for constructing
803
    // all terms that use the given constructor
804
178
    Trace("sygus-strat-slearn") << "...by strategy, can exclude operator "
805
89
                                << etis->d_cons << std::endl;
806
89
    needs_cons_curr[cindex] = false;
807
    // try to eliminate from etn's datatype all operators except TRUE/FALSE if
808
    // arguments of ITE are the same BOOL type
809
89
    if (restrictions.d_iteReturnBoolConst)
810
    {
811
11
      const DType& dt = etn.getDType();
812
22
      Node op = dt[cindex].getSygusOp();
813
22
      TypeNode sygus_tn = dt.getSygusType();
814
22
      if (op.getKind() == kind::BUILTIN
815
18
          && NodeManager::operatorToKind(op) == ITE && sygus_tn.isBoolean()
816
28
          && (dt[cindex].getArgType(1) == dt[cindex].getArgType(2)))
817
      {
818
6
        unsigned ncons = dt.getNumConstructors(), indexT = ncons,
819
6
                 indexF = ncons;
820
128
        for (unsigned k = 0; k < ncons; ++k)
821
        {
822
134
          Node op_arg = dt[k].getSygusOp();
823
122
          if (dt[k].getNumArgs() > 0 || !op_arg.isConst())
824
          {
825
110
            continue;
826
          }
827
12
          if (op_arg.getConst<bool>())
828
          {
829
6
            indexT = k;
830
          }
831
          else
832
          {
833
6
            indexF = k;
834
          }
835
        }
836
6
        if (indexT < ncons && indexF < ncons)
837
        {
838
12
          Trace("sygus-strat-slearn")
839
6
              << "...for ite boolean arg, can exclude all operators but T/F\n";
840
128
          for (unsigned k = 0; k < ncons; ++k)
841
          {
842
122
            needs_cons_curr[k] = false;
843
          }
844
6
          needs_cons_curr[indexT] = true;
845
6
          needs_cons_curr[indexF] = true;
846
        }
847
      }
848
    }
849
307
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
850
    {
851
218
      staticLearnRedundantOps(
852
          cec.first, cec.second, visited, needs_cons, restrictions);
853
    }
854
  }
855
  // get the current datatype
856
120
  const DType& dt = etn.getDType();
857
  // do not use recursive Boolean connectives for conditions of ITEs
858
120
  if (nrole == role_ite_condition && restrictions.d_iteCondOnlyAtoms)
859
  {
860
46
    TypeNode sygus_tn = dt.getSygusType();
861
204
    for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
862
    {
863
338
      Node op = dt[j].getSygusOp();
864
362
      Trace("sygus-strat-slearn")
865
181
          << "...for ite condition, look at operator : " << op << std::endl;
866
205
      if (op.isConst() && dt[j].getNumArgs() == 0)
867
      {
868
48
        Trace("sygus-strat-slearn")
869
24
            << "...for ite condition, can exclude Boolean constant " << op
870
24
            << std::endl;
871
24
        needs_cons_curr[j] = false;
872
24
        continue;
873
      }
874
157
      if (op.getKind() == kind::BUILTIN)
875
      {
876
42
        Kind kind = NodeManager::operatorToKind(op);
877
42
        if (kind == NOT || kind == OR || kind == AND || kind == ITE)
878
        {
879
          // can eliminate if their argument types are simple loops to this type
880
28
          bool type_ok = true;
881
84
          for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
882
          {
883
112
            TypeNode tn = dt[j].getArgType(k);
884
56
            if (tn != etn)
885
            {
886
              type_ok = false;
887
              break;
888
            }
889
          }
890
28
          if (type_ok)
891
          {
892
56
            Trace("sygus-strat-slearn")
893
28
                << "...for ite condition, can exclude Boolean connective : "
894
28
                << op << std::endl;
895
28
            needs_cons_curr[j] = false;
896
          }
897
        }
898
      }
899
    }
900
  }
901
  // all other constructors are needed
902
954
  for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
903
  {
904
834
    if (needs_cons_curr.find(j) == needs_cons_curr.end())
905
    {
906
591
      needs_cons_curr[j] = true;
907
    }
908
  }
909
  // update the constructors that the master enumerator needs
910
120
  if (needs_cons.find(e) == needs_cons.end())
911
  {
912
108
    needs_cons[e] = needs_cons_curr;
913
  }
914
  else
915
  {
916
73
    for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
917
    {
918
61
      needs_cons[e][j] = needs_cons[e][j] || needs_cons_curr[j];
919
    }
920
  }
921
}
922
923
394
void SygusUnifStrategy::finishInit(
924
    Node e,
925
    NodeRole nrole,
926
    std::map<Node, std::map<NodeRole, bool> >& visited,
927
    bool isCond)
928
{
929
394
  EnumInfo& ei = getEnumInfo(e);
930
788
  if (visited[e].find(nrole) != visited[e].end()
931
394
      && (!isCond || ei.isConditional()))
932
  {
933
448
    return;
934
  }
935
182
  visited[e][nrole] = true;
936
  // set conditional
937
182
  if (isCond)
938
  {
939
73
    ei.setConditional();
940
  }
941
182
  if (ei.isTemplated())
942
  {
943
24
    return;
944
  }
945
316
  TypeNode etn = e.getType();
946
158
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
947
158
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
948
289
  for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
949
  {
950
131
    EnumTypeInfoStrat* etis = snode.d_strats[j];
951
131
    StrategyType strat = etis->d_this;
952
131
    bool newIsCond = isCond || strat == strat_ITE;
953
464
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
954
    {
955
333
      finishInit(cec.first, cec.second, visited, newIsCond);
956
    }
957
  }
958
}
959
960
void SygusUnifStrategy::debugPrint(
961
    const char* c,
962
    Node e,
963
    NodeRole nrole,
964
    std::map<Node, std::map<NodeRole, bool> >& visited,
965
    int ind)
966
{
967
  if (visited[e].find(nrole) != visited[e].end())
968
  {
969
    indent(c, ind);
970
    Trace(c) << e << " :: node role : " << nrole << std::endl;
971
    return;
972
  }
973
  visited[e][nrole] = true;
974
  EnumInfo& ei = getEnumInfo(e);
975
976
  TypeNode etn = e.getType();
977
978
  indent(c, ind);
979
  Trace(c) << e << " :: node role : " << nrole;
980
  Trace(c) << ", type : " << etn.getDType().getName();
981
  if (ei.isConditional())
982
  {
983
    Trace(c) << ", conditional";
984
  }
985
  Trace(c) << ", enum role : " << ei.getRole();
986
987
  if (ei.isTemplated())
988
  {
989
    Trace(c) << ", templated : (lambda " << ei.d_template_arg << " "
990
             << ei.d_template << ")" << std::endl;
991
    return;
992
  }
993
  Trace(c) << std::endl;
994
995
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
996
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
997
  for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
998
  {
999
    EnumTypeInfoStrat* etis = snode.d_strats[j];
1000
    StrategyType strat = etis->d_this;
1001
    indent(c, ind + 1);
1002
    Trace(c) << "Strategy : " << strat << ", from cons : " << etis->d_cons
1003
             << std::endl;
1004
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
1005
    {
1006
      // recurse
1007
      debugPrint(c, cec.first, cec.second, visited, ind + 2);
1008
    }
1009
  }
1010
}
1011
1012
150
void EnumInfo::initialize(EnumRole role) { d_role = role; }
1013
1014
439
StrategyNode& EnumTypeInfo::getStrategyNode(NodeRole nrole)
1015
{
1016
439
  std::map<NodeRole, StrategyNode>::iterator it = d_snodes.find(nrole);
1017
439
  Assert(it != d_snodes.end());
1018
439
  return it->second;
1019
}
1020
1021
3077
bool EnumTypeInfoStrat::isValid(UnifContext& x)
1022
{
1023
6154
  if ((x.getCurrentRole() == role_string_prefix
1024
1355
       && d_this == strat_CONCAT_SUFFIX)
1025
6300
      || (x.getCurrentRole() == role_string_suffix
1026
773
          && d_this == strat_CONCAT_PREFIX))
1027
  {
1028
478
    return false;
1029
  }
1030
2599
  return true;
1031
}
1032
1033
276
StrategyNode::~StrategyNode()
1034
{
1035
233
  for (unsigned j = 0, size = d_strats.size(); j < size; j++)
1036
  {
1037
95
    delete d_strats[j];
1038
  }
1039
138
  d_strats.clear();
1040
138
}
1041
1042
void SygusUnifStrategy::indent(const char* c, int ind)
1043
{
1044
  if (Trace.isOn(c))
1045
  {
1046
    for (int i = 0; i < ind; i++)
1047
    {
1048
      Trace(c) << "  ";
1049
    }
1050
  }
1051
}
1052
1053
}  // namespace quantifiers
1054
}  // namespace theory
1055
29577
}  // namespace cvc5