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 |
1190 |
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 |
2794 |
Node FunDefEvaluator::evaluate(Node n) const |
55 |
|
{ |
56 |
|
// should do standard rewrite before this call |
57 |
2794 |
Assert(Rewriter::rewrite(n) == n); |
58 |
2794 |
Trace("fd-eval") << "FunDefEvaluator: evaluate " << n << std::endl; |
59 |
2794 |
NodeManager* nm = NodeManager::currentNM(); |
60 |
5588 |
std::unordered_map<TNode, unsigned> funDefCount; |
61 |
2794 |
std::unordered_map<TNode, unsigned>::iterator itCount; |
62 |
5588 |
std::unordered_map<TNode, Node> visited; |
63 |
2794 |
std::unordered_map<TNode, Node>::iterator it; |
64 |
2794 |
std::map<Node, FunDefInfo>::const_iterator itf; |
65 |
5588 |
std::vector<TNode> visit; |
66 |
5588 |
TNode cur; |
67 |
5588 |
TNode curEval; |
68 |
5588 |
Node f; |
69 |
2794 |
visit.push_back(n); |
70 |
79824 |
do |
71 |
|
{ |
72 |
82618 |
cur = visit.back(); |
73 |
82618 |
visit.pop_back(); |
74 |
82618 |
it = visited.find(cur); |
75 |
82618 |
Trace("fd-eval-debug") << "evaluate subterm " << cur << std::endl; |
76 |
|
|
77 |
82618 |
if (it == visited.end()) |
78 |
|
{ |
79 |
40732 |
if (cur.isConst()) |
80 |
|
{ |
81 |
13291 |
Trace("fd-eval-debug") << "constant " << cur << std::endl; |
82 |
13291 |
visited[cur] = cur; |
83 |
|
} |
84 |
27441 |
else if (cur.getKind() == ITE) |
85 |
|
{ |
86 |
2135 |
Trace("fd-eval-debug") << "ITE " << cur << std::endl; |
87 |
2135 |
visited[cur] = Node::null(); |
88 |
2135 |
visit.push_back(cur); |
89 |
2135 |
visit.push_back(cur[0]); |
90 |
|
} |
91 |
|
else |
92 |
|
{ |
93 |
25306 |
Trace("fd-eval-debug") << "recurse " << cur << std::endl; |
94 |
25306 |
visited[cur] = Node::null(); |
95 |
25306 |
visit.push_back(cur); |
96 |
75029 |
for (const Node& cn : cur) |
97 |
|
{ |
98 |
49723 |
visit.push_back(cn); |
99 |
|
} |
100 |
|
} |
101 |
|
} |
102 |
|
else |
103 |
|
{ |
104 |
41886 |
curEval = it->second; |
105 |
41886 |
if (curEval.isNull()) |
106 |
|
{ |
107 |
23874 |
Trace("fd-eval-debug") << "from arguments " << cur << std::endl; |
108 |
45896 |
Node ret = cur; |
109 |
23874 |
bool childChanged = false; |
110 |
45896 |
std::vector<Node> children; |
111 |
23874 |
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 |
23874 |
if (ck != APPLY_UF && cur.getMetaKind() == metakind::PARAMETERIZED) |
115 |
|
{ |
116 |
5250 |
children.push_back(cur.getOperator()); |
117 |
|
} |
118 |
18624 |
else if (ck == ITE) |
119 |
|
{ |
120 |
|
// get evaluation of condition |
121 |
1852 |
it = visited.find(cur[0]); |
122 |
1852 |
Assert(it != visited.end()); |
123 |
1852 |
Assert(!it->second.isNull()); |
124 |
1852 |
if (!it->second.isConst()) |
125 |
|
{ |
126 |
874 |
Trace("fd-eval") << "FunDefEvaluator: couldn't reduce condition of " |
127 |
|
"ITE to const, FAIL\n"; |
128 |
874 |
return Node::null(); |
129 |
|
} |
130 |
|
// pick child to evaluate depending on condition eval |
131 |
978 |
unsigned childIdxToEval = it->second.getConst<bool>() ? 1 : 2; |
132 |
1956 |
Trace("fd-eval-debug2") |
133 |
978 |
<< "FunDefEvaluator: result of ITE condition : " |
134 |
978 |
<< it->second.getConst<bool>() << "\n"; |
135 |
|
// the result will be the result of evaluation the child |
136 |
978 |
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 |
978 |
visit.push_back(cur); |
140 |
978 |
visit.push_back(cur[childIdxToEval]); |
141 |
1956 |
Trace("fd-eval-debug2") << "FunDefEvaluator: result will be from : " |
142 |
978 |
<< cur[childIdxToEval] << "\n"; |
143 |
978 |
continue; |
144 |
|
} |
145 |
22022 |
unsigned child CVC5_UNUSED = 0; |
146 |
64884 |
for (const Node& cn : cur) |
147 |
|
{ |
148 |
42862 |
it = visited.find(cn); |
149 |
42862 |
Assert(it != visited.end()); |
150 |
42862 |
Assert(!it->second.isNull()); |
151 |
42862 |
childChanged = childChanged || cn != it->second; |
152 |
42862 |
children.push_back(it->second); |
153 |
85724 |
Trace("fd-eval-debug2") << "argument " << child++ |
154 |
42862 |
<< " eval : " << it->second << std::endl; |
155 |
|
} |
156 |
22022 |
if (cur.getKind() == APPLY_UF) |
157 |
|
{ |
158 |
|
// need to evaluate it |
159 |
5325 |
f = cur.getOperator(); |
160 |
10650 |
Trace("fd-eval-debug2") |
161 |
5325 |
<< "FunDefEvaluator: need to eval " << f << "\n"; |
162 |
5325 |
itf = d_funDefMap.find(f); |
163 |
5325 |
itCount = funDefCount.find(f); |
164 |
5325 |
if (itCount == funDefCount.end()) |
165 |
|
{ |
166 |
3215 |
funDefCount[f] = 0; |
167 |
3215 |
itCount = funDefCount.find(f); |
168 |
|
} |
169 |
10650 |
if (itf == d_funDefMap.end() |
170 |
5325 |
|| 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 |
5325 |
++funDefCount[f]; |
180 |
|
// get the function definition |
181 |
10650 |
Node sbody = itf->second.d_body; |
182 |
10650 |
Trace("fd-eval-debug2") |
183 |
5325 |
<< "FunDefEvaluator: definition: " << sbody << "\n"; |
184 |
5325 |
const std::vector<Node>& args = itf->second.d_args; |
185 |
5325 |
if (!args.empty()) |
186 |
|
{ |
187 |
|
// invoke it on arguments using the evaluator |
188 |
5325 |
sbody = d_eval.eval(sbody, args, children); |
189 |
5325 |
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 |
5325 |
Assert(!sbody.isNull()); |
201 |
|
} |
202 |
|
// our result is the result of the body |
203 |
5325 |
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 |
5325 |
if (!sbody.isConst()) |
208 |
|
{ |
209 |
7416 |
Trace("fd-eval-debug2") << "FunDefEvaluator: will map " << cur |
210 |
3708 |
<< " from body " << sbody << "\n"; |
211 |
3708 |
visit.push_back(cur); |
212 |
3708 |
visit.push_back(sbody); |
213 |
|
} |
214 |
|
} |
215 |
|
else |
216 |
|
{ |
217 |
16697 |
if (childChanged) |
218 |
|
{ |
219 |
4311 |
ret = nm->mkNode(cur.getKind(), children); |
220 |
4311 |
ret = Rewriter::rewrite(ret); |
221 |
|
} |
222 |
16697 |
Trace("fd-eval-debug2") << "built from arguments " << ret << "\n"; |
223 |
16697 |
visited[cur] = ret; |
224 |
|
} |
225 |
|
} |
226 |
18012 |
else if (cur != curEval && !curEval.isConst()) |
227 |
|
{ |
228 |
2702 |
Trace("fd-eval-debug") << "from body " << cur << std::endl; |
229 |
2702 |
Trace("fd-eval-debug") << "and eval " << curEval << std::endl; |
230 |
|
// we had to evaluate our body, which should have a definition now |
231 |
2702 |
it = visited.find(curEval); |
232 |
2702 |
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 |
5402 |
Trace("fd-eval-debug2") |
242 |
2701 |
<< "eval with definition " << it->second << "\n"; |
243 |
2701 |
visited[cur] = it->second; |
244 |
|
} |
245 |
|
} |
246 |
|
} |
247 |
81744 |
} while (!visit.empty()); |
248 |
1920 |
Trace("fd-eval") << "FunDefEvaluator: return " << visited[n] << ", SUCCESS\n"; |
249 |
1920 |
Assert(visited.find(n) != visited.end()); |
250 |
1920 |
Assert(!visited.find(n)->second.isNull()); |
251 |
1920 |
return visited[n]; |
252 |
|
} |
253 |
|
|
254 |
171357 |
bool FunDefEvaluator::hasDefinitions() const { return !d_funDefMap.empty(); } |
255 |
|
|
256 |
424 |
const std::vector<Node>& FunDefEvaluator::getDefinitions() const |
257 |
|
{ |
258 |
424 |
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 |
29505 |
} // namespace cvc5 |