Path: blob/master/sage/algebras/steenrod/steenrod_algebra_bases.py
4156 views
"""1Steenrod algebra bases23AUTHORS:45- John H. Palmieri (2008-07-30): version 0.96- John H. Palmieri (2010-06-30): version 1.07- Simon King (2011-10-25): Fix the use of cached functions89This package defines functions for computing various bases of the10Steenrod algebra, and for converting between the Milnor basis and11any other basis.1213This packages implements a number of different bases, at least at14the prime 2. The Milnor and Serre-Cartan bases are the most15familiar and most standard ones, and all of the others are defined16in terms of one of these. The bases are described in the17documentation for the function18:func:`steenrod_algebra_basis`; also see the papers by19Monks [M] and Wood [W] for more information about them. For20commutator bases, see the preprint by Palmieri and Zhang [PZ].2122- 'milnor': Milnor basis.2324- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.2526Most of the rest of the bases are only defined when `p=2`. The only27exceptions are the `P^s_t`-bases and the commutator bases, which are28defined at all primes.2930- 'wood_y': Wood's Y basis.3132- 'wood_z': Wood's Z basis.3334- 'wall', 'wall_long': Wall's basis.3536- 'arnon_a', 'arnon_a_long': Arnon's A basis.3738- 'arnon_c': Arnon's C basis.3940- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz':41various `P^s_t`-bases.4243- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz',44or these with '_long' appended: various commutator bases.4546The main functions provided here are4748- :func:`steenrod_algebra_basis`. This computes a tuple representing49basis elements for the Steenrod algebra in a given degree, at a50given prime, with respect to a given basis. It is a cached function.5152- :func:`convert_to_milnor_matrix`. This returns the change-of-basis53matrix, in a given degree, from any basis to the Milnor basis. It is54a cached function.5556- :func:`convert_from_milnor_matrix`. This returns the inverse of the57previous matrix.5859INTERNAL DOCUMENTATION:6061If you want to implement a new basis for the Steenrod algebra:6263In the file :file:`steenrod_algebra.py`:6465For the class :class:`SteenrodAlgebra_generic66<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic>`, add functionality to the67methods:6869- :meth:`_repr_term <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic._repr_term>`7071- :meth:`degree_on_basis <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.degree_on_basis>`7273- :meth:`_milnor_on_basis <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic._milnor_on_basis>`7475- :meth:`an_element <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.an_element>`7677In the file :file:`steenrod_algebra_misc.py`:7879- add functionality to :func:`get_basis_name80<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`: this81should accept as input various synonyms for the basis, and its82output should be a canonical name for the basis.8384- add a function ``BASIS_mono_to_string`` like85:func:`milnor_mono_to_string86<sage.algebras.steenrod.steenrod_algebra_misc.milnor_mono_to_string>`87or one of the other similar functions.8889In this file :file:`steenrod_algebra_bases.py`:9091- add appropriate lines to :func:`steenrod_algebra_basis`.9293- add a function to compute the basis in a given dimension (to be94called by :func:`steenrod_algebra_basis`).9596- modify :func:`steenrod_basis_error_check` so it checks the new97basis.9899If the basis has an intrinsic way of defining a product, implement it100in the file :file:`steenrod_algebra_mult.py` and also in the101:meth:`product_on_basis102<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.product_on_basis>`103method for :class:`SteenrodAlgebra_generic104<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic>` in105:file:`steenrod_algebra.py`.106107REFERENCES:108109- [M] K. G. Monks, "Change of basis, monomial relations, and110`P^s_t` bases for the Steenrod algebra," J. Pure Appl.111Algebra 125 (1998), no. 1-3, 235-260.112113- [PZ] J. H. Palmieri and J. J. Zhang, "Commutators in the Steenrod114algebra," preprint (2008)115116- [W] R. M. W. Wood, "Problems in the Steenrod algebra," Bull. London117Math. Soc. 30 (1998), no. 5, 449-517.118"""119120#*****************************************************************************121# Copyright (C) 2008-2010 John H. Palmieri <[email protected]>122# Distributed under the terms of the GNU General Public License (GPL)123#*****************************************************************************124125from sage.misc.cachefunc import cached_function126127@cached_function128def convert_to_milnor_matrix(n, basis, p=2):129r"""130Change-of-basis matrix, 'basis' to Milnor, in dimension131`n`, at the prime `p`.132133INPUT:134135- ``n`` - non-negative integer, the dimension136- ``basis`` - string, the basis from which to convert137- ``p`` - positive prime number (optional, default 2)138139OUTPUT:140141``matrix`` - change-of-basis matrix, a square matrix over ``GF(p)``142143.. note::144145This is called internally. It is not intended for casual146users, so no error checking is made on the integer `n`, the147basis name, or the prime. Also, users should call148:func:`convert_to_milnor_matrix` instead of this function149(which has a trailing underscore in its name), because the150former is the cached version of the latter.151152EXAMPLES::153154sage: from sage.algebras.steenrod.steenrod_algebra_bases import convert_to_milnor_matrix155sage: convert_to_milnor_matrix(5, 'adem') # indirect doctest156[0 1]157[1 1]158sage: convert_to_milnor_matrix(45, 'milnor')159111 x 111 dense matrix over Finite Field of size 2160sage: convert_to_milnor_matrix(12,'wall')161[1 0 0 1 0 0 0]162[1 1 0 0 0 1 0]163[0 1 0 1 0 0 0]164[0 0 0 1 0 0 0]165[1 1 0 0 1 0 0]166[0 0 1 1 1 0 1]167[0 0 0 0 1 0 1]168169The function takes an optional argument, the prime `p` over170which to work::171172sage: convert_to_milnor_matrix(17,'adem',3)173[0 0 1 1]174[0 0 0 1]175[1 1 1 1]176[0 1 0 1]177sage: convert_to_milnor_matrix(48,'adem',5)178[0 1]179[1 1]180sage: convert_to_milnor_matrix(36,'adem',3)181[0 0 1]182[0 1 0]183[1 2 0]184"""185from sage.matrix.constructor import matrix186from sage.rings.all import GF187from steenrod_algebra import SteenrodAlgebra188if n == 0:189return matrix(GF(p), 1, 1, [[1]])190milnor_base = steenrod_algebra_basis(n,'milnor',p)191rows = []192A = SteenrodAlgebra(basis=basis, p=p)193for poly in A.basis(n):194d = poly.milnor().monomial_coefficients()195for v in milnor_base:196entry = d.get(v, 0)197rows = rows + [entry]198d = len(milnor_base)199return matrix(GF(p),d,d,rows)200201def convert_from_milnor_matrix(n, basis, p=2):202r"""203Change-of-basis matrix, Milnor to 'basis', in dimension204`n`.205206INPUT:207208- ``n`` - non-negative integer, the dimension209210- ``basis`` - string, the basis to which to convert211212- ``p`` - positive prime number (optional, default 2)213214OUTPUT: ``matrix`` - change-of-basis matrix, a square matrix over215GF(p)216217.. note::218219This is called internally. It is not intended for casual220users, so no error checking is made on the integer `n`, the221basis name, or the prime.222223EXAMPLES::224225sage: from sage.algebras.steenrod.steenrod_algebra_bases import convert_from_milnor_matrix, convert_to_milnor_matrix226sage: convert_from_milnor_matrix(12,'wall')227[1 0 0 1 0 0 0]228[0 0 1 1 0 0 0]229[0 0 0 1 0 1 1]230[0 0 0 1 0 0 0]231[1 0 1 0 1 0 0]232[1 1 1 0 0 0 0]233[1 0 1 0 1 0 1]234sage: convert_from_milnor_matrix(38,'serre_cartan')23572 x 72 dense matrix over Finite Field of size 2236sage: x = convert_to_milnor_matrix(20,'wood_y')237sage: y = convert_from_milnor_matrix(20,'wood_y')238sage: x*y239[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]240[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]241[0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]242[0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0]243[0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]244[0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]245[0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0]246[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]247[0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0]248[0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]249[0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0]250[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]251[0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]252[0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]253[0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0]254[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0]255[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]256257The function takes an optional argument, the prime `p` over258which to work::259260sage: convert_from_milnor_matrix(17,'adem',3)261[2 1 1 2]262[0 2 0 1]263[1 2 0 0]264[0 1 0 0]265"""266mat = convert_to_milnor_matrix(n,basis,p)267if mat.nrows() != 0:268return convert_to_milnor_matrix(n,basis,p).inverse()269else:270return mat271272@cached_function273def steenrod_algebra_basis(n, basis='milnor', p=2, **kwds):274r"""275Basis for the Steenrod algebra in degree `n`.276277INPUT:278279- ``n`` - non-negative integer280- ``basis`` - string, which basis to use (optional, default = 'milnor')281- ``p`` - positive prime number (optional, default = 2)282- ``profile`` - profile function (optional, default None). This283is just passed on to the functions :func:`milnor_basis` and284:func:`pst_basis`.285- ``truncation_type`` - truncation type, either 0 or Infinity286(optional, default Infinity if no profile function is specified,2870 otherwise). This is just passed on to the function288:func:`milnor_basis`.289290OUTPUT:291292Tuple of objects representing basis elements for the Steenrod algebra293in dimension n.294295.. note::296297Users should use :func:`steenrod_algebra_basis` instead of298this function (which has a trailing underscore in its name):299:func:`steenrod_algebra_basis` is the cached version of this300one, and so will be faster.301302The choices for the string ``basis`` are as follows; see the303documentation for :mod:`sage.algebras.steenrod.steenrod_algebra`304for details on each basis:305306- 'milnor': Milnor basis.307- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.308- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz':309various `P^s_t`-bases.310- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz', or311any of these with '_long' appended: various commutator bases.312313The rest of these bases are only defined when `p=2`.314315- 'wood_y': Wood's Y basis.316- 'wood_z': Wood's Z basis.317- 'wall' or 'wall_long': Wall's basis.318- 'arnon_a' or 'arnon_a_long': Arnon's A basis.319- 'arnon_c': Arnon's C basis.320321EXAMPLES::322323sage: from sage.algebras.steenrod.steenrod_algebra_bases import steenrod_algebra_basis324sage: steenrod_algebra_basis(7,'milnor') # indirect doctest325((0, 0, 1), (1, 2), (4, 1), (7,))326sage: steenrod_algebra_basis(5) # milnor basis is the default327((2, 1), (5,))328329Bases in negative dimensions are empty::330331sage: steenrod_algebra_basis(-2, 'wall')332()333334The third (optional) argument to 'steenrod_algebra_basis' is the335prime p::336337sage: steenrod_algebra_basis(9, 'milnor', p=3)338(((1,), (1,)), ((0,), (2,)))339sage: steenrod_algebra_basis(9, 'milnor', 3)340(((1,), (1,)), ((0,), (2,)))341sage: steenrod_algebra_basis(17, 'milnor', 3)342(((2,), ()), ((1,), (3,)), ((0,), (0, 1)), ((0,), (4,)))343344Other bases::345346sage: steenrod_algebra_basis(7,'admissible')347((7,), (6, 1), (4, 2, 1), (5, 2))348sage: steenrod_algebra_basis(13,'admissible',p=3)349((1, 3, 0), (0, 3, 1))350sage: steenrod_algebra_basis(5,'wall')351(((2, 2), (0, 0)), ((1, 1), (1, 0)))352sage: steenrod_algebra_basis(5,'wall_long')353(((2, 2), (0, 0)), ((1, 1), (1, 0)))354sage: steenrod_algebra_basis(5,'pst-rlex')355(((0, 1), (2, 1)), ((1, 1), (0, 2)))356"""357from steenrod_algebra_misc import get_basis_name358try:359if n < 0 or int(n) != n:360return ()361except TypeError:362return ()363364basis_name = get_basis_name(basis, p)365if basis_name.find('long') >= 0:366long = True367basis_name = basis_name.rsplit('_', 1)[0]368else:369long = False370371profile = kwds.get("profile", None)372if (profile is not None and profile != () and profile != ((), ())373and basis != 'milnor' and basis.find('pst') == -1):374raise ValueError("Profile functions may only be used with the Milnor or pst bases")375376## Milnor basis377if basis_name == 'milnor':378return milnor_basis(n,p, **kwds)379## Serre-Cartan basis380elif basis_name == 'serre-cartan':381return serre_cartan_basis(n,p)382## Atomic bases, p odd:383elif p > 2 and (basis_name.find('pst') >= 0384or basis_name.find('comm') >= 0):385return atomic_basis_odd(n, basis_name, p, **kwds)386## Atomic bases, p=2387elif p == 2 and (basis_name == 'woody' or basis_name == 'woodz'388or basis_name == 'wall' or basis_name == 'arnona'389or basis_name.find('pst') >= 0390or basis_name.find('comm') >= 0):391return atomic_basis(n, basis_name, **kwds)392## Arnon 'C' basis393elif p == 2 and basis == 'arnonc':394return arnonC_basis(n)395else:396raise ValueError("Unknown basis: %s at the prime %s" % (basis, p))397398# helper functions for producing bases399400def restricted_partitions(n, l, no_repeats=False):401"""402List of 'restricted' partitions of n: partitions with parts taken403from list.404405INPUT:406407- ``n`` - non-negative integer408- ``l`` - list of positive integers409- ``no_repeats`` - boolean (optional, default = False), if True,410only return partitions with no repeated parts411412OUTPUT: list of lists413414One could also use ``Partitions(n, parts_in=l)``, but this415function may be faster. Also, while ``Partitions(n, parts_in=l,416max_slope=-1)`` should in theory return the partitions of `n` with417parts in ``l`` with no repetitions, the ``max_slope=-1`` argument418is ignored, so it doesn't work. (At the moment, the419``no_repeats=True`` case is the only one used in the code.)420421EXAMPLES::422423sage: from sage.algebras.steenrod.steenrod_algebra_bases import restricted_partitions424sage: restricted_partitions(10, [7,5,1])425[[7, 1, 1, 1], [5, 5], [5, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]426sage: restricted_partitions(10, [6,5,4,3,2,1], no_repeats=True)427[[6, 4], [6, 3, 1], [5, 4, 1], [5, 3, 2], [4, 3, 2, 1]]428sage: restricted_partitions(10, [6,4,2])429[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]430sage: restricted_partitions(10, [6,4,2], no_repeats=True)431[[6, 4]]432433'l' may have repeated elements. If 'no_repeats' is False, this434has no effect. If 'no_repeats' is True, and if the repeated435elements appear consecutively in 'l', then each element may be436used only as many times as it appears in 'l'::437438sage: restricted_partitions(10, [6,4,2,2], no_repeats=True)439[[6, 4], [6, 2, 2]]440sage: restricted_partitions(10, [6,4,2,2,2], no_repeats=True)441[[6, 4], [6, 2, 2], [4, 2, 2, 2]]442443(If the repeated elements don't appear consecutively, the results444are likely meaningless, containing several partitions more than445once, for example.)446447In the following examples, 'no_repeats' is False::448449sage: restricted_partitions(10, [6,4,2])450[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]451sage: restricted_partitions(10, [6,4,2,2,2])452[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]453sage: restricted_partitions(10, [6,4,4,4,2,2,2,2,2,2])454[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]455"""456if n < 0:457return []458elif n == 0:459return [[]]460else:461results = []462if no_repeats:463index = 1464else:465index = 0466old_i = 0467for i in l:468if old_i != i:469for sigma in restricted_partitions(n-i, l[index:], no_repeats):470results.append([i] + sigma)471index += 1472old_i = i473return results474475def xi_degrees(n,p=2, reverse=True):476r"""477Decreasing list of degrees of the xi_i's, starting in degree n.478479INPUT:480481- `n` - integer482- `p` - prime number, optional (default 2)483- ``reverse`` - bool, optional (default True)484485OUTPUT: ``list`` - list of integers486487When `p=2`: decreasing list of the degrees of the `\xi_i`'s with488degree at most n.489490At odd primes: decreasing list of these degrees, each divided by491`2(p-1)`.492493If ``reverse`` is False, then return an increasing list rather494than a decreasing one.495496EXAMPLES::497498sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17)499[15, 7, 3, 1]500sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17, reverse=False)501[1, 3, 7, 15]502sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17,p=3)503[13, 4, 1]504sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(400,p=17)505[307, 18, 1]506"""507from sage.rings.all import Integer508if n <= 0: return []509N = Integer(n*(p-1) + 1)510l = [int((p**d-1)/(p-1)) for d in range(1,N.exact_log(p)+1)]511if not reverse:512return l513l.reverse()514return l515516########################################################517# Functions for defining bases.518519# These should each return a tuple of tuples of the appropriate form520# for the basis. For example, at the prime 2, the Milnor basis521# element Sq(a,b,c,...) corresponds to the tuple (a, b, c, ...), while522# at odd primes, the element Q_i Q_j ... P(a, b, ...) corresponds to523# the pair ((i, j, ...), (a, b, ...)). See each function for more524# information.525526def milnor_basis(n, p=2, **kwds):527r"""528Milnor basis in dimension `n` with profile function ``profile``.529530INPUT:531532- ``n`` - non-negative integer533534- ``p`` - positive prime number (optional, default 2)535536- ``profile`` - profile function (optional, default None).537Together with ``truncation_type``, specify the profile function538to be used; None means the profile function for the entire539Steenrod algebra. See540:mod:`sage.algebras.steenrod.steenrod_algebra` and541:func:`SteenrodAlgebra <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra>`542for information on profile functions.543544- ``truncation_type`` - truncation type, either 0 or Infinity545(optional, default Infinity if no profile function is specified,5460 otherwise)547548OUTPUT: tuple of mod p Milnor basis elements in dimension n549550At the prime 2, the Milnor basis consists of symbols of the form551`\text{Sq}(m_1, m_2, ..., m_t)`, where each552`m_i` is a non-negative integer and if `t>1`, then553`m_t \neq 0`. At odd primes, it consists of symbols of the554form `Q_{e_1} Q_{e_2} ... P(m_1, m_2, ..., m_t)`,555where `0 \leq e_1 < e_2 < ...`, each `m_i` is a556non-negative integer, and if `t>1`, then557`m_t \neq 0`.558559EXAMPLES::560561sage: from sage.algebras.steenrod.steenrod_algebra_bases import milnor_basis562sage: milnor_basis(7)563((0, 0, 1), (1, 2), (4, 1), (7,))564sage: milnor_basis(7, 2)565((0, 0, 1), (1, 2), (4, 1), (7,))566sage: milnor_basis(4, 2)567((1, 1), (4,))568sage: milnor_basis(4, 2, profile=[2,1])569((1, 1),)570sage: milnor_basis(4, 2, profile=(), truncation_type=0)571()572sage: milnor_basis(4, 2, profile=(), truncation_type=Infinity)573((1, 1), (4,))574sage: milnor_basis(9, 3)575(((1,), (1,)), ((0,), (2,)))576sage: milnor_basis(17, 3)577(((2,), ()), ((1,), (3,)), ((0,), (0, 1)), ((0,), (4,)))578sage: milnor_basis(48, p=5)579(((), (0, 1)), ((), (6,)))580sage: len(milnor_basis(100,3))58113582sage: len(milnor_basis(200,7))5830584sage: len(milnor_basis(240,7))5853586sage: len(milnor_basis(240,7, profile=((),()), truncation_type=Infinity))5873588sage: len(milnor_basis(240,7, profile=((),()), truncation_type=0))5890590"""591if n == 0:592if p == 2:593return ((),)594else:595return (((), ()),)596597from sage.rings.infinity import Infinity598from sage.combinat.integer_vector_weighted import WeightedIntegerVectors599profile = kwds.get("profile", None)600trunc = kwds.get("truncation_type", None)601if trunc is None:602if profile is not None:603trunc = 0604else:605trunc = Infinity606607result = []608if p == 2:609for mono in WeightedIntegerVectors(n, xi_degrees(n, reverse=False)):610exponents = list(mono)611while len(exponents) > 0 and exponents[-1] == 0:612exponents.pop(-1)613# check profile:614okay = True615if profile is not None and len(profile) > 0:616for i in range(len(exponents)):617if ((len(profile) > i and exponents[i] >= 2**profile[i])618or (len(profile) <= i and trunc < Infinity619and exponents[i] >= 2**trunc)):620okay = False621break622else:623# profile is empty624okay = (trunc == Infinity)625if okay:626result.append(tuple(exponents))627else: # p odd628# first find the P part of each basis element.629# in this part of the code (the P part), all dimensions are630# divided by 2(p-1).631for dim in range(n/(2*(p-1)) + 1):632if dim == 0:633P_result = [[0]]634else:635P_result = []636for mono in WeightedIntegerVectors(dim, xi_degrees(dim, p=p, reverse=False)):637p_mono = list(mono)638while len(p_mono) > 0 and p_mono[-1] == 0:639p_mono.pop(-1)640if len(p_mono) > 0:641P_result.append(p_mono)642# now find the Q part of the basis element.643# dimensions here are back to normal.644for p_mono in P_result:645deg = n - 2*dim*(p-1)646q_degrees = [1+2*(p-1)*d for d in647xi_degrees(int((deg - 1)/(2*(p-1))), p)] + [1]648q_degrees_decrease = q_degrees649q_degrees.reverse()650if deg % (2*(p-1)) <= len(q_degrees):651# if this inequality fails, no way to have a partition652# with distinct parts.653for sigma in restricted_partitions(deg,654q_degrees_decrease,655no_repeats = True):656index = 0657q_mono = []658for q in q_degrees:659if q in sigma:660q_mono.append(index)661index += 1662# check profile:663okay = True664if profile is not None and (len(profile[0]) > 0665or len(profile[1]) > 0):666# check profile function for q_mono667for i in q_mono:668if ((len(profile[1]) > i and profile[1][i] == 1)669or (len(profile[1]) <= i and trunc == 0)):670okay = False671break672# check profile function for p_mono673for i in range(len(p_mono)):674if okay and ((len(profile[0]) > i and p_mono[i] >= p**profile[0][i])675or (len(profile[0]) <= i and trunc < Infinity676and p_mono[i] >= p**trunc)):677okay = False678break679else:680# profile is empty681okay = (trunc == Infinity)682if okay:683if list(p_mono) == [0]:684p_mono = []685result.append((tuple(q_mono), tuple(p_mono)))686return tuple(result)687688def serre_cartan_basis(n, p=2, bound=1):689r"""690Serre-Cartan basis in dimension `n`.691692INPUT:693694- ``n`` - non-negative integer695- ``bound`` - positive integer (optional)696- ``prime`` - positive prime number (optional, default 2)697698OUTPUT: tuple of mod p Serre-Cartan basis elements in dimension n699700The Serre-Cartan basis consists of 'admissible monomials in the701Steenrod squares'. Thus at the prime 2, it consists of monomials702`\text{Sq}^{m_1} \text{Sq}^{m_2} ... \text{Sq}^{m_t}` with `m_i703\geq 2m_{i+1}` for each `i`. At odd primes, it consists of704monomials `\beta^{e_0} P^{s_1} \beta^{e_1} P^{s_2} ... P^{s_k}705\beta^{e_k}` with each `e_i` either 0 or 1, `s_i \geq p s_{i+1} +706e_i` for all `i`, and `s_k \geq 1`.707708EXAMPLES::709710sage: from sage.algebras.steenrod.steenrod_algebra_bases import serre_cartan_basis711sage: serre_cartan_basis(7)712((7,), (6, 1), (4, 2, 1), (5, 2))713sage: serre_cartan_basis(13,3)714((1, 3, 0), (0, 3, 1))715sage: serre_cartan_basis(50,5)716((1, 5, 0, 1, 1), (1, 6, 1))717718If optional argument ``bound`` is present, include only those monomials719whose last term is at least ``bound`` (when p=2), or those for which720`s_k - e_k \geq bound` (when p is odd). ::721722sage: serre_cartan_basis(7, bound=2)723((7,), (5, 2))724sage: serre_cartan_basis(13, 3, bound=3)725((1, 3, 0),)726"""727if n == 0:728return ((),)729else:730if p == 2:731# Build basis recursively. last = last term.732# last is >= bound, and we will append (last,) to the end of733# elements from serre_cartan_basis (n - last, bound=2 * last).734# This means that 2 last <= n - last, or 3 last <= n.735result = [(n,)]736for last in range(bound, 1+n/3):737for vec in serre_cartan_basis(n - last, bound = 2*last):738new = vec + (last,)739result.append(new)740else: # p odd741if n % (2 * (p-1)) == 0 and n/(2 * (p-1)) >= bound:742result = [(0, int(n/(2 * (p-1))), 0)]743elif n == 1:744result = [(1,)]745else:746result = []747# 2 cases: append P^{last}, or append P^{last} beta748# case 1: append P^{last}749for last in range(bound, 1+n/(2*(p - 1))):750if n - 2*(p-1)*last > 0:751for vec in serre_cartan_basis(n - 2*(p-1)*last,752p, p*last):753result.append(vec + (last,0))754# case 2: append P^{last} beta755if bound == 1:756bound = 0757for last in range(bound+1, 1+n/(2*(p - 1))):758basis = serre_cartan_basis(n - 2*(p-1)*last - 1,759p, p*last)760for vec in basis:761if vec == ():762vec = (0,)763new = vec + (last, 1)764result.append(new)765return tuple(result)766767def atomic_basis(n, basis, **kwds):768r"""769Basis for dimension `n` made of elements in 'atomic' degrees:770degrees of the form `2^i (2^j - 1)`.771772This works at the prime 2 only.773774INPUT:775776- ``n`` - non-negative integer777- ``basis`` - string, the name of the basis778779- ``profile`` - profile function (optional, default None).780Together with ``truncation_type``, specify the profile function781to be used; None means the profile function for the entire782Steenrod algebra. See783:mod:`sage.algebras.steenrod.steenrod_algebra` and784:func:`SteenrodAlgebra` for information on profile functions.785786- ``truncation_type`` - truncation type, either 0 or Infinity787(optional, default Infinity if no profile function is specified,7880 otherwise).789790OUTPUT: tuple of basis elements in dimension n791792The atomic bases include Wood's Y and Z bases, Wall's basis,793Arnon's A basis, the `P^s_t`-bases, and the commutator794bases. (All of these bases are constructed similarly, hence their795constructions have been consolidated into a single function. Also,796see the documentation for 'steenrod_algebra_basis' for797descriptions of them.) For `P^s_t`-bases, you may also specify a798profile function and truncation type; profile functions are ignored799for the other bases.800801EXAMPLES::802803sage: from sage.algebras.steenrod.steenrod_algebra_bases import atomic_basis804sage: atomic_basis(6,'woody')805(((1, 0), (0, 1), (0, 0)), ((2, 0), (1, 0)), ((1, 1),))806sage: atomic_basis(8,'woodz')807(((2, 0), (0, 1), (0, 0)), ((0, 2), (0, 0)), ((1, 1), (1, 0)), ((3, 0),))808sage: atomic_basis(6,'woodz') == atomic_basis(6, 'woody')809True810sage: atomic_basis(9,'woodz') == atomic_basis(9, 'woody')811False812813Wall's basis::814815sage: atomic_basis(8,'wall')816(((2, 2), (1, 0), (0, 0)), ((2, 0), (0, 0)), ((2, 1), (1, 1)), ((3, 3),))817818Arnon's A basis::819820sage: atomic_basis(7,'arnona')821(((0, 0), (1, 1), (2, 2)), ((0, 0), (2, 1)), ((1, 0), (2, 2)), ((2, 0),))822823`P^s_t`-bases::824825sage: atomic_basis(7,'pst_rlex')826(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((2, 1), (0, 2)), ((0, 3),))827sage: atomic_basis(7,'pst_llex')828(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))829sage: atomic_basis(7,'pst_deg')830(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))831sage: atomic_basis(7,'pst_revz')832(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))833834Commutator bases::835836sage: atomic_basis(7,'comm_rlex')837(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((2, 1), (0, 2)), ((0, 3),))838sage: atomic_basis(7,'comm_llex')839(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))840sage: atomic_basis(7,'comm_deg')841(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))842sage: atomic_basis(7,'comm_revz')843(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))844"""845def degree_dictionary(n, basis):846"""847Dictionary of atomic degrees for basis up to degree n.848849The keys for the dictionary are the atomic degrees - the numbers of850the form 2^i (2^j - 1) - which are less than or equal to n. The value851associated to such a degree depends on basis; it has the form852(s,t), where (s,t) is a pair of integers which indexes the853corresponding element.854"""855dict = {}856if basis.find('wood') >= 0:857k=0858m=0859deg = 2**m * (2**(k+1) - 1)860while deg <= n:861dict[deg] = (m,k)862if m>0:863m = m - 1864k = k + 1865else:866m = k + 1867k = 0868deg = 2**m * (2**(k+1) - 1)869elif basis.find('wall') >= 0 or basis.find('arnon') >= 0:870k=0871m=0872deg = 2**k * (2**(m-k+1) - 1)873while deg <= n:874dict[deg] = (m,k)875if k == 0:876m = m + 1877k = m878else:879k = k - 1880deg = 2**k * (2**(m-k+1) - 1)881elif basis.find('pst') >= 0 or basis.find('comm') >= 0:882s=0883t=1884deg = 2**s * (2**t - 1)885while deg <= n:886if basis.find('pst') >= 0:887dict[deg] = (s,t)888else: # comm889dict[deg] = (s,t)890if s == 0:891s = t892t = 1893else:894s = s - 1895t = t + 1896deg = 2**s * (2**t - 1)897return dict898899def sorting_pair(s,t,basis): # pair used for sorting the basis900if basis.find('wood') >= 0 and basis.find('z') >= 0:901return (-s-t,-s)902elif basis.find('wood') >= 0 or basis.find('wall') >= 0 or \903basis.find('arnon') >= 0:904return (-s,-t)905elif basis.find('rlex') >= 0:906return (t,s)907elif basis.find('llex') >= 0:908return (s,t)909elif basis.find('deg') >= 0:910return (s+t,t)911elif basis.find('revz') >= 0:912return (s+t,s)913914from sage.misc.misc import prod915from sage.rings.infinity import Infinity916profile = kwds.get("profile", None)917trunc = kwds.get("truncation_type", None)918if profile is not None and trunc is None:919trunc = 0920921if n == 0:922return ((),)923else:924result = []925degrees_etc = degree_dictionary(n, basis)926degrees = degrees_etc.keys()927for sigma in restricted_partitions(n, degrees, no_repeats=True):928big_list = [degrees_etc[part] for part in sigma]929big_list.sort(cmp = lambda x, y: cmp(sorting_pair(x[0], x[1], basis),930sorting_pair(y[0], y[1], basis)))931# reverse = True)932# arnon: sort like wall, then reverse end result933if basis.find('arnon') >= 0:934big_list.reverse()935936# check profile:937okay = True938if basis.find('pst') >= 0:939if profile is not None and len(profile) > 0:940for (s,t) in big_list:941if ((len(profile) > t-1 and profile[t-1] <= s)942or (len(profile) <= t-1 and trunc < Infinity)):943okay = False944break945if okay:946result.append(tuple(big_list))947return tuple(result)948949def arnonC_basis(n,bound=1):950r"""951Arnon's C basis in dimension `n`.952953INPUT:954955- ``n`` - non-negative integer956957- ``bound`` - positive integer (optional)958959OUTPUT: tuple of basis elements in dimension n960961The elements of Arnon's C basis are monomials of the form962`\text{Sq}^{t_1} ... \text{Sq}^{t_m}` where for each963`i`, we have `t_i \leq 2t_{i+1}` and964`2^i | t_{m-i}`.965966EXAMPLES::967968sage: from sage.algebras.steenrod.steenrod_algebra_bases import arnonC_basis969sage: arnonC_basis(7)970((7,), (2, 5), (4, 3), (4, 2, 1))971972If optional argument ``bound`` is present, include only those monomials973whose first term is at least as large as ``bound``::974975sage: arnonC_basis(7,3)976((7,), (4, 3), (4, 2, 1))977"""978if n == 0:979return ((),)980else:981# Build basis recursively. first = first term.982# first is >= bound, and we will prepend (first,) to the983# elements from arnonC_basis (n - first, first / 2).984# first also must be divisible by 2**(len(old-basis-elt))985# This means that 3 first <= 2 n.986result = [(n,)]987for first in range(bound,1+2*n/3):988for vec in arnonC_basis(n - first, max(first/2,1)):989if first % 2**len(vec) == 0:990result.append((first,) + vec)991return tuple(result)992993def atomic_basis_odd(n, basis, p, **kwds):994r"""995`P^s_t`-bases and commutator basis in dimension `n` at odd primes.996997This function is called ``atomic_basis_odd`` in analogy with998:func:`atomic_basis`.9991000INPUT:10011002- ``n`` - non-negative integer1003- ``basis`` - string, the name of the basis1004- ``p`` - positive prime number10051006- ``profile`` - profile function (optional, default None).1007Together with ``truncation_type``, specify the profile function1008to be used; None means the profile function for the entire1009Steenrod algebra. See1010:mod:`sage.algebras.steenrod.steenrod_algebra` and1011:func:`SteenrodAlgebra` for information on profile functions.10121013- ``truncation_type`` - truncation type, either 0 or Infinity1014(optional, default Infinity if no profile function is specified,10150 otherwise).10161017OUTPUT: tuple of basis elements in dimension n10181019The only possible difference in the implementations for `P^s_t`1020bases and commutator bases is that the former make sense, and1021require filtering, if there is a nontrivial profile function.1022This function is called by :func:`steenrod_algebra_basis`, and it1023will not be called for commutator bases if there is a profile1024function, so we treat the two bases exactly the same.10251026EXAMPLES::10271028sage: from sage.algebras.steenrod.steenrod_algebra_bases import atomic_basis_odd1029sage: atomic_basis_odd(8, 'pst_rlex', 3)1030(((), (((0, 1), 2),)),)10311032sage: atomic_basis_odd(18, 'pst_rlex', 3)1033(((0, 2), ()), ((0, 1), (((1, 1), 1),)))1034sage: atomic_basis_odd(18, 'pst_rlex', 3, profile=((), (2,2,2)))1035(((0, 2), ()),)1036"""1037def sorting_pair(s,t,basis): # pair used for sorting the basis1038if basis.find('rlex') >= 0:1039return (t,s)1040elif basis.find('llex') >= 0:1041return (s,t)1042elif basis.find('deg') >= 0:1043return (s+t,t)1044elif basis.find('revz') >= 0:1045return (s+t,s)10461047if n == 0:1048if p == 2:1049return ((),)1050else:1051return (((), ()),)1052from sage.misc.misc import prod1053from sage.rings.all import Integer1054from sage.rings.infinity import Infinity1055from sage.combinat.integer_vector_weighted import WeightedIntegerVectors1056profile = kwds.get("profile", None)1057trunc = kwds.get("truncation_type", 0)10581059result = []1060for dim in range(n/(2*p-2) + 1):1061P_result = []1062for v in WeightedIntegerVectors(dim, xi_degrees(dim, p=p, reverse=False)):1063mono = []1064for t, a in enumerate(v):1065for s, pow in enumerate(Integer(a).digits(p)):1066if pow > 0:1067mono.append(((s, t+1), pow))1068P_result.append(mono)1069for p_mono in P_result:1070p_mono.sort(cmp = lambda x, y: cmp(sorting_pair(x[0][0], x[0][1], basis),1071sorting_pair(y[0][0], y[0][1], basis)))1072deg = n - 2*dim*(p-1)1073q_degrees = [1+2*(p-1)*d for d in1074xi_degrees(int((deg - 1)/(2*(p-1))), p)] + [1]1075q_degrees_decrease = q_degrees1076q_degrees.reverse()1077if deg % (2*(p-1)) <= len(q_degrees):1078# if this inequality fails, no way to have a partition1079# with distinct parts.1080for sigma in restricted_partitions(deg,1081q_degrees_decrease,1082no_repeats = True):1083index = 01084q_mono = []1085for q in q_degrees:1086if q in sigma:1087q_mono.append(index)1088index += 11089# check profile:1090okay = True1091if profile is not None and profile != ((), ()):1092# check profile function for q_mono1093for i in q_mono:1094if ((len(profile[1]) > i and profile[1][i] == 1)1095or (len(profile[1]) <= i and trunc == 0)):1096okay = False1097break10981099for ((s,t), exp) in p_mono:1100if ((len(profile[0]) > t-1 and profile[0][t-1] <= s)1101or (len(profile[0]) <= t-1 and trunc < Infinity)):1102okay = False1103break11041105if okay:1106if list(p_mono) == [0]:1107p_mono = []1108result.append((tuple(q_mono), tuple(p_mono)))1109return tuple(result)11101111#############################################################################1112def steenrod_basis_error_check(dim, p):1113"""1114This performs crude error checking.11151116INPUT:11171118- ``dim`` - non-negative integer1119- ``p`` - positive prime number11201121OUTPUT: None11221123This checks to see if the different bases have the same length, and1124if the change-of-basis matrices are invertible. If something goes1125wrong, an error message is printed.11261127This function checks at the prime ``p`` as the dimension goes up1128from 0 to ``dim``.11291130If you set the Sage verbosity level to a positive integer (using1131``set_verbose(n)``), then some extra messages will be printed.11321133EXAMPLES::11341135sage: from sage.algebras.steenrod.steenrod_algebra_bases import steenrod_basis_error_check1136sage: steenrod_basis_error_check(15,2) # long time1137sage: steenrod_basis_error_check(40,3) # long time1138sage: steenrod_basis_error_check(80,5) # long time1139"""1140import sage.misc.misc as misc11411142# Apparently, in this test function, we don't want to benefit from caching.1143# Hence, the uncached version of steenrod_algebra_basis and of1144# convert_to-milnor_matrix are used.1145if p == 2:1146bases = ('adem','woody', 'woodz', 'wall', 'arnona', 'arnonc',1147'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz',1148'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz')1149else:1150bases = ('adem',1151'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz',1152'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz')11531154for i in range(dim):1155if i % 5 == 0:1156misc.verbose("up to dimension %s"%i)1157milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p))1158for B in bases:1159if milnor_dim != len(steenrod_algebra_basis.f(i,B,p)):1160print "problem with milnor/" + B + " in dimension ", i1161mat = convert_to_milnor_matrix.f(i,B,p)1162if mat.nrows() != 0 and not mat.is_invertible():1163print "%s invertibility problem in dim %s at p=%s" % (B, i, p)11641165misc.verbose("done checking, no profiles")11661167bases = ('pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz')1168if p == 2:1169profiles = [(4,3,2,1), (2,2,3,1,1), (0,0,0,2)]1170else:1171profiles = [((3,2,1), ()), ((), (2,1,2)), ((3,2,1), (2,2,2,2))]11721173for i in range(dim):1174if i % 5 == 0:1175misc.verbose("up to dimension %s"%i)1176for pro in profiles:1177milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p,profile=pro))1178for B in bases:1179if milnor_dim != len(steenrod_algebra_basis.f(i,B,p,profile=pro)):1180print "problem with milnor/%s in dimension %s with profile %s"%(B, i, pro)11811182misc.verbose("done checking with profiles")118311841185