| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- """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):
- # XXX: The tolerance parameter is ignored but is kept for backward
- # compatibility for now.
- 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)
- # XXX: Neither of these is actually used anywhere.
- 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 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, x, y=0):
- # 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(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()
|