Skip to content

Commit

Permalink
Add manual Conway polynomial test and search functions
Browse files Browse the repository at this point in the history
Fixes #465, #466
  • Loading branch information
mhostetter committed Feb 1, 2023
1 parent ff28d97 commit c3bc7b1
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/galois/_polys/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
A subpackage containing arrays over Galois fields.
A subpackage containing univariate polynomials over Galois fields.
"""
from ._conway import *
from ._factor import *
Expand Down
317 changes: 294 additions & 23 deletions src/galois/_polys/_conway.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,274 @@
"""
from __future__ import annotations

import functools
from typing import Iterator, Sequence

from .._databases import ConwayPolyDatabase
from .._domains import _factory
from .._helper import export, verify_isinstance
from .._prime import is_prime
from .._helper import export, method_of, verify_isinstance
from .._prime import divisors, is_prime
from ._poly import Poly
from ._primitive import is_primitive


@method_of(Poly)
def is_conway(f: Poly, search: bool = False) -> bool:
r"""
Checks whether the degree-:math:`m` polynomial :math:`f(x)` over :math:`\mathrm{GF}(p)` is the
Conway polynomial :math:`C_{p,m}(x)`.
.. info::
This is a method, not a property, to indicate this test is computationally expensive.
Arguments:
search: Manually search for Conway polynomials if they are not included in `Frank Luebeck's database
<http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/index.html>`_. The default is `False`.
The manual search may take a long time.
Returns:
`True` if the polynomial :math:`f(x)` is the Conway polynomial :math:`C_{p,m}(x)`.
Raises:
LookupError: If `search=False` and the Conway polynomial :math:`C_{p,m}` is not found in Frank Luebeck's
database.
See Also:
conway_poly, Poly.is_conway_consistent, Poly.is_primitive
Notes:
A degree-:math:`m` polynomial :math:`f(x)` over :math:`\mathrm{GF}(p)` is the *Conway polynomial*
:math:`C_{p,m}(x)` if it is monic, primitive, compatible with Conway polynomials :math:`C_{p,n}(x)` for all
:math:`n\ |\ m`, and is lexicographically first according to a special ordering.
A Conway polynomial :math:`C_{p,m}(x)` is *compatible* with Conway polynomials :math:`C_{p,n}(x)` for
:math:`n\ |\ m` if :math:`C_{p,n}(x^r)` divides :math:`C_{p,m}(x)`, where :math:`r = \frac{p^m - 1}{p^n - 1}`.
The Conway lexicographic ordering is defined as follows. Given two degree-:math:`m` polynomials
:math:`g(x) = \sum_{i=0}^m g_i x^i` and :math:`h(x) = \sum_{i=0}^m h_i x^i`, then :math:`g < h` if and only if
there exists :math:`i` such that :math:`g_j = h_j` for all :math:`j > i` and
:math:`(-1)^{m-i} g_i < (-1)^{m-i} h_i`.
References:
- http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CP7.html
- Lenwood S. Heath, Nicholas A. Loehr, New algorithms for generating Conway polynomials over finite fields,
Journal of Symbolic Computation, Volume 38, Issue 2, 2004, Pages 1003-1024,
https://www.sciencedirect.com/science/article/pii/S0747717104000331.
Examples:
All Conway polynomials are primitive.
.. ipython:: python
GF = galois.GF(7)
f = galois.Poly([1, 1, 2, 4], field=GF); f
g = galois.Poly([1, 6, 0, 4], field=GF); g
f.is_primitive()
g.is_primitive()
They are also consistent with all smaller Conway polynomials.
.. ipython:: python
f.is_conway_consistent()
g.is_conway_consistent()
Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special
lexicographical order) is the Conway polynomial.
.. ipython:: python
f.is_conway()
g.is_conway()
galois.conway_poly(7, 3)
"""
verify_isinstance(search, bool)
if not is_prime(f.field.order):
raise ValueError(f"Conway polynomials are only defined over prime fields, not order {f.field.order}.")

p = f.field.order
m = f.degree
C_pm = conway_poly(p, m, search=search)

return f == C_pm


