GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/fp/theory_fp.cpp Lines: 343 475 72.2 %
Date: 2021-09-18 Branches: 727 2612 27.8 %

Line Exec Source
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
2
Node buildConjunct(const std::vector<TNode> &assumptions) {
41
2
  if (assumptions.size() == 0) {
42
    return NodeManager::currentNM()->mkConst<bool>(true);
43
44
2
  } else if (assumptions.size() == 1) {
45
    return assumptions[0];
46
47
  } else {
48
    // \todo see bv::utils::flattenAnd
49
50
4
    NodeBuilder conjunction(kind::AND);
51
6
    for (std::vector<TNode>::const_iterator it = assumptions.begin();
52
6
         it != assumptions.end(); ++it) {
53
4
      conjunction << *it;
54
    }
55
56
2
    return conjunction;
57
  }
58
}
59
}  // namespace helper
60
61
/** Constructs a new instance of TheoryFp w.r.t. the provided contexts. */
62
9940
TheoryFp::TheoryFp(Env& env, OutputChannel& out, Valuation valuation)
63
    : Theory(THEORY_FP, env, out, valuation),
64
      d_notification(*this),
65
9940
      d_registeredTerms(userContext()),
66
9940
      d_wordBlaster(new FpWordBlaster(userContext())),
67
      d_expansionRequested(false),
68
9940
      d_realToFloatMap(userContext()),
69
9940
      d_floatToRealMap(userContext()),
70
9940
      d_abstractionMap(userContext()),
71
      d_rewriter(userContext()),
72
      d_state(env, valuation),
73
      d_im(env, *this, d_state, d_pnm, "theory::fp::", true),
74
9940
      d_wbFactsCache(userContext()),
75
69580
      d_true(d_env.getNodeManager()->mkConst(true))
76
{
77
  // indicate we are using the default theory state and inference manager
78
9940
  d_theoryState = &d_state;
79
9940
  d_inferManager = &d_im;
80
9940
}
81
82
9940
TheoryRewriter* TheoryFp::getTheoryRewriter() { return &d_rewriter; }
83
84
3794
ProofRuleChecker* TheoryFp::getProofChecker() { return nullptr; }
85
86
9940
bool TheoryFp::needsEqualityEngine(EeSetupInfo& esi)
87
{
88
9940
  esi.d_notify = &d_notification;
89
9940
  esi.d_name = "theory::fp::ee";
90
9940
  return true;
91
}
92
93
9940
void TheoryFp::finishInit()
94
{
95
9940
  Assert(d_equalityEngine != nullptr);
96
  // Kinds that are to be handled in the congruence closure
97
98
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ABS);
99
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_NEG);
100
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ADD);
101
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SUB); // Removed
102
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MULT);
103
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_DIV);
104
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_FMA);
105
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SQRT);
106
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_REM);
107
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_RTI);
108
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN); // Removed
109
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX); // Removed
110
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN_TOTAL);
111
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX_TOTAL);
112
113
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_EQ); // Removed
114
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LEQ);
115
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LT);
116
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GEQ); // Removed
117
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GT); // Removed
118
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISN);
119
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISSN);
120
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISZ);
121
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISINF);
122
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNAN);
123
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNEG);
124
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISPOS);
125
126
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_IEEE_BITVECTOR);
127
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_FLOATINGPOINT);
128
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_REAL);
129
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR);
130
9940
  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
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_UBV_TOTAL);
139
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_SBV_TOTAL);
140
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_REAL_TOTAL);
141
142
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_NAN);
143
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_INF);
144
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_ZERO);
145
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGN);
146
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_EXPONENT);
147
9940
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGNIFICAND);
148
9940
  d_equalityEngine->addFunctionKind(kind::ROUNDINGMODE_BITBLAST);
