I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,57 @@
"""Implementation of mathematical domains. """
__all__ = [
'Domain', 'FiniteField', 'IntegerRing', 'RationalField', 'RealField',
'ComplexField', 'AlgebraicField', 'PolynomialRing', 'FractionField',
'ExpressionDomain', 'PythonRational',
'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW',
]
from .domain import Domain
from .finitefield import FiniteField, FF, GF
from .integerring import IntegerRing, ZZ
from .rationalfield import RationalField, QQ
from .algebraicfield import AlgebraicField
from .gaussiandomains import ZZ_I, QQ_I
from .realfield import RealField, RR
from .complexfield import ComplexField, CC
from .polynomialring import PolynomialRing
from .fractionfield import FractionField
from .expressiondomain import ExpressionDomain, EX
from .expressionrawdomain import EXRAW
from .pythonrational import PythonRational
# This is imported purely for backwards compatibility because some parts of
# the codebase used to import this from here and it's possible that downstream
# does as well:
from sympy.external.gmpy import GROUND_TYPES # noqa: F401
#
# The rest of these are obsolete and provided only for backwards
# compatibility:
#
from .pythonfinitefield import PythonFiniteField
from .gmpyfinitefield import GMPYFiniteField
from .pythonintegerring import PythonIntegerRing
from .gmpyintegerring import GMPYIntegerRing
from .pythonrationalfield import PythonRationalField
from .gmpyrationalfield import GMPYRationalField
FF_python = PythonFiniteField
FF_gmpy = GMPYFiniteField
ZZ_python = PythonIntegerRing
ZZ_gmpy = GMPYIntegerRing
QQ_python = PythonRationalField
QQ_gmpy = GMPYRationalField
__all__.extend((
'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing',
'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField',
'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy',
))

View File

@ -0,0 +1,607 @@
"""Implementation of :class:`AlgebraicField` class. """
from sympy.core.add import Add
from sympy.core.mul import Mul
from sympy.core.singleton import S
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyclasses import ANP
from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
from sympy.utilities import public
@public
class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
r"""Algebraic number field :ref:`QQ(a)`
A :ref:`QQ(a)` domain represents an `algebraic number field`_
`\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
:ref:`polys-domainsintro`).
A :py:class:`~.Poly` created from an expression involving `algebraic
numbers`_ will treat the algebraic numbers as generators if the generators
argument is not specified.
>>> from sympy import Poly, Symbol, sqrt
>>> x = Symbol('x')
>>> Poly(x**2 + sqrt(2))
Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
generators (variables). If the generators are explicitly specified then
``sqrt(2)`` will be considered to be a coefficient but by default the
:ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
domain the argument ``extension=True`` can be given.
>>> Poly(x**2 + sqrt(2), x)
Poly(x**2 + sqrt(2), x, domain='EX')
>>> Poly(x**2 + sqrt(2), x, extension=True)
Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
A generator of the algebraic field extension can also be specified
explicitly which is particularly useful if the coefficients are all
rational but an extension field is needed (e.g. to factor the
polynomial).
>>> Poly(x**2 + 1)
Poly(x**2 + 1, x, domain='ZZ')
>>> Poly(x**2 + 1, extension=sqrt(2))
Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
explicitly.
>>> from sympy import factor, QQ
>>> factor(x**2 - 2)
x**2 - 2
>>> factor(x**2 - 2, extension=sqrt(2))
(x - sqrt(2))*(x + sqrt(2))
>>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
(x - sqrt(2))*(x + sqrt(2))
>>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
(x - sqrt(2))*(x + sqrt(2))
The ``extension=True`` argument can be used but will only create an
extension that contains the coefficients which is usually not enough to
factorise the polynomial.
>>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
>>> factor(p) # treats sqrt(2) as a symbol
(x + sqrt(2))*(x**2 - 2)
>>> factor(p, extension=True)
(x - sqrt(2))*(x + sqrt(2))**2
>>> factor(x**2 - 2, extension=True) # all rational coefficients
x**2 - 2
It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
and :py:func:`~.gcd` functions.
>>> from sympy import cancel, gcd
>>> cancel((x**2 - 2)/(x - sqrt(2)))
(x**2 - 2)/(x - sqrt(2))
>>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
x + sqrt(2)
>>> gcd(x**2 - 2, x - sqrt(2))
1
>>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
x - sqrt(2)
When using the domain directly :ref:`QQ(a)` can be used as a constructor
to create instances which then support the operations ``+,-,*,**,/``. The
:py:meth:`~.Domain.algebraic_field` method is used to construct a
particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
can be used to create domain elements from normal SymPy expressions.
>>> K = QQ.algebraic_field(sqrt(2))
>>> K
QQ<sqrt(2)>
>>> xk = K.from_sympy(3 + 4*sqrt(2))
>>> xk # doctest: +SKIP
ANP([4, 3], [1, 0, -2], QQ)
Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
limited printing support. The raw display shows the internal
representation of the element as the list ``[4, 3]`` representing the
coefficients of ``1`` and ``sqrt(2)`` for this element in the form
``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
:py:meth:`~.Domain.to_sympy` to get a better printed form for the
elements and to see the results of operations.
>>> xk = K.from_sympy(3 + 4*sqrt(2))
>>> yk = K.from_sympy(2 + 3*sqrt(2))
>>> xk * yk # doctest: +SKIP
ANP([17, 30], [1, 0, -2], QQ)
>>> K.to_sympy(xk * yk)
17*sqrt(2) + 30
>>> K.to_sympy(xk + yk)
5 + 7*sqrt(2)
>>> K.to_sympy(xk ** 2)
24*sqrt(2) + 41
>>> K.to_sympy(xk / yk)
sqrt(2)/14 + 9/7
Any expression representing an algebraic number can be used to generate
a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
The function :py:func:`~.minpoly` function is used for this.
>>> from sympy import exp, I, pi, minpoly
>>> g = exp(2*I*pi/3)
>>> g
exp(2*I*pi/3)
>>> g.is_algebraic
True
>>> minpoly(g, x)
x**2 + x + 1
>>> factor(x**3 - 1, extension=g)
(x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
It is also possible to make an algebraic field from multiple extension
elements.
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
>>> K
QQ<sqrt(2) + sqrt(3)>
>>> p = x**4 - 5*x**2 + 6
>>> factor(p)
(x**2 - 3)*(x**2 - 2)
>>> factor(p, domain=K)
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
>>> factor(p, extension=[sqrt(2), sqrt(3)])
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
Multiple extension elements are always combined together to make a single
`primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
element is computed using the :py:func:`~.primitive_element` function.
>>> from sympy import primitive_element
>>> primitive_element([sqrt(2), sqrt(3)], x)
(x**4 - 10*x**2 + 1, [1, 1])
>>> minpoly(sqrt(2) + sqrt(3), x)
x**4 - 10*x**2 + 1
The extension elements that generate the domain can be accessed from the
domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
the primitive element as a :py:class:`~.DMP` instance is available as
:py:attr:`~.mod`.
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
>>> K
QQ<sqrt(2) + sqrt(3)>
>>> K.ext
sqrt(2) + sqrt(3)
>>> K.orig_ext
(sqrt(2), sqrt(3))
>>> K.mod # doctest: +SKIP
DMP_Python([1, 0, -10, 0, 1], QQ)
The `discriminant`_ of the field can be obtained from the
:py:meth:`~.discriminant` method, and an `integral basis`_ from the
:py:meth:`~.integral_basis` method. The latter returns a list of
:py:class:`~.ANP` instances by default, but can be made to return instances
of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
argument. The maximal order, or ring of integers, of the field can also be
obtained from the :py:meth:`~.maximal_order` method, as a
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
>>> zeta5 = exp(2*I*pi/5)
>>> K = QQ.algebraic_field(zeta5)
>>> K
QQ<exp(2*I*pi/5)>
>>> K.discriminant()
125
>>> K = QQ.algebraic_field(sqrt(5))
>>> K
QQ<sqrt(5)>
>>> K.integral_basis(fmt='sympy')
[1, 1/2 + sqrt(5)/2]
>>> K.maximal_order()
Submodule[[2, 0], [1, 1]]/2
The factorization of a rational prime into prime ideals of the field is
computed by the :py:meth:`~.primes_above` method, which returns a list
of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
>>> zeta7 = exp(2*I*pi/7)
>>> K = QQ.algebraic_field(zeta7)
>>> K
QQ<exp(2*I*pi/7)>
>>> K.primes_above(11)
[(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)]
The Galois group of the Galois closure of the field can be computed (when
the minimal polynomial of the field is of sufficiently small degree).
>>> K.galois_group(by_name=True)[0]
S6TransitiveSubgroups.C6
Notes
=====
It is not currently possible to generate an algebraic extension over any
domain other than :ref:`QQ`. Ideally it would be possible to generate
extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
implementations of this kind of quotient ring/extension in the
:py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
classes. Each of those implementations needs some work to make them fully
usable though.
.. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
.. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
.. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
.. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
.. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
.. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
"""
dtype = ANP
is_AlgebraicField = is_Algebraic = True
is_Numerical = True
has_assoc_Ring = False
has_assoc_Field = True
def __init__(self, dom, *ext, alias=None):
r"""
Parameters
==========
dom : :py:class:`~.Domain`
The base field over which this is an extension field.
Currently only :ref:`QQ` is accepted.
*ext : One or more :py:class:`~.Expr`
Generators of the extension. These should be expressions that are
algebraic over `\mathbb{Q}`.
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
If provided, this will be used as the alias symbol for the
primitive element of the :py:class:`~.AlgebraicField`.
If ``None``, while ``ext`` consists of exactly one
:py:class:`~.AlgebraicNumber`, its alias (if any) will be used.
"""
if not dom.is_QQ:
raise DomainError("ground domain must be a rational field")
from sympy.polys.numberfields import to_number_field
if len(ext) == 1 and isinstance(ext[0], tuple):
orig_ext = ext[0][1:]
else:
orig_ext = ext
if alias is None and len(ext) == 1:
alias = getattr(ext[0], 'alias', None)
self.orig_ext = orig_ext
"""
Original elements given to generate the extension.
>>> from sympy import QQ, sqrt
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
>>> K.orig_ext
(sqrt(2), sqrt(3))
"""
self.ext = to_number_field(ext, alias=alias)
"""
Primitive element used for the extension.
>>> from sympy import QQ, sqrt
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
>>> K.ext
sqrt(2) + sqrt(3)
"""
self.mod = self.ext.minpoly.rep
"""
Minimal polynomial for the primitive element of the extension.
>>> from sympy import QQ, sqrt
>>> K = QQ.algebraic_field(sqrt(2))
>>> K.mod
DMP([1, 0, -2], QQ)
"""
self.domain = self.dom = dom
self.ngens = 1
self.symbols = self.gens = (self.ext,)
self.unit = self([dom(1), dom(0)])
self.zero = self.dtype.zero(self.mod.to_list(), dom)
self.one = self.dtype.one(self.mod.to_list(), dom)
self._maximal_order = None
self._discriminant = None
self._nilradicals_mod_p = {}
def new(self, element):
return self.dtype(element, self.mod.to_list(), self.dom)
def __str__(self):
return str(self.dom) + '<' + str(self.ext) + '>'
def __hash__(self):
return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
if isinstance(other, AlgebraicField):
return self.dtype == other.dtype and self.ext == other.ext
else:
return NotImplemented
def algebraic_field(self, *extension, alias=None):
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias)
def to_alg_num(self, a):
"""Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
return self.ext.field_element(a)
def to_sympy(self, a):
"""Convert ``a`` of ``dtype`` to a SymPy object. """
# Precompute a converter to be reused:
if not hasattr(self, '_converter'):
self._converter = _make_converter(self)
return self._converter(a)
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
try:
return self([self.dom.from_sympy(a)])
except CoercionFailed:
pass
from sympy.polys.numberfields import to_number_field
try:
return self(to_number_field(a, self.ext).native_coeffs())
except (NotAlgebraic, IsomorphismFailed):
raise CoercionFailed(
"%s is not a valid algebraic number in %s" % (a, self))
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_QQ(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def get_ring(self):
"""Returns a ring associated with ``self``. """
raise DomainError('there is no ring associated with %s' % self)
def is_positive(self, a):
"""Returns True if ``a`` is positive. """
return self.dom.is_positive(a.LC())
def is_negative(self, a):
"""Returns True if ``a`` is negative. """
return self.dom.is_negative(a.LC())
def is_nonpositive(self, a):
"""Returns True if ``a`` is non-positive. """
return self.dom.is_nonpositive(a.LC())
def is_nonnegative(self, a):
"""Returns True if ``a`` is non-negative. """
return self.dom.is_nonnegative(a.LC())
def numer(self, a):
"""Returns numerator of ``a``. """
return a
def denom(self, a):
"""Returns denominator of ``a``. """
return self.one
def from_AlgebraicField(K1, a, K0):
"""Convert AlgebraicField element 'a' to another AlgebraicField """
return K1.from_sympy(K0.to_sympy(a))
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a GaussianInteger element 'a' to ``dtype``. """
return K1.from_sympy(K0.to_sympy(a))
def from_GaussianRationalField(K1, a, K0):
"""Convert a GaussianRational element 'a' to ``dtype``. """
return K1.from_sympy(K0.to_sympy(a))
def _do_round_two(self):
from sympy.polys.numberfields.basis import round_two
ZK, dK = round_two(self, radicals=self._nilradicals_mod_p)
self._maximal_order = ZK
self._discriminant = dK
def maximal_order(self):
"""
Compute the maximal order, or ring of integers, of the field.
Returns
=======
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
See Also
========
integral_basis
"""
if self._maximal_order is None:
self._do_round_two()
return self._maximal_order
def integral_basis(self, fmt=None):
r"""
Get an integral basis for the field.
Parameters
==========
fmt : str, None, optional (default=None)
If ``None``, return a list of :py:class:`~.ANP` instances.
If ``"sympy"``, convert each element of the list to an
:py:class:`~.Expr`, using ``self.to_sympy()``.
If ``"alg"``, convert each element of the list to an
:py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
Examples
========
>>> from sympy import QQ, AlgebraicNumber, sqrt
>>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
>>> k = QQ.algebraic_field(alpha)
>>> B0 = k.integral_basis()
>>> B1 = k.integral_basis(fmt='sympy')
>>> B2 = k.integral_basis(fmt='alg')
>>> print(B0[1]) # doctest: +SKIP
ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
>>> print(B1[1])
1/2 + alpha/2
>>> print(B2[1])
alpha/2 + 1/2
In the last two cases we get legible expressions, which print somewhat
differently because of the different types involved:
>>> print(type(B1[1]))
<class 'sympy.core.add.Add'>
>>> print(type(B2[1]))
<class 'sympy.core.numbers.AlgebraicNumber'>
See Also
========
to_sympy
to_alg_num
maximal_order
"""
ZK = self.maximal_order()
M = ZK.QQ_matrix
n = M.shape[1]
B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
if fmt == 'sympy':
return [self.to_sympy(b) for b in B]
elif fmt == 'alg':
return [self.to_alg_num(b) for b in B]
return B
def discriminant(self):
"""Get the discriminant of the field."""
if self._discriminant is None:
self._do_round_two()
return self._discriminant
def primes_above(self, p):
"""Compute the prime ideals lying above a given rational prime *p*."""
from sympy.polys.numberfields.primes import prime_decomp
ZK = self.maximal_order()
dK = self.discriminant()
rad = self._nilradicals_mod_p.get(p)
return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
def galois_group(self, by_name=False, max_tries=30, randomize=False):
"""
Compute the Galois group of the Galois closure of this field.
Examples
========
If the field is Galois, the order of the group will equal the degree
of the field:
>>> from sympy import QQ
>>> from sympy.abc import x
>>> k = QQ.alg_field_from_poly(x**4 + 1)
>>> G, _ = k.galois_group()
>>> G.order()
4
If the field is not Galois, then its Galois closure is a proper
extension, and the order of the Galois group will be greater than the
degree of the field:
>>> k = QQ.alg_field_from_poly(x**4 - 2)
>>> G, _ = k.galois_group()
>>> G.order()
8
See Also
========
sympy.polys.numberfields.galoisgroups.galois_group
"""
return self.ext.minpoly_of_element().galois_group(
by_name=by_name, max_tries=max_tries, randomize=randomize)
def _make_converter(K):
"""Construct the converter to convert back to Expr"""
# Precompute the effect of converting to SymPy and expanding expressions
# like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
# conversion from K to Expr is slow. Here we compute the expansions for
# each power of the generator and collect together the resulting algebraic
# terms and the rational coefficients into a matrix.
gen = K.ext.as_expr()
todom = K.dom.from_sympy
# We'll let Expr compute the expansions. We won't make any presumptions
# about what this results in except that it is QQ-linear in some terms
# that we will call algebraics. The final result will be expressed in
# terms of those.
powers = [S.One, gen]
for n in range(2, K.mod.degree()):
powers.append((gen * powers[-1]).expand())
# Collect the rational coefficients and algebraic Expr that can
# map the ANP coefficients into an expanded SymPy expression
terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
algebraics = set().union(*terms)
matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
# Create a function to do the conversion efficiently:
def converter(a):
"""Convert a to Expr using converter"""
ai = a.to_list()[::-1]
tosympy = K.dom.to_sympy
coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
coeffs_sympy = [tosympy(c) for c in coeffs_dom]
res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
return res
return converter

