finitefield.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. """Implementation of :class:`FiniteField` class. """
  2. from sympy.polys.domains.field import Field
  3. from sympy.polys.domains.modularinteger import ModularIntegerFactory
  4. from sympy.polys.domains.simpledomain import SimpleDomain
  5. from sympy.polys.polyerrors import CoercionFailed
  6. from sympy.utilities import public
  7. from sympy.polys.domains.groundtypes import SymPyInteger
  8. @public
  9. class FiniteField(Field, SimpleDomain):
  10. r"""Finite field of prime order :ref:`GF(p)`
  11. A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime
  12. order as :py:class:`~.Domain` in the domain system (see
  13. :ref:`polys-domainsintro`).
  14. A :py:class:`~.Poly` created from an expression with integer
  15. coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p``
  16. option is given then the domain will be a finite field instead.
  17. >>> from sympy import Poly, Symbol
  18. >>> x = Symbol('x')
  19. >>> p = Poly(x**2 + 1)
  20. >>> p
  21. Poly(x**2 + 1, x, domain='ZZ')
  22. >>> p.domain
  23. ZZ
  24. >>> p2 = Poly(x**2 + 1, modulus=2)
  25. >>> p2
  26. Poly(x**2 + 1, x, modulus=2)
  27. >>> p2.domain
  28. GF(2)
  29. It is possible to factorise a polynomial over :ref:`GF(p)` using the
  30. modulus argument to :py:func:`~.factor` or by specifying the domain
  31. explicitly. The domain can also be given as a string.
  32. >>> from sympy import factor, GF
  33. >>> factor(x**2 + 1)
  34. x**2 + 1
  35. >>> factor(x**2 + 1, modulus=2)
  36. (x + 1)**2
  37. >>> factor(x**2 + 1, domain=GF(2))
  38. (x + 1)**2
  39. >>> factor(x**2 + 1, domain='GF(2)')
  40. (x + 1)**2
  41. It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel`
  42. and :py:func:`~.gcd` functions.
  43. >>> from sympy import cancel, gcd
  44. >>> cancel((x**2 + 1)/(x + 1))
  45. (x**2 + 1)/(x + 1)
  46. >>> cancel((x**2 + 1)/(x + 1), domain=GF(2))
  47. x + 1
  48. >>> gcd(x**2 + 1, x + 1)
  49. 1
  50. >>> gcd(x**2 + 1, x + 1, domain=GF(2))
  51. x + 1
  52. When using the domain directly :ref:`GF(p)` can be used as a constructor
  53. to create instances which then support the operations ``+,-,*,**,/``
  54. >>> from sympy import GF
  55. >>> K = GF(5)
  56. >>> K
  57. GF(5)
  58. >>> x = K(3)
  59. >>> y = K(2)
  60. >>> x
  61. 3 mod 5
  62. >>> y
  63. 2 mod 5
  64. >>> x * y
  65. 1 mod 5
  66. >>> x / y
  67. 4 mod 5
  68. Notes
  69. =====
  70. It is also possible to create a :ref:`GF(p)` domain of **non-prime**
  71. order but the resulting ring is **not** a field: it is just the ring of
  72. the integers modulo ``n``.
  73. >>> K = GF(9)
  74. >>> z = K(3)
  75. >>> z
  76. 3 mod 9
  77. >>> z**2
  78. 0 mod 9
  79. It would be good to have a proper implementation of prime power fields
  80. (``GF(p**n)``) but these are not yet implemented in SymPY.
  81. .. _finite field: https://en.wikipedia.org/wiki/Finite_field
  82. """
  83. rep = 'FF'
  84. alias = 'FF'
  85. is_FiniteField = is_FF = True
  86. is_Numerical = True
  87. has_assoc_Ring = False
  88. has_assoc_Field = True
  89. dom = None
  90. mod = None
  91. def __init__(self, mod, symmetric=True):
  92. from sympy.polys.domains import ZZ
  93. dom = ZZ
  94. if mod <= 0:
  95. raise ValueError('modulus must be a positive integer, got %s' % mod)
  96. self.dtype = ModularIntegerFactory(mod, dom, symmetric, self)
  97. self.zero = self.dtype(0)
  98. self.one = self.dtype(1)
  99. self.dom = dom
  100. self.mod = mod
  101. def __str__(self):
  102. return 'GF(%s)' % self.mod
  103. def __hash__(self):
  104. return hash((self.__class__.__name__, self.dtype, self.mod, self.dom))
  105. def __eq__(self, other):
  106. """Returns ``True`` if two domains are equivalent. """
  107. return isinstance(other, FiniteField) and \
  108. self.mod == other.mod and self.dom == other.dom
  109. def characteristic(self):
  110. """Return the characteristic of this domain. """
  111. return self.mod
  112. def get_field(self):
  113. """Returns a field associated with ``self``. """
  114. return self
  115. def to_sympy(self, a):
  116. """Convert ``a`` to a SymPy object. """
  117. return SymPyInteger(int(a))
  118. def from_sympy(self, a):
  119. """Convert SymPy's Integer to SymPy's ``Integer``. """
  120. if a.is_Integer:
  121. return self.dtype(self.dom.dtype(int(a)))
  122. elif a.is_Float and int(a) == a:
  123. return self.dtype(self.dom.dtype(int(a)))
  124. else:
  125. raise CoercionFailed("expected an integer, got %s" % a)
  126. def from_FF(K1, a, K0=None):
  127. """Convert ``ModularInteger(int)`` to ``dtype``. """
  128. return K1.dtype(K1.dom.from_ZZ(a.val, K0.dom))
  129. def from_FF_python(K1, a, K0=None):
  130. """Convert ``ModularInteger(int)`` to ``dtype``. """
  131. return K1.dtype(K1.dom.from_ZZ_python(a.val, K0.dom))
  132. def from_ZZ(K1, a, K0=None):
  133. """Convert Python's ``int`` to ``dtype``. """
  134. return K1.dtype(K1.dom.from_ZZ_python(a, K0))
  135. def from_ZZ_python(K1, a, K0=None):
  136. """Convert Python's ``int`` to ``dtype``. """
  137. return K1.dtype(K1.dom.from_ZZ_python(a, K0))
  138. def from_QQ(K1, a, K0=None):
  139. """Convert Python's ``Fraction`` to ``dtype``. """
  140. if a.denominator == 1:
  141. return K1.from_ZZ_python(a.numerator)
  142. def from_QQ_python(K1, a, K0=None):
  143. """Convert Python's ``Fraction`` to ``dtype``. """
  144. if a.denominator == 1:
  145. return K1.from_ZZ_python(a.numerator)
  146. def from_FF_gmpy(K1, a, K0=None):
  147. """Convert ``ModularInteger(mpz)`` to ``dtype``. """
  148. return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom))
  149. def from_ZZ_gmpy(K1, a, K0=None):
  150. """Convert GMPY's ``mpz`` to ``dtype``. """
  151. return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0))
  152. def from_QQ_gmpy(K1, a, K0=None):
  153. """Convert GMPY's ``mpq`` to ``dtype``. """
  154. if a.denominator == 1:
  155. return K1.from_ZZ_gmpy(a.numerator)
  156. def from_RealField(K1, a, K0):
  157. """Convert mpmath's ``mpf`` to ``dtype``. """
  158. p, q = K0.to_rational(a)
  159. if q == 1:
  160. return K1.dtype(K1.dom.dtype(p))
  161. FF = GF = FiniteField