GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_unif_strat.cpp Lines: 479 571 83.9 %
Date: 2021-11-07 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
647
EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
61
{
62
647
  switch (r)
63
  {
64
297
    case role_equal: return enum_io; break;
65
112
    case role_string_prefix: return enum_concat_term; break;
66
118
    case role_string_suffix: return enum_concat_term; break;
67
120
    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
107
void SygusUnifStrategy::initialize(TermDbSygus* tds,
87
                                   Node f,
88
                                   std::vector<Node>& enums)
89
{
90
107
  Assert(d_candidate.isNull());
91
107
  d_candidate = f;
92
107
  d_root = f.getType();
93
107
  d_tds = tds;
94
95
  // collect the enumerator types and form the strategy
96
107
  buildStrategyGraph(d_root, role_equal);
97
  // add the enumerators
98
107
  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
214
  std::map<Node, std::map<NodeRole, bool> > visited;
102
107
  finishInit(getRootEnumerator(), role_equal, visited, false);
103
107
}
104
105
153
void SygusUnifStrategy::initializeType(TypeNode tn)
106
{
107
306
  Trace("sygus-unif") << "SygusUnifStrategy: initialize : " << tn << " for "
108
153
                      << d_candidate << std::endl;
109
153
  d_tinfo[tn].d_this_type = tn;
110
153
}
111
112
1310
Node SygusUnifStrategy::getRootEnumerator() const
113
{
114
1310
  std::map<TypeNode, EnumTypeInfo>::const_iterator itt = d_tinfo.find(d_root);
115
1310
  Assert(itt != d_tinfo.end());
116
  std::map<EnumRole, Node>::const_iterator it =
117
1310
      itt->second.d_enum.find(enum_io);
118
1310
  Assert(it != itt->second.d_enum.end());
119
1310
  return it->second;
120
}
121
122
12320
EnumInfo& SygusUnifStrategy::getEnumInfo(Node e)
123
{
124
12320
  std::map<Node, EnumInfo>::iterator it = d_einfo.find(e);
125
12320
  Assert(it != d_einfo.end());
126
12320
  return it->second;
127
}
128
129
7318
EnumTypeInfo& SygusUnifStrategy::getEnumTypeInfo(TypeNode tn)
130
{
131
14636
  Trace("sygus-unif") << "SygusUnifStrategy: get : " << tn << " for "
132
7318
                      << d_candidate << std::endl;
133
7318
  std::map<TypeNode, EnumTypeInfo>::iterator it = d_tinfo.find(tn);
134
7318
  Assert(it != d_tinfo.end());
135
7318
  return it->second;
136
}
137
// ----------------------------- establishing enumeration types
138
139
287
void SygusUnifStrategy::registerStrategyPoint(Node et,
140
                                           TypeNode tn,
141
                                           EnumRole enum_role,
142
                                           bool inSearch)
143
{
144
287
  if (d_einfo.find(et) == d_einfo.end())
145
  {
146
514
    Trace("sygus-unif-debug")
147
257
        << "...register " << et << " for " << tn.getDType().getName();
148
514
    Trace("sygus-unif-debug") << ", role = " << enum_role
149
257
                              << ", in search = " << inSearch << std::endl;
150
257
    d_einfo[et].initialize(enum_role);
151
    // if we are actually enumerating this (could be a compound node in the
152
    // strategy)
153
257
    if (inSearch)
154
    {
155
250
      std::map<TypeNode, Node>::iterator itn = d_master_enum.find(tn);
156
250
      if (itn == d_master_enum.end())
157
      {
158
        // use this for the search
159
146
        d_master_enum[tn] = et;
160
146
        d_esym_list.push_back(et);
161
146
        d_einfo[et].d_enum_slave.push_back(et);
162
      }
163
      else
164
      {
165
208
        Trace("sygus-unif-debug") << "Make " << et << " a slave of "
166
104
                                  << itn->second << std::endl;
167
104
        d_einfo[itn->second].d_enum_slave.push_back(et);
168
      }
169
    }
170
  }
171
287
}
172
173
510
void SygusUnifStrategy::buildStrategyGraph(TypeNode tn, NodeRole nrole)
174
{
175
510
  NodeManager* nm = NodeManager::currentNM();
176
510
  SkolemManager* sm = nm->getSkolemManager();
177
510
  if (d_tinfo.find(tn) == d_tinfo.end())
178
  {
179
    // register type
180
153
    Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl;
181
153
    initializeType(tn);
182
  }
183
510
  EnumTypeInfo& eti = d_tinfo[tn];
184
510
  std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
185
510
  if (itsn != eti.d_snodes.end())
186
  {
187
    // already initialized
188
590
    return;
189
  }
190
244
  StrategyNode& snode = eti.d_snodes[nrole];
191
192
  // get the enumerator for this
193
244
  EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
194
195
430
  Node ee;
196
244
  std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
197
244
  if (iten == eti.d_enum.end())
198
  {
199
214
    ee = sm->mkDummySkolem("ee", tn);
200
214
    eti.d_enum[erole] = ee;
201
428
    Trace("sygus-unif-debug")
202
428
        << "...enumerator " << ee << " for " << tn.getDType().getName()
203
214
        << ", role = " << erole << std::endl;
204
  }
205
  else
206
  {
207
30
    ee = iten->second;
208
  }
209
210
  // roles that we do not recurse on
211
244
  if (nrole == role_ite_condition)
212
  {
213
58
    Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
214
58
    registerStrategyPoint(ee, tn, erole, true);
215
58
    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
186
  Assert(tn.isDatatype());
222
186
  const DType& dt = tn.getDType();
223
186
  Assert(dt.isSygus());
224
225
372
  std::map<Node, std::vector<StrategyType> > cop_to_strat;
226
372
  std::map<Node, unsigned> cop_to_cindex;
227
372
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ;
228
372
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ_arg;
229
372
  std::map<Node, std::vector<unsigned> > cop_to_carg_list;
230
372
  std::map<Node, std::vector<TypeNode> > cop_to_child_types;
231
372
  std::map<Node, std::vector<Node> > cop_to_sks;
232
233
  // whether we will enumerate the current type
234
186
  bool search_this = false;
235
1363
  for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
236
  {
237
2354
    Node cop = dt[j].getConstructor();
238
2354
    Node op = dt[j].getSygusOp();
239
2354
    Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
240
1177
                              << " with sygus op " << op << "..." << std::endl;
241
242
    // expand the evaluation to see if this constuctor induces a strategy
243
2354
    std::vector<Node> utchildren;
244
1177
    utchildren.push_back(cop);
245
2354
    std::vector<Node> sks;
246
2354
    std::vector<TypeNode> sktns;
247
2208
    for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
248
    {
249
2062
      TypeNode ttn = dt[j][k].getRangeType();
250
2062
      Node kv = sm->mkDummySkolem("ut", ttn);
251
1031
      sks.push_back(kv);
252
1031
      cop_to_sks[cop].push_back(kv);
253
1031
      sktns.push_back(ttn);
254
1031
      utchildren.push_back(kv);
255
    }
256
2354
    Node ut = nm->mkNode(APPLY_CONSTRUCTOR, utchildren);
257
2354
    std::vector<Node> echildren;
258
1177
    echildren.push_back(ut);
259
2354
    Node sbvl = dt.getSygusVarList();
260
6267
    for (const Node& sbv : sbvl)
261
    {
262
5090
      echildren.push_back(sbv);
263
    }
264
2354
    Node eut = nm->mkNode(DT_SYGUS_EVAL, echildren);
265
2354
    Trace("sygus-unif-debug2") << "  Test evaluation of " << eut << "..."
266
1177
                               << std::endl;
267
1177
    eut = d_tds->rewriteNode(eut);
268
1177
    Trace("sygus-unif-debug2") << "  ...got " << eut;
269
1177
    Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
270
271
    // candidate strategy
272
1177
    if (eut.getKind() == ITE)
273
    {
274
68
      cop_to_strat[cop].push_back(strat_ITE);
275
    }
276
1109
    else if (eut.getKind() == STRING_CONCAT)
277
    {
278
76
      if (nrole != role_string_suffix)
279
      {
280
50
        cop_to_strat[cop].push_back(strat_CONCAT_PREFIX);
281
      }
282
76
      if (nrole != role_string_prefix)
283
      {
284
52
        cop_to_strat[cop].push_back(strat_CONCAT_SUFFIX);
285
      }
286
    }
287
1033
    else if (dt[j].isSygusIdFunc())
288
    {
289
5
      cop_to_strat[cop].push_back(strat_ID);
290
    }
291
292
    // the kinds for which there is a strategy
293
1177
    if (cop_to_strat.find(cop) != cop_to_strat.end())
294
    {
295
      // infer an injection from the arguments of the datatype
296
298
      std::map<unsigned, unsigned> templ_injection;
297
298
      std::vector<Node> vs;
298
298
      std::vector<Node> ss;
299
298
      std::map<Node, unsigned> templ_var_index;
300
522
      for (unsigned k = 0, sksize = sks.size(); k < sksize; k++)
301
      {
302
373
        Assert(sks[k].getType().isDatatype());
303
373
        echildren[0] = sks[k];
304
746
        Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k]
305
373
                                   << std::endl;
306
746
        Node esk = nm->mkNode(DT_SYGUS_EVAL, echildren);
307
373
        vs.push_back(esk);
308
746
        Node tvar = sm->mkDummySkolem("templ", esk.getType());
309
373
        templ_var_index[tvar] = k;
310
746
        Trace("sygus-unif-debug2") << "* template inference : looking for "
311
373
                                   << tvar << " for arg " << k << std::endl;
312
373
        ss.push_back(tvar);
313
746
        Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar
314
373
                                   << std::endl;
315
      }
316
149
      eut = eut.substitute(vs.begin(), vs.end(), ss.begin(), ss.end());
317
298
      Trace("sygus-unif-debug2") << "Constructor " << j << ", base term is "
318
149
                                 << eut << std::endl;
319
298
      std::map<unsigned, Node> test_args;
320
149
      if (dt[j].isSygusIdFunc())
321
      {
322
5
        test_args[0] = eut;
323
      }
324
      else
325
      {
326
524
        for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
327
        {
328
380
          test_args[k] = eut[k];
329
        }
330
      }
331
332
      // TODO : prefix grouping prefix/suffix
333
149
      bool isAssoc = TermUtil::isAssoc(eut.getKind());
334
298
      Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
335
149
                                 << std::endl;
336
298
      std::map<unsigned, std::vector<unsigned> > assoc_combine;
337
298
      std::vector<unsigned> assoc_waiting;
338
149
      int assoc_last_valid_index = -1;
339
534
      for (std::pair<const unsigned, Node>& ta : test_args)
340
      {
341
385
        unsigned k = ta.first;
342
770
        Node eut_c = ta.second;
343
        // success if we can find a injection from args to sygus args
344
385
        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
385
        std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
352
385
        if (itti != templ_injection.end())
353
        {
354
          // if associative, combine arguments if it is the same variable
355
525
          if (isAssoc && assoc_last_valid_index >= 0
356
449
              && 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
367
            assoc_last_valid_index = (int)k;
364
367
            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
367
            assoc_combine[k].push_back(k);
372
          }
373
        }
374
        else
375
        {
376
          // a ground argument
377
18
          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
18
            if (assoc_last_valid_index >= 0)
388
            {
389
18
              assoc_combine[assoc_last_valid_index].push_back(k);
390
            }
391
            else
392
            {
393
              assoc_waiting.push_back(k);
394
            }
395
          }
396
        }
397
      }
398
149
      if (cop_to_strat.find(cop) != cop_to_strat.end())
399
      {
400
        // construct the templates
401
149
        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
522
          for (std::pair<const unsigned, Node>& ta : test_args)
409
          {
410
379
            unsigned k = ta.first;
411
758
            Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
412
379
                                       << std::endl;
413
379
            if (templ_injection.find(k) != templ_injection.end())
414
            {
415
361
              unsigned sk_index = templ_injection[k];
416
1444
              if (std::find(cop_to_carg_list[cop].begin(),
417
361
                            cop_to_carg_list[cop].end(),
418
722
                            sk_index)
419
1083
                  == cop_to_carg_list[cop].end())
420
              {
421
355
                cop_to_carg_list[cop].push_back(sk_index);
422
              }
423
              else
424
              {
425
12
                Trace("sygus-unif-debug") << "...fail: duplicate argument used"
426
6
                                          << std::endl;
427
6
                cop_to_strat.erase(cop);
428
6
                break;
429
              }
430
              // also store the template information, if necessary
431
710
              Node teut;
432
355
              if (isAssoc)
433
              {
434
158
                std::vector<unsigned>& ac = assoc_combine[k];
435
158
                Assert(!ac.empty());
436
316
                std::vector<Node> children;
437
334
                for (unsigned ack = 0, size_ac = ac.size(); ack < size_ac;
438
                     ack++)
439
                {
440
176
                  children.push_back(eut[ac[ack]]);
441
                }
442
316
                teut = children.size() == 1
443
316
                           ? children[0]
444
                           : nm->mkNode(eut.getKind(), children);
445
158
                teut = rewrite(teut);
446
              }
447
              else
448
              {
449
197
                teut = ta.second;
450
              }
451
452
355
              if (!teut.isVar())
453
              {
454
43
                cop_to_child_templ[cop][k] = teut;
455
43
                cop_to_child_templ_arg[cop][k] = ss[sk_index];
456
86
                Trace("sygus-unif-debug")
457
43
                    << "  Arg " << k << " (template : " << teut << " arg "
458
43
                    << ss[sk_index] << "), index " << sk_index << std::endl;
459
              }
460
              else
461
              {
462
624
                Trace("sygus-unif-debug") << "  Arg " << k << ", index "
463
312
                                          << sk_index << std::endl;
464
312
                Assert(teut == ss[sk_index]);
465
              }
466
            }
467
            else
468
            {
469
18
              Assert(isAssoc);
470
            }
471
          }
472
        }
473
      }
474
    }
475
476
1177
    std::map<Node, std::vector<StrategyType> >::iterator itcs = cop_to_strat.find(cop);
477
1177
    if (itcs != cop_to_strat.end())
478
    {
479
286
      Trace("sygus-unif") << "-> constructor " << cop
480
286
                          << " matches strategy for " << eut.getKind() << "..."
481
143
                          << std::endl;
482
      // collect children types
483
492
      for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
484
      {
485
698
        TypeNode ctn = sktns[cop_to_carg_list[cop][k]];
486
698
        Trace("sygus-unif-debug") << "   Child type " << k << " : "
487
349
                                  << ctn.getDType().getName() << std::endl;
488
349
        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
1177
    if (cop_to_strat.find(cop) == cop_to_strat.end())
494
    {
495
2068
      Trace("sygus-unif") << "...constructor " << cop
496
1034
                          << " does not correspond to a strategy." << std::endl;
497
1034
      search_this = true;
498
    }
499
  }
500
501
  // check whether we should also enumerate the current type
502
186
  Trace("sygus-unif-debug2") << "  register this strategy ..." << std::endl;
503
186
  registerStrategyPoint(ee, tn, erole, search_this);
504
505
186
  if (cop_to_strat.empty())
506
  {
507
108
    Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type"
508
54
                        << std::endl;
509
  }
510
  else
511
  {
512
275
    for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
513
    {
514
286
      Node cop = cstr.first;
515
286
      Trace("sygus-unif-debug") << "Constructor " << cop << " has "
516
286
                                << cstr.second.size() << " strategies..."
517
143
                                << std::endl;
518
312
      for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
519
      {
520
169
        EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
521
169
        StrategyType strat = cstr.second[s];
522
523
169
        cons_strat->d_this = strat;
524
169
        cons_strat->d_cons = cop;
525
338
        Trace("sygus-unif-debug") << "Process strategy #" << s
526
169
                                  << " for operator : " << cop << " : " << strat
527
169
                                  << std::endl;
528
169
        Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
529
169
        std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
530
169
        Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
531
169
        std::vector<unsigned>& cargList = cop_to_carg_list[cop];
532
533
338
        std::vector<Node> sol_templ_children;
534
169
        sol_templ_children.resize(cop_to_sks[cop].size());
535
536
572
        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
403
          NodeRole nrole_c = nrole;
541
403
          if (strat == strat_ITE)
542
          {
543
186
            if (j == 0)
544
            {
545
62
              nrole_c = role_ite_condition;
546
            }
547
          }
548
217
          else if (strat == strat_CONCAT_PREFIX)
549
          {
550
104
            if ((j + 1) < childTypes.size())
551
            {
552
54
              nrole_c = role_string_prefix;
553
            }
554
          }
555
113
          else if (strat == strat_CONCAT_SUFFIX)
556
          {
557
108
            if (j > 0)
558
            {
559
56
              nrole_c = role_string_suffix;
560
            }
561
          }
562
          // in all other cases, role is same as parent
563
564
          // register the child type
565
806
          TypeNode ct = childTypes[j];
566
806
          Node csk = cop_to_sks[cop][cargList[j]];
567
403
          cons_strat->d_sol_templ_args.push_back(csk);
568
403
          sol_templ_children[cargList[j]] = csk;
569
570
403
          EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
571
          // make the enumerator
572
806
          Node et;
573
          // Build the strategy recursively, regardless of whether the
574
          // enumerator is templated.
575
403
          buildStrategyGraph(ct, nrole_c);
576
403
          if (cop_to_child_templ[cop].find(j) != cop_to_child_templ[cop].end())
577
          {
578
            // it is templated, allocate a fresh variable
579
43
            et = sm->mkDummySkolem("et", ct);
580
86
            Trace("sygus-unif-debug") << "...enumerate " << et << " of type "
581
43
                                      << ct.getDType().getName();
582
86
            Trace("sygus-unif-debug") << " for arg " << j << " of "
583
43
                                      << tn.getDType().getName() << std::endl;
584
43
            registerStrategyPoint(et, ct, erole_c, true);
585
43
            d_einfo[et].d_template = cop_to_child_templ[cop][j];
586
43
            d_einfo[et].d_template_arg = cop_to_child_templ_arg[cop][j];
587
43
            Assert(!d_einfo[et].d_template.isNull());
588
43
            Assert(!d_einfo[et].d_template_arg.isNull());
589
          }
590
          else
591
          {
592
720
            Trace("sygus-unif-debug")
593
720
                << "...child type enumerate " << ct.getDType().getName()
594
360
                << ", node role = " << nrole_c << std::endl;
595
            // otherwise use the previous
596
360
            Assert(d_tinfo[ct].d_enum.find(erole_c)
597
                   != d_tinfo[ct].d_enum.end());
598
360
            et = d_tinfo[ct].d_enum[erole_c];
599
          }
600
806
          Trace("sygus-unif-debug") << "Register child enumerator " << et
601
403
                                    << ", arg " << j << " of " << cop
602
403
                                    << ", role = " << erole_c << std::endl;
603
403
          Assert(!et.isNull());
604
403
          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
596
        for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
608
             j++)
609
        {
610
427
          if (sol_templ_children[j].isNull())
611
          {
612
24
            sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
613
          }
614
        }
615
169
        sol_templ_children.insert(sol_templ_children.begin(), cop);
616
338
        cons_strat->d_sol_templ =
617
507
            nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
618
169
        if (strat == strat_CONCAT_SUFFIX)
619
        {
620
52
          std::reverse(cons_strat->d_cenum.begin(), cons_strat->d_cenum.end());
621
52
          std::reverse(cons_strat->d_sol_templ_args.begin(),
622
52
                       cons_strat->d_sol_templ_args.end());
623
        }
624
169
        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
169
        snode.d_strats.push_back(cons_strat);
640
      }
641
    }
642
  }
643
}
644
645
449
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
449
  if (n.getNumChildren() == 0)
652
  {
653
408
    std::map<Node, unsigned>::iterator itt = templ_var_index.find(n);
654
408
    if (itt != templ_var_index.end())
655
    {
656
367
      unsigned kk = itt->second;
657
367
      std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
658
367
      if (itti == templ_injection.end())
659
      {
660
734
        Trace("sygus-unif-debug") << "...set template injection " << k << " -> "
661
367
                                  << kk << std::endl;
662
367
        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
408
    return true;
671
  }
672
  else
673
  {
674
105
    for (unsigned i = 0; i < n.getNumChildren(); i++)
675
    {
676
64
      if (!inferTemplate(k, n[i], templ_var_index, templ_injection))
677
      {
678
        return false;
679
      }
680
    }
681
  }
682
41
  return true;
683
}
684
685
84
void SygusUnifStrategy::staticLearnRedundantOps(
686
    std::map<Node, std::vector<Node>>& strategy_lemmas)
687
{
688
168
  StrategyRestrictions restrictions;
689
84
  staticLearnRedundantOps(strategy_lemmas, restrictions);
690
84
}
691
692
107
void SygusUnifStrategy::staticLearnRedundantOps(
693
    std::map<Node, std::vector<Node>>& strategy_lemmas,
694
    StrategyRestrictions& restrictions)
695
{
696
253
  for (unsigned i = 0; i < d_esym_list.size(); i++)
697
  {
698
292
    Node e = d_esym_list[i];
699
146
    std::map<Node, EnumInfo>::iterator itn = d_einfo.find(e);
700
146
    Assert(itn != d_einfo.end());
701
    // see if there is anything we can eliminate
702
292
    Trace("sygus-unif") << "* Search enumerator #" << i << " : type "
703
146
                        << e.getType().getDType().getName() << " : ";
704
292
    Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size()
705
146
                        << " slaves:" << std::endl;
706
396
    for (unsigned j = 0; j < itn->second.d_enum_slave.size(); j++)
707
    {
708
500
      Node es = itn->second.d_enum_slave[j];
709
250
      std::map<Node, EnumInfo>::iterator itns = d_einfo.find(es);
710
250
      Assert(itns != d_einfo.end());
711
500
      Trace("sygus-unif") << "  " << es << ", role = " << itns->second.getRole()
712
250
                          << std::endl;
713
    }
714
  }
715
107
  Trace("sygus-unif") << std::endl;
716
214
  Trace("sygus-unif") << "Strategy for candidate " << d_candidate
717
107
                      << " is : " << std::endl;
718
107
  debugPrint("sygus-unif");
719
214
  std::map<Node, std::map<NodeRole, bool> > visited;
720
214
  std::map<Node, std::map<unsigned, bool> > needs_cons;
721
107
  staticLearnRedundantOps(
722
214
      getRootEnumerator(), role_equal, visited, needs_cons, restrictions);
723
  // now, check the needs_cons map
724
299
  for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
725
  {
726
384
    Node em = nce.first;
727
192
    const DType& dt = em.getType().getDType();
728
384
    std::vector<Node> lemmas;
729
1486
    for (std::pair<const unsigned, bool>& nc : nce.second)
730
    {
731
1294
      Assert(nc.first < dt.getNumConstructors());
732
1294
      if (!nc.second)
733
      {
734
690
        Node tst = datatypes::utils::mkTester(em, nc.first, dt).negate();
735
736
345
        if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
737
        {
738
690
          Trace("sygus-unif") << "...can exclude based on  : " << tst
739
345
                              << std::endl;
740
345
          lemmas.push_back(tst);
741
        }
742
      }
743
    }
744
192
    if (!lemmas.empty())
745
    {
746
128
      strategy_lemmas[em] = lemmas;
747
    }
748
  }
749
107
}
750
751
107
void SygusUnifStrategy::debugPrint(const char* c)
752
{
753
107
  if (Trace.isOn(c))
754
  {
755
    std::map<Node, std::map<NodeRole, bool> > visited;
756
    debugPrint(c, getRootEnumerator(), role_equal, visited, 0);
757
  }
758
107
}
759
760
495
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
495
  if (visited[e].find(nrole) != visited[e].end())
768
  {
769
521
    return;
770
  }
771
506
  Trace("sygus-strat-slearn") << "Learn redundant operators " << e << " "
772
253
                              << nrole << "..." << std::endl;
773
253
  visited[e][nrole] = true;
774
253
  EnumInfo& ei = getEnumInfo(e);
775
253
  if (ei.isTemplated())
776
  {
777
37
    return;
778
  }
779
432
  TypeNode etn = e.getType();
780
216
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
781
216
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
782
  // the constructors of the current strategy point we need
783
432
  std::map<unsigned, bool> needs_cons_curr;
784
  // get the unused strategies
785
  std::map<Node, std::unordered_set<unsigned>>::iterator itus =
786
216
      restrictions.d_unused_strategies.find(e);
787
432
  std::unordered_set<unsigned> unused_strats;
788
216
  if (itus != restrictions.d_unused_strategies.end())
789
  {
790
3
    unused_strats.insert(itus->second.begin(), itus->second.end());
791
  }
792
379
  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
163
    if (unused_strats.find(j) != unused_strats.end())
796
    {
797
3
      continue;
798
    }
799
160
    EnumTypeInfoStrat* etis = snode.d_strats[j];
800
160
    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
320
    Trace("sygus-strat-slearn") << "...by strategy, can exclude operator "
805
160
                                << etis->d_cons << std::endl;
806
160
    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
160
    if (restrictions.d_iteReturnBoolConst)
810
    {
811
17
      const DType& dt = etn.getDType();
812
34
      Node op = dt[cindex].getSygusOp();
813
34
      TypeNode sygus_tn = dt.getSygusType();
814
34
      if (op.getKind() == kind::BUILTIN
815
27
          && NodeManager::operatorToKind(op) == ITE && sygus_tn.isBoolean()
816
42
          && (dt[cindex].getArgType(1) == dt[cindex].getArgType(2)))
817
      {
818
8
        unsigned ncons = dt.getNumConstructors(), indexT = ncons,
819
8
                 indexF = ncons;
820
183
        for (unsigned k = 0; k < ncons; ++k)
821
        {
822
191
          Node op_arg = dt[k].getSygusOp();
823
175
          if (dt[k].getNumArgs() > 0 || !op_arg.isConst())
824
          {
825
159
            continue;
826
          }
827
16
          if (op_arg.getConst<bool>())
828
          {
829
8
            indexT = k;
830
          }
831
          else
832
          {
833
8
            indexF = k;
834
          }
835
        }
836
8
        if (indexT < ncons && indexF < ncons)
837
        {
838
16
          Trace("sygus-strat-slearn")
839
8
              << "...for ite boolean arg, can exclude all operators but T/F\n";
840
183
          for (unsigned k = 0; k < ncons; ++k)
841
          {
842
175
            needs_cons_curr[k] = false;
843
          }
844
8
          needs_cons_curr[indexT] = true;
845
8
          needs_cons_curr[indexF] = true;
846
        }
847
      }
848
    }
849
548
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
850
    {
851
388
      staticLearnRedundantOps(
852
          cec.first, cec.second, visited, needs_cons, restrictions);
853
    }
854
  }
855
  // get the current datatype
856
216
  const DType& dt = etn.getDType();
857
  // do not use recursive Boolean connectives for conditions of ITEs
858
216
  if (nrole == role_ite_condition && restrictions.d_iteCondOnlyAtoms)
859
  {
860
78
    TypeNode sygus_tn = dt.getSygusType();
861
323
    for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
862
    {
863
528
      Node op = dt[j].getSygusOp();
864
568
      Trace("sygus-strat-slearn")
865
284
          << "...for ite condition, look at operator : " << op << std::endl;
866
324
      if (op.isConst() && dt[j].getNumArgs() == 0)
867
      {
868
80
        Trace("sygus-strat-slearn")
869
40
            << "...for ite condition, can exclude Boolean constant " << op
870
40
            << std::endl;
871
40
        needs_cons_curr[j] = false;
872
40
        continue;
873
      }
874
244
      if (op.getKind() == kind::BUILTIN)
875
      {
876
60
        Kind kind = NodeManager::operatorToKind(op);
877
60
        if (kind == NOT || kind == OR || kind == AND || kind == ITE)
878
        {
879
          // can eliminate if their argument types are simple loops to this type
880
40
          bool type_ok = true;
881
120
          for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
882
          {
883
160
            TypeNode tn = dt[j].getArgType(k);
884
80
            if (tn != etn)
885
            {
886
              type_ok = false;
887
              break;
888
            }
889
          }
890
40
          if (type_ok)
891
          {
892
80
            Trace("sygus-strat-slearn")
893
40
                << "...for ite condition, can exclude Boolean connective : "
894
40
                << op << std::endl;
895
40
            needs_cons_curr[j] = false;
896
          }
897
        }
898
      }
899
    }
900
  }
901
  // all other constructors are needed
902
1632
  for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
903
  {
904
1416
    if (needs_cons_curr.find(j) == needs_cons_curr.end())
905
    {
906
1035
      needs_cons_curr[j] = true;
907
    }
908
  }
909
  // update the constructors that the master enumerator needs
910
216
  if (needs_cons.find(e) == needs_cons.end())
911
  {
912
192
    needs_cons[e] = needs_cons_curr;
913
  }
914
  else
915
  {
916
146
    for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
917
    {
918
122
      needs_cons[e][j] = needs_cons[e][j] || needs_cons_curr[j];
919
    }
920
  }
921
}
922
923
687
void SygusUnifStrategy::finishInit(
924
    Node e,
925
    NodeRole nrole,
926
    std::map<Node, std::map<NodeRole, bool> >& visited,
927
    bool isCond)
