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