View File

@ -0,0 +1,15 @@
"""Implementation of :class:`CharacteristicZero` class. """
from sympy.polys.domains.domain import Domain
from sympy.utilities import public
@public
class CharacteristicZero(Domain):
"""Domain that has infinite number of elements. """
has_CharacteristicZero = True
def characteristic(self):
"""Return the characteristic of this domain. """
return 0

View File

@ -0,0 +1,185 @@
"""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.mpelements import MPContext
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyerrors import DomainError, CoercionFailed
from sympy.utilities import public
@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._context.tolerance
def __init__(self, prec=_default_precision, dps=None, tol=None):
context = MPContext(prec, dps, tol, False)
context._parent = self
self._context = context
self._dtype = context.mpc
self.zero = self.dtype(0)
self.one = self.dtype(1)
@property
def tp(self):
# XXX: Domain treats tp as an alis of dtype. Here we need to 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
and self.tolerance == other.tolerance)
def __hash__(self):
return hash((self.__class__.__name__, self._dtype, self.precision, self.tolerance))
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):
if self == base:
return element
else:
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()

View File

@ -0,0 +1,52 @@
"""Implementation of :class:`CompositeDomain` class. """
from sympy.polys.domains.domain import Domain
from sympy.polys.polyerrors import GeneratorsError
from sympy.utilities import public
@public
class CompositeDomain(Domain):
"""Base class for composite domains, e.g. ZZ[x], ZZ(X). """
is_Composite = True
gens, ngens, symbols, domain = [None]*4
def inject(self, *symbols):
"""Inject generators into this domain. """
if not (set(self.symbols) & set(symbols)):
return self.__class__(self.domain, self.symbols + symbols, self.order)
else:
raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols))
def drop(self, *symbols):
"""Drop generators from this domain. """
symset = set(symbols)
newsyms = tuple(s for s in self.symbols if s not in symset)
domain = self.domain.drop(*symbols)
if not newsyms:
return domain
else:
return self.__class__(domain, newsyms, self.order)
def set_domain(self, domain):
"""Set the ground domain of this domain. """
return self.__class__(domain, self.symbols, self.order)
@property
def is_Exact(self):
"""Returns ``True`` if this domain is exact. """
return self.domain.is_Exact
def get_exact(self):
"""Returns an exact version of this domain. """
return self.set_domain(self.domain.get_exact())
@property
def has_CharacteristicZero(self):
return self.domain.has_CharacteristicZero
def characteristic(self):
return self.domain.characteristic()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
"""Trait for implementing domain elements. """
from sympy.utilities import public
@public
class DomainElement:
"""
Represents an element of a domain.
Mix in this trait into a class whose instances should be recognized as
elements of a domain. Method ``parent()`` gives that domain.
"""
__slots__ = ()
def parent(self):
"""Get the domain associated with ``self``
Examples
========
>>> from sympy import ZZ, symbols
>>> x, y = symbols('x, y')
>>> K = ZZ[x,y]
>>> p = K(x)**2 + K(y)**2
>>> p
x**2 + y**2
>>> p.parent()
ZZ[x,y]
Notes
=====
This is used by :py:meth:`~.Domain.convert` to identify the domain
associated with a domain element.
"""
raise NotImplementedError("abstract method")

View File

@ -0,0 +1,278 @@
"""Implementation of :class:`ExpressionDomain` class. """
from sympy.core import sympify, SympifyError
from sympy.polys.domains.domainelement import DomainElement
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyutils import PicklableWithSlots
from sympy.utilities import public
eflags = {"deep": False, "mul": True, "power_exp": False, "power_base": False,
"basic": False, "multinomial": False, "log": False}
@public
class ExpressionDomain(Field, CharacteristicZero, SimpleDomain):
"""A class for arbitrary expressions. """
is_SymbolicDomain = is_EX = True
class Expression(DomainElement, PicklableWithSlots):
"""An arbitrary expression. """
__slots__ = ('ex',)
def __init__(self, ex):
if not isinstance(ex, self.__class__):
self.ex = sympify(ex)
else:
self.ex = ex.ex
def __repr__(f):
return 'EX(%s)' % repr(f.ex)
def __str__(f):
return 'EX(%s)' % str(f.ex)
def __hash__(self):
return hash((self.__class__.__name__, self.ex))
def parent(self):
return EX
def as_expr(f):
return f.ex
def numer(f):
return f.__class__(f.ex.as_numer_denom()[0])
def denom(f):
return f.__class__(f.ex.as_numer_denom()[1])
def simplify(f, ex):
return f.__class__(ex.cancel().expand(**eflags))
def __abs__(f):
return f.__class__(abs(f.ex))
def __neg__(f):
return f.__class__(-f.ex)
def _to_ex(f, g):
try:
return f.__class__(g)
except SympifyError:
return None
def __lt__(f, g):
return f.ex.sort_key() < g.ex.sort_key()
def __add__(f, g):
g = f._to_ex(g)
if g is None:
return NotImplemented
elif g == EX.zero:
return f
elif f == EX.zero:
return g
else:
return f.simplify(f.ex + g.ex)
def __radd__(f, g):
return f.simplify(f.__class__(g).ex + f.ex)
def __sub__(f, g):
g = f._to_ex(g)
if g is None:
return NotImplemented
elif g == EX.zero:
return f
elif f == EX.zero:
return -g
else:
return f.simplify(f.ex - g.ex)
def __rsub__(f, g):
return f.simplify(f.__class__(g).ex - f.ex)
def __mul__(f, g):
g = f._to_ex(g)
if g is None:
return NotImplemented
if EX.zero in (f, g):
return EX.zero
elif f.ex.is_Number and g.ex.is_Number:
return f.__class__(f.ex*g.ex)
return f.simplify(f.ex*g.ex)
def __rmul__(f, g):
return f.simplify(f.__class__(g).ex*f.ex)
def __pow__(f, n):
n = f._to_ex(n)
if n is not None:
return f.simplify(f.ex**n.ex)
else:
return NotImplemented
def __truediv__(f, g):
g = f._to_ex(g)
if g is not None:
return f.simplify(f.ex/g.ex)
else:
return NotImplemented
def __rtruediv__(f, g):
return f.simplify(f.__class__(g).ex/f.ex)
def __eq__(f, g):
return f.ex == f.__class__(g).ex
def __ne__(f, g):
return not f == g
def __bool__(f):
return not f.ex.is_zero
def gcd(f, g):
from sympy.polys import gcd
return f.__class__(gcd(f.ex, f.__class__(g).ex))
def lcm(f, g):
from sympy.polys import lcm
return f.__class__(lcm(f.ex, f.__class__(g).ex))
dtype = Expression
zero = Expression(0)
one = Expression(1)
rep = 'EX'
has_assoc_Ring = False
has_assoc_Field = True
def __init__(self):
pass
def __eq__(self, other):
if isinstance(other, ExpressionDomain):
return True
else:
return NotImplemented
def __hash__(self):
return hash("EX")
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return a.as_expr()
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
return self.dtype(a)
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_QQ(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a ``GaussianRational`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_GaussianRationalField(K1, a, K0):
"""Convert a ``GaussianRational`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_AlgebraicField(K1, a, K0):
"""Convert an ``ANP`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_ComplexField(K1, a, K0):
"""Convert a mpmath ``mpc`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_PolynomialRing(K1, a, K0):
"""Convert a ``DMP`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_FractionField(K1, a, K0):
"""Convert a ``DMF`` object to ``dtype``. """
return K1(K0.to_sympy(a))
def from_ExpressionDomain(K1, a, K0):
"""Convert a ``EX`` object to ``dtype``. """
return a
def get_ring(self):
"""Returns a ring associated with ``self``. """
return self # XXX: EX is not a ring but we don't have much choice here.
def get_field(self):
"""Returns a field associated with ``self``. """
return self
def is_positive(self, a):
"""Returns True if ``a`` is positive. """
return a.ex.as_coeff_mul()[0].is_positive
def is_negative(self, a):
"""Returns True if ``a`` is negative. """
return a.ex.could_extract_minus_sign()
def is_nonpositive(self, a):
"""Returns True if ``a`` is non-positive. """
return a.ex.as_coeff_mul()[0].is_nonpositive
def is_nonnegative(self, a):
"""Returns True if ``a`` is non-negative. """
return a.ex.as_coeff_mul()[0].is_nonnegative
def numer(self, a):
"""Returns numerator of ``a``. """
return a.numer()
def denom(self, a):
"""Returns denominator of ``a``. """
return a.denom()
def gcd(self, a, b):
return self(1)
def lcm(self, a, b):
return a.lcm(b)
EX = ExpressionDomain()

View File

@ -0,0 +1,57 @@
"""Implementation of :class:`ExpressionRawDomain` class. """
from sympy.core import Expr, S, sympify, Add
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class ExpressionRawDomain(Field, CharacteristicZero, SimpleDomain):
"""A class for arbitrary expressions but without automatic simplification. """
is_SymbolicRawDomain = is_EXRAW = True
dtype = Expr
zero = S.Zero
one = S.One
rep = 'EXRAW'
has_assoc_Ring = False
has_assoc_Field = True
def __init__(self):
pass
@classmethod
def new(self, a):
return sympify(a)
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return a
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
if not isinstance(a, Expr):
raise CoercionFailed(f"Expecting an Expr instance but found: {type(a).__name__}")
return a
def convert_from(self, a, K):
"""Convert a domain element from another domain to EXRAW"""
return K.to_sympy(a)
def get_field(self):
"""Returns a field associated with ``self``. """
return self
def sum(self, items):
return Add(*items)
EXRAW = ExpressionRawDomain()

View File

@ -0,0 +1,104 @@
"""Implementation of :class:`Field` class. """
from sympy.polys.domains.ring import Ring
from sympy.polys.polyerrors import NotReversible, DomainError
from sympy.utilities import public
@public
class Field(Ring):
"""Represents a field domain. """
is_Field = True
is_PID = True
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``. """
return self
def exquo(self, a, b):
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
return a / b
def quo(self, a, b):
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
return a / b
def rem(self, a, b):
"""Remainder of ``a`` and ``b``, implies nothing. """
return self.zero
def div(self, a, b):
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
return a / b, self.zero
def gcd(self, a, b):
"""
Returns GCD of ``a`` and ``b``.
This definition of GCD over fields allows to clear denominators
in `primitive()`.
Examples
========
>>> from sympy.polys.domains import QQ
>>> from sympy import S, gcd, primitive
>>> from sympy.abc import x
>>> QQ.gcd(QQ(2, 3), QQ(4, 9))
2/9
>>> gcd(S(2)/3, S(4)/9)
2/9
>>> primitive(2*x/3 + S(4)/9)
(2/9, 3*x + 2)
"""
try:
ring = self.get_ring()
except DomainError:
return self.one
p = ring.gcd(self.numer(a), self.numer(b))
q = ring.lcm(self.denom(a), self.denom(b))
return self.convert(p, ring)/q
def lcm(self, a, b):
"""
Returns LCM of ``a`` and ``b``.
>>> from sympy.polys.domains import QQ
>>> from sympy import S, lcm
>>> QQ.lcm(QQ(2, 3), QQ(4, 9))
4/3
>>> lcm(S(2)/3, S(4)/9)
4/3
"""
try:
ring = self.get_ring()
except DomainError:
return a*b
p = ring.lcm(self.numer(a), self.numer(b))
q = ring.gcd(self.denom(a), self.denom(b))
return self.convert(p, ring)/q
def revert(self, a):
"""Returns ``a**(-1)`` if possible. """
if a:
return 1/a
else:
raise NotReversible('zero is not reversible')
def is_unit(self, a):
"""Return true if ``a`` is a invertible"""
return bool(a)

View File

@ -0,0 +1,328 @@
"""Implementation of :class:`FiniteField` class. """
import operator
from sympy.external.gmpy import GROUND_TYPES
from sympy.utilities.decorator import doctest_depends_on
from sympy.core.numbers import int_valued
from sympy.polys.domains.field import Field
from sympy.polys.domains.modularinteger import ModularIntegerFactory
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.galoistools import gf_zassenhaus, gf_irred_p_rabin
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
from sympy.polys.domains.groundtypes import SymPyInteger
if GROUND_TYPES == 'flint':
__doctest_skip__ = ['FiniteField']
if GROUND_TYPES == 'flint':
import flint
# Don't use python-flint < 0.5.0 because nmod was missing some features in
# previous versions of python-flint and fmpz_mod was not yet added.
_major, _minor, *_ = flint.__version__.split('.')
if (int(_major), int(_minor)) < (0, 5):
flint = None
else:
flint = None
def _modular_int_factory(mod, dom, symmetric, self):
# Use flint if available
if flint is not None:
nmod = flint.nmod
fmpz_mod_ctx = flint.fmpz_mod_ctx
index = operator.index
try:
mod = dom.convert(mod)
except CoercionFailed:
raise ValueError('modulus must be an integer, got %s' % mod)
# mod might be e.g. Integer
try:
fmpz_mod_ctx(mod)
except TypeError:
mod = index(mod)
# flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine)
try:
nmod(0, mod)
except OverflowError:
# Use fmpz_mod
fctx = fmpz_mod_ctx(mod)
def ctx(x):
try:
return fctx(x)
except TypeError:
# x might be Integer
return fctx(index(x))
else:
# Use nmod
def ctx(x):
try:
return nmod(x, mod)
except TypeError:
return nmod(index(x), mod)
return ctx
# Use the Python implementation
return ModularIntegerFactory(mod, dom, symmetric, self)
@public
@doctest_depends_on(modules=['python', 'gmpy'])
class FiniteField(Field, SimpleDomain):
r"""Finite field of prime order :ref:`GF(p)`
A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime
order as :py:class:`~.Domain` in the domain system (see
:ref:`polys-domainsintro`).
A :py:class:`~.Poly` created from an expression with integer
coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p``
option is given then the domain will be a finite field instead.
>>> from sympy import Poly, Symbol
>>> x = Symbol('x')
>>> p = Poly(x**2 + 1)
>>> p
Poly(x**2 + 1, x, domain='ZZ')
>>> p.domain
ZZ
>>> p2 = Poly(x**2 + 1, modulus=2)
>>> p2
Poly(x**2 + 1, x, modulus=2)
>>> p2.domain
GF(2)
It is possible to factorise a polynomial over :ref:`GF(p)` using the
modulus argument to :py:func:`~.factor` or by specifying the domain
explicitly. The domain can also be given as a string.
>>> from sympy import factor, GF
>>> factor(x**2 + 1)
x**2 + 1
>>> factor(x**2 + 1, modulus=2)
(x + 1)**2
>>> factor(x**2 + 1, domain=GF(2))
(x + 1)**2
>>> factor(x**2 + 1, domain='GF(2)')
(x + 1)**2
It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel`
and :py:func:`~.gcd` functions.
>>> from sympy import cancel, gcd
>>> cancel((x**2 + 1)/(x + 1))
(x**2 + 1)/(x + 1)
>>> cancel((x**2 + 1)/(x + 1), domain=GF(2))
x + 1
>>> gcd(x**2 + 1, x + 1)
1
>>> gcd(x**2 + 1, x + 1, domain=GF(2))
x + 1
When using the domain directly :ref:`GF(p)` can be used as a constructor
to create instances which then support the operations ``+,-,*,**,/``
>>> from sympy import GF
>>> K = GF(5)
>>> K
GF(5)
>>> x = K(3)
>>> y = K(2)
>>> x
3 mod 5
>>> y
2 mod 5
>>> x * y
1 mod 5
>>> x / y
4 mod 5
Notes
=====
It is also possible to create a :ref:`GF(p)` domain of **non-prime**
order but the resulting ring is **not** a field: it is just the ring of
the integers modulo ``n``.
>>> K = GF(9)
>>> z = K(3)
>>> z
3 mod 9
>>> z**2
0 mod 9
It would be good to have a proper implementation of prime power fields
(``GF(p**n)``) but these are not yet implemented in SymPY.
.. _finite field: https://en.wikipedia.org/wiki/Finite_field
"""
rep = 'FF'
alias = 'FF'
is_FiniteField = is_FF = True
is_Numerical = True
has_assoc_Ring = False
has_assoc_Field = True
dom = None
mod = None
def __init__(self, mod, symmetric=True):
from sympy.polys.domains import ZZ
dom = ZZ
if mod <= 0:
raise ValueError('modulus must be a positive integer, got %s' % mod)
self.dtype = _modular_int_factory(mod, dom, symmetric, self)
self.zero = self.dtype(0)
self.one = self.dtype(1)
self.dom = dom
self.mod = mod
self.sym = symmetric
self._tp = type(self.zero)
@property
def tp(self):
return self._tp
def __str__(self):
return 'GF(%s)' % self.mod
def __hash__(self):
return hash((self.__class__.__name__, self.dtype, self.mod, self.dom))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
return isinstance(other, FiniteField) and \
self.mod == other.mod and self.dom == other.dom
def characteristic(self):
"""Return the characteristic of this domain. """
return self.mod
def get_field(self):
"""Returns a field associated with ``self``. """
return self
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyInteger(self.to_int(a))
def from_sympy(self, a):
"""Convert SymPy's Integer to SymPy's ``Integer``. """
if a.is_Integer:
return self.dtype(self.dom.dtype(int(a)))
elif int_valued(a):
return self.dtype(self.dom.dtype(int(a)))
else:
raise CoercionFailed("expected an integer, got %s" % a)
def to_int(self, a):
"""Convert ``val`` to a Python ``int`` object. """
aval = int(a)
if self.sym and aval > self.mod // 2:
aval -= self.mod
return aval
def is_positive(self, a):
"""Returns True if ``a`` is positive. """
return bool(a)
def is_nonnegative(self, a):
"""Returns True if ``a`` is non-negative. """
return True
def is_negative(self, a):
"""Returns True if ``a`` is negative. """
return False
def is_nonpositive(self, a):
"""Returns True if ``a`` is non-positive. """
return not a
def from_FF(K1, a, K0=None):
"""Convert ``ModularInteger(int)`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ(int(a), K0.dom))
def from_FF_python(K1, a, K0=None):
"""Convert ``ModularInteger(int)`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ_python(int(a), K0.dom))
def from_ZZ(K1, a, K0=None):
"""Convert Python's ``int`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
def from_ZZ_python(K1, a, K0=None):
"""Convert Python's ``int`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
def from_QQ(K1, a, K0=None):
"""Convert Python's ``Fraction`` to ``dtype``. """
if a.denominator == 1:
return K1.from_ZZ_python(a.numerator)
def from_QQ_python(K1, a, K0=None):
"""Convert Python's ``Fraction`` to ``dtype``. """
if a.denominator == 1:
return K1.from_ZZ_python(a.numerator)
def from_FF_gmpy(K1, a, K0=None):
"""Convert ``ModularInteger(mpz)`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom))
def from_ZZ_gmpy(K1, a, K0=None):
"""Convert GMPY's ``mpz`` to ``dtype``. """
return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0))
def from_QQ_gmpy(K1, a, K0=None):
"""Convert GMPY's ``mpq`` to ``dtype``. """
if a.denominator == 1:
return K1.from_ZZ_gmpy(a.numerator)
def from_RealField(K1, a, K0):
"""Convert mpmath's ``mpf`` to ``dtype``. """
p, q = K0.to_rational(a)
if q == 1:
return K1.dtype(K1.dom.dtype(p))
def is_square(self, a):
"""Returns True if ``a`` is a quadratic residue modulo p. """
# a is not a square <=> x**2-a is irreducible
poly = [int(x) for x in [self.one, self.zero, -a]]
return not gf_irred_p_rabin(poly, self.mod, self.dom)
def exsqrt(self, a):
"""Square root modulo p of ``a`` if it is a quadratic residue.
Explanation
===========
Always returns the square root that is no larger than ``p // 2``.
"""
# x**2-a is not square-free if a=0 or the field is characteristic 2
if self.mod == 2 or a == 0:
return a
# Otherwise, use square-free factorization routine to factorize x**2-a
poly = [int(x) for x in [self.one, self.zero, -a]]
for factor in gf_zassenhaus(poly, self.mod, self.dom):
if len(factor) == 2 and factor[1] <= self.mod // 2:
return self.dtype(factor[1])
return None
FF = GF = FiniteField

