mirror of
https://github.com/Z3Prover/z3
synced 2025-04-11 03:33:35 +00:00
494 lines
15 KiB
C++
494 lines
15 KiB
C++
/*++
|
|
Copyright (c) 2011 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
algebraic_numbers.h
|
|
|
|
Abstract:
|
|
|
|
Real Algebraic Numbers
|
|
|
|
Author:
|
|
|
|
Leonardo (leonardo) 2011-11-22
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#ifndef _ALGEBRAIC_NUMBERS_H_
|
|
#define _ALGEBRAIC_NUMBERS_H_
|
|
|
|
#include"rational.h"
|
|
#include"mpq.h"
|
|
#include"polynomial.h"
|
|
#include"z3_exception.h"
|
|
#include"scoped_numeral.h"
|
|
#include"scoped_numeral_vector.h"
|
|
#include"tptr.h"
|
|
#include"statistics.h"
|
|
#include"params.h"
|
|
|
|
class small_object_allocator;
|
|
class mpbq_manager;
|
|
class mpbq;
|
|
|
|
namespace algebraic_numbers {
|
|
class anum;
|
|
class manager;
|
|
|
|
class algebraic_exception : public default_exception {
|
|
public:
|
|
algebraic_exception(char const * msg):default_exception(msg) {}
|
|
};
|
|
|
|
class manager {
|
|
public:
|
|
struct imp;
|
|
private:
|
|
imp * m_imp;
|
|
small_object_allocator * m_allocator;
|
|
bool m_own_allocator;
|
|
public:
|
|
static bool precise() { return true; }
|
|
static bool field() { return true; }
|
|
typedef anum numeral;
|
|
typedef svector<numeral> numeral_vector;
|
|
typedef _scoped_numeral<manager> scoped_numeral;
|
|
typedef _scoped_numeral_vector<manager> scoped_numeral_vector;
|
|
|
|
manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
|
~manager();
|
|
|
|
static void get_param_descrs(param_descrs & r);
|
|
static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
|
|
|
|
void set_cancel(bool f);
|
|
|
|
void updt_params(params_ref const & p);
|
|
|
|
unsynch_mpq_manager & qm() const;
|
|
|
|
mpbq_manager & bqm() const;
|
|
|
|
void del(numeral & a);
|
|
|
|
/**
|
|
\brief a <- 0
|
|
*/
|
|
void reset(numeral & a);
|
|
|
|
/**
|
|
\brief Return true if a is zero.
|
|
*/
|
|
bool is_zero(numeral const & a);
|
|
|
|
/**
|
|
\brief Return true if a is positive.
|
|
*/
|
|
bool is_pos(numeral const & a);
|
|
|
|
/**
|
|
\brief Return true if a is negative.
|
|
*/
|
|
bool is_neg(numeral const & a);
|
|
|
|
/**
|
|
\brief Return true if a is a rational number.
|
|
*/
|
|
bool is_rational(numeral const & a);
|
|
|
|
/**
|
|
\brief Return true if a is an integer.
|
|
*/
|
|
bool is_int(numeral const & a);
|
|
|
|
/**
|
|
\brief Degree of the algebraic number.
|
|
That is, degree of the polynomial that is used to encode \c a.
|
|
*/
|
|
unsigned degree(numeral const & a);
|
|
|
|
/**
|
|
\brief Convert a into a rational number.
|
|
|
|
\pre is_rational(a)
|
|
*/
|
|
void to_rational(numeral const & a, mpq & r);
|
|
|
|
/**
|
|
\brief Convert a into a rational number.
|
|
|
|
\pre is_rational(a)
|
|
*/
|
|
void to_rational(numeral const & a, rational & r);
|
|
|
|
/**
|
|
\brief a <- n
|
|
*/
|
|
void set(numeral & a, int n);
|
|
void set(numeral & a, mpz const & n);
|
|
void set(numeral & a, mpq const & n);
|
|
void set(numeral & a, numeral const & n);
|
|
|
|
void swap(numeral & a, numeral & b);
|
|
|
|
/**
|
|
\brief Store in b an integer value smaller than 'a'.
|
|
|
|
Remark: this is not the floor, but b <= floor(a)
|
|
*/
|
|
void int_lt(numeral const & a, numeral & b);
|
|
|
|
/**
|
|
\brief Store in b an integer value bigger than 'a'
|
|
|
|
Remark: this is not the ceil, but b >= ceil(a)
|
|
*/
|
|
void int_gt(numeral const & a, numeral & b);
|
|
|
|
/**
|
|
\brief Store in result a value in the interval (prev, next)
|
|
|
|
\pre lt(pre,v next)
|
|
*/
|
|
void select(numeral const & prev, numeral const & curr, numeral & result);
|
|
|
|
/**
|
|
\brief Isolate the roots of (an univariate polynomial) p, and store them as algebraic numbers in \c root.
|
|
That is, p is in Z[x].
|
|
*/
|
|
void isolate_roots(polynomial_ref const & p, numeral_vector & roots);
|
|
|
|
/**
|
|
\brief Isolate the roots of a multivariate polynomial p such that all but one variable of p is fixed by x2v, and
|
|
store them as algebraic numbers in \c root.
|
|
|
|
That is, we are viewing p as a polynomial in Z[y_1, ..., y_n][x]:
|
|
q_n(y_1, ..., y_n)x^n + ... + q_1(y_1, ..., y_n)*x + q_0
|
|
And we are returning the roots of
|
|
q_n(x2v(y_1), ..., x2v(y_n))x^n + ... + q_1(x2v(y_1), ..., x2v(y_n))*x + q_0
|
|
*/
|
|
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots);
|
|
|
|
/**
|
|
\brief Isolate the roots of the given polynomial, and compute its sign between them.
|
|
*/
|
|
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector<int> & signs);
|
|
|
|
/**
|
|
\brief Store in r the i-th root of p.
|
|
|
|
This method throws an exception if p does not have at least i roots.
|
|
|
|
This method is not really used in the nonlinear procedure.
|
|
It is mainly used for debugging purposes, and creating regression tests
|
|
|
|
\pre i > 0
|
|
*/
|
|
void mk_root(polynomial_ref const & p, unsigned i, numeral & r);
|
|
|
|
/**
|
|
\brief Store in r the i-th root of p.
|
|
This method throws an exception if the s-expression p does not represent
|
|
an univariate polynomial, of if p does not have at least i roots.
|
|
|
|
This method is not really used in the nonlinear procedure.
|
|
It is mainly used for debugging purposes, and "reading" root objects in the SMT 2.0 front-end.
|
|
|
|
\pre i > 0
|
|
*/
|
|
void mk_root(sexpr const * p, unsigned i, numeral & r);
|
|
|
|
/**
|
|
\brief Return a^{1/k}
|
|
|
|
Throws an exception if the result is not a real.
|
|
That is, (a is negative and k is even) or (k is zero).
|
|
*/
|
|
void root(numeral const & a, unsigned k, numeral & b);
|
|
|
|
/**
|
|
\brief Return a^k
|
|
|
|
Throws an exception if 0^0.
|
|
*/
|
|
void power(numeral const & a, unsigned k, numeral & b);
|
|
|
|
/**
|
|
\brief c <- a + b
|
|
*/
|
|
void add(numeral const & a, numeral const & b, numeral & c);
|
|
void add(numeral const & a, mpz const & b, numeral & c);
|
|
|
|
/**
|
|
\brief c <- a - b
|
|
*/
|
|
void sub(numeral const & a, numeral const & b, numeral & c);
|
|
|
|
/**
|
|
\brief c <- a * b
|
|
*/
|
|
void mul(numeral const & a, numeral const & b, numeral & c);
|
|
|
|
/**
|
|
\brief a <- -a
|
|
*/
|
|
void neg(numeral & a);
|
|
|
|
/**
|
|
\brief a <- 1/a if a != 0
|
|
*/
|
|
void inv(numeral & a);
|
|
|
|
/**
|
|
\brief c <- a/b if b != 0
|
|
*/
|
|
void div(numeral const & a, numeral const & b, numeral & c);
|
|
|
|
/**
|
|
Return -1 if a < b
|
|
Return 0 if a == b
|
|
Return 1 if a > b
|
|
*/
|
|
int compare(numeral const & a, numeral const & b);
|
|
|
|
/**
|
|
\brief a == b
|
|
*/
|
|
bool eq(numeral const & a, numeral const & b);
|
|
bool eq(numeral const & a, mpq const & b);
|
|
bool eq(numeral const & a, mpz const & b);
|
|
|
|
/**
|
|
\brief a != b
|
|
*/
|
|
bool neq(numeral const & a, numeral const & b) { return !eq(a, b); }
|
|
bool neq(numeral const & a, mpq const & b) { return !eq(a, b); }
|
|
bool neq(numeral const & a, mpz const & b) { return !eq(a, b); }
|
|
|
|
/**
|
|
\brief a < b
|
|
*/
|
|
bool lt(numeral const & a, numeral const & b);
|
|
bool lt(numeral const & a, mpq const & b);
|
|
bool lt(numeral const & a, mpz const & b);
|
|
|
|
/**
|
|
\brief a > b
|
|
*/
|
|
bool gt(numeral const & a, numeral const & b) { return lt(b, a); }
|
|
bool gt(numeral const & a, mpq const & b);
|
|
bool gt(numeral const & a, mpz const & b);
|
|
|
|
/**
|
|
\brief a <= b
|
|
*/
|
|
bool le(numeral const & a, numeral const & b) { return !gt(a, b); }
|
|
bool le(numeral const & a, mpq const & b) { return !gt(a, b); }
|
|
bool le(numeral const & a, mpz const & b) { return !gt(a, b); }
|
|
|
|
/**
|
|
\brief a >= b
|
|
*/
|
|
bool ge(numeral const & a, numeral const & b) { return !lt(a, b); }
|
|
bool ge(numeral const & a, mpq const & b) { return !lt(a, b); }
|
|
bool ge(numeral const & a, mpz const & b) { return !lt(a, b); }
|
|
|
|
/**
|
|
\brief Evaluate the sign of a multivariate polynomial p(x_1, ..., x_n)
|
|
at assignment x2v: [x_1 -> alpha_1, ..., x_n -> alpha_n].
|
|
|
|
\remark forall variable x in p, we have that x2v.contains(x) is true
|
|
|
|
Return negative number if p(alpha_1, ..., alpha_n) < 0
|
|
Return 0 if p(alpha_1, ..., alpha_n) == 0
|
|
Return positive number if p(alpha_1, ..., alpha_n) > 0
|
|
*/
|
|
int eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v);
|
|
|
|
void get_polynomial(numeral const & a, svector<mpz> & r);
|
|
|
|
// Procedures for getting lower and upper bounds for irrational numbers
|
|
void get_lower(numeral const & a, mpbq & l);
|
|
void get_lower(numeral const & a, mpq & l);
|
|
void get_lower(numeral const & a, rational & l);
|
|
void get_lower(numeral const & a, mpq & l, unsigned precision);
|
|
void get_lower(numeral const & a, rational & l, unsigned precision);
|
|
|
|
void get_upper(numeral const & a, mpbq & u);
|
|
void get_upper(numeral const & a, mpq & u);
|
|
void get_upper(numeral const & a, rational & u);
|
|
void get_upper(numeral const & a, mpq & l, unsigned precision);
|
|
void get_upper(numeral const & a, rational & l, unsigned precision);
|
|
|
|
/**
|
|
\brief Display algebraic number as a rational if is_rational(n)
|
|
Otherwise, display it as an interval.
|
|
*/
|
|
void display_interval(std::ostream & out, numeral const & a) const;
|
|
|
|
/**
|
|
\brief Display algebraic number in decimal notation.
|
|
A question mark is added based on the precision requested.
|
|
*/
|
|
void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const;
|
|
|
|
/**
|
|
\brief Display algebraic number as a root object: (p, i)
|
|
That is, 'a' is the i-th root of p.
|
|
*/
|
|
void display_root(std::ostream & out, numeral const & a) const;
|
|
|
|
/**
|
|
\brief Display algebraic number as a root object in SMT 2.0 style: (root-obj p i)
|
|
That is, 'a' is the i-th root of p.
|
|
*/
|
|
void display_root_smt2(std::ostream & out, numeral const & a) const;
|
|
|
|
/**
|
|
\brief Display algebraic number in Mathematica format.
|
|
*/
|
|
void display_mathematica(std::ostream & out, numeral const & a) const;
|
|
|
|
void display(std::ostream & out, numeral const & a) { return display_decimal(out, a); }
|
|
|
|
void reset_statistics();
|
|
|
|
void collect_statistics(statistics & st) const;
|
|
};
|
|
|
|
struct basic_cell;
|
|
struct algebraic_cell;
|
|
|
|
enum anum_kind { BASIC = 0, ROOT };
|
|
|
|
class anum {
|
|
friend struct manager::imp;
|
|
friend class manager;
|
|
void * m_cell;
|
|
anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {}
|
|
anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {}
|
|
bool is_basic() const { return GET_TAG(m_cell) == BASIC; }
|
|
basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); }
|
|
algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); }
|
|
public:
|
|
anum():m_cell(0) {}
|
|
};
|
|
};
|
|
|
|
typedef algebraic_numbers::manager anum_manager;
|
|
typedef algebraic_numbers::manager::numeral anum;
|
|
typedef algebraic_numbers::manager::numeral_vector anum_vector;
|
|
typedef algebraic_numbers::manager::scoped_numeral scoped_anum;
|
|
typedef algebraic_numbers::manager::scoped_numeral_vector scoped_anum_vector;
|
|
|
|
#define AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \
|
|
inline bool EXTERNAL(scoped_anum const & a, TYPE const & b) { \
|
|
anum_manager & m = a.m(); \
|
|
scoped_anum _b(m); \
|
|
m.set(_b, b); \
|
|
return m.INTERNAL(a, _b); \
|
|
}
|
|
|
|
#define AN_MK_COMPARISON(EXTERNAL, INTERNAL) \
|
|
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \
|
|
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \
|
|
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq)
|
|
|
|
AN_MK_COMPARISON(operator==, eq);
|
|
AN_MK_COMPARISON(operator!=, neq);
|
|
AN_MK_COMPARISON(operator<, lt);
|
|
AN_MK_COMPARISON(operator<=, le);
|
|
AN_MK_COMPARISON(operator>, gt);
|
|
AN_MK_COMPARISON(operator>=, ge);
|
|
|
|
#undef AN_MK_COMPARISON
|
|
#undef AN_MK_COMPARISON_CORE
|
|
|
|
#define AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \
|
|
inline scoped_anum EXTERNAL(scoped_anum const & a, TYPE const & b) { \
|
|
anum_manager & m = a.m(); \
|
|
scoped_anum _b(m); \
|
|
m.set(_b, b); \
|
|
scoped_anum r(m); \
|
|
m.INTERNAL(a, _b, r); \
|
|
return r; \
|
|
}
|
|
|
|
#define AN_MK_BINARY(EXTERNAL, INTERNAL) \
|
|
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \
|
|
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \
|
|
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq)
|
|
|
|
AN_MK_BINARY(operator+, add)
|
|
AN_MK_BINARY(operator-, sub)
|
|
AN_MK_BINARY(operator*, mul)
|
|
AN_MK_BINARY(operator/, div)
|
|
|
|
#undef AN_MK_BINARY
|
|
#undef AN_MK_BINARY_CORE
|
|
|
|
inline scoped_anum root(scoped_anum const & a, unsigned k) {
|
|
scoped_anum r(a.m());
|
|
a.m().root(a, k, r);
|
|
return r;
|
|
}
|
|
|
|
inline scoped_anum power(scoped_anum const & a, unsigned k) {
|
|
scoped_anum r(a.m());
|
|
a.m().power(a, k, r);
|
|
return r;
|
|
}
|
|
|
|
inline scoped_anum operator^(scoped_anum const & a, unsigned k) {
|
|
return power(a, k);
|
|
}
|
|
|
|
inline bool is_int(scoped_anum const & a) {
|
|
return a.m().is_int(a);
|
|
}
|
|
|
|
inline bool is_rational(scoped_anum const & a) {
|
|
return a.m().is_rational(a);
|
|
}
|
|
|
|
struct root_obj_pp {
|
|
anum_manager & m;
|
|
anum const & n;
|
|
root_obj_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
|
|
root_obj_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
|
|
};
|
|
|
|
inline std::ostream & operator<<(std::ostream & out, root_obj_pp const & n) {
|
|
n.m.display_root(out, n.n);
|
|
return out;
|
|
}
|
|
|
|
struct decimal_pp {
|
|
anum_manager & m;
|
|
anum const & n;
|
|
unsigned prec;
|
|
decimal_pp(anum_manager & _m, anum const & _n, unsigned p):m(_m), n(_n), prec(p) {}
|
|
decimal_pp(scoped_anum const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {}
|
|
};
|
|
|
|
inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) {
|
|
n.m.display_decimal(out, n.n, n.prec);
|
|
return out;
|
|
}
|
|
|
|
struct interval_pp {
|
|
anum_manager & m;
|
|
anum const & n;
|
|
interval_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
|
|
interval_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
|
|
};
|
|
|
|
inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) {
|
|
n.m.display_interval(out, n.n);
|
|
return out;
|
|
}
|
|
|
|
#endif
|