GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/node_algorithm.cpp Lines: 321 359 89.4 %
Date: 2021-05-21 Branches: 517 1018 50.8 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Haniel Barbosa
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
 * Common algorithms on nodes.
13
 *
14
 * This file implements common algorithms applied to nodes, such as checking if
15
 * a node contains a free or a bound variable.
16
 */
17
18
#include "expr/node_algorithm.h"
19
20
#include "expr/attribute.h"
21
#include "expr/dtype.h"
22
23
namespace cvc5 {
24
namespace expr {
25
26
679316
bool hasSubterm(TNode n, TNode t, bool strict)
27
{
28
679316
  if (!strict && n == t)
29
  {
30
7
    return true;
31
  }
32
33
1358618
  std::unordered_set<TNode> visited;
34
1358618
  std::vector<TNode> toProcess;
35
36
679309
  toProcess.push_back(n);
37
38
  // incrementally iterate and add to toProcess
39
2219098
  for (unsigned i = 0; i < toProcess.size(); ++i)
40
  {
41
3641698
    TNode current = toProcess[i];
42
16980911
    for (unsigned j = 0, j_end = current.getNumChildren(); j <= j_end; ++j)
43
    {
44
28886949
      TNode child;
45
      // try children then operator
46
16094653
      if (j < j_end)
47
      {
48
14554862
        child = current[j];
49
      }
50
1539791
      else if (current.hasOperator())
51
      {
52
886260
        child = current.getOperator();
53
      }
54
      else
55
      {
56
653531
        break;
57
      }
58
15441122
      if (child == t)
59
      {
60
562120
        return true;
61
      }
62
14879002
      if (visited.find(child) != visited.end())
63
      {
64
2086706
        continue;
65
      }
66
      else
67
      {
68
12792296
        visited.insert(child);
69
12792296
        toProcess.push_back(child);
70
      }
71
    }
72
  }
73
74
117189
  return false;
75
}
76
77
bool hasSubtermMulti(TNode n, TNode t)
78
{
79
  std::unordered_map<TNode, bool> visited;
80
  std::unordered_map<TNode, bool> contains;
81
  std::unordered_map<TNode, bool>::iterator it;
82
  std::vector<TNode> visit;
83
  TNode cur;
84
  visit.push_back(n);
85
  do
86
  {
87
    cur = visit.back();
88
    visit.pop_back();
89
    it = visited.find(cur);
90
91
    if (it == visited.end())
92
    {
93
      if (cur == t)
94
      {
95
        visited[cur] = true;
96
        contains[cur] = true;
97
      }
98
      else
99
      {
100
        visited[cur] = false;
101
        visit.push_back(cur);
102
        for (const Node& cc : cur)
103
        {
104
          visit.push_back(cc);
105
        }
106
      }
107
    }
108
    else if (!it->second)
109
    {
110
      bool doesContain = false;
111
      for (const Node& cn : cur)
112
      {
113
        it = contains.find(cn);
114
        Assert(it != contains.end());
115
        if (it->second)
116
        {
117
          if (doesContain)
118
          {
119
            // two children have t, return true
120
            return true;
121
          }
122
          doesContain = true;
123
        }
124
      }
125
      contains[cur] = doesContain;
126
      visited[cur] = true;
127
    }
128
  } while (!visit.empty());
129
  return false;
130
}
131
132
857703
bool hasSubtermKind(Kind k, Node n)
133
{
134
1715406
  std::unordered_set<TNode> visited;
135
1715406
  std::vector<TNode> visit;
136
1715406
  TNode cur;
137
857703
  visit.push_back(n);
138
6112652
  do
139
  {
140
6970355
    cur = visit.back();
141
6970355
    visit.pop_back();
142
6970355
    if (visited.find(cur) == visited.end())
143
    {
144
5843622
      visited.insert(cur);
145
5843622
      if (cur.getKind() == k)
146
      {
147
124
        return true;
148
      }
149
11956249
      for (const Node& cn : cur)
150
      {
151
6112751
        visit.push_back(cn);
152
      }
153
    }
154
6970231
  } while (!visit.empty());
155
857579
  return false;
156
}
157
158
2950
bool hasSubtermKinds(const std::unordered_set<Kind, kind::KindHashFunction>& ks,
159
                     Node n)
160
{
161
2950
  if (ks.empty())
162
  {
163
    return false;
164
  }
165
5900
  std::unordered_set<TNode> visited;
166
5900
  std::vector<TNode> visit;
167
5900
  TNode cur;
168
2950
  visit.push_back(n);
169
5196
  do
170
  {
171
8146
    cur = visit.back();
172
8146
    visit.pop_back();
173
8146
    if (visited.find(cur) == visited.end())
174
    {
175
7247
      if (ks.find(cur.getKind()) != ks.end())
176
      {
177
38
        return true;
178
      }
179
7209
      visited.insert(cur);
180
7209
      visit.insert(visit.end(), cur.begin(), cur.end());
181
    }
182
8108
  } while (!visit.empty());
183
2912
  return false;
184
}
185
186
1588
bool hasSubterm(TNode n, const std::vector<Node>& t, bool strict)
187
{
188
1588
  if (t.empty())
189
  {
190
    return false;
191
  }
192
1588
  if (!strict && std::find(t.begin(), t.end(), n) != t.end())
193
  {
194
4
    return true;
195
  }
196
197
3168
  std::unordered_set<TNode> visited;
198
3168
  std::vector<TNode> toProcess;
199
200
1584
  toProcess.push_back(n);
201
202
  // incrementally iterate and add to toProcess
203
13444
  for (unsigned i = 0; i < toProcess.size(); ++i)
204
  {
205
23993
    TNode current = toProcess[i];
206
26037
    for (unsigned j = 0, j_end = current.getNumChildren(); j <= j_end; ++j)
207
    {
208
34300
      TNode child;
209
      // try children then operator
210
22200
      if (j < j_end)
211
      {
212
10340
        child = current[j];
213
      }
214
11860
      else if (current.hasOperator())
215
      {
216
3837
        child = current.getOperator();
217
      }
218
      else
219
      {
220
8023
        break;
221
      }
222
14177
      if (std::find(t.begin(), t.end(), child) != t.end())
223
      {
224
273
        return true;
225
      }
226
13904
      if (visited.find(child) != visited.end())
227
      {
228
1804
        continue;
229
      }
230
      else
231
      {
232
12100
        visited.insert(child);
233
12100
        toProcess.push_back(child);
234
      }
235
    }
236
  }
237
238
1311
  return false;
239
}
240
241
struct HasBoundVarTag
242
{
243
};
244
struct HasBoundVarComputedTag
245
{
246
};
247
/** Attribute true for expressions with bound variables in them */
248
typedef expr::Attribute<HasBoundVarTag, bool> HasBoundVarAttr;
249
typedef expr::Attribute<HasBoundVarComputedTag, bool> HasBoundVarComputedAttr;
250
251
11657969
bool hasBoundVar(TNode n)
252
{
253
11657969
  if (!n.getAttribute(HasBoundVarComputedAttr()))
254
  {
255
2182541
    bool hasBv = false;
256
2182541
    if (n.getKind() == kind::BOUND_VARIABLE)
257
    {
258
63451
      hasBv = true;
259
    }
260
    else
261
    {
262
6582948
      for (auto i = n.begin(); i != n.end() && !hasBv; ++i)
263
      {
264
4845067
        if (hasBoundVar(*i))
265
        {
266
381209
          hasBv = true;
267
381209
          break;
268
        }
269
      }
270
    }
271
2182541
    if (!hasBv && n.hasOperator())
272
    {
273
1512606
      hasBv = hasBoundVar(n.getOperator());
274
    }
275
2182541
    n.setAttribute(HasBoundVarAttr(), hasBv);
276
2182541
    n.setAttribute(HasBoundVarComputedAttr(), true);
277
4365082
    Debug("bva") << n << " has bva : " << n.getAttribute(HasBoundVarAttr())
278
2182541
                 << std::endl;
279
2182541
    return hasBv;
280
  }
281
9475428
  return n.getAttribute(HasBoundVarAttr());
282
}
283
284
1276341
bool hasFreeVar(TNode n)
285
{
286
2552682
  std::unordered_set<Node> fvs;
287
2552682
  return getFreeVariables(n, fvs, false);
288
}
289
290
struct HasClosureTag
291
{
292
};
293
struct HasClosureComputedTag
294
{
295
};
296
/** Attribute true for expressions with closures in them */
297
typedef expr::Attribute<HasClosureTag, bool> HasClosureAttr;
298
typedef expr::Attribute<HasClosureComputedTag, bool> HasClosureComputedAttr;
299
300
3168
bool hasClosure(Node n)
301
{
302
3168
  if (!n.getAttribute(HasClosureComputedAttr()))
303
  {
304
1807
    bool hasC = false;
305
1807
    if (n.isClosure())
306
    {
307
23
      hasC = true;
308
    }
309
    else
310
    {
311
3553
      for (auto i = n.begin(); i != n.end() && !hasC; ++i)
312
      {
313
1769
        hasC = hasClosure(*i);
314
      }
315
    }
316
1807
    if (!hasC && n.hasOperator())
317
    {
318
855
      hasC = hasClosure(n.getOperator());
319
    }
320
1807
    n.setAttribute(HasClosureAttr(), hasC);
321
1807
    n.setAttribute(HasClosureComputedAttr(), true);
322
1807
    return hasC;
323
  }
324
1361
  return n.getAttribute(HasClosureAttr());
325
}
326
327
1317656
bool getFreeVariables(TNode n, std::unordered_set<Node>& fvs, bool computeFv)
328
{
329
2635312
  std::unordered_set<TNode> scope;
330
2635312
  return getFreeVariablesScope(n, fvs, scope, computeFv);
331
}
332
333
1420896
bool getFreeVariablesScope(TNode n,
334
                           std::unordered_set<Node>& fvs,
335
                           std::unordered_set<TNode>& scope,
336
                           bool computeFv)
337
{
338
2841792
  std::unordered_set<TNode> visited;
339
2841792
  std::vector<TNode> visit;
340
2841792
  TNode cur;
341
1420896
  visit.push_back(n);
342
2941128
  do
343
  {
344
4362024
    cur = visit.back();
345
4362024
    visit.pop_back();
346
    // can skip if it doesn't have a bound variable
347
4362024
    if (!hasBoundVar(cur))
348
    {
349
2537110
      continue;
350
    }
351
1824914
    std::unordered_set<TNode>::iterator itv = visited.find(cur);
352
1824914
    if (itv == visited.end())
353
    {
354
1346817
      visited.insert(cur);
355
1346817
      if (cur.getKind() == kind::BOUND_VARIABLE)
356
      {
357
294601
        if (scope.find(cur) == scope.end())
358
        {
359
49022
          if (computeFv)
360
          {
361
48693
            fvs.insert(cur);
362
          }
363
          else
364
          {
365
658
            return true;
366
          }
367
        }
368
      }
369
1052216
      else if (cur.isClosure())
370
      {
371
        // add to scope
372
330031
        for (const TNode& cn : cur[0])
373
        {
374
          // should not shadow
375
456528
          Assert(scope.find(cn) == scope.end())
376
228264
              << "Shadowed variable " << cn << " in " << cur << "\n";
377
228264
          scope.insert(cn);
378
        }
379
        // must make recursive call to use separate cache
380
101767
        if (getFreeVariablesScope(cur[1], fvs, scope, computeFv) && !computeFv)
381
        {
382
          return true;
383
        }
384
        // cleanup
385
330031
        for (const TNode& cn : cur[0])
386
        {
387
228264
          scope.erase(cn);
388
        }
389
      }
390
      else
391
      {
392
950449
        if (cur.hasOperator())
393
        {
394
950449
          visit.push_back(cur.getOperator());
395
        }
396
950449
        visit.insert(visit.end(), cur.begin(), cur.end());
397
      }
398
    }
399
4361695
  } while (!visit.empty());
400
401
1420567
  return !fvs.empty();
402
}
403
404
543
bool getVariables(TNode n, std::unordered_set<TNode>& vs)
405
{
406
1086
  std::unordered_set<TNode> visited;
407
1086
  std::vector<TNode> visit;
408
1086
  TNode cur;
409
543
  visit.push_back(n);
410
4992
  do
411
  {
412
5535
    cur = visit.back();
413
5535
    visit.pop_back();
414
5535
    std::unordered_set<TNode>::iterator itv = visited.find(cur);
415
5535
    if (itv == visited.end())
416
    {
417
4409
      if (cur.isVar())
418
      {
419
1593
        vs.insert(cur);
420
      }
421
      else
422
      {
423
2816
        visit.insert(visit.end(), cur.begin(), cur.end());
424
      }
425
4409
      visited.insert(cur);
426
    }
427
5535
  } while (!visit.empty());
428
429
1086
  return !vs.empty();
430
}
431
432
3740
void getSymbols(TNode n, std::unordered_set<Node>& syms)
433
{
434
7480
  std::unordered_set<TNode> visited;
435
3740
  getSymbols(n, syms, visited);
436
3740
}
437
438
22160
void getSymbols(TNode n,
439
                std::unordered_set<Node>& syms,
440
                std::unordered_set<TNode>& visited)
441
{
442
44320
  std::vector<TNode> visit;
443
44320
  TNode cur;
444
22160
  visit.push_back(n);
445
664776
  do
446
  {
447
686936
    cur = visit.back();
448
686936
    visit.pop_back();
449
686936
    if (visited.find(cur) == visited.end())
450
    {
451
336952
      visited.insert(cur);
452
336952
      if (cur.isVar() && cur.getKind() != kind::BOUND_VARIABLE)
453
      {
454
40668
        syms.insert(cur);
455
      }
456
336952
      if (cur.hasOperator())
457
      {
458
210359
        visit.push_back(cur.getOperator());
459
      }
460
336952
      visit.insert(visit.end(), cur.begin(), cur.end());
461
    }
462
686936
  } while (!visit.empty());
463
22160
}
464
465
131
void getKindSubterms(TNode n,
466
                     Kind k,
467
                     bool topLevel,
468
                     std::unordered_set<Node>& ts)
469
{
470
262
  std::unordered_set<TNode> visited;
471
262
  std::vector<TNode> visit;
472
262
  TNode cur;
473
131
  visit.push_back(n);
474
3578
  do
475
  {
476
3709
    cur = visit.back();
477
3709
    visit.pop_back();
478
3709
    if (visited.find(cur) == visited.end())
479
    {
480
2152
      visited.insert(cur);
481
2152
      if (cur.getKind() == k)
482
      {
483
37
        ts.insert(cur);
484
37
        if (topLevel)
485
        {
486
          // only considering top-level applications
487
37
          continue;
488
        }
489
      }
490
2115
      if (cur.hasOperator())
491
      {
492
1106
        visit.push_back(cur.getOperator());
493
      }
494
2115
      visit.insert(visit.end(), cur.begin(), cur.end());
495
    }
496
3709
  } while (!visit.empty());
497
131
}
498
499
3
void getOperatorsMap(TNode n, std::map<TypeNode, std::unordered_set<Node>>& ops)
500
{
501
6
  std::unordered_set<TNode> visited;
502
3
  getOperatorsMap(n, ops, visited);
503
3
}
504
505
3
void getOperatorsMap(TNode n,
506
                     std::map<TypeNode, std::unordered_set<Node>>& ops,
507
                     std::unordered_set<TNode>& visited)
508
{
509
  // nodes that we still need to visit
510
6
  std::vector<TNode> visit;
511
  // current node
512
6
  TNode cur;
513
3
  visit.push_back(n);
514
76
  do
515
  {
516
79
    cur = visit.back();
517
79
    visit.pop_back();
518
    // if cur is in the cache, do nothing
519
79
    if (visited.find(cur) == visited.end())
520
    {
521
      // fetch the correct type
522
158
      TypeNode tn = cur.getType();
523
      // add the current operator to the result
524
79
      if (cur.hasOperator())
525
      {
526
78
       Node o;
527
39
       if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) {
528
4
         o = cur.getOperator();
529
       } else {
530
35
         o = NodeManager::currentNM()->operatorOf(cur.getKind());
531
       }
532
39
        ops[tn].insert(o);
533
      }
534
      // add children to visit in the future
535
79
      visit.insert(visit.end(), cur.begin(), cur.end());
536
    }
537
79
  } while (!visit.empty());
538
3
}
539
540
21741
Node substituteCaptureAvoiding(TNode n, Node src, Node dest)
541
{
542
21741
  if (n == src)
543
  {
544
    return dest;
545
  }
546
21741
  if (src == dest)
547
  {
548
    return n;
549
  }
550
43482
  std::vector<Node> srcs;
551
43482
  std::vector<Node> dests;
552
21741
  srcs.push_back(src);
553
21741
  dests.push_back(dest);
554
21741
  return substituteCaptureAvoiding(n, srcs, dests);
555
}
556
557
22324
Node substituteCaptureAvoiding(TNode n,
558
                               std::vector<Node>& src,
559
                               std::vector<Node>& dest)
560
{
561
44648
  std::unordered_map<TNode, Node> visited;
562
22324
  std::unordered_map<TNode, Node>::iterator it;
563
44648
  std::vector<TNode> visit;
564
44648
  TNode curr;
565
22324
  visit.push_back(n);
566
22324
  Assert(src.size() == dest.size())
567
      << "Substitution domain and range must be equal size";
568
192205
  do
569
  {
570
214529
    curr = visit.back();
571
214529
    visit.pop_back();
572
214529
    it = visited.find(curr);
573
574
214529
    if (it == visited.end())
575
    {
576
137193
      auto itt = std::find(src.rbegin(), src.rend(), curr);
577
160618
      if (itt != src.rend())
578
      {
579
23425
        Assert(
580
            (std::distance(src.begin(), itt.base()) - 1) >= 0
581
            && static_cast<unsigned>(std::distance(src.begin(), itt.base()) - 1)
582
                   < dest.size());
583
23425
        visited[curr] = dest[std::distance(src.begin(), itt.base()) - 1];
584
95865
        continue;
585
      }
586
162783
      if (curr.getNumChildren() == 0)
587
      {
588
49015
        visited[curr] = curr;
589
49015
        continue;
590
      }
591
592
64753
      visited[curr] = Node::null();
593
      // if binder, rename variables to avoid capture
594
64753
      if (curr.isClosure())
595
      {
596
779
        NodeManager* nm = NodeManager::currentNM();
597
        // have new vars -> renames subs in the end of current sub
598
1734
        for (const Node& v : curr[0])
599
        {
600
955
          src.push_back(v);
601
955
          dest.push_back(nm->mkBoundVar(v.getType()));
602
        }
603
      }
604
      // save for post-visit
605
64753
      visit.push_back(curr);
606
      // visit children
607
64753
      if (curr.getMetaKind() == kind::metakind::PARAMETERIZED)
608
      {
609
        // push the operator
610
1411
        visit.push_back(curr.getOperator());
611
      }
612
190794
      for (unsigned i = 0, size = curr.getNumChildren(); i < size; ++i)
613
      {
614
126041
        visit.push_back(curr[i]);
615
      }
616
    }
617
77336
    else if (it->second.isNull())
618
    {
619
      // build node
620
129506
      NodeBuilder nb(curr.getKind());
621
64753
      if (curr.getMetaKind() == kind::metakind::PARAMETERIZED)
622
      {
623
        // push the operator
624
1411
        Assert(visited.find(curr.getOperator()) != visited.end());
625
1411
        nb << visited[curr.getOperator()];
626
      }
627
      // collect substituted children
628
190794
      for (unsigned i = 0, size = curr.getNumChildren(); i < size; ++i)
629
      {
630
126041
        Assert(visited.find(curr[i]) != visited.end());
631
126041
        nb << visited[curr[i]];
632
      }
633
64753
      visited[curr] = nb;
634
635
      // remove renaming
636
64753
      if (curr.isClosure())
637
      {
638
        // remove beginning of sub which correspond to renaming of variables in
639
        // this binder
640
779
        unsigned nchildren = curr[0].getNumChildren();
641
779
        src.resize(src.size() - nchildren);
642
779
        dest.resize(dest.size() - nchildren);
643
      }
644
    }
645
214529
  } while (!visit.empty());
646
22324
  Assert(visited.find(n) != visited.end());
647
44648
  return visited[n];
648
}
649
650
11905
void getComponentTypes(TypeNode t, std::unordered_set<TypeNode>& types)
651
{
652
23810
  std::vector<TypeNode> toProcess;
653
11905
  toProcess.push_back(t);
654
116
  do
655
  {
656
24042
    TypeNode curr = toProcess.back();
657
12021
    toProcess.pop_back();
658
    // if not already visited
659
12021
    if (types.find(curr) == types.end())
660
    {
661
11924
      types.insert(curr);
662
      // get component types from the children
663
12040
      for (unsigned i = 0, nchild = curr.getNumChildren(); i < nchild; i++)
664
      {
665
116
        toProcess.push_back(curr[i]);
666
      }
667
    }
668
12021
  } while (!toProcess.empty());
669
11905
}
670
671
1219
bool match(Node x, Node y, std::unordered_map<Node, Node>& subs)
672
{
673
2438
  std::unordered_set<std::pair<TNode, TNode>, TNodePairHashFunction> visited;
674
  std::unordered_set<std::pair<TNode, TNode>, TNodePairHashFunction>::iterator
675
1219
      it;
676
1219
  std::unordered_map<Node, Node>::iterator subsIt;
677
678
2438
  std::vector<std::pair<TNode, TNode>> stack;
679
1219
  stack.emplace_back(x, y);
680
2438
  std::pair<TNode, TNode> curr;
681
682
1593
  while (!stack.empty())
683
  {
684
1363
    curr = stack.back();
685
1363
    stack.pop_back();
686
1363
    if (curr.first == curr.second)
687
    {
688
      // holds trivially
689
11
      continue;
690
    }
691
1352
    it = visited.find(curr);
692
1352
    if (it != visited.end())
693
    {
694
      // already processed
695
2
      continue;
696
    }
697
1350
    visited.insert(curr);
698
1350
    if (curr.first.getNumChildren() == 0)
699
    {
700
130
      if (!curr.first.getType().isComparableTo(curr.second.getType()))
701
      {
702
        // the two subterms have different types
703
2
        return false;
704
      }
705
      // if the two subterms are not equal and the first one is a bound
706
      // variable...
707
128
      if (curr.first.getKind() == kind::BOUND_VARIABLE)
708
      {
709
        // and we have not seen this variable before...
710
98
        subsIt = subs.find(curr.first);
711
98
        if (subsIt == subs.cend())
712
        {
713
          // add the two subterms to `sub`
714
92
          subs.emplace(curr.first, curr.second);
715
        }
716
        else
717
        {
718
          // if we saw this variable before, make sure that (now and before) it
719
          // maps to the same subterm
720
6
          if (curr.second != subsIt->second)
721
          {
722
2
            return false;
723
          }
724
        }
725
      }
726
      else
727
      {
728
        // the two subterms are not equal
729
30
        return false;
730
      }
731
    }
732
    else
733
    {
734
      // if the two subterms are not equal, make sure that their operators are
735
      // equal
736
      // we compare operators instead of kinds because different terms may have
737
      // the same kind (both `(id x)` and `(square x)` have kind APPLY_UF)
738
      // since many builtin operators like `PLUS` allow arbitrary number of
739
      // arguments, we also need to check if the two subterms have the same
740
      // number of children
741
2440
      if (curr.first.getNumChildren() != curr.second.getNumChildren()
742
2440
          || curr.first.getOperator() != curr.second.getOperator())
743
      {
744
1142
        return false;
745
      }
746
      // recurse on children
747
236
      for (size_t i = 0, n = curr.first.getNumChildren(); i < n; ++i)
748
      {
749
158
        stack.emplace_back(curr.first[i], curr.second[i]);
750
      }
751
    }
752
  }
753
43
  return true;
754
}
755
756
}  // namespace expr
757
27735
}  // namespace cvc5