|
"""Implementation of :class:`ComplexField` class. """ |
|
|
|
|
|
from sympy.external.gmpy import SYMPY_INTS |
|
from sympy.core.numbers import Float, I |
|
from sympy.polys.domains.characteristiczero import CharacteristicZero |
|
from sympy.polys.domains.field import Field |
|
from sympy.polys.domains.gaussiandomains import QQ_I |
|
from sympy.polys.domains.simpledomain import SimpleDomain |
|
from sympy.polys.polyerrors import DomainError, CoercionFailed |
|
from sympy.utilities import public |
|
|
|
from mpmath import MPContext |
|
|
|
|
|
@public |
|
class ComplexField(Field, CharacteristicZero, SimpleDomain): |
|
"""Complex numbers up to the given precision. """ |
|
|
|
rep = 'CC' |
|
|
|
is_ComplexField = is_CC = True |
|
|
|
is_Exact = False |
|
is_Numerical = True |
|
|
|
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): |
|
|
|
|
|
|
|
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.mpc |
|
self.zero = self.dtype(0) |
|
self.one = self.dtype(1) |
|
|
|
|
|
self._max_denom = max(2**context.prec // 200, 99) |
|
self._tolerance = self.one / self._max_denom |
|
|
|
@property |
|
def tp(self): |
|
|
|
|
|
|
|
|
|
return self._dtype |
|
|
|
def dtype(self, x, y=0): |
|
|
|
|
|
|
|
if isinstance(x, SYMPY_INTS): |
|
x = int(x) |
|
if isinstance(y, SYMPY_INTS): |
|
y = int(y) |
|
return self._dtype(x, y) |
|
|
|
def __eq__(self, other): |
|
return isinstance(other, ComplexField) 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.real, self.dps) + I*Float(element.imag, self.dps) |
|
|
|
def from_sympy(self, expr): |
|
"""Convert SymPy's number to ``dtype``. """ |
|
number = expr.evalf(n=self.dps) |
|
real, imag = number.as_real_imag() |
|
|
|
if real.is_Number and imag.is_Number: |
|
return self.dtype(real, imag) |
|
else: |
|
raise CoercionFailed("expected complex number, got %s" % expr) |
|
|
|
def from_ZZ(self, element, base): |
|
return self.dtype(element) |
|
|
|
def from_ZZ_gmpy(self, element, base): |
|
return self.dtype(int(element)) |
|
|
|
def from_ZZ_python(self, element, base): |
|
return self.dtype(element) |
|
|
|
def from_QQ(self, element, base): |
|
return self.dtype(int(element.numerator)) / int(element.denominator) |
|
|
|
def from_QQ_python(self, element, base): |
|
return self.dtype(element.numerator) / element.denominator |
|
|
|
def from_QQ_gmpy(self, element, base): |
|
return self.dtype(int(element.numerator)) / int(element.denominator) |
|
|
|
def from_GaussianIntegerRing(self, element, base): |
|
return self.dtype(int(element.x), int(element.y)) |
|
|
|
def from_GaussianRationalField(self, element, base): |
|
x = element.x |
|
y = element.y |
|
return (self.dtype(int(x.numerator)) / int(x.denominator) + |
|
self.dtype(0, int(y.numerator)) / int(y.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): |
|
return self.dtype(element) |
|
|
|
def get_ring(self): |
|
"""Returns a ring associated with ``self``. """ |
|
raise DomainError("there is no ring associated with %s" % self) |
|
|
|
def get_exact(self): |
|
"""Returns an exact domain associated with ``self``. """ |
|
return QQ_I |
|
|
|
def is_negative(self, element): |
|
"""Returns ``False`` for any ``ComplexElement``. """ |
|
return False |
|
|
|
def is_positive(self, element): |
|
"""Returns ``False`` for any ``ComplexElement``. """ |
|
return False |
|
|
|
def is_nonnegative(self, element): |
|
"""Returns ``False`` for any ``ComplexElement``. """ |
|
return False |
|
|
|
def is_nonpositive(self, element): |
|
"""Returns ``False`` for any ``ComplexElement``. """ |
|
return False |
|
|
|
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``. Every complex number has a complex square root.""" |
|
return True |
|
|
|
def exsqrt(self, a): |
|
r"""Returns the principal complex square root of ``a``. |
|
|
|
Explanation |
|
=========== |
|
The argument of the principal square root is always within |
|
$(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be |
|
slightly inaccurate due to floating point rounding error. |
|
""" |
|
return a ** 0.5 |
|
|
|
CC = ComplexField() |
|
|