| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- """Implementation of :class:`RealField` class. """
- from sympy.external.gmpy import SYMPY_INTS, MPQ
- from sympy.core.numbers import Float
- from sympy.polys.domains.field import Field
- from sympy.polys.domains.simpledomain import SimpleDomain
- from sympy.polys.domains.characteristiczero import CharacteristicZero
- from sympy.polys.polyerrors import CoercionFailed
- from sympy.utilities import public
- from mpmath import MPContext
- from mpmath.libmp import to_rational as _mpmath_to_rational
- def to_rational(s, max_denom, limit=True):
- p, q = _mpmath_to_rational(s._mpf_)
- # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
- # to_rational() function returns a gmpy2.mpz instance and if MPQ is
- # flint.fmpq then MPQ(p, q) will fail.
- p = int(p)
- q = int(q)
- if not limit or q <= max_denom:
- return p, q
- p0, q0, p1, q1 = 0, 1, 1, 0
- n, d = p, q
- while True:
- a = n//d
- q2 = q0 + a*q1
- if q2 > max_denom:
- break
- p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
- n, d = d, n - a*d
- k = (max_denom - q0)//q1
- number = MPQ(p, q)
- bound1 = MPQ(p0 + k*p1, q0 + k*q1)
- bound2 = MPQ(p1, q1)
- if not bound2 or not bound1:
- return p, q
- elif abs(bound2 - number) <= abs(bound1 - number):
- return bound2.numerator, bound2.denominator
- else:
- return bound1.numerator, bound1.denominator
- @public
- class RealField(Field, CharacteristicZero, SimpleDomain):
- """Real numbers up to the given precision. """
- rep = 'RR'
- is_RealField = is_RR = True
- is_Exact = False
- is_Numerical = True
- is_PID = False
- has_assoc_Ring = False
- has_assoc_Field = True
- _default_precision = 53
- @property
- def has_default_precision(self):
- return self.precision == self._default_precision
- @property
- def precision(self):
- return self._context.prec
- @property
- def dps(self):
- return self._context.dps
- @property
- def tolerance(self):
- return self._tolerance
- def __init__(self, prec=None, dps=None, tol=None):
- # XXX: The tol parameter is ignored but is kept for now for backwards
- # compatibility.
- context = MPContext()
- if prec is None and dps is None:
- context.prec = self._default_precision
- elif dps is None:
- context.prec = prec
- elif prec is None:
- context.dps = dps
- else:
- raise TypeError("Cannot set both prec and dps")
- self._context = context
- self._dtype = context.mpf
- self.zero = self.dtype(0)
- self.one = self.dtype(1)
- # Only max_denom here is used for anything and is only used for
- # to_rational.
- self._max_denom = max(2**context.prec // 200, 99)
- self._tolerance = self.one / self._max_denom
- @property
- def tp(self):
- # XXX: Domain treats tp as an alias of dtype. Here we need to two
- # separate things: dtype is a callable to make/convert instances.
- # We use tp with isinstance to check if an object is an instance
- # of the domain already.
- return self._dtype
- def dtype(self, arg):
- # XXX: This is needed because mpmath does not recognise fmpz.
- # It might be better to add conversion routines to mpmath and if that
- # happens then this can be removed.
- if isinstance(arg, SYMPY_INTS):
- arg = int(arg)
- return self._dtype(arg)
- def __eq__(self, other):
- return isinstance(other, RealField) and self.precision == other.precision
- def __hash__(self):
- return hash((self.__class__.__name__, self._dtype, self.precision))
- def to_sympy(self, element):
- """Convert ``element`` to SymPy number. """
- return Float(element, self.dps)
- def from_sympy(self, expr):
- """Convert SymPy's number to ``dtype``. """
- number = expr.evalf(n=self.dps)
- if number.is_Number:
- return self.dtype(number)
- else:
- raise CoercionFailed("expected real number, got %s" % expr)
- def from_ZZ(self, element, base):
- return self.dtype(element)
- def from_ZZ_python(self, element, base):
- return self.dtype(element)
- def from_ZZ_gmpy(self, element, base):
- return self.dtype(int(element))
- # XXX: We need to convert the denominators to int here because mpmath does
- # not recognise mpz. Ideally mpmath would handle this and if it changed to
- # do so then the calls to int here could be removed.
- def from_QQ(self, element, base):
- return self.dtype(element.numerator) / int(element.denominator)
- def from_QQ_python(self, element, base):
- return self.dtype(element.numerator) / int(element.denominator)
- def from_QQ_gmpy(self, element, base):
- return self.dtype(int(element.numerator)) / int(element.denominator)
- def from_AlgebraicField(self, element, base):
- return self.from_sympy(base.to_sympy(element).evalf(self.dps))
- def from_RealField(self, element, base):
- return self.dtype(element)
- def from_ComplexField(self, element, base):
- if not element.imag:
- return self.dtype(element.real)
- def to_rational(self, element, limit=True):
- """Convert a real number to rational number. """
- return to_rational(element, self._max_denom, limit=limit)
- def get_ring(self):
- """Returns a ring associated with ``self``. """
- return self
- def get_exact(self):
- """Returns an exact domain associated with ``self``. """
- from sympy.polys.domains import QQ
- return QQ
- def gcd(self, a, b):
- """Returns GCD of ``a`` and ``b``. """
- return self.one
- def lcm(self, a, b):
- """Returns LCM of ``a`` and ``b``. """
- return a*b
- def almosteq(self, a, b, tolerance=None):
- """Check if ``a`` and ``b`` are almost equal. """
- return self._context.almosteq(a, b, tolerance)
- def is_square(self, a):
- """Returns ``True`` if ``a >= 0`` and ``False`` otherwise. """
- return a >= 0
- def exsqrt(self, a):
- """Non-negative square root for ``a >= 0`` and ``None`` otherwise.
- Explanation
- ===========
- The square root may be slightly inaccurate due to floating point
- rounding error.
- """
- return a ** 0.5 if a >= 0 else None
- RR = RealField()
|