@method_of(Poly)
@functools.lru_cache()
def is_conway_consistent(f: Poly, search: bool = False) -> bool:
r"""
Determines whether the degree-:math:`m` polynomial :math:`f(x)` over :math:`\mathrm{GF}(p)` is consistent
with smaller Conway polynomials :math:`C_{p,n}(x)` for all :math:`n\ |\ m`.
.. info::
This is a method, not a property, to indicate this test is computationally expensive.
Arguments:
search: Manually search for Conway polynomials if they are not included in `Frank Luebeck's database
<http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/index.html>`_. The default is `False`.
The manual search may take a long time.
Returns:
`True` if the polynomial :math:`f(x)` is primitive and consistent with smaller Conway polynomials
:math:`C_{p,n}(x)` for all :math:`n\ |\ m`.
Raises:
LookupError: If `search=False` and a smaller Conway polynomial :math:`C_{p,n}` is not found in Frank Luebeck's
database.
See Also:
conway_poly, Poly.is_conway, Poly.is_primitive
Notes:
A degree-:math:`m` polynomial :math:`f(x)` over :math:`\mathrm{GF}(p)` is *compatible* with Conway polynomials
:math:`C_{p,n}(x)` for :math:`n\ |\ m` if :math:`C_{p,n}(x^r)` divides :math:`f(x)`, where
:math:`r = \frac{p^m - 1}{p^n - 1}`.
A Conway-consistent polynomial has all the properties of a Conway polynomial except that it is not
necessarily lexicographically first (according to a special ordering).
References:
- http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CP7.html
- Lenwood S. Heath, Nicholas A. Loehr, New algorithms for generating Conway polynomials over finite fields,
Journal of Symbolic Computation, Volume 38, Issue 2, 2004, Pages 1003-1024,
https://www.sciencedirect.com/science/article/pii/S0747717104000331.
Examples:
All Conway polynomials are primitive.
.. ipython:: python
GF = galois.GF(7)
f = galois.Poly([1, 1, 2, 4], field=GF); f
g = galois.Poly([1, 6, 0, 4], field=GF); g
f.is_primitive()
g.is_primitive()
They are also consistent with all smaller Conway polynomials.
.. ipython:: python
f.is_conway_consistent()
g.is_conway_consistent()
Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special
lexicographical order) is the Conway polynomial.
.. ipython:: python
f.is_conway()
g.is_conway()
galois.conway_poly(7, 3)
"""
verify_isinstance(search, bool)
if not is_prime(f.field.order):
raise ValueError(f"Conway polynomials are only defined over prime fields, not order {f.field.order}.")

field = f.field
p = field.order
m = f.degree
x = Poly.Identity(field)

if not is_primitive(f):
# A Conway polynomial must be primitive.
return False

# For f_m(x) to be a Conway polynomial, f_n(x^r) must divide f_m(x) for all n | m,
# where r = (p^m - 1) // (p^n - 1).
proper_divisors = divisors(m)[:-1] # Exclude m itself

for n in proper_divisors:
f_n = conway_poly(p, n, search=search)
r = (p**m - 1) // (p**n - 1)
x_r = pow(x, r, f)
g = f_n(x_r) % f
if g != 0:
return False

return True


@export
def conway_poly(characteristic: int, degree: int) -> Poly:
def conway_poly(characteristic: int, degree: int, search: bool = False) -> Poly:
r"""
Returns the Conway polynomial :math:`C_{p,m}(x)` over :math:`\mathrm{GF}(p)` with degree :math:`m`.
Arguments:
characteristic: The prime characteristic :math:`p` of the field :math:`\mathrm{GF}(p)` that the polynomial
is over.
degree: The degree :math:`m` of the Conway polynomial.
search: Manually search for Conway polynomials if they are not included in `Frank Luebeck's database
<http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/index.html>`_. The default is `False`.
The manual search may take a long time.
Returns:
The degree-:math:`m` Conway polynomial :math:`C_{p,m}(x)` over :math:`\mathrm{GF}(p)`.
See Also:
Poly.is_primitive, primitive_poly, matlab_primitive_poly
Poly.is_conway, Poly.is_conway_consistent, Poly.is_primitive, primitive_poly
Raises:
LookupError: If the Conway polynomial :math:`C_{p,m}(x)` is not found in Frank Luebeck's database.
LookupError: If `search=False` and the Conway polynomial :math:`C_{p,m}` is not found in Frank Luebeck's
database.
Notes:
A Conway polynomial is an irreducible and primitive polynomial over :math:`\mathrm{GF}(p)` that provides a
standard representation of :math:`\mathrm{GF}(p^m)` as a splitting field of :math:`C_{p,m}(x)`.
Conway polynomials provide compatibility between fields and their subfields and, hence, are the common way to
represent extension fields.
A degree-:math:`m` polynomial :math:`f(x)` over :math:`\mathrm{GF}(p)` is the *Conway polynomial*
:math:`C_{p,m}(x)` if it is monic, primitive, compatible with Conway polynomials :math:`C_{p,n}(x)` for all
:math:`n\ |\ m`, and is lexicographically first according to a special ordering.
The Conway polynomial :math:`C_{p,m}(x)` is defined as the lexicographically-first monic primitive polynomial
of degree :math:`m` over :math:`\mathrm{GF}(p)` that is compatible with all :math:`C_{p,n}(x)` for :math:`n`
dividing :math:`m`.
A Conway polynomial :math:`C_{p,m}(x)` is *compatible* with Conway polynomials :math:`C_{p,n}(x)` for
:math:`n\ |\ m` if :math:`C_{p,n}(x^r)` divides :math:`C_{p,m}(x)`, where :math:`r = \frac{p^m - 1}{p^n - 1}`.
This function uses `Frank Luebeck's Conway polynomial database
<http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/index.html>`_ for fast lookup, not construction.
The Conway lexicographic ordering is defined as follows. Given two degree-:math:`m` polynomials
:math:`g(x) = \sum_{i=0}^m g_i x^i` and :math:`h(x) = \sum_{i=0}^m h_i x^i`, then :math:`g < h` if and only if
there exists :math:`i` such that :math:`g_j = h_j` for all :math:`j > i` and
:math:`(-1)^{m-i} g_i < (-1)^{m-i} h_i`.
The Conway polynomial :math:`C_{p,m}(x)` provides a standard representation of :math:`\mathrm{GF}(p^m)` as a
splitting field of :math:`C_{p,m}(x)`. Conway polynomials provide compatibility between fields and their
subfields and, hence, are the common way to represent extension fields.
References:
- http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CP7.html
- Lenwood S. Heath, Nicholas A. Loehr, New algorithms for generating Conway polynomials over finite fields,
Journal of Symbolic Computation, Volume 38, Issue 2, 2004, Pages 1003-1024,
https://www.sciencedirect.com/science/article/pii/S0747717104000331.
Examples:
Notice :func:`~galois.primitive_poly` returns the lexicographically-first primitive polynomial but
:func:`~galois.conway_poly` returns the lexicographically-first primitive polynomial that is *consistent*
with smaller Conway polynomials. This is sometimes the same polynomial.
All Conway polynomials are primitive.
.. ipython:: python
GF = galois.GF(7)
f = galois.Poly([1, 1, 2, 4], field=GF); f
g = galois.Poly([1, 6, 0, 4], field=GF); g
f.is_primitive()
g.is_primitive()
They are also consistent with all smaller Conway polynomials.
.. ipython:: python
galois.primitive_poly(2, 4)
galois.conway_poly(2, 4)
f.is_conway_consistent()
g.is_conway_consistent()
However, it is not always.
Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special
lexicographical order) is the Conway polynomial.
.. ipython:: python
galois.primitive_poly(7, 10)
galois.conway_poly(7, 10)
f.is_conway()
g.is_conway()
galois.conway_poly(7, 3)
Group:
polys-primitive
"""
verify_isinstance(characteristic, int)
verify_isinstance(degree, int)
verify_isinstance(search, bool)