149
9940
}
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
6
Node TheoryFp::abstractFloatToReal(Node node)
185
{
186
6
  Assert(node.getKind() == kind::FLOATINGPOINT_TO_REAL_TOTAL);
187
12
  TypeNode t(node[0].getType());
188
6
  Assert(t.getKind() == kind::FLOATINGPOINT_TYPE);
189
190
6
  NodeManager *nm = NodeManager::currentNM();
191
6
  SkolemManager* sm = nm->getSkolemManager();
192
6
  ConversionAbstractionMap::const_iterator i(d_floatToRealMap.find(t));
193
194
12
  Node fun;
195
6
  if (i == d_floatToRealMap.end())
196
  {
197
12
    std::vector<TypeNode> args(2);
198
6
    args[0] = t;
199
6
    args[1] = nm->realType();
200
18
    fun = sm->mkDummySkolem("floatingpoint_abstract_float_to_real",
201
12
                            nm->mkFunctionType(args, nm->realType()),
202
                            "floatingpoint_abstract_float_to_real",
203
                            NodeManager::SKOLEM_EXACT_NAME);
204
6
    d_floatToRealMap.insert(t, fun);
205
  }
206
  else
207
  {
208
    fun = (*i).second;
209
  }
210
6
  Node uf = nm->mkNode(kind::APPLY_UF, fun, node[0], node[1]);
211
212
6
  d_abstractionMap.insert(uf, node);
213
214
12
  return uf;
215
}
216
217
2726
TrustNode TheoryFp::ppRewrite(TNode node, std::vector<SkolemLemma>& lems)
218
{
219
2726
  Trace("fp-ppRewrite") << "TheoryFp::ppRewrite(): " << node << std::endl;
220
  // first, see if we need to expand definitions
221
5452
  TrustNode texp = d_rewriter.expandDefinition(node);
222
2726
  if (!texp.isNull())
223
  {
224
10
    return texp;
225
  }
226
227
5432
  Node res = node;
228
229
  // Abstract conversion functions
230
2716
  if (node.getKind() == kind::FLOATINGPOINT_TO_REAL_TOTAL)
231
  {
232
6
    res = abstractFloatToReal(node);
233
234
    // Generate some lemmas
235
6
    NodeManager *nm = NodeManager::currentNM();
236
237
    Node pd =
238
        nm->mkNode(kind::IMPLIES,
239
24
                   nm->mkNode(kind::OR,
240
12
                              nm->mkNode(kind::FLOATINGPOINT_ISNAN, node[0]),
241
12
                              nm->mkNode(kind::FLOATINGPOINT_ISINF, node[0])),
242
24
                   nm->mkNode(kind::EQUAL, res, node[1]));
243
6
    handleLemma(pd, InferenceId::FP_PREPROCESS);
244
245
    Node z =
246
        nm->mkNode(kind::IMPLIES,
247
12
                   nm->mkNode(kind::FLOATINGPOINT_ISZ, node[0]),
248
24
                   nm->mkNode(kind::EQUAL, res, nm->mkConst(Rational(0U))));
249
6
    handleLemma(z, InferenceId::FP_PREPROCESS);
250
251
    // TODO : bounds on the output from largest floats, #1914
252
  }
253
2710
  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
2716
  if (res != node)
278
  {
279
12
    Trace("fp-ppRewrite") << "TheoryFp::ppRewrite(): node " << node
280
6
                          << " rewritten to " << res << std::endl;
281
6
    return TrustNode::mkTrustRewrite(node, res, nullptr);
282
  }
283
284
2710
  return TrustNode::null();
285
}
286
287
6
bool TheoryFp::refineAbstraction(TheoryModel *m, TNode abstract, TNode concrete)
288
{
289
12
  Trace("fp-refineAbstraction") << "TheoryFp::refineAbstraction(): " << abstract
290
6
                                << " vs. " << concrete << std::endl;
291
6
  Kind k = concrete.getKind();
292
6
  if (k == kind::FLOATINGPOINT_TO_REAL_TOTAL)
293
  {
294
    // Get the values
295
6
    Assert(m->hasTerm(abstract));
296
6
    Assert(m->hasTerm(concrete[0]));
297
6
    Assert(m->hasTerm(concrete[1]));
298
299
12
    Node abstractValue = m->getValue(abstract);
300
12
    Node floatValue = m->getValue(concrete[0]);
301
12
    Node undefValue = m->getValue(concrete[1]);
302
303
6
    Assert(!abstractValue.isNull());
304
6
    Assert(!floatValue.isNull());
305
6
    Assert(!undefValue.isNull());
306
6
    Assert(abstractValue.isConst());
307
6
    Assert(floatValue.isConst());
308
6
    Assert(undefValue.isConst());
309
310
    // Work out the actual value for those args
311
6
    NodeManager *nm = NodeManager::currentNM();
312
313
    Node evaluate =
314
12
        nm->mkNode(kind::FLOATINGPOINT_TO_REAL_TOTAL, floatValue, undefValue);
315
12
    Node concreteValue = rewrite(evaluate);
316
6
    Assert(concreteValue.isConst());
317
318
12
    Trace("fp-refineAbstraction")
319
12
        << "TheoryFp::refineAbstraction(): " << concrete[0] << " = "
320
6
        << floatValue << std::endl
321
12
        << "TheoryFp::refineAbstraction(): " << concrete[1] << " = "
322
6
        << undefValue << std::endl
323
6
        << "TheoryFp::refineAbstraction(): " << abstract << " = "
324
6
        << abstractValue << std::endl
325
6
        << "TheoryFp::refineAbstraction(): " << concrete << " = "
326
6
        << concreteValue << std::endl;
327
328
6
    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 = 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 = 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
6
      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 = 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
            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
2094
void TheoryFp::wordBlastAndEquateTerm(TNode node)
515
{
516
4188
  Trace("fp-wordBlastTerm")
517
2094
      << "TheoryFp::wordBlastTerm(): " << node << std::endl;
518
519
2094
  size_t oldSize = d_wordBlaster->d_additionalAssertions.size();
520
521
4188
  Node wordBlasted(d_wordBlaster->wordBlast(node));
522
523
2094
  size_t newSize = d_wordBlaster->d_additionalAssertions.size();
524
525
2094
  if (wordBlasted != node)
526
  {
527
1004
    Debug("fp-wordBlastTerm")
528
502
        << "TheoryFp::wordBlastTerm(): before " << node << std::endl;
529
1004
    Debug("fp-wordBlastTerm")
530
502
        << "TheoryFp::wordBlastTerm(): after  " << wordBlasted << std::endl;
531
  }
532
533
2094
  Assert(oldSize <= newSize);
534
535
2398
  while (oldSize < newSize)
536
  {
537
304
    Node addA = d_wordBlaster->d_additionalAssertions[oldSize];
538
539
304
    Debug("fp-wordBlastTerm")
540
152
        << "TheoryFp::wordBlastTerm(): additional assertion  " << addA
541
152
        << std::endl;
542
543
152
    NodeManager* nm = NodeManager::currentNM();
544
545
152
    handleLemma(
546
304
        nm->mkNode(kind::EQUAL, addA, nm->mkConst(::cvc5::BitVector(1U, 1U))),
547
        InferenceId::FP_EQUATE_TERM);
548
549
152
    ++oldSize;
550
  }
551
552
  // Equate the floating-point atom and the wordBlasted one.
553
  // Also adds the bit-vectors to the bit-vector solver.
554
2094
  if (node.getType().isBoolean())
555
  {
556
689
    if (wordBlasted != node)
557
    {
558
498
      Assert(wordBlasted.getType().isBitVector());
559
560
498
      NodeManager* nm = NodeManager::currentNM();
561
562
498
      handleLemma(
563
996
          nm->mkNode(kind::EQUAL,
564
                     node,
565
996
                     nm->mkNode(kind::EQUAL,
566
                                wordBlasted,
567
996
                                nm->mkConst(::cvc5::BitVector(1U, 1U)))),
568
          InferenceId::FP_EQUATE_TERM);
569
    }
570
    else
571
    {
572
191
      Assert((node.getKind() == kind::EQUAL));
573
    }
574
  }
575
1405
  else if (node.getType().isBitVector())
576
  {
577
845
    if (wordBlasted != node)
578
    {
579
4
      Assert(wordBlasted.getType().isBitVector());
580
581
4
      handleLemma(
582
8
          NodeManager::currentNM()->mkNode(kind::EQUAL, node, wordBlasted),
583
          InferenceId::FP_EQUATE_TERM);
584
    }
585
  }
586
587
4188
  return;
588
}
589
590
7866
void TheoryFp::registerTerm(TNode node)
591
{
592
7866
  Trace("fp-registerTerm") << "TheoryFp::registerTerm(): " << node << std::endl;
593
594
7866
  if (!isRegistered(node))
595
  {
596
2106
    Kind k = node.getKind();
597
2106
    Assert(k != kind::FLOATINGPOINT_TO_FP_GENERIC
598
           && k != kind::FLOATINGPOINT_SUB && k != kind::FLOATINGPOINT_EQ
599
           && k != kind::FLOATINGPOINT_GEQ && k != kind::FLOATINGPOINT_GT);
600
601
2106
    bool success = d_registeredTerms.insert(node);
602
    (void)success;  // Only used for assertion
603
2106
    Assert(success);
604
605
    // Add to the equality engine
606
2106
    if (k == kind::EQUAL)
607
    {
608
548
      d_equalityEngine->addTriggerPredicate(node);
609
    }
610
    else
611
    {
612
1558
      d_equalityEngine->addTerm(node);
613
    }
614
615
    // Give the expansion of classifications in terms of equalities
616
    // This should make equality reasoning slightly more powerful.
617
2106
    if ((k == kind::FLOATINGPOINT_ISNAN) || (k == kind::FLOATINGPOINT_ISZ)
618
2075
        || (k == kind::FLOATINGPOINT_ISINF))
619
    {
620
61
      NodeManager *nm = NodeManager::currentNM();
621
61
      FloatingPointSize s = node[0].getType().getConst<FloatingPointSize>();
622
122
      Node equalityAlias = Node::null();
623
624
61
      if (k == kind::FLOATINGPOINT_ISNAN)
625
      {
626
24
        equalityAlias = nm->mkNode(
627
48
            kind::EQUAL, node[0], nm->mkConst(FloatingPoint::makeNaN(s)));
628
      }
629
37
      else if (k == kind::FLOATINGPOINT_ISZ)
630
      {
631
21
        equalityAlias = nm->mkNode(
632
            kind::OR,
633
14
            nm->mkNode(kind::EQUAL,
634
                       node[0],
635
14
                       nm->mkConst(FloatingPoint::makeZero(s, true))),
636
14
            nm->mkNode(kind::EQUAL,
637
                       node[0],
638
14
                       nm->mkConst(FloatingPoint::makeZero(s, false))));
639
      }
640
30
      else if (k == kind::FLOATINGPOINT_ISINF)
641
      {
642
90
        equalityAlias = nm->mkNode(
643
            kind::OR,
644
60
            nm->mkNode(kind::EQUAL,
645
                       node[0],
646
60
                       nm->mkConst(FloatingPoint::makeInf(s, true))),
647
60
            nm->mkNode(kind::EQUAL,
648
                       node[0],
649
60
                       nm->mkConst(FloatingPoint::makeInf(s, false))));
650
      }
651
      else
652
      {
653
        Unreachable() << "Only isNaN, isInf and isZero have aliases";
654
      }
655
656
61
      handleLemma(nm->mkNode(kind::EQUAL, node, equalityAlias),
657
                  InferenceId::FP_REGISTER_TERM);
658
    }
659
660
    /* When not word-blasting lazier, we word-blast every term on
661
     * registration. */
662
2106
    if (!options().fp.fpLazyWb)
663
    {
664
2066
      wordBlastAndEquateTerm(node);
665
    }
666
  }
667
7866
  return;
668
}
669
670
17950
bool TheoryFp::isRegistered(TNode node)
671
{
672
17950
  return d_registeredTerms.find(node) != d_registeredTerms.end();
673
}
674
675
2703
void TheoryFp::preRegisterTerm(TNode node)
676
{
677
2703
  if (!options().fp.fpExp)
678
  {
679
1214
    TypeNode tn = node.getType();
680
607
    if (tn.isFloatingPoint())
681
    {
682
177
      unsigned exp_sz = tn.getFloatingPointExponentSize();
683
177
      unsigned sig_sz = tn.getFloatingPointSignificandSize();
684
177
      if (!((exp_sz == 8 && sig_sz == 24) || (exp_sz == 11 && sig_sz == 53)))
685
      {
686
        std::stringstream ss;
687
        ss << "FP term " << node << " with type whose size is " << exp_sz << "/"
688
           << sig_sz
689
           << " is not supported, only Float32 (8/24) or Float64 (11/53) types "
690
              "are supported in default mode. Try the experimental solver via "
691
              "--fp-exp. Note: There are known issues with the experimental "
692
              "solver, use at your own risk.";
693
        throw LogicException(ss.str());
694
      }
695
    }
696
  }
697
5406
  Trace("fp-preRegisterTerm")
698
2703
      << "TheoryFp::preRegisterTerm(): " << node << std::endl;
699
2703
  registerTerm(node);
700
2703
  return;
701
}
702
703
727
void TheoryFp::handleLemma(Node node, InferenceId id)
704
{
705
727
  Trace("fp") << "TheoryFp::handleLemma(): asserting " << node << std::endl;
706
  // will be preprocessed when sent, which is important because it contains
707
  // embedded ITEs
708
727
  if (rewrite(node) != d_true)
709
  {
710
    /* We only send non-trivial lemmas. */
711
725
    d_im.lemma(node, id);
712
  }
713
727
}
714
715
910
bool TheoryFp::propagateLit(TNode node)
716
{
717
910
  Trace("fp") << "TheoryFp::propagateLit(): propagate " << node << std::endl;
718
910
  return d_im.propagateLit(node);
719
}
720
721
4
void TheoryFp::conflictEqConstantMerge(TNode t1, TNode t2)
722
{
723
8
  Trace("fp") << "TheoryFp::conflictEqConstantMerge(): conflict detected"
724
4
              << std::endl;
725
4
  d_im.conflictEqConstantMerge(t1, t2);
726
4
}
727
728
5952
bool TheoryFp::needsCheckLastEffort()
729
{
730
  // only need to check if we have added to the abstraction map, otherwise
731
  // postCheck below is a no-op.
732
5952
  return !d_abstractionMap.empty();
733
}
734
735
26483
void TheoryFp::postCheck(Effort level)
736
{
737
  /* Resolve the abstractions for the conversion lemmas */
738
26483
  if (level == EFFORT_LAST_CALL)
739
  {
740
6
    Trace("fp") << "TheoryFp::check(): checking abstractions" << std::endl;
741
6
    TheoryModel* m = getValuation().getModel();
742
6
    bool lemmaAdded = false;
743
744
12
    for (AbstractionMap::const_iterator i = d_abstractionMap.begin();
745
12
         i != d_abstractionMap.end();
746
         ++i)
747
    {
748
6
      if (m->hasTerm((*i).first))
749
      {  // Is actually used in the model
750
6
        lemmaAdded |= refineAbstraction(m, (*i).first, (*i).second);
751
      }
752
    }
753
  }
754
755
26483
  Trace("fp") << "TheoryFp::check(): completed" << std::endl;
756
  /* Checking should be handled by the bit-vector engine */
757
26483
}
758
759
5521
bool TheoryFp::preNotifyFact(
760
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
761
{
762
  /* Word-blast lazier if configured. */
763
11042
  if (options().fp.fpLazyWb
764
11042
      && d_wbFactsCache.find(atom) == d_wbFactsCache.end())
765
  {
766
8
    d_wbFactsCache.insert(atom);
767
8
    wordBlastAndEquateTerm(atom);
768
  }
769
770
5521
  if (atom.getKind() == kind::EQUAL)
771
  {
772
5163
    Assert(!(atom[0].getType().isFloatingPoint()
773
             || atom[0].getType().isRoundingMode())
774
           || isRegistered(atom[0]));
775
5163
    Assert(!(atom[1].getType().isFloatingPoint()
776
             || atom[1].getType().isRoundingMode())
777
           || isRegistered(atom[1]));
778
5163
    registerTerm(atom);  // Needed for float equalities
779
  }
780
  else
781
  {
782
    // A system-wide invariant; predicates are registered before they are
783
    // asserted
784
358
    Assert(isRegistered(atom));
785
786
358
    if (!d_equalityEngine->isFunctionKind(atom.getKind()))
787
    {
788
      return true;
789
    }
790
  }
791
5521
  return false;
792
}
793
794
835
void TheoryFp::notifySharedTerm(TNode n)
795
{
796
  /* Word-blast lazier if configured. */
797
835
  if (options().fp.fpLazyWb && d_wbFactsCache.find(n) == d_wbFactsCache.end())
798
  {
799
20
    d_wbFactsCache.insert(n);
800
20
    wordBlastAndEquateTerm(n);
801
  }
802
835
}
803
804
2
TrustNode TheoryFp::explain(TNode n)
805
{
806
2
  Trace("fp") << "TheoryFp::explain(): explain " << n << std::endl;
807
808
  // All things we assert directly (and not via bit-vector) should
809
  // come from the equality engine so this should be sufficient...
810
4
  std::vector<TNode> assumptions;
811
812
2
  bool polarity = n.getKind() != kind::NOT;
813
4
  TNode atom = polarity ? n : n[0];
814
2
  if (atom.getKind() == kind::EQUAL) {
815
2
    d_equalityEngine->explainEquality(atom[0], atom[1], polarity, assumptions);
816
  } else {
817
    d_equalityEngine->explainPredicate(atom, polarity, assumptions);
818
  }
819
820
4
  Node exp = helper::buildConjunct(assumptions);
821
4
  return TrustNode::mkTrustPropExp(n, exp, nullptr);
822
}
823
824
Node TheoryFp::getModelValue(TNode var) {
825
  return d_wordBlaster->getValue(d_valuation, var);
826
}
827
828
4922
bool TheoryFp::collectModelInfo(TheoryModel* m,
829
                                const std::set<Node>& relevantTerms)
830
{
831
  // this override behavior to not assert equality engine
832
4922
  return collectModelValues(m, relevantTerms);
833
}
834
835
4922
bool TheoryFp::collectModelValues(TheoryModel* m,
836
                                  const std::set<Node>& relevantTerms)
837
{
838
9844
  Trace("fp-collectModelInfo")
839
4922
      << "TheoryFp::collectModelInfo(): begin" << std::endl;
840
4922
  if (Trace.isOn("fp-collectModelInfo")) {
841
    for (std::set<Node>::const_iterator i(relevantTerms.begin());
842
         i != relevantTerms.end(); ++i) {
843
      Trace("fp-collectModelInfo")
844
          << "TheoryFp::collectModelInfo(): relevantTerms " << *i << std::endl;
845
    }
846
  }
847
848
9844
  std::unordered_set<TNode> visited;
849
9844
  std::stack<TNode> working;
850
9844
  std::set<TNode> relevantVariables;
851
12688
  for (std::set<Node>::const_iterator i(relevantTerms.begin());
852
12688
       i != relevantTerms.end(); ++i) {
853
7766
    working.push(*i);
854
  }
855
856
31516
  while (!working.empty()) {
857
26594
    TNode current = working.top();
858
13297
    working.pop();
859
860
    // Ignore things that have already been explored
861
13297
    if (visited.find(current) == visited.end()) {
862
7974
      visited.insert(current);
863
864
15948
      TypeNode t(current.getType());
865
866
23184
      if ((t.isRoundingMode() || t.isFloatingPoint()) &&
867
15210
          this->isLeaf(current)) {
868
3297
        relevantVariables.insert(current);
869
      }
870
871
13505
      for (size_t i = 0; i < current.getNumChildren(); ++i) {
872
5531
        working.push(current[i]);
873
      }
874
    }
875
  }
876
877
8219
  for (std::set<TNode>::const_iterator i(relevantVariables.begin());
878
8219
       i != relevantVariables.end();
879
       ++i)
880
  {
881
6594
    TNode node = *i;
882
883
6594
    Trace("fp-collectModelInfo")
884
3297
        << "TheoryFp::collectModelInfo(): relevantVariable " << node
885
3297
        << std::endl;
886
887
6594
    Node wordBlasted = d_wordBlaster->getValue(d_valuation, node);
888
    // We only assign the value if the FpWordBlaster actually has one, that is,
889
    // if FpWordBlaster::getValue() does not return a null node.
890
3297
    if (!wordBlasted.isNull() && !m->assertEquality(node, wordBlasted, true))
891
    {
892
      return false;
893
    }
894
895
9891
    if (Configuration::isAssertionBuild() && isLeaf(node) && !node.isConst()
896
7210
        && node.getType().isFloatingPoint())
897
    {
898
      // Check that the equality engine has asssigned values to all the
899
      // components of `node` except `(sign node)` (the sign component is
900
      // assignable, meaning that the model builder can pick an arbitrary value
901
      // for it if it hasn't been assigned in the equality engine).
902
609
      NodeManager* nm = NodeManager::currentNM();
903
1218
      Node compNaN = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_NAN, node);
904
1218
      Node compInf = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_INF, node);
905
1218
      Node compZero = nm->mkNode(kind::FLOATINGPOINT_COMPONENT_ZERO, node);
906
      Node compExponent =
907
1218
          nm->mkNode(kind::FLOATINGPOINT_COMPONENT_EXPONENT, node);
908
      Node compSignificand =
909
1218
          nm->mkNode(kind::FLOATINGPOINT_COMPONENT_SIGNIFICAND, node);
910
911
609
      eq::EqualityEngine* ee = m->getEqualityEngine();
912
609
      Assert(ee->hasTerm(compNaN) && ee->getRepresentative(compNaN).isConst());
913
609
      Assert(ee->hasTerm(compInf) && ee->getRepresentative(compInf).isConst());
914
609
      Assert(ee->hasTerm(compZero)
915
             && ee->getRepresentative(compZero).isConst());
916
609
      Assert(ee->hasTerm(compExponent)
917
             && ee->getRepresentative(compExponent).isConst());
918
609
      Assert(ee->hasTerm(compSignificand));
919
609
      Assert(ee->getRepresentative(compSignificand).isConst());
920
921
      // At most one of the flags (NaN, inf, zero) can be set
922
1218
      Node one = nm->mkConst(BitVector(1U, 1U));
923
609
      size_t numFlags = 0;
924
609
      numFlags += ee->getRepresentative(compNaN) == one ? 1 : 0;
925
609
      numFlags += ee->getRepresentative(compInf) == one ? 1 : 0;
926
609
      numFlags += ee->getRepresentative(compZero) == one ? 1 : 0;
927
609
      Assert(numFlags <= 1);
928
    }
929
  }
930
931
4922
  return true;
932
}
933
934
543
bool TheoryFp::NotifyClass::eqNotifyTriggerPredicate(TNode predicate,
935
                                                     bool value) {
936
1086
  Debug("fp-eq")
937
543
      << "TheoryFp::eqNotifyTriggerPredicate(): call back as predicate "
938
543
      << predicate << " is " << value << std::endl;
939
940
543
  if (value) {
941
376
    return d_theorySolver.propagateLit(predicate);
942
  }
943
167
  return d_theorySolver.propagateLit(predicate.notNode());
944
}
945
946
367
bool TheoryFp::NotifyClass::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1,
947
                                                        TNode t2, bool value) {
948
734
  Debug("fp-eq") << "TheoryFp::eqNotifyTriggerTermEquality(): call back as "
949
367
                 << t1 << (value ? " = " : " != ") << t2 << std::endl;
950
951
367
  if (value) {
952
331
    return d_theorySolver.propagateLit(t1.eqNode(t2));
953
  }
954
36
  return d_theorySolver.propagateLit(t1.eqNode(t2).notNode());
955
}
956
957
4
void TheoryFp::NotifyClass::eqNotifyConstantTermMerge(TNode t1, TNode t2) {
958
8
  Debug("fp-eq") << "TheoryFp::eqNotifyConstantTermMerge(): call back as " << t1
959
4
                 << " = " << t2 << std::endl;
960
4
  d_theorySolver.conflictEqConstantMerge(t1, t2);
961
4
}
962
963
}  // namespace fp
964
}  // namespace theory
965
29574
}  // namespace cvc5