Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Cleaning up finite field factory
Browse files Browse the repository at this point in the history
so a few more extensions work (again)
  • Loading branch information
saraedum committed Sep 14, 2019
1 parent 382362d commit 5e294d5
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 91 deletions.
11 changes: 6 additions & 5 deletions src/sage/rings/finite_rings/element_relative.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
sage: k = GF(4).extension(2, absolute=False)
sage: k.random_element()
0
AUTHORS:
Expand All @@ -21,9 +22,9 @@
# http://www.gnu.org/licenses/
#*****************************************************************************
from sage.structure.element import CommutativeRingElement
from sage.rings.ring_extension_element import RingExtensionElement
from sage.rings.ring_extension_element import RingExtensionWithBasisElement

class FiniteField_relativeElement(RingExtensionElement):
class FiniteField_relativeElement(RingExtensionWithBasisElement):
r"""
Element of a
:class:`sage.rings.finite_rings.finite_field_relative.FiniteField_relative`.
Expand All @@ -32,7 +33,7 @@ class FiniteField_relativeElement(RingExtensionElement):
sage: k = GF(9).extension(2, absolute=False)
sage: a = k.gen(); a
2*b4^2 + 2
z4
sage: k.modulus()(a) == 0
True
Expand All @@ -42,11 +43,11 @@ def __init__(self, parent, x):
TESTS::
sage: from sage.rings.finite_rings.element_relative import FiniteField_relativeElement
sage: k = GF(2).extension(2, absolute=False)
sage: k = GF(8).extension(2, absolute=False)
sage: isinstance(k.gen(), FiniteField_relativeElement)
True
"""
# if x.parent() is not parent:
# raise ValueError("parent must be %s but it is %s"%(parent, x.parent()))
RingExtensionElement.__init__(self, parent, x)
RingExtensionWithBasisElement.__init__(self, parent, x)
19 changes: 14 additions & 5 deletions src/sage/rings/finite_rings/finite_field_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ cdef class FiniteField(Field):
"""
return True

def __repr__(self):
def _repr_(self):
"""
String representation of this finite field.
Expand Down Expand Up @@ -450,7 +450,7 @@ cdef class FiniteField(Field):

base_hom = self.base_ring()._any_embedding(codomain)
minpoly = self.gen().minpoly().change_ring(base_hom)
return self.hom(codomain, [minpoly.any_root()], base_map=base_hom)
return self.hom([minpoly.any_root()], codomain=codomain, base_map=base_hom)

