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