928
{
929
687
  EnumInfo& ei = getEnumInfo(e);
930
1374
  if (visited[e].find(nrole) != visited[e].end()
931
687
      && (!isCond || ei.isConditional()))
932
  {
933
781
    return;
934
  }
935
315
  visited[e][nrole] = true;
936
  // set conditional
937
315
  if (isCond)
938
  {
939
121
    ei.setConditional();
940
  }
941
315
  if (ei.isTemplated())
942
  {
943
37
    return;
944
  }
945
556
  TypeNode etn = e.getType();
946
278
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
947
278
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
948
508
  for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
949
  {
950
230
    EnumTypeInfoStrat* etis = snode.d_strats[j];
951
230
    StrategyType strat = etis->d_this;
952
230
    bool newIsCond = isCond || strat == strat_ITE;
953
810
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
954
    {
955
580
      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
257
void EnumInfo::initialize(EnumRole role) { d_role = role; }
1013
1014
661
StrategyNode& EnumTypeInfo::getStrategyNode(NodeRole nrole)
1015
{
1016
661
  std::map<NodeRole, StrategyNode>::iterator it = d_snodes.find(nrole);
1017
661
  Assert(it != d_snodes.end());
1018
661
  return it->second;
1019
}
1020
1021
5776
bool EnumTypeInfoStrat::isValid(UnifContext& x)
1022
{
1023
11552
  if ((x.getCurrentRole() == role_string_prefix
1024
2578
       && d_this == strat_CONCAT_SUFFIX)
1025
11844
      || (x.getCurrentRole() == role_string_suffix
1026
1546
          && d_this == strat_CONCAT_PREFIX))
1027
  {
1028
912
    return false;
1029
  }
1030
4864
  return true;
1031
}
1032
1033
488
StrategyNode::~StrategyNode()
1034
{
1035
413
  for (unsigned j = 0, size = d_strats.size(); j < size; j++)
1036
  {
1037
169
    delete d_strats[j];
1038
  }
1039
244
  d_strats.clear();
1040
244
}
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
31137
}  // namespace cvc5