View File

@ -0,0 +1,177 @@
"""Implementation of :class:`FractionField` class. """
from sympy.polys.domains.compositedomain import CompositeDomain
from sympy.polys.domains.field import Field
from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
from sympy.utilities import public
@public
class FractionField(Field, CompositeDomain):
"""A class for representing multivariate rational function fields. """
is_FractionField = is_Frac = True
has_assoc_Ring = True
has_assoc_Field = True
def __init__(self, domain_or_field, symbols=None, order=None):
from sympy.polys.fields import FracField
if isinstance(domain_or_field, FracField) and symbols is None and order is None:
field = domain_or_field
else:
field = FracField(symbols, domain_or_field, order)
self.field = field
self.dtype = field.dtype
self.gens = field.gens
self.ngens = field.ngens
self.symbols = field.symbols
self.domain = field.domain
# TODO: remove this
self.dom = self.domain
def new(self, element):
return self.field.field_new(element)
@property
def zero(self):
return self.field.zero
@property
def one(self):
return self.field.one
@property
def order(self):
return self.field.order
def __str__(self):
return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')'
def __hash__(self):
return hash((self.__class__.__name__, self.dtype.field, self.domain, self.symbols))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
return isinstance(other, FractionField) and \
(self.dtype.field, self.domain, self.symbols) ==\
(other.dtype.field, other.domain, other.symbols)
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return a.as_expr()
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
return self.field.from_expr(a)
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_QQ(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
dom = K1.domain
conv = dom.convert_from
if dom.is_ZZ:
return K1(conv(K0.numer(a), K0)) / K1(conv(K0.denom(a), K0))
else:
return K1(conv(a, K0))
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_GaussianRationalField(K1, a, K0):
"""Convert a ``GaussianRational`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a ``GaussianInteger`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_ComplexField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1(K1.domain.convert(a, K0))
def from_AlgebraicField(K1, a, K0):
"""Convert an algebraic number to ``dtype``. """
if K1.domain != K0:
a = K1.domain.convert_from(a, K0)
if a is not None:
return K1.new(a)
def from_PolynomialRing(K1, a, K0):
"""Convert a polynomial to ``dtype``. """
if a.is_ground:
return K1.convert_from(a.coeff(1), K0.domain)
try:
return K1.new(a.set_ring(K1.field.ring))
except (CoercionFailed, GeneratorsError):
# XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y]
# and the poly a in K0 has non-integer coefficients.
# It seems that K1.new can handle this but K1.new doesn't work
# when K0.domain is an algebraic field...
try:
return K1.new(a)
except (CoercionFailed, GeneratorsError):
return None
def from_FractionField(K1, a, K0):
"""Convert a rational function to ``dtype``. """
try:
return a.set_field(K1.field)
except (CoercionFailed, GeneratorsError):
return None
def get_ring(self):
"""Returns a field associated with ``self``. """
return self.field.to_ring().to_domain()
def is_positive(self, a):
"""Returns True if ``LC(a)`` is positive. """
return self.domain.is_positive(a.numer.LC)
def is_negative(self, a):
"""Returns True if ``LC(a)`` is negative. """
return self.domain.is_negative(a.numer.LC)
def is_nonpositive(self, a):
"""Returns True if ``LC(a)`` is non-positive. """
return self.domain.is_nonpositive(a.numer.LC)
def is_nonnegative(self, a):
"""Returns True if ``LC(a)`` is non-negative. """
return self.domain.is_nonnegative(a.numer.LC)
def numer(self, a):
"""Returns numerator of ``a``. """
return a.numer
def denom(self, a):
"""Returns denominator of ``a``. """
return a.denom
def factorial(self, a):
"""Returns factorial of ``a``. """
return self.dtype(self.domain.factorial(a))

View File

@ -0,0 +1,686 @@
"""Domains of Gaussian type."""
from sympy.core.numbers import I
from sympy.polys.polyerrors import CoercionFailed
from sympy.polys.domains.integerring import ZZ
from sympy.polys.domains.rationalfield import QQ
from sympy.polys.domains.algebraicfield import AlgebraicField
from sympy.polys.domains.domain import Domain
from sympy.polys.domains.domainelement import DomainElement
from sympy.polys.domains.field import Field
from sympy.polys.domains.ring import Ring
class GaussianElement(DomainElement):
"""Base class for elements of Gaussian type domains."""
base: Domain
_parent: Domain
__slots__ = ('x', 'y')
def __new__(cls, x, y=0):
conv = cls.base.convert
return cls.new(conv(x), conv(y))
@classmethod
def new(cls, x, y):
"""Create a new GaussianElement of the same domain."""
obj = super().__new__(cls)
obj.x = x
obj.y = y
return obj
def parent(self):
"""The domain that this is an element of (ZZ_I or QQ_I)"""
return self._parent
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.x == other.x and self.y == other.y
else:
return NotImplemented
def __lt__(self, other):
if not isinstance(other, GaussianElement):
return NotImplemented
return [self.y, self.x] < [other.y, other.x]
def __pos__(self):
return self
def __neg__(self):
return self.new(-self.x, -self.y)
def __repr__(self):
return "%s(%s, %s)" % (self._parent.rep, self.x, self.y)
def __str__(self):
return str(self._parent.to_sympy(self))
@classmethod
def _get_xy(cls, other):
if not isinstance(other, cls):
try:
other = cls._parent.convert(other)
except CoercionFailed:
return None, None
return other.x, other.y
def __add__(self, other):
x, y = self._get_xy(other)
if x is not None:
return self.new(self.x + x, self.y + y)
else:
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
x, y = self._get_xy(other)
if x is not None:
return self.new(self.x - x, self.y - y)
else:
return NotImplemented
def __rsub__(self, other):
x, y = self._get_xy(other)
if x is not None:
return self.new(x - self.x, y - self.y)
else:
return NotImplemented
def __mul__(self, other):
x, y = self._get_xy(other)
if x is not None:
return self.new(self.x*x - self.y*y, self.x*y + self.y*x)
else:
return NotImplemented
__rmul__ = __mul__
def __pow__(self, exp):
if exp == 0:
return self.new(1, 0)
if exp < 0:
self, exp = 1/self, -exp
if exp == 1:
return self
pow2 = self
prod = self if exp % 2 else self._parent.one
exp //= 2
while exp:
pow2 *= pow2
if exp % 2:
prod *= pow2
exp //= 2
return prod
def __bool__(self):
return bool(self.x) or bool(self.y)
def quadrant(self):
"""Return quadrant index 0-3.
0 is included in quadrant 0.
"""
if self.y > 0:
return 0 if self.x > 0 else 1
elif self.y < 0:
return 2 if self.x < 0 else 3
else:
return 0 if self.x >= 0 else 2
def __rdivmod__(self, other):
try:
other = self._parent.convert(other)
except CoercionFailed:
return NotImplemented
else:
return other.__divmod__(self)
def __rtruediv__(self, other):
try:
other = QQ_I.convert(other)
except CoercionFailed:
return NotImplemented
else:
return other.__truediv__(self)
def __floordiv__(self, other):
qr = self.__divmod__(other)
return qr if qr is NotImplemented else qr[0]
def __rfloordiv__(self, other):
qr = self.__rdivmod__(other)
return qr if qr is NotImplemented else qr[0]
def __mod__(self, other):
qr = self.__divmod__(other)
return qr if qr is NotImplemented else qr[1]
def __rmod__(self, other):
qr = self.__rdivmod__(other)
return qr if qr is NotImplemented else qr[1]
class GaussianInteger(GaussianElement):
"""Gaussian integer: domain element for :ref:`ZZ_I`
>>> from sympy import ZZ_I
>>> z = ZZ_I(2, 3)
>>> z
(2 + 3*I)
>>> type(z)
<class 'sympy.polys.domains.gaussiandomains.GaussianInteger'>
"""
base = ZZ
def __truediv__(self, other):
"""Return a Gaussian rational."""
return QQ_I.convert(self)/other
def __divmod__(self, other):
if not other:
raise ZeroDivisionError('divmod({}, 0)'.format(self))
x, y = self._get_xy(other)
if x is None:
return NotImplemented
# multiply self and other by x - I*y
# self/other == (a + I*b)/c
a, b = self.x*x + self.y*y, -self.x*y + self.y*x
c = x*x + y*y
# find integers qx and qy such that
# |a - qx*c| <= c/2 and |b - qy*c| <= c/2
qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c
qy = (2*b + c) // (2*c)
q = GaussianInteger(qx, qy)
# |self/other - q| < 1 since
# |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1
return q, self - q*other # |r| < |other|
class GaussianRational(GaussianElement):
"""Gaussian rational: domain element for :ref:`QQ_I`
>>> from sympy import QQ_I, QQ
>>> z = QQ_I(QQ(2, 3), QQ(4, 5))
>>> z
(2/3 + 4/5*I)
>>> type(z)
<class 'sympy.polys.domains.gaussiandomains.GaussianRational'>
"""
base = QQ
def __truediv__(self, other):
"""Return a Gaussian rational."""
if not other:
raise ZeroDivisionError('{} / 0'.format(self))
x, y = self._get_xy(other)
if x is None:
return NotImplemented
c = x*x + y*y
return GaussianRational((self.x*x + self.y*y)/c,
(-self.x*y + self.y*x)/c)
def __divmod__(self, other):
try:
other = self._parent.convert(other)
except CoercionFailed:
return NotImplemented
if not other:
raise ZeroDivisionError('{} % 0'.format(self))
else:
return self/other, QQ_I.zero
class GaussianDomain():
"""Base class for Gaussian domains."""
dom = None # type: Domain
is_Numerical = True
is_Exact = True
has_assoc_Ring = True
has_assoc_Field = True
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
conv = self.dom.to_sympy
return conv(a.x) + I*conv(a.y)
def from_sympy(self, a):
"""Convert a SymPy object to ``self.dtype``."""
r, b = a.as_coeff_Add()
x = self.dom.from_sympy(r) # may raise CoercionFailed
if not b:
return self.new(x, 0)
r, b = b.as_coeff_Mul()
y = self.dom.from_sympy(r)
if b is I:
return self.new(x, y)
else:
raise CoercionFailed("{} is not Gaussian".format(a))
def inject(self, *gens):
"""Inject generators into this domain. """
return self.poly_ring(*gens)
def canonical_unit(self, d):
unit = self.units[-d.quadrant()] # - for inverse power
return unit
def is_negative(self, element):
"""Returns ``False`` for any ``GaussianElement``. """
return False
def is_positive(self, element):
"""Returns ``False`` for any ``GaussianElement``. """
return False
def is_nonnegative(self, element):
"""Returns ``False`` for any ``GaussianElement``. """
return False
def is_nonpositive(self, element):
"""Returns ``False`` for any ``GaussianElement``. """
return False
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY mpz to ``self.dtype``."""
return K1(a)
def from_ZZ(K1, a, K0):
"""Convert a ZZ_python element to ``self.dtype``."""
return K1(a)
def from_ZZ_python(K1, a, K0):
"""Convert a ZZ_python element to ``self.dtype``."""
return K1(a)
def from_QQ(K1, a, K0):
"""Convert a GMPY mpq to ``self.dtype``."""
return K1(a)
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY mpq to ``self.dtype``."""
return K1(a)
def from_QQ_python(K1, a, K0):
"""Convert a QQ_python element to ``self.dtype``."""
return K1(a)
def from_AlgebraicField(K1, a, K0):
"""Convert an element from ZZ<I> or QQ<I> to ``self.dtype``."""
if K0.ext.args[0] == I:
return K1.from_sympy(K0.to_sympy(a))
class GaussianIntegerRing(GaussianDomain, Ring):
r"""Ring of Gaussian integers ``ZZ_I``
The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]`
as a :py:class:`~.Domain` in the domain system (see
:ref:`polys-domainsintro`).
By default a :py:class:`~.Poly` created from an expression with
coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`)
will have the domain :ref:`ZZ_I`.
>>> from sympy import Poly, Symbol, I
>>> x = Symbol('x')
>>> p = Poly(x**2 + I)
>>> p
Poly(x**2 + I, x, domain='ZZ_I')
>>> p.domain
ZZ_I
The :ref:`ZZ_I` domain can be used to factorise polynomials that are
reducible over the Gaussian integers.
>>> from sympy import factor
>>> factor(x**2 + 1)
x**2 + 1
>>> factor(x**2 + 1, domain='ZZ_I')
(x - I)*(x + I)
The corresponding `field of fractions`_ is the domain of the Gaussian
rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_
of :ref:`QQ_I`.
>>> from sympy import ZZ_I, QQ_I
>>> ZZ_I.get_field()
QQ_I
>>> QQ_I.get_ring()
ZZ_I
When using the domain directly :ref:`ZZ_I` can be used as a constructor.
>>> ZZ_I(3, 4)
(3 + 4*I)
>>> ZZ_I(5)
(5 + 0*I)
The domain elements of :ref:`ZZ_I` are instances of
:py:class:`~.GaussianInteger` which support the rings operations
``+,-,*,**``.
>>> z1 = ZZ_I(5, 1)
>>> z2 = ZZ_I(2, 3)
>>> z1
(5 + 1*I)
>>> z2
(2 + 3*I)
>>> z1 + z2
(7 + 4*I)
>>> z1 * z2
(7 + 17*I)
>>> z1 ** 2
(24 + 10*I)
Both floor (``//``) and modulo (``%``) division work with
:py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method).
>>> z3, z4 = ZZ_I(5), ZZ_I(1, 3)
>>> z3 // z4 # floor division
(1 + -1*I)
>>> z3 % z4 # modulo division (remainder)
(1 + -2*I)
>>> (z3//z4)*z4 + z3%z4 == z3
True
True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The
:py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when
exact division is possible.
>>> z1 / z2
(1 + -1*I)
>>> ZZ_I.exquo(z1, z2)
(1 + -1*I)
>>> z3 / z4
(1/2 + -3/2*I)
>>> ZZ_I.exquo(z3, z4)
Traceback (most recent call last):
...
ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I
The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any
two elements.
>>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2))
(2 + 0*I)
>>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1))
(2 + 1*I)
.. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer
.. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor
"""
dom = ZZ
dtype = GaussianInteger
zero = dtype(ZZ(0), ZZ(0))
one = dtype(ZZ(1), ZZ(0))
imag_unit = dtype(ZZ(0), ZZ(1))
units = (one, imag_unit, -one, -imag_unit) # powers of i
rep = 'ZZ_I'
is_GaussianRing = True
is_ZZ_I = True
def __init__(self): # override Domain.__init__
"""For constructing ZZ_I."""
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
if isinstance(other, GaussianIntegerRing):
return True
else:
return NotImplemented
def __hash__(self):
"""Compute hash code of ``self``. """
return hash('ZZ_I')
@property
def has_CharacteristicZero(self):
return True
def characteristic(self):
return 0
def get_ring(self):
"""Returns a ring associated with ``self``. """
return self
def get_field(self):
"""Returns a field associated with ``self``. """
return QQ_I
def normalize(self, d, *args):
"""Return first quadrant element associated with ``d``.
Also multiply the other arguments by the same power of i.
"""
unit = self.canonical_unit(d)
d *= unit
args = tuple(a*unit for a in args)
return (d,) + args if args else d
def gcd(self, a, b):
"""Greatest common divisor of a and b over ZZ_I."""
while b:
a, b = b, a % b
return self.normalize(a)
def lcm(self, a, b):
"""Least common multiple of a and b over ZZ_I."""
return (a * b) // self.gcd(a, b)
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a ZZ_I element to ZZ_I."""
return a
def from_GaussianRationalField(K1, a, K0):
"""Convert a QQ_I element to ZZ_I."""
return K1.new(ZZ.convert(a.x), ZZ.convert(a.y))
ZZ_I = GaussianInteger._parent = GaussianIntegerRing()
class GaussianRationalField(GaussianDomain, Field):
r"""Field of Gaussian rationals ``QQ_I``
The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)`
as a :py:class:`~.Domain` in the domain system (see
:ref:`polys-domainsintro`).
By default a :py:class:`~.Poly` created from an expression with
coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`)
will have the domain :ref:`QQ_I`.
>>> from sympy import Poly, Symbol, I
>>> x = Symbol('x')
>>> p = Poly(x**2 + I/2)
>>> p
Poly(x**2 + I/2, x, domain='QQ_I')
>>> p.domain
QQ_I
The polys option ``gaussian=True`` can be used to specify that the domain
should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are
all integers.
>>> Poly(x**2)
Poly(x**2, x, domain='ZZ')
>>> Poly(x**2 + I)
Poly(x**2 + I, x, domain='ZZ_I')
>>> Poly(x**2/2)
Poly(1/2*x**2, x, domain='QQ')
>>> Poly(x**2, gaussian=True)
Poly(x**2, x, domain='QQ_I')
>>> Poly(x**2 + I, gaussian=True)
Poly(x**2 + I, x, domain='QQ_I')
>>> Poly(x**2/2, gaussian=True)
Poly(1/2*x**2, x, domain='QQ_I')
The :ref:`QQ_I` domain can be used to factorise polynomials that are
reducible over the Gaussian rationals.
>>> from sympy import factor, QQ_I
>>> factor(x**2/4 + 1)
(x**2 + 4)/4
>>> factor(x**2/4 + 1, domain='QQ_I')
(x - 2*I)*(x + 2*I)/4
>>> factor(x**2/4 + 1, domain=QQ_I)
(x - 2*I)*(x + 2*I)/4
It is also possible to specify the :ref:`QQ_I` domain explicitly with
polys functions like :py:func:`~.apart`.
>>> from sympy import apart
>>> apart(1/(1 + x**2))
1/(x**2 + 1)
>>> apart(1/(1 + x**2), domain=QQ_I)
I/(2*(x + I)) - I/(2*(x - I))
The corresponding `ring of integers`_ is the domain of the Gaussian
integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_
of :ref:`ZZ_I`.
>>> from sympy import ZZ_I, QQ_I, QQ
>>> ZZ_I.get_field()
QQ_I
>>> QQ_I.get_ring()
ZZ_I
When using the domain directly :ref:`QQ_I` can be used as a constructor.
>>> QQ_I(3, 4)
(3 + 4*I)
>>> QQ_I(5)
(5 + 0*I)
>>> QQ_I(QQ(2, 3), QQ(4, 5))
(2/3 + 4/5*I)
The domain elements of :ref:`QQ_I` are instances of
:py:class:`~.GaussianRational` which support the field operations
``+,-,*,**,/``.
>>> z1 = QQ_I(5, 1)
>>> z2 = QQ_I(2, QQ(1, 2))
>>> z1
(5 + 1*I)
>>> z2
(2 + 1/2*I)
>>> z1 + z2
(7 + 3/2*I)
>>> z1 * z2
(19/2 + 9/2*I)
>>> z2 ** 2
(15/4 + 2*I)
True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and
is always exact.
>>> z1 / z2
(42/17 + -2/17*I)
>>> QQ_I.exquo(z1, z2)
(42/17 + -2/17*I)
>>> z1 == (z1/z2)*z2
True
Both floor (``//``) and modulo (``%``) division can be used with
:py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`)
but division is always exact so there is no remainder.
>>> z1 // z2
(42/17 + -2/17*I)
>>> z1 % z2
(0 + 0*I)
>>> QQ_I.div(z1, z2)
((42/17 + -2/17*I), (0 + 0*I))
>>> (z1//z2)*z2 + z1%z2 == z1
True
.. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational
"""
dom = QQ
dtype = GaussianRational
zero = dtype(QQ(0), QQ(0))
one = dtype(QQ(1), QQ(0))
imag_unit = dtype(QQ(0), QQ(1))
units = (one, imag_unit, -one, -imag_unit) # powers of i
rep = 'QQ_I'
is_GaussianField = True
is_QQ_I = True
def __init__(self): # override Domain.__init__
"""For constructing QQ_I."""
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
if isinstance(other, GaussianRationalField):
return True
else:
return NotImplemented
def __hash__(self):
"""Compute hash code of ``self``. """
return hash('QQ_I')
@property
def has_CharacteristicZero(self):
return True
def characteristic(self):
return 0
def get_ring(self):
"""Returns a ring associated with ``self``. """
return ZZ_I
def get_field(self):
"""Returns a field associated with ``self``. """
return self
def as_AlgebraicField(self):
"""Get equivalent domain as an ``AlgebraicField``. """
return AlgebraicField(self.dom, I)
def numer(self, a):
"""Get the numerator of ``a``."""
ZZ_I = self.get_ring()
return ZZ_I.convert(a * self.denom(a))
def denom(self, a):
"""Get the denominator of ``a``."""
ZZ = self.dom.get_ring()
QQ = self.dom
ZZ_I = self.get_ring()
denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y))
return ZZ_I(denom_ZZ, ZZ.zero)
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a ZZ_I element to QQ_I."""
return K1.new(a.x, a.y)
def from_GaussianRationalField(K1, a, K0):
"""Convert a QQ_I element to QQ_I."""
return a
def from_ComplexField(K1, a, K0):
"""Convert a ComplexField element to QQ_I."""
return K1.new(QQ.convert(a.real), QQ.convert(a.imag))
QQ_I = GaussianRational._parent = GaussianRationalField()

View File

@ -0,0 +1,16 @@
"""Implementation of :class:`GMPYFiniteField` class. """
from sympy.polys.domains.finitefield import FiniteField
from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing
from sympy.utilities import public
@public
class GMPYFiniteField(FiniteField):
"""Finite field based on GMPY integers. """
alias = 'FF_gmpy'
def __init__(self, mod, symmetric=True):
super().__init__(mod, GMPYIntegerRing(), symmetric)

View File

@ -0,0 +1,105 @@
"""Implementation of :class:`GMPYIntegerRing` class. """
from sympy.polys.domains.groundtypes import (
GMPYInteger, SymPyInteger,
factorial as gmpy_factorial,
gmpy_gcdex, gmpy_gcd, gmpy_lcm, sqrt as gmpy_sqrt,
)
from sympy.core.numbers import int_valued
from sympy.polys.domains.integerring import IntegerRing
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class GMPYIntegerRing(IntegerRing):
"""Integer ring based on GMPY's ``mpz`` type.
This will be the implementation of :ref:`ZZ` if ``gmpy`` or ``gmpy2`` is
installed. Elements will be of type ``gmpy.mpz``.
"""
dtype = GMPYInteger
zero = dtype(0)
one = dtype(1)
tp = type(one)
alias = 'ZZ_gmpy'
def __init__(self):
"""Allow instantiation of this domain. """
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyInteger(int(a))
def from_sympy(self, a):
"""Convert SymPy's Integer to ``dtype``. """
if a.is_Integer:
return GMPYInteger(a.p)
elif int_valued(a):
return GMPYInteger(int(a))
else:
raise CoercionFailed("expected an integer, got %s" % a)
def from_FF_python(K1, a, K0):
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
return K0.to_int(a)
def from_ZZ_python(K1, a, K0):
"""Convert Python's ``int`` to GMPY's ``mpz``. """
return GMPYInteger(a)
def from_QQ(K1, a, K0):
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
if a.denominator == 1:
return GMPYInteger(a.numerator)
def from_QQ_python(K1, a, K0):
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
if a.denominator == 1:
return GMPYInteger(a.numerator)
def from_FF_gmpy(K1, a, K0):
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
return K0.to_int(a)
def from_ZZ_gmpy(K1, a, K0):
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
return a
def from_QQ_gmpy(K1, a, K0):
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
if a.denominator == 1:
return a.numerator
def from_RealField(K1, a, K0):
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
p, q = K0.to_rational(a)
if q == 1:
return GMPYInteger(p)
def from_GaussianIntegerRing(K1, a, K0):
if a.y == 0:
return a.x
def gcdex(self, a, b):
"""Compute extended GCD of ``a`` and ``b``. """
h, s, t = gmpy_gcdex(a, b)
return s, t, h
def gcd(self, a, b):
"""Compute GCD of ``a`` and ``b``. """
return gmpy_gcd(a, b)
def lcm(self, a, b):
"""Compute LCM of ``a`` and ``b``. """
return gmpy_lcm(a, b)
def sqrt(self, a):
"""Compute square root of ``a``. """
return gmpy_sqrt(a)
def factorial(self, a):
"""Compute factorial of ``a``. """
return gmpy_factorial(a)

