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