def zeta_order(self):
"""
Expand Down Expand Up @@ -1191,9 +1191,13 @@ cdef class FiniteField(Field):

absolute_degree = self.absolute_degree() * relative_degree

if not absolute and absolute_degree == 1:
# To get a trivial extension, we need to provide an explicit modulus
modulus = PolynomialRing(self, 'x').gen()

# Create the actual (relative) extension ring E
if is_field:
E = GF(self.characteristic() ** absolute_degree, name=name, modulus=modulus, base=self, **kwds)
E = GF(self.characteristic() ** absolute_degree, name=name, modulus=modulus, base=None if absolute else self, **kwds)
else:
E = Field.extension(self, modulus, name=name, **kwds)

Expand All @@ -1205,7 +1209,12 @@ cdef class FiniteField(Field):
if not is_field:
raise NotImplementedError("non-field extensions of finite fields not supported yet when absolute=True")

return E.absolute_field(map=map)
F = E.absolute_field(map=map)
if map:
F, F_to_E, E_to_F = F
return F, E_to_F * self_to_E
else:
return F
else:
if map:
return E, self_to_E
Expand Down Expand Up @@ -1699,7 +1708,7 @@ cdef class FiniteFieldAbsolute(FiniteField):
TESTS::
sage: from sage.rings.finite_rins.finite_field_base import FiniteFieldAbsolute
sage: from sage.rings.finite_rings.finite_field_base import FiniteFieldAbsolute
sage: isinstance(k, FiniteFieldAbsolute)
True
"""
Expand Down
178 changes: 102 additions & 76 deletions src/sage/rings/finite_rings/finite_field_constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ class FiniteFieldFactory(UniqueFactory):
- ``check_irreducible`` -- verify that the polynomial modulus is
irreducible
- ``base`` -- when given, the resulting field is a relative extension. Note
that over the prime field there is no distinction between a relative and
an absolute extension except for trivial extensions.
- ``proof`` -- bool (default: ``True``): if ``True``, use provable
primality test; otherwise only use pseudoprimality test.
Expand Down Expand Up @@ -408,11 +412,8 @@ class FiniteFieldFactory(UniqueFactory):
sage: L = GF(7, 'b')
sage: K is L # name is ignored for prime fields
True
Note, however, that with an explicit modulus you get the trivial extension of the prime field here::
sage: K is GF(7, modulus=K.modulus())
False
sage: K is GF(7, modulus=K.modulus()) # default modulus is ignored for prime fields
True
You may print finite field elements as integers. This currently
only works if the order of field is `<2^{16}`, though::
Expand Down Expand Up @@ -561,69 +562,89 @@ def create_key_and_extra_args(self, order, name=None, modulus=None, names=None,
if order <= 1:
raise ValueError("the order of a finite field must be at least 2")

# determine p and absolute_degree
p, absolute_degree = order.is_prime_power(get_data=True)

if absolute_degree == 0:
raise ValueError("the order of a finite field must be a prime power")

if absolute_degree == 1 and base is None:
if impl is None:
# determine relative_degree
if base is None:
relative_degree = absolute_degree
else:
relative_degree = absolute_degree // base.absolute_degree()

# normalize base
if absolute_degree == relative_degree:
# There are no relative extension of Fp other than the trivial ones.
if absolute_degree != 1:
base = None

# determine impl
if impl is None:
if absolute_degree == 1:
impl = 'modn'
names = ('x',) # Ignore name
# Every polynomial of degree 1 is irreducible
check_irreducible = False
elif order < zech_log_bound:
impl = 'givaro'
elif p == 2:
impl = 'ntl'
else:
impl = 'pari_ffelt'

# determine generator names and prefix
if names is None:
names = name
if names or modulus or base:
# The prefix is used as a flag to signal to the field that it
# lives in a compatible tower of finite fields inside Fpbar.
# When either of these are specified, we need to signal that
# this is not the case:
prefix = None
else:
if names is None:
names = name
if names is not None:
names = normalize_names(1, names)

if names is None:
if prefix is None:
prefix = 'z'
names = (prefix + str(absolute_degree),)
if modulus is not None:
raise ValueError("no modulus may be specified if variable name not given")

if base is None:
# Fpbar will have a strong reference, since algebraic_closure caches its results,
# and the coefficients of modulus lie in GF(p)
Fpbar = GF(p).algebraic_closure(prefix)
# This will give a Conway polynomial if p,absolute_degree is small enough to be in the database
# and a pseudo-Conway polynomial if it's not.
modulus = Fpbar._get_polynomial(absolute_degree)
check_irreducible = False

if impl is None:
if order < zech_log_bound:
impl = 'givaro'
elif p == 2:
impl = 'ntl'
else:
impl = 'pari_ffelt'
if impl == 'modn':
raise ValueError("finite field implementation 'modn' only allowed for prime fields")

relative_degree = absolute_degree
if base is not None:
relative_degree //= base.absolute_degree()

# A lazy polynomial ring over the base. We only create this on
# demand since otherwise creating GF(p) would lead to an infinite
# recursion.
R = lambda: PolynomialRing(base or GF(p), 'x')

if isinstance(modulus, str):
# a string specifies an algorithm to find a suitable modulus
modulus = R().irreducible_element(relative_degree, algorithm=modulus)

if modulus is None:
if impl != 'modn':
modulus = R().irreducible_element(relative_degree)
prefix = prefix or 'z'
if absolute_degree == 1 and base is None:
# The name does not matter for Fp itself so we explicitly
# ignore it to get no distinct copies of Fp.
names = ('x',)
if names is None:
names = ((prefix or 'z') + str(absolute_degree),)
if names is not None:
names = normalize_names(1, names)

# determine a modulus
from sage.rings.polynomial.polynomial_element import is_Polynomial
if absolute_degree == 1 and base is None and (
modulus is None or is_Polynomial(modulus) and modulus.coefficients(sparse=False) == [-1, 1]):
# Ignore the default modulus for a prime field (we cannot
# write down the modulus x-1 over Fp[x] here since we would
# recursively need Fp for that first.
modulus = None
elif modulus is None and prefix:
# When creating an absolute extension without a
# prescribed modulus, we need to make sure that it
# embeds into the compatible tower of finite fields
# inside Fpbar.
Fpbar = GF(p).algebraic_closure(prefix)
# This will give a Conway polynomial if
# p,absolute_degree is small enough to be in the
# database and a pseudo-Conway polynomial if it's not.
modulus = Fpbar._get_polynomial(absolute_degree)
check_irreducible = False
elif modulus is None or isinstance(modulus, str):
R = PolynomialRing(base or GF(p), 'x')
modulus = R.irreducible_element(relative_degree, algorithm=modulus)
check_irreducible = False


# normalize modulus
if modulus is not None:
modulus = R()(modulus).monic()
modulus = PolynomialRing(base or GF(p), 'x')(modulus)
modulus = modulus.monic()

# sanity checks
if impl == 'modn' and absolute_degree != 1:
raise ValueError("finite field implementation 'modn' only allowed for prime fields")
if modulus is not None:
if modulus.degree() != relative_degree:
raise ValueError("degree of the modulus does not match the relative degree of finite field")
if check_irreducible and not modulus.is_irreducible():
Expand All @@ -641,7 +662,7 @@ def create_key_and_extra_args(self, order, name=None, modulus=None, names=None,
repr = None
elem_cache = None

return (order, names, modulus, impl, p, absolute_degree, proof, prefix, repr, elem_cache), {}
return (order, names, modulus, base, impl, p, absolute_degree, proof, prefix, repr, elem_cache), {}

def _change(self, obj, **kwds):
r"""
Expand All @@ -661,7 +682,7 @@ def _change(self, obj, **kwds):
return obj

key = obj._factory_data[2]
order, names, modulus, impl, p, absolute_degree, proof, prefix, repr, elem_cache = key
order, names, modulus, base, impl, p, absolute_degree, proof, prefix, repr, elem_cache = key

order = kwds.pop("order", order)

Expand All @@ -684,7 +705,7 @@ def _change(self, obj, **kwds):

elem_cache = kwds.pop("elem_cache", elem_cache)

base = kwds.pop("base", None if obj.base() is obj.base_ring() else modulus.base_ring())
base = kwds.pop("base", base)

if kwds:
raise ValueError("can only change arguments that GF understands when changing a finite field")
Expand Down Expand Up @@ -744,6 +765,8 @@ def create_object(self, version, key, **kwds):
# fields may not be created using this factory object, e.g., residue
# class fields.

# TODO: This needs to change. Old pickles should unpickle to unique
# parents, not to copies.
if len(key) == 5:
# for backward compatibility of pickles (see trac 10975).
order, name, modulus, impl, _ = key
Expand All @@ -762,29 +785,32 @@ def create_object(self, version, key, **kwds):
# as they are otherwise ignored
repr = kwds.get('repr', 'poly')
elem_cache = kwds.get('elem_cache', (order < 500))
else:
elif len(key) == 10:
order, name, modulus, impl, p, n, proof, prefix, repr, elem_cache = key
base = None
else:
order, name, modulus, base, impl, p, n, proof, prefix, repr, elem_cache = key

from sage.structure.proof.all import WithProof
with WithProof('arithmetic', proof):
if modulus is not None and modulus.base_ring().base() is not modulus.base_ring():
if modulus is not None and base:
from .finite_field_relative import FiniteField_relative
K = FiniteField_relative(base=modulus.base_ring(), modulus=modulus, names=name, impl=impl, proof=proof, prefix=prefix)
K = FiniteField_relative(base=base, modulus=modulus, names=name, impl=impl, proof=proof, prefix=prefix)
elif impl =='modn':
if n != 1:
raise ValueError("the 'modn' implementation requires a prime order")
from .finite_field_prime_modn import FiniteField_prime_modn
K = FiniteField_prime_modn(order, check=False, modulus=modulus)
elif impl == 'givaro':
K = FiniteField_givaro(order, name, modulus, repr, elem_cache)
elif impl == 'ntl':
from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e
K = FiniteField_ntl_gf2e(order, name, modulus)
elif impl == 'pari_ffelt' or impl == 'pari':
from .finite_field_pari_ffelt import FiniteField_pari_ffelt
K = FiniteField_pari_ffelt(p, modulus, name)
else:
raise ValueError("no such finite field implementation: %r" % impl)
modulus = modulus or PolynomialRing(GF(p), 'x')([-1, 1])
if impl == 'givaro':
K = FiniteField_givaro(order, name, modulus, repr, elem_cache)
elif impl == 'ntl':
from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e
K = FiniteField_ntl_gf2e(order, name, modulus)
elif impl == 'pari_ffelt' or impl == 'pari':
from .finite_field_pari_ffelt import FiniteField_pari_ffelt
K = FiniteField_pari_ffelt(p, modulus, name)
else:
raise ValueError("no such finite field implementation: %r" % impl)

if prefix is not None:
K._prefix = prefix
Expand Down Expand Up @@ -815,6 +841,6 @@ def is_PrimeFiniteField(x):
from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic

return isinstance(x, FiniteField_prime_modn) or \
(isinstance(x, FiniteField_generic) and x.degree() == 1)
(isinstance(x, FiniteField_generic) and x.base() is x)

zech_log_bound = 2**16
2 changes: 1 addition & 1 deletion src/sage/rings/finite_rings/finite_field_ntl_gf2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def gen(self, n=0):
sage: GF(2, impl='ntl').gen()
1
sage: GF(2, impl='ntl', modulus=polygen(GF(2)) ).gen()
sage: GF(2, impl='ntl', modulus=polygen(GF(2))).gen()
0
sage: GF(2^19, 'a').gen(1)
Traceback (most recent call last):
Expand Down
Loading

0 comments on commit 5e294d5

Please sign in to comment.