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 techniques for evaluating terms with recursively |
14 |
|
* defined functions. |
15 |
|
*/ |
16 |
|
|
17 |
|
#include "theory/quantifiers/fun_def_evaluator.h" |
18 |
|
|
19 |
|
#include "options/quantifiers_options.h" |
20 |
|
#include "theory/quantifiers/quantifiers_attributes.h" |
21 |
|
#include "theory/rewriter.h" |
22 |
|
|
23 |
|
using namespace cvc5::kind; |
24 |
|
|
25 |
|
namespace cvc5 { |
26 |
|
namespace theory { |
27 |
|
namespace quantifiers { |
28 |
|
|
29 |
1529 |
FunDefEvaluator::FunDefEvaluator() {} |
30 |
|
|
31 |
32 |
void FunDefEvaluator::assertDefinition(Node q) |
32 |
|
{ |
33 |
32 |
Trace("fd-eval") << "FunDefEvaluator: assertDefinition " << q << std::endl; |
34 |
64 |
Node h = QuantAttributes::getFunDefHead(q); |
35 |
32 |
if (h.isNull()) |
36 |
|
{ |
37 |
|
// not a function definition |
38 |
|
return; |
39 |
|
} |
40 |
|
// h possibly with zero arguments? |
41 |
64 |
Node f = h.hasOperator() ? h.getOperator() : h; |
42 |
32 |
Assert(d_funDefMap.find(f) == d_funDefMap.end()) |
43 |
|
<< "FunDefEvaluator::assertDefinition: function already defined"; |
44 |
32 |
FunDefInfo& fdi = d_funDefMap[f]; |
45 |
32 |
fdi.d_body = QuantAttributes::getFunDefBody(q); |
46 |
32 |
Assert(!fdi.d_body.isNull()); |
47 |
32 |
fdi.d_args.insert(fdi.d_args.end(), q[0].begin(), q[0].end()); |
48 |
64 |
Trace("fd-eval") << "FunDefEvaluator: function " << f << " is defined with " |
49 |
32 |
<< fdi.d_args << " / " << fdi.d_body << std::endl; |
50 |
|
} |
51 |
|
|
52 |
2524 |
Node FunDefEvaluator::evaluate(Node n) const |
53 |
|
{ |
54 |
|
// should do standard rewrite before this call |
55 |
2524 |
Assert(Rewriter::rewrite(n) == n); |
56 |
2524 |
Trace("fd-eval") << "FunDefEvaluator: evaluate " << n << std::endl; |
57 |
2524 |
NodeManager* nm = NodeManager::currentNM(); |
58 |
5048 |
std::unordered_map<TNode, unsigned> funDefCount; |
59 |
2524 |
std::unordered_map<TNode, unsigned>::iterator itCount; |
60 |
5048 |
std::unordered_map<TNode, Node> visited; |
61 |
2524 |
std::unordered_map<TNode, Node>::iterator it; |
62 |
2524 |
std::map<Node, FunDefInfo>::const_iterator itf; |
63 |
5048 |
std::vector<TNode> visit; |
64 |
5048 |
TNode cur; |
65 |
5048 |
TNode curEval; |
66 |
5048 |
Node f; |
67 |
2524 |
visit.push_back(n); |
68 |
77616 |
do |
69 |
|
{ |
70 |
80140 |
cur = visit.back(); |
71 |
80140 |
visit.pop_back(); |
72 |
80140 |
it = visited.find(cur); |
73 |
80140 |
Trace("fd-eval-debug") << "evaluate subterm " << cur << std::endl; |
74 |
|
|
75 |
80140 |
if (it == visited.end()) |
76 |
|
{ |
77 |
39239 |
if (cur.isConst()) |
78 |
|
{ |
79 |
12729 |
Trace("fd-eval-debug") << "constant " << cur << std::endl; |
80 |
12729 |
visited[cur] = cur; |
81 |
|
} |
82 |
26510 |
else if (cur.getKind() == ITE) |
83 |
|
{ |
84 |
2087 |
Trace("fd-eval-debug") << "ITE " << cur << std::endl; |
85 |
2087 |
visited[cur] = Node::null(); |
86 |
2087 |
visit.push_back(cur); |
87 |
2087 |
visit.push_back(cur[0]); |
88 |
|
} |
89 |
|
else |
90 |
|
{ |
91 |
24423 |
Trace("fd-eval-debug") << "recurse " << cur << std::endl; |
92 |
24423 |
visited[cur] = Node::null(); |
93 |
24423 |
visit.push_back(cur); |
94 |
72752 |
for (const Node& cn : cur) |
95 |
|
{ |
96 |
48329 |
visit.push_back(cn); |
97 |
|
} |
98 |
|
} |
99 |
|
} |
100 |
|
else |
101 |
|
{ |
102 |
40901 |
curEval = it->second; |
103 |
40901 |
if (curEval.isNull()) |
104 |
|
{ |
105 |
23067 |
Trace("fd-eval-debug") << "from arguments " << cur << std::endl; |
106 |
44330 |
Node ret = cur; |
107 |
23067 |
bool childChanged = false; |
108 |
44330 |
std::vector<Node> children; |
109 |
23067 |
Kind ck = cur.getKind(); |
110 |
|
// If a parameterized node that is not APPLY_UF (which is handled below, |
111 |
|
// we add it to the children vector. |
112 |
23067 |
if (ck != APPLY_UF && cur.getMetaKind() == metakind::PARAMETERIZED) |
113 |
|
{ |
114 |
5253 |
children.push_back(cur.getOperator()); |
115 |
|
} |
116 |
17814 |
else if (ck == ITE) |
117 |
|
{ |
118 |
|
// get evaluation of condition |
119 |
1804 |
it = visited.find(cur[0]); |
120 |
1804 |
Assert(it != visited.end()); |
121 |
1804 |
Assert(!it->second.isNull()); |
122 |
1804 |
if (!it->second.isConst()) |
123 |
|
{ |
124 |
826 |
Trace("fd-eval") << "FunDefEvaluator: couldn't reduce condition of " |
125 |
|
"ITE to const, FAIL\n"; |
126 |
826 |
return Node::null(); |
127 |
|
} |
128 |
|
// pick child to evaluate depending on condition eval |
129 |
978 |
unsigned childIdxToEval = it->second.getConst<bool>() ? 1 : 2; |
130 |
1956 |
Trace("fd-eval-debug2") |
131 |
978 |
<< "FunDefEvaluator: result of ITE condition : " |
132 |
978 |
<< it->second.getConst<bool>() << "\n"; |
133 |
|
// the result will be the result of evaluation the child |
134 |
978 |
visited[cur] = cur[childIdxToEval]; |
135 |
|
// push back self and child. The child will be evaluated first and |
136 |
|
// result will be the result of evaluation child |
137 |
978 |
visit.push_back(cur); |
138 |
978 |
visit.push_back(cur[childIdxToEval]); |
139 |
1956 |
Trace("fd-eval-debug2") << "FunDefEvaluator: result will be from : " |
140 |
978 |
<< cur[childIdxToEval] << "\n"; |
141 |
978 |
continue; |
142 |
|
} |
143 |
21263 |
unsigned child CVC5_UNUSED = 0; |
144 |
62979 |
for (const Node& cn : cur) |
145 |
|
{ |
146 |
41716 |
it = visited.find(cn); |
147 |
41716 |
Assert(it != visited.end()); |
148 |
41716 |
Assert(!it->second.isNull()); |
149 |
41716 |
childChanged = childChanged || cn != it->second; |
150 |
41716 |
children.push_back(it->second); |
151 |
83432 |
Trace("fd-eval-debug2") << "argument " << child++ |
152 |
41716 |
<< " eval : " << it->second << std::endl; |
153 |
|
} |
154 |
21263 |
if (cur.getKind() == APPLY_UF) |
155 |
|
{ |
156 |
|
// need to evaluate it |
157 |
5236 |
f = cur.getOperator(); |
158 |
10472 |
Trace("fd-eval-debug2") |
159 |
5236 |
<< "FunDefEvaluator: need to eval " << f << "\n"; |
160 |
5236 |
itf = d_funDefMap.find(f); |
161 |
5236 |
itCount = funDefCount.find(f); |
162 |
5236 |
if (itCount == funDefCount.end()) |
163 |
|
{ |
164 |
3138 |
funDefCount[f] = 0; |
165 |
3138 |
itCount = funDefCount.find(f); |
166 |
|
} |
167 |
10472 |
if (itf == d_funDefMap.end() |
168 |
10472 |
|| itCount->second > options::sygusRecFunEvalLimit()) |
169 |
|
{ |
170 |
|
Trace("fd-eval") |
171 |
|
<< "FunDefEvaluator: " |
172 |
|
<< (itf == d_funDefMap.end() ? "no definition for " |
173 |
|
: "too many evals for ") |
174 |
|
<< f << ", FAIL" << std::endl; |
175 |
|
return Node::null(); |
176 |
|
} |
177 |
5236 |
++funDefCount[f]; |
178 |
|
// get the function definition |
179 |
10472 |
Node sbody = itf->second.d_body; |
180 |
10472 |
Trace("fd-eval-debug2") |
181 |
5236 |
<< "FunDefEvaluator: definition: " << sbody << "\n"; |
182 |
5236 |
const std::vector<Node>& args = itf->second.d_args; |
183 |
5236 |
if (!args.empty()) |
184 |
|
{ |
185 |
|
// invoke it on arguments using the evaluator |
186 |
5236 |
sbody = d_eval.eval(sbody, args, children); |
187 |
5236 |
if (Trace.isOn("fd-eval-debug2")) |
188 |
|
{ |
189 |
|
Trace("fd-eval-debug2") |
190 |
|
<< "FunDefEvaluator: evaluation with args:\n"; |
191 |
|
for (const Node& ch : children) |
192 |
|
{ |
193 |
|
Trace("fd-eval-debug2") << "..." << ch << "\n"; |
194 |
|
} |
195 |
|
Trace("fd-eval-debug2") |
196 |
|
<< "FunDefEvaluator: results in " << sbody << "\n"; |
197 |
|
} |
198 |
5236 |
Assert(!sbody.isNull()); |
199 |
|
} |
200 |
|
// our result is the result of the body |
201 |
5236 |
visited[cur] = sbody; |
202 |
|
// If its not constant, we push back self and the substituted body. |
203 |
|
// Thus, we evaluate the body first; our result will be the result of |
204 |
|
// evaluating the body. |
205 |
5236 |
if (!sbody.isConst()) |
206 |
|
{ |
207 |
7296 |
Trace("fd-eval-debug2") << "FunDefEvaluator: will map " << cur |
208 |
3648 |
<< " from body " << sbody << "\n"; |
209 |
3648 |
visit.push_back(cur); |
210 |
3648 |
visit.push_back(sbody); |
211 |
|
} |
212 |
|
} |
213 |
|
else |
214 |
|
{ |
215 |
16027 |
if (childChanged) |
216 |
|
{ |
217 |
4279 |
ret = nm->mkNode(cur.getKind(), children); |
218 |
4279 |
ret = Rewriter::rewrite(ret); |
219 |
|
} |
220 |
16027 |
Trace("fd-eval-debug2") << "built from arguments " << ret << "\n"; |
221 |
16027 |
visited[cur] = ret; |
222 |
|
} |
223 |
|
} |
224 |
17834 |
else if (cur != curEval && !curEval.isConst()) |
225 |
|
{ |
226 |
2690 |
Trace("fd-eval-debug") << "from body " << cur << std::endl; |
227 |
2690 |
Trace("fd-eval-debug") << "and eval " << curEval << std::endl; |
228 |
|
// we had to evaluate our body, which should have a definition now |
229 |
2690 |
it = visited.find(curEval); |
230 |
2690 |
if (it == visited.end()) |
231 |
|
{ |
232 |
1 |
Trace("fd-eval-debug2") << "eval without definition\n"; |
233 |
|
// this is the case where curEval was not a constant but it was |
234 |
|
// irreducible, for example (DT_SYGUS_EVAL e args) |
235 |
1 |
visited[cur] = curEval; |
236 |
|
} |
237 |
|
else |
238 |
|
{ |
239 |
5378 |
Trace("fd-eval-debug2") |
240 |
2689 |
<< "eval with definition " << it->second << "\n"; |
241 |
2689 |
visited[cur] = it->second; |
242 |
|
} |
243 |
|
} |
244 |
|
} |
245 |
79314 |
} while (!visit.empty()); |
246 |
1698 |
Trace("fd-eval") << "FunDefEvaluator: return " << visited[n] << ", SUCCESS\n"; |
247 |
1698 |
Assert(visited.find(n) != visited.end()); |
248 |
1698 |
Assert(!visited.find(n)->second.isNull()); |
249 |
1698 |
return visited[n]; |
250 |
|
} |
251 |
|
|
252 |
163409 |
bool FunDefEvaluator::hasDefinitions() const { return !d_funDefMap.empty(); } |
253 |
|
|
254 |
|
} // namespace quantifiers |
255 |
|
} // namespace theory |
256 |
33427 |
} // namespace cvc5 |