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 |
1233 |
FunDefEvaluator::FunDefEvaluator() {} |
30 |
|
|
31 |
33 |
void FunDefEvaluator::assertDefinition(Node q) |
32 |
|
{ |
33 |
33 |
Trace("fd-eval") << "FunDefEvaluator: assertDefinition " << q << std::endl; |
34 |
66 |
Node h = QuantAttributes::getFunDefHead(q); |
35 |
33 |
if (h.isNull()) |
36 |
|
{ |
37 |
|
// not a function definition |
38 |
|
return; |
39 |
|
} |
40 |
|
// h possibly with zero arguments? |
41 |
66 |
Node f = h.hasOperator() ? h.getOperator() : h; |
42 |
33 |
Assert(d_funDefMap.find(f) == d_funDefMap.end()) |
43 |
|
<< "FunDefEvaluator::assertDefinition: function already defined"; |
44 |
33 |
d_funDefs.push_back(q); |
45 |
33 |
FunDefInfo& fdi = d_funDefMap[f]; |
46 |
33 |
fdi.d_quant = q; |
47 |
33 |
fdi.d_body = QuantAttributes::getFunDefBody(q); |
48 |
33 |
Assert(!fdi.d_body.isNull()); |
49 |
33 |
fdi.d_args.insert(fdi.d_args.end(), q[0].begin(), q[0].end()); |
50 |
66 |
Trace("fd-eval") << "FunDefEvaluator: function " << f << " is defined with " |
51 |
33 |
<< fdi.d_args << " / " << fdi.d_body << std::endl; |
52 |
|
} |
53 |
|
|
54 |
975 |
Node FunDefEvaluator::evaluate(Node n) const |
55 |
|
{ |
56 |
|
// should do standard rewrite before this call |
57 |
975 |
Assert(Rewriter::rewrite(n) == n); |
58 |
975 |
Trace("fd-eval") << "FunDefEvaluator: evaluate " << n << std::endl; |
59 |
975 |
NodeManager* nm = NodeManager::currentNM(); |
60 |
1950 |
std::unordered_map<TNode, unsigned> funDefCount; |
61 |
975 |
std::unordered_map<TNode, unsigned>::iterator itCount; |
62 |
1950 |
std::unordered_map<TNode, Node> visited; |
63 |
975 |
std::unordered_map<TNode, Node>::iterator it; |
64 |
975 |
std::map<Node, FunDefInfo>::const_iterator itf; |
65 |
1950 |
std::vector<TNode> visit; |
66 |
1950 |
TNode cur; |
67 |
1950 |
TNode curEval; |
68 |
1950 |
Node f; |
69 |
975 |
visit.push_back(n); |
70 |
34865 |
do |
71 |
|
{ |
72 |
35840 |
cur = visit.back(); |
73 |
35840 |
visit.pop_back(); |
74 |
35840 |
it = visited.find(cur); |
75 |
35840 |
Trace("fd-eval-debug") << "evaluate subterm " << cur << std::endl; |
76 |
|
|
77 |
35840 |
if (it == visited.end()) |
78 |
|
{ |
79 |
16359 |
if (cur.isConst()) |
80 |
|
{ |
81 |
5131 |
Trace("fd-eval-debug") << "constant " << cur << std::endl; |
82 |
5131 |
visited[cur] = cur; |
83 |
|
} |
84 |
11228 |
else if (cur.getKind() == ITE) |
85 |
|
{ |
86 |
934 |
Trace("fd-eval-debug") << "ITE " << cur << std::endl; |
87 |
934 |
visited[cur] = Node::null(); |
88 |
934 |
visit.push_back(cur); |
89 |
934 |
visit.push_back(cur[0]); |
90 |
|
} |
91 |
|
else |
92 |
|
{ |
93 |
10294 |
Trace("fd-eval-debug") << "recurse " << cur << std::endl; |
94 |
10294 |
visited[cur] = Node::null(); |
95 |
10294 |
visit.push_back(cur); |
96 |
30686 |
for (const Node& cn : cur) |
97 |
|
{ |
98 |
20392 |
visit.push_back(cn); |
99 |
|
} |
100 |
|
} |
101 |
|
} |
102 |
|
else |
103 |
|
{ |
104 |
19481 |
curEval = it->second; |
105 |
19481 |
if (curEval.isNull()) |
106 |
|
{ |
107 |
10106 |
Trace("fd-eval-debug") << "from arguments " << cur << std::endl; |
108 |
19318 |
Node ret = cur; |
109 |
10106 |
bool childChanged = false; |
110 |
19318 |
std::vector<Node> children; |
111 |
10106 |
Kind ck = cur.getKind(); |
112 |
|
// If a parameterized node that is not APPLY_UF (which is handled below, |
113 |
|
// we add it to the children vector. |
114 |
10106 |
if (ck != APPLY_UF && cur.getMetaKind() == metakind::PARAMETERIZED) |
115 |
|
{ |
116 |
1875 |
children.push_back(cur.getOperator()); |
117 |
|
} |
118 |
8231 |
else if (ck == ITE) |
119 |
|
{ |
120 |
|
// get evaluation of condition |
121 |
894 |
it = visited.find(cur[0]); |
122 |
894 |
Assert(it != visited.end()); |
123 |
894 |
Assert(!it->second.isNull()); |
124 |
894 |
if (!it->second.isConst()) |
125 |
|
{ |
126 |
370 |
Trace("fd-eval") << "FunDefEvaluator: couldn't reduce condition of " |
127 |
|
"ITE to const, FAIL\n"; |
128 |
370 |
return Node::null(); |
129 |
|
} |
130 |
|
// pick child to evaluate depending on condition eval |
131 |
524 |
unsigned childIdxToEval = it->second.getConst<bool>() ? 1 : 2; |
132 |
1048 |
Trace("fd-eval-debug2") |
133 |
524 |
<< "FunDefEvaluator: result of ITE condition : " |
134 |
524 |
<< it->second.getConst<bool>() << "\n"; |
135 |
|
// the result will be the result of evaluation the child |
136 |
524 |
visited[cur] = cur[childIdxToEval]; |
137 |
|
// push back self and child. The child will be evaluated first and |
138 |
|
// result will be the result of evaluation child |
139 |
524 |
visit.push_back(cur); |
140 |
524 |
visit.push_back(cur[childIdxToEval]); |
141 |
1048 |
Trace("fd-eval-debug2") << "FunDefEvaluator: result will be from : " |
142 |
524 |
<< cur[childIdxToEval] << "\n"; |
143 |
524 |
continue; |
144 |
|
} |
145 |
9212 |
unsigned child CVC5_UNUSED = 0; |
146 |
27477 |
for (const Node& cn : cur) |
147 |
|
{ |
148 |
18265 |
it = visited.find(cn); |
149 |
18265 |
Assert(it != visited.end()); |
150 |
18265 |
Assert(!it->second.isNull()); |
151 |
18265 |
childChanged = childChanged || cn != it->second; |
152 |
18265 |
children.push_back(it->second); |
153 |
36530 |
Trace("fd-eval-debug2") << "argument " << child++ |
154 |
18265 |
<< " eval : " << it->second << std::endl; |
155 |
|
} |
156 |
9212 |
if (cur.getKind() == APPLY_UF) |
157 |
|
{ |
158 |
|
// need to evaluate it |
159 |
3404 |
f = cur.getOperator(); |
160 |
6808 |
Trace("fd-eval-debug2") |
161 |
3404 |
<< "FunDefEvaluator: need to eval " << f << "\n"; |
162 |
3404 |
itf = d_funDefMap.find(f); |
163 |
3404 |
itCount = funDefCount.find(f); |
164 |
3404 |
if (itCount == funDefCount.end()) |
165 |
|
{ |
166 |
1373 |
funDefCount[f] = 0; |
167 |
1373 |
itCount = funDefCount.find(f); |
168 |
|
} |
169 |
6808 |
if (itf == d_funDefMap.end() |
170 |
3404 |
|| itCount->second > options::sygusRecFunEvalLimit()) |
171 |
|
{ |
172 |
|
Trace("fd-eval") |
173 |
|
<< "FunDefEvaluator: " |
174 |
|
<< (itf == d_funDefMap.end() ? "no definition for " |
175 |
|
: "too many evals for ") |
176 |
|
<< f << ", FAIL" << std::endl; |
177 |
|
return Node::null(); |
178 |
|
} |
179 |
3404 |
++funDefCount[f]; |
180 |
|
// get the function definition |
181 |
6808 |
Node sbody = itf->second.d_body; |
182 |
6808 |
Trace("fd-eval-debug2") |
183 |
3404 |
<< "FunDefEvaluator: definition: " << sbody << "\n"; |
184 |
3404 |
const std::vector<Node>& args = itf->second.d_args; |
185 |
3404 |
if (!args.empty()) |
186 |
|
{ |
187 |
|
// invoke it on arguments using the evaluator |
188 |
3404 |
sbody = d_eval.eval(sbody, args, children); |
189 |
3404 |
if (Trace.isOn("fd-eval-debug2")) |
190 |
|
{ |
191 |
|
Trace("fd-eval-debug2") |
192 |
|
<< "FunDefEvaluator: evaluation with args:\n"; |
193 |
|
for (const Node& ch : children) |
194 |
|
{ |
195 |
|
Trace("fd-eval-debug2") << "..." << ch << "\n"; |
196 |
|
} |
197 |
|
Trace("fd-eval-debug2") |
198 |
|
<< "FunDefEvaluator: results in " << sbody << "\n"; |
199 |
|
} |
200 |
3404 |
Assert(!sbody.isNull()); |
201 |
|
} |
202 |
|
// our result is the result of the body |
203 |
3404 |
visited[cur] = sbody; |
204 |
|
// If its not constant, we push back self and the substituted body. |
205 |
|
// Thus, we evaluate the body first; our result will be the result of |
206 |
|
// evaluating the body. |
207 |
3404 |
if (!sbody.isConst()) |
208 |
|
{ |
209 |
3804 |
Trace("fd-eval-debug2") << "FunDefEvaluator: will map " << cur |
210 |
1902 |
<< " from body " << sbody << "\n"; |
211 |
1902 |
visit.push_back(cur); |
212 |
1902 |
visit.push_back(sbody); |
213 |
|
} |
214 |
|
} |
215 |
|
else |
216 |
|
{ |
217 |
5808 |
if (childChanged) |
218 |
|
{ |
219 |
2471 |
ret = nm->mkNode(cur.getKind(), children); |
220 |
2471 |
ret = Rewriter::rewrite(ret); |
221 |
|
} |
222 |
5808 |
Trace("fd-eval-debug2") << "built from arguments " << ret << "\n"; |
223 |
5808 |
visited[cur] = ret; |
224 |
|
} |
225 |
|
} |
226 |
9375 |
else if (cur != curEval && !curEval.isConst()) |
227 |
|
{ |
228 |
1839 |
Trace("fd-eval-debug") << "from body " << cur << std::endl; |
229 |
1839 |
Trace("fd-eval-debug") << "and eval " << curEval << std::endl; |
230 |
|
// we had to evaluate our body, which should have a definition now |
231 |
1839 |
it = visited.find(curEval); |
232 |
1839 |
if (it == visited.end()) |
233 |
|
{ |
234 |
1 |
Trace("fd-eval-debug2") << "eval without definition\n"; |
235 |
|
// this is the case where curEval was not a constant but it was |
236 |
|
// irreducible, for example (DT_SYGUS_EVAL e args) |
237 |
1 |
visited[cur] = curEval; |
238 |
|
} |
239 |
|
else |
240 |
|
{ |
241 |
3676 |
Trace("fd-eval-debug2") |
242 |
1838 |
<< "eval with definition " << it->second << "\n"; |
243 |
1838 |
visited[cur] = it->second; |
244 |
|
} |
245 |
|
} |
246 |
|
} |
247 |
35470 |
} while (!visit.empty()); |
248 |
605 |
Trace("fd-eval") << "FunDefEvaluator: return " << visited[n] << ", SUCCESS\n"; |
249 |
605 |
Assert(visited.find(n) != visited.end()); |
250 |
605 |
Assert(!visited.find(n)->second.isNull()); |
251 |
605 |
return visited[n]; |
252 |
|
} |
253 |
|
|
254 |
48776 |
bool FunDefEvaluator::hasDefinitions() const { return !d_funDefMap.empty(); } |
255 |
|
|
256 |
376 |
const std::vector<Node>& FunDefEvaluator::getDefinitions() const |
257 |
|
{ |
258 |
376 |
return d_funDefs; |
259 |
|
} |
260 |
178 |
Node FunDefEvaluator::getDefinitionFor(Node f) const |
261 |
|
{ |
262 |
178 |
std::map<Node, FunDefInfo>::const_iterator it = d_funDefMap.find(f); |
263 |
178 |
if (it != d_funDefMap.end()) |
264 |
|
{ |
265 |
35 |
return it->second.d_quant; |
266 |
|
} |
267 |
143 |
return Node::null(); |
268 |
|
} |
269 |
|
|
270 |
|
} // namespace quantifiers |
271 |
|
} // namespace theory |
272 |
29574 |
} // namespace cvc5 |