View File

@ -0,0 +1,100 @@
"""Implementation of :class:`GMPYRationalField` class. """
from sympy.polys.domains.groundtypes import (
GMPYRational, SymPyRational,
gmpy_numer, gmpy_denom, factorial as gmpy_factorial,
)
from sympy.polys.domains.rationalfield import RationalField
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class GMPYRationalField(RationalField):
"""Rational field based on GMPY's ``mpq`` type.
This will be the implementation of :ref:`QQ` if ``gmpy`` or ``gmpy2`` is
installed. Elements will be of type ``gmpy.mpq``.
"""
dtype = GMPYRational
zero = dtype(0)
one = dtype(1)
tp = type(one)
alias = 'QQ_gmpy'
def __init__(self):
pass
def get_ring(self):
"""Returns ring associated with ``self``. """
from sympy.polys.domains import GMPYIntegerRing
return GMPYIntegerRing()
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyRational(int(gmpy_numer(a)),
int(gmpy_denom(a)))
def from_sympy(self, a):
"""Convert SymPy's Integer to ``dtype``. """
if a.is_Rational:
return GMPYRational(a.p, a.q)
elif a.is_Float:
from sympy.polys.domains import RR
return GMPYRational(*map(int, RR.to_rational(a)))
else:
raise CoercionFailed("expected ``Rational`` object, got %s" % a)
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return GMPYRational(a)
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return GMPYRational(a.numerator, a.denominator)
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return GMPYRational(a)
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return a
def from_GaussianRationalField(K1, a, K0):
"""Convert a ``GaussianElement`` object to ``dtype``. """
if a.y == 0:
return GMPYRational(a.x)
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return GMPYRational(*map(int, K0.to_rational(a)))
def exquo(self, a, b):
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
return GMPYRational(a) / GMPYRational(b)
def quo(self, a, b):
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
return GMPYRational(a) / GMPYRational(b)
def rem(self, a, b):
"""Remainder of ``a`` and ``b``, implies nothing. """
return self.zero
def div(self, a, b):
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
return GMPYRational(a) / GMPYRational(b), self.zero
def numer(self, a):
"""Returns numerator of ``a``. """
return a.numerator
def denom(self, a):
"""Returns denominator of ``a``. """
return a.denominator
def factorial(self, a):
"""Returns factorial of ``a``. """
return GMPYRational(gmpy_factorial(int(a)))

View File

@ -0,0 +1,99 @@
"""Ground types for various mathematical domains in SymPy. """
import builtins
from sympy.external.gmpy import GROUND_TYPES, factorial, sqrt, is_square, sqrtrem
PythonInteger = builtins.int
PythonReal = builtins.float
PythonComplex = builtins.complex
from .pythonrational import PythonRational
from sympy.core.intfunc import (
igcdex as python_gcdex,
igcd2 as python_gcd,
ilcm as python_lcm,
)
from sympy.core.numbers import (Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational)
class _GMPYInteger:
def __init__(self, obj):
pass
class _GMPYRational:
def __init__(self, obj):
pass
if GROUND_TYPES == 'gmpy':
from gmpy2 import (
mpz as GMPYInteger,
mpq as GMPYRational,
numer as gmpy_numer,
denom as gmpy_denom,
gcdext as gmpy_gcdex,
gcd as gmpy_gcd,
lcm as gmpy_lcm,
qdiv as gmpy_qdiv,
)
gcdex = gmpy_gcdex
gcd = gmpy_gcd
lcm = gmpy_lcm
elif GROUND_TYPES == 'flint':
from flint import fmpz as _fmpz
GMPYInteger = _GMPYInteger
GMPYRational = _GMPYRational
gmpy_numer = None
gmpy_denom = None
gmpy_gcdex = None
gmpy_gcd = None
gmpy_lcm = None
gmpy_qdiv = None
def gcd(a, b):
return a.gcd(b)
def gcdex(a, b):
x, y, g = python_gcdex(a, b)
return _fmpz(x), _fmpz(y), _fmpz(g)
def lcm(a, b):
return a.lcm(b)
else:
GMPYInteger = _GMPYInteger
GMPYRational = _GMPYRational
gmpy_numer = None
gmpy_denom = None
gmpy_gcdex = None
gmpy_gcd = None
gmpy_lcm = None
gmpy_qdiv = None
gcdex = python_gcdex
gcd = python_gcd
lcm = python_lcm
__all__ = [
'PythonInteger', 'PythonReal', 'PythonComplex',
'PythonRational',
'python_gcdex', 'python_gcd', 'python_lcm',
'SymPyReal', 'SymPyInteger', 'SymPyRational',
'GMPYInteger', 'GMPYRational', 'gmpy_numer',
'gmpy_denom', 'gmpy_gcdex', 'gmpy_gcd', 'gmpy_lcm',
'gmpy_qdiv',
'factorial', 'sqrt', 'is_square', 'sqrtrem',
'GMPYInteger', 'GMPYRational',
]

