GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/fp/theory_fp.cpp Lines: 335 467 71.7 %
Date: 2021-08-01 Branches: 717 2600 27.6 %

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_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
9838
TheoryFp::TheoryFp(context::Context* c,
64
                   context::UserContext* u,
65
                   OutputChannel& out,
66
                   Valuation valuation,
67
                   const LogicInfo& logicInfo,
68
9838
                   ProofNodeManager* pnm)
69
    : Theory(THEORY_FP, c, u, out, valuation, logicInfo, pnm),
70
      d_notification(*this),
71
      d_registeredTerms(u),
72
9838
      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
19676
      d_wbFactsCache(u)
81
{
82
  // indicate we are using the default theory state and inference manager
83
9838
  d_theoryState = &d_state;
84
9838
  d_inferManager = &d_im;
85
9838
}
86
87
9838
TheoryRewriter* TheoryFp::getTheoryRewriter() { return &d_rewriter; }
88
89
3756
ProofRuleChecker* TheoryFp::getProofChecker() { return nullptr; }
90
91
9838
bool TheoryFp::needsEqualityEngine(EeSetupInfo& esi)
92
{
93
9838
  esi.d_notify = &d_notification;
94
9838
  esi.d_name = "theory::fp::ee";
95
9838
  return true;
96
}
97
98
9838
void TheoryFp::finishInit()
99
{
100
9838
  Assert(d_equalityEngine != nullptr);
101
  // Kinds that are to be handled in the congruence closure
102
103
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ABS);
104
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_NEG);
105
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ADD);
106
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SUB); // Removed
107
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MULT);
108
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_DIV);
109
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_FMA);
110
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_SQRT);
111
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_REM);
112
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_RTI);
113
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN); // Removed
114
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX); // Removed
115
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MIN_TOTAL);
116
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_MAX_TOTAL);
117
118
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_EQ); // Removed
119
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LEQ);
120
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_LT);
121
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GEQ); // Removed
122
  // d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_GT); // Removed
123
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISN);
124
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISSN);
125
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISZ);
126
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISINF);
127
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNAN);
128
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISNEG);
129
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_ISPOS);
130
131
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_IEEE_BITVECTOR);
132
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_FLOATINGPOINT);
133
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_REAL);
134
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR);
135
9838
  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
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_UBV_TOTAL);
144
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_SBV_TOTAL);
145
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_TO_REAL_TOTAL);
146
147
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_NAN);
148
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_INF);
149
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_ZERO);
150
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGN);
151
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_EXPONENT);
152
9838
  d_equalityEngine->addFunctionKind(kind::FLOATINGPOINT_COMPONENT_SIGNIFICAND);
153
9838
  d_equalityEngine->addFunctionKind(kind::ROUNDINGMODE_BITBLAST);
154
9838
}
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
10307
    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
5304
  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
24219
void TheoryFp::postCheck(Effort level)
733
{
734
  /* Resolve the abstractions for the conversion lemmas */
735
24219
  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
24219
  Trace("fp") << "TheoryFp::check(): completed" << std::endl;
753
  /* Checking should be handled by the bit-vector engine */
754
24219
}
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
29280
}  // namespace cvc5