Ticket #14958: trac_14958-pseudo_conway.patch

File trac_14958-pseudo_conway.patch, 39.5 KB (added by pbruin, 9 years ago)

implement pseudo-Conway polynomials

  • sage/rings/finite_rings/all.py

    # HG changeset patch
    # User Peter Bruin <peter.bruin@math.uzh.ch>
    # Date 1374591311 -7200
    # Node ID 2e04315c45d778833b90a6fee4bfd3ae583d7f2d
    # Parent  5c832b93abf36281e7364d854704f34d40dbd3dd
    Trac 14958: implement pseudo-Conway polynomials
    
    diff --git a/sage/rings/finite_rings/all.py b/sage/rings/finite_rings/all.py
    a b  
    1818#                  http://www.gnu.org/licenses/
    1919#*****************************************************************************
    2020
    21 from constructor import (FiniteField, is_FiniteField, is_PrimeFiniteField,
    22                           conway_polynomial, exists_conway_polynomial)
     21from constructor import FiniteField, is_FiniteField, is_PrimeFiniteField
     22from conway_polynomials import conway_polynomial, exists_conway_polynomial
    2323GF = FiniteField
    2424
    2525from element_base import FinitePolyExtElement as FiniteFieldElement # for backward compatibility; is this needed?
  • sage/rings/finite_rings/constructor.py

    diff --git a/sage/rings/finite_rings/constructor.py b/sage/rings/finite_rings/constructor.py
    a b  
    168168from finite_field_givaro import FiniteField_givaro
    169169
    170170import sage.interfaces.gap
    171 import sage.databases.conway
    172171
    173172from sage.structure.factory import UniqueFactory
    174173
     
    177176    Return the globally unique finite field of given order with
    178177    generator labeled by the given name and possibly with given
    179178    modulus.
    180    
     179
    181180    INPUT:
    182    
    183    
    184     -  ``order`` - int
    185    
    186     -  ``name`` - string; must be specified if not a prime
    187        field
    188    
    189     -  ``modulus`` - (optional) either a defining polynomial for the
    190        field, i.e., generator of the field will be a root of this
    191        polynomial; or a string:
    192181
    193           - 'conway': force the use of a Conway polynomial, will
    194             raise a RuntimeError if none is found in the database;
    195           - 'random': use a random irreducible polynomial;
    196           - 'default': a Conway polynomial is used if found. Otherwise
    197             a sparse polynomial is used for binary fields and a
    198             random polynomial is used for other characteristics.
     182    - ``order`` -- a prime power
    199183
    200        Other options might be available depending on the
    201        implementation.
    202    
    203     -  ``elem_cache`` - cache all elements to avoid
    204        creation time (default: order < 500)
    205    
    206     -  ``check_irreducible`` - verify that the polynomial
    207        modulus is irreducible
     184    - ``name`` -- string; must be specified unless ``order`` is prime.
    208185
    209     - ``proof`` -- bool (default: True); if True use provable
     186    - ``modulus`` -- (optional) either a defining polynomial for the
     187      field, or a string specifying an algorithm to use to generate
     188      such a polynomial.  If ``modulus`` is a string, it is passed to
     189      :meth:`~sage.rings.polynomial.irreducible_element()` as the
     190      parameter ``algorithm``; see there for the permissible values of
     191      this parameter.
     192
     193    - ``elem_cache`` -- cache all elements to avoid creation time
     194      (default: order < 500)
     195
     196    - ``check_irreducible`` -- verify that the polynomial modulus is
     197      irreducible
     198
     199    - ``proof`` -- bool (default: ``True``): if ``True``, use provable
    210200      primality test; otherwise only use pseudoprimality test.
    211    
    212     -  ``args`` - additional parameters passed to finite
    213        field implementations
    214    
    215     -  ``kwds`` - additional keyword parameters passed to
    216        finite field implementations
    217    
    218    
    219     ALIAS: You can also use GF instead of FiniteField - they are
    220     identical.
     201
     202    - ``args`` -- additional parameters passed to finite field
     203      implementations
     204
     205    - ``kwds`` -- additional keyword parameters passed to finite field
     206      implementations
     207
     208    ALIAS: You can also use ``GF`` instead of ``FiniteField`` -- they
     209    are identical.
    221210
    222211    EXAMPLES::
    223    
     212
    224213        sage: k.<a> = FiniteField(9); k
    225214        Finite Field in a of size 3^2
    226215        sage: parent(a)
    227216        Finite Field in a of size 3^2
    228217        sage: charpoly(a, 'y')
    229218        y^2 + 2*y + 2
    230    
     219
    231220    We illustrate the proof flag.  The following example would hang
    232     for a very long time if we didn't use proof=False.  (NOTE: Magma
    233     only supports proof=False for making finite fields, so falsely
    234     appears to be faster than Sage -- see Trac 10975.)::
     221    for a very long time if we didn't use ``proof=False``.
     222
     223    .. NOTE::
     224
     225        Magma only supports ``proof=False`` for making finite fields,
     226        so falsely appears to be faster than Sage -- see :trac:10975.
     227
     228    ::
    235229
    236230        sage: k = FiniteField(10^1000 + 453, proof=False)
    237231        sage: k = FiniteField((10^1000 + 453)^2, 'a', proof=False)      # long time -- about 5 seconds
     
    244238        x^5 + 4*x + 1
    245239        sage: type(f)
    246240         <type 'sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint'>
    247    
     241
    248242    The modulus must be irreducible::
    249    
    250         sage: K.<a> = GF(5**5, name='a', modulus=x^5 - x )
     243
     244        sage: K.<a> = GF(5**5, name='a', modulus=x^5 - x)
    251245        Traceback (most recent call last):
    252246        ...
    253247        ValueError: finite field modulus must be irreducible but it is not.
    254    
    255     You can't accidentally fool the constructor into thinking the modulus
    256     is irreducible when it isn't mod p, since it actually tests
    257     irreducibility modulo p.  Also, the modulus has to be of the right degree.
    258    
    259     ::
    260    
     248
     249    You can't accidentally fool the constructor into thinking the
     250    modulus is irreducible when it is not, since it actually tests
     251    irreducibility modulo `p`.  Also, the modulus has to be of the
     252    right degree::
     253
    261254        sage: F.<x> = QQ[]
    262255        sage: factor(x^5 + 2)
    263256        x^5 + 2
    264         sage: K.<a> = GF(5**5, name='a', modulus=x^5 + 2 )
     257        sage: K.<a> = GF(5**5, name='a', modulus=x^5 + 2)
    265258        Traceback (most recent call last):
    266259        ...
    267260        ValueError: finite field modulus must be irreducible but it is not.
     
    270263        ...
    271264        ValueError: The degree of the modulus does not correspond to the
    272265        cardinality of the field.
    273    
     266
    274267    If you wish to live dangerously, you can tell the constructor not
    275     to test irreducibility using check_irreducible=False, but this can
    276     easily lead to crashes and hangs - so do not do it unless you know
    277     that the modulus really is irreducible and has the correct degree!
    278    
     268    to test irreducibility using ``check_irreducible=False``, but this
     269    can easily lead to crashes and hangs -- so do not do it unless you
     270    know that the modulus really is irreducible and has the correct
     271    degree!
     272
    279273    ::
    280    
     274
    281275        sage: F.<x> = GF(5)[]
    282276        sage: K.<a> = GF(5**2, name='a', modulus=x^2 + 2, check_irreducible=False)
    283277
    284     ::
    285 
    286278        sage: L = GF(3**2, name='a', modulus=QQ[x](x - 1), check_irreducible=False)
    287279        sage: L.list()  # random
    288280        [0, a, 1, 2, 1, 2, 1, 2, 1]
    289281
    290282    The order of a finite field must be a prime power::
    291    
     283
    292284        sage: GF(1)
    293285        Traceback (most recent call last):
    294286        ...
     
    297289        Traceback (most recent call last):
    298290        ...
    299291        ValueError: the order of a finite field must be a prime power.
    300    
     292
    301293    Finite fields with explicit random modulus are not cached::
    302    
     294
    303295        sage: k.<a> = GF(5**10, modulus='random')
    304296        sage: n.<a> = GF(5**10, modulus='random')
    305297        sage: n is k
    306298        False
    307299        sage: GF(5**10, 'a') is GF(5**10, 'a')
    308300        True
    309    
     301
    310302    We check that various ways of creating the same finite field yield
    311     the same object, which is cached.
    312    
    313     ::
    314    
     303    the same object, which is cached::
     304
    315305        sage: K = GF(7, 'a')
    316306        sage: L = GF(7, 'b')
    317307        sage: K is L
     
    325315        sage: K is M
    326316        True
    327317   
    328     You may print finite field elements as integers. This
    329     currently only works if the order of field is `<2^{16}`,
    330     though.
    331    
    332     ::
    333    
     318    You may print finite field elements as integers. This currently
     319    only works if the order of field is `<2^{16}`, though::
     320
    334321        sage: k.<a> = GF(2^8, repr='int')
    335322        sage: a
    336323        2
     
    513500
    514501    return isinstance(x, FiniteField_prime_modn) or \
    515502           (isinstance(x, FiniteField_generic) and x.degree() == 1)
    516    
    517 ##################################################################
    518    
    519 def conway_polynomial(p, n):
    520     r"""
    521     Return the Conway polynomial of degree n over GF(p), which is
    522     loaded from a table.
    523    
    524     If the requested polynomial is not known, this function raises a
    525     RuntimeError exception.
    526    
    527     INPUT:
    528    
    529    
    530     -  ``p`` - int
    531    
    532     -  ``n`` - int
    533    
    534    
    535     OUTPUT:
    536    
    537    
    538     -  ``Polynomial`` - a polynomial over the prime finite
    539        field GF(p).
    540    
    541    
    542     .. note::
    543 
    544        The first time this function is called a table is read from
    545        disk, which takes a fraction of a second. Subsequent calls do
    546        not require reloading the table.
    547    
    548     See also the ``ConwayPolynomials()`` object, which is a
    549     table of Conway polynomials. For example, if
    550     ``c=ConwayPolynomials()``, then
    551     ``c.primes()`` is a list of all primes for which the
    552     polynomials are known, and for a given prime `p`,
    553     ``c.degree(p)`` is a list of all degrees for which the
    554     Conway polynomials are known.
    555    
    556     EXAMPLES::
    557    
    558         sage: conway_polynomial(2,5)
    559         x^5 + x^2 + 1
    560         sage: conway_polynomial(101,5)
    561         x^5 + 2*x + 99
    562         sage: conway_polynomial(97,101)
    563         Traceback (most recent call last):
    564         ...
    565         RuntimeError: requested conway polynomial not in database.
    566     """
    567     (p,n)=(int(p),int(n))
    568     R = FiniteField(p)['x']
    569     try:
    570         return R(sage.databases.conway.ConwayPolynomials()[p][n])
    571     except KeyError:
    572         raise RuntimeError("requested conway polynomial not in database.")
    573 
    574 def exists_conway_polynomial(p, n):
    575     r"""
    576     Return True if the Conway polynomial over `F_p` of degree
    577     `n` is in the database and False otherwise.
    578    
    579     If the Conway polynomial is in the database, to obtain it use the
    580     command ``conway_polynomial(p,n)``.
    581    
    582     EXAMPLES::
    583    
    584         sage: exists_conway_polynomial(2,3)
    585         True
    586         sage: exists_conway_polynomial(2,-1)
    587         False
    588         sage: exists_conway_polynomial(97,200)
    589         False
    590         sage: exists_conway_polynomial(6,6)
    591         False
    592     """
    593     return sage.databases.conway.ConwayPolynomials().has_polynomial(p,n)
    594 
    595503
    596504zech_log_bound = 2**16
  • new file sage/rings/finite_rings/conway_polynomials.py

    diff --git a/sage/rings/finite_rings/conway_polynomials.py b/sage/rings/finite_rings/conway_polynomials.py
    new file mode 100644
    - +  
     1"""
     2Routines for Conway and pseudo-Conway polynomials.
     3
     4AUTHORS:
     5
     6- David Roe
     7
     8- Jean-Pierre Flori
     9
     10"""
     11from sage.structure.sage_object import SageObject
     12from constructor import FiniteField
     13import sage.databases.conway
     14import weakref
     15
     16def conway_polynomial(p, n):
     17    """
     18    Return the Conway polynomial of degree `n` over ``GF(p)``.
     19
     20    If the requested polynomial is not known, this function raises a
     21    ``RuntimeError`` exception.
     22
     23    INPUT:
     24
     25    - ``p`` -- prime number
     26
     27    - ``n`` -- positive integer
     28
     29    OUTPUT:
     30
     31    - the Conway polynomial of degree `n` over the finite field
     32      ``GF(p)``, loaded from a table.
     33
     34    .. NOTE::
     35
     36       The first time this function is called a table is read from
     37       disk, which takes a fraction of a second. Subsequent calls do
     38       not require reloading the table.
     39
     40    See also the ``ConwayPolynomials()`` object, which is a table of
     41    Conway polynomials.
     42
     43    For example, if ``c = ConwayPolynomials()``, then ``c.primes()``
     44    is a list of all primes for which the polynomials are known, and
     45    for a given prime `p`, ``c.degree(p)`` is a list of all degrees
     46    for which the Conway polynomials are known.
     47
     48    EXAMPLES::
     49
     50        sage: conway_polynomial(2,5)
     51        x^5 + x^2 + 1
     52        sage: conway_polynomial(101,5)
     53        x^5 + 2*x + 99
     54        sage: conway_polynomial(97,101)
     55        Traceback (most recent call last):
     56        ...
     57        RuntimeError: requested Conway polynomial not in database.
     58    """
     59    (p, n) = (int(p), int(n))
     60    R = FiniteField(p)['x']
     61    try:
     62        return R(sage.databases.conway.ConwayPolynomials()[p][n])
     63    except KeyError:
     64        raise RuntimeError("requested Conway polynomial not in database.")
     65
     66def exists_conway_polynomial(p, n):
     67    """
     68    Return ``True`` if the Conway polynomial of degree `n` over
     69    ``GF(p)`` is in the database and ``False`` otherwise.
     70
     71    If the Conway polynomial is in the database, it can be obtained
     72    using the command ``conway_polynomial(p,n)``.
     73
     74    EXAMPLES::
     75
     76        sage: exists_conway_polynomial(2,3)
     77        True
     78        sage: exists_conway_polynomial(2,-1)
     79        False
     80        sage: exists_conway_polynomial(97,200)
     81        False
     82        sage: exists_conway_polynomial(6,6)
     83        False
     84    """
     85    return sage.databases.conway.ConwayPolynomials().has_polynomial(p,n)
     86
     87pseudo_conway_poly = {}
     88
     89class PseudoConwayPolyTree(SageObject):
     90    """
     91    An object holding references to pseudo-Conway polynomials for
     92    divisors of the given degree, so that they aren't garbage
     93    collected.
     94    """
     95    def __init__(self, p, n, nodes_dict, f):
     96        """
     97        INPUT::
     98
     99        - ``p`` -- The prime for which this defines an extension of
     100          ``GF(p)``.
     101
     102        - ``n`` -- The degree of the extension.
     103
     104        - ``nodes_dict`` -- dict or bool:
     105
     106          - either a dictionary of ``PseudoConwayPolyTree`` objects,
     107            indexed by prime divisors of `n`.  The entry for `q`
     108            corresponds to the pseudo-Conway extension of degree
     109            `n/q`.
     110
     111          - or a boolean.  If True, then this polynomial is actually
     112            in the Conway polynomials database, and no references to
     113            other PCPTs are stored.  If False, then `n` is prime and
     114            no references are stored (since there is no compatibility
     115            condition).
     116
     117        - ``f`` -- The polynomial defining this extension.
     118
     119        EXAMPLES::
     120
     121            sage: from sage.rings.finite_rings.conway_polynomials import find_pseudo_conway_polynomial_tree
     122            sage: PCPT = find_pseudo_conway_polynomial_tree(2, 6, False)
     123            sage: PCPT.get_pseudo_conway_poly(3)
     124            x^3 + x + 1
     125
     126        """
     127        self.p = p
     128        self.n = n
     129        self.nodes_dict = nodes_dict
     130        self.f = f
     131
     132    def get_pseudo_conway_poly(self, m):
     133        """
     134        Return the pseudo-Conway polynomial associated to a divisor
     135        of the degree of this tree.
     136
     137        EXAMPLES::
     138
     139            sage: from sage.rings.finite_rings.conway_polynomials import find_pseudo_conway_polynomial_tree
     140            sage: PCPT = find_pseudo_conway_polynomial_tree(2, 6, False)
     141            sage: PCPT.get_pseudo_conway_poly(3)
     142            x^3 + x + 1
     143            sage: PCPT.get_pseudo_conway_poly(4)
     144            Traceback (most recent call last):
     145            ...
     146            RuntimeError: 4 does not divide 6
     147
     148        """
     149        if m == self.n:
     150            return self.f
     151        if not m.divides(self.n):
     152            raise RuntimeError, "%s does not divide %s"%(m, self.n)
     153        try:
     154            return pseudo_conway_poly[self.p][m]().f
     155        except (KeyError, AttributeError):
     156            return conway_polynomial(self.p, m)
     157
     158    def check_consistency(self):
     159        """
     160        Checks that the pseudo-Conway polynomials dividing the degree
     161        of this tree satisfy the required compatibility conditions.
     162
     163        EXAMPLES::
     164
     165            sage: from sage.rings.finite_rings.conway_polynomials import find_pseudo_conway_polynomial_tree
     166            sage: PCPT = find_pseudo_conway_polynomial_tree(2, 6, False)
     167            sage: PCPT.check_consistency()
     168            sage: PCPT = find_pseudo_conway_polynomial_tree(2, 60, False) # long
     169            sage: PCPT.check_consistency() # long
     170
     171        """
     172        p, n = self.p, self.n
     173        K = FiniteField(p**n, modulus = self.f, names='a')
     174        a = K.gen()
     175        for m in n.divisors():
     176            assert (a**((p**n-1)//(p**m-1))).minimal_polynomial() == self.get_pseudo_conway_poly(m)
     177
     178def find_pseudo_conway_polynomial_tree(p, n, use_database=True):
     179    r"""
     180    Return an object holding references to a set of consistent
     181    pseudo-Conway polynomials for degrees dividing `n`.
     182
     183    The Conway polynomial `f` of degree `n` over `\Bold{F}_p` is
     184    defined by the following three conditions:
     185
     186    - `f` is irreducible.
     187
     188    - In the quotient field `\Bold{F}_p[x]/(f)`, the element
     189      `x\bmod f` generates the multiplicative group.
     190
     191    - The minimal polynomial of `(x\bmod f)^{\frac{p^n-1}{p^m-1}}`
     192      equals the Conway polynomial of degree `m` for every divisor `m`
     193      of `n`.
     194
     195    - `f` is lexicographically least among all such polynomials, under
     196      a certain ordering.
     197
     198    The final condition is needed only in order to make the Conway
     199    polynomial unique.  We define a pseudo-Conway polynomial to be one
     200    satisfying the first three conditions.
     201
     202    INPUT:
     203
     204    - `p` -- prime number
     205
     206    - `n` -- integer greater than 1
     207
     208    - `use_database` -- whether to use the Conway polynomials database
     209      if the Conway polynomial for `p, n` exists within it (versus
     210      computing a pseudo-Conway polynomial)
     211
     212    EXAMPLES::
     213
     214        sage: from sage.rings.finite_rings.conway_polynomials import find_pseudo_conway_polynomial_tree
     215        sage: PCPT = find_pseudo_conway_polynomial_tree(2, 12, False)
     216        sage: PCPT.f
     217        x^12 + x^11 + x^9 + x^5 + x^3 + x + 1
     218        sage: PCPT = find_pseudo_conway_polynomial_tree(5, 6, False)
     219        sage: PCPT.f
     220        x^6 + x^5 + 4*x^4 + 3*x^3 + 3*x^2 + 2*x + 2
     221        sage: PCPT = find_pseudo_conway_polynomial_tree(5, 11, False)
     222        sage: PCPT.f
     223        x^11 + x^6 + 3*x^3 + 4*x + 3
     224    """
     225    if not pseudo_conway_poly.has_key(p):
     226        pseudo_conway_poly[p] = {}
     227    pdict = pseudo_conway_poly[p]
     228    n = sage.rings.integer.Integer(n)
     229    if pdict.has_key(n):
     230        pcp = pdict[n]()
     231        if pcp is not None:
     232            return pcp
     233    if use_database and exists_conway_polynomial(p, n):
     234        ans = PseudoConwayPolyTree(p, n, True, FiniteField(p)['x'](conway_polynomial(p, n)))
     235    elif n == 1:
     236        ans = PseudoConwayPolyTree(p, n, False, compute_pseudo_conway_polynomial(p, n, None))
     237    else:
     238        nodes = {}
     239        for q in n.prime_factors():
     240            nodes[q] = find_pseudo_conway_polynomial_tree(p, n//q, use_database)
     241        ans = PseudoConwayPolyTree(p, n, nodes, compute_pseudo_conway_polynomial(p, n, nodes))
     242    pdict[n] = weakref.ref(ans)
     243    return ans
     244
     245def _find_pow_of_frobenius(p, n, x, y):
     246    """
     247    Find the power of Frobenius which yields `x` when applied to `y`.
     248
     249    INPUT:
     250
     251    - ``p`` -- prime number
     252
     253    - ``n`` -- positive integer
     254
     255    - ``x`` -- an element of a field `K` of `p^n` elements so that
     256      the multiplicative order of `x` is `p^n - 1`.
     257
     258    - ``y`` -- an element of `K` with the same minimal polynomial as
     259      `x`.
     260
     261    OUTPUT:
     262
     263    - an element `i` of the integers modulo `n` such that `x = y^{p^i}`.
     264
     265    EXAMPLES::
     266
     267        sage: from sage.rings.finite_rings.conway_polynomials import _find_pow_of_frobenius
     268        sage: K.<a> = GF(3^14)
     269        sage: x = K.multiplicative_generator()
     270        sage: y = x^27
     271        sage: _find_pow_of_frobenius(3, 14, x, y)
     272        11
     273
     274    """
     275    from integer_mod import mod
     276    for i in xrange(n):
     277        if x == y: break
     278        y = y**p
     279    else:
     280        raise RuntimeError, "No appropriate power of Frobenius found"
     281    return mod(i, n)
     282
     283def _crt_non_coprime(running, a):
     284    """
     285    Extension of the ``crt`` method of ``IntegerMod`` to the case of
     286    non-relatively prime modulus.
     287
     288    EXAMPLES::
     289
     290        sage: from sage.rings.finite_rings.conway_polynomials import _crt_non_coprime
     291        sage: a = _crt_non_coprime(mod(14, 18), mod(20,30)); a
     292        50
     293        sage: a.modulus()
     294        90
     295        sage: _crt_non_coprime(mod(13, 18), mod(20,30))
     296        Traceback (most recent call last):
     297        ...
     298        AssertionError
     299
     300    """
     301    g = running.modulus().gcd(a.modulus())
     302    if g == 1:
     303        return running.crt(a)
     304    else:
     305        assert running % g == a % g
     306        running_modulus = running.modulus()
     307        a_modulus = a.modulus()
     308        for qq in g.prime_divisors():
     309            a_val_unit = a_modulus.val_unit(qq)
     310            running_val_unit = running_modulus.val_unit(qq)
     311            if a_val_unit[0] > running_val_unit[0]:
     312                running_modulus = running_val_unit[1]
     313            else:
     314                a_modulus = a_val_unit[1]
     315        return (running % running_modulus).crt(a % a_modulus)
     316
     317def _frobenius_shift(K, generators, check_only=False):
     318    """
     319    Given a field `K` of degree `n` over ``GF(p)`` and a dictionary
     320    holding, for each divisor `q` of `n`, an element with minimal
     321    polynomial a pseudo-Conway polynomial of degree `n/q`, modify
     322    these generators into a compatible system.
     323
     324    Such a system of generators is said to be compatible if for each
     325    pair of prime divisors `q_1` and `q_2` and each common divisor `m`
     326    of `n/q_1` and `n/q_2`, the equality
     327
     328    ``generators[q1]^((p^(n/q1)-1)/(p^m-1)) == generators[q2]^((p^(n/q2)-1)/(p^m-1))``
     329
     330    holds.
     331
     332    INPUT:
     333
     334    - ``K`` -- a finite field of degree `n` over its prime field
     335
     336    - ``generators`` -- a dictionary, indexed by prime divisors `q` of
     337      `n`, whose entries are elements of `K` satisfying the `n/q`
     338      pseudo-Conway polynomial.
     339
     340    - ``check_only`` -- if ``True``, just check that the given
     341      generators form a compatible system.
     342
     343    EXAMPLES::
     344
     345        sage: R.<x> = GF(2)[]
     346        sage: f30 = x^30 + x^28 + x^27 + x^25 + x^24 + x^20 + x^19 + x^18 + x^16 + x^15 + x^12 + x^10 + x^7 + x^2 + 1
     347        sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1
     348        sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1
     349        sage: K.<a> = GF(2^60, modulus='first_lexicographic')
     350        sage: x30 = f30.any_root(K)
     351        sage: x20 = f20.any_root(K)
     352        sage: x12 = f12.any_root(K)
     353        sage: generators = {2: x30, 3: x20, 5: x12}
     354        sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius
     355        sage: _frobenius_shift(K, generators)
     356        sage: _find_pow_of_frobenius(2, 30, x30, generators[2])
     357        0
     358        sage: _find_pow_of_frobenius(2, 20, x20, generators[3])
     359        13
     360        sage: _find_pow_of_frobenius(2, 12, x12, generators[5])
     361        8
     362
     363    """
     364    if len(generators) == 1:
     365        return generators
     366    p = K.characteristic()
     367    n = K.degree()
     368    compatible = {}
     369    from integer_mod import mod
     370    for m in n.divisors():
     371        compatible[m] = {}
     372    for q, x in generators.iteritems():
     373        for m in (n//q).divisors():
     374            compatible[m][q] = x**((p**(n//q)-1)//(p**m-1))
     375    if check_only:
     376        for m in n.divisors():
     377            try:
     378                q, x = compatible[m].popitem()
     379            except KeyError:
     380                break
     381            for qq, xx in compatible[m].iteritems():
     382                assert x == xx
     383        return
     384    crt = {}
     385    qlist = sorted(generators.keys())
     386    for j in range(1, len(qlist)):
     387        for i in range(j):
     388            crt[(i, j)] = []
     389    for m in n.divisors():
     390        mqlist = sorted(compatible[m].keys())
     391        for k in range(1,len(mqlist)):
     392            j = qlist.index(mqlist[k])
     393            i = qlist.index(mqlist[k-1])
     394            crt[(i,j)].append(_find_pow_of_frobenius(p, m, compatible[m][qlist[j]], compatible[m][qlist[i]]))
     395    from integer_mod import mod
     396    pairs = crt.keys()
     397    for i, j in pairs:
     398        L = crt[(i,j)]
     399        running = mod(0,1)
     400        for a in L:
     401            running = _crt_non_coprime(running, a)
     402        crt[(i,j)] = [(mod(running, q**(running.modulus().valuation(q))), running.modulus().valuation(q)) for q in qlist]
     403        crt[(j,i)] = [(-a, level) for a, level in crt[(i,j)]]
     404    # Let x_j be the power of Frobenius we apply to generators[qlist[j]], for 0 < j < len(qlist)
     405    # We have some direct conditions on the x_j: x_j reduces to each entry in crt[(0,j)].
     406    # But we also have the equations x_j - x_i reduces to each entry in crt[(i,j)].
     407    # We solve for x_j one prime at a time.  For each prime, we have an equations of the form
     408    # x_j - x_i = c_ij.  The modulus of the currently known value of x_j, x_i and c_ij will all be powers
     409    # (possibly 0, possibly different) of the same prime.
     410
     411    # We can set x_0=0 everywhere, can get an initial setting of x_j from the c_0j.
     412    # We go through prime by prime.
     413    import bisect
     414    frob_powers=[mod(0,1) for q in qlist]
     415    def find_leveller(qindex, level, x, xleveled, searched, i):
     416        searched[i] = True
     417        crt_possibles = []
     418        for j in range(1,len(qlist)):
     419            if i==j: continue
     420            if crt[(i,j)][qindex][1] >= level:
     421                if xleveled[j]:
     422                    return [j]
     423                elif not searched.has_key(j):
     424                    crt_possibles.append(j)
     425        for j in crt_possibles:
     426            path = find_leveller(qindex, level, x, xleveled, searched, j)
     427            if path is not None:
     428                path.append(j)
     429                return path
     430        return None
     431    def propogate_levelling(qindex, level, x, xleveled, i):
     432        for j in range(1, len(qlist)):
     433            if i==j: continue
     434            if not xleveled[j] and crt[(i,j)][qindex][1] >= level:
     435                newxj = x[i][0] + crt[(i,j)][qindex][0]
     436                x[j] = (newxj, min(x[i][1], crt[(i,j)][qindex][1]))
     437                xleveled[j] = True
     438                propogate_levelling(qindex, level, x, xleveled, j)
     439
     440    for qindex in range(len(qlist)):
     441        q = qlist[qindex]
     442        # We include the initial 0 to match up our indexing with crt.
     443        x = [0] + [crt[(0,j)][qindex] for j in range(1,len(qlist))]
     444        # We first check that our equations are consistent and
     445        # determine which powers of q occur as moduli.
     446        levels = []
     447        for j in range(2, len(qlist)):
     448            for i in range(j):
     449                # we need crt[(0,j)] = crt[(0,i)] + crt[(i,j)]
     450                if i != 0:
     451                    assert x[j][0] == x[i][0] + crt[(i,j)][qindex][0]
     452                level = crt[(i,j)][qindex][1]
     453                if level > 0:
     454                    ins = bisect.bisect_left(levels,level)
     455                    if ins == len(levels):
     456                        levels.append(level)
     457                    elif levels[ins] != level:
     458                        levels.insert(ins, level)
     459        for level in levels:
     460            xleveled = [0] + [x[i][1] >= level for i in range(1,len(qlist))]
     461            while True:
     462                try:
     463                    i = xleveled.index(False, 1)
     464                    searched = {}
     465                    levelling_path = find_leveller(qindex, level, x, xleveled, searched, i)
     466                    if levelling_path is None:
     467                        # Any lift will work, since there are no constraints.
     468                        x[i] = (mod(x[i][0].lift(), q**level), level)
     469                        xleveled[i] = True
     470                        propogate_levelling(qindex, level, x, xleveled, i)
     471                    else:
     472                        levelling_path.append(i)
     473                        for m in range(1,len(path)):
     474                            # This point on the path may have already
     475                            # been leveled in a previous propagation.
     476                            if not xleveled[path[m]]:
     477                                newx = x[path[m-1]][0] + crt[(path[m-1],path[m])][qindex][0]
     478                                x[path[m]] = (newx, min(x[path[m-1]][1], crt[(path[m-1],path[m])][qindex][1]))
     479                                xleveled[path[m]] = True
     480                                propogate_levelling(qindex, level, x, xleveled, path[m])
     481                except ValueError:
     482                    break
     483        for j in range(1,len(qlist)):
     484            frob_powers[j] = frob_powers[j].crt(x[j][0])
     485    for j in range(1, len(qlist)):
     486        generators[qlist[j]] = generators[qlist[j]]**(p**(-frob_powers[j]).lift())
     487    _frobenius_shift(K, generators, check_only=True)
     488
     489def compute_pseudo_conway_polynomial(p, n, nodes):
     490    r"""
     491    Compute a pseudo-Conway polynomial of degree `n` for the prime
     492    `p`.
     493
     494    The Conway polynomial `f` of degree `n` over `\Bold{F}_p` is
     495    defined by the following three conditions:
     496
     497    - `f` is irreducible.
     498
     499    - In the quotient field `\Bold{F}_p[x]/(f)`, the element
     500      `x\bmod f` generates the multiplicative group.
     501
     502    - The minimal polynomial of `(x\bmod f)^{\frac{p^n-1}{p^m-1}}`
     503      equals the Conway polynomial of degree `m` for every divisor `m`
     504      of `n`.
     505
     506    - `f` is lexicographically least among all such polynomials, under
     507      a certain ordering.
     508
     509    The final condition is needed only in order to make the Conway
     510    polynomial unique.  We define a pseudo-Conway polynomial to be one
     511    satisfying the first three conditions.
     512
     513    INPUT:
     514
     515    - `p` -- prime number
     516
     517    - `n` -- positive integer
     518
     519    - ``nodes`` -- None (in the case that `n` is prime) or a
     520      dictionary of ``PseudoConwayPolyTree`` objects, indexed by prime
     521      divisors `q` of `n`, with entries corresponding to pseudo-Conway
     522      polynomials of degree `n/q`.
     523
     524    OUTPUT:
     525
     526    - a pseudo-Conway polynomial of degree `n` for the prime `p`.
     527
     528    ALGORITHM:
     529
     530    Uses an algorithm described in [HL99]_, modified to find
     531    pseudo-Conway polynomials rather than Conway polynomials.  The
     532    major difference is that we stop as soon as we find a primitive
     533    polynomial.
     534
     535    REFERENCE:
     536
     537    .. [HL99] L. Heath and N. Loehr (1999).
     538       New algorithms for generating Conway polynomials over finite fields.
     539       Proceedings of the tenth annual ACM-SIAM symposium on discrete
     540       algorithms, pp. 429-437.
     541
     542    EXAMPLES::
     543
     544        sage: from sage.rings.finite_rings.conway_polynomials import compute_pseudo_conway_polynomial, find_pseudo_conway_polynomial_tree
     545        sage: PCPT30 = find_pseudo_conway_polynomial_tree(2, 30, False)
     546        sage: PCPT20 = find_pseudo_conway_polynomial_tree(2, 20, False)
     547        sage: PCPT12 = find_pseudo_conway_polynomial_tree(2, 12, False)
     548        sage: compute_pseudo_conway_polynomial(2, 60, {2:PCPT30, 3:PCPT20, 5:PCPT12})
     549        x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1
     550    """
     551    k = FiniteField(p)
     552    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
     553    R = PolynomialRing(k, names='x')
     554    x = R.gen()
     555    if n == 1:
     556        return x - k.multiplicative_generator()
     557
     558    # Work in an arbitrary field K of order p**n.
     559    K = FiniteField(p**n, names='a')
     560    r = p**n - 1
     561    xi = {}
     562    for q in nodes.iterkeys():
     563        xi[q] = nodes[q].f.any_root(K, -n//q, assume_squarefree=True)
     564
     565    # The following is needed to ensure that in the concrete instantiation
     566    # of the "new" extension all previous choices are compatible.
     567    _frobenius_shift(K, xi)
     568
     569    # Construct a compatible element having order the lcm of orders
     570    q, x = xi.popitem()
     571    v = p**(n//q) - 1
     572    for q, xitem in xi.iteritems():
     573        w = p**(n//q) - 1
     574        g, alpha, beta = v.xgcd(w)
     575        x = x**beta * xitem**alpha
     576        v = v.lcm(w)
     577
     578    # Get the missing part of the order to be primitive
     579    g = r // v
     580    # Iterate through g-th roots of x until a primitive one is found
     581    z = x.nth_root(g)
     582    root = K.multiplicative_generator()**v
     583    while z.multiplicative_order() != r:
     584        z *= root
     585    # The following should work but tries to create a huge list
     586    # whose length overflows Python's ints for large parameters
     587    #Z = x.nth_root(g, all=True)
     588    #for z in Z:
     589    #    if z.multiplicative_order() == r:
     590    #         break
     591    return z.minimal_polynomial()
  • sage/rings/finite_rings/element_givaro.pyx

    diff --git a/sage/rings/finite_rings/element_givaro.pyx b/sage/rings/finite_rings/element_givaro.pyx
    a b  
    125125    ConwayPolynomials = sage.databases.conway.ConwayPolynomials
    126126   
    127127    import sage.rings.finite_rings.constructor
    128     conway_polynomial = sage.rings.finite_rings.constructor.conway_polynomial
     128    conway_polynomial = sage.rings.finite_rings.conway_polynomials.conway_polynomial
    129129
    130130    import sage.rings.polynomial.multi_polynomial_element
    131131    MPolynomial = sage.rings.polynomial.multi_polynomial_element.MPolynomial
  • sage/rings/finite_rings/element_ntl_gf2e.pyx

    diff --git a/sage/rings/finite_rings/element_ntl_gf2e.pyx b/sage/rings/finite_rings/element_ntl_gf2e.pyx
    a b  
    108108    import sage.databases.conway
    109109    ConwayPolynomials = sage.databases.conway.ConwayPolynomials
    110110   
    111     import sage.rings.finite_rings.constructor
    112     conway_polynomial = sage.rings.finite_rings.constructor.conway_polynomial
     111    import sage.rings.finite_rings.conway_polynomials
     112    conway_polynomial = sage.rings.finite_rings.conway_polynomials.conway_polynomial
    113113
    114114    import sage.rings.polynomial.multi_polynomial_element
    115115    MPolynomial = sage.rings.polynomial.multi_polynomial_element.MPolynomial
  • sage/rings/finite_rings/finite_field_base.pyx

    diff --git a/sage/rings/finite_rings/finite_field_base.pyx b/sage/rings/finite_rings/finite_field_base.pyx
    a b  
    777777            sage: GF(next_prime(2^16, 2), 'a').is_conway()
    778778            False
    779779        """
    780         from constructor import conway_polynomial, exists_conway_polynomial
     780        from conway_polynomials import conway_polynomial, exists_conway_polynomial
    781781        p = self.characteristic()
    782782        n = self.degree()
    783783        return (exists_conway_polynomial(p, n)
  • sage/rings/finite_rings/finite_field_ext_pari.py

    diff --git a/sage/rings/finite_rings/finite_field_ext_pari.py b/sage/rings/finite_rings/finite_field_ext_pari.py
    a b  
    206206        self.__is_field = True
    207207
    208208        if modulus is None or modulus == "default":
    209             from constructor import exists_conway_polynomial
     209            from conway_polynomials import exists_conway_polynomial
    210210            if exists_conway_polynomial(self.__char, self.__degree):
    211211                modulus = "conway"
    212212            else:
     
    214214
    215215        if isinstance(modulus,str):
    216216            if modulus == "conway":
    217                 from constructor import conway_polynomial
     217                from conway_polynomials import conway_polynomial
    218218                modulus = conway_polynomial(self.__char, self.__degree)
    219219            elif modulus == "random":
    220220                # The following is fast/deterministic, but has serious problems since
  • sage/rings/finite_rings/finite_field_givaro.py

    diff --git a/sage/rings/finite_rings/finite_field_givaro.py b/sage/rings/finite_rings/finite_field_givaro.py
    a b  
    128128            if k == 1:
    129129                modulus = 'random' # this will use the gfq_factory_pk function.
    130130            elif ConwayPolynomials().has_polynomial(p, k):
    131                 from sage.rings.finite_rings.constructor import conway_polynomial
     131                from sage.rings.finite_rings.conway_polynomials import conway_polynomial
    132132                modulus = conway_polynomial(p, k)
    133133            elif modulus is None:
    134134                modulus = 'random'
  • sage/rings/finite_rings/finite_field_ntl_gf2e.py

    diff --git a/sage/rings/finite_rings/finite_field_ntl_gf2e.py b/sage/rings/finite_rings/finite_field_ntl_gf2e.py
    a b  
    22Finite Fields of Characteristic 2
    33"""
    44
    5 
    65from sage.rings.finite_rings.finite_field_base import FiniteField
    76from sage.libs.pari.all import pari
    87from finite_field_ext_pari import FiniteField_ext_pari
     
    2928    import sage.rings.finite_rings.finite_field_base
    3029    is_FiniteField = sage.rings.finite_rings.finite_field_base.is_FiniteField
    3130
    32     import sage.rings.finite_rings.constructor
    33     exists_conway_polynomial = sage.rings.finite_rings.constructor.exists_conway_polynomial
    34     conway_polynomial = sage.rings.finite_rings.constructor.conway_polynomial
    35 
    36     import sage.rings.finite_rings.constructor
    37     exists_conway_polynomial = sage.rings.finite_rings.constructor.exists_conway_polynomial
     31    import sage.rings.finite_rings.conway_polynomials
     32    exists_conway_polynomial = sage.rings.finite_rings.conway_polynomials.exists_conway_polynomial
     33    conway_polynomial = sage.rings.finite_rings.conway_polynomials.conway_polynomial
    3834
    3935    import sage.rings.finite_rings.element_ntl_gf2e
    4036    Cache_ntl_gf2e = sage.rings.finite_rings.element_ntl_gf2e.Cache_ntl_gf2e
     
    9692        sage: k.<a> = GF(2^211, modulus='conway')
    9793        sage: k.modulus()
    9894        x^211 + x^9 + x^6 + x^5 + x^3 + x + 1
    99         sage: k.<a> = GF(2^411, modulus='conway')
    100         Traceback (most recent call last):
    101         ...
    102         RuntimeError: requested conway polynomial not in database.
     95        sage: k.<a> = GF(2^23, modulus='conway')
     96        sage: a.multiplicative_order() == k.order() - 1
     97        True
    10398    """
    10499
    105100    def __init__(self, q, names="a",  modulus=None, repr="poly"):
  • sage/rings/polynomial/polynomial_ring.py

    diff --git a/sage/rings/polynomial/polynomial_ring.py b/sage/rings/polynomial/polynomial_ring.py
    a b  
    21432143            degree `n` with minimal number of non-zero coefficients.
    21442144            Only implemented for `p = 2`.
    21452145
     2146          - ``'pseudo-conway'``: return a pseudo-Conway polynomial of
     2147            degree `n` over the field of `p` elements.
     2148
    21462149          - ``'random'``: try random polynomials until an irreducible
    21472150            one is found.
    21482151
     
    21612164            x^2 + 4*x + 2
    21622165            sage: GF(5)['x'].irreducible_element(2, algorithm="adleman-lenstra")
    21632166            x^2 + x + 1
     2167            sage: GF(5)['x'].irreducible_element(2, algorithm="pseudo-conway")
     2168            x^2 + 4*x + 2
    21642169
    21652170            sage: GF(2)['x'].irreducible_element(33)
    21662171            x^33 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^3 + 1
     
    21722177        - Peter Bruin (June 2013)
    21732178        """
    21742179        from sage.libs.pari.all import pari
    2175         from sage.rings.finite_rings.constructor import (conway_polynomial,
    2176                                                          exists_conway_polynomial)
     2180        from sage.rings.finite_rings.conway_polynomials import \
     2181            conway_polynomial, exists_conway_polynomial, find_pseudo_conway_polynomial_tree
    21772182        from polynomial_gf2x import (GF2X_BuildIrred_list, GF2X_BuildSparseIrred_list,
    21782183                                     GF2X_BuildRandomIrred_list)
    21792184
     
    21932198            return self(pari(p).ffinit(n))
    21942199        elif algorithm == "conway":
    21952200            return self(conway_polynomial(p, n))
     2201        elif algorithm == "pseudo-conway":
     2202            pcpt = find_pseudo_conway_polynomial_tree(p, n)
     2203            return self(pcpt.f)
    21962204        elif algorithm == "first_lexicographic":
    21972205            if p == 2:
    21982206                return self(GF2X_BuildIrred_list(n))