Theory of Floating-Points

examples/api/cpp/floating_point_arith.cpp

  1/******************************************************************************
  2 * Top contributors (to current version):
  3 *   Aina Niemetz, Mudathir Mohamed, Andres Noetzli
  4 *
  5 * This file is part of the cvc5 project.
  6 *
  7 * Copyright (c) 2009-2024 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 * An example of solving floating-point problems with cvc5's cpp API.
 14 *
 15 * This example shows to create floating-point types, variables and expressions,
 16 * and how to create rounding mode constants by solving toy problems. The
 17 * example also shows making special values (such as NaN and +oo) and converting
 18 * an IEEE 754-2008 bit-vector to a floating-point number.
 19 */
 20
 21#include <cvc5/cvc5.h>
 22
 23#include <iostream>
 24#include <cassert>
 25
 26using namespace cvc5;
 27
 28int main()
 29{
 30  TermManager tm;
 31  Solver solver(tm);
 32  solver.setOption("incremental", "true");
 33  solver.setOption("produce-models", "true");
 34
 35  // Make single precision floating-point variables
 36  Sort fpt32 = tm.mkFloatingPointSort(8, 24);
 37  Term a = tm.mkConst(fpt32, "a");
 38  Term b = tm.mkConst(fpt32, "b");
 39  Term c = tm.mkConst(fpt32, "c");
 40  Term d = tm.mkConst(fpt32, "d");
 41  Term e = tm.mkConst(fpt32, "e");
 42  // Rounding mode
 43  Term rm = tm.mkRoundingMode(RoundingMode::ROUND_NEAREST_TIES_TO_EVEN);
 44
 45  std::cout << "Show that fused multiplication and addition `(fp.fma RM a b c)`"
 46            << std::endl
 47            << "is different from `(fp.add RM (fp.mul a b) c)`:" << std::endl;
 48  solver.push(1);
 49  Term fma = tm.mkTerm(Kind::FLOATINGPOINT_FMA, {rm, a, b, c});
 50  Term mul = tm.mkTerm(Kind::FLOATINGPOINT_MULT, {rm, a, b});
 51  Term add = tm.mkTerm(Kind::FLOATINGPOINT_ADD, {rm, mul, c});
 52  solver.assertFormula(tm.mkTerm(Kind::DISTINCT, {fma, add}));
 53  Result r = solver.checkSat();  // result is sat
 54  std::cout << "Expect sat: " << r << std::endl;
 55  std::cout << "Value of `a`: " << solver.getValue(a) << std::endl;
 56  std::cout << "Value of `b`: " << solver.getValue(b) << std::endl;
 57  std::cout << "Value of `c`: " << solver.getValue(c) << std::endl;
 58  std::cout << "Value of `(fp.fma RNE a b c)`: " << solver.getValue(fma)
 59            << std::endl;
 60  std::cout << "Value of `(fp.add RNE (fp.mul a b) c)`: "
 61            << solver.getValue(add) << std::endl;
 62  std::cout << std::endl;
 63  solver.pop(1);
 64
 65  std::cout << "Show that floating-point addition is not associative:"
 66            << std::endl;
 67  std::cout << "(a + (b + c)) != ((a + b) + c)" << std::endl;
 68  solver.push(1);
 69  solver.assertFormula(tm.mkTerm(
 70      Kind::DISTINCT,
 71      {tm.mkTerm(Kind::FLOATINGPOINT_ADD,
 72                 {rm, a, tm.mkTerm(Kind::FLOATINGPOINT_ADD, {rm, b, c})}),
 73       tm.mkTerm(Kind::FLOATINGPOINT_ADD,
 74                 {rm, tm.mkTerm(Kind::FLOATINGPOINT_ADD, {rm, a, b}), c})}));
 75
 76  r = solver.checkSat();  // result is sat
 77  std::cout << "Expect sat: " << r << std::endl;
 78  assert (r.isSat());
 79
 80  std::cout << "Value of `a`: " << solver.getValue(a) << std::endl;
 81  std::cout << "Value of `b`: " << solver.getValue(b) << std::endl;
 82  std::cout << "Value of `c`: " << solver.getValue(c) << std::endl;
 83  std::cout << std::endl;
 84
 85  std::cout << "Now, restrict `a` to be either NaN or positive infinity:"
 86            << std::endl;
 87  Term nan = tm.mkFloatingPointNaN(8, 24);
 88  Term inf = tm.mkFloatingPointPosInf(8, 24);
 89  solver.assertFormula(tm.mkTerm(
 90      Kind::OR,
 91      {tm.mkTerm(Kind::EQUAL, {a, inf}), tm.mkTerm(Kind::EQUAL, {a, nan})}));
 92
 93  r = solver.checkSat();  // result is sat
 94  std::cout << "Expect sat: " << r << std::endl;
 95  assert (r.isSat());
 96
 97  std::cout << "Value of `a`: " << solver.getValue(a) << std::endl;
 98  std::cout << "Value of `b`: " << solver.getValue(b) << std::endl;
 99  std::cout << "Value of `c`: " << solver.getValue(c) << std::endl;
100  std::cout << std::endl;
101  solver.pop(1);
102
103  std::cout << "Now, try to find a (normal) floating-point number that rounds"
104            << std::endl
105            << "to different integer values for different rounding modes:"
106            << std::endl;
107  solver.push(1);
108  Term rtp = tm.mkRoundingMode(RoundingMode::ROUND_TOWARD_POSITIVE);
109  Term rtn = tm.mkRoundingMode(RoundingMode::ROUND_TOWARD_NEGATIVE);
110  Op op = tm.mkOp(Kind::FLOATINGPOINT_TO_UBV, {16});  // (_ fp.to_ubv 16)
111  Term lhs = tm.mkTerm(op, {rtp, d});
112  Term rhs = tm.mkTerm(op, {rtn, d});
113  solver.assertFormula(tm.mkTerm(Kind::FLOATINGPOINT_IS_NORMAL, {d}));
114  solver.assertFormula(
115      tm.mkTerm(Kind::NOT, {tm.mkTerm(Kind::EQUAL, {lhs, rhs})}));
116
117  r = solver.checkSat();  // result is sat
118  std::cout << "Expect sat: " << r << std::endl;
119  assert (r.isSat());
120  std::cout << std::endl;
121
122  std::cout << "Get value of `d` as floating-point, bit-vector and real:"
123            << std::endl;
124  Term val = solver.getValue(d);
125  std::cout << "Value of `d`: " << val << std::endl;
126  std::cout << "Value of `((_ fp.to_ubv 16) RTP d)`: " << solver.getValue(lhs)
127            << std::endl;
128  std::cout << "Value of `((_ fp.to_ubv 16) RTN d)`: " << solver.getValue(rhs)
129            << std::endl;
130  std::cout << "Value of `(fp.to_real d)` "
131            << solver.getValue(tm.mkTerm(Kind::FLOATINGPOINT_TO_REAL, {val}))
132            << std::endl;
133  std::cout << std::endl;
134  solver.pop(1);
135
136  std::cout << "Finally, try to find a floating-point number between positive"
137            << std::endl
138            << "zero and the smallest positive floating-point number:"
139            << std::endl;
140  Term zero = tm.mkFloatingPointPosZero(8, 24);
141  Term smallest = tm.mkFloatingPoint(8, 24, tm.mkBitVector(32, 1));
142  solver.assertFormula(
143      tm.mkTerm(Kind::AND,
144                {tm.mkTerm(Kind::FLOATINGPOINT_LT, {zero, e}),
145                 tm.mkTerm(Kind::FLOATINGPOINT_LT, {e, smallest})}));
146
147  r = solver.checkSat();  // result is unsat
148  std::cout << "Expect unsat: " << r << std::endl;
149  assert(r.isUnsat());
150}