realfield.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. """Implementation of :class:`RealField` class. """
  2. from sympy.external.gmpy import SYMPY_INTS, MPQ
  3. from sympy.core.numbers import Float
  4. from sympy.polys.domains.field import Field
  5. from sympy.polys.domains.simpledomain import SimpleDomain
  6. from sympy.polys.domains.characteristiczero import CharacteristicZero
  7. from sympy.polys.polyerrors import CoercionFailed
  8. from sympy.utilities import public
  9. from mpmath import MPContext
  10. from mpmath.libmp import to_rational as _mpmath_to_rational
  11. def to_rational(s, max_denom, limit=True):
  12. p, q = _mpmath_to_rational(s._mpf_)
  13. # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
  14. # to_rational() function returns a gmpy2.mpz instance and if MPQ is
  15. # flint.fmpq then MPQ(p, q) will fail.
  16. p = int(p)
  17. q = int(q)
  18. if not limit or q <= max_denom:
  19. return p, q
  20. p0, q0, p1, q1 = 0, 1, 1, 0
  21. n, d = p, q
  22. while True:
  23. a = n//d
  24. q2 = q0 + a*q1
  25. if q2 > max_denom:
  26. break
  27. p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
  28. n, d = d, n - a*d
  29. k = (max_denom - q0)//q1
  30. number = MPQ(p, q)
  31. bound1 = MPQ(p0 + k*p1, q0 + k*q1)
  32. bound2 = MPQ(p1, q1)
  33. if not bound2 or not bound1:
  34. return p, q
  35. elif abs(bound2 - number) <= abs(bound1 - number):
  36. return bound2.numerator, bound2.denominator
  37. else:
  38. return bound1.numerator, bound1.denominator
  39. @public
  40. class RealField(Field, CharacteristicZero, SimpleDomain):
  41. """Real numbers up to the given precision. """
  42. rep = 'RR'
  43. is_RealField = is_RR = True
  44. is_Exact = False
  45. is_Numerical = True
  46. is_PID = False
  47. has_assoc_Ring = False
  48. has_assoc_Field = True
  49. _default_precision = 53
  50. @property
  51. def has_default_precision(self):
  52. return self.precision == self._default_precision
  53. @property
  54. def precision(self):
  55. return self._context.prec
  56. @property
  57. def dps(self):
  58. return self._context.dps
  59. @property
  60. def tolerance(self):
  61. return self._tolerance
  62. def __init__(self, prec=None, dps=None, tol=None):
  63. # XXX: The tol parameter is ignored but is kept for now for backwards
  64. # compatibility.
  65. context = MPContext()
  66. if prec is None and dps is None:
  67. context.prec = self._default_precision
  68. elif dps is None:
  69. context.prec = prec
  70. elif prec is None:
  71. context.dps = dps
  72. else:
  73. raise TypeError("Cannot set both prec and dps")
  74. self._context = context
  75. self._dtype = context.mpf
  76. self.zero = self.dtype(0)
  77. self.one = self.dtype(1)
  78. # Only max_denom here is used for anything and is only used for
  79. # to_rational.
  80. self._max_denom = max(2**context.prec // 200, 99)
  81. self._tolerance = self.one / self._max_denom
  82. @property
  83. def tp(self):
  84. # XXX: Domain treats tp as an alias of dtype. Here we need to two
  85. # separate things: dtype is a callable to make/convert instances.
  86. # We use tp with isinstance to check if an object is an instance
  87. # of the domain already.
  88. return self._dtype
  89. def dtype(self, arg):
  90. # XXX: This is needed because mpmath does not recognise fmpz.
  91. # It might be better to add conversion routines to mpmath and if that
  92. # happens then this can be removed.
  93. if isinstance(arg, SYMPY_INTS):
  94. arg = int(arg)
  95. return self._dtype(arg)
  96. def __eq__(self, other):
  97. return isinstance(other, RealField) and self.precision == other.precision
  98. def __hash__(self):
  99. return hash((self.__class__.__name__, self._dtype, self.precision))
  100. def to_sympy(self, element):
  101. """Convert ``element`` to SymPy number. """
  102. return Float(element, self.dps)
  103. def from_sympy(self, expr):
  104. """Convert SymPy's number to ``dtype``. """
  105. number = expr.evalf(n=self.dps)
  106. if number.is_Number:
  107. return self.dtype(number)
  108. else:
  109. raise CoercionFailed("expected real number, got %s" % expr)
  110. def from_ZZ(self, element, base):
  111. return self.dtype(element)
  112. def from_ZZ_python(self, element, base):
  113. return self.dtype(element)
  114. def from_ZZ_gmpy(self, element, base):
  115. return self.dtype(int(element))
  116. # XXX: We need to convert the denominators to int here because mpmath does
  117. # not recognise mpz. Ideally mpmath would handle this and if it changed to
  118. # do so then the calls to int here could be removed.
  119. def from_QQ(self, element, base):
  120. return self.dtype(element.numerator) / int(element.denominator)
  121. def from_QQ_python(self, element, base):
  122. return self.dtype(element.numerator) / int(element.denominator)
  123. def from_QQ_gmpy(self, element, base):
  124. return self.dtype(int(element.numerator)) / int(element.denominator)
  125. def from_AlgebraicField(self, element, base):
  126. return self.from_sympy(base.to_sympy(element).evalf(self.dps))
  127. def from_RealField(self, element, base):
  128. return self.dtype(element)
  129. def from_ComplexField(self, element, base):
  130. if not element.imag:
  131. return self.dtype(element.real)
  132. def to_rational(self, element, limit=True):
  133. """Convert a real number to rational number. """
  134. return to_rational(element, self._max_denom, limit=limit)
  135. def get_ring(self):
  136. """Returns a ring associated with ``self``. """
  137. return self
  138. def get_exact(self):
  139. """Returns an exact domain associated with ``self``. """
  140. from sympy.polys.domains import QQ
  141. return QQ
  142. def gcd(self, a, b):
  143. """Returns GCD of ``a`` and ``b``. """
  144. return self.one
  145. def lcm(self, a, b):
  146. """Returns LCM of ``a`` and ``b``. """
  147. return a*b
  148. def almosteq(self, a, b, tolerance=None):
  149. """Check if ``a`` and ``b`` are almost equal. """
  150. return self._context.almosteq(a, b, tolerance)
  151. def is_square(self, a):
  152. """Returns ``True`` if ``a >= 0`` and ``False`` otherwise. """
  153. return a >= 0
  154. def exsqrt(self, a):
  155. """Non-negative square root for ``a >= 0`` and ``None`` otherwise.
  156. Explanation
  157. ===========
  158. The square root may be slightly inaccurate due to floating point
  159. rounding error.
  160. """
  161. return a ** 0.5 if a >= 0 else None
  162. RR = RealField()