1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219 |
- """Implementation of :class:`Domain` class. """
- from typing import Any, Optional, Type
- from sympy.core import Basic, sympify
- from sympy.core.sorting import default_sort_key, ordered
- from sympy.external.gmpy import HAS_GMPY
- from sympy.polys.domains.domainelement import DomainElement
- from sympy.polys.orderings import lex
- from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError
- from sympy.polys.polyutils import _unify_gens, _not_a_coeff
- from sympy.utilities import public
- from sympy.utilities.iterables import is_sequence
- @public
- class Domain:
- """Superclass for all domains in the polys domains system.
- See :ref:`polys-domainsintro` for an introductory explanation of the
- domains system.
- The :py:class:`~.Domain` class is an abstract base class for all of the
- concrete domain types. There are many different :py:class:`~.Domain`
- subclasses each of which has an associated ``dtype`` which is a class
- representing the elements of the domain. The coefficients of a
- :py:class:`~.Poly` are elements of a domain which must be a subclass of
- :py:class:`~.Domain`.
- Examples
- ========
- The most common example domains are the integers :ref:`ZZ` and the
- rationals :ref:`QQ`.
- >>> from sympy import Poly, symbols, Domain
- >>> x, y = symbols('x, y')
- >>> p = Poly(x**2 + y)
- >>> p
- Poly(x**2 + y, x, y, domain='ZZ')
- >>> p.domain
- ZZ
- >>> isinstance(p.domain, Domain)
- True
- >>> Poly(x**2 + y/2)
- Poly(x**2 + 1/2*y, x, y, domain='QQ')
- The domains can be used directly in which case the domain object e.g.
- (:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of
- ``dtype``.
- >>> from sympy import ZZ, QQ
- >>> ZZ(2)
- 2
- >>> ZZ.dtype # doctest: +SKIP
- <class 'int'>
- >>> type(ZZ(2)) # doctest: +SKIP
- <class 'int'>
- >>> QQ(1, 2)
- 1/2
- >>> type(QQ(1, 2)) # doctest: +SKIP
- <class 'sympy.polys.domains.pythonrational.PythonRational'>
- The corresponding domain elements can be used with the arithmetic
- operations ``+,-,*,**`` and depending on the domain some combination of
- ``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor
- division) and ``%`` (modulo division) can be used but ``/`` (true
- division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements
- can be used with ``/`` but ``//`` and ``%`` should not be used. Some
- domains have a :py:meth:`~.Domain.gcd` method.
- >>> ZZ(2) + ZZ(3)
- 5
- >>> ZZ(5) // ZZ(2)
- 2
- >>> ZZ(5) % ZZ(2)
- 1
- >>> QQ(1, 2) / QQ(2, 3)
- 3/4
- >>> ZZ.gcd(ZZ(4), ZZ(2))
- 2
- >>> QQ.gcd(QQ(2,7), QQ(5,3))
- 1/21
- >>> ZZ.is_Field
- False
- >>> QQ.is_Field
- True
- There are also many other domains including:
- 1. :ref:`GF(p)` for finite fields of prime order.
- 2. :ref:`RR` for real (floating point) numbers.
- 3. :ref:`CC` for complex (floating point) numbers.
- 4. :ref:`QQ(a)` for algebraic number fields.
- 5. :ref:`K[x]` for polynomial rings.
- 6. :ref:`K(x)` for rational function fields.
- 7. :ref:`EX` for arbitrary expressions.
- Each domain is represented by a domain object and also an implementation
- class (``dtype``) for the elements of the domain. For example the
- :ref:`K[x]` domains are represented by a domain object which is an
- instance of :py:class:`~.PolynomialRing` and the elements are always
- instances of :py:class:`~.PolyElement`. The implementation class
- represents particular types of mathematical expressions in a way that is
- more efficient than a normal SymPy expression which is of type
- :py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and
- :py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr`
- to a domain element and vice versa.
- >>> from sympy import Symbol, ZZ, Expr
- >>> x = Symbol('x')
- >>> K = ZZ[x] # polynomial ring domain
- >>> K
- ZZ[x]
- >>> type(K) # class of the domain
- <class 'sympy.polys.domains.polynomialring.PolynomialRing'>
- >>> K.dtype # class of the elements
- <class 'sympy.polys.rings.PolyElement'>
- >>> p_expr = x**2 + 1 # Expr
- >>> p_expr
- x**2 + 1
- >>> type(p_expr)
- <class 'sympy.core.add.Add'>
- >>> isinstance(p_expr, Expr)
- True
- >>> p_domain = K.from_sympy(p_expr)
- >>> p_domain # domain element
- x**2 + 1
- >>> type(p_domain)
- <class 'sympy.polys.rings.PolyElement'>
- >>> K.to_sympy(p_domain) == p_expr
- True
- The :py:meth:`~.Domain.convert_from` method is used to convert domain
- elements from one domain to another.
- >>> from sympy import ZZ, QQ
- >>> ez = ZZ(2)
- >>> eq = QQ.convert_from(ez, ZZ)
- >>> type(ez) # doctest: +SKIP
- <class 'int'>
- >>> type(eq) # doctest: +SKIP
- <class 'sympy.polys.domains.pythonrational.PythonRational'>
- Elements from different domains should not be mixed in arithmetic or other
- operations: they should be converted to a common domain first. The domain
- method :py:meth:`~.Domain.unify` is used to find a domain that can
- represent all the elements of two given domains.
- >>> from sympy import ZZ, QQ, symbols
- >>> x, y = symbols('x, y')
- >>> ZZ.unify(QQ)
- QQ
- >>> ZZ[x].unify(QQ)
- QQ[x]
- >>> ZZ[x].unify(QQ[y])
- QQ[x,y]
- If a domain is a :py:class:`~.Ring` then is might have an associated
- :py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and
- :py:meth:`~.Domain.get_ring` methods will find or create the associated
- domain.
- >>> from sympy import ZZ, QQ, Symbol
- >>> x = Symbol('x')
- >>> ZZ.has_assoc_Field
- True
- >>> ZZ.get_field()
- QQ
- >>> QQ.has_assoc_Ring
- True
- >>> QQ.get_ring()
- ZZ
- >>> K = QQ[x]
- >>> K
- QQ[x]
- >>> K.get_field()
- QQ(x)
- See also
- ========
- DomainElement: abstract base class for domain elements
- construct_domain: construct a minimal domain for some expressions
- """
- dtype = None # type: Optional[Type]
- """The type (class) of the elements of this :py:class:`~.Domain`:
- >>> from sympy import ZZ, QQ, Symbol
- >>> ZZ.dtype
- <class 'int'>
- >>> z = ZZ(2)
- >>> z
- 2
- >>> type(z)
- <class 'int'>
- >>> type(z) == ZZ.dtype
- True
- Every domain has an associated **dtype** ("datatype") which is the
- class of the associated domain elements.
- See also
- ========
- of_type
- """
- zero = None # type: Optional[Any]
- """The zero element of the :py:class:`~.Domain`:
- >>> from sympy import QQ
- >>> QQ.zero
- 0
- >>> QQ.of_type(QQ.zero)
- True
- See also
- ========
- of_type
- one
- """
- one = None # type: Optional[Any]
- """The one element of the :py:class:`~.Domain`:
- >>> from sympy import QQ
- >>> QQ.one
- 1
- >>> QQ.of_type(QQ.one)
- True
- See also
- ========
- of_type
- zero
- """
- is_Ring = False
- """Boolean flag indicating if the domain is a :py:class:`~.Ring`.
- >>> from sympy import ZZ
- >>> ZZ.is_Ring
- True
- Basically every :py:class:`~.Domain` represents a ring so this flag is
- not that useful.
- See also
- ========
- is_PID
- is_Field
- get_ring
- has_assoc_Ring
- """
- is_Field = False
- """Boolean flag indicating if the domain is a :py:class:`~.Field`.
- >>> from sympy import ZZ, QQ
- >>> ZZ.is_Field
- False
- >>> QQ.is_Field
- True
- See also
- ========
- is_PID
- is_Ring
- get_field
- has_assoc_Field
- """
- has_assoc_Ring = False
- """Boolean flag indicating if the domain has an associated
- :py:class:`~.Ring`.
- >>> from sympy import QQ
- >>> QQ.has_assoc_Ring
- True
- >>> QQ.get_ring()
- ZZ
- See also
- ========
- is_Field
- get_ring
- """
- has_assoc_Field = False
- """Boolean flag indicating if the domain has an associated
- :py:class:`~.Field`.
- >>> from sympy import ZZ
- >>> ZZ.has_assoc_Field
- True
- >>> ZZ.get_field()
- QQ
- See also
- ========
- is_Field
- get_field
- """
- is_FiniteField = is_FF = False
- is_IntegerRing = is_ZZ = False
- is_RationalField = is_QQ = False
- is_GaussianRing = is_ZZ_I = False
- is_GaussianField = is_QQ_I = False
- is_RealField = is_RR = False
- is_ComplexField = is_CC = False
- is_AlgebraicField = is_Algebraic = False
- is_PolynomialRing = is_Poly = False
- is_FractionField = is_Frac = False
- is_SymbolicDomain = is_EX = False
- is_SymbolicRawDomain = is_EXRAW = False
- is_FiniteExtension = False
- is_Exact = True
- is_Numerical = False
- is_Simple = False
- is_Composite = False
- is_PID = False
- """Boolean flag indicating if the domain is a `principal ideal domain`_.
- >>> from sympy import ZZ
- >>> ZZ.has_assoc_Field
- True
- >>> ZZ.get_field()
- QQ
- .. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain
- See also
- ========
- is_Field
- get_field
- """
- has_CharacteristicZero = False
- rep = None # type: Optional[str]
- alias = None # type: Optional[str]
- def __init__(self):
- raise NotImplementedError
- def __str__(self):
- return self.rep
- def __repr__(self):
- return str(self)
- def __hash__(self):
- return hash((self.__class__.__name__, self.dtype))
- def new(self, *args):
- return self.dtype(*args)
- @property
- def tp(self):
- """Alias for :py:attr:`~.Domain.dtype`"""
- return self.dtype
- def __call__(self, *args):
- """Construct an element of ``self`` domain from ``args``. """
- return self.new(*args)
- def normal(self, *args):
- return self.dtype(*args)
- def convert_from(self, element, base):
- """Convert ``element`` to ``self.dtype`` given the base domain. """
- if base.alias is not None:
- method = "from_" + base.alias
- else:
- method = "from_" + base.__class__.__name__
- _convert = getattr(self, method)
- if _convert is not None:
- result = _convert(element, base)
- if result is not None:
- return result
- raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self))
- def convert(self, element, base=None):
- """Convert ``element`` to ``self.dtype``. """
- if base is not None:
- if _not_a_coeff(element):
- raise CoercionFailed('%s is not in any domain' % element)
- return self.convert_from(element, base)
- if self.of_type(element):
- return element
- if _not_a_coeff(element):
- raise CoercionFailed('%s is not in any domain' % element)
- from sympy.polys.domains import ZZ, QQ, RealField, ComplexField
- if ZZ.of_type(element):
- return self.convert_from(element, ZZ)
- if isinstance(element, int):
- return self.convert_from(ZZ(element), ZZ)
- if HAS_GMPY:
- integers = ZZ
- if isinstance(element, integers.tp):
- return self.convert_from(element, integers)
- rationals = QQ
- if isinstance(element, rationals.tp):
- return self.convert_from(element, rationals)
- if isinstance(element, float):
- parent = RealField(tol=False)
- return self.convert_from(parent(element), parent)
- if isinstance(element, complex):
- parent = ComplexField(tol=False)
- return self.convert_from(parent(element), parent)
- if isinstance(element, DomainElement):
- return self.convert_from(element, element.parent())
- # TODO: implement this in from_ methods
- if self.is_Numerical and getattr(element, 'is_ground', False):
- return self.convert(element.LC())
- if isinstance(element, Basic):
- try:
- return self.from_sympy(element)
- except (TypeError, ValueError):
- pass
- else: # TODO: remove this branch
- if not is_sequence(element):
- try:
- element = sympify(element, strict=True)
- if isinstance(element, Basic):
- return self.from_sympy(element)
- except (TypeError, ValueError):
- pass
- raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self))
- def of_type(self, element):
- """Check if ``a`` is of type ``dtype``. """
- return isinstance(element, self.tp) # XXX: this isn't correct, e.g. PolyElement
- def __contains__(self, a):
- """Check if ``a`` belongs to this domain. """
- try:
- if _not_a_coeff(a):
- raise CoercionFailed
- self.convert(a) # this might raise, too
- except CoercionFailed:
- return False
- return True
- def to_sympy(self, a):
- """Convert domain element *a* to a SymPy expression (Expr).
- Explanation
- ===========
- Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most
- public SymPy functions work with objects of type :py:class:`~.Expr`.
- The elements of a :py:class:`~.Domain` have a different internal
- representation. It is not possible to mix domain elements with
- :py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and
- :py:meth:`~.Domain.from_sympy` methods to convert its domain elements
- to and from :py:class:`~.Expr`.
- Parameters
- ==========
- a: domain element
- An element of this :py:class:`~.Domain`.
- Returns
- =======
- expr: Expr
- A normal SymPy expression of type :py:class:`~.Expr`.
- Examples
- ========
- Construct an element of the :ref:`QQ` domain and then convert it to
- :py:class:`~.Expr`.
- >>> from sympy import QQ, Expr
- >>> q_domain = QQ(2)
- >>> q_domain
- 2
- >>> q_expr = QQ.to_sympy(q_domain)
- >>> q_expr
- 2
- Although the printed forms look similar these objects are not of the
- same type.
- >>> isinstance(q_domain, Expr)
- False
- >>> isinstance(q_expr, Expr)
- True
- Construct an element of :ref:`K[x]` and convert to
- :py:class:`~.Expr`.
- >>> from sympy import Symbol
- >>> x = Symbol('x')
- >>> K = QQ[x]
- >>> x_domain = K.gens[0] # generator x as a domain element
- >>> p_domain = x_domain**2/3 + 1
- >>> p_domain
- 1/3*x**2 + 1
- >>> p_expr = K.to_sympy(p_domain)
- >>> p_expr
- x**2/3 + 1
- The :py:meth:`~.Domain.from_sympy` method is used for the opposite
- conversion from a normal SymPy expression to a domain element.
- >>> p_domain == p_expr
- False
- >>> K.from_sympy(p_expr) == p_domain
- True
- >>> K.to_sympy(p_domain) == p_expr
- True
- >>> K.from_sympy(K.to_sympy(p_domain)) == p_domain
- True
- >>> K.to_sympy(K.from_sympy(p_expr)) == p_expr
- True
- The :py:meth:`~.Domain.from_sympy` method makes it easier to construct
- domain elements interactively.
- >>> from sympy import Symbol
- >>> x = Symbol('x')
- >>> K = QQ[x]
- >>> K.from_sympy(x**2/3 + 1)
- 1/3*x**2 + 1
- See also
- ========
- from_sympy
- convert_from
- """
- raise NotImplementedError
- def from_sympy(self, a):
- """Convert a SymPy expression to an element of this domain.
- Explanation
- ===========
- See :py:meth:`~.Domain.to_sympy` for explanation and examples.
- Parameters
- ==========
- expr: Expr
- A normal SymPy expression of type :py:class:`~.Expr`.
- Returns
- =======
- a: domain element
- An element of this :py:class:`~.Domain`.
- See also
- ========
- to_sympy
- convert_from
- """
- raise NotImplementedError
- def sum(self, args):
- return sum(args)
- def from_FF(K1, a, K0):
- """Convert ``ModularInteger(int)`` to ``dtype``. """
- return None
- def from_FF_python(K1, a, K0):
- """Convert ``ModularInteger(int)`` to ``dtype``. """
- return None
- def from_ZZ_python(K1, a, K0):
- """Convert a Python ``int`` object to ``dtype``. """
- return None
- def from_QQ_python(K1, a, K0):
- """Convert a Python ``Fraction`` object to ``dtype``. """
- return None
- def from_FF_gmpy(K1, a, K0):
- """Convert ``ModularInteger(mpz)`` to ``dtype``. """
- return None
- def from_ZZ_gmpy(K1, a, K0):
- """Convert a GMPY ``mpz`` object to ``dtype``. """
- return None
- def from_QQ_gmpy(K1, a, K0):
- """Convert a GMPY ``mpq`` object to ``dtype``. """
- return None
- def from_RealField(K1, a, K0):
- """Convert a real element object to ``dtype``. """
- return None
- def from_ComplexField(K1, a, K0):
- """Convert a complex element to ``dtype``. """
- return None
- def from_AlgebraicField(K1, a, K0):
- """Convert an algebraic number to ``dtype``. """
- return None
- def from_PolynomialRing(K1, a, K0):
- """Convert a polynomial to ``dtype``. """
- if a.is_ground:
- return K1.convert(a.LC, K0.dom)
- def from_FractionField(K1, a, K0):
- """Convert a rational function to ``dtype``. """
- return None
- def from_MonogenicFiniteExtension(K1, a, K0):
- """Convert an ``ExtensionElement`` to ``dtype``. """
- return K1.convert_from(a.rep, K0.ring)
- def from_ExpressionDomain(K1, a, K0):
- """Convert a ``EX`` object to ``dtype``. """
- return K1.from_sympy(a.ex)
- def from_ExpressionRawDomain(K1, a, K0):
- """Convert a ``EX`` object to ``dtype``. """
- return K1.from_sympy(a)
- def from_GlobalPolynomialRing(K1, a, K0):
- """Convert a polynomial to ``dtype``. """
- if a.degree() <= 0:
- return K1.convert(a.LC(), K0.dom)
- def from_GeneralizedPolynomialRing(K1, a, K0):
- return K1.from_FractionField(a, K0)
- def unify_with_symbols(K0, K1, symbols):
- if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))):
- raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols)))
- return K0.unify(K1)
- def unify(K0, K1, symbols=None):
- """
- Construct a minimal domain that contains elements of ``K0`` and ``K1``.
- Known domains (from smallest to largest):
- - ``GF(p)``
- - ``ZZ``
- - ``QQ``
- - ``RR(prec, tol)``
- - ``CC(prec, tol)``
- - ``ALG(a, b, c)``
- - ``K[x, y, z]``
- - ``K(x, y, z)``
- - ``EX``
- """
- if symbols is not None:
- return K0.unify_with_symbols(K1, symbols)
- if K0 == K1:
- return K0
- if K0.is_EXRAW:
- return K0
- if K1.is_EXRAW:
- return K1
- if K0.is_EX:
- return K0
- if K1.is_EX:
- return K1
- if K0.is_FiniteExtension or K1.is_FiniteExtension:
- if K1.is_FiniteExtension:
- K0, K1 = K1, K0
- if K1.is_FiniteExtension:
- # Unifying two extensions.
- # Try to ensure that K0.unify(K1) == K1.unify(K0)
- if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus:
- K0, K1 = K1, K0
- return K1.set_domain(K0)
- else:
- # Drop the generator from other and unify with the base domain
- K1 = K1.drop(K0.symbol)
- K1 = K0.domain.unify(K1)
- return K0.set_domain(K1)
- if K0.is_Composite or K1.is_Composite:
- K0_ground = K0.dom if K0.is_Composite else K0
- K1_ground = K1.dom if K1.is_Composite else K1
- K0_symbols = K0.symbols if K0.is_Composite else ()
- K1_symbols = K1.symbols if K1.is_Composite else ()
- domain = K0_ground.unify(K1_ground)
- symbols = _unify_gens(K0_symbols, K1_symbols)
- order = K0.order if K0.is_Composite else K1.order
- if ((K0.is_FractionField and K1.is_PolynomialRing or
- K1.is_FractionField and K0.is_PolynomialRing) and
- (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field
- and domain.has_assoc_Ring):
- domain = domain.get_ring()
- if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing):
- cls = K0.__class__
- else:
- cls = K1.__class__
- from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing
- if cls == GlobalPolynomialRing:
- return cls(domain, symbols)
- return cls(domain, symbols, order)
- def mkinexact(cls, K0, K1):
- prec = max(K0.precision, K1.precision)
- tol = max(K0.tolerance, K1.tolerance)
- return cls(prec=prec, tol=tol)
- if K1.is_ComplexField:
- K0, K1 = K1, K0
- if K0.is_ComplexField:
- if K1.is_ComplexField or K1.is_RealField:
- return mkinexact(K0.__class__, K0, K1)
- else:
- return K0
- if K1.is_RealField:
- K0, K1 = K1, K0
- if K0.is_RealField:
- if K1.is_RealField:
- return mkinexact(K0.__class__, K0, K1)
- elif K1.is_GaussianRing or K1.is_GaussianField:
- from sympy.polys.domains.complexfield import ComplexField
- return ComplexField(prec=K0.precision, tol=K0.tolerance)
- else:
- return K0
- if K1.is_AlgebraicField:
- K0, K1 = K1, K0
- if K0.is_AlgebraicField:
- if K1.is_GaussianRing:
- K1 = K1.get_field()
- if K1.is_GaussianField:
- K1 = K1.as_AlgebraicField()
- if K1.is_AlgebraicField:
- return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext))
- else:
- return K0
- if K0.is_GaussianField:
- return K0
- if K1.is_GaussianField:
- return K1
- if K0.is_GaussianRing:
- if K1.is_RationalField:
- K0 = K0.get_field()
- return K0
- if K1.is_GaussianRing:
- if K0.is_RationalField:
- K1 = K1.get_field()
- return K1
- if K0.is_RationalField:
- return K0
- if K1.is_RationalField:
- return K1
- if K0.is_IntegerRing:
- return K0
- if K1.is_IntegerRing:
- return K1
- if K0.is_FiniteField and K1.is_FiniteField:
- return K0.__class__(max(K0.mod, K1.mod, key=default_sort_key))
- from sympy.polys.domains import EX
- return EX
- def __eq__(self, other):
- """Returns ``True`` if two domains are equivalent. """
- return isinstance(other, Domain) and self.dtype == other.dtype
- def __ne__(self, other):
- """Returns ``False`` if two domains are equivalent. """
- return not self == other
- def map(self, seq):
- """Rersively apply ``self`` to all elements of ``seq``. """
- result = []
- for elt in seq:
- if isinstance(elt, list):
- result.append(self.map(elt))
- else:
- result.append(self(elt))
- return result
- def get_ring(self):
- """Returns a ring associated with ``self``. """
- raise DomainError('there is no ring associated with %s' % self)
- def get_field(self):
- """Returns a field associated with ``self``. """
- raise DomainError('there is no field associated with %s' % self)
- def get_exact(self):
- """Returns an exact domain associated with ``self``. """
- return self
- def __getitem__(self, symbols):
- """The mathematical way to make a polynomial ring. """
- if hasattr(symbols, '__iter__'):
- return self.poly_ring(*symbols)
- else:
- return self.poly_ring(symbols)
- def poly_ring(self, *symbols, order=lex):
- """Returns a polynomial ring, i.e. `K[X]`. """
- from sympy.polys.domains.polynomialring import PolynomialRing
- return PolynomialRing(self, symbols, order)
- def frac_field(self, *symbols, order=lex):
- """Returns a fraction field, i.e. `K(X)`. """
- from sympy.polys.domains.fractionfield import FractionField
- return FractionField(self, symbols, order)
- def old_poly_ring(self, *symbols, **kwargs):
- """Returns a polynomial ring, i.e. `K[X]`. """
- from sympy.polys.domains.old_polynomialring import PolynomialRing
- return PolynomialRing(self, *symbols, **kwargs)
- def old_frac_field(self, *symbols, **kwargs):
- """Returns a fraction field, i.e. `K(X)`. """
- from sympy.polys.domains.old_fractionfield import FractionField
- return FractionField(self, *symbols, **kwargs)
- def algebraic_field(self, *extension):
- r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """
- raise DomainError("Cannot create algebraic field over %s" % self)
- def inject(self, *symbols):
- """Inject generators into this domain. """
- raise NotImplementedError
- def drop(self, *symbols):
- """Drop generators from this domain. """
- if self.is_Simple:
- return self
- raise NotImplementedError # pragma: no cover
- def is_zero(self, a):
- """Returns True if ``a`` is zero. """
- return not a
- def is_one(self, a):
- """Returns True if ``a`` is one. """
- return a == self.one
- def is_positive(self, a):
- """Returns True if ``a`` is positive. """
- return a > 0
- def is_negative(self, a):
- """Returns True if ``a`` is negative. """
- return a < 0
- def is_nonpositive(self, a):
- """Returns True if ``a`` is non-positive. """
- return a <= 0
- def is_nonnegative(self, a):
- """Returns True if ``a`` is non-negative. """
- return a >= 0
- def canonical_unit(self, a):
- if self.is_negative(a):
- return -self.one
- else:
- return self.one
- def abs(self, a):
- """Absolute value of ``a``, implies ``__abs__``. """
- return abs(a)
- def neg(self, a):
- """Returns ``a`` negated, implies ``__neg__``. """
- return -a
- def pos(self, a):
- """Returns ``a`` positive, implies ``__pos__``. """
- return +a
- def add(self, a, b):
- """Sum of ``a`` and ``b``, implies ``__add__``. """
- return a + b
- def sub(self, a, b):
- """Difference of ``a`` and ``b``, implies ``__sub__``. """
- return a - b
- def mul(self, a, b):
- """Product of ``a`` and ``b``, implies ``__mul__``. """
- return a * b
- def pow(self, a, b):
- """Raise ``a`` to power ``b``, implies ``__pow__``. """
- return a ** b
- def exquo(self, a, b):
- """Exact quotient of *a* and *b*. Analogue of ``a / b``.
- Explanation
- ===========
- This is essentially the same as ``a / b`` except that an error will be
- raised if the division is inexact (if there is any remainder) and the
- result will always be a domain element. When working in a
- :py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ`
- or :ref:`K[x]`) ``exquo`` should be used instead of ``/``.
- The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does
- not raise an exception) then ``a == b*q``.
- Examples
- ========
- We can use ``K.exquo`` instead of ``/`` for exact division.
- >>> from sympy import ZZ
- >>> ZZ.exquo(ZZ(4), ZZ(2))
- 2
- >>> ZZ.exquo(ZZ(5), ZZ(2))
- Traceback (most recent call last):
- ...
- ExactQuotientFailed: 2 does not divide 5 in ZZ
- Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero
- divisor) is always exact so in that case ``/`` can be used instead of
- :py:meth:`~.Domain.exquo`.
- >>> from sympy import QQ
- >>> QQ.exquo(QQ(5), QQ(2))
- 5/2
- >>> QQ(5) / QQ(2)
- 5/2
- Parameters
- ==========
- a: domain element
- The dividend
- b: domain element
- The divisor
- Returns
- =======
- q: domain element
- The exact quotient
- Raises
- ======
- ExactQuotientFailed: if exact division is not possible.
- ZeroDivisionError: when the divisor is zero.
- See also
- ========
- quo: Analogue of ``a // b``
- rem: Analogue of ``a % b``
- div: Analogue of ``divmod(a, b)``
- Notes
- =====
- Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int``
- (or ``mpz``) division as ``a / b`` should not be used as it would give
- a ``float``.
- >>> ZZ(4) / ZZ(2)
- 2.0
- >>> ZZ(5) / ZZ(2)
- 2.5
- Using ``/`` with :ref:`ZZ` will lead to incorrect results so
- :py:meth:`~.Domain.exquo` should be used instead.
- """
- raise NotImplementedError
- def quo(self, a, b):
- """Quotient of *a* and *b*. Analogue of ``a // b``.
- ``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See
- :py:meth:`~.Domain.div` for more explanation.
- See also
- ========
- rem: Analogue of ``a % b``
- div: Analogue of ``divmod(a, b)``
- exquo: Analogue of ``a / b``
- """
- raise NotImplementedError
- def rem(self, a, b):
- """Modulo division of *a* and *b*. Analogue of ``a % b``.
- ``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See
- :py:meth:`~.Domain.div` for more explanation.
- See also
- ========
- quo: Analogue of ``a // b``
- div: Analogue of ``divmod(a, b)``
- exquo: Analogue of ``a / b``
- """
- raise NotImplementedError
- def div(self, a, b):
- """Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)``
- Explanation
- ===========
- This is essentially the same as ``divmod(a, b)`` except that is more
- consistent when working over some :py:class:`~.Field` domains such as
- :ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the
- :py:meth:`~.Domain.div` method should be used instead of ``divmod``.
- The key invariant is that if ``q, r = K.div(a, b)`` then
- ``a == b*q + r``.
- The result of ``K.div(a, b)`` is the same as the tuple
- ``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and
- remainder are needed then it is more efficient to use
- :py:meth:`~.Domain.div`.
- Examples
- ========
- We can use ``K.div`` instead of ``divmod`` for floor division and
- remainder.
- >>> from sympy import ZZ, QQ
- >>> ZZ.div(ZZ(5), ZZ(2))
- (2, 1)
- If ``K`` is a :py:class:`~.Field` then the division is always exact
- with a remainder of :py:attr:`~.Domain.zero`.
- >>> QQ.div(QQ(5), QQ(2))
- (5/2, 0)
- Parameters
- ==========
- a: domain element
- The dividend
- b: domain element
- The divisor
- Returns
- =======
- (q, r): tuple of domain elements
- The quotient and remainder
- Raises
- ======
- ZeroDivisionError: when the divisor is zero.
- See also
- ========
- quo: Analogue of ``a // b``
- rem: Analogue of ``a % b``
- exquo: Analogue of ``a / b``
- Notes
- =====
- If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as
- the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type
- defines ``divmod`` in a way that is undesirable so
- :py:meth:`~.Domain.div` should be used instead of ``divmod``.
- >>> a = QQ(1)
- >>> b = QQ(3, 2)
- >>> a # doctest: +SKIP
- mpq(1,1)
- >>> b # doctest: +SKIP
- mpq(3,2)
- >>> divmod(a, b) # doctest: +SKIP
- (mpz(0), mpq(1,1))
- >>> QQ.div(a, b) # doctest: +SKIP
- (mpq(2,3), mpq(0,1))
- Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so
- :py:meth:`~.Domain.div` should be used instead.
- """
- raise NotImplementedError
- def invert(self, a, b):
- """Returns inversion of ``a mod b``, implies something. """
- raise NotImplementedError
- def revert(self, a):
- """Returns ``a**(-1)`` if possible. """
- raise NotImplementedError
- def numer(self, a):
- """Returns numerator of ``a``. """
- raise NotImplementedError
- def denom(self, a):
- """Returns denominator of ``a``. """
- raise NotImplementedError
- def half_gcdex(self, a, b):
- """Half extended GCD of ``a`` and ``b``. """
- s, t, h = self.gcdex(a, b)
- return s, h
- def gcdex(self, a, b):
- """Extended GCD of ``a`` and ``b``. """
- raise NotImplementedError
- def cofactors(self, a, b):
- """Returns GCD and cofactors of ``a`` and ``b``. """
- gcd = self.gcd(a, b)
- cfa = self.quo(a, gcd)
- cfb = self.quo(b, gcd)
- return gcd, cfa, cfb
- def gcd(self, a, b):
- """Returns GCD of ``a`` and ``b``. """
- raise NotImplementedError
- def lcm(self, a, b):
- """Returns LCM of ``a`` and ``b``. """
- raise NotImplementedError
- def log(self, a, b):
- """Returns b-base logarithm of ``a``. """
- raise NotImplementedError
- def sqrt(self, a):
- """Returns square root of ``a``. """
- raise NotImplementedError
- def evalf(self, a, prec=None, **options):
- """Returns numerical approximation of ``a``. """
- return self.to_sympy(a).evalf(prec, **options)
- n = evalf
- def real(self, a):
- return a
- def imag(self, a):
- return self.zero
- def almosteq(self, a, b, tolerance=None):
- """Check if ``a`` and ``b`` are almost equal. """
- return a == b
- def characteristic(self):
- """Return the characteristic of this domain. """
- raise NotImplementedError('characteristic()')
- __all__ = ['Domain']
|