View File

@ -0,0 +1,276 @@
"""Implementation of :class:`IntegerRing` class. """
from sympy.external.gmpy import MPZ, GROUND_TYPES
from sympy.core.numbers import int_valued
from sympy.polys.domains.groundtypes import (
SymPyInteger,
factorial,
gcdex, gcd, lcm, sqrt, is_square, sqrtrem,
)
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.ring import Ring
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
import math
@public
class IntegerRing(Ring, CharacteristicZero, SimpleDomain):
r"""The domain ``ZZ`` representing the integers `\mathbb{Z}`.
The :py:class:`IntegerRing` class represents the ring of integers as a
:py:class:`~.Domain` in the domain system. :py:class:`IntegerRing` is a
super class of :py:class:`PythonIntegerRing` and
:py:class:`GMPYIntegerRing` one of which will be the implementation for
:ref:`ZZ` depending on whether or not ``gmpy`` or ``gmpy2`` is installed.
See also
========
Domain
"""
rep = 'ZZ'
alias = 'ZZ'
dtype = MPZ
zero = dtype(0)
one = dtype(1)
tp = type(one)
is_IntegerRing = is_ZZ = True
is_Numerical = True
is_PID = True
has_assoc_Ring = True
has_assoc_Field = True
def __init__(self):
"""Allow instantiation of this domain. """
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
if isinstance(other, IntegerRing):
return True
else:
return NotImplemented
def __hash__(self):
"""Compute a hash value for this domain. """
return hash('ZZ')
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyInteger(int(a))
def from_sympy(self, a):
"""Convert SymPy's Integer to ``dtype``. """
if a.is_Integer:
return MPZ(a.p)
elif int_valued(a):
return MPZ(int(a))
else:
raise CoercionFailed("expected an integer, got %s" % a)
def get_field(self):
r"""Return the associated field of fractions :ref:`QQ`
Returns
=======
:ref:`QQ`:
The associated field of fractions :ref:`QQ`, a
:py:class:`~.Domain` representing the rational numbers
`\mathbb{Q}`.
Examples
========
>>> from sympy import ZZ
>>> ZZ.get_field()
QQ
"""
from sympy.polys.domains import QQ
return QQ
def algebraic_field(self, *extension, alias=None):
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
Parameters
==========
*extension : One or more :py:class:`~.Expr`.
Generators of the extension. These should be expressions that are
algebraic over `\mathbb{Q}`.
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
If provided, this will be used as the alias symbol for the
primitive element of the returned :py:class:`~.AlgebraicField`.
Returns
=======
:py:class:`~.AlgebraicField`
A :py:class:`~.Domain` representing the algebraic field extension.
Examples
========
>>> from sympy import ZZ, sqrt
>>> ZZ.algebraic_field(sqrt(2))
QQ<sqrt(2)>
"""
return self.get_field().algebraic_field(*extension, alias=alias)
def from_AlgebraicField(K1, a, K0):
"""Convert a :py:class:`~.ANP` object to :ref:`ZZ`.
See :py:meth:`~.Domain.convert`.
"""
if a.is_ground:
return K1.convert(a.LC(), K0.dom)
def log(self, a, b):
r"""Logarithm of *a* to the base *b*.
Parameters
==========
a: number
b: number
Returns
=======
$\\lfloor\log(a, b)\\rfloor$:
Floor of the logarithm of *a* to the base *b*
Examples
========
>>> from sympy import ZZ
>>> ZZ.log(ZZ(8), ZZ(2))
3
>>> ZZ.log(ZZ(9), ZZ(2))
3
Notes
=====
This function uses ``math.log`` which is based on ``float`` so it will
fail for large integer arguments.
"""
return self.dtype(int(math.log(int(a), b)))
def from_FF(K1, a, K0):
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
return MPZ(K0.to_int(a))
def from_FF_python(K1, a, K0):
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
return MPZ(K0.to_int(a))
def from_ZZ(K1, a, K0):
"""Convert Python's ``int`` to GMPY's ``mpz``. """
return MPZ(a)
def from_ZZ_python(K1, a, K0):
"""Convert Python's ``int`` to GMPY's ``mpz``. """
return MPZ(a)
def from_QQ(K1, a, K0):
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
if a.denominator == 1:
return MPZ(a.numerator)
def from_QQ_python(K1, a, K0):
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
if a.denominator == 1:
return MPZ(a.numerator)
def from_FF_gmpy(K1, a, K0):
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
return MPZ(K0.to_int(a))
def from_ZZ_gmpy(K1, a, K0):
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
return a
def from_QQ_gmpy(K1, a, K0):
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
if a.denominator == 1:
return a.numerator
def from_RealField(K1, a, K0):
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
p, q = K0.to_rational(a)
if q == 1:
# XXX: If MPZ is flint.fmpz and p is a gmpy2.mpz, then we need
# to convert via int because fmpz and mpz do not know about each
# other.
return MPZ(int(p))
def from_GaussianIntegerRing(K1, a, K0):
if a.y == 0:
return a.x
def from_EX(K1, a, K0):
"""Convert ``Expression`` to GMPY's ``mpz``. """
if a.is_Integer:
return K1.from_sympy(a)
def gcdex(self, a, b):
"""Compute extended GCD of ``a`` and ``b``. """
h, s, t = gcdex(a, b)
# XXX: This conditional logic should be handled somewhere else.
if GROUND_TYPES == 'gmpy':
return s, t, h
else:
return h, s, t
def gcd(self, a, b):
"""Compute GCD of ``a`` and ``b``. """
return gcd(a, b)
def lcm(self, a, b):
"""Compute LCM of ``a`` and ``b``. """
return lcm(a, b)
def sqrt(self, a):
"""Compute square root of ``a``. """
return sqrt(a)
def is_square(self, a):
"""Return ``True`` if ``a`` is a square.
Explanation
===========
An integer is a square if and only if there exists an integer
``b`` such that ``b * b == a``.
"""
return is_square(a)
def exsqrt(self, a):
"""Non-negative square root of ``a`` if ``a`` is a square.
See also
========
is_square
"""
if a < 0:
return None
root, rem = sqrtrem(a)
if rem != 0:
return None
return root
def factorial(self, a):
"""Compute factorial of ``a``. """
return factorial(a)
ZZ = IntegerRing()

View File

@ -0,0 +1,237 @@
"""Implementation of :class:`ModularInteger` class. """
from __future__ import annotations
from typing import Any
import operator
from sympy.polys.polyutils import PicklableWithSlots
from sympy.polys.polyerrors import CoercionFailed
from sympy.polys.domains.domainelement import DomainElement
from sympy.utilities import public
from sympy.utilities.exceptions import sympy_deprecation_warning
@public
class ModularInteger(PicklableWithSlots, DomainElement):
"""A class representing a modular integer. """
mod, dom, sym, _parent = None, None, None, None
__slots__ = ('val',)
def parent(self):
return self._parent
def __init__(self, val):
if isinstance(val, self.__class__):
self.val = val.val % self.mod
else:
self.val = self.dom.convert(val) % self.mod
def modulus(self):
return self.mod
def __hash__(self):
return hash((self.val, self.mod))
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.val)
def __str__(self):
return "%s mod %s" % (self.val, self.mod)
def __int__(self):
return int(self.val)
def to_int(self):
sympy_deprecation_warning(
"""ModularInteger.to_int() is deprecated.
Use int(a) or K = GF(p) and K.to_int(a) instead of a.to_int().
""",
deprecated_since_version="1.13",
active_deprecations_target="modularinteger-to-int",
)
if self.sym:
if self.val <= self.mod // 2:
return self.val
else:
return self.val - self.mod
else:
return self.val
def __pos__(self):
return self
def __neg__(self):
return self.__class__(-self.val)
@classmethod
def _get_val(cls, other):
if isinstance(other, cls):
return other.val
else:
try:
return cls.dom.convert(other)
except CoercionFailed:
return None
def __add__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(self.val + val)
else:
return NotImplemented
def __radd__(self, other):
return self.__add__(other)
def __sub__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(self.val - val)
else:
return NotImplemented
def __rsub__(self, other):
return (-self).__add__(other)
def __mul__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(self.val * val)
else:
return NotImplemented
def __rmul__(self, other):
return self.__mul__(other)
def __truediv__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(self.val * self._invert(val))
else:
return NotImplemented
def __rtruediv__(self, other):
return self.invert().__mul__(other)
def __mod__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(self.val % val)
else:
return NotImplemented
def __rmod__(self, other):
val = self._get_val(other)
if val is not None:
return self.__class__(val % self.val)
else:
return NotImplemented
def __pow__(self, exp):
if not exp:
return self.__class__(self.dom.one)
if exp < 0:
val, exp = self.invert().val, -exp
else:
val = self.val
return self.__class__(pow(val, int(exp), self.mod))
def _compare(self, other, op):
val = self._get_val(other)
if val is None:
return NotImplemented
return op(self.val, val % self.mod)
def _compare_deprecated(self, other, op):
val = self._get_val(other)
if val is None:
return NotImplemented
sympy_deprecation_warning(
"""Ordered comparisons with modular integers are deprecated.
Use e.g. int(a) < int(b) instead of a < b.
""",
deprecated_since_version="1.13",
active_deprecations_target="modularinteger-compare",
stacklevel=4,
)
return op(self.val, val % self.mod)
def __eq__(self, other):
return self._compare(other, operator.eq)
def __ne__(self, other):
return self._compare(other, operator.ne)
def __lt__(self, other):
return self._compare_deprecated(other, operator.lt)
def __le__(self, other):
return self._compare_deprecated(other, operator.le)
def __gt__(self, other):
return self._compare_deprecated(other, operator.gt)
def __ge__(self, other):
return self._compare_deprecated(other, operator.ge)
def __bool__(self):
return bool(self.val)
@classmethod
def _invert(cls, value):
return cls.dom.invert(value, cls.mod)
def invert(self):
return self.__class__(self._invert(self.val))
_modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {}
def ModularIntegerFactory(_mod, _dom, _sym, parent):
"""Create custom class for specific integer modulus."""
try:
_mod = _dom.convert(_mod)
except CoercionFailed:
ok = False
else:
ok = True
if not ok or _mod < 1:
raise ValueError("modulus must be a positive integer, got %s" % _mod)
key = _mod, _dom, _sym
try:
cls = _modular_integer_cache[key]
except KeyError:
class cls(ModularInteger):
mod, dom, sym = _mod, _dom, _sym
_parent = parent
if _sym:
cls.__name__ = "SymmetricModularIntegerMod%s" % _mod
else:
cls.__name__ = "ModularIntegerMod%s" % _mod
_modular_integer_cache[key] = cls
return cls

View File

@ -0,0 +1,177 @@
"""Real and complex elements. """
from sympy.external.gmpy import MPQ
from sympy.polys.domains.domainelement import DomainElement
from sympy.utilities import public
from mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant
from mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan,
round_nearest, mpf_mul, repr_dps, int_types,
from_int, from_float, from_str, to_rational)
@public
class RealElement(_mpf, DomainElement):
"""An element of a real domain. """
__slots__ = ('__mpf__',)
def _set_mpf(self, val):
self.__mpf__ = val
_mpf_ = property(lambda self: self.__mpf__, _set_mpf)
def parent(self):
return self.context._parent
@public
class ComplexElement(_mpc, DomainElement):
"""An element of a complex domain. """
__slots__ = ('__mpc__',)
def _set_mpc(self, val):
self.__mpc__ = val
_mpc_ = property(lambda self: self.__mpc__, _set_mpc)
def parent(self):
return self.context._parent
new = object.__new__
@public
class MPContext(PythonMPContext):
def __init__(ctx, prec=53, dps=None, tol=None, real=False):
ctx._prec_rounding = [prec, round_nearest]
if dps is None:
ctx._set_prec(prec)
else:
ctx._set_dps(dps)
ctx.mpf = RealElement
ctx.mpc = ComplexElement
ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding]
if real:
ctx.mpf.context = ctx
else:
ctx.mpc.context = ctx
ctx.constant = _constant
ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
ctx.constant.context = ctx
ctx.types = [ctx.mpf, ctx.mpc, ctx.constant]
ctx.trap_complex = True
ctx.pretty = True
if tol is None:
ctx.tol = ctx._make_tol()
elif tol is False:
ctx.tol = fzero
else:
ctx.tol = ctx._convert_tol(tol)
ctx.tolerance = ctx.make_mpf(ctx.tol)
if not ctx.tolerance:
ctx.max_denom = 1000000
else:
ctx.max_denom = int(1/ctx.tolerance)
ctx.zero = ctx.make_mpf(fzero)
ctx.one = ctx.make_mpf(fone)
ctx.j = ctx.make_mpc((fzero, fone))
ctx.inf = ctx.make_mpf(finf)
ctx.ninf = ctx.make_mpf(fninf)
ctx.nan = ctx.make_mpf(fnan)
def _make_tol(ctx):
hundred = (0, 25, 2, 5)
eps = (0, MPZ_ONE, 1-ctx.prec, 1)
return mpf_mul(hundred, eps)
def make_tol(ctx):
return ctx.make_mpf(ctx._make_tol())
def _convert_tol(ctx, tol):
if isinstance(tol, int_types):
return from_int(tol)
if isinstance(tol, float):
return from_float(tol)
if hasattr(tol, "_mpf_"):
return tol._mpf_
prec, rounding = ctx._prec_rounding
if isinstance(tol, str):
return from_str(tol, prec, rounding)
raise ValueError("expected a real number, got %s" % tol)
def _convert_fallback(ctx, x, strings):
raise TypeError("cannot create mpf from " + repr(x))
@property
def _repr_digits(ctx):
return repr_dps(ctx._prec)
@property
def _str_digits(ctx):
return ctx._dps
def to_rational(ctx, s, limit=True):
p, q = to_rational(s._mpf_)
# Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
# to_rational() function returns a gmpy2.mpz instance and if MPQ is
# flint.fmpq then MPQ(p, q) will fail.
p = int(p)
if not limit or q <= ctx.max_denom:
return p, q
p0, q0, p1, q1 = 0, 1, 1, 0
n, d = p, q
while True:
a = n//d
q2 = q0 + a*q1
if q2 > ctx.max_denom:
break
p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
n, d = d, n - a*d
k = (ctx.max_denom - q0)//q1
number = MPQ(p, q)
bound1 = MPQ(p0 + k*p1, q0 + k*q1)
bound2 = MPQ(p1, q1)
if not bound2 or not bound1:
return p, q
elif abs(bound2 - number) <= abs(bound1 - number):
return bound2.numerator, bound2.denominator
else:
return bound1.numerator, bound1.denominator
def almosteq(ctx, s, t, rel_eps=None, abs_eps=None):
t = ctx.convert(t)
if abs_eps is None and rel_eps is None:
rel_eps = abs_eps = ctx.tolerance or ctx.make_tol()
if abs_eps is None:
abs_eps = ctx.convert(rel_eps)
elif rel_eps is None:
rel_eps = ctx.convert(abs_eps)
diff = abs(s-t)
if diff <= abs_eps:
return True
abss = abs(s)
abst = abs(t)
if abss < abst:
err = diff/abst
else:
err = diff/abss
return err <= rel_eps

View File