if not is_prime(characteristic):
raise ValueError(f"Argument 'characteristic' must be prime, not {characteristic}.")
Expand All @@ -72,9 +279,73 @@ def conway_poly(characteristic: int, degree: int) -> Poly:
f"Argument 'degree' must be at least 1, not {degree}. There are no primitive polynomials with degree 0."
)

try:
return _conway_poly_database(characteristic, degree)
except LookupError as e:
if search:
return _conway_poly_search(characteristic, degree)
raise e


def _conway_poly_database(characteristic: int, degree: int) -> Poly:
r"""
Returns the Conway polynomial :math:`C_{p,m}(x)` over :math:`\mathrm{GF}(p)` with degree :math:`m`
from Frank Luebeck's database.
Raises:
LookupError: If the Conway polynomial :math:`C_{p,m}(x)` is not found in Frank Luebeck's database.
"""
db = ConwayPolyDatabase()
coeffs = db.fetch(characteristic, degree)
field = _factory.FIELD_FACTORY(characteristic)
poly = Poly(coeffs, field=field)

return poly


@functools.lru_cache()
def _conway_poly_search(characteristic: int, degree: int) -> Poly:
r"""
Manually searches for the Conway polynomial :math:`C_{p,m}(x)` over :math:`\mathrm{GF}(p)` with degree :math:`m`.
"""
for poly in _conway_lexicographic_order(characteristic, degree):
if is_conway_consistent(poly):
return poly

raise RuntimeError(
f"The Conway polynomial C_{{{characteristic},{degree}}}(x) could not be found. "
"This should never happen. Please open a GitHub issue."
)


def _conway_lexicographic_order(
characteristic: int,
degree: int,
) -> Iterator[Poly]:
r"""
Yields all monic polynomials of degree :math:`m` over :math:`\mathrm{GF}(p)` in the lexicographic order
defined for Conway polynomials.
"""
field = _factory.FIELD_FACTORY(characteristic)

def recursive(
degrees: Sequence[int],
coeffs: Sequence[int],
) -> Iterator[Poly]:
if len(degrees) == degree + 1:
# print(characteristic, degree, degrees, coeffs)
yield Poly.Degrees(degrees, coeffs, field=field)
else:
d = degrees[-1] - 1 # The new degree

if (degree - d) % 2 == 1:
all_coeffs = [0, *reversed(range(1, characteristic))]
else:
all_coeffs = [0, *range(1, characteristic)]
# print(d, all_coeffs)

for x in all_coeffs:
next_degrees = (*degrees, d)
next_coeffs = (*coeffs, x)
yield from recursive(next_degrees, next_coeffs)

yield from recursive([degree], [1])
Loading

0 comments on commit c3bc7b1

Please sign in to comment.