1 /********************* */
2 /*! \file node_algorithm.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Haniel Barbosa, Andres Noetzli
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 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.\endverbatim
12 ** \brief Common algorithms on nodes
14 ** This file implements common algorithms applied to nodes, such as checking if
15 ** a node contains a free or a bound variable.
18 #include "expr/node_algorithm.h"
20 #include "expr/attribute.h"
21 #include "expr/dtype.h"
26 bool hasSubterm(TNode n
, TNode t
, bool strict
)
28 if (!strict
&& n
== t
)
33 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
34 std::vector
<TNode
> toProcess
;
36 toProcess
.push_back(n
);
38 // incrementally iterate and add to toProcess
39 for (unsigned i
= 0; i
< toProcess
.size(); ++i
)
41 TNode current
= toProcess
[i
];
42 for (unsigned j
= 0, j_end
= current
.getNumChildren(); j
<= j_end
; ++j
)
45 // try children then operator
50 else if (current
.hasOperator())
52 child
= current
.getOperator();
62 if (visited
.find(child
) != visited
.end())
68 visited
.insert(child
);
69 toProcess
.push_back(child
);
77 bool hasSubtermMulti(TNode n
, TNode t
)
79 std::unordered_map
<TNode
, bool, TNodeHashFunction
> visited
;
80 std::unordered_map
<TNode
, bool, TNodeHashFunction
> contains
;
81 std::unordered_map
<TNode
, bool, TNodeHashFunction
>::iterator it
;
82 std::vector
<TNode
> visit
;
89 it
= visited
.find(cur
);
91 if (it
== visited
.end())
100 visited
[cur
] = false;
101 visit
.push_back(cur
);
102 for (const Node
& cc
: cur
)
108 else if (!it
->second
)
110 bool doesContain
= false;
111 for (const Node
& cn
: cur
)
113 it
= contains
.find(cn
);
114 Assert(it
!= contains
.end());
119 // two children have t, return true
125 contains
[cur
] = doesContain
;
128 } while (!visit
.empty());
132 bool hasSubtermKind(Kind k
, Node n
)
134 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
135 std::vector
<TNode
> visit
;
142 if (visited
.find(cur
) == visited
.end())
145 if (cur
.getKind() == k
)
149 for (const Node
& cn
: cur
)
154 } while (!visit
.empty());
158 bool hasSubterm(TNode n
, const std::vector
<Node
>& t
, bool strict
)
164 if (!strict
&& std::find(t
.begin(), t
.end(), n
) != t
.end())
169 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
170 std::vector
<TNode
> toProcess
;
172 toProcess
.push_back(n
);
174 // incrementally iterate and add to toProcess
175 for (unsigned i
= 0; i
< toProcess
.size(); ++i
)
177 TNode current
= toProcess
[i
];
178 for (unsigned j
= 0, j_end
= current
.getNumChildren(); j
<= j_end
; ++j
)
181 // try children then operator
186 else if (current
.hasOperator())
188 child
= current
.getOperator();
194 if (std::find(t
.begin(), t
.end(), child
) != t
.end())
198 if (visited
.find(child
) != visited
.end())
204 visited
.insert(child
);
205 toProcess
.push_back(child
);
213 struct HasBoundVarTag
216 struct HasBoundVarComputedTag
219 /** Attribute true for expressions with bound variables in them */
220 typedef expr::Attribute
<HasBoundVarTag
, bool> HasBoundVarAttr
;
221 typedef expr::Attribute
<HasBoundVarComputedTag
, bool> HasBoundVarComputedAttr
;
223 bool hasBoundVar(TNode n
)
225 if (!n
.getAttribute(HasBoundVarComputedAttr()))
228 if (n
.getKind() == kind::BOUND_VARIABLE
)
234 for (auto i
= n
.begin(); i
!= n
.end() && !hasBv
; ++i
)
236 hasBv
= hasBoundVar(*i
);
239 if (!hasBv
&& n
.hasOperator())
241 hasBv
= hasBoundVar(n
.getOperator());
243 n
.setAttribute(HasBoundVarAttr(), hasBv
);
244 n
.setAttribute(HasBoundVarComputedAttr(), true);
245 Debug("bva") << n
<< " has bva : " << n
.getAttribute(HasBoundVarAttr())
249 return n
.getAttribute(HasBoundVarAttr());
252 bool hasFreeVar(TNode n
)
254 std::unordered_set
<Node
, NodeHashFunction
> fvs
;
255 return getFreeVariables(n
, fvs
, false);
261 struct HasClosureComputedTag
264 /** Attribute true for expressions with closures in them */
265 typedef expr::Attribute
<HasClosureTag
, bool> HasClosureAttr
;
266 typedef expr::Attribute
<HasClosureComputedTag
, bool> HasClosureComputedAttr
;
268 bool hasClosure(Node n
)
270 if (!n
.getAttribute(HasClosureComputedAttr()))
279 for (auto i
= n
.begin(); i
!= n
.end() && !hasC
; ++i
)
281 hasC
= hasClosure(*i
);
284 if (!hasC
&& n
.hasOperator())
286 hasC
= hasClosure(n
.getOperator());
288 n
.setAttribute(HasClosureAttr(), hasC
);
289 n
.setAttribute(HasClosureComputedAttr(), true);
292 return n
.getAttribute(HasClosureAttr());
295 bool getFreeVariables(TNode n
,
296 std::unordered_set
<Node
, NodeHashFunction
>& fvs
,
299 std::unordered_set
<TNode
, TNodeHashFunction
> bound_var
;
300 std::unordered_map
<TNode
, bool, TNodeHashFunction
> visited
;
301 std::vector
<TNode
> visit
;
308 // can skip if it doesn't have a bound variable
309 if (!hasBoundVar(cur
))
313 Kind k
= cur
.getKind();
314 bool isQuant
= cur
.isClosure();
315 std::unordered_map
<TNode
, bool, TNodeHashFunction
>::iterator itv
=
317 if (itv
== visited
.end())
319 if (k
== kind::BOUND_VARIABLE
)
321 if (bound_var
.find(cur
) == bound_var
.end())
335 for (const TNode
& cn
: cur
[0])
338 Assert(bound_var
.find(cn
) == bound_var
.end());
339 bound_var
.insert(cn
);
341 visit
.push_back(cur
);
343 // must visit quantifiers again to clean up below
344 visited
[cur
] = !isQuant
;
345 if (cur
.hasOperator())
347 visit
.push_back(cur
.getOperator());
349 for (const TNode
& cn
: cur
)
354 else if (!itv
->second
)
357 for (const TNode
& cn
: cur
[0])
363 } while (!visit
.empty());
368 bool getVariables(TNode n
, std::unordered_set
<TNode
, TNodeHashFunction
>& vs
)
370 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
371 std::vector
<TNode
> visit
;
378 std::unordered_set
<TNode
, TNodeHashFunction
>::iterator itv
=
380 if (itv
== visited
.end())
388 for (const TNode
& cn
: cur
)
395 } while (!visit
.empty());
400 void getSymbols(TNode n
, std::unordered_set
<Node
, NodeHashFunction
>& syms
)
402 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
403 getSymbols(n
, syms
, visited
);
406 void getSymbols(TNode n
,
407 std::unordered_set
<Node
, NodeHashFunction
>& syms
,
408 std::unordered_set
<TNode
, TNodeHashFunction
>& visited
)
410 std::vector
<TNode
> visit
;
417 if (visited
.find(cur
) == visited
.end())
420 if (cur
.isVar() && cur
.getKind() != kind::BOUND_VARIABLE
)
424 if (cur
.hasOperator())
426 visit
.push_back(cur
.getOperator());
433 } while (!visit
.empty());
436 void getOperatorsMap(
438 std::map
<TypeNode
, std::unordered_set
<Node
, NodeHashFunction
>>& ops
)
440 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
441 getOperatorsMap(n
, ops
, visited
);
444 void getOperatorsMap(
446 std::map
<TypeNode
, std::unordered_set
<Node
, NodeHashFunction
>>& ops
,
447 std::unordered_set
<TNode
, TNodeHashFunction
>& visited
)
449 // nodes that we still need to visit
450 std::vector
<TNode
> visit
;
458 // if cur is in the cache, do nothing
459 if (visited
.find(cur
) == visited
.end())
461 // fetch the correct type
462 TypeNode tn
= cur
.getType();
463 // add the current operator to the result
464 if (cur
.hasOperator())
466 ops
[tn
].insert(NodeManager::currentNM()->operatorOf(cur
.getKind()));
468 // add children to visit in the future
474 } while (!visit
.empty());
477 Node
substituteCaptureAvoiding(TNode n
, Node src
, Node dest
)
487 std::vector
<Node
> srcs
;
488 std::vector
<Node
> dests
;
490 dests
.push_back(dest
);
491 return substituteCaptureAvoiding(n
, srcs
, dests
);
494 Node
substituteCaptureAvoiding(TNode n
,
495 std::vector
<Node
>& src
,
496 std::vector
<Node
>& dest
)
498 std::unordered_map
<TNode
, Node
, TNodeHashFunction
> visited
;
499 std::unordered_map
<TNode
, Node
, TNodeHashFunction
>::iterator it
;
500 std::vector
<TNode
> visit
;
503 Assert(src
.size() == dest
.size())
504 << "Substitution domain and range must be equal size";
509 it
= visited
.find(curr
);
511 if (it
== visited
.end())
513 auto itt
= std::find(src
.rbegin(), src
.rend(), curr
);
514 if (itt
!= src
.rend())
517 (std::distance(src
.begin(), itt
.base()) - 1) >= 0
518 && static_cast<unsigned>(std::distance(src
.begin(), itt
.base()) - 1)
520 Node n
= dest
[std::distance(src
.begin(), itt
.base()) - 1];
524 if (curr
.getNumChildren() == 0)
526 visited
[curr
] = curr
;
530 visited
[curr
] = Node::null();
531 // if binder, rename variables to avoid capture
532 if (curr
.isClosure())
534 NodeManager
* nm
= NodeManager::currentNM();
535 // have new vars -> renames subs in the end of current sub
536 for (const Node
& v
: curr
[0])
539 dest
.push_back(nm
->mkBoundVar(v
.getType()));
542 // save for post-visit
543 visit
.push_back(curr
);
545 if (curr
.getMetaKind() == kind::metakind::PARAMETERIZED
)
548 visit
.push_back(curr
.getOperator());
550 for (unsigned i
= 0, size
= curr
.getNumChildren(); i
< size
; ++i
)
552 visit
.push_back(curr
[i
]);
555 else if (it
->second
.isNull())
558 NodeBuilder
<> nb(curr
.getKind());
559 if (curr
.getMetaKind() == kind::metakind::PARAMETERIZED
)
562 Assert(visited
.find(curr
.getOperator()) != visited
.end());
563 nb
<< visited
[curr
.getOperator()];
565 // collect substituted children
566 for (unsigned i
= 0, size
= curr
.getNumChildren(); i
< size
; ++i
)
568 Assert(visited
.find(curr
[i
]) != visited
.end());
569 nb
<< visited
[curr
[i
]];
575 if (curr
.isClosure())
577 // remove beginning of sub which correspond to renaming of variables in
579 unsigned nchildren
= curr
[0].getNumChildren();
580 src
.resize(src
.size() - nchildren
);
581 dest
.resize(dest
.size() - nchildren
);
584 } while (!visit
.empty());
585 Assert(visited
.find(n
) != visited
.end());
589 void getComponentTypes(
590 TypeNode t
, std::unordered_set
<TypeNode
, TypeNodeHashFunction
>& types
)
592 std::vector
<TypeNode
> toProcess
;
593 toProcess
.push_back(t
);
596 TypeNode curr
= toProcess
.back();
597 toProcess
.pop_back();
598 // if not already visited
599 if (types
.find(t
) == types
.end())
602 // get component types from the children
603 for (unsigned i
= 0, nchild
= t
.getNumChildren(); i
< nchild
; i
++)
605 toProcess
.push_back(t
[i
]);
608 } while (!toProcess
.empty());