@ -0,0 +1,188 @@
"""Implementation of :class:`FractionField` class. """
from sympy.polys.domains.field import Field
from sympy.polys.domains.compositedomain import CompositeDomain
from sympy.polys.polyclasses import DMF
from sympy.polys.polyerrors import GeneratorsNeeded
from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
from sympy.utilities import public
@public
class FractionField(Field, CompositeDomain):
"""A class for representing rational function fields. """
dtype = DMF
is_FractionField = is_Frac = True
has_assoc_Ring = True
has_assoc_Field = True
def __init__(self, dom, *gens):
if not gens:
raise GeneratorsNeeded("generators not specified")
lev = len(gens) - 1
self.ngens = len(gens)
self.zero = self.dtype.zero(lev, dom)
self.one = self.dtype.one(lev, dom)
self.domain = self.dom = dom
self.symbols = self.gens = gens
def set_domain(self, dom):
"""Make a new fraction field with given domain. """
return self.__class__(dom, *self.gens)
def new(self, element):
return self.dtype(element, self.dom, len(self.gens) - 1)
def __str__(self):
return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')'
def __hash__(self):
return hash((self.__class__.__name__, self.dtype, self.dom, self.gens))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
return isinstance(other, FractionField) and \
self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
p, q = a.as_numer_denom()
num, _ = dict_from_basic(p, gens=self.gens)
den, _ = dict_from_basic(q, gens=self.gens)
for k, v in num.items():
num[k] = self.dom.from_sympy(v)
for k, v in den.items():
den[k] = self.dom.from_sympy(v)
return self((num, den)).cancel()
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1(K1.dom.convert(a, K0))
def from_GlobalPolynomialRing(K1, a, K0):
"""Convert a ``DMF`` object to ``dtype``. """
if K1.gens == K0.gens:
if K1.dom == K0.dom:
return K1(a.to_list())
else:
return K1(a.convert(K1.dom).to_list())
else:
monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
if K1.dom != K0.dom:
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
return K1(dict(zip(monoms, coeffs)))
def from_FractionField(K1, a, K0):
"""
Convert a fraction field element to another fraction field.
Examples
========
>>> from sympy.polys.polyclasses import DMF
>>> from sympy.polys.domains import ZZ, QQ
>>> from sympy.abc import x
>>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ)
>>> QQx = QQ.old_frac_field(x)
>>> ZZx = ZZ.old_frac_field(x)
>>> QQx.from_FractionField(f, ZZx)
DMF([1, 2], [1, 1], QQ)
"""
if K1.gens == K0.gens:
if K1.dom == K0.dom:
return a
else:
return K1((a.numer().convert(K1.dom).to_list(),
a.denom().convert(K1.dom).to_list()))
elif set(K0.gens).issubset(K1.gens):
nmonoms, ncoeffs = _dict_reorder(
a.numer().to_dict(), K0.gens, K1.gens)
dmonoms, dcoeffs = _dict_reorder(
a.denom().to_dict(), K0.gens, K1.gens)
if K1.dom != K0.dom:
ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ]
dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ]
return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs))))
def get_ring(self):
"""Returns a ring associated with ``self``. """
from sympy.polys.domains import PolynomialRing
return PolynomialRing(self.dom, *self.gens)
def poly_ring(self, *gens):
"""Returns a polynomial ring, i.e. `K[X]`. """
raise NotImplementedError('nested domains not allowed')
def frac_field(self, *gens):
"""Returns a fraction field, i.e. `K(X)`. """
raise NotImplementedError('nested domains not allowed')
def is_positive(self, a):
"""Returns True if ``a`` is positive. """
return self.dom.is_positive(a.numer().LC())
def is_negative(self, a):
"""Returns True if ``a`` is negative. """
return self.dom.is_negative(a.numer().LC())
def is_nonpositive(self, a):
"""Returns True if ``a`` is non-positive. """
return self.dom.is_nonpositive(a.numer().LC())
def is_nonnegative(self, a):
"""Returns True if ``a`` is non-negative. """
return self.dom.is_nonnegative(a.numer().LC())
def numer(self, a):
"""Returns numerator of ``a``. """
return a.numer()
def denom(self, a):
"""Returns denominator of ``a``. """
return a.denom()
def factorial(self, a):
"""Returns factorial of ``a``. """
return self.dtype(self.dom.factorial(a))

View File

@ -0,0 +1,490 @@
"""Implementation of :class:`PolynomialRing` class. """
from sympy.polys.agca.modules import FreeModulePolyRing
from sympy.polys.domains.compositedomain import CompositeDomain
from sympy.polys.domains.old_fractionfield import FractionField
from sympy.polys.domains.ring import Ring
from sympy.polys.orderings import monomial_key, build_product_order
from sympy.polys.polyclasses import DMP, DMF
from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError,
CoercionFailed, ExactQuotientFailed, NotReversible)
from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
from sympy.utilities import public
from sympy.utilities.iterables import iterable
@public
class PolynomialRingBase(Ring, CompositeDomain):
"""
Base class for generalized polynomial rings.
This base class should be used for uniform access to generalized polynomial
rings. Subclasses only supply information about the element storage etc.
Do not instantiate.
"""
has_assoc_Ring = True
has_assoc_Field = True
default_order = "grevlex"
def __init__(self, dom, *gens, **opts):
if not gens:
raise GeneratorsNeeded("generators not specified")
lev = len(gens) - 1
self.ngens = len(gens)
self.zero = self.dtype.zero(lev, dom)
self.one = self.dtype.one(lev, dom)
self.domain = self.dom = dom
self.symbols = self.gens = gens
# NOTE 'order' may not be set if inject was called through CompositeDomain
self.order = opts.get('order', monomial_key(self.default_order))
def set_domain(self, dom):
"""Return a new polynomial ring with given domain. """
return self.__class__(dom, *self.gens, order=self.order)
def new(self, element):
return self.dtype(element, self.dom, len(self.gens) - 1)
def _ground_new(self, element):
return self.one.ground_new(element)
def _from_dict(self, element):
return DMP.from_dict(element, len(self.gens) - 1, self.dom)
def __str__(self):
s_order = str(self.order)
orderstr = (
" order=" + s_order) if s_order != self.default_order else ""
return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']'
def __hash__(self):
return hash((self.__class__.__name__, self.dtype, self.dom,
self.gens, self.order))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
return isinstance(other, PolynomialRingBase) and \
self.dtype == other.dtype and self.dom == other.dom and \
self.gens == other.gens and self.order == other.order
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_QQ(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return K1._ground_new(K1.dom.convert(a, K0))
def from_AlgebraicField(K1, a, K0):
"""Convert a ``ANP`` object to ``dtype``. """
if K1.dom == K0:
return K1._ground_new(a)
def from_PolynomialRing(K1, a, K0):
"""Convert a ``PolyElement`` object to ``dtype``. """
if K1.gens == K0.symbols:
if K1.dom == K0.dom:
return K1(dict(a)) # set the correct ring
else:
convert_dom = lambda c: K1.dom.convert_from(c, K0.dom)
return K1._from_dict({m: convert_dom(c) for m, c in a.items()})
else:
monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens)
if K1.dom != K0.dom:
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
return K1._from_dict(dict(zip(monoms, coeffs)))
def from_GlobalPolynomialRing(K1, a, K0):
"""Convert a ``DMP`` object to ``dtype``. """
if K1.gens == K0.gens:
if K1.dom != K0.dom:
a = a.convert(K1.dom)
return K1(a.to_list())
else:
monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
if K1.dom != K0.dom:
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
return K1(dict(zip(monoms, coeffs)))
def get_field(self):
"""Returns a field associated with ``self``. """
return FractionField(self.dom, *self.gens)
def poly_ring(self, *gens):
"""Returns a polynomial ring, i.e. ``K[X]``. """
raise NotImplementedError('nested domains not allowed')
def frac_field(self, *gens):
"""Returns a fraction field, i.e. ``K(X)``. """
raise NotImplementedError('nested domains not allowed')
def revert(self, a):
try:
return self.exquo(self.one, a)
except (ExactQuotientFailed, ZeroDivisionError):
raise NotReversible('%s is not a unit' % a)
def gcdex(self, a, b):
"""Extended GCD of ``a`` and ``b``. """
return a.gcdex(b)
def gcd(self, a, b):
"""Returns GCD of ``a`` and ``b``. """
return a.gcd(b)
def lcm(self, a, b):
"""Returns LCM of ``a`` and ``b``. """
return a.lcm(b)
def factorial(self, a):
"""Returns factorial of ``a``. """
return self.dtype(self.dom.factorial(a))
def _vector_to_sdm(self, v, order):
"""
For internal use by the modules class.
Convert an iterable of elements of this ring into a sparse distributed
module element.
"""
raise NotImplementedError
def _sdm_to_dics(self, s, n):
"""Helper for _sdm_to_vector."""
from sympy.polys.distributedmodules import sdm_to_dict
dic = sdm_to_dict(s)
res = [{} for _ in range(n)]
for k, v in dic.items():
res[k[0]][k[1:]] = v
return res
def _sdm_to_vector(self, s, n):
"""
For internal use by the modules class.
Convert a sparse distributed module into a list of length ``n``.
Examples
========
>>> from sympy import QQ, ilex
>>> from sympy.abc import x, y
>>> R = QQ.old_poly_ring(x, y, order=ilex)
>>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))]
>>> R._sdm_to_vector(L, 2)
[DMF([[1], [2, 0]], [[1]], QQ), DMF([[1, 0], []], [[1]], QQ)]
"""
dics = self._sdm_to_dics(s, n)
# NOTE this works for global and local rings!
return [self(x) for x in dics]
def free_module(self, rank):
"""
Generate a free module of rank ``rank`` over ``self``.
Examples
========
>>> from sympy.abc import x
>>> from sympy import QQ
>>> QQ.old_poly_ring(x).free_module(2)
QQ[x]**2
"""
return FreeModulePolyRing(self, rank)
def _vector_to_sdm_helper(v, order):
"""Helper method for common code in Global and Local poly rings."""
from sympy.polys.distributedmodules import sdm_from_dict
d = {}
for i, e in enumerate(v):
for key, value in e.to_dict().items():
d[(i,) + key] = value
return sdm_from_dict(d, order)
@public
class GlobalPolynomialRing(PolynomialRingBase):
"""A true polynomial ring, with objects DMP. """
is_PolynomialRing = is_Poly = True
dtype = DMP
def new(self, element):
if isinstance(element, dict):
return DMP.from_dict(element, len(self.gens) - 1, self.dom)
elif element in self.dom:
return self._ground_new(self.dom.convert(element))
else:
return self.dtype(element, self.dom, len(self.gens) - 1)
def from_FractionField(K1, a, K0):
"""
Convert a ``DMF`` object to ``DMP``.
Examples
========
>>> from sympy.polys.polyclasses import DMP, DMF
>>> from sympy.polys.domains import ZZ
>>> from sympy.abc import x
>>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ)
>>> K = ZZ.old_frac_field(x)
>>> F = ZZ.old_poly_ring(x).from_FractionField(f, K)
>>> F == DMP([ZZ(1), ZZ(1)], ZZ)
True
>>> type(F) # doctest: +SKIP
<class 'sympy.polys.polyclasses.DMP_Python'>
"""
if a.denom().is_one:
return K1.from_GlobalPolynomialRing(a.numer(), K0)
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return basic_from_dict(a.to_sympy_dict(), *self.gens)
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
try:
rep, _ = dict_from_basic(a, gens=self.gens)
except PolynomialError:
raise CoercionFailed("Cannot convert %s to type %s" % (a, self))
for k, v in rep.items():
rep[k] = self.dom.from_sympy(v)
return DMP.from_dict(rep, self.ngens - 1, self.dom)
def is_positive(self, a):
"""Returns True if ``LC(a)`` is positive. """
return self.dom.is_positive(a.LC())
def is_negative(self, a):
"""Returns True if ``LC(a)`` is negative. """
return self.dom.is_negative(a.LC())
def is_nonpositive(self, a):
"""Returns True if ``LC(a)`` is non-positive. """
return self.dom.is_nonpositive(a.LC())
def is_nonnegative(self, a):
"""Returns True if ``LC(a)`` is non-negative. """
return self.dom.is_nonnegative(a.LC())
def _vector_to_sdm(self, v, order):
"""
Examples
========
>>> from sympy import lex, QQ
>>> from sympy.abc import x, y
>>> R = QQ.old_poly_ring(x, y)
>>> f = R.convert(x + 2*y)
>>> g = R.convert(x * y)
>>> R._vector_to_sdm([f, g], lex)
[((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)]
"""
return _vector_to_sdm_helper(v, order)
class GeneralizedPolynomialRing(PolynomialRingBase):
"""A generalized polynomial ring, with objects DMF. """
dtype = DMF
def new(self, a):
"""Construct an element of ``self`` domain from ``a``. """
res = self.dtype(a, self.dom, len(self.gens) - 1)
# make sure res is actually in our ring
if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens):
from sympy.printing.str import sstr
raise CoercionFailed("denominator %s not allowed in %s"
% (sstr(res), self))
return res
def __contains__(self, a):
try:
a = self.convert(a)
except CoercionFailed:
return False
return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens)
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
def from_sympy(self, a):
"""Convert SymPy's expression to ``dtype``. """
p, q = a.as_numer_denom()
num, _ = dict_from_basic(p, gens=self.gens)
den, _ = dict_from_basic(q, gens=self.gens)
for k, v in num.items():
num[k] = self.dom.from_sympy(v)
for k, v in den.items():
den[k] = self.dom.from_sympy(v)
return self((num, den)).cancel()
def exquo(self, a, b):
"""Exact quotient of ``a`` and ``b``. """
# Elements are DMF that will always divide (except 0). The result is
# not guaranteed to be in this ring, so we have to check that.
r = a / b
try:
r = self.new((r.num, r.den))
except CoercionFailed:
raise ExactQuotientFailed(a, b, self)
return r
def from_FractionField(K1, a, K0):
dmf = K1.get_field().from_FractionField(a, K0)
return K1((dmf.num, dmf.den))
def _vector_to_sdm(self, v, order):
"""
Turn an iterable into a sparse distributed module.
Note that the vector is multiplied by a unit first to make all entries
polynomials.
Examples
========
>>> from sympy import ilex, QQ
>>> from sympy.abc import x, y
>>> R = QQ.old_poly_ring(x, y, order=ilex)
>>> f = R.convert((x + 2*y) / (1 + x))
>>> g = R.convert(x * y)
>>> R._vector_to_sdm([f, g], ilex)
[((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1,
2, 1), 1)]
"""
# NOTE this is quite inefficient...
u = self.one.numer()
for x in v:
u *= x.denom()
return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order)
@public
def PolynomialRing(dom, *gens, **opts):
r"""
Create a generalized multivariate polynomial ring.
A generalized polynomial ring is defined by a ground field `K`, a set
of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`.
The monomial order can be global, local or mixed. In any case it induces
a total ordering on the monomials, and there exists for every (non-zero)
polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial"
`LM(f) = LM(f, >)`. One can then define a multiplicative subset
`S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized
polynomial ring corresponding to the monomial order is
`R = S^{-1}K[x_1, \ldots, x_n]`.
If `>` is a so-called global order, that is `1` is the smallest monomial,
then we just have `S = K` and `R = K[x_1, \ldots, x_n]`.
Examples
========
A few examples may make this clearer.
>>> from sympy.abc import x, y
>>> from sympy import QQ
Our first ring uses global lexicographic order.
>>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),))
The second ring uses local lexicographic order. Note that when using a
single (non-product) order, you can just specify the name and omit the
variables:
>>> R2 = QQ.old_poly_ring(x, y, order="ilex")
The third and fourth rings use a mixed orders:
>>> o1 = (("ilex", x), ("lex", y))
>>> o2 = (("lex", x), ("ilex", y))
>>> R3 = QQ.old_poly_ring(x, y, order=o1)
>>> R4 = QQ.old_poly_ring(x, y, order=o2)
We will investigate what elements of `K(x, y)` are contained in the various
rings.
>>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)]
>>> test = lambda R: [f in R for f in L]
The first ring is just `K[x, y]`:
>>> test(R1)
[True, False, False, False, False]
The second ring is R1 localised at the maximal ideal (x, y):
>>> test(R2)
[True, False, True, True, True]
The third ring is R1 localised at the prime ideal (x):
>>> test(R3)
[True, False, True, False, True]
Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`:
>>> test(R4)
[True, False, False, True, False]
"""
order = opts.get("order", GeneralizedPolynomialRing.default_order)
if iterable(order):
order = build_product_order(order, gens)
order = monomial_key(order)
opts['order'] = order
if order.is_global:
return GlobalPolynomialRing(dom, *gens, **opts)
else:
return GeneralizedPolynomialRing(dom, *gens, **opts)

View File

