chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -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',
|
||||
))
|
||||
@@ -0,0 +1,638 @@
|
||||
"""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.core.symbol import Dummy, symbols
|
||||
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.
|
||||
|
||||
ext = K.ext.as_expr()
|
||||
todom = K.dom.from_sympy
|
||||
toexpr = K.dom.to_sympy
|
||||
|
||||
if not ext.is_Add:
|
||||
powers = [ext**n for n in range(K.mod.degree())]
|
||||
else:
|
||||
# primitive_element generates a QQ-linear combination of lower degree
|
||||
# algebraic numbers to generate the higher degree extension e.g.
|
||||
# QQ<sqrt(2)+sqrt(3)> That means that we end up having high powers of low
|
||||
# degree algebraic numbers that can be reduced. Here we will use the
|
||||
# minimal polynomials of the algebraic numbers to reduce those powers
|
||||
# before converting to Expr.
|
||||
from sympy.polys.numberfields.minpoly import minpoly
|
||||
|
||||
# Decompose ext as a linear combination of gens and make a symbol for
|
||||
# each gen.
|
||||
gens, coeffs = zip(*ext.as_coefficients_dict().items())
|
||||
syms = symbols(f'a:{len(gens)}', cls=Dummy)
|
||||
sym2gen = dict(zip(syms, gens))
|
||||
|
||||
# Make a polynomial ring that can express ext and minpolys of all gens
|
||||
# in terms of syms.
|
||||
R = K.dom[syms]
|
||||
monoms = [R.ring.monomial_basis(i) for i in range(R.ngens)]
|
||||
ext_dict = {m: todom(c) for m, c in zip(monoms, coeffs)}
|
||||
ext_poly = R.ring.from_dict(ext_dict)
|
||||
minpolys = [R.from_sympy(minpoly(g, s)) for s, g in sym2gen.items()]
|
||||
|
||||
# Compute all powers of ext_poly reduced modulo minpolys
|
||||
powers = [R.one, ext_poly]
|
||||
for n in range(2, K.mod.degree()):
|
||||
ext_poly_n = (powers[-1] * ext_poly).rem(minpolys)
|
||||
powers.append(ext_poly_n)
|
||||
|
||||
# Convert the powers back to Expr. This will recombine some things like
|
||||
# sqrt(2)*sqrt(3) -> sqrt(6).
|
||||
powers = [p.as_expr().xreplace(sym2gen) for p in powers]
|
||||
|
||||
# This also expands some rational powers
|
||||
powers = [p.expand() for p in powers]
|
||||
|
||||
# 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]
|
||||
coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
|
||||
coeffs_sympy = [toexpr(c) for c in coeffs_dom]
|
||||
res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
|
||||
return res
|
||||
|
||||
return converter
|
||||
@@ -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
|
||||
@@ -0,0 +1,198 @@
|
||||
"""Implementation of :class:`ComplexField` class. """
|
||||
|
||||
|
||||
from sympy.external.gmpy import SYMPY_INTS
|
||||
from sympy.core.numbers import Float, I
|
||||
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
||||
from sympy.polys.domains.field import Field
|
||||
from sympy.polys.domains.gaussiandomains import QQ_I
|
||||
from sympy.polys.domains.simpledomain import SimpleDomain
|
||||
from sympy.polys.polyerrors import DomainError, CoercionFailed
|
||||
from sympy.utilities import public
|
||||
|
||||
from mpmath import MPContext
|
||||
|
||||
|
||||
@public
|
||||
class ComplexField(Field, CharacteristicZero, SimpleDomain):
|
||||
"""Complex numbers up to the given precision. """
|
||||
|
||||
rep = 'CC'
|
||||
|
||||
is_ComplexField = is_CC = True
|
||||
|
||||
is_Exact = False
|
||||
is_Numerical = True
|
||||
|
||||
has_assoc_Ring = False
|
||||
has_assoc_Field = True
|
||||
|
||||
_default_precision = 53
|
||||
|
||||
@property
|
||||
def has_default_precision(self):
|
||||
return self.precision == self._default_precision
|
||||
|
||||
@property
|
||||
def precision(self):
|
||||
return self._context.prec
|
||||
|
||||
@property
|
||||
def dps(self):
|
||||
return self._context.dps
|
||||
|
||||
@property
|
||||
def tolerance(self):
|
||||
return self._tolerance
|
||||
|
||||
def __init__(self, prec=None, dps=None, tol=None):
|
||||
# XXX: The tolerance parameter is ignored but is kept for backward
|
||||
# compatibility for now.
|
||||
|
||||
context = MPContext()
|
||||
|
||||
if prec is None and dps is None:
|
||||
context.prec = self._default_precision
|
||||
elif dps is None:
|
||||
context.prec = prec
|
||||
elif prec is None:
|
||||
context.dps = dps
|
||||
else:
|
||||
raise TypeError("Cannot set both prec and dps")
|
||||
|
||||
self._context = context
|
||||
|
||||
self._dtype = context.mpc
|
||||
self.zero = self.dtype(0)
|
||||
self.one = self.dtype(1)
|
||||
|
||||
# XXX: Neither of these is actually used anywhere.
|
||||
self._max_denom = max(2**context.prec // 200, 99)
|
||||
self._tolerance = self.one / self._max_denom
|
||||
|
||||
@property
|
||||
def tp(self):
|
||||
# XXX: Domain treats tp as an alias of dtype. Here we need two separate
|
||||
# things: dtype is a callable to make/convert instances. We use tp with
|
||||
# isinstance to check if an object is an instance of the domain
|
||||
# already.
|
||||
return self._dtype
|
||||
|
||||
def dtype(self, x, y=0):
|
||||
# XXX: This is needed because mpmath does not recognise fmpz.
|
||||
# It might be better to add conversion routines to mpmath and if that
|
||||
# happens then this can be removed.
|
||||
if isinstance(x, SYMPY_INTS):
|
||||
x = int(x)
|
||||
if isinstance(y, SYMPY_INTS):
|
||||
y = int(y)
|
||||
return self._dtype(x, y)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, ComplexField) and self.precision == other.precision
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.__class__.__name__, self._dtype, self.precision))
|
||||
|
||||
def to_sympy(self, element):
|
||||
"""Convert ``element`` to SymPy number. """
|
||||
return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
|
||||
|
||||
def from_sympy(self, expr):
|
||||
"""Convert SymPy's number to ``dtype``. """
|
||||
number = expr.evalf(n=self.dps)
|
||||
real, imag = number.as_real_imag()
|
||||
|
||||
if real.is_Number and imag.is_Number:
|
||||
return self.dtype(real, imag)
|
||||
else:
|
||||
raise CoercionFailed("expected complex number, got %s" % expr)
|
||||
|
||||
def from_ZZ(self, element, base):
|
||||
return self.dtype(element)
|
||||
|
||||
def from_ZZ_gmpy(self, element, base):
|
||||
return self.dtype(int(element))
|
||||
|
||||
def from_ZZ_python(self, element, base):
|
||||
return self.dtype(element)
|
||||
|
||||
def from_QQ(self, element, base):
|
||||
return self.dtype(int(element.numerator)) / int(element.denominator)
|
||||
|
||||
def from_QQ_python(self, element, base):
|
||||
return self.dtype(element.numerator) / element.denominator
|
||||
|
||||
def from_QQ_gmpy(self, element, base):
|
||||
return self.dtype(int(element.numerator)) / int(element.denominator)
|
||||
|
||||
def from_GaussianIntegerRing(self, element, base):
|
||||
return self.dtype(int(element.x), int(element.y))
|
||||
|
||||
def from_GaussianRationalField(self, element, base):
|
||||
x = element.x
|
||||
y = element.y
|
||||
return (self.dtype(int(x.numerator)) / int(x.denominator) +
|
||||
self.dtype(0, int(y.numerator)) / int(y.denominator))
|
||||
|
||||
def from_AlgebraicField(self, element, base):
|
||||
return self.from_sympy(base.to_sympy(element).evalf(self.dps))
|
||||
|
||||
def from_RealField(self, element, base):
|
||||
return self.dtype(element)
|
||||
|
||||
def from_ComplexField(self, element, base):
|
||||
return self.dtype(element)
|
||||
|
||||
def get_ring(self):
|
||||
"""Returns a ring associated with ``self``. """
|
||||
raise DomainError("there is no ring associated with %s" % self)
|
||||
|
||||
def get_exact(self):
|
||||
"""Returns an exact domain associated with ``self``. """
|
||||
return QQ_I
|
||||
|
||||
def is_negative(self, element):
|
||||
"""Returns ``False`` for any ``ComplexElement``. """
|
||||
return False
|
||||
|
||||
def is_positive(self, element):
|
||||
"""Returns ``False`` for any ``ComplexElement``. """
|
||||
return False
|
||||
|
||||
def is_nonnegative(self, element):
|
||||
"""Returns ``False`` for any ``ComplexElement``. """
|
||||
return False
|
||||
|
||||
def is_nonpositive(self, element):
|
||||
"""Returns ``False`` for any ``ComplexElement``. """
|
||||
return False
|
||||
|
||||
def gcd(self, a, b):
|
||||
"""Returns GCD of ``a`` and ``b``. """
|
||||
return self.one
|
||||
|
||||
def lcm(self, a, b):
|
||||
"""Returns LCM of ``a`` and ``b``. """
|
||||
return a*b
|
||||
|
||||
def almosteq(self, a, b, tolerance=None):
|
||||
"""Check if ``a`` and ``b`` are almost equal. """
|
||||
return self._context.almosteq(a, b, tolerance)
|
||||
|
||||
def is_square(self, a):
|
||||
"""Returns ``True``. Every complex number has a complex square root."""
|
||||
return True
|
||||
|
||||
def exsqrt(self, a):
|
||||
r"""Returns the principal complex square root of ``a``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
The argument of the principal square root is always within
|
||||
$(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be
|
||||
slightly inaccurate due to floating point rounding error.
|
||||
"""
|
||||
return a ** 0.5
|
||||
|
||||
CC = ComplexField()
|
||||
@@ -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
@@ -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")
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -0,0 +1,118 @@
|
||||
"""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 gcdex(self, a, b):
|
||||
"""
|
||||
Returns x, y, g such that a * x + b * y == g == gcd(a, b)
|
||||
"""
|
||||
d = self.gcd(a, b)
|
||||
|
||||
if a == self.zero:
|
||||
if b == self.zero:
|
||||
return self.zero, self.one, self.zero
|
||||
else:
|
||||
return self.zero, d/b, d
|
||||
else:
|
||||
return d/a, self.zero, d
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,368 @@
|
||||
"""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_nmod(mod):
|
||||
# nmod only recognises int
|
||||
index = operator.index
|
||||
mod = index(mod)
|
||||
nmod = flint.nmod
|
||||
nmod_poly = flint.nmod_poly
|
||||
|
||||
# flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine)
|
||||
try:
|
||||
nmod(0, mod)
|
||||
except OverflowError:
|
||||
return None, None
|
||||
|
||||
def ctx(x):
|
||||
try:
|
||||
return nmod(x, mod)
|
||||
except TypeError:
|
||||
return nmod(index(x), mod)
|
||||
|
||||
def poly_ctx(cs):
|
||||
return nmod_poly(cs, mod)
|
||||
|
||||
return ctx, poly_ctx
|
||||
|
||||
|
||||
def _modular_int_factory_fmpz_mod(mod):
|
||||
index = operator.index
|
||||
fctx = flint.fmpz_mod_ctx(mod)
|
||||
fctx_poly = flint.fmpz_mod_poly_ctx(mod)
|
||||
fmpz_mod_poly = flint.fmpz_mod_poly
|
||||
|
||||
def ctx(x):
|
||||
try:
|
||||
return fctx(x)
|
||||
except TypeError:
|
||||
# x might be Integer
|
||||
return fctx(index(x))
|
||||
|
||||
def poly_ctx(cs):
|
||||
return fmpz_mod_poly(cs, fctx_poly)
|
||||
|
||||
return ctx, poly_ctx
|
||||
|
||||
|
||||
def _modular_int_factory(mod, dom, symmetric, self):
|
||||
# Convert the modulus to ZZ
|
||||
try:
|
||||
mod = dom.convert(mod)
|
||||
except CoercionFailed:
|
||||
raise ValueError('modulus must be an integer, got %s' % mod)
|
||||
|
||||
ctx, poly_ctx, is_flint = None, None, False
|
||||
|
||||
# Don't use flint if the modulus is not prime as it often crashes.
|
||||
if flint is not None and mod.is_prime():
|
||||
|
||||
is_flint = True
|
||||
|
||||
# Try to use flint's nmod first
|
||||
ctx, poly_ctx = _modular_int_factory_nmod(mod)
|
||||
|
||||
if ctx is None:
|
||||
# Use fmpz_mod for larger moduli
|
||||
ctx, poly_ctx = _modular_int_factory_fmpz_mod(mod)
|
||||
|
||||
if ctx is None:
|
||||
# Use the Python implementation if flint is not available or the
|
||||
# modulus is not prime.
|
||||
ctx = ModularIntegerFactory(mod, dom, symmetric, self)
|
||||
poly_ctx = None # not used
|
||||
|
||||
return ctx, poly_ctx, is_flint
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
ctx, poly_ctx, is_flint = _modular_int_factory(mod, dom, symmetric, self)
|
||||
|
||||
self.dtype = ctx
|
||||
self._poly_ctx = poly_ctx
|
||||
self._is_flint = is_flint
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def is_Field(self):
|
||||
is_field = getattr(self, '_is_field', None)
|
||||
if is_field is None:
|
||||
from sympy.ntheory.primetest import isprime
|
||||
self._is_field = is_field = isprime(self.mod)
|
||||
return is_field
|
||||
|
||||
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
|
||||
@@ -0,0 +1,181 @@
|
||||
"""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)
|
||||
|
||||
def of_type(self, element):
|
||||
"""Check if ``a`` is of type ``dtype``. """
|
||||
return self.field.is_element(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.field, self.domain, self.symbols))
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Returns ``True`` if two domains are equivalent. """
|
||||
if not isinstance(other, FractionField):
|
||||
return NotImplemented
|
||||
return self.field == other.field
|
||||
|
||||
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))
|
||||
@@ -0,0 +1,706 @@
|
||||
"""Domains of Gaussian type."""
|
||||
|
||||
from __future__ import annotations
|
||||
from sympy.core.numbers import I
|
||||
from sympy.polys.polyclasses import DMP
|
||||
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: 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
|
||||
mod = DMP([ZZ.one, ZZ.zero, ZZ.one], 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
|
||||
is_PID = 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 gcdex(self, a, b):
|
||||
"""Return x, y, g such that x * a + y * b = g = gcd(a, b)"""
|
||||
x_a = self.one
|
||||
x_b = self.zero
|
||||
y_a = self.zero
|
||||
y_b = self.one
|
||||
while b:
|
||||
q = a // b
|
||||
a, b = b, a - q * b
|
||||
x_a, x_b = x_b, x_a - q * x_b
|
||||
y_a, y_b = y_b, y_a - q * y_b
|
||||
|
||||
a, x_a, y_a = self.normalize(a, x_a, y_a)
|
||||
return x_a, y_a, 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
|
||||
mod = DMP([QQ.one, QQ.zero, QQ.one], 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()
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)))
|
||||
@@ -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',
|
||||
]
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -0,0 +1,181 @@
|
||||
#
|
||||
# This module is deprecated and should not be used any more. The actual
|
||||
# implementation of RR and CC now uses mpmath's mpf and mpc types directly.
|
||||
#
|
||||
"""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
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -0,0 +1,203 @@
|
||||
"""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)
|
||||
|
||||
def of_type(self, element):
|
||||
"""Check if ``a`` is of type ``dtype``. """
|
||||
return self.ring.is_element(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.ring, self.domain, self.symbols))
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Returns `True` if two domains are equivalent. """
|
||||
if not isinstance(other, PolynomialRing):
|
||||
return NotImplemented
|
||||
return self.ring == other.ring
|
||||
|
||||
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))
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -0,0 +1,220 @@
|
||||
"""Implementation of :class:`RealField` class. """
|
||||
|
||||
|
||||
from sympy.external.gmpy import SYMPY_INTS, MPQ
|
||||
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.polyerrors import CoercionFailed
|
||||
from sympy.utilities import public
|
||||
|
||||
from mpmath import MPContext
|
||||
from mpmath.libmp import to_rational as _mpmath_to_rational
|
||||
|
||||
|
||||
def to_rational(s, max_denom, limit=True):
|
||||
|
||||
p, q = _mpmath_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)
|
||||
q = int(q)
|
||||
|
||||
if not limit or q <= 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 > max_denom:
|
||||
break
|
||||
p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
|
||||
n, d = d, n - a*d
|
||||
|
||||
k = (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
|
||||
|
||||
|
||||
@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._tolerance
|
||||
|
||||
def __init__(self, prec=None, dps=None, tol=None):
|
||||
# XXX: The tol parameter is ignored but is kept for now for backwards
|
||||
# compatibility.
|
||||
|
||||
context = MPContext()
|
||||
|
||||
if prec is None and dps is None:
|
||||
context.prec = self._default_precision
|
||||
elif dps is None:
|
||||
context.prec = prec
|
||||
elif prec is None:
|
||||
context.dps = dps
|
||||
else:
|
||||
raise TypeError("Cannot set both prec and dps")
|
||||
|
||||
self._context = context
|
||||
|
||||
self._dtype = context.mpf
|
||||
self.zero = self.dtype(0)
|
||||
self.one = self.dtype(1)
|
||||
|
||||
# Only max_denom here is used for anything and is only used for
|
||||
# to_rational.
|
||||
self._max_denom = max(2**context.prec // 200, 99)
|
||||
self._tolerance = self.one / self._max_denom
|
||||
|
||||
@property
|
||||
def tp(self):
|
||||
# XXX: Domain treats tp as an alias of dtype. Here we need 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
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.__class__.__name__, self._dtype, self.precision))
|
||||
|
||||
def to_sympy(self, element):
|
||||
"""Convert ``element`` to SymPy number. """
|
||||
return Float(element, 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):
|
||||
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 to_rational(element, self._max_denom, limit=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()
|
||||
@@ -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)
|
||||
@@ -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
@@ -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))
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user