1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Martin Brain, Andrew Reynolds, Andres Noetzli |
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 |
|
* Theory of floating-point arithmetic. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/fp/theory_fp.h" |
17 |
|
|
18 |
|
#include <set> |
19 |
|
#include <stack> |
20 |
|
#include <unordered_set> |
21 |
|
#include <vector> |
22 |
|
|
23 |
|
#include "base/configuration.h" |
24 |
|
#include "expr/skolem_manager.h" |
25 |
|
#include "options/fp_options.h" |
26 |
|
#include "smt/logic_exception.h" |
27 |
|
#include "theory/fp/fp_converter.h" |
28 |
|
#include "theory/fp/theory_fp_rewriter.h" |
29 |
|
#include "theory/output_channel.h" |
30 |
|
#include "theory/rewriter.h" |
31 |
|
#include "theory/theory_model.h" |
32 |
|
#include "util/floatingpoint.h" |
33 |
|
|
34 |
|
using namespace std; |
35 |
|
|
36 |
|
namespace cvc5 { |
37 |
|
namespace theory { |
38 |
|
namespace fp { |
39 |
|
|
40 |
|
namespace helper { |
41 |
2 |
Node buildConjunct(const std::vector<TNode> &assumptions) { |
42 |
2 |
if (assumptions.size() == 0) { |
43 |
|
return NodeManager::currentNM()->mkConst<bool>(true); |
44 |
|
|
45 |
2 |
} else if (assumptions.size() == 1) { |
46 |
|
return assumptions[0]; |
47 |
|
|
48 |
|
} else { |
49 |
|
// \todo see bv::utils::flattenAnd |
50 |
|
|
51 |
4 |
NodeBuilder conjunction(kind::AND); |
52 |
6 |
for (std::vector<TNode>::const_iterator it = assumptions.begin(); |
53 |
6 |
it != assumptions.end(); ++it) { |
54 |
4 |
conjunction << *it; |
55 |
|
} |
56 |
|
|
57 |
2 |
return conjunction; |
58 |
|
} |
59 |
|
} |
60 |
|
} // namespace helper |
61 |
|
|
62 |
|
/** Constructs a new instance of TheoryFp w.r.t. the provided contexts. */ |
63 |
9927 |
TheoryFp::TheoryFp(Env& env, OutputChannel& out, Valuation valuation) |
64 |
|
: Theory(THEORY_FP, env, out, valuation), |
65 |
|
d_notification(*this), |
66 |
9927 |
d_registeredTerms(getUserContext()), |
67 |
9927 |
d_conv(new FpConverter(getUserContext())), |
68 |
|
d_expansionRequested(false), |
69 |
9927 |
d_realToFloatMap(getUserContext()), |
70 |
9927 |
d_floatToRealMap(getUserContext()), |
71 |
9927 |
d_abstractionMap(getUserContext()), |
72 |
|
d_rewriter(getUserContext()), |
73 |
|
d_state(env, valuation), |
74 |
|
d_im(*this, d_state, d_pnm, "theory::fp::", false), |
75 |
59562 |
d_wbFactsCache(getUserContext()) |
76 |
|
{ |
77 |
|
// indicate we are using the default theory state and inference manager |
78 |
9927 |
d_theoryState = &d_state; |
79 |
9927 |
d_inferManager = &d_im; |
80 |
9927 |
} |
81 |
|
|
82 |
9927 |
TheoryRewriter* TheoryFp::getTheoryRewriter() { return &d_rewriter; } |
83 |
|
|
84 |
3784 |
ProofRuleChecker* TheoryFp::getProofChecker() { return nullptr; } |
85 |
|
|
86 |
9927 |
bool TheoryFp::needsEqualityEngine(EeSetupInfo& esi) |
87 |
|
{ |
88 |
9927 |
esi.d_notify = &d_notification; |
89 |
9927 |
esi.d_name = "theory::fp::ee"; |
90 |
9927 |
return true; |
91 |
|
} |
92 |
|
|
93 |
9927 |
void TheoryFp::finishInit() |
94 |
|
{ |
95 |
9927 |
Assert(d_equalityEngine != nullptr); |
96 |
|
// Kinds that are to be handled in the congruence closure |
97 |
|
|
98 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ABS); |
99 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_NEG); |
100 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ADD); |
101 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SUB); // Removed |
102 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MULT); |
103 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_DIV); |
104 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_FMA); |
105 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SQRT); |
106 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_REM); |
107 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_RTI); |
108 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN); // Removed |
109 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX); // Removed |
110 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN_TOTAL); |
111 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX_TOTAL); |
112 |
|
|
113 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_EQ); // Removed |
114 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LEQ); |
115 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LT); |
116 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GEQ); // Removed |
117 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GT); // Removed |
118 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISN); |
119 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISSN); |
120 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISZ); |
121 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISINF); |
122 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNAN); |
123 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNEG); |
124 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISPOS); |
125 |
|
|
126 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_IEEE_BITVECTOR); |
127 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_FLOATINGPOINT); |
128 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_REAL); |
129 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR); |
130 |
9927 |
d_equalityEngine->addFunctionKind( |
131 |
|
kind::FLOATINGPOINT_TO_FP_UNSIGNED_BITVECTOR); |
132 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_GENERIC); // |
133 |
|
// Needed in parsing, should be rewritten away |
134 |
|
|
135 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_UBV); // Removed |
136 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_SBV); // Removed |
137 |
|
// d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_REAL); // Removed |
138 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_UBV_TOTAL); |
139 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_SBV_TOTAL); |
140 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_REAL_TOTAL); |
141 |
|
|
142 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_NAN); |
143 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_INF); |
144 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_ZERO); |
145 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGN); |
146 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_EXPONENT); |
147 |
9927 |
d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGNIFICAND); |
148 |
9927 |
d_equalityEngine->addFunctionKind(kind::ROUNDINGMODE_BITBLAST); |
149 |
9927 |
} |
150 |
|
|
151 |
|
Node TheoryFp::abstractRealToFloat(Node node) |
152 |
|
{ |
153 |
|
Assert(node.getKind() == kind::FLOATINGPOINT_TO_FP_REAL); |
154 |
|
TypeNode t(node.getType()); |
155 |
|
Assert(t.getKind() == kind::FLOATINGPOINT_TYPE); |
156 |
|
|
157 |
|
NodeManager *nm = NodeManager::currentNM(); |
158 |
|
SkolemManager* sm = nm->getSkolemManager(); |
159 |
|
ConversionAbstractionMap::const_iterator i(d_realToFloatMap.find(t)); |
160 |
|
|
161 |
|
Node fun; |
162 |
|
if (i == d_realToFloatMap.end()) |
163 |
|
{ |
164 |
|
std::vector<TypeNode> args(2); |
165 |
|
args[0] = node[0].getType(); |
166 |
|
args[1] = node[1].getType(); |
167 |
|
fun = sm->mkDummySkolem("floatingpoint_abstract_real_to_float", |
168 |
|
nm->mkFunctionType(args, node.getType()), |
169 |
|
"floatingpoint_abstract_real_to_float", |
170 |
|
NodeManager::SKOLEM_EXACT_NAME); |
171 |
|
d_realToFloatMap.insert(t, fun); |
172 |
|
} |
173 |
|
else |
174 |
|
{ |
175 |
|
fun = (*i).second; |
176 |
|
} |
177 |
|
Node uf = nm->mkNode(kind::APPLY_UF, fun, node[0], node[1]); |
178 |
|
|
179 |
|
d_abstractionMap.insert(uf, node); |
180 |
|
|
181 |
|
return uf; |
182 |
|
} |
183 |
|
|
184 |
4 |
Node TheoryFp::abstractFloatToReal(Node node) |
185 |
|
{ |
186 |
4 |
Assert(node.getKind() == kind::FLOATINGPOINT_TO_REAL_TOTAL); |
187 |
8 |
TypeNode t(node[0].getType()); |
188 |
4 |
Assert(t.getKind() == kind::FLOATINGPOINT_TYPE); |
189 |
|
|
190 |
4 |
NodeManager *nm = NodeManager::currentNM(); |
191 |
4 |
SkolemManager* sm = nm->getSkolemManager(); |
192 |
4 |
ConversionAbstractionMap::const_iterator i(d_floatToRealMap.find(t)); |
193 |
|
|
194 |
8 |
Node fun; |
195 |
4 |
if (i == d_floatToRealMap.end()) |
196 |
|
{ |
197 |
8 |
std::vector<TypeNode> args(2); |
198 |
4 |
args[0] = t; |
199 |
4 |
args[1] = nm->realType(); |
200 |
12 |
fun = sm->mkDummySkolem("floatingpoint_abstract_float_to_real", |
201 |
8 |
nm->mkFunctionType(args, nm->realType()), |
202 |
|
"floatingpoint_abstract_float_to_real", |
203 |
|
NodeManager::SKOLEM_EXACT_NAME); |
204 |
4 |
d_floatToRealMap.insert(t, fun); |
205 |
|
} |
206 |
|
else |
207 |
|
{ |
208 |
|
fun = (*i).second; |
209 |
|
} |
210 |
4 |
Node uf = nm->mkNode(kind::APPLY_UF, fun, node[0], node[1]); |
211 |
|
|
212 |
4 |
d_abstractionMap.insert(uf, node); |
213 |
|
|
214 |
8 |
return uf; |
215 |
|
} |
216 |
|
|
217 |
2683 |
TrustNode TheoryFp::ppRewrite(TNode node, std::vector<SkolemLemma>& lems) |
218 |
|
{ |
219 |
2683 |
Trace("fp-ppRewrite") << "TheoryFp::ppRewrite(): " << node << std::endl; |
220 |
|
// first, see if we need to expand definitions |
221 |
5366 |
TrustNode texp = d_rewriter.expandDefinition(node); |
222 |
2683 |
if (!texp.isNull()) |
223 |
|
{ |
224 |
8 |
return texp; |
225 |
|
} |
226 |
|
|
227 |
5350 |
Node res = node; |
228 |
|
|
229 |
|
// Abstract conversion functions |
230 |
2675 |
if (node.getKind() == kind::FLOATINGPOINT_TO_REAL_TOTAL) |
231 |
|
{ |
232 |
4 |
res = abstractFloatToReal(node); |
233 |
|
|
234 |
|
// Generate some lemmas |
235 |
4 |
NodeManager *nm = NodeManager::currentNM(); |
236 |
|
|
237 |
|
Node pd = |
238 |
|
nm->mkNode(kind::IMPLIES, |
239 |
16 |
nm->mkNode(kind::OR, |
240 |
8 |
nm->mkNode(kind::FLOATINGPOINT_ISNAN, node[0]), |
241 |
8 |
nm->mkNode(kind::FLOATINGPOINT_ISINF, node[0])), |
242 |
16 |
nm->mkNode(kind::EQUAL, res, node[1])); |
243 |
4 |
handleLemma(pd, InferenceId::FP_PREPROCESS); |
244 |
|
|
245 |
|
Node z = |
246 |
|
nm->mkNode(kind::IMPLIES, |
247 |
8 |
nm->mkNode(kind::FLOATINGPOINT_ISZ, node[0]), |
248 |
16 |
nm->mkNode(kind::EQUAL, res, nm->mkConst(Rational(0U)))); |
249 |
4 |
handleLemma(z, InferenceId::FP_PREPROCESS); |
250 |
|
|
251 |
|
// TODO : bounds on the output from largest floats, #1914 |
252 |
|
} |
253 |
2671 |
else if (node.getKind() == kind::FLOATINGPOINT_TO_FP_REAL) |
254 |
|
{ |
255 |
|
res = abstractRealToFloat(node); |
256 |
|
|
257 |
|
// Generate some lemmas |
258 |
|
NodeManager *nm = NodeManager::currentNM(); |
259 |
|
|
260 |
|
Node nnan = |
261 |
|
nm->mkNode(kind::NOT, nm->mkNode(kind::FLOATINGPOINT_ISNAN, res)); |
262 |
|
handleLemma(nnan, InferenceId::FP_PREPROCESS); |
263 |
|
|
264 |
|
Node z = nm->mkNode( |
265 |
|
kind::IMPLIES, |
266 |
|
nm->mkNode(kind::EQUAL, node[1], nm->mkConst(Rational(0U))), |
267 |
|
nm->mkNode(kind::EQUAL, |
268 |
|
res, |
269 |
|
nm->mkConst(FloatingPoint::makeZero( |
270 |
|
res.getType().getConst<FloatingPointSize>(), false)))); |
271 |
|
handleLemma(z, InferenceId::FP_PREPROCESS); |
272 |
|
|
273 |
|
// TODO : rounding-mode specific bounds on floats that don't give infinity |
274 |
|
// BEWARE of directed rounding! #1914 |
275 |
|
} |
276 |
|
|
277 |
2675 |
if (res != node) |
278 |
|
{ |
279 |
8 |
Trace("fp-ppRewrite") << "TheoryFp::ppRewrite(): node " << node |
280 |
4 |
<< " rewritten to " << res << std::endl; |
281 |
4 |
return TrustNode::mkTrustRewrite(node, res, nullptr); |
282 |
|
} |
283 |
|
|
284 |
2671 |
return TrustNode::null(); |
285 |
|
} |
286 |
|
|
287 |
4 |
bool TheoryFp::refineAbstraction(TheoryModel *m, TNode abstract, TNode concrete) |
288 |
|
{ |
289 |
8 |
Trace("fp-refineAbstraction") << "TheoryFp::refineAbstraction(): " << abstract |
290 |
4 |
<< " vs. " << concrete << std::endl; |
291 |
4 |
Kind k = concrete.getKind(); |
292 |
4 |
if (k == kind::FLOATINGPOINT_TO_REAL_TOTAL) |
293 |
|
{ |
294 |
|
// Get the values |
295 |
4 |
Assert(m->hasTerm(abstract)); |
296 |
4 |
Assert(m->hasTerm(concrete[0])); |
297 |
4 |
Assert(m->hasTerm(concrete[1])); |
298 |
|
|
299 |
8 |
Node abstractValue = m->getValue(abstract); |
300 |
8 |
Node floatValue = m->getValue(concrete[0]); |
301 |
8 |
Node undefValue = m->getValue(concrete[1]); |
302 |
|
|
303 |
4 |
Assert(!abstractValue.isNull()); |
304 |
4 |
Assert(!floatValue.isNull()); |
305 |
4 |
Assert(!undefValue.isNull()); |
306 |
4 |
Assert(abstractValue.isConst()); |
307 |
4 |
Assert(floatValue.isConst()); |
308 |
4 |
Assert(undefValue.isConst()); |
309 |
|
|
310 |
|
// Work out the actual value for those args |
311 |
4 |
NodeManager *nm = NodeManager::currentNM(); |
312 |
|
|
313 |
|
Node evaluate = |
314 |
8 |
nm->mkNode(kind::FLOATINGPOINT_TO_REAL_TOTAL, floatValue, undefValue); |
315 |
8 |
Node concreteValue = Rewriter::rewrite(evaluate); |
316 |
4 |
Assert(concreteValue.isConst()); |
317 |
|
|
318 |
8 |
Trace("fp-refineAbstraction") |
319 |
8 |
<< "TheoryFp::refineAbstraction(): " << concrete[0] << " = " |
320 |
4 |
<< floatValue << std::endl |
321 |
8 |
<< "TheoryFp::refineAbstraction(): " << concrete[1] << " = " |
322 |
4 |
<< undefValue << std::endl |
323 |
4 |
<< "TheoryFp::refineAbstraction(): " << abstract << " = " |
324 |
4 |
<< abstractValue << std::endl |
325 |
4 |
<< "TheoryFp::refineAbstraction(): " << concrete << " = " |
326 |
4 |
<< concreteValue << std::endl; |
327 |
|
|
328 |
4 |
if (abstractValue != concreteValue) |
329 |
|
{ |
330 |
|
// Need refinement lemmas |
331 |
|
// only in the normal and subnormal case |
332 |
|
Assert(floatValue.getConst<FloatingPoint>().isNormal() |
333 |
|
|| floatValue.getConst<FloatingPoint>().isSubnormal()); |
334 |
|
|
335 |
|
Node defined = nm->mkNode( |
336 |
|
kind::AND, |
337 |
|
nm->mkNode(kind::NOT, |
338 |
|
nm->mkNode(kind::FLOATINGPOINT_ISNAN, concrete[0])), |
339 |
|
nm->mkNode(kind::NOT, |
340 |
|
nm->mkNode(kind::FLOATINGPOINT_ISINF, concrete[0]))); |
341 |
|
// First the "forward" constraints |
342 |
|
Node fg = nm->mkNode( |
343 |
|
kind::IMPLIES, |
344 |
|
defined, |
345 |
|
nm->mkNode( |
346 |
|
kind::EQUAL, |
347 |
|
nm->mkNode(kind::FLOATINGPOINT_GEQ, concrete[0], floatValue), |
348 |
|
nm->mkNode(kind::GEQ, abstract, concreteValue))); |
349 |
|
handleLemma(fg, InferenceId::FP_PREPROCESS); |
350 |
|
|
351 |
|
Node fl = nm->mkNode( |
352 |
|
kind::IMPLIES, |
353 |
|
defined, |
354 |
|
nm->mkNode( |
355 |
|
kind::EQUAL, |
356 |
|
nm->mkNode(kind::FLOATINGPOINT_LEQ, concrete[0], floatValue), |
357 |
|
nm->mkNode(kind::LEQ, abstract, concreteValue))); |
358 |
|
handleLemma(fl, InferenceId::FP_PREPROCESS); |
359 |
|
|
360 |
|
// Then the backwards constraints |
361 |
|
Node floatAboveAbstract = Rewriter::rewrite( |
362 |
|
nm->mkNode(kind::FLOATINGPOINT_TO_FP_REAL, |
363 |
|
nm->mkConst(FloatingPointToFPReal( |
364 |
|
concrete[0].getType().getConst<FloatingPointSize>())), |
365 |
|
nm->mkConst(RoundingMode::ROUND_TOWARD_POSITIVE), |
366 |
|
abstractValue)); |
367 |
|
|
368 |
|
Node bg = nm->mkNode( |
369 |
|
kind::IMPLIES, |
370 |
|
defined, |
371 |
|
nm->mkNode( |
372 |
|
kind::EQUAL, |
373 |
|
nm->mkNode( |
374 |
|
kind::FLOATINGPOINT_GEQ, concrete[0], floatAboveAbstract), |
375 |
|
nm->mkNode(kind::GEQ, abstract, abstractValue))); |
376 |
|
handleLemma(bg, InferenceId::FP_PREPROCESS); |
377 |
|
|
378 |
|
Node floatBelowAbstract = Rewriter::rewrite( |
379 |
|
nm->mkNode(kind::FLOATINGPOINT_TO_FP_REAL, |
380 |
|
nm->mkConst(FloatingPointToFPReal( |
381 |
|
concrete[0].getType().getConst<FloatingPointSize>())), |
382 |
|
nm->mkConst(RoundingMode::ROUND_TOWARD_NEGATIVE), |
383 |
|
abstractValue)); |
384 |
|
|
385 |
|
Node bl = nm->mkNode( |
386 |
|
kind::IMPLIES, |
387 |
|
defined, |
388 |
|
nm->mkNode( |
389 |
|
kind::EQUAL, |
390 |
|
nm->mkNode( |
391 |
|
kind::FLOATINGPOINT_LEQ, concrete[0], floatBelowAbstract), |
392 |
|
nm->mkNode(kind::LEQ, abstract, abstractValue))); |
393 |
|
handleLemma(bl, InferenceId::FP_PREPROCESS); |
394 |
|
// TODO : see if the overflow conditions could be improved #1914 |
395 |
|
|
396 |
|
return true; |
397 |
|
} |
398 |
|
else |
399 |
|
{ |
400 |
|
// No refinement needed |
401 |
4 |
return false; |
402 |
|
} |
403 |
|
} |
404 |
|
else if (k == kind::FLOATINGPOINT_TO_FP_REAL) |
405 |
|
{ |
406 |
|
// Get the values |
407 |
|
Assert(m->hasTerm(abstract)); |
408 |
|
Assert(m->hasTerm(concrete[0])); |
409 |
|
Assert(m->hasTerm(concrete[1])); |
410 |
|
|
411 |
|
Node abstractValue = m->getValue(abstract); |
412 |
|
Node rmValue = m->getValue(concrete[0]); |
413 |
|
Node realValue = m->getValue(concrete[1]); |
414 |
|
|
415 |
|
Assert(!abstractValue.isNull()); |
416 |
|
Assert(!rmValue.isNull()); |
417 |
|
Assert(!realValue.isNull()); |
418 |
|
Assert(abstractValue.isConst()); |
419 |
|
Assert(rmValue.isConst()); |
420 |
|
Assert(realValue.isConst()); |
421 |
|
|
422 |
|
// Work out the actual value for those args |
423 |
|
NodeManager *nm = NodeManager::currentNM(); |
424 |
|
|
425 |
|
Node evaluate = |
426 |
|
nm->mkNode(kind::FLOATINGPOINT_TO_FP_REAL, |
427 |
|
nm->mkConst(FloatingPointToFPReal( |
428 |
|
concrete.getType().getConst<FloatingPointSize>())), |
429 |
|
rmValue, |
430 |
|
realValue); |
431 |
|
Node concreteValue = Rewriter::rewrite(evaluate); |
432 |
|
Assert(concreteValue.isConst()); |
433 |
|
|
434 |
|
Trace("fp-refineAbstraction") |
435 |
|
<< "TheoryFp::refineAbstraction(): " << concrete[0] << " = " << rmValue |
436 |
|
<< std::endl |
437 |
|
<< "TheoryFp::refineAbstraction(): " << concrete[1] << " = " |
438 |
|
<< realValue << std::endl |
439 |
|
<< "TheoryFp::refineAbstraction(): " << abstract << " = " |
440 |
|
<< abstractValue << std::endl |
441 |
|
<< "TheoryFp::refineAbstraction(): " << concrete << " = " |
442 |
|
<< concreteValue << std::endl; |
443 |
|
|
444 |
|
if (abstractValue != concreteValue) |
445 |
|
{ |
446 |
|
Assert(!abstractValue.getConst<FloatingPoint>().isNaN()); |
447 |
|
Assert(!concreteValue.getConst<FloatingPoint>().isNaN()); |
448 |
|
|
449 |
|
Node correctRoundingMode = nm->mkNode(kind::EQUAL, concrete[0], rmValue); |
450 |
|
// TODO : Generalise to all rounding modes #1914 |
451 |
|
|
452 |
|
// First the "forward" constraints |
453 |
|
Node fg = nm->mkNode( |
454 |
|
kind::IMPLIES, |
455 |
|
correctRoundingMode, |
456 |
|
nm->mkNode( |
457 |
|
kind::EQUAL, |
458 |
|
nm->mkNode(kind::GEQ, concrete[1], realValue), |
459 |
|
nm->mkNode(kind::FLOATINGPOINT_GEQ, abstract, concreteValue))); |
460 |
|
handleLemma(fg, InferenceId::FP_PREPROCESS); |
461 |
|
|
462 |
|
Node fl = nm->mkNode( |
463 |
|
kind::IMPLIES, |
464 |
|
correctRoundingMode, |
465 |
|
nm->mkNode( |
466 |
|
kind::EQUAL, |
467 |
|
nm->mkNode(kind::LEQ, concrete[1], realValue), |
468 |
|
nm->mkNode(kind::FLOATINGPOINT_LEQ, abstract, concreteValue))); |
469 |
|
handleLemma(fl, InferenceId::FP_PREPROCESS); |
470 |
|
|
471 |
|
// Then the backwards constraints |
472 |
|
if (!abstractValue.getConst<FloatingPoint>().isInfinite()) |
473 |
|
{ |
474 |
|
Node realValueOfAbstract = |
475 |
|
Rewriter::rewrite(nm->mkNode(kind::FLOATINGPOINT_TO_REAL_TOTAL, |
476 |
|
abstractValue, |
477 |
|
nm->mkConst(Rational(0U)))); |
478 |
|
|
479 |
|
Node bg = nm->mkNode( |
480 |
|
kind::IMPLIES, |
481 |
|
correctRoundingMode, |
482 |
|
nm->mkNode( |
483 |
|
kind::EQUAL, |
484 |
|
nm->mkNode(kind::GEQ, concrete[1], realValueOfAbstract), |
485 |
|
nm->mkNode(kind::FLOATINGPOINT_GEQ, abstract, abstractValue))); |
486 |
|
handleLemma(bg, InferenceId::FP_PREPROCESS); |
487 |
|
|
488 |
|
Node bl = nm->mkNode( |
489 |
|
kind::IMPLIES, |
490 |
|
correctRoundingMode, |
491 |
|
nm->mkNode( |
492 |
|
kind::EQUAL, |
493 |
|
nm->mkNode(kind::LEQ, concrete[1], realValueOfAbstract), |
494 |
|
nm->mkNode(kind::FLOATINGPOINT_LEQ, abstract, abstractValue))); |
495 |
|
handleLemma(bl, InferenceId::FP_PREPROCESS); |
496 |
|
} |
497 |
|
|
498 |
|
return true; |
499 |
|
} |
500 |
|
else |
501 |
|
{ |
502 |
|
// No refinement needed |
503 |
|
return false; |
504 |
|
} |
505 |
|
} |
506 |
|
else |
507 |
|
{ |
508 |
|
Unreachable() << "Unknown abstraction"; |
509 |
|
} |
510 |
|
|
511 |
|
return false; |
512 |
|
} |
513 |
|
|
514 |
2050 |
void TheoryFp::convertAndEquateTerm(TNode node) |
515 |
|
{ |
516 |
2050 |
Trace("fp-convertTerm") << "TheoryFp::convertTerm(): " << node << std::endl; |
517 |
|
|
518 |
2050 |
size_t oldSize = d_conv->d_additionalAssertions.size(); |
519 |
|
|
520 |
4100 |
Node converted(d_conv->convert(node)); |
521 |
|
|
522 |
2050 |
size_t newSize = d_conv->d_additionalAssertions.size(); |
523 |
|
|
524 |
2050 |
if (converted != node) { |
525 |
976 |
Debug("fp-convertTerm") |
526 |
488 |
<< "TheoryFp::convertTerm(): before " << node << std::endl; |
527 |
976 |
Debug("fp-convertTerm") |
528 |
488 |
<< "TheoryFp::convertTerm(): after " << converted << std::endl; |
529 |
|
} |
530 |
|
|
531 |
2050 |
Assert(oldSize <= newSize); |
532 |
|
|
533 |
2350 |
while (oldSize < newSize) |
534 |
|
{ |
535 |
300 |
Node addA = d_conv->d_additionalAssertions[oldSize]; |
536 |
|
|
537 |
300 |
Debug("fp-convertTerm") << "TheoryFp::convertTerm(): additional assertion " |
538 |
150 |
<< addA << std::endl; |
539 |
|
|
540 |
150 |
NodeManager* nm = NodeManager::currentNM(); |
541 |
|
|
542 |
150 |
handleLemma( |
543 |
300 |
nm->mkNode(kind::EQUAL, addA, nm->mkConst(::cvc5::BitVector(1U, 1U))), |
544 |
|
InferenceId::FP_EQUATE_TERM); |
545 |
|
|
546 |
150 |
++oldSize; |
547 |
|
} |
548 |
|
|
549 |
|
// Equate the floating-point atom and the converted one. |
550 |
|
// Also adds the bit-vectors to the bit-vector solver. |
551 |
2050 |
if (node.getType().isBoolean()) |
552 |
|
{ |
553 |
662 |
if (converted != node) |
554 |
|
{ |
555 |
484 |
Assert(converted.getType().isBitVector()); |
556 |
|
|
557 |
484 |
NodeManager* nm = NodeManager::currentNM(); |
558 |
|
|
559 |
484 |
handleLemma( |
560 |
968 |
nm->mkNode(kind::EQUAL, |
561 |
|
node, |
562 |
968 |
nm->mkNode(kind::EQUAL, |
563 |
|
converted, |
564 |
968 |
nm->mkConst(::cvc5::BitVector(1U, 1U)))), |
565 |
|
InferenceId::FP_EQUATE_TERM); |
566 |
|
} |
567 |
|
else |
568 |
|
{ |
569 |
178 |
Assert((node.getKind() == kind::EQUAL)); |
570 |
|
} |
571 |
|
} |
572 |
1388 |
else if (node.getType().isBitVector()) |
573 |
|
{ |
574 |
833 |
if (converted != node) { |
575 |
4 |
Assert(converted.getType().isBitVector()); |
576 |
|
|
577 |
4 |
handleLemma( |
578 |
8 |
NodeManager::currentNM()->mkNode(kind::EQUAL, node, converted), |
579 |
|
InferenceId::FP_EQUATE_TERM); |
580 |
|
} |
581 |
|
} |
582 |
|
|
583 |
4100 |
return; |
584 |
|
} |
585 |
|
|
586 |
7627 |
void TheoryFp::registerTerm(TNode node) |
587 |
|
{ |
588 |
7627 |
Trace("fp-registerTerm") << "TheoryFp::registerTerm(): " << node << std::endl; |
589 |
|
|
590 |
7627 |
if (!isRegistered(node)) |
591 |
|
{ |
592 |
2062 |
Kind k = node.getKind(); |
593 |
2062 |
Assert(k != kind::FLOATINGPOINT_TO_FP_GENERIC |
594 |
|
&& k != kind::FLOATINGPOINT_SUB && k != kind::FLOATINGPOINT_EQ |
595 |
|
&& k != kind::FLOATINGPOINT_GEQ && k != kind::FLOATINGPOINT_GT); |
596 |
|
|
597 |
2062 |
bool success = d_registeredTerms.insert(node); |
598 |
|
(void)success; // Only used for assertion |
599 |
2062 |
Assert(success); |
600 |
|
|
601 |
|
// Add to the equality engine |
602 |
2062 |
if (k == kind::EQUAL) |
603 |
|
{ |
604 |
523 |
d_equalityEngine->addTriggerPredicate(node); |
605 |
|
} |
606 |
|
else |
607 |
|
{ |
608 |
1539 |
d_equalityEngine->addTerm(node); |
609 |
|
} |
610 |
|
|
611 |
|
// Give the expansion of classifications in terms of equalities |
612 |
|
// This should make equality reasoning slightly more powerful. |
613 |
2062 |
if ((k == kind::FLOATINGPOINT_ISNAN) || (k == kind::FLOATINGPOINT_ISZ) |
614 |
2031 |
|| (k == kind::FLOATINGPOINT_ISINF)) |
615 |
|
{ |
616 |
60 |
NodeManager *nm = NodeManager::currentNM(); |
617 |
60 |
FloatingPointSize s = node[0].getType().getConst<FloatingPointSize>(); |
618 |
120 |
Node equalityAlias = Node::null(); |
619 |
|
|
620 |
60 |
if (k == kind::FLOATINGPOINT_ISNAN) |
621 |
|
{ |
622 |
24 |
equalityAlias = nm->mkNode( |
623 |
48 |
kind::EQUAL, node[0], nm->mkConst(FloatingPoint::makeNaN(s))); |
624 |
|
} |
625 |
36 |
else if (k == kind::FLOATINGPOINT_ISZ) |
626 |
|
{ |
627 |
21 |
equalityAlias = nm->mkNode( |
628 |
|
kind::OR, |
629 |
14 |
nm->mkNode(kind::EQUAL, |
630 |
|
node[0], |
631 |
14 |
nm->mkConst(FloatingPoint::makeZero(s, true))), |
632 |
14 |
nm->mkNode(kind::EQUAL, |
633 |
|
node[0], |
634 |
14 |
nm->mkConst(FloatingPoint::makeZero(s, false)))); |
635 |
|
} |
636 |
29 |
else if (k == kind::FLOATINGPOINT_ISINF) |
637 |
|
{ |
638 |
87 |
equalityAlias = nm->mkNode( |
639 |
|
kind::OR, |
640 |
58 |
nm->mkNode(kind::EQUAL, |
641 |
|
node[0], |
642 |
58 |
nm->mkConst(FloatingPoint::makeInf(s, true))), |
643 |
58 |
nm->mkNode(kind::EQUAL, |
644 |
|
node[0], |
645 |
58 |
nm->mkConst(FloatingPoint::makeInf(s, false)))); |
646 |
|
} |
647 |
|
else |
648 |
|
{ |
649 |
|
Unreachable() << "Only isNaN, isInf and isZero have aliases"; |
650 |
|
} |
651 |
|
|
652 |
60 |
handleLemma(nm->mkNode(kind::EQUAL, node, equalityAlias), |
653 |
|
InferenceId::FP_REGISTER_TERM); |
654 |
|
} |
655 |
|
|
656 |
|
/* When not word-blasting lazier, we word-blast every term on |
657 |
|
* registration. */ |
658 |
2062 |
if (!options::fpLazyWb()) |
659 |
|
{ |
660 |
2022 |
convertAndEquateTerm(node); |
661 |
|
} |
662 |
|
} |
663 |
7627 |
return; |
664 |
|
} |
665 |
|
|
666 |
17604 |
bool TheoryFp::isRegistered(TNode node) |
667 |
|
{ |
668 |
17604 |
return d_registeredTerms.find(node) != d_registeredTerms.end(); |
669 |
|
} |
670 |
|
|
671 |
2652 |
void TheoryFp::preRegisterTerm(TNode node) |
672 |
|
{ |
673 |
2652 |
if (!options::fpExp()) |
674 |
|
{ |
675 |
1210 |
TypeNode tn = node.getType(); |
676 |
605 |
if (tn.isFloatingPoint()) |
677 |
|
{ |
678 |
175 |
unsigned exp_sz = tn.getFloatingPointExponentSize(); |
679 |
175 |
unsigned sig_sz = tn.getFloatingPointSignificandSize(); |
680 |
175 |
if (!((exp_sz == 8 && sig_sz == 24) || (exp_sz == 11 && sig_sz == 53))) |
681 |
|
{ |
682 |
|
std::stringstream ss; |
683 |
|
ss << "FP term " << node << " with type whose size is " << exp_sz << "/" |
684 |
|
<< sig_sz |
685 |
|
<< " is not supported, only Float32 (8/24) or Float64 (11/53) types " |
686 |
|
"are supported in default mode. Try the experimental solver via " |
687 |
|
"--fp-exp. Note: There are known issues with the experimental " |
688 |
|
"solver, use at your own risk."; |
689 |
|
throw LogicException(ss.str()); |
690 |
|
} |
691 |
|
} |
692 |
|
} |
693 |
5304 |
Trace("fp-preRegisterTerm") |
694 |
2652 |
<< "TheoryFp::preRegisterTerm(): " << node << std::endl; |
695 |
2652 |
registerTerm(node); |
696 |
2652 |
return; |
697 |
|
} |
698 |
|
|
699 |
706 |
void TheoryFp::handleLemma(Node node, InferenceId id) |
700 |
|
{ |
701 |
706 |
Trace("fp") << "TheoryFp::handleLemma(): asserting " << node << std::endl; |
702 |
|
// will be preprocessed when sent, which is important because it contains |
703 |
|
// embedded ITEs |
704 |
706 |
d_im.lemma(node, id); |
705 |
706 |
} |
706 |
|
|
707 |
732 |
bool TheoryFp::propagateLit(TNode node) |
708 |
|
{ |
709 |
732 |
Trace("fp") << "TheoryFp::propagateLit(): propagate " << node << std::endl; |
710 |
732 |
return d_im.propagateLit(node); |
711 |
|
} |
712 |
|
|
713 |
4 |
void TheoryFp::conflictEqConstantMerge(TNode t1, TNode t2) |
714 |
|
{ |
715 |
8 |
Trace("fp") << "TheoryFp::conflictEqConstantMerge(): conflict detected" |
716 |
4 |
<< std::endl; |
717 |
4 |
d_im.conflictEqConstantMerge(t1, t2); |
718 |
4 |
} |
719 |
|
|
720 |
6190 |
bool TheoryFp::needsCheckLastEffort() |
721 |
|
{ |
722 |
|
// only need to check if we have added to the abstraction map, otherwise |
723 |
|
// postCheck below is a no-op. |
724 |
6190 |
return !d_abstractionMap.empty(); |
725 |
|
} |
726 |
|
|
727 |
26857 |
void TheoryFp::postCheck(Effort level) |
728 |
|
{ |
729 |
|
/* Resolve the abstractions for the conversion lemmas */ |
730 |
26857 |
if (level == EFFORT_LAST_CALL) |
731 |
|
{ |
732 |
4 |
Trace("fp") << "TheoryFp::check(): checking abstractions" << std::endl; |
733 |
4 |
TheoryModel* m = getValuation().getModel(); |
734 |
4 |
bool lemmaAdded = false; |
735 |
|
|
736 |
8 |
for (AbstractionMap::const_iterator i = d_abstractionMap.begin(); |
737 |
8 |
i != d_abstractionMap.end(); |
738 |
|
++i) |
739 |
|
{ |
740 |
4 |
if (m->hasTerm((*i).first)) |
741 |
|
{ // Is actually used in the model |
742 |
4 |
lemmaAdded |= refineAbstraction(m, (*i).first, (*i).second); |
743 |
|
} |
744 |
|
} |
745 |
|
} |
746 |
|
|
747 |
26857 |
Trace("fp") << "TheoryFp::check(): completed" << std::endl; |
748 |
|
/* Checking should be handled by the bit-vector engine */ |
749 |
26857 |
} |
750 |
|
|
751 |
5366 |
bool TheoryFp::preNotifyFact( |
752 |
|
TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) |
753 |
|
{ |
754 |
|
/* Word-blast lazier if configured. */ |
755 |
5366 |
if (options::fpLazyWb() && d_wbFactsCache.find(atom) == d_wbFactsCache.end()) |
756 |
|
{ |
757 |
8 |
d_wbFactsCache.insert(atom); |
758 |
8 |
convertAndEquateTerm(atom); |
759 |
|
} |
760 |
|
|
761 |
5366 |
if (atom.getKind() == kind::EQUAL) |
762 |
|
{ |
763 |
4975 |
Assert(!(atom[0].getType().isFloatingPoint() |
764 |
|
|| atom[0].getType().isRoundingMode()) |
765 |
|
|| isRegistered(atom[0])); |
766 |
4975 |
Assert(!(atom[1].getType().isFloatingPoint() |
767 |
|
|| atom[1].getType().isRoundingMode()) |
768 |
|
|| isRegistered(atom[1])); |
769 |
4975 |
registerTerm(atom); // Needed for float equalities |
770 |
|
} |
771 |
|
else |
772 |
|
{ |
773 |
|
// A system-wide invariant; predicates are registered before they are |
774 |
|
// asserted |
775 |
391 |
Assert(isRegistered(atom)); |
776 |
|
|
777 |
391 |
if (!d_equalityEngine->isFunctionKind(atom.getKind())) |
778 |
|
{ |
779 |
|
return true; |
780 |
|
} |
781 |
|
} |
782 |
5366 |
return false; |
783 |
|
} |
784 |
|
|
785 |
817 |
void TheoryFp::notifySharedTerm(TNode n) |
786 |
|
{ |
787 |
|
/* Word-blast lazier if configured. */ |
788 |
817 |
if (options::fpLazyWb() && d_wbFactsCache.find(n) == d_wbFactsCache.end()) |
789 |
|
{ |
790 |
20 |
d_wbFactsCache.insert(n); |
791 |
20 |
convertAndEquateTerm(n); |
792 |
|
} |
793 |
817 |
} |
794 |
|
|
795 |
2 |
TrustNode TheoryFp::explain(TNode n) |
796 |
|
{ |
797 |
2 |
Trace("fp") << "TheoryFp::explain(): explain " << n << std::endl; |
798 |
|
|
799 |
|
// All things we assert directly (and not via bit-vector) should |
800 |
|
// come from the equality engine so this should be sufficient... |
801 |
4 |
std::vector<TNode> assumptions; |
802 |
|
|
803 |
2 |
bool polarity = n.getKind() != kind::NOT; |
804 |
4 |
TNode atom = polarity ? n : n[0]; |
805 |
2 |
if (atom.getKind() == kind::EQUAL) { |
806 |
2 |
d_equalityEngine->explainEquality(atom[0], atom[1], polarity, assumptions); |
807 |
|
} else { |
808 |
|
d_equalityEngine->explainPredicate(atom, polarity, assumptions); |
809 |
|
} |
810 |
|
|
811 |
4 |
Node exp = helper::buildConjunct(assumptions); |
812 |
4 |
return TrustNode::mkTrustPropExp(n, exp, nullptr); |
813 |
|
} |
814 |
|
|
815 |
|
Node TheoryFp::getModelValue(TNode var) { |
816 |
|
return d_conv->getValue(d_valuation, var); |
817 |
|
} |
818 |
|
|
819 |
5168 |
bool TheoryFp::collectModelInfo(TheoryModel* m, |
820 |
|
const std::set<Node>& relevantTerms) |
821 |
|
{ |
822 |
|
// this override behavior to not assert equality engine |
823 |
5168 |
return collectModelValues(m, relevantTerms); |
824 |
|
} |
825 |
|
|
826 |
5168 |
bool TheoryFp::collectModelValues(TheoryModel* m, |
827 |
|
const std::set<Node>& relevantTerms) |
828 |
|
{ |
829 |
10336 |
Trace("fp-collectModelInfo") |
830 |
5168 |
<< "TheoryFp::collectModelInfo(): begin" << std::endl; |
831 |
5168 |
if (Trace.isOn("fp-collectModelInfo")) { |
832 |
|
for (std::set<Node>::const_iterator i(relevantTerms.begin()); |
833 |
|
i != relevantTerms.end(); ++i) { |
834 |
|
Trace("fp-collectModelInfo") |
835 |
|
<< "TheoryFp::collectModelInfo(): relevantTerms " << *i << std::endl; |
836 |
|
} |
837 |
|
} |
838 |
|
|
839 |
10336 |
std::unordered_set<TNode> visited; |
840 |
10336 |
std::stack<TNode> working; |
841 |
10336 |
std::set<TNode> relevantVariables; |
842 |
11812 |
for (std::set<Node>::const_iterator i(relevantTerms.begin()); |
843 |
11812 |
i != relevantTerms.end(); ++i) { |
844 |
6644 |
working.push(*i); |
845 |
|
} |
846 |
|
|
847 |
26942 |
while (!working.empty()) { |
848 |
21774 |
TNode current = working.top(); |
849 |
10887 |
working.pop(); |
850 |
|
|
851 |
|
// Ignore things that have already been explored |
852 |
10887 |
if (visited.find(current) == visited.end()) { |
853 |
6766 |
visited.insert(current); |
854 |
|
|
855 |
13532 |
TypeNode t(current.getType()); |
856 |
|
|
857 |
20426 |
if ((t.isRoundingMode() || t.isFloatingPoint()) && |
858 |
13660 |
this->isLeaf(current)) { |
859 |
3126 |
relevantVariables.insert(current); |
860 |
|
} |
861 |
|
|
862 |
11009 |
for (size_t i = 0; i < current.getNumChildren(); ++i) { |
863 |
4243 |
working.push(current[i]); |
864 |
|
} |
865 |
|
} |
866 |
|
} |
867 |
|
|
868 |
8294 |
for (std::set<TNode>::const_iterator i(relevantVariables.begin()); |
869 |
8294 |
i != relevantVariables.end(); |
870 |
|
++i) |
871 |
|
{ |
872 |
6252 |
TNode node = *i; |
873 |
|
|
874 |
6252 |
Trace("fp-collectModelInfo") |
875 |
3126 |
<< "TheoryFp::collectModelInfo(): relevantVariable " << node |
876 |
3126 |
<< std::endl; |
877 |
|
|
878 |
6252 |
Node converted = d_conv->getValue(d_valuation, node); |
879 |
|
// We only assign the value if the FpConverter actually has one, that is, |
880 |
|
// if FpConverter::getValue() does not return a null node. |
881 |
3126 |
if (!converted.isNull() && !m->assertEquality(node, converted, true)) |
882 |
|
{ |
883 |
|
return false; |
884 |
|
} |
885 |
|
|
886 |
9378 |
if (Configuration::isAssertionBuild() && isLeaf(node) && !node.isConst() |
887 |
6737 |
&& node.getType().isFloatingPoint()) |
888 |
|
{ |
889 |
|
// Check that the equality engine has asssigned values to all the |
890 |
|
// components of `node` except `(sign node)` (the sign component is |
891 |
|
// assignable, meaning that the model builder can pick an arbitrary value |
892 |
|
// for it if it hasn't been assigned in the equality engine). |
893 |
478 |
NodeManager* nm = NodeManager::currentNM(); |
894 |
956 |
Node compNaN = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_NAN, node); |
895 |
956 |
Node compInf = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_INF, node); |
896 |
956 |
Node compZero = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_ZERO, node); |
897 |
|
Node compExponent = |
898 |
956 |
nm->mkNode(kind::FLOATINGPOINT_COMPONENT_EXPONENT, node); |
899 |
|
Node compSignificand = |
900 |
956 |
nm->mkNode(kind::FLOATINGPOINT_COMPONENT_SIGNIFICAND, node); |
901 |
|
|
902 |
478 |
eq::EqualityEngine* ee = m->getEqualityEngine(); |
903 |
478 |
Assert(ee->hasTerm(compNaN) && ee->getRepresentative(compNaN).isConst()); |
904 |
478 |
Assert(ee->hasTerm(compInf) && ee->getRepresentative(compInf).isConst()); |
905 |
478 |
Assert(ee->hasTerm(compZero) |
906 |
|
&& ee->getRepresentative(compZero).isConst()); |
907 |
478 |
Assert(ee->hasTerm(compExponent) |
908 |
|
&& ee->getRepresentative(compExponent).isConst()); |
909 |
478 |
Assert(ee->hasTerm(compSignificand)); |
910 |
478 |
Assert(ee->getRepresentative(compSignificand).isConst()); |
911 |
|
|
912 |
|
// At most one of the flags (NaN, inf, zero) can be set |
913 |
956 |
Node one = nm->mkConst(BitVector(1U, 1U)); |
914 |
478 |
size_t numFlags = 0; |
915 |
478 |
numFlags += ee->getRepresentative(compNaN) == one ? 1 : 0; |
916 |
478 |
numFlags += ee->getRepresentative(compInf) == one ? 1 : 0; |
917 |
478 |
numFlags += ee->getRepresentative(compZero) == one ? 1 : 0; |
918 |
478 |
Assert(numFlags <= 1); |
919 |
|
} |
920 |
|
} |
921 |
|
|
922 |
5168 |
return true; |
923 |
|
} |
924 |
|
|
925 |
519 |
bool TheoryFp::NotifyClass::eqNotifyTriggerPredicate(TNode predicate, |
926 |
|
bool value) { |
927 |
1038 |
Debug("fp-eq") |
928 |
519 |
<< "TheoryFp::eqNotifyTriggerPredicate(): call back as predicate " |
929 |
519 |
<< predicate << " is " << value << std::endl; |
930 |
|
|
931 |
519 |
if (value) { |
932 |
356 |
return d_theorySolver.propagateLit(predicate); |
933 |
|
} |
934 |
163 |
return d_theorySolver.propagateLit(predicate.notNode()); |
935 |
|
} |
936 |
|
|
937 |
213 |
bool TheoryFp::NotifyClass::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, |
938 |
|
TNode t2, bool value) { |
939 |
426 |
Debug("fp-eq") << "TheoryFp::eqNotifyTriggerTermEquality(): call back as " |
940 |
213 |
<< t1 << (value ? " = " : " != ") << t2 << std::endl; |
941 |
|
|
942 |
213 |
if (value) { |
943 |
197 |
return d_theorySolver.propagateLit(t1.eqNode(t2)); |
944 |
|
} |
945 |
16 |
return d_theorySolver.propagateLit(t1.eqNode(t2).notNode()); |
946 |
|
} |
947 |
|
|
948 |
4 |
void TheoryFp::NotifyClass::eqNotifyConstantTermMerge(TNode t1, TNode t2) { |
949 |
8 |
Debug("fp-eq") << "TheoryFp::eqNotifyConstantTermMerge(): call back as " << t1 |
950 |
4 |
<< " = " << t2 << std::endl; |
951 |
4 |
d_theorySolver.conflictEqConstantMerge(t1, t2); |
952 |
4 |
} |
953 |
|
|
954 |
|
} // namespace fp |
955 |
|
} // namespace theory |
956 |
29505 |
} // namespace cvc5 |