@ -0,0 +1,199 @@
"""Implementation of :class:`PolynomialRing` class. """
from sympy.polys.domains.ring import Ring
from sympy.polys.domains.compositedomain import CompositeDomain
from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
from sympy.utilities import public
@public
class PolynomialRing(Ring, CompositeDomain):
"""A class for representing multivariate polynomial rings. """
is_PolynomialRing = is_Poly = True
has_assoc_Ring = True
has_assoc_Field = True
def __init__(self, domain_or_ring, symbols=None, order=None):
from sympy.polys.rings import PolyRing
if isinstance(domain_or_ring, PolyRing) and symbols is None and order is None:
ring = domain_or_ring
else:
ring = PolyRing(symbols, domain_or_ring, order)
self.ring = ring
self.dtype = ring.dtype
self.gens = ring.gens
self.ngens = ring.ngens
self.symbols = ring.symbols
self.domain = ring.domain
if symbols:
if ring.domain.is_Field and ring.domain.is_Exact and len(symbols)==1:
self.is_PID = True
# TODO: remove this
self.dom = self.domain
def new(self, element):
return self.ring.ring_new(element)
@property
def zero(self):
return self.ring.zero
@property
def one(self):
return self.ring.one
@property
def order(self):
return self.ring.order
def __str__(self):
return str(self.domain) + '[' + ','.join(map(str, self.symbols)) + ']'
def __hash__(self):
return hash((self.__class__.__name__, self.dtype.ring, self.domain, self.symbols))
def __eq__(self, other):
"""Returns `True` if two domains are equivalent. """
return isinstance(other, PolynomialRing) and \
(self.dtype.ring, self.domain, self.symbols) == \
(other.dtype.ring, other.domain, other.symbols)
def is_unit(self, a):
"""Returns ``True`` if ``a`` is a unit of ``self``"""
if not a.is_ground:
return False
K = self.domain
return K.is_unit(K.convert_from(a, self))
def canonical_unit(self, a):
u = self.domain.canonical_unit(a.LC)
return self.ring.ground_new(u)
def to_sympy(self, a):
"""Convert `a` to a SymPy object. """
return a.as_expr()
def from_sympy(self, a):
"""Convert SymPy's expression to `dtype`. """
return self.ring.from_expr(a)
def from_ZZ(K1, a, K0):
"""Convert a Python `int` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_ZZ_python(K1, a, K0):
"""Convert a Python `int` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_QQ(K1, a, K0):
"""Convert a Python `Fraction` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_QQ_python(K1, a, K0):
"""Convert a Python `Fraction` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY `mpz` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY `mpq` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_GaussianIntegerRing(K1, a, K0):
"""Convert a `GaussianInteger` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_GaussianRationalField(K1, a, K0):
"""Convert a `GaussianRational` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_RealField(K1, a, K0):
"""Convert a mpmath `mpf` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_ComplexField(K1, a, K0):
"""Convert a mpmath `mpf` object to `dtype`. """
return K1(K1.domain.convert(a, K0))
def from_AlgebraicField(K1, a, K0):
"""Convert an algebraic number to ``dtype``. """
if K1.domain != K0:
a = K1.domain.convert_from(a, K0)
if a is not None:
return K1.new(a)
def from_PolynomialRing(K1, a, K0):
"""Convert a polynomial to ``dtype``. """
try:
return a.set_ring(K1.ring)
except (CoercionFailed, GeneratorsError):
return None
def from_FractionField(K1, a, K0):
"""Convert a rational function to ``dtype``. """
if K1.domain == K0:
return K1.ring.from_list([a])
q, r = K0.numer(a).div(K0.denom(a))
if r.is_zero:
return K1.from_PolynomialRing(q, K0.field.ring.to_domain())
else:
return None
def from_GlobalPolynomialRing(K1, a, K0):
"""Convert from old poly ring to ``dtype``. """
if K1.symbols == K0.gens:
ad = a.to_dict()
if K1.domain != K0.domain:
ad = {m: K1.domain.convert(c) for m, c in ad.items()}
return K1(ad)
elif a.is_ground and K0.domain == K1:
return K1.convert_from(a.to_list()[0], K0.domain)
def get_field(self):
"""Returns a field associated with `self`. """
return self.ring.to_field().to_domain()
def is_positive(self, a):
"""Returns True if `LC(a)` is positive. """
return self.domain.is_positive(a.LC)
def is_negative(self, a):
"""Returns True if `LC(a)` is negative. """
return self.domain.is_negative(a.LC)
def is_nonpositive(self, a):
"""Returns True if `LC(a)` is non-positive. """
return self.domain.is_nonpositive(a.LC)
def is_nonnegative(self, a):
"""Returns True if `LC(a)` is non-negative. """
return self.domain.is_nonnegative(a.LC)
def gcdex(self, a, b):
"""Extended GCD of `a` and `b`. """
return a.gcdex(b)
def gcd(self, a, b):
"""Returns GCD of `a` and `b`. """
return a.gcd(b)
def lcm(self, a, b):
"""Returns LCM of `a` and `b`. """
return a.lcm(b)
def factorial(self, a):
"""Returns factorial of `a`. """
return self.dtype(self.domain.factorial(a))

View File

@ -0,0 +1,16 @@
"""Implementation of :class:`PythonFiniteField` class. """
from sympy.polys.domains.finitefield import FiniteField
from sympy.polys.domains.pythonintegerring import PythonIntegerRing
from sympy.utilities import public
@public
class PythonFiniteField(FiniteField):
"""Finite field based on Python's integers. """
alias = 'FF_python'
def __init__(self, mod, symmetric=True):
super().__init__(mod, PythonIntegerRing(), symmetric)

View File

@ -0,0 +1,98 @@
"""Implementation of :class:`PythonIntegerRing` class. """
from sympy.core.numbers import int_valued
from sympy.polys.domains.groundtypes import (
PythonInteger, SymPyInteger, sqrt as python_sqrt,
factorial as python_factorial, python_gcdex, python_gcd, python_lcm,
)
from sympy.polys.domains.integerring import IntegerRing
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class PythonIntegerRing(IntegerRing):
"""Integer ring based on Python's ``int`` type.
This will be used as :ref:`ZZ` if ``gmpy`` and ``gmpy2`` are not
installed. Elements are instances of the standard Python ``int`` type.
"""
dtype = PythonInteger
zero = dtype(0)
one = dtype(1)
alias = 'ZZ_python'
def __init__(self):
"""Allow instantiation of this domain. """
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyInteger(a)
def from_sympy(self, a):
"""Convert SymPy's Integer to ``dtype``. """
if a.is_Integer:
return PythonInteger(a.p)
elif int_valued(a):
return PythonInteger(int(a))
else:
raise CoercionFailed("expected an integer, got %s" % a)
def from_FF_python(K1, a, K0):
"""Convert ``ModularInteger(int)`` to Python's ``int``. """
return K0.to_int(a)
def from_ZZ_python(K1, a, K0):
"""Convert Python's ``int`` to Python's ``int``. """
return a
def from_QQ(K1, a, K0):
"""Convert Python's ``Fraction`` to Python's ``int``. """
if a.denominator == 1:
return a.numerator
def from_QQ_python(K1, a, K0):
"""Convert Python's ``Fraction`` to Python's ``int``. """
if a.denominator == 1:
return a.numerator
def from_FF_gmpy(K1, a, K0):
"""Convert ``ModularInteger(mpz)`` to Python's ``int``. """
return PythonInteger(K0.to_int(a))
def from_ZZ_gmpy(K1, a, K0):
"""Convert GMPY's ``mpz`` to Python's ``int``. """
return PythonInteger(a)
def from_QQ_gmpy(K1, a, K0):
"""Convert GMPY's ``mpq`` to Python's ``int``. """
if a.denom() == 1:
return PythonInteger(a.numer())
def from_RealField(K1, a, K0):
"""Convert mpmath's ``mpf`` to Python's ``int``. """
p, q = K0.to_rational(a)
if q == 1:
return PythonInteger(p)
def gcdex(self, a, b):
"""Compute extended GCD of ``a`` and ``b``. """
return python_gcdex(a, b)
def gcd(self, a, b):
"""Compute GCD of ``a`` and ``b``. """
return python_gcd(a, b)
def lcm(self, a, b):
"""Compute LCM of ``a`` and ``b``. """
return python_lcm(a, b)
def sqrt(self, a):
"""Compute square root of ``a``. """
return python_sqrt(a)
def factorial(self, a):
"""Compute factorial of ``a``. """
return python_factorial(a)

View File

@ -0,0 +1,22 @@
"""
Rational number type based on Python integers.
The PythonRational class from here has been moved to
sympy.external.pythonmpq
This module is just left here for backwards compatibility.
"""
from sympy.core.numbers import Rational
from sympy.core.sympify import _sympy_converter
from sympy.utilities import public
from sympy.external.pythonmpq import PythonMPQ
PythonRational = public(PythonMPQ)
def sympify_pythonrational(arg):
return Rational(arg.numerator, arg.denominator)
_sympy_converter[PythonRational] = sympify_pythonrational

View File

@ -0,0 +1,73 @@
"""Implementation of :class:`PythonRationalField` class. """
from sympy.polys.domains.groundtypes import PythonInteger, PythonRational, SymPyRational
from sympy.polys.domains.rationalfield import RationalField
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class PythonRationalField(RationalField):
"""Rational field based on :ref:`MPQ`.
This will be used as :ref:`QQ` if ``gmpy`` and ``gmpy2`` are not
installed. Elements are instances of :ref:`MPQ`.
"""
dtype = PythonRational
zero = dtype(0)
one = dtype(1)
alias = 'QQ_python'
def __init__(self):
pass
def get_ring(self):
"""Returns ring associated with ``self``. """
from sympy.polys.domains import PythonIntegerRing
return PythonIntegerRing()
def to_sympy(self, a):
"""Convert `a` to a SymPy object. """
return SymPyRational(a.numerator, a.denominator)
def from_sympy(self, a):
"""Convert SymPy's Rational to `dtype`. """
if a.is_Rational:
return PythonRational(a.p, a.q)
elif a.is_Float:
from sympy.polys.domains import RR
p, q = RR.to_rational(a)
return PythonRational(int(p), int(q))
else:
raise CoercionFailed("expected `Rational` object, got %s" % a)
def from_ZZ_python(K1, a, K0):
"""Convert a Python `int` object to `dtype`. """
return PythonRational(a)
def from_QQ_python(K1, a, K0):
"""Convert a Python `Fraction` object to `dtype`. """
return a
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY `mpz` object to `dtype`. """
return PythonRational(PythonInteger(a))
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY `mpq` object to `dtype`. """
return PythonRational(PythonInteger(a.numer()),
PythonInteger(a.denom()))
def from_RealField(K1, a, K0):
"""Convert a mpmath `mpf` object to `dtype`. """
p, q = K0.to_rational(a)
return PythonRational(int(p), int(q))
def numer(self, a):
"""Returns numerator of `a`. """
return a.numerator
def denom(self, a):
"""Returns denominator of `a`. """
return a.denominator

View File

@ -0,0 +1,202 @@
"""Implementation of :class:`QuotientRing` class."""
from sympy.polys.agca.modules import FreeModuleQuotientRing
from sympy.polys.domains.ring import Ring
from sympy.polys.polyerrors import NotReversible, CoercionFailed
from sympy.utilities import public
# TODO
# - successive quotients (when quotient ideals are implemented)
# - poly rings over quotients?
# - division by non-units in integral domains?
@public
class QuotientRingElement:
"""
Class representing elements of (commutative) quotient rings.
Attributes:
- ring - containing ring
- data - element of ring.ring (i.e. base ring) representing self
"""
def __init__(self, ring, data):
self.ring = ring
self.data = data
def __str__(self):
from sympy.printing.str import sstr
data = self.ring.ring.to_sympy(self.data)
return sstr(data) + " + " + str(self.ring.base_ideal)
__repr__ = __str__
def __bool__(self):
return not self.ring.is_zero(self)
def __add__(self, om):
if not isinstance(om, self.__class__) or om.ring != self.ring:
try:
om = self.ring.convert(om)
except (NotImplementedError, CoercionFailed):
return NotImplemented
return self.ring(self.data + om.data)
__radd__ = __add__
def __neg__(self):
return self.ring(self.data*self.ring.ring.convert(-1))
def __sub__(self, om):
return self.__add__(-om)
def __rsub__(self, om):
return (-self).__add__(om)
def __mul__(self, o):
if not isinstance(o, self.__class__):
try:
o = self.ring.convert(o)
except (NotImplementedError, CoercionFailed):
return NotImplemented
return self.ring(self.data*o.data)
__rmul__ = __mul__
def __rtruediv__(self, o):
return self.ring.revert(self)*o
def __truediv__(self, o):
if not isinstance(o, self.__class__):
try:
o = self.ring.convert(o)
except (NotImplementedError, CoercionFailed):
return NotImplemented
return self.ring.revert(o)*self
def __pow__(self, oth):
if oth < 0:
return self.ring.revert(self) ** -oth
return self.ring(self.data ** oth)
def __eq__(self, om):
if not isinstance(om, self.__class__) or om.ring != self.ring:
return False
return self.ring.is_zero(self - om)
def __ne__(self, om):
return not self == om
class QuotientRing(Ring):
"""
Class representing (commutative) quotient rings.
You should not usually instantiate this by hand, instead use the constructor
from the base ring in the construction.
>>> from sympy.abc import x
>>> from sympy import QQ
>>> I = QQ.old_poly_ring(x).ideal(x**3 + 1)
>>> QQ.old_poly_ring(x).quotient_ring(I)
QQ[x]/<x**3 + 1>
Shorter versions are possible:
>>> QQ.old_poly_ring(x)/I
QQ[x]/<x**3 + 1>
>>> QQ.old_poly_ring(x)/[x**3 + 1]
QQ[x]/<x**3 + 1>
Attributes:
- ring - the base ring
- base_ideal - the ideal used to form the quotient
"""
has_assoc_Ring = True
has_assoc_Field = False
dtype = QuotientRingElement
def __init__(self, ring, ideal):
if not ideal.ring == ring:
raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal))
self.ring = ring
self.base_ideal = ideal
self.zero = self(self.ring.zero)
self.one = self(self.ring.one)
def __str__(self):
return str(self.ring) + "/" + str(self.base_ideal)
def __hash__(self):
return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal))
def new(self, a):
"""Construct an element of ``self`` domain from ``a``. """
if not isinstance(a, self.ring.dtype):
a = self.ring(a)
# TODO optionally disable reduction?
return self.dtype(self, self.base_ideal.reduce_element(a))
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
return isinstance(other, QuotientRing) and \
self.ring == other.ring and self.base_ideal == other.base_ideal
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return K1(K1.ring.convert(a, K0))
from_ZZ_python = from_ZZ
from_QQ_python = from_ZZ_python
from_ZZ_gmpy = from_ZZ_python
from_QQ_gmpy = from_ZZ_python
from_RealField = from_ZZ_python
from_GlobalPolynomialRing = from_ZZ_python
from_FractionField = from_ZZ_python
def from_sympy(self, a):
return self(self.ring.from_sympy(a))
def to_sympy(self, a):
return self.ring.to_sympy(a.data)
def from_QuotientRing(self, a, K0):
if K0 == self:
return a
def poly_ring(self, *gens):
"""Returns a polynomial ring, i.e. ``K[X]``. """
raise NotImplementedError('nested domains not allowed')
def frac_field(self, *gens):
"""Returns a fraction field, i.e. ``K(X)``. """
raise NotImplementedError('nested domains not allowed')
def revert(self, a):
"""
Compute a**(-1), if possible.
"""
I = self.ring.ideal(a.data) + self.base_ideal
try:
return self(I.in_terms_of_generators(1)[0])
except ValueError: # 1 not in I
raise NotReversible('%s not a unit in %r' % (a, self))
def is_zero(self, a):
return self.base_ideal.contains(a.data)
def free_module(self, rank):
"""
Generate a free module of rank ``rank`` over ``self``.
>>> from sympy.abc import x
>>> from sympy import QQ
>>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2)
(QQ[x]/<x**2 + 1>)**2
"""
return FreeModuleQuotientRing(self, rank)

View File

