| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- """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
|