Skip to content

Commit 1e1793b

Browse files
committed
Represent (a subset of) real numbers as algebraic numbers
This representation is used by Z3 for it covers a subset of irrational numbers in addition to rational numbers.
1 parent 30fca83 commit 1e1793b

File tree

7 files changed

+159
-107
lines changed

7 files changed

+159
-107
lines changed
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33
--trace --smt2
4+
^ a=x ∈ ℝ\.\(x^2 \+ -2 = 0\)
45
^EXIT=10$
56
^SIGNAL=0$
67
--
78
--
8-
This currently fails an invariant in parse_literal, but really requires
9-
adjusting the implementation of realt to be coefficients of a polynomial. We can
10-
then parse, e.g., (root-obj (+ (^ x 2) (- 2)) 1), which Z3 returns as part of
11-
the model.

src/solvers/smt2/smt2_conv.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Author: Daniel Kroening, kroening@kroening.com
1111

1212
#include "smt2_conv.h"
1313

14+
#include <util/algebraic_number.h>
1415
#include <util/arith_tools.h>
1516
#include <util/bitvector_expr.h>
1617
#include <util/byte_operators.h>
@@ -31,7 +32,6 @@ Author: Daniel Kroening, kroening@kroening.com
3132
#include <util/range.h>
3233
#include <util/rational.h>
3334
#include <util/rational_tools.h>
34-
#include <util/real.h>
3535
#include <util/simplify_expr.h>
3636
#include <util/std_expr.h>
3737
#include <util/string2int.h>
@@ -447,10 +447,11 @@ constant_exprt smt2_convt::parse_literal(
447447
}
448448
else if(type.id() == ID_real)
449449
{
450-
mp_integer first = string2integer(s.substr(0, pos));
451-
mp_integer second = string2integer(s.substr(pos, std::string::npos));
452-
realt real{first, second};
453-
return real.as_expr();
450+
rationalt rational_value;
451+
bool failed = to_rational(
452+
constant_exprt{src.id(), rational_typet{}}, rational_value);
453+
CHECK_RETURN(!failed);
454+
return algebraic_numbert{rational_value}.as_expr();
454455
}
455456
else
456457
{
@@ -538,6 +539,37 @@ constant_exprt smt2_convt::parse_literal(
538539
std::size_t s = unsafe_string2size_t(src.get_sub()[3].id_string());
539540
return ieee_float_valuet::NaN(ieee_float_spect(s - 1, e)).to_expr();
540541
}
542+
else if(
543+
src.get_sub().size() == 3 &&
544+
src.get_sub()[0].id() == "root-obj") // (root-obj (+ ...) 1)
545+
{
546+
DATA_INVARIANT_WITH_DIAGNOSTICS(
547+
src.get_sub()[1].id().empty() && src.get_sub()[1].get_sub().size() == 3 &&
548+
src.get_sub()[1].get_sub()[0].id() == "+" &&
549+
src.get_sub()[2].id() == "1",
550+
"unexpected root-obj expression",
551+
src.pretty());
552+
irept sum_rhs = src.get_sub()[1].get_sub()[2];
553+
rationalt constant_coeff;
554+
bool failed =
555+
to_rational(parse_literal(sum_rhs, rational_typet{}), constant_coeff);
556+
DATA_INVARIANT_WITH_DIAGNOSTICS(
557+
!failed, "failed to parse rational constant coefficient", src.pretty());
558+
irept sum_lhs = src.get_sub()[1].get_sub()[1];
559+
DATA_INVARIANT_WITH_DIAGNOSTICS(
560+
sum_lhs.id().empty() && sum_lhs.get_sub().size() == 3 &&
561+
sum_lhs.get_sub()[0].id() == "^" && sum_lhs.get_sub()[1].id() == "x",
562+
"unexpected first operand to root-obj",
563+
src.pretty());
564+
std::size_t degree = unsafe_string2size_t(sum_lhs.get_sub()[2].id_string());
565+
DATA_INVARIANT_WITH_DIAGNOSTICS(
566+
degree > 0, "polynomial degree must be positive", src.pretty());
567+
std::vector<rationalt> coefficients{degree + 1, rationalt{}};
568+
coefficients.front() = constant_coeff;
569+
coefficients.back() = rationalt{1};
570+
algebraic_numbert a{coefficients};
571+
return a.as_expr();
572+
}
541573

542574
if(type.id()==ID_signedbv ||
543575
type.id()==ID_unsignedbv ||

src/util/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
SRC = arith_tools.cpp \
1+
SRC = \
2+
algebraic_number.cpp \
3+
arith_tools.cpp \
24
array_element_from_pointer.cpp \
35
array_name.cpp \
46
bitvector_expr.cpp \
@@ -66,7 +68,6 @@ SRC = arith_tools.cpp \
6668
prefix_filter.cpp \
6769
rational.cpp \
6870
rational_tools.cpp \
69-
real.cpp \
7071
ref_expr_set.cpp \
7172
refined_string_type.cpp \
7273
rename.cpp \

src/util/algebraic_number.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*******************************************************************\
2+
3+
Module: Algebraic Numbers
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Algebraic numbers
11+
12+
#include "algebraic_number.h"
13+
14+
#include "mathematical_types.h"
15+
#include "rational_tools.h"
16+
17+
constant_exprt algebraic_numbert::as_expr() const
18+
{
19+
if(coefficients.size() == 2 && coefficients.back().is_one())
20+
{
21+
// root of x - c
22+
auto c = from_rational(-coefficients.front());
23+
c.type() = real_typet{};
24+
return c;
25+
}
26+
else
27+
{
28+
std::ostringstream oss;
29+
oss << *this;
30+
return constant_exprt{oss.str(), real_typet{}};
31+
}
32+
}
33+
34+
std::ostream &operator<<(std::ostream &out, const algebraic_numbert &a)
35+
{
36+
out << "x ∈ ℝ.(";
37+
38+
const auto &coefficients = a.get_coefficients();
39+
40+
bool need_plus = false;
41+
for(std::size_t d = coefficients.size(); d > 0; --d)
42+
{
43+
if(coefficients[d - 1].is_zero())
44+
continue;
45+
if(need_plus)
46+
out << " + ";
47+
if(d == 1)
48+
out << coefficients[d - 1];
49+
else
50+
{
51+
if(!coefficients[d - 1].is_one())
52+
out << coefficients[d - 1] << "*";
53+
out << "x^" << d - 1;
54+
}
55+
need_plus = true;
56+
}
57+
58+
if(!need_plus)
59+
out << "0";
60+
out << " = 0)";
61+
62+
return out;
63+
}

src/util/algebraic_number.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*******************************************************************\
2+
3+
Module: Algebraic numbers
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_UTIL_ALGEBRAIC_NUMBER_H
10+
#define CPROVER_UTIL_ALGEBRAIC_NUMBER_H
11+
12+
#include "rational.h"
13+
#include "std_expr.h"
14+
15+
/// Represents real numbers as roots (zeros) of a polynomial with rational
16+
/// coefficients. Cannot represent transcendental numbers.
17+
class algebraic_numbert
18+
{
19+
protected:
20+
// the i-th entry is the coefficient of degree i
21+
using coefficientst = std::vector<rationalt>;
22+
coefficientst coefficients;
23+
24+
public:
25+
/// Represent a real number as roots of a polynomial with rational
26+
/// coefficients \p coeff.
27+
explicit algebraic_numbert(const std::vector<rationalt> &coeff)
28+
: coefficients(coeff)
29+
{
30+
DATA_INVARIANT(coefficients.size() >= 2, "minimum degree is 1");
31+
}
32+
33+
/// The default constructor builds a `algebraic_numbert` representing real
34+
/// number 0.
35+
algebraic_numbert() : algebraic_numbert({rationalt{0}, rationalt{1}})
36+
{
37+
}
38+
39+
/// Represent a rational number as `algebraic_numbert`.
40+
algebraic_numbert(const rationalt &r) : algebraic_numbert({-r, rationalt{1}})
41+
{
42+
}
43+
44+
constant_exprt as_expr() const;
45+
46+
const coefficientst &get_coefficients() const
47+
{
48+
return coefficients;
49+
}
50+
};
51+
52+
std::ostream &operator<<(std::ostream &out, const algebraic_numbert &a);
53+
54+
#endif // CPROVER_UTIL_ALGEBRAIC_NUMBER_H

src/util/real.cpp

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/util/real.h

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)