@ -0,0 +1,200 @@
"""Implementation of :class:`RationalField` class. """
from sympy.external.gmpy import MPQ
from sympy.polys.domains.groundtypes import SymPyRational, is_square, sqrtrem
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class RationalField(Field, CharacteristicZero, SimpleDomain):
r"""Abstract base class for the domain :ref:`QQ`.
The :py:class:`RationalField` class represents the field of rational
numbers $\mathbb{Q}$ as a :py:class:`~.Domain` in the domain system.
:py:class:`RationalField` is a superclass of
:py:class:`PythonRationalField` and :py:class:`GMPYRationalField` one of
which will be the implementation for :ref:`QQ` depending on whether either
of ``gmpy`` or ``gmpy2`` is installed or not.
See also
========
Domain
"""
rep = 'QQ'
alias = 'QQ'
is_RationalField = is_QQ = True
is_Numerical = True
has_assoc_Ring = True
has_assoc_Field = True
dtype = MPQ
zero = dtype(0)
one = dtype(1)
tp = type(one)
def __init__(self):
pass
def __eq__(self, other):
"""Returns ``True`` if two domains are equivalent. """
if isinstance(other, RationalField):
return True
else:
return NotImplemented
def __hash__(self):
"""Returns hash code of ``self``. """
return hash('QQ')
def get_ring(self):
"""Returns ring associated with ``self``. """
from sympy.polys.domains import ZZ
return ZZ
def to_sympy(self, a):
"""Convert ``a`` to a SymPy object. """
return SymPyRational(int(a.numerator), int(a.denominator))
def from_sympy(self, a):
"""Convert SymPy's Integer to ``dtype``. """
if a.is_Rational:
return MPQ(a.p, a.q)
elif a.is_Float:
from sympy.polys.domains import RR
return MPQ(*map(int, RR.to_rational(a)))
else:
raise CoercionFailed("expected `Rational` object, got %s" % a)
def algebraic_field(self, *extension, alias=None):
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
Parameters
==========
*extension : One or more :py:class:`~.Expr`
Generators of the extension. These should be expressions that are
algebraic over `\mathbb{Q}`.
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
If provided, this will be used as the alias symbol for the
primitive element of the returned :py:class:`~.AlgebraicField`.
Returns
=======
:py:class:`~.AlgebraicField`
A :py:class:`~.Domain` representing the algebraic field extension.
Examples
========
>>> from sympy import QQ, sqrt
>>> QQ.algebraic_field(sqrt(2))
QQ<sqrt(2)>
"""
from sympy.polys.domains import AlgebraicField
return AlgebraicField(self, *extension, alias=alias)
def from_AlgebraicField(K1, a, K0):
"""Convert a :py:class:`~.ANP` object to :ref:`QQ`.
See :py:meth:`~.Domain.convert`
"""
if a.is_ground:
return K1.convert(a.LC(), K0.dom)
def from_ZZ(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return MPQ(a)
def from_ZZ_python(K1, a, K0):
"""Convert a Python ``int`` object to ``dtype``. """
return MPQ(a)
def from_QQ(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return MPQ(a.numerator, a.denominator)
def from_QQ_python(K1, a, K0):
"""Convert a Python ``Fraction`` object to ``dtype``. """
return MPQ(a.numerator, a.denominator)
def from_ZZ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpz`` object to ``dtype``. """
return MPQ(a)
def from_QQ_gmpy(K1, a, K0):
"""Convert a GMPY ``mpq`` object to ``dtype``. """
return a
def from_GaussianRationalField(K1, a, K0):
"""Convert a ``GaussianElement`` object to ``dtype``. """
if a.y == 0:
return MPQ(a.x)
def from_RealField(K1, a, K0):
"""Convert a mpmath ``mpf`` object to ``dtype``. """
return MPQ(*map(int, K0.to_rational(a)))
def exquo(self, a, b):
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
return MPQ(a) / MPQ(b)
def quo(self, a, b):
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
return MPQ(a) / MPQ(b)
def rem(self, a, b):
"""Remainder of ``a`` and ``b``, implies nothing. """
return self.zero
def div(self, a, b):
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
return MPQ(a) / MPQ(b), self.zero
def numer(self, a):
"""Returns numerator of ``a``. """
return a.numerator
def denom(self, a):
"""Returns denominator of ``a``. """
return a.denominator
def is_square(self, a):
"""Return ``True`` if ``a`` is a square.
Explanation
===========
A rational number is a square if and only if there exists
a rational number ``b`` such that ``b * b == a``.
"""
return is_square(a.numerator) and is_square(a.denominator)
def exsqrt(self, a):
"""Non-negative square root of ``a`` if ``a`` is a square.
See also
========
is_square
"""
if a.numerator < 0: # denominator is always positive
return None
p_sqrt, p_rem = sqrtrem(a.numerator)
if p_rem != 0:
return None
q_sqrt, q_rem = sqrtrem(a.denominator)
if q_rem != 0:
return None
return MPQ(p_sqrt, q_sqrt)
QQ = RationalField()

View File

@ -0,0 +1,167 @@
"""Implementation of :class:`RealField` class. """
from sympy.external.gmpy import SYMPY_INTS
from sympy.core.numbers import Float
from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.domains.characteristiczero import CharacteristicZero
from sympy.polys.domains.mpelements import MPContext
from sympy.polys.polyerrors import CoercionFailed
from sympy.utilities import public
@public
class RealField(Field, CharacteristicZero, SimpleDomain):
"""Real numbers up to the given precision. """
rep = 'RR'
is_RealField = is_RR = True
is_Exact = False
is_Numerical = True
is_PID = False
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._context.tolerance
def __init__(self, prec=_default_precision, dps=None, tol=None):
context = MPContext(prec, dps, tol, True)
context._parent = self
self._context = context
self._dtype = context.mpf
self.zero = self.dtype(0)
self.one = self.dtype(1)
@property
def tp(self):
# XXX: Domain treats tp as an alis of dtype. Here we need to 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, arg):
# 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(arg, SYMPY_INTS):
arg = int(arg)
return self._dtype(arg)
def __eq__(self, other):
return (isinstance(other, RealField)
and self.precision == other.precision
and self.tolerance == other.tolerance)
def __hash__(self):
return hash((self.__class__.__name__, self._dtype, self.precision, self.tolerance))
def to_sympy(self, element):
"""Convert ``element`` to SymPy number. """
return Float(element, self.dps)
def from_sympy(self, expr):
"""Convert SymPy's number to ``dtype``. """
number = expr.evalf(n=self.dps)
if number.is_Number:
return self.dtype(number)
else:
raise CoercionFailed("expected real number, got %s" % expr)
def from_ZZ(self, element, base):
return self.dtype(element)
def from_ZZ_python(self, element, base):
return self.dtype(element)
def from_ZZ_gmpy(self, element, base):
return self.dtype(int(element))
# XXX: We need to convert the denominators to int here because mpmath does
# not recognise mpz. Ideally mpmath would handle this and if it changed to
# do so then the calls to int here could be removed.
def from_QQ(self, element, base):
return self.dtype(element.numerator) / int(element.denominator)
def from_QQ_python(self, element, base):
return self.dtype(element.numerator) / int(element.denominator)
def from_QQ_gmpy(self, element, base):
return self.dtype(int(element.numerator)) / int(element.denominator)
def from_AlgebraicField(self, element, base):
return self.from_sympy(base.to_sympy(element).evalf(self.dps))
def from_RealField(self, element, base):
if self == base:
return element
else:
return self.dtype(element)
def from_ComplexField(self, element, base):
if not element.imag:
return self.dtype(element.real)
def to_rational(self, element, limit=True):
"""Convert a real number to rational number. """
return self._context.to_rational(element, limit)
def get_ring(self):
"""Returns a ring associated with ``self``. """
return self
def get_exact(self):
"""Returns an exact domain associated with ``self``. """
from sympy.polys.domains import QQ
return QQ
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`` if ``a >= 0`` and ``False`` otherwise. """
return a >= 0
def exsqrt(self, a):
"""Non-negative square root for ``a >= 0`` and ``None`` otherwise.
Explanation
===========
The square root may be slightly inaccurate due to floating point
rounding error.
"""
return a ** 0.5 if a >= 0 else None
RR = RealField()

View File

@ -0,0 +1,118 @@
"""Implementation of :class:`Ring` class. """
from sympy.polys.domains.domain import Domain
from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible
from sympy.utilities import public
@public
class Ring(Domain):
"""Represents a ring domain. """
is_Ring = True
def get_ring(self):
"""Returns a ring associated with ``self``. """
return self
def exquo(self, a, b):
"""Exact quotient of ``a`` and ``b``, implies ``__floordiv__``. """
if a % b:
raise ExactQuotientFailed(a, b, self)
else:
return a // b
def quo(self, a, b):
"""Quotient of ``a`` and ``b``, implies ``__floordiv__``. """
return a // b
def rem(self, a, b):
"""Remainder of ``a`` and ``b``, implies ``__mod__``. """
return a % b
def div(self, a, b):
"""Division of ``a`` and ``b``, implies ``__divmod__``. """
return divmod(a, b)
def invert(self, a, b):
"""Returns inversion of ``a mod b``. """
s, t, h = self.gcdex(a, b)
if self.is_one(h):
return s % b
else:
raise NotInvertible("zero divisor")
def revert(self, a):
"""Returns ``a**(-1)`` if possible. """
if self.is_one(a) or self.is_one(-a):
return a
else:
raise NotReversible('only units are reversible in a ring')
def is_unit(self, a):
try:
self.revert(a)
return True
except NotReversible:
return False
def numer(self, a):
"""Returns numerator of ``a``. """
return a
def denom(self, a):
"""Returns denominator of `a`. """
return self.one
def free_module(self, rank):
"""
Generate a free module of rank ``rank`` over self.
>>> from sympy.abc import x
>>> from sympy import QQ
>>> QQ.old_poly_ring(x).free_module(2)
QQ[x]**2
"""
raise NotImplementedError
def ideal(self, *gens):
"""
Generate an ideal of ``self``.
>>> from sympy.abc import x
>>> from sympy import QQ
>>> QQ.old_poly_ring(x).ideal(x**2)
<x**2>
"""
from sympy.polys.agca.ideals import ModuleImplementedIdeal
return ModuleImplementedIdeal(self, self.free_module(1).submodule(
*[[x] for x in gens]))
def quotient_ring(self, e):
"""
Form a quotient ring of ``self``.
Here ``e`` can be an ideal or an iterable.
>>> from sympy.abc import x
>>> from sympy import QQ
>>> QQ.old_poly_ring(x).quotient_ring(QQ.old_poly_ring(x).ideal(x**2))
QQ[x]/<x**2>
>>> QQ.old_poly_ring(x).quotient_ring([x**2])
QQ[x]/<x**2>
The division operator has been overloaded for this:
>>> QQ.old_poly_ring(x)/[x**2]
QQ[x]/<x**2>
"""
from sympy.polys.agca.ideals import Ideal
from sympy.polys.domains.quotientring import QuotientRing
if not isinstance(e, Ideal):
e = self.ideal(*e)
return QuotientRing(self, e)
def __truediv__(self, e):
return self.quotient_ring(e)

View File

@ -0,0 +1,15 @@
"""Implementation of :class:`SimpleDomain` class. """
from sympy.polys.domains.domain import Domain
from sympy.utilities import public
@public
class SimpleDomain(Domain):
"""Base class for simple domains, e.g. ZZ, QQ. """
is_Simple = True
def inject(self, *gens):
"""Inject generators into this domain. """
return self.poly_ring(*gens)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
"""Tests for the PolynomialRing classes. """
from sympy.polys.domains import QQ, ZZ
from sympy.polys.polyerrors import ExactQuotientFailed, CoercionFailed, NotReversible
from sympy.abc import x, y
from sympy.testing.pytest import raises
def test_build_order():
R = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y)))
assert R.order((1, 5)) == ((1,), (-5,))
def test_globalring():
Qxy = QQ.old_frac_field(x, y)
R = QQ.old_poly_ring(x, y)
X = R.convert(x)
Y = R.convert(y)
assert x in R
assert 1/x not in R
assert 1/(1 + x) not in R
assert Y in R
assert X * (Y**2 + 1) == R.convert(x * (y**2 + 1))
assert X + 1 == R.convert(x + 1)
raises(ExactQuotientFailed, lambda: X/Y)
raises(TypeError, lambda: x/Y)
raises(TypeError, lambda: X/y)
assert X**2 / X == X
assert R.from_GlobalPolynomialRing(ZZ.old_poly_ring(x, y).convert(x), ZZ.old_poly_ring(x, y)) == X
assert R.from_FractionField(Qxy.convert(x), Qxy) == X
assert R.from_FractionField(Qxy.convert(x/y), Qxy) is None
assert R._sdm_to_vector(R._vector_to_sdm([X, Y], R.order), 2) == [X, Y]
def test_localring():
Qxy = QQ.old_frac_field(x, y)
R = QQ.old_poly_ring(x, y, order="ilex")
X = R.convert(x)
Y = R.convert(y)
assert x in R
assert 1/x not in R
assert 1/(1 + x) in R
assert Y in R
assert X*(Y**2 + 1)/(1 + X) == R.convert(x*(y**2 + 1)/(1 + x))
raises(TypeError, lambda: x/Y)
raises(TypeError, lambda: X/y)
assert X + 1 == R.convert(x + 1)
assert X**2 / X == X
assert R.from_GlobalPolynomialRing(ZZ.old_poly_ring(x, y).convert(x), ZZ.old_poly_ring(x, y)) == X
assert R.from_FractionField(Qxy.convert(x), Qxy) == X
raises(CoercionFailed, lambda: R.from_FractionField(Qxy.convert(x/y), Qxy))
raises(ExactQuotientFailed, lambda: R.exquo(X, Y))
raises(NotReversible, lambda: R.revert(X))
assert R._sdm_to_vector(
R._vector_to_sdm([X/(X + 1), Y/(1 + X*Y)], R.order), 2) == \
[X*(1 + X*Y), Y*(1 + X)]
def test_conversion():
L = QQ.old_poly_ring(x, y, order="ilex")
G = QQ.old_poly_ring(x, y)
assert L.convert(x) == L.convert(G.convert(x), G)
assert G.convert(x) == G.convert(L.convert(x), L)
raises(CoercionFailed, lambda: G.convert(L.convert(1/(1 + x)), L))
def test_units():
R = QQ.old_poly_ring(x)
assert R.is_unit(R.convert(1))
assert R.is_unit(R.convert(2))
assert not R.is_unit(R.convert(x))
assert not R.is_unit(R.convert(1 + x))
R = QQ.old_poly_ring(x, order='ilex')
assert R.is_unit(R.convert(1))
assert R.is_unit(R.convert(2))
assert not R.is_unit(R.convert(x))
assert R.is_unit(R.convert(1 + x))
R = ZZ.old_poly_ring(x)
assert R.is_unit(R.convert(1))
assert not R.is_unit(R.convert(2))
assert not R.is_unit(R.convert(x))
assert not R.is_unit(R.convert(1 + x))

View File

@ -0,0 +1,52 @@
"""Tests for quotient rings."""
from sympy.polys.domains.integerring import ZZ
from sympy.polys.domains.rationalfield import QQ
from sympy.abc import x, y
from sympy.polys.polyerrors import NotReversible
from sympy.testing.pytest import raises
def test_QuotientRingElement():
R = QQ.old_poly_ring(x)/[x**10]
X = R.convert(x)
assert X*(X + 1) == R.convert(x**2 + x)
assert X*x == R.convert(x**2)
assert x*X == R.convert(x**2)
assert X + x == R.convert(2*x)
assert x + X == 2*X
assert X**2 == R.convert(x**2)
assert 1/(1 - X) == R.convert(sum(x**i for i in range(10)))
assert X**10 == R.zero
assert X != x
raises(NotReversible, lambda: 1/X)
def test_QuotientRing():
I = QQ.old_poly_ring(x).ideal(x**2 + 1)
R = QQ.old_poly_ring(x)/I
assert R == QQ.old_poly_ring(x)/[x**2 + 1]
assert R == QQ.old_poly_ring(x)/QQ.old_poly_ring(x).ideal(x**2 + 1)
assert R != QQ.old_poly_ring(x)
assert R.convert(1)/x == -x + I
assert -1 + I == x**2 + I
assert R.convert(ZZ(1), ZZ) == 1 + I
assert R.convert(R.convert(x), R) == R.convert(x)
X = R.convert(x)
Y = QQ.old_poly_ring(x).convert(x)
assert -1 + I == X**2 + I
assert -1 + I == Y**2 + I
assert R.to_sympy(X) == x
raises(ValueError, lambda: QQ.old_poly_ring(x)/QQ.old_poly_ring(x, y).ideal(x))
R = QQ.old_poly_ring(x, order="ilex")
I = R.ideal(x)
assert R.convert(1) + I == (R/I).convert(1)