GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_unif_strat.cpp Lines: 475 571 83.2 %
Date: 2021-08-06 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
366
EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
62
{
63
366
  switch (r)
64
  {
65
171
    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
72
    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
61
void SygusUnifStrategy::initialize(TermDbSygus* tds,
88
                                   Node f,
89
                                   std::vector<Node>& enums)
90
{
91
61
  Assert(d_candidate.isNull());
92
61
  d_candidate = f;
93
61
  d_root = f.getType();
94
61
  d_tds = tds;
95
96
  // collect the enumerator types and form the strategy
97
61
  buildStrategyGraph(d_root, role_equal);
98
  // add the enumerators
99
61
  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
122
  std::map<Node, std::map<NodeRole, bool> > visited;
103
61
  finishInit(getRootEnumerator(), role_equal, visited, false);
104
61
}
105
106
86
void SygusUnifStrategy::initializeType(TypeNode tn)
107
{
108
172
  Trace("sygus-unif") << "SygusUnifStrategy: initialize : " << tn << " for "
109
86
                      << d_candidate << std::endl;
110
86
  d_tinfo[tn].d_this_type = tn;
111
86
}
112
113
889
Node SygusUnifStrategy::getRootEnumerator() const
114
{
115
889
  std::map<TypeNode, EnumTypeInfo>::const_iterator itt = d_tinfo.find(d_root);
116
889
  Assert(itt != d_tinfo.end());
117
  std::map<EnumRole, Node>::const_iterator it =
118
889
      itt->second.d_enum.find(enum_io);
119
889
  Assert(it != itt->second.d_enum.end());
120
889
  return it->second;
121
}
122
123
6833
EnumInfo& SygusUnifStrategy::getEnumInfo(Node e)
124
{
125
6833
  std::map<Node, EnumInfo>::iterator it = d_einfo.find(e);
126
6833
  Assert(it != d_einfo.end());
127
6833
  return it->second;
128
}
129
130
4096
EnumTypeInfo& SygusUnifStrategy::getEnumTypeInfo(TypeNode tn)
131
{
132
8192
  Trace("sygus-unif") << "SygusUnifStrategy: get : " << tn << " for "
133
4096
                      << d_candidate << std::endl;
134
4096
  std::map<TypeNode, EnumTypeInfo>::iterator it = d_tinfo.find(tn);
135
4096
  Assert(it != d_tinfo.end());
136
4096
  return it->second;
137
}
138
// ----------------------------- establishing enumeration types
139
140
166
void SygusUnifStrategy::registerStrategyPoint(Node et,
141
                                           TypeNode tn,
142
                                           EnumRole enum_role,
143
                                           bool inSearch)
144
{
145
166
  if (d_einfo.find(et) == d_einfo.end())
146
  {
147
300
    Trace("sygus-unif-debug")
148
150
        << "...register " << et << " for " << tn.getDType().getName();
149
300
    Trace("sygus-unif-debug") << ", role = " << enum_role
150
150
                              << ", in search = " << inSearch << std::endl;
151
150
    d_einfo[et].initialize(enum_role);
152
    // if we are actually enumerating this (could be a compound node in the
153
    // strategy)
154
150
    if (inSearch)
155
    {
156
146
      std::map<TypeNode, Node>::iterator itn = d_master_enum.find(tn);
157
146
      if (itn == d_master_enum.end())
158
      {
159
        // use this for the search
160
82
        d_master_enum[tn] = et;
161
82
        d_esym_list.push_back(et);
162
82
        d_einfo[et].d_enum_slave.push_back(et);
163
      }
164
      else
165
      {
166
128
        Trace("sygus-unif-debug") << "Make " << et << " a slave of "
167
64
                                  << itn->second << std::endl;
168
64
        d_einfo[itn->second].d_enum_slave.push_back(et);
169
      }
170
    }
171
  }
172
166
}
173
174
289
void SygusUnifStrategy::buildStrategyGraph(TypeNode tn, NodeRole nrole)
175
{
176
289
  NodeManager* nm = NodeManager::currentNM();
177
289
  SkolemManager* sm = nm->getSkolemManager();
178
289
  if (d_tinfo.find(tn) == d_tinfo.end())
179
  {
180
    // register type
181
86
    Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl;
182
86
    initializeType(tn);
183
  }
184
289
  EnumTypeInfo& eti = d_tinfo[tn];
185
289
  std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
186
289
  if (itsn != eti.d_snodes.end())
187
  {
188
    // already initialized
189
337
    return;
190
  }
191
138
  StrategyNode& snode = eti.d_snodes[nrole];
192
193
  // get the enumerator for this
194
138
  EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
195
196
241
  Node ee;
197
138
  std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
198
138
  if (iten == eti.d_enum.end())
199
  {
200
122
    ee = sm->mkDummySkolem("ee", tn);
201
122
    eti.d_enum[erole] = ee;
202
244
    Trace("sygus-unif-debug")
203
244
        << "...enumerator " << ee << " for " << tn.getDType().getName()
204
122
        << ", role = " << erole << std::endl;
205
  }
206
  else
207
  {
208
16
    ee = iten->second;
209
  }
210
211
  // roles that we do not recurse on
212
138
  if (nrole == role_ite_condition)
213
  {
214
35
    Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
215
35
    registerStrategyPoint(ee, tn, erole, true);
216
35
    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
103
  Assert(tn.isDatatype());
223
103
  const DType& dt = tn.getDType();
224
103
  Assert(dt.isSygus());
225
226
206
  std::map<Node, std::vector<StrategyType> > cop_to_strat;
227
206
  std::map<Node, unsigned> cop_to_cindex;
228
206
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ;
229
206
  std::map<Node, std::map<unsigned, Node> > cop_to_child_templ_arg;
230
206
  std::map<Node, std::vector<unsigned> > cop_to_carg_list;
231
206
  std::map<Node, std::vector<TypeNode> > cop_to_child_types;
232
206
  std::map<Node, std::vector<Node> > cop_to_sks;
233
234
  // whether we will enumerate the current type
235
103
  bool search_this = false;
236
786
  for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
237
  {
238
1366
    Node cop = dt[j].getConstructor();
239
1366
    Node op = dt[j].getSygusOp();
240
1366
    Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
241
683
                              << " with sygus op " << op << "..." << std::endl;
242
243
    // expand the evaluation to see if this constuctor induces a strategy
244
1366
    std::vector<Node> utchildren;
245
683
    utchildren.push_back(cop);
246
1366
    std::vector<Node> sks;
247
1366
    std::vector<TypeNode> sktns;
248
1286
    for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
249
    {
250
1206
      TypeNode ttn = dt[j][k].getRangeType();
251
1206
      Node kv = sm->mkDummySkolem("ut", ttn);
252
603
      sks.push_back(kv);
253
603
      cop_to_sks[cop].push_back(kv);
254
603
      sktns.push_back(ttn);
255
603
      utchildren.push_back(kv);
256
    }
257
1366
    Node ut = nm->mkNode(APPLY_CONSTRUCTOR, utchildren);
258
1366
    std::vector<Node> echildren;
259
683
    echildren.push_back(ut);
260
1366
    Node sbvl = dt.getSygusVarList();
261
3937
    for (const Node& sbv : sbvl)
262
    {
263
3254
      echildren.push_back(sbv);
264
    }
265
1366
    Node eut = nm->mkNode(DT_SYGUS_EVAL, echildren);
266
1366
    Trace("sygus-unif-debug2") << "  Test evaluation of " << eut << "..."
267
683
                               << std::endl;
268
683
    eut = d_tds->getEvalUnfold()->unfold(eut);
269
683
    Trace("sygus-unif-debug2") << "  ...got " << eut;
270
683
    Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
271
272
    // candidate strategy
273
683
    if (eut.getKind() == ITE)
274
    {
275
37
      cop_to_strat[cop].push_back(strat_ITE);
276
    }
277
646
    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
605
    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
683
    if (cop_to_strat.find(cop) != cop_to_strat.end())
295
    {
296
      // infer an injection from the arguments of the datatype
297
162
      std::map<unsigned, unsigned> templ_injection;
298
162
      std::vector<Node> vs;
299
162
      std::vector<Node> ss;
300
162
      std::map<Node, unsigned> templ_var_index;
301
292
      for (unsigned k = 0, sksize = sks.size(); k < sksize; k++)
302
      {
303
211
        Assert(sks[k].getType().isDatatype());
304
211
        echildren[0] = sks[k];
305
422
        Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k]
306
211
                                   << std::endl;
307
422
        Node esk = nm->mkNode(DT_SYGUS_EVAL, echildren);
308
211
        vs.push_back(esk);
309
422
        Node tvar = sm->mkDummySkolem("templ", esk.getType());
310
211
        templ_var_index[tvar] = k;
311
422
        Trace("sygus-unif-debug2") << "* template inference : looking for "
312
211
                                   << tvar << " for arg " << k << std::endl;
313
211
        ss.push_back(tvar);
314
422
        Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar
315
211
                                   << std::endl;
316
      }
317
81
      eut = eut.substitute(vs.begin(), vs.end(), ss.begin(), ss.end());
318
162
      Trace("sygus-unif-debug2") << "Constructor " << j << ", base term is "
319
81
                                 << eut << std::endl;
320
162
      std::map<unsigned, Node> test_args;
321
81
      if (dt[j].isSygusIdFunc())
322
      {
323
3
        test_args[0] = eut;
324
      }
325
      else
326
      {
327
286
        for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
328
        {
329
208
          test_args[k] = eut[k];
330
        }
331
      }
332
333
      // TODO : prefix grouping prefix/suffix
334
81
      bool isAssoc = TermUtil::isAssoc(eut.getKind());
335
162
      Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
336
81
                                 << std::endl;
337
162
      std::map<unsigned, std::vector<unsigned> > assoc_combine;
338
162
      std::vector<unsigned> assoc_waiting;
339
81
      int assoc_last_valid_index = -1;
340
292
      for (std::pair<const unsigned, Node>& ta : test_args)
341
      {
342
211
        unsigned k = ta.first;
343
422
        Node eut_c = ta.second;
344
        // success if we can find a injection from args to sygus args
345
211
        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
211
        std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
353
211
        if (itti != templ_injection.end())
354
        {
355
          // if associative, combine arguments if it is the same variable
356
284
          if (isAssoc && assoc_last_valid_index >= 0
357
243
              && 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
199
            assoc_last_valid_index = (int)k;
365
199
            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
199
            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
81
      if (cop_to_strat.find(cop) != cop_to_strat.end())
400
      {
401
        // construct the templates
402
81
        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
292
          for (std::pair<const unsigned, Node>& ta : test_args)
410
          {
411
211
            unsigned k = ta.first;
412
422
            Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
413
211
                                       << std::endl;
414
211
            if (templ_injection.find(k) != templ_injection.end())
415
            {
416
199
              unsigned sk_index = templ_injection[k];
417
796
              if (std::find(cop_to_carg_list[cop].begin(),
418
199
                            cop_to_carg_list[cop].end(),
419
398
                            sk_index)
420
597
                  == cop_to_carg_list[cop].end())
421
              {
422
199
                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
398
              Node teut;
433
199
              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
114
                teut = ta.second;
451
              }
452
453
199
              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
350
                Trace("sygus-unif-debug") << "  Arg " << k << ", index "
464
175
                                          << sk_index << std::endl;
465
175
                Assert(teut == ss[sk_index]);
466
              }
467
            }
468
            else
469
            {
470
12
              Assert(isAssoc);
471
            }
472
          }
473
        }
474
      }
475
    }
476
477
683
    std::map<Node, std::vector<StrategyType> >::iterator itcs = cop_to_strat.find(cop);
478
683
    if (itcs != cop_to_strat.end())
479
    {
480
162
      Trace("sygus-unif") << "-> constructor " << cop
481
162
                          << " matches strategy for " << eut.getKind() << "..."
482
81
                          << std::endl;
483
      // collect children types
484
280
      for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
485
      {
486
398
        TypeNode ctn = sktns[cop_to_carg_list[cop][k]];
487
398
        Trace("sygus-unif-debug") << "   Child type " << k << " : "
488
199
                                  << ctn.getDType().getName() << std::endl;
489
199
        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
683
    if (cop_to_strat.find(cop) == cop_to_strat.end())
495
    {
496
1204
      Trace("sygus-unif") << "...constructor " << cop
497
602
                          << " does not correspond to a strategy." << std::endl;
498
602
      search_this = true;
499
    }
500
  }
501
502
  // check whether we should also enumerate the current type
503
103
  Trace("sygus-unif-debug2") << "  register this strategy ..." << std::endl;
504
103
  registerStrategyPoint(ee, tn, erole, search_this);
505
506
103
  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
156
    for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
514
    {
515
162
      Node cop = cstr.first;
516
162
      Trace("sygus-unif-debug") << "Constructor " << cop << " has "
517
162
                                << cstr.second.size() << " strategies..."
518
81
                                << std::endl;
519
176
      for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
520
      {
521
95
        EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
522
95
        StrategyType strat = cstr.second[s];
523
524
95
        cons_strat->d_this = strat;
525
95
        cons_strat->d_cons = cop;
526
190
        Trace("sygus-unif-debug") << "Process strategy #" << s
527
95
                                  << " for operator : " << cop << " : " << strat
528
95
                                  << std::endl;
529
95
        Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
530
95
        std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
531
95
        Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
532
95
        std::vector<unsigned>& cargList = cop_to_carg_list[cop];
533
534
190
        std::vector<Node> sol_templ_children;
535
95
        sol_templ_children.resize(cop_to_sks[cop].size());
536
537
323
        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
228
          NodeRole nrole_c = nrole;
542
228
          if (strat == strat_ITE)
543
          {
544
111
            if (j == 0)
545
            {
546
37
              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
456
          TypeNode ct = childTypes[j];
567
456
          Node csk = cop_to_sks[cop][cargList[j]];
568
228
          cons_strat->d_sol_templ_args.push_back(csk);
569
228
          sol_templ_children[cargList[j]] = csk;
570
571
228
          EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
572
          // make the enumerator
573
456
          Node et;
574
          // Build the strategy recursively, regardless of whether the
575
          // enumerator is templated.
576
228
          buildStrategyGraph(ct, nrole_c);
577
228
          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
400
            Trace("sygus-unif-debug")
594
400
                << "...child type enumerate " << ct.getDType().getName()
595
200
                << ", node role = " << nrole_c << std::endl;
596
            // otherwise use the previous
597
200
            Assert(d_tinfo[ct].d_enum.find(erole_c)
598
                   != d_tinfo[ct].d_enum.end());
599
200
            et = d_tinfo[ct].d_enum[erole_c];
600
          }
601
456
          Trace("sygus-unif-debug") << "Register child enumerator " << et
602
228
                                    << ", arg " << j << " of " << cop
603
228
                                    << ", role = " << erole_c << std::endl;
604
228
          Assert(!et.isNull());
605
228
          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
339
        for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
609
             j++)
610
        {
611
244
          if (sol_templ_children[j].isNull())
612
          {
613
16
            sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
614
          }
615
        }
616
95
        sol_templ_children.insert(sol_templ_children.begin(), cop);
617
190
        cons_strat->d_sol_templ =
618
285
            nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
619
95
        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
95
        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
95
        snode.d_strats.push_back(cons_strat);
641
      }
642
    }
643
  }
644
}
645
646
239
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
239
  if (n.getNumChildren() == 0)
653
  {
654
225
    std::map<Node, unsigned>::iterator itt = templ_var_index.find(n);
655
225
    if (itt != templ_var_index.end())
656
    {
657
199
      unsigned kk = itt->second;
658
199
      std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
659
199
      if (itti == templ_injection.end())
660
      {
661
398
        Trace("sygus-unif-debug") << "...set template injection " << k << " -> "
662
199
                                  << kk << std::endl;
663
199
        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
225
    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
61
void SygusUnifStrategy::staticLearnRedundantOps(
694
    std::map<Node, std::vector<Node>>& strategy_lemmas,
695
    StrategyRestrictions& restrictions)
696
{
697
143
  for (unsigned i = 0; i < d_esym_list.size(); i++)
698
  {
699
164
    Node e = d_esym_list[i];
700
82
    std::map<Node, EnumInfo>::iterator itn = d_einfo.find(e);
701
82
    Assert(itn != d_einfo.end());
702
    // see if there is anything we can eliminate
703
164
    Trace("sygus-unif") << "* Search enumerator #" << i << " : type "
704
82
                        << e.getType().getDType().getName() << " : ";
705
164
    Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size()
706
82
                        << " slaves:" << std::endl;
707
228
    for (unsigned j = 0; j < itn->second.d_enum_slave.size(); j++)
708
    {
709
292
      Node es = itn->second.d_enum_slave[j];
710
146
      std::map<Node, EnumInfo>::iterator itns = d_einfo.find(es);
711
146
      Assert(itns != d_einfo.end());
712
292
      Trace("sygus-unif") << "  " << es << ", role = " << itns->second.getRole()
713
146
                          << std::endl;
714
    }
715
  }
716
61
  Trace("sygus-unif") << std::endl;
717
122
  Trace("sygus-unif") << "Strategy for candidate " << d_candidate
718
61
                      << " is : " << std::endl;
719
61
  debugPrint("sygus-unif");
720
122
  std::map<Node, std::map<NodeRole, bool> > visited;
721
122
  std::map<Node, std::map<unsigned, bool> > needs_cons;
722
61
  staticLearnRedundantOps(
723
122
      getRootEnumerator(), role_equal, visited, needs_cons, restrictions);
724
  // now, check the needs_cons map
725
169
  for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
726
  {
727
216
    Node em = nce.first;
728
108
    const DType& dt = em.getType().getDType();
729
216
    std::vector<Node> lemmas;
730
881
    for (std::pair<const unsigned, bool>& nc : nce.second)
731
    {
732
773
      Assert(nc.first < dt.getNumConstructors());
733
773
      if (!nc.second)
734
      {
735
442
        Node tst = datatypes::utils::mkTester(em, nc.first, dt).negate();
736
737
221
        if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
738
        {
739
442
          Trace("sygus-unif") << "...can exclude based on  : " << tst
740
221
                              << std::endl;
741
221
          lemmas.push_back(tst);
742
        }
743
      }
744
    }
745
108
    if (!lemmas.empty())
746
    {
747
74
      strategy_lemmas[em] = lemmas;
748
    }
749
  }
750
61
}
751
752
61
void SygusUnifStrategy::debugPrint(const char* c)
753
{
754
61
  if (Trace.isOn(c))
755
  {
756
    std::map<Node, std::map<NodeRole, bool> > visited;
757
    debugPrint(c, getRootEnumerator(), role_equal, visited, 0);
758
  }
759
61
}
760
761
279
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
279
  if (visited[e].find(nrole) != visited[e].end())
769
  {
770
294
    return;
771
  }
772
288
  Trace("sygus-strat-slearn") << "Learn redundant operators " << e << " "
773
144
                              << nrole << "..." << std::endl;
774
144
  visited[e][nrole] = true;
775
144
  EnumInfo& ei = getEnumInfo(e);
776
144
  if (ei.isTemplated())
777
  {
778
24
    return;
779
  }
780
240
  TypeNode etn = e.getType();
781
120
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
782
120
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
783
  // the constructors of the current strategy point we need
784
240
  std::map<unsigned, bool> needs_cons_curr;
785
  // get the unused strategies
786
  std::map<Node, std::unordered_set<unsigned>>::iterator itus =
787
120
      restrictions.d_unused_strategies.find(e);
788
240
  std::unordered_set<unsigned> unused_strats;
789
120
  if (itus != restrictions.d_unused_strategies.end())
790
  {
791
2
    unused_strats.insert(itus->second.begin(), itus->second.end());
792
  }
793
211
  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
91
    if (unused_strats.find(j) != unused_strats.end())
797
    {
798
2
      continue;
799
    }
800
89
    EnumTypeInfoStrat* etis = snode.d_strats[j];
801
89
    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
178
    Trace("sygus-strat-slearn") << "...by strategy, can exclude operator "
806
89
                                << etis->d_cons << std::endl;
807
89
    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
89
    if (restrictions.d_iteReturnBoolConst)
811
    {
812
11
      const DType& dt = etn.getDType();
813
22
      Node op = dt[cindex].getSygusOp();
814
22
      TypeNode sygus_tn = dt.getSygusType();
815
22
      if (op.getKind() == kind::BUILTIN
816
18
          && NodeManager::operatorToKind(op) == ITE && sygus_tn.isBoolean()
817
28
          && (dt[cindex].getArgType(1) == dt[cindex].getArgType(2)))
818
      {
819
6
        unsigned ncons = dt.getNumConstructors(), indexT = ncons,
820
6
                 indexF = ncons;
821
128
        for (unsigned k = 0; k < ncons; ++k)
822
        {
823
134
          Node op_arg = dt[k].getSygusOp();
824
122
          if (dt[k].getNumArgs() > 0 || !op_arg.isConst())
825
          {
826
110
            continue;
827
          }
828
12
          if (op_arg.getConst<bool>())
829
          {
830
6
            indexT = k;
831
          }
832
          else
833
          {
834
6
            indexF = k;
835
          }
836
        }
837
6
        if (indexT < ncons && indexF < ncons)
838
        {
839
12
          Trace("sygus-strat-slearn")
840
6
              << "...for ite boolean arg, can exclude all operators but T/F\n";
841
128
          for (unsigned k = 0; k < ncons; ++k)
842
          {
843
122
            needs_cons_curr[k] = false;
844
          }
845
6
          needs_cons_curr[indexT] = true;
846
6
          needs_cons_curr[indexF] = true;
847
        }
848
      }
849
    }
850
307
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
851
    {
852
218
      staticLearnRedundantOps(
853
          cec.first, cec.second, visited, needs_cons, restrictions);
854
    }
855
  }
856
  // get the current datatype
857
120
  const DType& dt = etn.getDType();
858
  // do not use recursive Boolean connectives for conditions of ITEs
859
120
  if (nrole == role_ite_condition && restrictions.d_iteCondOnlyAtoms)
860
  {
861
46
    TypeNode sygus_tn = dt.getSygusType();
862
204
    for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
863
    {
864
338
      Node op = dt[j].getSygusOp();
865
362
      Trace("sygus-strat-slearn")
866
181
          << "...for ite condition, look at operator : " << op << std::endl;
867
205
      if (op.isConst() && dt[j].getNumArgs() == 0)
868
      {
869
48
        Trace("sygus-strat-slearn")
870
24
            << "...for ite condition, can exclude Boolean constant " << op
871
24
            << std::endl;
872
24
        needs_cons_curr[j] = false;
873
24
        continue;
874
      }
875
157
      if (op.getKind() == kind::BUILTIN)
876
      {
877
42
        Kind kind = NodeManager::operatorToKind(op);
878
42
        if (kind == NOT || kind == OR || kind == AND || kind == ITE)
879
        {
880
          // can eliminate if their argument types are simple loops to this type
881
28
          bool type_ok = true;
882
84
          for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
883
          {
884
112
            TypeNode tn = dt[j].getArgType(k);
885
56
            if (tn != etn)
886
            {
887
              type_ok = false;
888
              break;
889
            }
890
          }
891
28
          if (type_ok)
892
          {
893
56
            Trace("sygus-strat-slearn")
894
28
                << "...for ite condition, can exclude Boolean connective : "
895
28
                << op << std::endl;
896
28
            needs_cons_curr[j] = false;
897
          }
898
        }
899
      }
900
    }
901
  }
902
  // all other constructors are needed
903
954
  for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
904
  {
905
834
    if (needs_cons_curr.find(j) == needs_cons_curr.end())
906
    {
907
591
      needs_cons_curr[j] = true;
908
    }
909
  }
910
  // update the constructors that the master enumerator needs
911
120
  if (needs_cons.find(e) == needs_cons.end())
912
  {
913
108
    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
394
void SygusUnifStrategy::finishInit(
925
    Node e,
926
    NodeRole nrole,
927
    std::map<Node, std::map<NodeRole, bool> >& visited,
928
    bool isCond)
929
{
930
394
  EnumInfo& ei = getEnumInfo(e);
931
788
  if (visited[e].find(nrole) != visited[e].end()
932
394
      && (!isCond || ei.isConditional()))
933
  {
934
448
    return;
935
  }
936
182
  visited[e][nrole] = true;
937
  // set conditional
938
182
  if (isCond)
939
  {
940
73
    ei.setConditional();
941
  }
942
182
  if (ei.isTemplated())
943
  {
944
24
    return;
945
  }
946
316
  TypeNode etn = e.getType();
947
158
  EnumTypeInfo& tinfo = getEnumTypeInfo(etn);
948
158
  StrategyNode& snode = tinfo.getStrategyNode(nrole);
949
289
  for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
950
  {
951
131
    EnumTypeInfoStrat* etis = snode.d_strats[j];
952
131
    StrategyType strat = etis->d_this;
953
131
    bool newIsCond = isCond || strat == strat_ITE;
954
464
    for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
955
    {
956
333
      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
150
void EnumInfo::initialize(EnumRole role) { d_role = role; }
1014
1015
518
StrategyNode& EnumTypeInfo::getStrategyNode(NodeRole nrole)
1016
{
1017
518
  std::map<NodeRole, StrategyNode>::iterator it = d_snodes.find(nrole);
1018
518
  Assert(it != d_snodes.end());
1019
518
  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
276
StrategyNode::~StrategyNode()
1035
{
1036
233
  for (unsigned j = 0, size = d_strats.size(); j < size; j++)
1037
  {
1038
95
    delete d_strats[j];
1039
  }
1040
138
  d_strats.clear();
1041
138
}
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
29322
}  // namespace cvc5