Skip to content

Commit

Permalink
Move galois.is_irreducible() to Poly.is_irreducible()
Browse files Browse the repository at this point in the history
  • Loading branch information
mhostetter committed May 17, 2022
1 parent 89e8ec7 commit 008b971
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 153 deletions.
1 change: 0 additions & 1 deletion docs/api/galois-fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ Irreducible polynomials

irreducible_poly
irreducible_polys
is_irreducible

Primitive polynomials
.....................
Expand Down
1 change: 0 additions & 1 deletion docs/api/galois.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ Functions
irreducible_polys
is_composite
is_cyclic
is_irreducible
is_perfect_power
is_powersmooth
is_prime
Expand Down
1 change: 0 additions & 1 deletion docs/api/polys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,5 @@ Tests

.. autosummary::

is_irreducible
is_primitive
is_square_free
4 changes: 2 additions & 2 deletions docs/tutorials/intro-to-extension-fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ Also note, when factored, :math:`f(x)` has no irreducible factors other than its

.. ipython:: python
galois.is_irreducible(f)
galois.factors(f)
f.is_irreducible()
f.factors()
Arithmetic
----------
Expand Down
4 changes: 2 additions & 2 deletions galois/_fields/_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from .._modular import primitive_root, is_primitive_root
from .._overrides import set_module
from .._polys import Poly, conway_poly, primitive_element, is_irreducible, is_primitive_element
from .._polys import Poly, conway_poly, primitive_element, is_primitive_element
from .._prime import factors
from ..typing import PolyLike

