From 800a4e4ada7b8b020f3e2fa5431b0e95d4777be7 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Mon, 7 Dec 2020 05:26:13 -0600 Subject: Fix bugs in getFreeVariables (#5601) Did not consider scopes properly, which would fail to say that formulas with escaped variables (both bound and unbound) in formula had free variables. --- src/expr/node_algorithm.cpp | 59 ++++++++++++++++++++++++++------------------- src/expr/node_algorithm.h | 14 +++++++++++ 2 files changed, 48 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/expr/node_algorithm.cpp b/src/expr/node_algorithm.cpp index f41bef37b..2439e28b6 100644 --- a/src/expr/node_algorithm.cpp +++ b/src/expr/node_algorithm.cpp @@ -328,8 +328,16 @@ bool getFreeVariables(TNode n, std::unordered_set& fvs, bool computeFv) { - std::unordered_set bound_var; - std::unordered_map visited; + std::unordered_set scope; + return getFreeVariablesScope(n, fvs, scope, computeFv); +} + +bool getFreeVariablesScope(TNode n, + std::unordered_set& fvs, + std::unordered_set& scope, + bool computeFv) +{ + std::unordered_set visited; std::vector visit; TNode cur; visit.push_back(n); @@ -342,15 +350,14 @@ bool getFreeVariables(TNode n, { continue; } - Kind k = cur.getKind(); - bool isQuant = cur.isClosure(); - std::unordered_map::iterator itv = + std::unordered_set::iterator itv = visited.find(cur); if (itv == visited.end()) { - if (k == kind::BOUND_VARIABLE) + visited.insert(cur); + if (cur.getKind() == kind::BOUND_VARIABLE) { - if (bound_var.find(cur) == bound_var.end()) + if (scope.find(cur) == scope.end()) { if (computeFv) { @@ -362,32 +369,34 @@ bool getFreeVariables(TNode n, } } } - else if (isQuant) + else if (cur.isClosure()) { + // add to scope for (const TNode& cn : cur[0]) { // should not shadow - Assert(bound_var.find(cn) == bound_var.end()); - bound_var.insert(cn); + Assert(scope.find(cn) == scope.end()); + scope.insert(cn); + } + // must make recursive call to use separate cache + if (getFreeVariablesScope(cur[1], fvs, scope, computeFv) && !computeFv) + { + return true; + } + // cleanup + for (const TNode& cn : cur[0]) + { + scope.erase(cn); } - visit.push_back(cur); - } - // must visit quantifiers again to clean up below - visited[cur] = !isQuant; - if (cur.hasOperator()) - { - visit.push_back(cur.getOperator()); } - visit.insert(visit.end(), cur.begin(), cur.end()); - } - else if (!itv->second) - { - Assert(isQuant); - for (const TNode& cn : cur[0]) + else { - bound_var.erase(cn); + if (cur.hasOperator()) + { + visit.push_back(cur.getOperator()); + } + visit.insert(visit.end(), cur.begin(), cur.end()); } - visited[cur] = true; } } while (!visit.empty()); diff --git a/src/expr/node_algorithm.h b/src/expr/node_algorithm.h index 7b6dd4f8c..f5d9f6516 100644 --- a/src/expr/node_algorithm.h +++ b/src/expr/node_algorithm.h @@ -105,6 +105,20 @@ bool hasClosure(Node n); bool getFreeVariables(TNode n, std::unordered_set& fvs, bool computeFv = true); +/** + * Get the free variables in n, that is, the subterms of n of kind + * BOUND_VARIABLE that are not bound in n or occur in scope, adds these to fvs. + * @param n The node under investigation + * @param fvs The set which free variables are added to + * @param scope The scope we are considering. + * @param computeFv If this flag is false, then we only return true/false and + * do not add to fvs. + * @return true iff this node contains a free variable. + */ +bool getFreeVariablesScope(TNode n, + std::unordered_set& fvs, + std::unordered_set& scope, + bool computeFv = true); /** * Get all variables in n. -- cgit v1.2.3