complexfield.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. """Implementation of :class:`ComplexField` class. """
  2. from sympy.external.gmpy import SYMPY_INTS
  3. from sympy.core.numbers import Float, I
  4. from sympy.polys.domains.characteristiczero import CharacteristicZero
  5. from sympy.polys.domains.field import Field
  6. from sympy.polys.domains.gaussiandomains import QQ_I
  7. from sympy.polys.domains.simpledomain import SimpleDomain
  8. from sympy.polys.polyerrors import DomainError, CoercionFailed
  9. from sympy.utilities import public
  10. from mpmath import MPContext
  11. @public
  12. class ComplexField(Field, CharacteristicZero, SimpleDomain):
  13. """Complex numbers up to the given precision. """
  14. rep = 'CC'
  15. is_ComplexField = is_CC = True
  16. is_Exact = False
  17. is_Numerical = True
  18. has_assoc_Ring = False
  19. has_assoc_Field = True
  20. _default_precision = 53
  21. @property
  22. def has_default_precision(self):
  23. return self.precision == self._default_precision
  24. @property
  25. def precision(self):
  26. return self._context.prec
  27. @property
  28. def dps(self):
  29. return self._context.dps
  30. @property
  31. def tolerance(self):
  32. return self._tolerance
  33. def __init__(self, prec=None, dps=None, tol=None):
  34. # XXX: The tolerance parameter is ignored but is kept for backward
  35. # compatibility for now.
  36. context = MPContext()
  37. if prec is None and dps is None:
  38. context.prec = self._default_precision
  39. elif dps is None:
  40. context.prec = prec
  41. elif prec is None:
  42. context.dps = dps
  43. else:
  44. raise TypeError("Cannot set both prec and dps")
  45. self._context = context
  46. self._dtype = context.mpc
  47. self.zero = self.dtype(0)
  48. self.one = self.dtype(1)
  49. # XXX: Neither of these is actually used anywhere.
  50. self._max_denom = max(2**context.prec // 200, 99)
  51. self._tolerance = self.one / self._max_denom
  52. @property
  53. def tp(self):
  54. # XXX: Domain treats tp as an alias of dtype. Here we need two separate
  55. # things: dtype is a callable to make/convert instances. We use tp with
  56. # isinstance to check if an object is an instance of the domain
  57. # already.
  58. return self._dtype
  59. def dtype(self, x, y=0):
  60. # XXX: This is needed because mpmath does not recognise fmpz.
  61. # It might be better to add conversion routines to mpmath and if that
  62. # happens then this can be removed.
  63. if isinstance(x, SYMPY_INTS):
  64. x = int(x)
  65. if isinstance(y, SYMPY_INTS):
  66. y = int(y)
  67. return self._dtype(x, y)
  68. def __eq__(self, other):
  69. return isinstance(other, ComplexField) and self.precision == other.precision
  70. def __hash__(self):
  71. return hash((self.__class__.__name__, self._dtype, self.precision))
  72. def to_sympy(self, element):
  73. """Convert ``element`` to SymPy number. """
  74. return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
  75. def from_sympy(self, expr):
  76. """Convert SymPy's number to ``dtype``. """
  77. number = expr.evalf(n=self.dps)
  78. real, imag = number.as_real_imag()
  79. if real.is_Number and imag.is_Number:
  80. return self.dtype(real, imag)
  81. else:
  82. raise CoercionFailed("expected complex number, got %s" % expr)
  83. def from_ZZ(self, element, base):
  84. return self.dtype(element)
  85. def from_ZZ_gmpy(self, element, base):
  86. return self.dtype(int(element))
  87. def from_ZZ_python(self, element, base):
  88. return self.dtype(element)
  89. def from_QQ(self, element, base):
  90. return self.dtype(int(element.numerator)) / int(element.denominator)
  91. def from_QQ_python(self, element, base):
  92. return self.dtype(element.numerator) / element.denominator
  93. def from_QQ_gmpy(self, element, base):
  94. return self.dtype(int(element.numerator)) / int(element.denominator)
  95. def from_GaussianIntegerRing(self, element, base):
  96. return self.dtype(int(element.x), int(element.y))
  97. def from_GaussianRationalField(self, element, base):
  98. x = element.x
  99. y = element.y
  100. return (self.dtype(int(x.numerator)) / int(x.denominator) +
  101. self.dtype(0, int(y.numerator)) / int(y.denominator))
  102. def from_AlgebraicField(self, element, base):
  103. return self.from_sympy(base.to_sympy(element).evalf(self.dps))
  104. def from_RealField(self, element, base):
  105. return self.dtype(element)
  106. def from_ComplexField(self, element, base):
  107. return self.dtype(element)
  108. def get_ring(self):
  109. """Returns a ring associated with ``self``. """
  110. raise DomainError("there is no ring associated with %s" % self)
  111. def get_exact(self):
  112. """Returns an exact domain associated with ``self``. """
  113. return QQ_I
  114. def is_negative(self, element):
  115. """Returns ``False`` for any ``ComplexElement``. """
  116. return False
  117. def is_positive(self, element):
  118. """Returns ``False`` for any ``ComplexElement``. """
  119. return False
  120. def is_nonnegative(self, element):
  121. """Returns ``False`` for any ``ComplexElement``. """
  122. return False
  123. def is_nonpositive(self, element):
  124. """Returns ``False`` for any ``ComplexElement``. """
  125. return False
  126. def gcd(self, a, b):
  127. """Returns GCD of ``a`` and ``b``. """
  128. return self.one
  129. def lcm(self, a, b):
  130. """Returns LCM of ``a`` and ``b``. """
  131. return a*b
  132. def almosteq(self, a, b, tolerance=None):
  133. """Check if ``a`` and ``b`` are almost equal. """
  134. return self._context.almosteq(a, b, tolerance)
  135. def is_square(self, a):
  136. """Returns ``True``. Every complex number has a complex square root."""
  137. return True
  138. def exsqrt(self, a):
  139. r"""Returns the principal complex square root of ``a``.
  140. Explanation
  141. ===========
  142. The argument of the principal square root is always within
  143. $(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be
  144. slightly inaccurate due to floating point rounding error.
  145. """
  146. return a ** 0.5
  147. CC = ComplexField()