Expand Down Expand Up @@ -356,7 +356,7 @@ def _GF_extension(
field.display(display)
return field

if verify_poly and not is_irreducible(irreducible_poly_):
if verify_poly and not irreducible_poly_.is_irreducible():
raise ValueError(f"Argument `irreducible_poly` must be irreducible, {irreducible_poly_} is not.")
if verify_element and not is_primitive_element(alpha, irreducible_poly_):
raise ValueError(f"Argument `primitive_element` must be a multiplicative generator of {name}, {alpha} is not.")
Expand Down
122 changes: 8 additions & 114 deletions galois/_polys/_irreducible.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,117 +10,11 @@

from .._domains import _factory
from .._overrides import set_module
from .._prime import factors, is_prime_power
from .._prime import is_prime_power

from ._functions import gcd
from ._poly import Poly

__all__ = ["is_irreducible", "irreducible_poly", "irreducible_polys"]


@set_module("galois")
def is_irreducible(poly: Poly) -> bool:
r"""
Determines whether the polynomial :math:`f(x)` over :math:`\mathrm{GF}(p^m)` is irreducible.
Parameters
----------
poly
A polynomial :math:`f(x)` over :math:`\mathrm{GF}(p^m)`.
Returns
-------
:
`True` if the polynomial is irreducible.
See Also
--------
is_primitive, irreducible_poly, irreducible_polys
Notes
-----
A polynomial :math:`f(x) \in \mathrm{GF}(p^m)[x]` is *reducible* over :math:`\mathrm{GF}(p^m)` if it can
be represented as :math:`f(x) = g(x) h(x)` for some :math:`g(x), h(x) \in \mathrm{GF}(p^m)[x]` of strictly
lower degree. If :math:`f(x)` is not reducible, it is said to be *irreducible*. Since Galois fields are not algebraically
closed, such irreducible polynomials exist.
This function implements Rabin's irreducibility test. It says a degree-:math:`m` polynomial :math:`f(x)`
over :math:`\mathrm{GF}(q)` for prime power :math:`q` is irreducible if and only if :math:`f(x)\ |\ (x^{q^m} - x)`
and :math:`\textrm{gcd}(f(x),\ x^{q^{m_i}} - x) = 1` for :math:`1 \le i \le k`, where :math:`m_i = m/p_i` for
the :math:`k` prime divisors :math:`p_i` of :math:`m`.
References
----------
* Rabin, M. Probabilistic algorithms in finite fields. SIAM Journal on Computing (1980), 273–280. https://apps.dtic.mil/sti/pdfs/ADA078416.pdf
* Gao, S. and Panarino, D. Tests and constructions of irreducible polynomials over finite fields. https://www.math.clemson.edu/~sgao/papers/GP97a.pdf
* Section 4.5.1 from https://cacr.uwaterloo.ca/hac/about/chap4.pdf
* https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields
Examples
--------
.. ipython:: python
# Conway polynomials are always irreducible (and primitive)
f = galois.conway_poly(2, 5); f
# f(x) has no roots in GF(2), a necessary but not sufficient condition of being irreducible
f.roots()
galois.is_irreducible(f)
.. ipython:: python
g = galois.irreducible_poly(2**4, 2, method="random"); g
h = galois.irreducible_poly(2**4, 3, method="random"); h
f = g * h; f
galois.is_irreducible(f)
"""
# pylint: disable=too-many-return-statements
if not isinstance(poly, Poly):
raise TypeError(f"Argument `poly` must be a galois.Poly, not {type(poly)}.")

if poly.degree == 0:
# Over fields, f(x) = 0 is the zero element of GF(p^m)[x] and f(x) = c are the units of GF(p^m)[x]. Both the
# zero element and the units are not irreducible over the polynomial ring GF(p^m)[x].
return False

if poly.degree == 1:
# f(x) = x + a (even a = 0) in any Galois field is irreducible
return True

if poly.coeffs[-1] == 0:
# g(x) = x can be factored, therefore it is not irreducible
return False

if poly.field.order == 2 and poly.nonzero_coeffs.size % 2 == 0:
# Polynomials over GF(2) with degree at least 2 and an even number of terms satisfy f(1) = 0, hence
# g(x) = x + 1 can be factored. Section 4.5.2 from https://cacr.uwaterloo.ca/hac/about/chap4.pdf.
return False

field = poly.field
q = field.order
m = poly.degree
x = Poly.Identity(field)

primes, _ = factors(m)
h0 = Poly.Identity(field)
n0 = 0
for ni in sorted([m // pi for pi in primes]):
# The GCD of f(x) and (x^(q^(m/pi)) - x) must be 1 for f(x) to be irreducible, where pi are the prime factors of m
hi = pow(h0, q**(ni - n0), poly)
g = gcd(poly, hi - x)
if g != 1:
return False
h0, n0 = hi, ni

# f(x) must divide (x^(q^m) - x) to be irreducible
h = pow(h0, q**(m - n0), poly)
g = (h - x) % poly
if g != 0:
return False

return True
__all__ = ["irreducible_poly", "irreducible_polys"]


@set_module("galois")
Expand Down Expand Up @@ -148,7 +42,7 @@ def irreducible_poly(order: int, degree: int, method: Literal["min", "max", "ran
See Also
--------
is_irreducible, primitive_poly, conway_poly
Poly.is_irreducible, primitive_poly, conway_poly
Notes
-----
Expand Down Expand Up @@ -188,15 +82,15 @@ def irreducible_poly(order: int, degree: int, method: Literal["min", "max", "ran
.. ipython:: python
f = galois.irreducible_poly(7, 5, method="random"); f
galois.is_irreducible(f)
f.is_irreducible()
Monic irreducible polynomials scaled by non-zero field elements (now non-monic) are also irreducible.
.. ipython:: python
GF = galois.GF(7)
g = f * GF(3); g
galois.is_irreducible(g)
g.is_irreducible()
"""
if not isinstance(order, (int, np.integer)):
raise TypeError(f"Argument `order` must be an integer, not {type(order)}.")
Expand Down Expand Up @@ -238,7 +132,7 @@ def irreducible_polys(order: int, degree: int, reverse: bool = False) -> Iterato
See Also
--------
is_irreducible, primitive_polys
Poly.is_irreducible, primitive_polys
Notes
-----
Expand Down Expand Up @@ -320,7 +214,7 @@ def _deterministic_search(field, start, stop, step) -> Optional[Poly]:
"""
for element in range(start, stop, step):
poly = Poly.Int(element, field=field)
if is_irreducible(poly):
if poly.is_irreducible():
return poly

return None
Expand All @@ -339,5 +233,5 @@ def _random_search(order, degree) -> Poly:
while True:
integer = random.randint(start, stop - 1)
poly = Poly.Int(integer, field=field)
if is_irreducible(poly):
if poly.is_irreducible():
return poly
124 changes: 112 additions & 12 deletions galois/_polys/_poly.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from .._domains import Array, _factory
from .._overrides import set_module
from .._prime import factors
from ..typing import ElementLike, ArrayLike, PolyLike

from . import _binary, _dense, _sparse
Expand Down Expand Up @@ -884,9 +885,9 @@ def square_free_factors(self) -> Tuple[List[Poly], List[int]]:
.. ipython:: python
GF = galois.GF(5)
a = galois.Poly([1,0], field=GF); a, galois.is_irreducible(a)
b = galois.Poly([1,0,2,4], field=GF); b, galois.is_irreducible(b)
c = galois.Poly([1,4,1], field=GF); c, galois.is_irreducible(c)
a = galois.Poly([1,0], field=GF); a, a.is_irreducible()
b = galois.Poly([1,0,2,4], field=GF); b, b.is_irreducible()
c = galois.Poly([1,4,1], field=GF); c, c.is_irreducible()
f = a * b * c**3; f
The square-free factorization is :math:`\{x(x^3 + 2x + 4), x^2 + 4x + 1\}` with multiplicities :math:`\{1, 3\}`.
Expand Down Expand Up @@ -990,11 +991,11 @@ def distinct_degree_factors(self) -> Tuple[List[Poly], List[int]]:
.. ipython:: python
a = galois.Poly([1, 0]); a, galois.is_irreducible(a)
b = galois.Poly([1, 1]); b, galois.is_irreducible(b)
c = galois.Poly([1, 1, 1]); c, galois.is_irreducible(c)
d = galois.Poly([1, 0, 1, 1]); d, galois.is_irreducible(d)
e = galois.Poly([1, 1, 0, 1]); e, galois.is_irreducible(e)
a = galois.Poly([1, 0]); a, a.is_irreducible()
b = galois.Poly([1, 1]); b, b.is_irreducible()
c = galois.Poly([1, 1, 1]); c, c.is_irreducible()
d = galois.Poly([1, 0, 1, 1]); d, d.is_irreducible()
e = galois.Poly([1, 1, 0, 1]); e, e.is_irreducible()
f = a * b * c * d * e; f
The distinct-degree factorization is :math:`\{x(x + 1), x^2 + x + 1, (x^3 + x + 1)(x^3 + x^2 + 1)\}` whose irreducible factors
Expand Down Expand Up @@ -1079,8 +1080,8 @@ def equal_degree_factors(self, degree: int) -> List[Poly]:
.. ipython:: python
a = galois.Poly([1, 0]); a, galois.is_irreducible(a)
b = galois.Poly([1, 1]); b, galois.is_irreducible(b)
a = galois.Poly([1, 0]); a, a.is_irreducible()
b = galois.Poly([1, 1]); b, b.is_irreducible()
f = a * b; f
f.equal_degree_factors(1)
Expand All @@ -1089,8 +1090,8 @@ def equal_degree_factors(self, degree: int) -> List[Poly]:
.. ipython:: python
GF = galois.GF(5)
a = galois.Poly([1, 0, 2, 1], field=GF); a, galois.is_irreducible(a)
b = galois.Poly([1, 4, 4, 4], field=GF); b, galois.is_irreducible(b)
a = galois.Poly([1, 0, 2, 1], field=GF); a, a.is_irreducible()
b = galois.Poly([1, 4, 4, 4], field=GF); b, b.is_irreducible()
f = a * b; f
f.equal_degree_factors(3)
"""
Expand Down Expand Up @@ -1303,6 +1304,105 @@ def derivative(self, k: int = 1) -> Poly:
else:
return p_prime

def is_irreducible(self) -> bool:
r"""
Determines whether the polynomial :math:`f(x)` over :math:`\mathrm{GF}(p^m)` is irreducible.
Important
---------
This is a method, not a property, to indicate this test is computationally expensive.
Returns
-------
:
`True` if the polynomial is irreducible.
See Also
--------
irreducible_poly, irreducible_polys
Notes
-----
A polynomial :math:`f(x) \in \mathrm{GF}(p^m)[x]` is *reducible* over :math:`\mathrm{GF}(p^m)` if it can
be represented as :math:`f(x) = g(x) h(x)` for some :math:`g(x), h(x) \in \mathrm{GF}(p^m)[x]` of strictly
lower degree. If :math:`f(x)` is not reducible, it is said to be *irreducible*. Since Galois fields are not algebraically
closed, such irreducible polynomials exist.
This function implements Rabin's irreducibility test. It says a degree-:math:`m` polynomial :math:`f(x)`
over :math:`\mathrm{GF}(q)` for prime power :math:`q` is irreducible if and only if :math:`f(x)\ |\ (x^{q^m} - x)`
and :math:`\textrm{gcd}(f(x),\ x^{q^{m_i}} - x) = 1` for :math:`1 \le i \le k`, where :math:`m_i = m/p_i` for
the :math:`k` prime divisors :math:`p_i` of :math:`m`.
References
----------
* Rabin, M. Probabilistic algorithms in finite fields. SIAM Journal on Computing (1980), 273-280. https://apps.dtic.mil/sti/pdfs/ADA078416.pdf
* Gao, S. and Panarino, D. Tests and constructions of irreducible polynomials over finite fields. https://www.math.clemson.edu/~sgao/papers/GP97a.pdf
* Section 4.5.1 from https://cacr.uwaterloo.ca/hac/about/chap4.pdf
* https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields
Examples
--------
.. ipython:: python
# Conway polynomials are always irreducible (and primitive)
f = galois.conway_poly(2, 5); f
# f(x) has no roots in GF(2), a necessary but not sufficient condition of being irreducible
f.roots()
f.is_irreducible()
.. ipython:: python
g = galois.irreducible_poly(2**4, 2, method="random"); g
h = galois.irreducible_poly(2**4, 3, method="random"); h
f = g * h; f
f.is_irreducible()
"""
# pylint: disable=too-many-return-statements
if self.degree == 0:
# Over fields, f(x) = 0 is the zero element of GF(p^m)[x] and f(x) = c are the units of GF(p^m)[x]. Both the
# zero element and the units are not irreducible over the polynomial ring GF(p^m)[x].
return False

if self.degree == 1:
# f(x) = x + a (even a = 0) in any Galois field is irreducible
return True

if self.coeffs[-1] == 0:
# g(x) = x can be factored, therefore it is not irreducible
return False

if self.field.order == 2 and self.nonzero_coeffs.size % 2 == 0:
# Polynomials over GF(2) with degree at least 2 and an even number of terms satisfy f(1) = 0, hence
# g(x) = x + 1 can be factored. Section 4.5.2 from https://cacr.uwaterloo.ca/hac/about/chap4.pdf.
return False

field = self.field
q = field.order
m = self.degree
x = Poly.Identity(field)

primes, _ = factors(m)
h0 = Poly.Identity(field)
n0 = 0
for ni in sorted([m // pi for pi in primes]):
# The GCD of f(x) and (x^(q^(m/pi)) - x) must be 1 for f(x) to be irreducible, where pi are the prime factors of m
hi = pow(h0, q**(ni - n0), self)
g = GCD(self, hi - x)
if g != 1:
return False
h0, n0 = hi, ni

# f(x) must divide (x^(q^m) - x) to be irreducible
h = pow(h0, q**(m - n0), self)
g = (h - x) % self
if g != 0:
return False

return True

###############################################################################
# Overridden dunder methods
###############################################################################
Expand Down
Loading

0 comments on commit 008b971

Please sign in to comment.