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