Path: blob/master/src/sage/algebras/steenrod/steenrod_algebra.py
8822 views
r"""1The Steenrod algebra23AUTHORS:45- John H. Palmieri (2008-07-30): version 0.9: Initial implementation.6- John H. Palmieri (2010-06-30): version 1.0: Implemented sub-Hopf7algebras and profile functions; direct multiplication of admissible8sequences (rather than conversion to the Milnor basis); implemented9the Steenrod algebra using CombinatorialFreeModule; improved the10test suite.1112This module defines the mod `p` Steenrod algebra `\mathcal{A}_p`, some13of its properties, and ways to define elements of it.1415From a topological point of view, `\mathcal{A}_p` is the algebra of16stable cohomology operations on mod `p` cohomology; thus for any17topological space `X`, its mod `p` cohomology algebra18`H^*(X,\mathbf{F}_p)` is a module over `\mathcal{A}_p`.1920From an algebraic point of view, `\mathcal{A}_p` is an21`\mathbf{F}_p`-algebra; when `p=2`, it is generated by elements22`\text{Sq}^i` for `i\geq 0` (the *Steenrod squares*), and when `p` is23odd, it is generated by elements `\mathcal{P}^i` for `i \geq 0` (the24*Steenrod reduced pth powers*) along with an element `\beta` (the *mod25p Bockstein*). The Steenrod algebra is graded: `\text{Sq}^i` is in26degree `i` for each `i`, `\beta` is in degree 1, and `\mathcal{P}^i`27is in degree `2(p-1)i`.2829The unit element is `\text{Sq}^0` when `p=2` and30`\mathcal{P}^0` when `p` is odd. The generating31elements also satisfy the *Adem relations*. At the prime 2, these32have the form3334.. math::3536\text{Sq}^a \text{Sq}^b = \sum_{c=0}^{[a/2]} \binom{b-c-1}{a-2c} \text{Sq}^{a+b-c} \text{Sq}^c.3738At odd primes, they are a bit more complicated; see Steenrod and39Epstein [SE] or :mod:`sage.algebras.steenrod.steenrod_algebra_bases`40for full details. These relations lead to the existence of the41*Serre-Cartan* basis for `\mathcal{A}_p`.4243The mod `p` Steenrod algebra has the structure of a Hopf44algebra, and Milnor [Mil] has a beautiful description of the dual,45leading to a construction of the *Milnor basis* for46`\mathcal{A}_p`. In this module, elements in the Steenrod47algebra are represented, by default, using the Milnor basis.4849.. rubric:: Bases for the Steenrod algebra5051There are a handful of other bases studied in the literature; the52paper by Monks is a good reference. Here is a quick summary:5354- The *Milnor basis*. When `p=2`, the Milnor basis consists of symbols55of the form `\text{Sq}(m_1, m_2, ..., m_t)`, where each `m_i` is a56non-negative integer and if `t>1`, then the last entry `m_t > 0`.57When `p` is odd, the Milnor basis consists of symbols of the form58`Q_{e_1} Q_{e_2} ... \mathcal{P}(m_1, m_2, ..., m_t)`, where `0 \leq59e_1 < e_2 < ...`, each `m_i` is a non-negative integer, and if60`t>1`, then the last entry `m_t > 0`.6162When `p=2`, it can be convenient to use the notation63`\mathcal{P}(-)` to mean `\text{Sq}(-)`, so that there is consistent64notation for all primes.6566- The *Serre-Cartan basis*. This basis consists of 'admissible67monomials' in the Steenrod operations. Thus at the prime 2, it68consists of monomials `\text{Sq}^{m_1} \text{Sq}^{m_2}69... \text{Sq}^{m_t}` with `m_i \geq 2m_{i+1}` for each `i`. At odd70primes, this basis consists of monomials `\beta^{\epsilon_0}71\mathcal{P}^{s_1} \beta^{\epsilon_1} \mathcal{P}^{s_2} ...72\mathcal{P}^{s_k} \beta^{\epsilon_k}` with each `\epsilon_i` either730 or 1, `s_i \geq p s_{i+1} + \epsilon_i`, and `s_k \geq 1`.7475Most of the rest of the bases are only defined when `p=2`. The only76exceptions are the `P^s_t`-bases and the commutator bases, which are77defined at all primes.7879- *Wood's Y basis*. For pairs of non-negative integers `(m,k)`, let80`w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`. Wood's `Y` basis consists of81monomials `w(m_0,k_0) ... w(m_t, k_t)` with `(m_i,k_i) >82(m_{i+1},k_{i+1})`, in left lex order.8384- *Wood's Z basis*. For pairs of non-negative integers `(m,k)`, let85`w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`. Wood's `Z` basis consists of86monomials `w(m_0,k_0) ... w(m_t, k_t)` with `(m_i+k_i,m_i) >87(m_{i+1}+k_{i+1},m_{i+1})`, in left lex order.8889- *Wall's basis*. For any pair of integers `(m,k)` with `m \geq k \geq900`, let `Q^m_k = \text{Sq}^{2^k} \text{Sq}^{2^{k+1}}91... \text{Sq}^{2^m}`. The elements of Wall's basis are monomials92`Q^{m_0}_{k_0} ... Q^{m_t}_{k_t}` with `(m_i, k_i) > (m_{i+1},93k_{i+1})`, ordered left lexicographically.9495(Note that `Q^m_k` is the reverse of the element `X^m_k` used in96defining Arnon's A basis.)9798- *Arnon's A basis*. For any pair of integers `(m,k)` with `m \geq k99\geq 0`, let `X^m_k = \text{Sq}^{2^m} \text{Sq}^{2^{m-1}}100... \text{Sq}^{2^k}`. The elements of Arnon's A basis are monomials101`X^{m_0}_{k_0} ... X^{m_t}_{k_t}` with `(m_i, k_i) < (m_{i+1},102k_{i+1})`, ordered left lexicographically.103104(Note that `X^m_k` is the reverse of the element `Q^m_k` used in105defining Wall's basis.)106107- *Arnon's C basis*. The elements of Arnon's C basis are monomials of108the form `\text{Sq}^{t_1} ... \text{Sq}^{t_m}` where for each `i`,109we have `t_i \leq 2t_{i+1}` and `2^i | t_{m-i}`.110111- `P^s_t` *bases*. Let `p=2`. For integers `s \geq 0` and `t > 0`,112the element `P^s_t` is the Milnor basis element `\mathcal{P}(0, ...,1130, p^s, 0, ...)`, with the nonzero entry in position `t`. To obtain114a `P^s_t`-basis, for each set `\{P^{s_1}_{t_1}, ...,115P^{s_k}_{t_k}\}` of (distinct) `P^s_t`'s, one chooses an ordering116and forms the monomials117118.. math::119120(P^{s_1}_{t_1})^{i_1} ... (P^{s_k}_{t_k})^{i_k}121122for all exponents `i_j` with `0 < i_j < p`. When `p=2`, the set of123all such monomials then forms a basis, and when `p` is odd, if one124multiplies each such monomial on the left by products of the form125`Q_{e_1} Q_{e_2} ...` with `0 \leq e_1 < e_2 < ...`, one obtains a126basis.127128Thus one gets a basis by choosing an ordering on each set of129`P^s_t`'s. There are infinitely many orderings possible, and we130have implemented four of them:131132- 'rlex': right lexicographic ordering133134- 'llex': left lexicographic ordering135136- 'deg': ordered by degree, which is the same as left lexicographic137ordering on the pair `(s+t,t)`138139- 'revz': left lexicographic ordering on the pair `(s+t,s)`, which140is the reverse of the ordering used (on elements in the same141degrees as the `P^s_t`'s) in Wood's Z basis: 'revz' stands for142'reversed Z'. This is the default: 'pst' is the same as143'pst_revz'.144145- *Commutator bases*. Let `c_{i,1} = \mathcal{P}(p^i)`, let `c_{i,2}146= [c_{i+1,1}, c_{i,1}]`, and inductively define `c_{i,k} =147[c_{i+k-1,1}, c_{i,k-1}]`. Thus `c_{i,k}` is a `k`-fold iterated148commutator of the elements `\mathcal{P}(p^i)`, ...,149`\mathcal{P}(p^{i+k-1})`. Note that `\dim c_{i,k} = \dim P^i_k`.150151Commutator bases are obtained in much the same way as `P^s_t`-bases:152for each set `\{c_{s_1,t_1}, ..., c_{s_k,t_k}\}` of (distinct)153`c_{s,t}`'s, one chooses an ordering and forms the resulting154monomials155156.. math::157158c_{s_1, t_1}^{i_1} ... c_{s_k,t_k}^{i_k}159160for all exponents `i_j` with `0 < i_j < p`. When `p` is odd, one161also needs to left-multiply by products of the `Q_i`'s. As for162`P^s_t`-bases, every ordering on each set of iterated commutators163determines a basis, and the same four orderings have been defined164for these bases as for the `P^s_t` bases: 'rlex', 'llex', 'deg',165'revz'.166167.. rubric:: Sub-Hopf algebras of the Steenrod algebra168169The sub-Hopf algebras of the Steenrod algebra have been170classified. Milnor proved that at the prime 2, the dual of the171Steenrod algebra `A_*` is isomorphic to a polynomial algebra172173.. math::174175A_* \cong \GF{2} [\xi_1, \xi_2, \xi_3, ...].176177The Milnor basis is dual to the monomial basis. Furthermore, any sub-Hopf178algebra corresponds to a quotient of this of the form179180.. math::181182A_* /(\xi_1^{2^{e_1}}, \xi_2^{2^{e_2}}, \xi_3^{2^{e_3}}, ...).183184The list of exponents `(e_1, e_2, ...)` may be considered a function185`e` from the positive integers to the extended non-negative integers186(the non-negative integers and `\infty`); this is called the *profile187function* for the sub-Hopf algebra. The profile function must satisfy188the condition189190- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.191192At odd primes, the situation is similar: the dual is isomorphic to the193tensor product of a polynomial algebra and an exterior algebra,194195.. math::196197A_* = \GF{p} [\xi_1, \xi_2, \xi_3, ...] \otimes \Lambda (\tau_0, \tau_1, ...),198199and any sub-Hopf algebra corresponds to a quotient of this of the form200201.. math::202203A_* / (\xi_1^{p^{e_1}}, \xi_2^{p^{e_2}}, ...; \tau_0^{k_0}, \tau_1^{k_1}, ...).204205Here the profile function has two pieces, `e` as at the prime 2, and206`k`, which maps the non-negative integers to the set `\{1, 2\}`.207These must satisfy the following conditions:208209- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.210211- if `k(i+j) = 1`, then either `e(i) \leq j` or `k(j) = 1` for all `i212\geq 1`, `j \geq 0`.213214(See Adams-Margolis, for example, for these results on profile215functions.)216217This module allows one to construct the Steenrod algebra or any of its218sub-Hopf algebras, at any prime. When defining a sub-Hopf algebra,219you must work with the Milnor basis or a `P^s_t`-basis.220221.. rubric:: Elements of the Steenrod algebra222223Basic arithmetic, `p=2`. To construct an element of the mod 2 Steenrod224algebra, use the function ``Sq``::225226sage: a = Sq(1,2)227sage: b = Sq(4,1)228sage: z = a + b229sage: z230Sq(1,2) + Sq(4,1)231sage: Sq(4) * Sq(1,2)232Sq(1,1,1) + Sq(2,3) + Sq(5,2)233sage: z**2 # non-negative exponents work as they should234Sq(1,2,1) + Sq(4,1,1)235sage: z**02361237238Basic arithmetic, `p>2`. To construct an element of the mod `p`239Steenrod algebra when `p` is odd, you should first define a Steenrod240algebra, using the ``SteenrodAlgebra`` command::241242sage: A3 = SteenrodAlgebra(3)243244Having done this, the newly created algebra ``A3`` has methods ``Q``245and ``P`` which construct elements of ``A3``::246247sage: c = A3.Q(1,3,6); c248Q_1 Q_3 Q_6249sage: d = A3.P(2,0,1); d250P(2,0,1)251sage: c * d252Q_1 Q_3 Q_6 P(2,0,1)253sage: e = A3.P(3)254sage: d * e255P(5,0,1)256sage: e * d257P(1,1,1) + P(5,0,1)258sage: c * c2590260sage: e ** 32612 P(1,2)262263Note that one can construct an element like ``c`` above in one step,264without first constructing the algebra::265266sage: c = SteenrodAlgebra(3).Q(1,3,6)267sage: c268Q_1 Q_3 Q_6269270And of course, you can do similar constructions with the mod 2271Steenrod algebra::272273sage: A = SteenrodAlgebra(2); A274mod 2 Steenrod algebra, milnor basis275sage: A.Sq(2,3,5)276Sq(2,3,5)277sage: A.P(2,3,5) # when p=2, P = Sq278Sq(2,3,5)279sage: A.Q(1,4) # when p=2, this gives a product of Milnor primitives280Sq(0,1,0,0,1)281282Associated to each element is its prime (the characteristic of the283underlying base field) and its basis (the basis for the Steenrod284algebra in which it lies)::285286sage: a = SteenrodAlgebra(basis='milnor').Sq(1,2,1)287sage: a.prime()2882289sage: a.basis_name()290'milnor'291sage: a.degree()29214293294It can be viewed in other bases::295296sage: a.milnor() # same as a297Sq(1,2,1)298sage: a.change_basis('adem')299Sq^9 Sq^4 Sq^1 + Sq^11 Sq^2 Sq^1 + Sq^13 Sq^1300sage: a.change_basis('adem').change_basis('milnor')301Sq(1,2,1)302303Regardless of the prime, each element has an ``excess``, and if the304element is homogeneous, a ``degree``. The excess of305`\text{Sq}(i_1,i_2,i_3,...)` is `i_1 + i_2 + i_3 + ...`; when `p` is306odd, the excess of `Q_{0}^{e_0} Q_{1}^{e_1} ... \mathcal{P}(r_1, r_2,307...)` is `\sum e_i + 2 \sum r_i`. The excess of a linear combination308of Milnor basis elements is the minimum of the excesses of those basis309elements.310311The degree of `\text{Sq}(i_1,i_2,i_3,...)` is `sum (2^n-1) i_n`, and312when `p` is odd, the degree of `Q_{0}^{\epsilon_0} Q_{1}^{\epsilon_1}313... \mathcal{P}(r_1, r_2, ...)` is `\sum \epsilon_i (2p^i - 1) + \sum314r_j (2p^j - 2)`. The degree of a linear combination of such terms is315only defined if the terms all have the same degree.316317Here are some simple examples::318319sage: z = Sq(1,2) + Sq(4,1)320sage: z.degree()3217322sage: (Sq(0,0,1) + Sq(5,3)).degree()323Traceback (most recent call last):324...325ValueError: Element is not homogeneous.326sage: Sq(7,2,1).excess()32710328sage: z.excess()3293330sage: B = SteenrodAlgebra(3)331sage: x = B.Q(1,4)332sage: y = B.P(1,2,3)333sage: x.degree()334166335sage: x.excess()3362337sage: y.excess()33812339340Elements have a ``weight`` in the May filtration, which (when `p=2`)341is related to the ``height`` function defined by Wall::342343sage: Sq(2,1,5).may_weight()3449345sage: Sq(2,1,5).wall_height()346[2, 3, 2, 1, 1]347sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)348sage: b.may_weight()3492350sage: b.wall_height()351[0, 0, 1, 1]352353Odd primary May weights::354355sage: A5 = SteenrodAlgebra(5)356sage: a = A5.Q(1,2,4)357sage: b = A5.P(1,2,1)358sage: a.may_weight()35910360sage: b.may_weight()3618362sage: (a * b).may_weight()36318364sage: A5.P(0,0,1).may_weight()3653366367Since the Steenrod algebra is a Hopf algebra, every element has a368coproduct and an antipode.369370::371372sage: Sq(5).coproduct()3731 # Sq(5) + Sq(1) # Sq(4) + Sq(2) # Sq(3) + Sq(3) # Sq(2) + Sq(4) # Sq(1) + Sq(5) # 1374sage: Sq(5).antipode()375Sq(2,1) + Sq(5)376sage: d = Sq(0,0,1); d377Sq(0,0,1)378sage: d.antipode()379Sq(0,0,1)380sage: Sq(4).antipode()381Sq(1,1) + Sq(4)382sage: (Sq(4) * Sq(2)).antipode()383Sq(6)384sage: SteenrodAlgebra(7).P(3,1).antipode()385P(3,1)386387Applying the antipode twice returns the original element::388389sage: y = Sq(8)*Sq(4)390sage: y == (y.antipode()).antipode()391True392393Internal representation: you can use any element as an iterator (``for394x in a: ...``), and the method :meth:`monomial_coefficients` returns a395dictionary with keys tuples representing basis elements and with396corresponding value representing the coefficient of that term::397398sage: c = Sq(5).antipode(); c399Sq(2,1) + Sq(5)400sage: for mono, coeff in c: print coeff, mono4011 (5,)4021 (2, 1)403sage: c.monomial_coefficients()404{(5,): 1, (2, 1): 1}405sage: c.monomials()406[Sq(2,1), Sq(5)]407sage: c.support()408[(2, 1), (5,)]409sage: Adem = SteenrodAlgebra(basis='adem')410sage: (Adem.Sq(10) + Adem.Sq(9) * Adem.Sq(1)).monomials()411[Sq^9 Sq^1, Sq^10]412413sage: A7 = SteenrodAlgebra(p=7)414sage: a = A7.P(1) * A7.P(1); a4152 P(2)416sage: a.leading_coefficient()4172418sage: a.leading_monomial()419P(2)420sage: a.leading_term()4212 P(2)422sage: a.change_basis('adem').monomial_coefficients()423{(0, 2, 0): 2}424425The tuple in the previous output stands for the element `\beta^0426P^2 \beta^0`, i.e., `P^2`. Going in the other direction, if you427want to specify a basis element by giving the corresponding tuple,428you can use the :meth:`monomial` method on the algebra::429430sage: SteenrodAlgebra(p=7, basis='adem').monomial((0, 2, 0))431P^2432sage: 10 * SteenrodAlgebra(p=7, basis='adem').monomial((0, 2, 0))4333 P^2434435In the following example, elements in Wood's Z basis are certain436products of the elements `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.437Internally, each `w(m,k)` is represented by the pair `(m,k)`, and438products of them are represented by tuples of such pairs. ::439440sage: A = SteenrodAlgebra(basis='wood_z')441sage: t = ((2, 0), (0, 0))442sage: A.monomial(t)443Sq^4 Sq^1444445See the documentation for :func:`SteenrodAlgebra` for more details and446examples.447448REFERENCES:449450- [AM] J. F. Adams, and H. R. Margolis, "Sub-Hopf-algebras of the451Steenrod algebra," Proc. Cambridge Philos. Soc. 76 (1974), 45-52.452453- [Mil] J. W. Milnor, "The Steenrod algebra and its dual," Ann. of454Math. (2) 67 (1958), 150-171.455456- [Mon] K. G. Monks, "Change of basis, monomial relations, and457`P^s_t` bases for the Steenrod algebra," J. Pure Appl.458Algebra 125 (1998), no. 1-3, 235-260.459460- [SE] N. E. Steenrod and D. B. A. Epstein, Cohomology operations,461Ann. of Math. Stud. 50 (Princeton University Press, 1962).462"""463464#*****************************************************************************465# Copyright (C) 2008-2010 John H. Palmieri <[email protected]>466# Distributed under the terms of the GNU General Public License (GPL)467#*****************************************************************************468469from sage.combinat.free_module import CombinatorialFreeModule, \470CombinatorialFreeModuleElement471from sage.misc.lazy_attribute import lazy_attribute472from sage.misc.cachefunc import cached_method473from sage.categories.all import ModulesWithBasis, tensor, Hom474475######################################################476# the main class477######################################################478479class SteenrodAlgebra_generic(CombinatorialFreeModule):480r"""481The mod `p` Steenrod algebra.482483Users should not call this, but use the function484:func:`SteenrodAlgebra` instead. See that function for485extensive documentation.486487EXAMPLES::488489sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic()490mod 2 Steenrod algebra, milnor basis491sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5)492mod 5 Steenrod algebra, milnor basis493sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5, 'adem')494mod 5 Steenrod algebra, serre-cartan basis495"""496@staticmethod497def __classcall__(self, p=2, basis='milnor', **kwds):498"""499This normalizes the basis name and the profile, to make unique500representation work properly.501502EXAMPLES::503504sage: SteenrodAlgebra(basis='adem') is SteenrodAlgebra(basis='serre-cartan')505True506sage: SteenrodAlgebra(profile=[3,2,1,0]) is SteenrodAlgebra(profile=lambda n: max(4-n,0), truncation_type=0)507True508"""509from steenrod_algebra_misc import get_basis_name, normalize_profile510profile = kwds.get('profile', None)511precision = kwds.get('precision', None)512truncation_type = kwds.get('truncation_type', 'auto')513514std_basis = get_basis_name(basis, p)515std_profile, std_type = normalize_profile(profile, precision=precision, truncation_type=truncation_type, p=p)516return super(SteenrodAlgebra_generic, self).__classcall__(self, p=p, basis=std_basis, profile=std_profile, truncation_type=std_type)517518def __init__(self, p=2, basis='milnor', **kwds):519r"""520INPUT:521522- ``p`` - positive prime integer (optional, default 2)523- ``basis`` - string (optional, default = 'milnor')524- ``profile`` - profile function (optional, default ``None``)525- ``truncation_type`` - (optional, default 'auto')526- ``precision`` - (optional, default ``None``)527528OUTPUT: mod `p` Steenrod algebra with basis, or a sub-Hopf529algebra of the mod `p` Steenrod algebra defined by the given530profile function.531532See :func:`SteenrodAlgebra` for full documentation.533534EXAMPLES::535536sage: SteenrodAlgebra() # 2 is the default prime537mod 2 Steenrod algebra, milnor basis538sage: SteenrodAlgebra(5)539mod 5 Steenrod algebra, milnor basis540sage: SteenrodAlgebra(2, 'milnor').Sq(0,1)541Sq(0,1)542sage: SteenrodAlgebra(2, 'adem').Sq(0,1)543Sq^2 Sq^1 + Sq^3544545TESTS::546547sage: TestSuite(SteenrodAlgebra()).run()548sage: TestSuite(SteenrodAlgebra(profile=[4,3,2,2,1])).run()549sage: TestSuite(SteenrodAlgebra(basis='adem')).run()550sage: TestSuite(SteenrodAlgebra(basis='wall')).run()551sage: TestSuite(SteenrodAlgebra(basis='arnonc')).run() # long time552sage: TestSuite(SteenrodAlgebra(basis='woody')).run() # long time553sage: A3 = SteenrodAlgebra(3)554sage: A3.category()555Category of graded hopf algebras with basis over Finite Field of size 3556sage: TestSuite(A3).run()557sage: TestSuite(SteenrodAlgebra(basis='adem', p=3)).run()558sage: TestSuite(SteenrodAlgebra(basis='pst_llex', p=7)).run() # long time559sage: TestSuite(SteenrodAlgebra(basis='comm_deg', p=5)).run() # long time560"""561from sage.rings.arith import is_prime562from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis563from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets564from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets565from sage.rings.infinity import Infinity566from sage.sets.set_from_iterator import EnumeratedSetFromIterator567from functools import partial568from steenrod_algebra_bases import steenrod_algebra_basis569from sage.rings.all import GF570profile = kwds.get('profile', None)571truncation_type = kwds.get('truncation_type', 'auto')572573if not is_prime(p):574raise ValueError("%s is not prime." % p)575self._prime = p576base_ring = GF(p)577self._profile = profile578self._truncation_type = truncation_type579if ((p==2 and ((len(profile) > 0 and profile[0] < Infinity)))580or (p>2 and profile != ((), ()) and len(profile[0]) > 0581and profile[0][0] < Infinity)582or (truncation_type < Infinity)):583if basis != 'milnor' and basis.find('pst') == -1:584raise NotImplementedError("For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.")585self._basis_name = basis586basis_category = FiniteEnumeratedSets() if self.is_finite() else InfiniteEnumeratedSets()587basis_set = EnumeratedSetFromIterator(self._basis_key_iterator,588category=basis_category,589name = "basis key family of %s" % self,590cache = False)591592self._basis_fcn = partial(steenrod_algebra_basis,593p=p,594basis=basis,595profile=profile,596truncation_type=truncation_type)597598CombinatorialFreeModule.__init__(self,599base_ring,600basis_set,601prefix=self._basis_name,602element_class=self.Element,603category = GradedHopfAlgebrasWithBasis(base_ring),604scalar_mult = ' ')605606def _basis_key_iterator(self):607"""608An iterator for the basis keys of the Steenrod algebra.609610EXAMPLES::611612sage: A = SteenrodAlgebra(3,basis='adem')613sage: for (idx,key) in zip((1,..,10),A._basis_key_iterator()):614... print "> %2d %-20s %s" % (idx,key,A.monomial(key))615> 1 () 1616> 2 (1,) beta617> 3 (0, 1, 0) P^1618> 4 (1, 1, 0) beta P^1619> 5 (0, 1, 1) P^1 beta620> 6 (1, 1, 1) beta P^1 beta621> 7 (0, 2, 0) P^2622> 8 (1, 2, 0) beta P^2623> 9 (0, 2, 1) P^2 beta624> 10 (1, 2, 1) beta P^2 beta625"""626from steenrod_algebra_bases import steenrod_algebra_basis627from sage.sets.integer_range import IntegerRange628from sage.rings.integer import Integer629from sage.rings.infinity import Infinity630from functools import partial631import itertools632if self.is_finite():633maxdim = self.top_class().degree()634I = IntegerRange(Integer(0),Integer(maxdim+1))635else:636I = IntegerRange(Integer(0),Infinity)637basfnc = partial(steenrod_algebra_basis,638p=self.prime(),639basis=self._basis_name,640profile=self._profile,641truncation_type=self._truncation_type)642return itertools.chain.from_iterable(basfnc(dim) for dim in I)643644def prime(self):645r"""646The prime associated to self.647648EXAMPLES::649650sage: SteenrodAlgebra(p=2, profile=[1,1]).prime()6512652sage: SteenrodAlgebra(p=7).prime()6537654"""655return self._prime656657def basis_name(self):658r"""659The basis name associated to self.660661EXAMPLES::662663sage: SteenrodAlgebra(p=2, profile=[1,1]).basis_name()664'milnor'665sage: SteenrodAlgebra(basis='serre-cartan').basis_name()666'serre-cartan'667sage: SteenrodAlgebra(basis='adem').basis_name()668'serre-cartan'669"""670return self.prefix()671672def _has_nontrivial_profile(self):673r"""674True if the profile function for this algebra seems to be that675for a proper sub-Hopf algebra of the Steenrod algebra.676677EXAMPLES::678679sage: SteenrodAlgebra()._has_nontrivial_profile()680False681sage: SteenrodAlgebra(p=3)._has_nontrivial_profile()682False683sage: SteenrodAlgebra(profile=[3,2,1])._has_nontrivial_profile()684True685sage: SteenrodAlgebra(profile=([1], [2, 2]), p=3)._has_nontrivial_profile()686True687688Check that a bug in #11832 has been fixed::689690sage: P3 = SteenrodAlgebra(p=3, profile=(lambda n: Infinity, lambda n: 1))691sage: P3._has_nontrivial_profile()692True693"""694from sage.rings.infinity import Infinity695profile = self._profile696trunc = self._truncation_type697if self.prime() == 2:698return ((len(profile) > 0 and len(profile) > 0699and profile[0] < Infinity)700or (trunc < Infinity))701return ((profile != ((), ()) and702((len(profile[0]) > 0 and profile[0][0] < Infinity)703or (len(profile[1]) > 0 and min(profile[1]) == 1)))704or (trunc < Infinity))705706def _repr_(self):707r"""708Printed representation of the Steenrod algebra.709710EXAMPLES::711712sage: SteenrodAlgebra(3)713mod 3 Steenrod algebra, milnor basis714sage: SteenrodAlgebra(2, basis='adem')715mod 2 Steenrod algebra, serre-cartan basis716sage: B = SteenrodAlgebra(2003)717sage: B._repr_()718'mod 2003 Steenrod algebra, milnor basis'719720sage: SteenrodAlgebra(profile=(3,2,1,0))721sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]722sage: SteenrodAlgebra(profile=lambda n: 4)723sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [4, 4, 4, ..., 4, 4, +Infinity, +Infinity, +Infinity, ...]724sage: SteenrodAlgebra(p=5, profile=(lambda n: 4, lambda n: 1))725sub-Hopf algebra of mod 5 Steenrod algebra, milnor basis, profile function ([4, 4, 4, ..., 4, 4, +Infinity, +Infinity, +Infinity, ...], [1, 1, 1, ..., 1, 1, 2, 2, ...])726"""727def abridge_list(l):728"""729String rep for list ``l`` if ``l`` is short enough;730otherwise print the first few terms and the last few731terms, with an ellipsis in between.732"""733if len(l) < 8:734l_str = str(l)735else:736l_str = str(l[:3]).rstrip("]") + ", ..., " + str(l[-2:]).lstrip("[")737return l_str738739from sage.rings.infinity import Infinity740profile = self._profile741trunc = self._truncation_type742p = self.prime()743if self._has_nontrivial_profile():744if p == 2:745pro_str = abridge_list(list(profile))746if trunc != 0:747pro_str = pro_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"748else:749e_str = abridge_list(list(profile[0]))750k_str = abridge_list(list(profile[1]))751if trunc != 0:752e_str = e_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"753k_str = k_str.rstrip("]") + ", " + str([2] * 2).strip("[]") + ", ...]"754pro_str = "(%s, %s)" % (e_str, k_str)755return "sub-Hopf algebra of mod %d Steenrod algebra, %s basis, profile function %s" % (self.prime(), self._basis_name, pro_str)756return "mod %d Steenrod algebra, %s basis" % (self.prime(), self._basis_name)757758def _latex_(self):759r"""760LaTeX representation of the Steenrod algebra.761762EXAMPLES::763764sage: C = SteenrodAlgebra(3)765sage: C766mod 3 Steenrod algebra, milnor basis767sage: C._latex_()768'\\mathcal{A}_{3}'769"""770return "\\mathcal{A}_{%s}" % self.prime()771772def _repr_term(self, t):773r"""774String representation of the monomial specified by the tuple ``t``.775776INPUT:777778- ``t`` - tuple, representing basis element in the current basis.779780OUTPUT: string781782This is tested in many places: any place elements are printed783is essentially a doctest for this method. Also, each basis784has its own method for printing monomials, and those are785doctested individually. We give a few doctests here, in786addition.787788EXAMPLES::789790sage: SteenrodAlgebra()._repr_term((3,2))791'Sq(3,2)'792sage: SteenrodAlgebra(p=7)._repr_term(((0,2), (3,2)))793'Q_0 Q_2 P(3,2)'794sage: SteenrodAlgebra(basis='adem')._repr_term((14,2))795'Sq^14 Sq^2'796sage: SteenrodAlgebra(basis='adem', p=3)._repr_term((1,3,0))797'beta P^3'798sage: SteenrodAlgebra(basis='pst')._repr_term(((0,2), (1,3)))799'P^0_2 P^1_3'800sage: SteenrodAlgebra(basis='arnon_a')._repr_term(((0,2), (1,3)))801'X^0_2 X^1_3'802803sage: A7 = SteenrodAlgebra(7)804sage: x = A7.Q(0,3) * A7.P(2,2)805sage: x._repr_()806'Q_0 Q_3 P(2,2)'807sage: x808Q_0 Q_3 P(2,2)809sage: a = SteenrodAlgebra().Sq(0,0,2)810sage: a811Sq(0,0,2)812sage: A2_adem = SteenrodAlgebra(2,'admissible')813sage: A2_adem(a)814Sq^8 Sq^4 Sq^2 + Sq^9 Sq^4 Sq^1 + Sq^10 Sq^3 Sq^1 +815Sq^10 Sq^4 + Sq^11 Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^13 Sq^1816+ Sq^14817sage: SteenrodAlgebra(2, 'woodz')(a)818Sq^6 Sq^7 Sq^1 + Sq^14 + Sq^4 Sq^7 Sq^3 + Sq^4 Sq^7819Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^8 Sq^6 + Sq^8 Sq^4 Sq^2820sage: SteenrodAlgebra(2, 'arnonc')(a)821Sq^4 Sq^2 Sq^8 + Sq^4 Sq^4 Sq^6 + Sq^4 Sq^6 Sq^4 +822Sq^6 Sq^8 + Sq^8 Sq^4 Sq^2 + Sq^8 Sq^6823sage: SteenrodAlgebra(2, 'pst_llex')(a)824P^1_3825sage: SteenrodAlgebra(2, 'comm_revz')(a)826c_0,1 c_1,1 c_0,3 c_2,1 + c_0,2 c_0,3 c_2,1 + c_1,3827"""828from steenrod_algebra_misc import milnor_mono_to_string, \829serre_cartan_mono_to_string, wood_mono_to_string, \830wall_mono_to_string, wall_long_mono_to_string, \831arnonA_mono_to_string, arnonA_long_mono_to_string, \832pst_mono_to_string, \833comm_long_mono_to_string, comm_mono_to_string834p = self.prime()835basis = self.basis_name()836if basis == 'milnor':837s = milnor_mono_to_string(t, p=p)838elif basis == 'serre-cartan':839s = serre_cartan_mono_to_string(t, p=p)840elif basis.find('wood') >= 0:841s = wood_mono_to_string(t)842elif basis == 'wall':843s = wall_mono_to_string(t)844elif basis == 'wall_long':845s = wall_long_mono_to_string(t)846elif basis == 'arnona':847s = arnonA_mono_to_string(t)848elif basis == 'arnona_long':849s = arnonA_long_mono_to_string(t)850elif basis == 'arnonc':851s = serre_cartan_mono_to_string(t)852elif basis.find('pst') >= 0:853s = pst_mono_to_string(t, p=p)854elif basis.find('comm') >= 0 and basis.find('long') >= 0:855s = comm_long_mono_to_string(t, p=p)856elif basis.find('comm') >= 0:857s = comm_mono_to_string(t, p=p)858s = s.translate(None, "{}")859return s860861def _latex_term(self, t):862"""863LaTeX representation of the monomial specified by the tuple ``t``.864865INPUT:866867- ``t`` - tuple, representing basis element in the current basis.868869OUTPUT: string870871The string depends on the basis over which the element is defined.872873EXAMPLES::874875sage: A7 = SteenrodAlgebra(7)876sage: A7._latex_term(((0, 3), (2,2)))877'Q_{0} Q_{3} \\mathcal{P}(2,2)'878sage: x = A7.Q(0,3) * A7.P(2,2)879sage: x._latex_() # indirect doctest880'Q_{0} Q_{3} \\mathcal{P}(2,2)'881sage: latex(x)882Q_{0} Q_{3} \mathcal{P}(2,2)883sage: b = Sq(0,2)884sage: b.change_basis('adem')._latex_()885'\\text{Sq}^{4} \\text{Sq}^{2} + \\text{Sq}^{5} \\text{Sq}^{1} +886\\text{Sq}^{6}'887sage: b.change_basis('woody')._latex_()888'\\text{Sq}^{2} \\text{Sq}^{3} \\text{Sq}^{1} + \\text{Sq}^{6} +889\\text{Sq}^{4} \\text{Sq}^{2}'890sage: SteenrodAlgebra(2, 'arnona')(b)._latex_()891'X^{1}_{1} X^{2}_{2} + X^{2}_{1}'892sage: SteenrodAlgebra(p=3, basis='serre-cartan').Q(0)._latex_()893'\\beta'894sage: latex(Sq(2).change_basis('adem').coproduct())8951 \otimes \text{Sq}^{2} + \text{Sq}^{1} \otimes \text{Sq}^{1} + \text{Sq}^{2} \otimes 1896"""897import re898s = self._repr_term(t)899s = re.sub(r"\^([0-9]*)", r"^{\1}", s)900s = re.sub("_([0-9,]*)", r"_{\1}", s)901s = s.replace("Sq", "\\text{Sq}")902s = s.replace("P", "\\mathcal{P}")903s = s.replace("beta", "\\beta")904return s905906def __eq__(self, right):907r"""908Two Steenrod algebras are equal iff their associated primes,909bases, and profile functions (if present) are equal. Because910this class inherits from :class:`UniqueRepresentation`, this911means that they are equal if and only they are identical: ``A912== B`` is True if and only if ``A is B`` is True.913914EXAMPLES::915916sage: A = SteenrodAlgebra(2)917sage: B = SteenrodAlgebra(2, 'adem')918sage: A == B919False920sage: C = SteenrodAlgebra(17)921sage: A == C922False923924sage: A1 = SteenrodAlgebra(2, profile=[2,1])925sage: A1 == A926False927sage: A1 == SteenrodAlgebra(2, profile=[2,1,0])928True929sage: A1 == SteenrodAlgebra(2, profile=[2,1], basis='pst')930False931"""932return self is right933934def __ne__(self, right):935r"""936The negation of the method ``__eq__``.937938EXAMPLES::939940sage: SteenrodAlgebra(p=2) != SteenrodAlgebra(p=2, profile=[2,1])941True942"""943return not self.__eq__(right)944945def profile(self, i, component=0):946r"""947Profile function for this algebra.948949INPUT:950951- `i` - integer952- ``component`` - either 0 or 1, optional (default 0)953954OUTPUT: integer or `\infty`955956See the documentation for957:mod:`sage.algebras.steenrod.steenrod_algebra` and958:func:`SteenrodAlgebra` for information on profile functions.959960This applies the profile function to the integer `i`. Thus961when `p=2`, `i` must be a positive integer. When `p` is odd,962there are two profile functions, `e` and `k` (in the notation963of the aforementioned documentation), corresponding,964respectively to ``component=0`` and ``component=1``. So when965`p` is odd and ``component`` is 0, `i` must be positive, while966when ``component`` is 1, `i` must be non-negative.967968EXAMPLES::969970sage: SteenrodAlgebra().profile(3)971+Infinity972sage: SteenrodAlgebra(profile=[3,2,1]).profile(1)9733974sage: SteenrodAlgebra(profile=[3,2,1]).profile(2)9752976977When the profile is specified by a list, the default behavior978is to return zero values outside the range of the list. This979can be overridden if the algebra is created with an infinite980``truncation_type``::981982sage: SteenrodAlgebra(profile=[3,2,1]).profile(9)9830984sage: SteenrodAlgebra(profile=[3,2,1], truncation_type=Infinity).profile(9)985+Infinity986987sage: B = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1))988sage: B.profile(3)9893990sage: B.profile(3, component=1)9911992"""993# determine the tuple t to use994p = self.prime()995if p == 2:996t = self._profile997elif component == 0:998t = self._profile[0]999else:1000t = self._profile[1]1001# case 1: exponents of the xi's1002if p == 2 or component == 0:1003if i <= 0:1004return 01005try:1006return t[i-1]1007except IndexError:1008return self._truncation_type1009else:1010# case 2: exponents of the tau's1011if i < 0:1012return 11013try:1014return t[i]1015except IndexError:1016if self._truncation_type > 0:1017return 21018else:1019return 110201021def homogeneous_component(self, n):1022"""1023Return the nth homogeneous piece of the Steenrod algebra.10241025INPUT:10261027- `n` - integer10281029OUTPUT: a vector space spanned by the basis for this algebra1030in dimension `n`10311032EXAMPLES::10331034sage: A = SteenrodAlgebra()1035sage: A.homogeneous_component(4)1036Vector space spanned by (Sq(1,1), Sq(4)) over Finite Field of size 21037sage: SteenrodAlgebra(profile=[2,1,0]).homogeneous_component(4)1038Vector space spanned by (Sq(1,1),) over Finite Field of size 210391040The notation A[n] may also be used::10411042sage: A[5]1043Vector space spanned by (Sq(2,1), Sq(5)) over Finite Field of size 21044sage: SteenrodAlgebra(basis='wall')[4]1045Vector space spanned by (Q^1_0 Q^0_0, Q^2_2) over Finite Field of size 21046sage: SteenrodAlgebra(p=5)[17]1047Vector space spanned by (Q_1 P(1), Q_0 P(2)) over Finite Field of size 510481049Note that A[n] is just a vector space, not a Hopf algebra, so1050its elements don't have products, coproducts, or antipodes1051defined on them. If you want to use operations like this on1052elements of some A[n], then convert them back to elements of A::10531054sage: A[5].basis()1055Finite family {(5,): milnor[(5,)], (2, 1): milnor[(2, 1)]}1056sage: a = list(A[5].basis())[1]1057sage: a # not in A, doesn't print like an element of A1058milnor[(5,)]1059sage: A(a) # in A1060Sq(5)1061sage: A(a) * A(a)1062Sq(7,1)1063sage: a * A(a) # only need to convert one factor1064Sq(7,1)1065sage: a.antipode() # not defined1066Traceback (most recent call last):1067...1068AttributeError: 'CombinatorialFreeModule_with_category.element_class' object has no attribute 'antipode'1069sage: A(a).antipode() # convert to elt of A, then compute antipode1070Sq(2,1) + Sq(5)10711072sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')10731074TESTS:10751076The following sort of thing is also tested by the function1077:func:`steenrod_basis_error_check1078<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_basis_error_check>`::10791080sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])1081sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')1082sage: max([H[n].dimension() - G[n].dimension() for n in range(100)])108301084"""1085from sage.rings.all import GF1086basis = self._basis_fcn(n)1087M = CombinatorialFreeModule(GF(self.prime()), basis,1088element_class=self.Element,1089prefix=self._basis_name)1090M._name = "Vector space spanned by %s"%(tuple([self.monomial(a) for a in basis]),)1091return M10921093__getitem__ = homogeneous_component10941095def one_basis(self):1096"""1097The index of the element 1 in the basis for the Steenrod algebra.10981099EXAMPLES::11001101sage: SteenrodAlgebra(p=2).one_basis()1102()1103sage: SteenrodAlgebra(p=7).one_basis()1104((), ())1105"""1106p = self.prime()1107basis = self.basis_name()1108if basis == 'serre-cartan' or basis == 'arnonc':1109return (0,)1110if p == 2:1111return ()1112return ((), ())11131114def product_on_basis(self, t1, t2):1115"""1116The product of two basis elements of this algebra11171118INPUT:11191120- ``t1``, ``t2`` -- tuples, the indices of two basis elements of self11211122OUTPUT: the product of the two corresponding basis elements,1123as an element of self11241125ALGORITHM: If the two elements are represented in the Milnor1126basis, use Milnor multiplication as implemented in1127:mod:`sage.algebras.steenrod.steenrod_algebra_mult`. If the two1128elements are represented in the Serre-Cartan basis, then1129multiply them using Adem relations (also implemented in1130:mod:`sage.algebras.steenrod.steenrod_algebra_mult`). This1131provides a good way of checking work -- multiply Milnor1132elements, then convert them to Adem elements and multiply1133those, and see if the answers correspond.11341135If the two elements are represented in some other basis, then1136convert them both to the Milnor basis and multiply.11371138EXAMPLES::11391140sage: Milnor = SteenrodAlgebra()1141sage: Milnor.product_on_basis((2,), (2,))1142Sq(1,1)1143sage: Adem = SteenrodAlgebra(basis='adem')1144sage: Adem.Sq(2) * Adem.Sq(2) # indirect doctest1145Sq^3 Sq^111461147When multiplying elements from different bases, the left-hand1148factor determines the form of the output::11491150sage: Adem.Sq(2) * Milnor.Sq(2)1151Sq^3 Sq^11152sage: Milnor.Sq(2) * Adem.Sq(2)1153Sq(1,1)11541155TESTS::11561157sage: all([Adem(Milnor.Sq(n) ** 3)._repr_() == (Adem.Sq(n) ** 3)._repr_() for n in range(10)])1158True1159sage: Wall = SteenrodAlgebra(basis='wall')1160sage: Wall(Adem.Sq(4,4) * Milnor.Sq(4)) == Adem(Wall.Sq(4,4) * Milnor.Sq(4))1161True11621163sage: A3 = SteenrodAlgebra(p=3, basis='adem')1164sage: M3 = SteenrodAlgebra(p=3, basis='milnor')1165sage: all([A3(M3.P(n) * M3.Q(0) * M3.P(n))._repr_() == (A3.P(n) * A3.Q(0) * A3.P(n))._repr_() for n in range(5)])1166True1167"""1168p = self.prime()1169basis = self.basis_name()1170if basis == 'milnor':1171if p == 2:1172from steenrod_algebra_mult import milnor_multiplication1173d = milnor_multiplication(t1, t2)1174else:1175from steenrod_algebra_mult import milnor_multiplication_odd1176d = milnor_multiplication_odd(t1, t2, p)1177return self._from_dict(d, coerce=True)1178elif basis == 'serre-cartan':1179from steenrod_algebra_mult import make_mono_admissible1180if p > 2:1181# make sure output has an odd number of terms. if both t11182# and t2 have an odd number, concatenate them, adding the1183# middle term...1184#1185# if either t1 or t2 has an even number of terms, append a1186# 0.1187if (len(t1) % 2) == 0:1188t1 = t1 + (0,)1189if (len(t2) % 2) == 0:1190t2 = t2 + (0,)1191if t1[-1] + t2[0] == 2:1192return self.zero()1193mono = t1[:-1] + (t1[-1] + t2[0],) + t2[1:]1194d = make_mono_admissible(mono, p)1195else: # p=21196mono = t1 + t21197while len(mono) > 1 and mono[-1] == 0:1198mono = mono[:-1]1199d = make_mono_admissible(mono)1200return self._from_dict(d, coerce=True)1201else:1202x = self({t1: 1})1203y = self({t2: 1})1204A = SteenrodAlgebra(basis='milnor', p=p)1205return self(A(x) * A(y))12061207def coproduct_on_basis(self, t, algorithm=None):1208r"""1209The coproduct of a basis element of this algebra12101211INPUT:12121213- ``t`` -- tuple, the index of a basis element of self12141215- ``algorithm`` -- None or a string, either 'milnor' or1216'serre-cartan' (or anything which will be converted to one1217of these by the function :func:`get_basis_name1218<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.1219If None, default to 'milnor' unless current basis is1220'serre-cartan', in which case use 'serre-cartan'.12211222ALGORITHM: The coproduct on a Milnor basis element `P(n_1,1223n_2, ...)` is `\sum P(i_1, i_2, ...) \otimes P(j_1, j_2,1224...)`, summed over all `i_k + j_k = n_k` for each `k`. At odd1225primes, each element `Q_n` is primitive: its coproduct is `Q_n1226\otimes 1 + 1 \otimes Q_n`.12271228One can deduce a coproduct formula for the Serre-Cartan basis1229from this: the coproduct on each `P^n` is `\sum P^i \otimes1230P^{n-i}` and at odd primes `\beta` is primitive. Since the1231coproduct is an algebra map, one can then compute the1232coproduct on any Serre-Cartan basis element.12331234Which of these methods is used is controlled by whether1235``algorithm`` is 'milnor' or 'serre-cartan'.12361237OUTPUT: the coproduct of the corresponding basis element,1238as an element of self tensor self.12391240EXAMPLES::12411242sage: A = SteenrodAlgebra()1243sage: A.coproduct_on_basis((3,))12441 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 112451246TESTS::12471248sage: all([A.coproduct_on_basis((n,1), algorithm='milnor') == A.coproduct_on_basis((n,1), algorithm='adem') for n in range(9)]) # long time1249True1250sage: A7 = SteenrodAlgebra(p=7, basis='adem')1251sage: all([A7.coproduct_on_basis((0,n,1), algorithm='milnor') == A7.coproduct_on_basis((0,n,1), algorithm='adem') for n in range(9)]) # long time1252True1253"""1254def coprod_list(t):1255"""1256if t = (n0, n1, ...), then return list of terms (i0, i1,1257...) where ik <= nk for each k. From each such term, can1258recover the second factor in the coproduct.1259"""1260if len(t) == 0:1261return [()]1262if len(t) == 1:1263return [[a] for a in range(t[0] + 1)]1264ans = []1265for i in range(t[0] + 1):1266ans.extend([[i] + x for x in coprod_list(t[1:])])1267return ans12681269from steenrod_algebra_misc import get_basis_name1270p = self.prime()1271basis = self.basis_name()1272if algorithm is None:1273if basis == 'serre-cartan':1274algorithm = 'serre-cartan'1275else:1276algorithm = 'milnor'1277else:1278algorithm = get_basis_name(algorithm, p)1279if basis == algorithm:1280if basis == 'milnor':1281if p == 2:1282left = coprod_list(t)1283right = [[x-y for (x,y) in zip(t, m)] for m in left]1284old = list(left)1285left = []1286# trim trailing zeros:1287for a in old:1288while len(a) > 0 and a[-1] == 0:1289a = a[:-1]1290left.append(tuple(a))1291old = list(right)1292right = []1293for a in old:1294while len(a) > 0 and a[-1] == 0:1295a = a[:-1]1296right.append(tuple(a))1297tens = dict().fromkeys(zip(left, right), 1)1298return self.tensor_square()._from_dict(tens)1299else: # p odd1300from sage.combinat.permutation import Permutation1301from steenrod_algebra_misc import convert_perm1302from sage.sets.set import Set1303left_p = coprod_list(t[1])1304right_p = [[x-y for (x,y) in zip(t[1], m)] for m in left_p]1305old = list(left_p)1306left_p = []1307# trim trailing zeros:1308for a in old:1309while len(a) > 0 and a[-1] == 0:1310a = a[:-1]1311left_p.append(tuple(a))1312old = list(right_p)1313right_p = []1314for a in old:1315while len(a) > 0 and a[-1] == 0:1316a = a[:-1]1317right_p.append(tuple(a))1318all_q = Set(t[0])1319tens_q = {}1320for a in all_q.subsets():1321left_q = sorted(list(a))1322right_q = sorted(list(all_q - a))1323sign = Permutation(convert_perm(left_q + right_q)).signature()1324tens_q[(tuple(left_q), tuple(right_q))] = sign1325tens = {}1326for l, r in zip(left_p, right_p):1327for q in tens_q:1328tens[((q[0], l), (q[1], r))] = tens_q[q]1329return self.tensor_square()._from_dict(tens)1330elif basis == 'serre-cartan':1331result = self.tensor_square().one()1332if p == 2:1333for n in t:1334s = self.tensor_square().zero()1335for i in range(0, n+1):1336s += tensor((self.Sq(i), self.Sq(n-i)))1337result = result * s1338return result1339else:1340bockstein = True1341for n in t:1342if bockstein:1343if n != 0:1344s = tensor((self.Q(0), self.one())) + tensor((self.one(), self.Q(0)))1345else:1346s = self.tensor_square().one()1347bockstein = False1348else:1349s = self.tensor_square().zero()1350for i in range(0, n+1):1351s += tensor((self.P(i), self.P(n-i)))1352bockstein = True1353result = result * s1354return result1355else:1356A = SteenrodAlgebra(p=p, basis=algorithm)1357x = A(self._change_basis_on_basis(t, algorithm)).coproduct(algorithm=algorithm)1358result = []1359for (a,b), coeff in x:1360result.append((tensor((A._change_basis_on_basis(a, basis),1361A._change_basis_on_basis(b, basis))),coeff))1362return self.tensor_square().linear_combination(result)13631364def coproduct(self, x, algorithm='milnor'):1365r"""1366Return the coproduct of an element ``x`` of this algebra.13671368INPUT:13691370- ``x`` -- element of self13711372- ``algorithm`` -- None or a string, either 'milnor' or1373'serre-cartan' (or anything which will be converted to one1374of these by the function :func:`get_basis_name1375<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.1376If None, default to 'serre-cartan' if current basis is1377'serre-cartan'; otherwise use 'milnor'.13781379This calls :meth:`coproduct_on_basis` on the summands of ``x``1380and extends linearly.13811382EXAMPLES::13831384sage: SteenrodAlgebra().Sq(3).coproduct()13851 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 113861387The element `\text{Sq}(0,1)` is primitive::13881389sage: SteenrodAlgebra(basis='adem').Sq(0,1).coproduct()13901 # Sq^2 Sq^1 + 1 # Sq^3 + Sq^2 Sq^1 # 1 + Sq^3 # 11391sage: SteenrodAlgebra(basis='pst').Sq(0,1).coproduct()13921 # P^0_2 + P^0_2 # 113931394sage: SteenrodAlgebra(p=3).P(4).coproduct()13951 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 11396sage: SteenrodAlgebra(p=3).P(4).coproduct(algorithm='serre-cartan')13971 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 11398sage: SteenrodAlgebra(p=3, basis='serre-cartan').P(4).coproduct()13991 # P^4 + P^1 # P^3 + P^2 # P^2 + P^3 # P^1 + P^4 # 11400sage: SteenrodAlgebra(p=11, profile=((), (2,1,2))).Q(0,2).coproduct()14011 # Q_0 Q_2 + Q_0 # Q_2 + Q_0 Q_2 # 1 - Q_2 # Q_01402"""1403# taken from categories.coalgebras_with_basis, then modified1404# to allow the use of the "algorithm" keyword1405coprod = lambda x: self.coproduct_on_basis(x, algorithm)1406return Hom(self, tensor([self, self]), ModulesWithBasis(self.base_ring()))(on_basis = coprod)(x)14071408def antipode_on_basis(self, t):1409r"""1410The antipode of a basis element of this algebra14111412INPUT:14131414- ``t`` -- tuple, the index of a basis element of self14151416OUTPUT: the antipode of the corresponding basis element,1417as an element of self.14181419ALGORITHM: according to a result of Milnor's, the antipode of1420`\text{Sq}(n)` is the sum of all of the Milnor basis elements1421in dimension `n`. So: convert the element to the Serre-Cartan1422basis, thus writing it as a sum of products of elements1423`\text{Sq}(n)`, and use Milnor's formula for the antipode of1424`\text{Sq}(n)`, together with the fact that the antipode is an1425antihomomorphism: if we call the antipode `c`, then `c(ab) =1426c(b) c(a)`.14271428At odd primes, a similar method is used: the antipode of1429`P(n)` is the sum of the Milnor P basis elements in dimension1430`n*2(p-1)`, multiplied by `(-1)^n`, and the antipode of `\beta1431= Q_0` is `-Q_0`. So convert to the Serre-Cartan basis, as in1432the `p=2` case.14331434EXAMPLES::14351436sage: A = SteenrodAlgebra()1437sage: A.antipode_on_basis((4,))1438Sq(1,1) + Sq(4)1439sage: A.Sq(4).antipode()1440Sq(1,1) + Sq(4)1441sage: Adem = SteenrodAlgebra(basis='adem')1442sage: Adem.Sq(4).antipode()1443Sq^3 Sq^1 + Sq^41444sage: SteenrodAlgebra(basis='pst').Sq(3).antipode()1445P^0_1 P^1_1 + P^0_21446sage: a = SteenrodAlgebra(basis='wall_long').Sq(10)1447sage: a.antipode()1448Sq^1 Sq^2 Sq^4 Sq^1 Sq^2 + Sq^2 Sq^4 Sq^1 Sq^2 Sq^1 + Sq^8 Sq^21449sage: a.antipode().antipode() == a1450True14511452sage: SteenrodAlgebra(p=3).P(6).antipode()1453P(2,1) + P(6)1454sage: SteenrodAlgebra(p=3).P(6).antipode().antipode()1455P(6)14561457TESTS::14581459sage: Milnor = SteenrodAlgebra()1460sage: all([x.antipode().antipode() == x for x in Milnor.basis(11)]) # long time1461True1462sage: A5 = SteenrodAlgebra(p=5, basis='adem')1463sage: all([x.antipode().antipode() == x for x in A5.basis(25)])1464True1465sage: H = SteenrodAlgebra(profile=[2,2,1])1466sage: H.Sq(1,2).antipode() in H1467True1468"""1469p = self.prime()1470if self.basis_name() == 'serre-cartan':1471antipode = self.one()1472if p == 2:1473for n in t:1474antipode = self(sum(SteenrodAlgebra().basis(n))) * antipode1475else:1476from sage.misc.functional import is_even1477for index, n in enumerate(t):1478if is_even(index):1479if n != 0:1480antipode = -self.Q(0) * antipode1481else:1482B = SteenrodAlgebra(p=p).basis(n * 2 * (p-1))1483s = self(0)1484for b in B:1485if len(b.leading_support()[0]) == 0:1486s += self(b)1487antipode = (-1)**n * s * antipode1488return antipode1489return self(self._change_basis_on_basis(t, 'serre-cartan').antipode())14901491def counit_on_basis(self, t):1492"""1493The counit sends all elements of positive degree to zero.14941495INPUT:14961497- ``t`` -- tuple, the index of a basis element of self14981499EXAMPLES::15001501sage: A2 = SteenrodAlgebra(p=2)1502sage: A2.counit_on_basis(())150311504sage: A2.counit_on_basis((0,0,1))150501506sage: parent(A2.counit_on_basis((0,0,1)))1507Finite Field of size 21508sage: A3 = SteenrodAlgebra(p=3)1509sage: A3.counit_on_basis(((1,2,3), (1,1,1)))151001511sage: A3.counit_on_basis(((), ()))151211513sage: A3.counit(A3.P(10,5))151401515sage: A3.counit(A3.P(0))151611517"""1518if t != () and t != ((), ()):1519return self.base_ring().zero()1520else:1521return self.base_ring().one()15221523def _milnor_on_basis(self, t):1524"""1525Convert the tuple t in the current basis to an element in the1526Milnor basis.15271528INPUT:15291530- t - tuple, representing basis element in the current basis.15311532OUTPUT: element of the Steenrod algebra with the Milnor basis15331534ALGORITHM: there is a simple conversion from each basis to the1535Milnor basis, so use that. In more detail:15361537- If the current basis is the Milnor basis, just return the1538corresponding element.15391540- If the current basis is the Serre-Cartan basis: when `p=2`,1541the element `\text{Sq}^a` equals the Milnor element1542`\text{Sq}(a)`; when `p` is odd, `\mathcal{P}^a =1543\mathcal{P}(a)` and `\beta = Q_0`. Hence for any1544Serre-Cartan basis element, represent it in the1545Milnor basis by computing an appropriate product using1546Milnor multiplication.15471548- The same goes for Arnon's C basis, since the elements are1549monomials in the Steenrod squares.15501551- If the current basis is Wood's Y or Z bases, then each basis1552element is a monomial in the classes `w(m,k) =1553\text{Sq}^{2^m (2^{k+1}-1)}`. So again, multiply the1554corresponding Milnor elements together.15551556- The Wall basis: each basis element is a monomial in the1557elements `Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.15581559- Arnon's A basis: each basis element is a monomial in the1560elements `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`.15611562- The `P^s_t` bases: when `p=2`, each basis element is a1563monomial in the elements `P^s_t`. When `p` is odd, each1564basis element is a product of elements `Q_i` and a monomial1565in the elements `(P^s_t)^n` where `0 < n < p`.15661567- The commutator bases: when `p=2`, each basis element is a1568monomial in the iterated commutators `c_{i,j}`, defined by1569`c_{i,1} = \text{Sq}(2^i)` and `c_{i,j} = [c_{i,j-1},1570\text{Sq}(2^{i+j-1})]`. When `p` is odd, each basis element1571is a product of elements `Q_i` and a monomial in the1572elements `c_{i,j}^n` where `0 < n < p`, `c_{i,1} =1573P(p^i)` and `c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}]`.15741575EXAMPLES::15761577sage: Adem = SteenrodAlgebra(basis='serre-cartan')1578sage: Adem._milnor_on_basis((2,1)) # Sq^2 Sq^11579Sq(0,1) + Sq(3)1580sage: Pst = SteenrodAlgebra(basis='pst')1581sage: Pst._milnor_on_basis(((0,1), (1,1), (2,1)))1582Sq(7)1583"""1584basis = self.basis_name()1585p = self.prime()1586A = SteenrodAlgebra(p=p)1587# milnor1588if basis == 'milnor':1589return A({t: 1})15901591ans = A(1)1592# serre-cartan, arnonc1593if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):1594for j in t:1595ans = ans * A.Sq(j)15961597elif p > 2 and basis == 'serre-cartan':1598bockstein = True1599for j in t:1600if bockstein:1601if j != 0:1602ans = ans * A.Q(0)1603bockstein = False1604else:1605ans = ans * A.P(j)1606bockstein = True1607# wood_y:1608elif basis == 'woody' or basis == 'woodz':1609# each entry in t is a pair (m,k), corresponding to w(m,k), defined by1610# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.1611for (m,k) in t:1612ans = ans * A.Sq(2**m * (2**(k+1) - 1))16131614# wall[_long]1615elif basis.find('wall') >= 0:1616# each entry in t is a pair (m,k), corresponding to Q^m_k, defined by1617#`Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.1618for (m,k) in t:1619exponent = 2**k1620ans = ans * A.Sq(exponent)1621for i in range(m-k):1622exponent = exponent * 21623ans = ans * A.Sq(exponent)16241625# pst...1626elif basis.find('pst') >= 0:1627if p == 2:1628# each entry in t is a pair (i,j), corresponding to P^i_j1629for (i,j) in t:1630ans = ans * A.pst(i,j)1631else:1632# t = (Q, P) where Q is the tuple of Q_i's, and P is a1633# tuple with entries of the form ((i,j), n),1634# corresponding to (P^i_j)^n1635if len(t[0]) > 0:1636ans = ans * A.Q(*t[0])1637for ((i, j), n) in t[1]:1638ans = ans * (A.pst(i,j))**n16391640# arnona[_long]1641elif basis.find('arnona') >= 0:1642# each entry in t is a pair (m,k), corresponding to X^m_k, defined by1643# `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`1644for (m,k) in t:1645exponent = 2**k1646X = A.Sq(exponent)1647for i in range(m-k):1648exponent = exponent * 21649X = A.Sq(exponent) * X1650ans = ans * X16511652# comm...[_long]1653elif basis.find('comm') >= 0:1654if p == 2:1655# each entry in t is a pair (i,j), corresponding to1656# c_{i,j}, the iterated commutator defined by c_{i,1}1657# = Sq(2^i) and c_{i,j} = [c_{i,j-1}, Sq(2^{i+j-1})].1658for (i,j) in t:1659comm = A.Sq(2**i)1660for k in range(2, j+1):1661y = A.Sq(2**(i+k-1))1662comm = comm * y + y * comm1663ans = ans * comm1664else:1665# t = (Q, P) where Q is the tuple of Q_i's, and P is a1666# tuple with entries of the form ((i,j), n),1667# corresponding to (c_{i,j})^n. Here c_{i,j} is the1668# iterated commutator defined by c_{i,1} = P(p^i) and1669# c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}].1670if len(t[0]) > 0:1671ans = ans * A.Q(*t[0])1672for ((i, j), n) in t[1]:1673comm = A.P(p**i)1674for k in range(2, j+1):1675y = A.P(p**(i+k-1))1676comm = y * comm - comm * y1677ans = ans * comm**n1678return ans16791680@lazy_attribute1681def milnor(self):1682"""1683Convert an element of this algebra to the Milnor basis16841685INPUT:16861687- x - an element of this algebra16881689OUTPUT: x converted to the Milnor basis16901691ALGORITHM: use the method ``_milnor_on_basis`` and linearity.16921693EXAMPLES::16941695sage: Adem = SteenrodAlgebra(basis='adem')1696sage: a = Adem.Sq(2) * Adem.Sq(1)1697sage: Adem.milnor(a)1698Sq(0,1) + Sq(3)1699"""1700A = SteenrodAlgebra(p=self.prime(), basis='milnor')1701return self._module_morphism(self._milnor_on_basis, codomain=A)17021703def _change_basis_on_basis(self, t, basis='milnor'):1704"""1705Convert the tuple t to the named basis.17061707INPUT:17081709- ``t`` - tuple, representing basis element in the current basis.17101711- ``basis`` - string, the basis to which to convert, optional1712(default 'milnor')17131714OUTPUT: an element of the Steenrod algebra with basis ``basis``.17151716ALGORITHM: it's straightforward to convert to the Milnor basis1717(using :meth:`milnor` or :meth:`_milnor_on_basis`), so it's1718straightforward to produce a matrix representing this1719conversion in any degree. The function1720:func:`convert_from_milnor_matrix1721<steenrod_algebra_bases.convert_from_milnor_matrix>` provides1722the inverse operation.17231724So: convert from the current basis to the Milnor basis, then1725from the Milnor basis to the new basis.17261727EXAMPLES::17281729sage: Adem = SteenrodAlgebra(basis='adem')1730sage: a = Adem({(2,1): 1}); a1731Sq^2 Sq^11732sage: a.change_basis('adem') # indirect doctest1733Sq^2 Sq^11734sage: a.change_basis('milnor')1735Sq(0,1) + Sq(3)1736sage: a.change_basis('pst')1737P^0_1 P^1_1 + P^0_21738sage: a.change_basis('milnor').change_basis('adem').change_basis('adem')1739Sq^2 Sq^11740sage: a.change_basis('wall') == a.change_basis('woody')1741True17421743TESTS::17441745sage: a = sum(SteenrodAlgebra(basis='comm').basis(10))1746sage: a.change_basis('adem').change_basis('wall').change_basis('comm')._repr_() == a._repr_()1747True1748sage: a.change_basis('pst').change_basis('milnor').change_basis('comm')._repr_() == a._repr_()1749True1750sage: a.change_basis('woody').change_basis('arnona').change_basis('comm')._repr_() == a._repr_()1751True17521753sage: b = sum(SteenrodAlgebra(p=3).basis(41))1754sage: b.change_basis('adem').change_basis('adem').change_basis('milnor')._repr_() == b._repr_()1755True1756"""1757from sage.matrix.constructor import matrix1758from sage.rings.all import GF1759from steenrod_algebra_bases import steenrod_algebra_basis,\1760convert_from_milnor_matrix1761from steenrod_algebra_misc import get_basis_name1762basis = get_basis_name(basis, self.prime())1763if basis == self.basis_name():1764return self({t: 1})1765a = self._milnor_on_basis(t)1766if basis == 'milnor':1767return a1768d = a.monomial_coefficients()1769p = self.prime()1770deg = a.degree()1771A = SteenrodAlgebra(basis=basis, p=p)1772if deg == 0:1773return A(a.leading_coefficient())1774Bnew = steenrod_algebra_basis(deg, basis, p)1775Bmil = steenrod_algebra_basis(deg, 'milnor', p)1776v = []1777for a in Bmil:1778v.append(d.get(a, 0))1779out = (matrix(GF(p), 1, len(v), v) *1780convert_from_milnor_matrix(deg, basis, p))1781new_d = dict(zip(Bnew, out[0]))1782return A(new_d)17831784def _change_basis(self, x, basis='milnor'):1785"""1786Convert an element of this algebra to the specified basis17871788INPUT:17891790- ``x`` - an element of this algebra.17911792- ``basis`` - string, the basis to which to convert, optional1793(default 'milnor')17941795OUTPUT: an element of the Steenrod algebra with basis ``basis``.17961797ALGORITHM: use :meth:`_change_basis_on_basis` and linearity17981799EXAMPLES::18001801sage: Adem = SteenrodAlgebra(basis='adem')1802sage: a = Adem({(2,1): 1}); a1803Sq^2 Sq^11804sage: a.change_basis('adem') # indirect doctest1805Sq^2 Sq^11806sage: a.change_basis('milnor')1807Sq(0,1) + Sq(3)1808sage: a.change_basis('pst')1809P^0_1 P^1_1 + P^0_21810"""1811if basis == 'milnor':1812return x.milnor()1813A = SteenrodAlgebra(p=self.prime(), basis=basis)1814change = lambda y: self._change_basis_on_basis(y, basis)1815f = self._module_morphism(change, codomain=A)1816return f(x)18171818def degree_on_basis(self, t):1819r"""1820The degree of the monomial specified by the tuple ``t``.18211822INPUT:18231824- ``t`` - tuple, representing basis element in the current basis.18251826OUTPUT: integer, the degree of the corresponding element.18271828The degree of `\text{Sq}(i_1,i_2,i_3,...)` is18291830.. math::18311832i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....18331834At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the1835degree of `\mathcal{P}(i_1, i_2, ...)` is18361837.. math::18381839\sum_{k \geq 0} 2(p^k - 1) i_k.18401841ALGORITHM: Each basis element is represented in terms relevant1842to the particular basis: 'milnor' basis elements (at the prime18432) are given by tuples ``(a,b,c,...)`` corresponding to the1844element `\text{Sq}(a,b,c,...)`, while 'pst' basis elements are1845given by tuples of pairs ``((a, b), (c, d), ...)``,1846corresponding to the product `P^a_b P^c_d ...`. The other1847bases have similar descriptions. The degree of each basis1848element is computed from this data, rather than converting the1849element to the Milnor basis, for example, and then computing1850the degree.18511852EXAMPLES::18531854sage: SteenrodAlgebra().degree_on_basis((0,0,1))185571856sage: Sq(7).degree()1857718581859sage: A11 = SteenrodAlgebra(p=11)1860sage: A11.degree_on_basis(((), (1,1)))18612601862sage: A11.degree_on_basis(((2,), ()))18632411864"""1865def p_degree(m, mult=1, prime=2):1866"""1867For m=(n_1, n_2, n_3, ...), Sum_i (mult) * n_i * (p^i - 1)1868"""1869i = 01870deg = 01871for n in m:1872i += 11873deg += n*mult*(prime**i - 1)1874return deg18751876def q_degree(m, prime=3):1877"""1878For m=(n_0, n_1, n_2, ...), Sum_i 2*p^(n_i) - 11879"""1880deg = 01881for n in m:1882deg += 2*prime**n - 11883return deg18841885p = self.prime()1886basis = self.basis_name()1887# milnor1888if basis == 'milnor':1889if p == 2:1890return p_degree(t)1891else:1892return q_degree(t[0], prime=p) + p_degree(t[1], prime=p, mult=2)1893# serre-cartan, arnonc1894if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):1895return sum(t)1896if p > 2 and basis == 'serre-cartan':1897bockstein = True1898n = 01899for j in t:1900if bockstein:1901if j != 0:1902n += 11903bockstein = False1904else:1905n += 2 * j * (p - 1)1906bockstein = True1907return n19081909# wood_y:1910if basis == 'woody' or basis == 'woodz':1911# each entry in t is a pair (m,k), corresponding to w(m,k), defined by1912# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.1913return sum(2**m * (2**(k+1)-1) for (m,k) in t)19141915# wall, arnon_a1916if basis.find('wall') >= 0 or basis.find('arnona') >= 0:1917# Wall: each entry in t is a pair (m,k), corresponding to1918# Q^m_k, defined by `Q^m_k = Sq(2^k) Sq(2^{k+1})1919# ... Sq(2^m)`.1920#1921# Arnon A: each entry in t is a pair (m,k), corresponding1922# to X^m_k, defined by `X^m_k = Sq(2^m) ... Sq(2^{k+1})1923# Sq(2^k)`1924return sum(2**k * (2**(m-k+1)-1) for (m,k) in t)19251926# pst, comm1927if basis.find('pst') >= 0 or basis.find('comm') >= 0:1928if p == 2:1929# Pst: each entry in t is a pair (i,j), corresponding to P^i_j1930#1931# Comm: each entry in t is a pair (i,j), corresponding1932# to c_{i,j}, the iterated commutator defined by1933# c_{i,1} = Sq(2^i) and c_{i,j} = [c_{i,j-1},1934# Sq(2^{i+j-1})].1935return sum(2**m * (2**k - 1) for (m,k) in t)1936# p odd:1937#1938# Pst: have pair (Q, P) where Q is a tuple of Q's, as in1939# the Milnor basis, and P is a tuple of terms of the form1940# ((i,j), n), corresponding to (P^i_j)^n.1941#1942# Comm: similarly (Q, C) with Q as above and C a tuple1943# with each entry in t is of the form ((s,t), n),1944# corresponding to c_{s,t}^n. here c_{s,t} is the the1945# iterated commutator defined by c_{s,1} = P(p^s) and1946# c_{s,t} = [P(p^{s+t-1}), c_{s,t-1}].1947q_deg = q_degree(t[0], prime=p)1948p_deg = sum(2 * n * p**s * (p**t - 1) for ((s,t), n) in t[1])1949return q_deg + p_deg19501951# coercion methods:19521953def _coerce_map_from_(self, S):1954r"""1955True if there is a coercion from ``S`` to ``self``, False otherwise.19561957INPUT:19581959- ``S`` - a Sage object.19601961The algebras that coerce into the mod p Steenrod algebra are:19621963- the mod p Steenrod algebra `A`1964- its sub-Hopf algebras1965- its homogeneous components1966- its base field `GF(p)`1967- `ZZ`19681969Similarly, a sub-Hopf algebra `B` of `A` coerces into another1970sub-Hopf algebra `C` if and only if the profile function for1971`B` is less than or equal to that of `C`, pointwise.19721973EXAMPLES::19741975sage: A = SteenrodAlgebra()1976sage: A1 = SteenrodAlgebra(profile=[2,1])1977sage: A2 = SteenrodAlgebra(profile=[3,2,1])1978sage: B = SteenrodAlgebra(profile=[1,2,1])1979sage: A._coerce_map_from_(A1)1980True1981sage: A2._coerce_map_from_(A1)1982True1983sage: A1._coerce_map_from_(A)1984False1985sage: A1._coerce_map_from_(B)1986False1987sage: B._coerce_map_from_(A1)1988False19891990sage: A._coerce_map_from_(A[12])1991True19921993sage: A3 = SteenrodAlgebra(p=3)1994sage: A31 = SteenrodAlgebra(p=3, profile=([1], [2, 2]))1995sage: B3 = SteenrodAlgebra(p=3, profile=([1, 2, 1], [1]))1996sage: A3._coerce_map_from_(A31)1997True1998sage: A31._coerce_map_from_(A3)1999False2000sage: A31._coerce_map_from_(B3)2001False2002sage: B3._coerce_map_from_(A31)2003False2004"""2005from sage.rings.all import ZZ, GF2006from sage.rings.infinity import Infinity2007p = self.prime()2008if S == ZZ or S == GF(p):2009return True2010if (isinstance(S, SteenrodAlgebra_generic) and p == S.prime()):2011# deal with profiles.2012if p == 2:2013self_prec = len(self._profile)2014S_prec = len(S._profile)2015return all([self.profile(i) >= S.profile(i)2016for i in range(1, max(self_prec, S_prec)+1)])2017self_prec = len(self._profile[0])2018S_prec = len(S._profile[0])2019return (all([self.profile(i) >= S.profile(i)2020for i in range(1, max(self_prec, S_prec)+1)])2021and all([self.profile(i, 1) >= S.profile(i, 1)2022for i in range(1, max(self_prec, S_prec)+1)]))2023if (isinstance(S, CombinatorialFreeModule)2024and S.dimension() < Infinity and p == S.base_ring().characteristic()):2025from steenrod_algebra_misc import get_basis_name2026try:2027get_basis_name(S.prefix(), S.base_ring().characteristic())2028# return all([a in self for a in S.basis()])2029return True2030except ValueError:2031return False2032return False20332034def _element_constructor_(self, x):2035r"""2036Try to turn ``x`` into an element of ``self``.20372038INPUT:20392040- ``x`` - an element of some Steenrod algebra or an element of2041`\ZZ` or `\GF{p}` or a dict20422043OUTPUT: ``x`` as a member of ``self``.20442045If ``x`` is a dict, then call :meth:`_from_dict` on it,2046coercing the coefficients into the base field. That is, treat2047it as having entries of the form ``tuple: coeff``, where2048``tuple`` is a tuple representing a basis element and2049``coeff`` is the coefficient of that element.20502051EXAMPLES::20522053sage: A1 = SteenrodAlgebra(profile=[2,1])2054sage: A1(Sq(2)) # indirect doctest2055Sq(2)2056sage: A1._element_constructor_(Sq(2))2057Sq(2)2058sage: A1(3) # map integer into A1205912060sage: A1._element_constructor_(Sq(4)) # Sq(4) not in A12061Traceback (most recent call last):2062...2063ValueError: Element does not lie in this Steenrod algebra2064sage: A1({(2,): 1, (1,): 13})2065Sq(1) + Sq(2)2066"""2067from sage.rings.all import ZZ, GF2068if x in GF(self.prime()) or x in ZZ:2069return self.from_base_ring_from_one_basis(x)20702071if isinstance(x, dict):2072A = SteenrodAlgebra(p=self.prime(), basis=self.basis_name())2073x = A._from_dict(x, coerce=True)2074if x in self:2075if x.basis_name() == self.basis_name():2076if x.parent() is self:2077return x2078return self._from_dict(x.monomial_coefficients(), coerce=True)2079else:2080a = x.milnor()2081if self.basis_name() == 'milnor':2082return a2083return a.change_basis(self.basis_name())2084raise ValueError("Element does not lie in this Steenrod algebra")20852086def __contains__(self, x):2087r"""2088True if self contains x.20892090EXAMPLES::20912092sage: Sq(3,1,1) in SteenrodAlgebra()2093True2094sage: Sq(3,1,1) in SteenrodAlgebra(p=5)2095False20962097sage: A1 = SteenrodAlgebra(profile=[2,1])2098sage: Sq(3) in A12099True2100sage: Sq(4) in A12101False2102sage: Sq(0,2) in A12103False21042105sage: A_3 = SteenrodAlgebra(p=3)2106sage: B_3 = SteenrodAlgebra(p=3, profile=([1], [2,2,1,1]))2107sage: A_3.P(2) in B_32108True2109sage: A_3.P(3) in B_32110False2111sage: A_3.Q(1) in B_32112True2113sage: A_3.P(1) * A_3.Q(2) in B_32114False2115"""2116from sage.rings.all import GF2117p = self.prime()2118if (GF(p).__contains__(x)):2119return True2120if (isinstance(x, self.Element)2121and x.prime() == p):2122A = SteenrodAlgebra(p=p, basis=self.basis_name())2123if self._has_nontrivial_profile():2124return all([self._check_profile_on_basis(mono)2125for mono in A(x).support()])2126return True # trivial profile, so True2127return False21282129def basis(self, d=None):2130"""2131Returns basis for self, either the whole basis or the basis in2132degree `d`.21332134INPUT:21352136- `d` - integer or None, optional (default None)21372138OUTPUT: If `d` is None, then return a basis of the algebra.2139Otherwise, return the basis in degree `d`.21402141EXAMPLES::21422143sage: A3 = SteenrodAlgebra(3)2144sage: A3.basis(13)2145Family (Q_1 P(2), Q_0 P(3))2146sage: SteenrodAlgebra(2, 'adem').basis(12)2147Family (Sq^12, Sq^11 Sq^1, Sq^9 Sq^2 Sq^1, Sq^8 Sq^3 Sq^1, Sq^10 Sq^2, Sq^9 Sq^3, Sq^8 Sq^4)21482149sage: A = SteenrodAlgebra(profile=[1,2,1])2150sage: A.basis(2)2151Family ()2152sage: A.basis(3)2153Family (Sq(0,1),)2154sage: SteenrodAlgebra().basis(3)2155Family (Sq(0,1), Sq(3))2156sage: A_pst = SteenrodAlgebra(profile=[1,2,1], basis='pst')2157sage: A_pst.basis(3)2158Family (P^0_2,)21592160sage: A7 = SteenrodAlgebra(p=7)2161sage: B = SteenrodAlgebra(p=7, profile=([1,2,1], [1]))2162sage: A7.basis(84)2163Family (P(7),)2164sage: B.basis(84)2165Family ()2166sage: C = SteenrodAlgebra(p=7, profile=([1], [2,2]))2167sage: A7.Q(0,1) in C.basis(14)2168True2169sage: A7.Q(2) in A7.basis(97)2170True2171sage: A7.Q(2) in C.basis(97)2172False21732174With no arguments, return the basis of the whole algebra.2175This doesn't print in a very helpful way, unfortunately::21762177sage: A7.basis()2178Lazy family (Term map from basis key family of mod 7 Steenrod algebra, milnor basis to mod 7 Steenrod algebra, milnor basis(i))_{i in basis key family of mod 7 Steenrod algebra, milnor basis}2179sage: for (idx,a) in zip((1,..,9),A7.basis()):2180... print idx, a21811 121822 Q_021833 P(1)21844 Q_121855 Q_0 P(1)21866 Q_0 Q_121877 P(2)21888 Q_1 P(1)21899 Q_0 P(2)2190sage: D = SteenrodAlgebra(p=3, profile=([1], [2,2]))2191sage: sorted(D.basis())2192[1, P(1), P(2), Q_0, Q_0 P(1), Q_0 P(2), Q_0 Q_1, Q_0 Q_1 P(1), Q_0 Q_1 P(2), Q_1, Q_1 P(1), Q_1 P(2)]2193"""2194from sage.sets.family import Family2195if d is None:2196return Family(self._basis_keys, self.monomial)2197else:2198return Family([self.monomial(tuple(a)) for a in self._basis_fcn(d)])21992200def _check_profile_on_basis(self, t):2201"""2202True if the element specified by the tuple ``t`` is in this2203algebra.22042205INPUT:22062207- ``t`` - tuple of ...220822092210EXAMPLES::22112212sage: A = SteenrodAlgebra(profile=[1,2,1])2213sage: A._check_profile_on_basis((0,0,1))2214True2215sage: A._check_profile_on_basis((0,0,2))2216False2217sage: A5 = SteenrodAlgebra(p=5, profile=([3,2,1], [2,2,2,2,2]))2218sage: A5._check_profile_on_basis(((), (1,5)))2219True2220sage: A5._check_profile_on_basis(((1,1,1), (1,5)))2221True2222sage: A5._check_profile_on_basis(((1,1,1), (1,5,5)))2223False2224"""2225if self.basis_name() != 'milnor':2226A = SteenrodAlgebra(p=self.prime(),2227profile=self._profile,2228truncation_type=self._truncation_type)2229return all([A._check_profile_on_basis(a[0])2230for a in self._milnor_on_basis(t)])22312232from sage.rings.infinity import Infinity2233p = self.prime()2234if not self._has_nontrivial_profile():2235return True2236if p == 2:2237return all([self.profile(i+1) == Infinity2238or t[i] < 2**self.profile(i+1)2239for i in range(len(t))])2240# p odd:2241if any([self.profile(i,1) != 2 for i in t[0]]):2242return False2243return all([self.profile(i+1,0) == Infinity2244or t[1][i] < p**self.profile(i+1,0)2245for i in range(len(t[1]))])22462247def P(self, *nums):2248r"""2249The element `P(a, b, c, ...)`22502251INPUT:22522253- ``a, b, c, ...`` - non-negative integers22542255OUTPUT: element of the Steenrod algebra given by the Milnor2256single basis element `P(a, b, c, ...)`22572258Note that at the prime 2, this is the same element as2259`\text{Sq}(a, b, c, ...)`.22602261EXAMPLES::22622263sage: A = SteenrodAlgebra(2)2264sage: A.P(5)2265Sq(5)2266sage: B = SteenrodAlgebra(3)2267sage: B.P(5,1,1)2268P(5,1,1)2269sage: B.P(1,1,-12,1)2270Traceback (most recent call last):2271...2272TypeError: entries must be non-negative integers22732274sage: SteenrodAlgebra(basis='serre-cartan').P(0,1)2275Sq^2 Sq^1 + Sq^32276"""2277from sage.rings.all import Integer2278if self.basis_name() != 'milnor':2279return self(SteenrodAlgebra(p=self.prime()).P(*nums))2280while len(nums) > 0 and nums[-1] == 0:2281nums = nums[:-1]2282if len(nums) == 0 or (len(nums) == 1 and nums[0] == 0):2283return self.one()2284for i in nums:2285try:2286assert Integer(i) >= 02287except (TypeError, AssertionError):2288raise TypeError("entries must be non-negative integers")22892290if self.prime() == 2:2291t = nums2292else:2293t = ((), nums)2294if self._check_profile_on_basis(t):2295A = SteenrodAlgebra_generic(p=self.prime())2296a = A.monomial(t)2297return self(a)2298raise ValueError("Element not in this algebra")22992300def Q_exp(self, *nums):2301r"""2302The element `Q_0^{e_0} Q_1^{e_1} ...` , given by2303specifying the exponents.23042305INPUT:23062307- ``e0, e1, ...`` - sequence of 0s and 1s23082309OUTPUT: The element `Q_0^{e_0} Q_1^{e_1} ...`23102311Note that at the prime 2, `Q_n` is the element2312`\text{Sq}(0,0,...,1)` , where the 1 is in the2313`(n+1)^{st}` position.23142315Compare this to the method :meth:`Q`, which defines a similar2316element, but by specifying the tuple of subscripts of terms2317with exponent 1.23182319EXAMPLES::23202321sage: A2 = SteenrodAlgebra(2)2322sage: A5 = SteenrodAlgebra(5)2323sage: A2.Q_exp(0,0,1,1,0)2324Sq(0,0,1,1)2325sage: A5.Q_exp(0,0,1,1,0)2326Q_2 Q_32327sage: A5.Q(2,3)2328Q_2 Q_32329sage: A5.Q_exp(0,0,1,1,0) == A5.Q(2,3)2330True2331"""2332if not set(nums).issubset(set((0,1))):2333raise ValueError("The tuple %s should consist " % (nums,) + \2334"only of 0's and 1's")2335else:2336if self.basis_name() != 'milnor':2337return self(SteenrodAlgebra(p=self.prime()).Q_exp(*nums))2338while nums[-1] == 0:2339nums = nums[:-1]2340if self.prime() == 2:2341return self.P(*nums)2342else:2343mono = ()2344index = 02345for e in nums:2346if e == 1:2347mono = mono + (index,)2348index += 12349return self.Q(*mono)23502351def Q(self, *nums):2352r"""2353The element `Q_{n0} Q_{n1} ...` , given by specifying the2354subscripts.23552356INPUT:23572358- ``n0, n1, ...`` - non-negative integers23592360OUTPUT: The element `Q_{n0} Q_{n1} ...`23612362Note that at the prime 2, `Q_n` is the element2363`\text{Sq}(0,0,...,1)` , where the 1 is in the2364`(n+1)^{st}` position.23652366Compare this to the method :meth:`Q_exp`, which defines a2367similar element, but by specifying the tuple of exponents.23682369EXAMPLES::23702371sage: A2 = SteenrodAlgebra(2)2372sage: A2.Q(2,3)2373Sq(0,0,1,1)2374sage: A5 = SteenrodAlgebra(5)2375sage: A5.Q(1,4)2376Q_1 Q_42377sage: A5.Q(1,4) == A5.Q_exp(0,1,0,0,1)2378True2379sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])2380sage: H.Q(2)2381Q_22382sage: H.Q(4)2383Traceback (most recent call last):2384...2385ValueError: Element not in this algebra2386"""2387if len(nums) != len(set(nums)):2388return self(0)2389else:2390if self.basis_name() != 'milnor':2391return self(SteenrodAlgebra(p=self.prime()).Q(*nums))2392if self.prime() == 2:2393if len(nums) == 0:2394return self.one()2395else:2396list = (1+max(nums)) * [0]2397for i in nums:2398list[i] = 12399return self.Sq(*tuple(list))2400else:2401answer = self.one()2402for i in nums:2403answer = answer * self.monomial(((i,), ()))2404t = answer.leading_support()2405if self._check_profile_on_basis(t):2406return answer2407raise ValueError("Element not in this algebra")24082409def an_element(self):2410"""2411An element of this Steenrod algebra. The element depends on2412the basis and whether there is a nontrivial profile function.2413(This is used by the automatic test suite, so having different2414elements in different bases may help in discovering bugs.)24152416EXAMPLES::24172418sage: SteenrodAlgebra().an_element()2419Sq(2,1)2420sage: SteenrodAlgebra(basis='adem').an_element()2421Sq^4 Sq^2 Sq^12422sage: SteenrodAlgebra(p=5).an_element()24234 Q_1 Q_3 P(2,1)2424sage: SteenrodAlgebra(basis='pst').an_element()2425P^3_12426sage: SteenrodAlgebra(basis='pst', profile=[3,2,1]).an_element()2427P^0_12428"""2429from sage.rings.all import GF2430basis = self.basis_name()2431p = self.prime()24322433if self._has_nontrivial_profile():2434if self.ngens() > 0:2435return self.gen(0)2436else:2437return self.one()24382439if basis == 'milnor' and p == 2:2440return self.monomial((2,1))2441if basis == 'milnor' and p > 2:2442return self.term(((1,3), (2,1)), GF(p)(p-1))2443if basis == 'serre-cartan' and p == 2:2444return self.monomial((4,2,1))2445if basis == 'serre-cartan' and p > 2:2446return self.term((1,p,0,1,0), GF(p)(p-1))2447if basis == 'woody' or basis == 'woodz':2448return self._from_dict({((3,0),): 1, ((1, 1), (1, 0)): 1}, coerce=True)2449if basis.find('wall') >= 0:2450return self._from_dict({((1,1), (1,0)): 1, ((2, 2), (0, 0)): 1}, coerce=True)2451if basis.find('arnona') >= 0:2452return self._from_dict({((3,3),): 1, ((1, 1), (2, 1)): 1}, coerce=True)2453if basis == 'arnonc':2454return self._from_dict({(8,): 1, (4, 4): 1}, coerce=True)2455if basis.find('pst') >= 0:2456if p == 2:2457return self.monomial(((3, 1),))2458return self.term(((1,), (((1,1), 2),)), GF(p)(p-1))2459if basis.find('comm') >= 0:2460if p == 2:2461return self.monomial(((1, 2),))2462return self.term(((), (((1,2), 1),)), GF(p)(p-1))24632464def pst(self,s,t):2465r"""2466The Margolis element `P^s_t`.24672468INPUT:24692470- ``s`` - non-negative integer24712472- ``t`` - positive integer24732474- ``p`` - positive prime number24752476OUTPUT: element of the Steenrod algebra24772478This returns the Margolis element `P^s_t` of the mod2479`p` Steenrod algebra: the element equal to2480`P(0,0,...,0,p^s)`, where the `p^s` is in position2481`t`.24822483EXAMPLES::24842485sage: A2 = SteenrodAlgebra(2)2486sage: A2.pst(3,5)2487Sq(0,0,0,0,8)2488sage: A2.pst(1,2) == Sq(4)*Sq(2) + Sq(2)*Sq(4)2489True2490sage: SteenrodAlgebra(5).pst(3,5)2491P(0,0,0,0,125)2492"""2493from sage.rings.all import Integer2494if self.basis_name() != 'milnor':2495return self(SteenrodAlgebra(p=self.prime()).pst(s,t))2496if not isinstance(s, (Integer, int)) and s >= 0:2497raise ValueError("%s is not a non-negative integer" % s)2498if not isinstance(t, (Integer, int)) and t > 0:2499raise ValueError("%s is not a positive integer" % t)2500nums = (0,)*(t-1) + (self.prime()**s,)2501return self.P(*nums)25022503def ngens(self):2504r"""2505Number of generators of self.25062507OUTPUT: number or Infinity25082509The Steenrod algebra is infinitely generated. A sub-Hopf2510algebra may be finitely or infinitely generated; in general,2511it is not clear what a minimal generating set is, nor the2512cardinality of that set. So: if the algebra is2513infinite-dimensional, this returns Infinity. If the algebra2514is finite-dimensional and is equal to one of the sub-Hopf2515algebras `A(n)`, then their minimal generating set is known,2516and this returns the cardinality of that set. Otherwise, any2517sub-Hopf algebra is (not necessarily minimally) generated by2518the `P^s_t`'s that it contains (along with the `Q_n`'s it2519contains, at odd primes), so this returns the number of2520`P^s_t`'s and `Q_n`'s in the algebra.25212522EXAMPLES::25232524sage: A = SteenrodAlgebra(3)2525sage: A.ngens()2526+Infinity2527sage: SteenrodAlgebra(profile=lambda n: n).ngens()2528+Infinity2529sage: SteenrodAlgebra(profile=[3,2,1]).ngens() # A(2)253032531sage: SteenrodAlgebra(profile=[3,2,1], basis='pst').ngens()253232533sage: SteenrodAlgebra(p=3, profile=[[3,2,1], [2,2,2,2]]).ngens() # A(3) at p=3253442535sage: SteenrodAlgebra(profile=[1,2,1,1]).ngens()253652537"""2538from sage.rings.infinity import Infinity2539if self._truncation_type == Infinity:2540return Infinity2541n = self.profile(1)2542p = self.prime()2543if p == 2 and self._profile == AA(n-1, p=p)._profile:2544return n2545if p > 2 and self._profile == AA(n, p=p)._profile:2546return n+12547if p == 2:2548return sum(self._profile)2549return sum(self._profile[0]) + len([a for a in self._profile[1] if a == 2])25502551def gens(self):2552r"""2553Family of generators for this algebra.25542555OUTPUT: family of elements of this algebra25562557At the prime 2, the Steenrod algebra is generated by the2558elements `\text{Sq}^{2^i}` for `i \geq 0`. At odd primes, it2559is generated by the elements `Q_0` and `\mathcal{P}^{p^i}` for2560`i \geq 0`. So if this algebra is the entire Steenrod2561algebra, return an infinite family made up of these elements.25622563For sub-Hopf algebras of the Steenrod algebra, it is not2564always clear what a minimal generating set is. The sub-Hopf2565algebra `A(n)` is minimally generated by the elements2566`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At2567odd primes, `A(n)` is minimally generated by `Q_0` along with2568`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this2569algebra is `A(n)`, return the appropriate list of generators.25702571For other sub-Hopf algebras: return a non-minimal generating2572set: the family of `P^s_t`'s and `Q_n`'s contained in the2573algebra.25742575EXAMPLES::25762577sage: A3 = SteenrodAlgebra(3, 'adem')2578sage: A3.gens()2579Lazy family (<bound method SteenrodAlgebra_generic_with_category.gen of mod 3 Steenrod algebra, serre-cartan basis>(i))_{i in Non negative integers}2580sage: A3.gens()[0]2581beta2582sage: A3.gens()[1]2583P^12584sage: A3.gens()[2]2585P^32586sage: SteenrodAlgebra(profile=[3,2,1]).gens()2587Family (Sq(1), Sq(2), Sq(4))25882589In the following case, return a non-minimal generating set.2590(It is not minimal because `\text{Sq}(0,0,1)` is the2591commutator of `\text{Sq}(1)` and `\text{Sq}(0,2)`.) ::25922593sage: SteenrodAlgebra(profile=[1,2,1]).gens()2594Family (Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1))2595sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).gens()2596Family (Q_0, P(1), P(5))2597sage: SteenrodAlgebra(profile=lambda n: n).gens()2598Lazy family (<bound method SteenrodAlgebra_mod_two_with_category.gen of sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, 3, ..., 98, 99, +Infinity, +Infinity, +Infinity, ...]>(i))_{i in Non negative integers}25992600You may also use ``algebra_generators`` instead of ``gens``::26012602sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).algebra_generators()2603Family (Q_0, P(1), P(5))2604"""2605from sage.sets.family import Family2606from sage.sets.non_negative_integers import NonNegativeIntegers2607from sage.rings.infinity import Infinity2608n = self.ngens()2609if n < Infinity:2610return Family([self.gen(i) for i in range(n)])2611return Family(NonNegativeIntegers(), self.gen)26122613algebra_generators = gens26142615def gen(self, i=0):2616r"""2617The ith generator of this algebra.26182619INPUT:26202621- ``i`` - non-negative integer26222623OUTPUT: the ith generator of this algebra26242625For the full Steenrod algebra, the `i^{th}` generator is2626`\text{Sq}(2^i)` at the prime 2; when `p` is odd, the 0th generator2627is `\beta = Q(0)`, and for `i>0`, the `i^{th}` generator is2628`P(p^{i-1})`.26292630For sub-Hopf algebras of the Steenrod algebra, it is not2631always clear what a minimal generating set is. The sub-Hopf2632algebra `A(n)` is minimally generated by the elements2633`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At2634odd primes, `A(n)` is minimally generated by `Q_0` along with2635`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this2636algebra is `A(n)`, return the appropriate generator.26372638For other sub-Hopf algebras: they are generated (but not2639necessarily minimally) by the `P^s_t`'s (and `Q_n`'s, if `p`2640is odd) that they contain. So order the `P^s_t`'s (and2641`Q_n`'s) in the algebra by degree and return the `i`-th one.26422643EXAMPLES::26442645sage: A = SteenrodAlgebra(2)2646sage: A.gen(4)2647Sq(16)2648sage: A.gen(200)2649Sq(1606938044258990275541962092341162602522202993782792835301376)2650sage: SteenrodAlgebra(2, basis='adem').gen(2)2651Sq^42652sage: SteenrodAlgebra(2, basis='pst').gen(2)2653P^2_12654sage: B = SteenrodAlgebra(5)2655sage: B.gen(0)2656Q_02657sage: B.gen(2)2658P(5)26592660sage: SteenrodAlgebra(profile=[2,1]).gen(1)2661Sq(2)2662sage: SteenrodAlgebra(profile=[1,2,1]).gen(1)2663Sq(0,1)2664sage: SteenrodAlgebra(profile=[1,2,1]).gen(5)2665Traceback (most recent call last):2666...2667ValueError: This algebra only has 4 generators, so call gen(i) with 0 <= i < 426682669sage: D = SteenrodAlgebra(profile=lambda n: n)2670sage: [D.gen(n) for n in range(5)]2671[Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1), Sq(0,0,2)]2672sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 2))2673sage: [D3.gen(n) for n in range(9)]2674[Q_0, P(1), Q_1, P(0,1), Q_2, P(0,3), P(0,0,1), Q_3, P(0,0,3)]2675sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1 if n<1 else 2))2676sage: [D3.gen(n) for n in range(9)]2677[P(1), Q_1, P(0,1), Q_2, P(0,3), P(0,0,1), Q_3, P(0,0,3), P(0,0,0,1)]2678sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst').gen(2)2679P^1_12680"""2681from sage.rings.infinity import Infinity2682from sage.rings.all import Integer2683p = self.prime()2684if not isinstance(i, (Integer, int)) and i >= 0:2685raise ValueError("%s is not a non-negative integer" % i)2686num = self.ngens()2687if num < Infinity:2688if i >= num:2689raise ValueError("This algebra only has %s generators, so call gen(i) with 0 <= i < %s" % (num, num))2690# check to see if equal to A(n) for some n.2691n = self.profile(1)2692if p == 2 and self._profile == AA(n-1, p=p)._profile:2693return self.pst(i,1)2694if p > 2 and self._profile == AA(n, p=p)._profile:2695if i == 0:2696return self.Q(0)2697return self.pst(i-1, 1)2698# if not A(n), return list of P^s_t's in algebra, along with Q's if p is odd2699idx = -12700if p == 2:2701last_t = len(self._profile)2702else:2703last_t = max(len(self._profile[0]), len(self._profile[1]))2704last_s = self.profile(last_t)2705for j in range(1, last_s + last_t + 1):2706if p > 2 and self.profile(j-1, 1) == 2:2707guess = self.Q(j-1)2708idx += 12709if idx == i:2710elt = guess2711break2712for t in range(1, min(j, last_t) + 1):2713s = j - t2714if self.profile(t) > s:2715guess = self.pst(s,t)2716idx += 12717if idx == i:2718elt = guess2719break2720return elt27212722# entire Steenrod algebra:2723if self.profile(1) == Infinity:2724if p == 2:2725return self.Sq(p**i)2726elif self.profile(0,1) == 2:2727if i == 0:2728return self.Q(0)2729else:2730return self.P(p**(i-1))27312732# infinite-dimensional sub-Hopf algebra2733idx = -12734tot = 12735found = False2736A = SteenrodAlgebra(p=p)2737while not found:2738if p > 2:2739test = A.Q(tot-1)2740if test in self:2741idx += 12742if idx == i:2743found = True2744break2745for t in range(1, tot+1):2746s = tot - t2747test = A.pst(s,t)2748if test in self:2749idx += 12750if idx == i:2751found = True2752break2753tot += 12754return test27552756def is_commutative(self):2757r"""2758True if ``self`` is graded commutative, as determined by the2759profile function. In particular, a sub-Hopf algebra of the2760mod 2 Steenrod algebra is commutative if and only if there is2761an integer `n>0` so that its profile function `e` satisfies27622763- `e(i) = 0` for `i < n`,2764- `e(i) \leq n` for `i \geq n`.27652766When `p` is odd, there must be an integer `n \geq 0` so that2767the profile functions `e` and `k` satisfy27682769- `e(i) = 0` for `i < n`,2770- `e(i) \leq n` for `i \geq n`.2771- `k(i) = 1` for `i < n`.27722773EXAMPLES::27742775sage: A = SteenrodAlgebra(p=3)2776sage: A.is_commutative()2777False2778sage: SteenrodAlgebra(profile=[2,1]).is_commutative()2779False2780sage: SteenrodAlgebra(profile=[0,2,2,1]).is_commutative()2781True27822783Note that if the profile function is specified by a function,2784then by default it has infinite truncation type: the profile2785function is assumed to be infinite after the 100th term. ::27862787sage: SteenrodAlgebra(profile=lambda n: 1).is_commutative()2788False2789sage: SteenrodAlgebra(profile=lambda n: 1, truncation_type=0).is_commutative()2790True27912792sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [])).is_commutative()2793True2794sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [1,1,2])).is_commutative()2795True2796sage: SteenrodAlgebra(p=5, profile=([0,2,1], [1,2,2,2])).is_commutative()2797False2798"""2799if not self._has_nontrivial_profile() or self._truncation_type > 0:2800return False2801p = self.prime()2802if p == 2:2803n = max(self._profile)2804return all([self.profile(i) == 0 for i in range(1, n)])2805n = max(self._profile[0])2806return (all([self.profile(i,0) == 0 for i in range(1, n)])2807and all([self.profile(i,1) == 1 for i in range(n)]))28082809def is_finite(self):2810r"""2811True if this algebra is finite-dimensional.28122813Therefore true if the profile function is finite, and in2814particular the ``truncation_type`` must be finite.28152816EXAMPLES::28172818sage: A = SteenrodAlgebra(p=3)2819sage: A.is_finite()2820False2821sage: SteenrodAlgebra(profile=[3,2,1]).is_finite()2822True2823sage: SteenrodAlgebra(profile=lambda n: n).is_finite()2824False2825"""2826return self._has_nontrivial_profile() and self._truncation_type == 028272828def dimension(self):2829r"""2830The dimension of this algebra as a vector space over `\GF{p}`.28312832If the algebra is infinite, return ``+Infinity``. Otherwise,2833the profile function must be finite. In this case, at the2834prime 2, its dimension is `2^s`, where `s` is the sum of the2835entries in the profile function. At odd primes, the dimension2836is `p^s * 2^t` where `s` is the sum of the `e` component of2837the profile function and `t` is the number of 2's in the `k`2838component of the profile function.28392840EXAMPLES::28412842sage: SteenrodAlgebra(p=7).dimension()2843+Infinity2844sage: SteenrodAlgebra(profile=[3,2,1]).dimension()2845642846sage: SteenrodAlgebra(p=3, profile=([1,1], [])).dimension()284792848sage: SteenrodAlgebra(p=5, profile=([1], [2,2])).dimension()2849202850"""2851from sage.rings.infinity import Infinity2852if not self.is_finite():2853return Infinity2854p = self.prime()2855if p == 2:2856return 2**sum(self._profile)2857return p**sum(self._profile[0]) * 2**len([a for a in self._profile[1] if a == 2])28582859@cached_method2860def top_class(self):2861r"""2862Highest dimensional basis element. This is only defined if the algebra is finite.28632864EXAMPLES::28652866sage: SteenrodAlgebra(2,profile=(3,2,1)).top_class()2867Sq(7,3,1)2868sage: SteenrodAlgebra(3,profile=((2,2,1),(1,2,2,2,2))).top_class()2869Q_1 Q_2 Q_3 Q_4 P(8,8,2)28702871TESTS::28722873sage: SteenrodAlgebra(2,profile=(3,2,1),basis='pst').top_class()2874P^0_1 P^0_2 P^1_1 P^0_3 P^1_2 P^2_12875sage: SteenrodAlgebra(5,profile=((0,),(2,1,2,2))).top_class()2876Q_0 Q_2 Q_32877sage: SteenrodAlgebra(5).top_class()2878Traceback (most recent call last):2879...2880ValueError: the algebra is not finite dimensional28812882Currently, we create the top class in the Milnor basis version and transform2883this result back into the requested basis. This approach is easy to implement2884but far from optimal for the 'pst' basis. Occasionally, it also gives an awkward2885leading coefficient::28862887sage: SteenrodAlgebra(3,profile=((2,1),(1,2,2)),basis='pst').top_class()28882 Q_1 Q_2 (P^0_1)^2 (P^0_2)^2 (P^1_1)^228892890TESTS::28912892sage: A=SteenrodAlgebra(2,profile=(3,2,1),basis='pst')2893sage: A.top_class().parent() is A2894True2895"""2896if not self.is_finite():2897raise ValueError, "the algebra is not finite dimensional"2898p = self.prime()2899# we create the top class in the Milnor basis version2900AM = SteenrodAlgebra(basis='milnor', p=p)2901if p==2:2902ans = AM.monomial(tuple((1<<k)-1 for k in self._profile))2903else:2904rp,ep = self._profile2905e = [kk for kk in range(0,len(ep)) if ep[kk]==2]2906r = [p**kk-1 for kk in rp]2907ans = AM.monomial((tuple(e),tuple(r)))2908return self(ans.change_basis(self.basis_name()))29092910def order(self):2911r"""2912The order of this algebra.29132914This is computed by computing its vector space dimension `d`2915and then returning `p^d`.29162917EXAMPLES::29182919sage: SteenrodAlgebra(p=7).order()2920+Infinity2921sage: SteenrodAlgebra(profile=[2,1]).dimension()292282923sage: SteenrodAlgebra(profile=[2,1]).order()29242562925sage: SteenrodAlgebra(p=3, profile=([1], [])).dimension()292632927sage: SteenrodAlgebra(p=3, profile=([1], [])).order()2928272929sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).dimension()293042931sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).order() == 5**42932True2933"""2934from sage.rings.infinity import Infinity2935if not self.is_finite():2936return Infinity2937return self.prime() ** self.dimension()29382939def is_division_algebra(self):2940r"""2941The only way this algebra can be a division algebra is if it2942is the ground field `\GF{p}`.29432944EXAMPLES::29452946sage: SteenrodAlgebra(11).is_division_algebra()2947False2948sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_division_algebra()2949True2950"""2951return self.is_field()29522953def is_field(self, proof = True):2954r"""2955The only way this algebra can be a field is if it is the2956ground field `\GF{p}`.29572958EXAMPLES::29592960sage: SteenrodAlgebra(11).is_field()2961False2962sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_field()2963True2964"""2965return self.dimension() == 129662967def is_integral_domain(self, proof = True):2968r"""2969The only way this algebra can be an integral domain is if it2970is the ground field `\GF{p}`.29712972EXAMPLES::29732974sage: SteenrodAlgebra(11).is_integral_domain()2975False2976sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_integral_domain()2977True2978"""2979return self.is_field()29802981def is_noetherian(self):2982"""2983This algebra is noetherian if and only if it is finite.29842985EXAMPLES::29862987sage: SteenrodAlgebra(3).is_noetherian()2988False2989sage: SteenrodAlgebra(profile=[1,2,1]).is_noetherian()2990True2991sage: SteenrodAlgebra(profile=lambda n: n+2).is_noetherian()2992False2993"""2994return self.is_finite()29952996######################################################2997# element class2998######################################################29993000class Element(CombinatorialFreeModuleElement):3001r"""3002Class for elements of the Steenrod algebra. Since the3003Steenrod algebra class is based on3004:class:`CombinatorialFreeModule3005<sage.combinat.free_module.CombinatorialFreeModule>`, this is3006based on :class:`CombinatorialFreeModuleElement3007<sage.combinat.free_module.CombinatorialFreeModuleElement>`.3008It has new methods reflecting its role, like :meth:`degree`3009for computing the degree of an element.30103011EXAMPLES:30123013Since this class inherits from3014:class:`CombinatorialFreeModuleElement3015<sage.combinat.free_module.CombinatorialFreeModuleElement>`,3016elements can be used as iterators, and there are other useful3017methods::30183019sage: c = Sq(5).antipode(); c3020Sq(2,1) + Sq(5)3021sage: for mono, coeff in c: print coeff, mono30221 (5,)30231 (2, 1)3024sage: c.monomial_coefficients()3025{(5,): 1, (2, 1): 1}3026sage: c.monomials()3027[Sq(2,1), Sq(5)]3028sage: c.support()3029[(2, 1), (5,)]30303031See the documentation for this module (type3032``sage.algebras.steenrod.steenrod_algebra?``) for more3033information about elements of the Steenrod algebra.3034"""3035def prime(self):3036"""3037The prime associated to self.30383039EXAMPLES::30403041sage: a = SteenrodAlgebra().Sq(3,2,1)3042sage: a.prime()304323044sage: a.change_basis('adem').prime()304523046sage: b = SteenrodAlgebra(p=7).basis(36)[0]3047sage: b.prime()304873049sage: SteenrodAlgebra(p=3, basis='adem').one().prime()305033051"""3052return self.base_ring().characteristic()30533054def basis_name(self):3055"""3056The basis name associated to self.30573058EXAMPLES::30593060sage: a = SteenrodAlgebra().Sq(3,2,1)3061sage: a.basis_name()3062'milnor'3063sage: a.change_basis('adem').basis_name()3064'serre-cartan'3065sage: a.change_basis('wood____y').basis_name()3066'woody'3067sage: b = SteenrodAlgebra(p=7).basis(36)[0]3068sage: b.basis_name()3069'milnor'3070sage: a.change_basis('adem').basis_name()3071'serre-cartan'3072"""3073return self.parent().prefix()30743075def is_homogeneous(self):3076"""3077Return True iff this element is homogeneous.30783079EXAMPLES::30803081sage: (Sq(0,0,1) + Sq(7)).is_homogeneous()3082True3083sage: (Sq(0,0,1) + Sq(2)).is_homogeneous()3084False3085"""3086monos = self.support()3087if len(monos) <= 1:3088return True3089degree = None3090deg = self.parent().degree_on_basis3091for mono in monos:3092if degree is None:3093degree = deg(mono)3094elif deg(mono) != degree:3095return False3096return True30973098def degree(self):3099r"""3100The degree of self.31013102The degree of `\text{Sq}(i_1,i_2,i_3,...)` is31033104.. math::31053106i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....31073108At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the3109degree of `\mathcal{P}(i_1, i_2, ...)` is31103111.. math::31123113\sum_{k \geq 0} 2(p^k - 1) i_k.31143115ALGORITHM: If :meth:`is_homogeneous` returns True, call3116:meth:`SteenrodAlgebra_generic.degree_on_basis` on the leading3117summand.31183119EXAMPLES::31203121sage: Sq(0,0,1).degree()312273123sage: (Sq(0,0,1) + Sq(7)).degree()312473125sage: (Sq(0,0,1) + Sq(2)).degree()3126Traceback (most recent call last):3127...3128ValueError: Element is not homogeneous.31293130sage: A11 = SteenrodAlgebra(p=11)3131sage: A11.P(1).degree()3132203133sage: A11.P(1,1).degree()31342603135sage: A11.Q(2).degree()313624131373138TESTS::31393140sage: all([x.degree() == 10 for x in SteenrodAlgebra(basis='woody').basis(10)])3141True3142sage: all([x.degree() == 11 for x in SteenrodAlgebra(basis='woodz').basis(11)])3143True3144sage: all([x.degree() == x.milnor().degree() for x in SteenrodAlgebra(basis='wall').basis(11)])3145True3146sage: a = SteenrodAlgebra(basis='pst').basis(10)[0]3147sage: a.degree() == a.change_basis('arnonc').degree()3148True3149sage: b = SteenrodAlgebra(basis='comm').basis(12)[1]3150sage: b.degree() == b.change_basis('adem').change_basis('arnona').degree()3151True3152sage: all([x.degree() == 9 for x in SteenrodAlgebra(basis='comm').basis(9)])3153True3154sage: all([x.degree() == 8 for x in SteenrodAlgebra(basis='adem').basis(8)])3155True3156sage: all([x.degree() == 7 for x in SteenrodAlgebra(basis='milnor').basis(7)])3157True3158sage: all([x.degree() == 24 for x in SteenrodAlgebra(p=3).basis(24)])3159True3160sage: all([x.degree() == 40 for x in SteenrodAlgebra(p=5, basis='serre-cartan').basis(40)])3161True3162"""3163if len(self.support()) == 0:3164raise ValueError("The zero element does not have a well-defined degree.")3165try:3166assert self.is_homogeneous()3167return self.parent().degree_on_basis(self.leading_support())3168except AssertionError:3169raise ValueError("Element is not homogeneous.")31703171def milnor(self):3172"""3173Return this element in the Milnor basis; that is, as an3174element of the appropriate Steenrod algebra.31753176This just calls the method3177:meth:`SteenrodAlgebra_generic.milnor`.31783179EXAMPLES::31803181sage: Adem = SteenrodAlgebra(basis='adem')3182sage: a = Adem.basis(4)[1]; a3183Sq^3 Sq^13184sage: a.milnor()3185Sq(1,1)3186"""3187A = self.parent()3188return A.milnor(self)31893190def change_basis(self, basis='milnor'):3191r"""3192Representation of element with respect to basis.31933194INPUT:31953196- ``basis`` - string, basis in which to work.31973198OUTPUT: representation of self in given basis31993200The choices for ``basis`` are:32013202- 'milnor' for the Milnor basis.3203- 'serre-cartan', 'serre_cartan', 'sc', 'adem', 'admissible'3204for the Serre-Cartan basis.3205- 'wood_y' for Wood's Y basis.3206- 'wood_z' for Wood's Z basis.3207- 'wall' for Wall's basis.3208- 'wall_long' for Wall's basis, alternate representation3209- 'arnon_a' for Arnon's A basis.3210- 'arnon_a_long' for Arnon's A basis, alternate representation.3211- 'arnon_c' for Arnon's C basis.3212- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz' for3213various `P^s_t`-bases.3214- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz'3215for various commutator bases.3216- 'comm_long', 'comm_rlex_long', etc., for commutator bases,3217alternate representations.32183219See documentation for this module (by browsing the3220reference manual or by typing3221``sage.algebras.steenrod.steenrod_algebra?``) for3222descriptions of the different bases.32233224EXAMPLES::32253226sage: c = Sq(2) * Sq(1)3227sage: c.change_basis('milnor')3228Sq(0,1) + Sq(3)3229sage: c.change_basis('serre-cartan')3230Sq^2 Sq^13231sage: d = Sq(0,0,1)3232sage: d.change_basis('arnonc')3233Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^73234"""3235A = self.parent()3236return A._change_basis(self, basis)32373238def _basis_dictionary(self,basis):3239r"""3240Convert self to ``basis``, returning a dictionary of terms of3241the form (mono: coeff), where mono is a monomial in the given3242basis.32433244INPUT:32453246- ``basis`` - string, basis in which to work32473248OUTPUT: dictionary32493250This just calls :meth:`change_basis` to get an element of the3251Steenrod algebra with the new basis, and then calls3252:meth:`monomial_coefficients` on this element to produce its3253dictionary representation.32543255EXAMPLES::32563257sage: c = Sq(2) * Sq(1)3258sage: c._basis_dictionary('milnor')3259{(0, 1): 1, (3,): 1}3260sage: c3261Sq(0,1) + Sq(3)3262sage: c._basis_dictionary('serre-cartan')3263{(2, 1): 1}3264sage: c.change_basis('serre-cartan')3265Sq^2 Sq^13266sage: d = Sq(0,0,1)3267sage: d._basis_dictionary('arnonc')3268{(7,): 1, (2, 5): 1, (4, 3): 1, (4, 2, 1): 1}3269sage: d.change_basis('arnonc')3270Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^732713272At odd primes::32733274sage: e = 2 * SteenrodAlgebra(3).P(1,2)3275sage: e._basis_dictionary('milnor')3276{((), (1, 2)): 2}3277sage: e32782 P(1,2)3279sage: e._basis_dictionary('serre-cartan')3280{(0, 7, 0, 2, 0): 2, (0, 8, 0, 1, 0): 2}3281sage: e.change_basis('adem')32822 P^7 P^2 + 2 P^8 P^13283"""3284a = self.change_basis(basis)3285return a.monomial_coefficients()32863287def basis(self, basis):3288r"""3289Representation of element with respect to basis.32903291INPUT:32923293- ``basis`` - string, basis in which to work.32943295OUTPUT: Representation of self in given basis32963297.. warning::32983299Deprecated (December 2010). Use :meth:`change_basis` instead.33003301EXAMPLES::33023303sage: c = Sq(2) * Sq(1)3304sage: c.basis('milnor')3305doctest:...: DeprecationWarning: The .basis() method is deprecated. Use .change_basis() instead.3306See http://trac.sagemath.org/10052 for details.3307Sq(0,1) + Sq(3)3308"""3309from sage.misc.superseded import deprecation3310deprecation(10052, 'The .basis() method is deprecated. Use .change_basis() instead.')3311return self.change_basis(basis)33123313def coproduct(self, algorithm='milnor'):3314"""3315The coproduct of this element.33163317INPUT:33183319- ``algorithm`` -- None or a string, either 'milnor' or3320'serre-cartan' (or anything which will be converted to3321one of these by the function :func:`get_basis_name3322<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`).3323If None, default to 'serre-cartan' if current basis is3324'serre-cartan'; otherwise use 'milnor'.33253326See :meth:`SteenrodAlgebra_generic.coproduct_on_basis` for3327more information on computing the coproduct.33283329EXAMPLES::33303331sage: a = Sq(2)3332sage: a.coproduct()33331 # Sq(2) + Sq(1) # Sq(1) + Sq(2) # 13334sage: b = Sq(4)3335sage: (a*b).coproduct() == (a.coproduct()) * (b.coproduct())3336True33373338sage: c = a.change_basis('adem'); c.coproduct(algorithm='milnor')33391 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 13340sage: c = a.change_basis('adem'); c.coproduct(algorithm='adem')33411 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 133423343sage: d = a.change_basis('comm_long'); d.coproduct()33441 # s_2 + s_1 # s_1 + s_2 # 133453346sage: A7 = SteenrodAlgebra(p=7)3347sage: a = A7.Q(1) * A7.P(1); a3348Q_1 P(1)3349sage: a.coproduct()33501 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 13351sage: a.coproduct(algorithm='adem')33521 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 13353"""3354A = self.parent()3355return A.coproduct(self, algorithm=algorithm)33563357def excess(self):3358r"""3359Excess of element.33603361OUTPUT: ``excess`` - non-negative integer33623363The excess of a Milnor basis element `\text{Sq}(a,b,c,...)` is3364`a + b + c + ...`. When `p` is odd, the excess of `Q_{0}^{e_0}3365Q_{1}^{e_1} ... P(r_1, r_2, ...)` is `\sum e_i + 2 \sum r_i`.3366The excess of a linear combination of Milnor basis elements is3367the minimum of the excesses of those basis elements.33683369See [Kra] for the proofs of these assertions.33703371REFERENCES:33723373- [Kra] D. Kraines, "On excess in the Milnor basis," Bull. London3374Math. Soc. 3 (1971), 363-365.33753376EXAMPLES::33773378sage: a = Sq(1,2,3)3379sage: a.excess()338063381sage: (Sq(0,0,1) + Sq(4,1) + Sq(7)).excess()338213383sage: [m.excess() for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]3384[1, 5, 7]3385sage: [m for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]3386[Sq(0,0,1), Sq(4,1), Sq(7)]3387sage: B = SteenrodAlgebra(7)3388sage: a = B.Q(1,2,5)3389sage: b = B.P(2,2,3)3390sage: a.excess()339133392sage: b.excess()3393143394sage: (a + b).excess()339533396sage: (a * b).excess()3397173398"""3399def excess_odd(mono):3400"""3401Excess of mono, where mono has the form ((s0, s1, ...), (r1, r2,3402...)).34033404Returns the length of the first component, since that is the number3405of factors, plus twice the sum of the terms in the second3406component.3407"""3408if len(mono) == 0:3409return 03410else:3411return len(mono[0]) + 2 * sum(mono[1])34123413p = self.prime()3414a = self.milnor()3415if p == 2:3416excesses = [sum(mono) for mono in a.support()]3417else:3418excesses = [excess_odd(mono) for mono in a.support()]3419return min(excesses)34203421def is_unit(self):3422"""3423True if element has a nonzero scalar multiple of P(0) as a summand,3424False otherwise.34253426EXAMPLES::34273428sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)3429sage: z.is_unit()3430False3431sage: u = Sq(0) + Sq(3,1)3432sage: u == 1 + Sq(3,1)3433True3434sage: u.is_unit()3435True3436sage: A5 = SteenrodAlgebra(5)3437sage: v = A5.P(0)3438sage: (v + v + v).is_unit()3439True3440"""3441return self.parent().one() in self.monomials()34423443def is_nilpotent(self):3444"""3445True if element is not a unit, False otherwise.34463447EXAMPLES::34483449sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)3450sage: z.is_nilpotent()3451True3452sage: u = 1 + Sq(3,1)3453sage: u == 1 + Sq(3,1)3454True3455sage: u.is_nilpotent()3456False3457"""3458return not self.is_unit()34593460def may_weight(self):3461r"""3462May's 'weight' of element.34633464OUTPUT: ``weight`` - non-negative integer34653466If we let `F_* (A)` be the May filtration of the Steenrod3467algebra, the weight of an element `x` is the integer `k` so3468that `x` is in `F_k(A)` and not in `F_{k+1}(A)`. According to3469Theorem 2.6 in May's thesis [May], the weight of a Milnor3470basis element is computed as follows: first, to compute the3471weight of `P(r_1,r_2, ...)`, write each `r_i` in base `p` as3472`r_i = \sum_j p^j r_{ij}`. Then each nonzero binary digit3473`r_{ij}` contributes `i` to the weight: the weight is3474`\sum_{i,j} i r_{ij}`. When `p` is odd, the weight of `Q_i` is3475`i+1`, so the weight of a product `Q_{i_1} Q_{i_2} ...` equals3476`(i_1+1) + (i_2+1) + ...`. Then the weight of `Q_{i_1} Q_{i_2}3477...P(r_1,r_2, ...)` is the sum of `(i_1+1) + (i_2+1) + ...`3478and `\sum_{i,j} i r_{ij}`.34793480The weight of a sum of Milnor basis elements is the minimum of3481the weights of the summands.34823483When `p=2`, we compute the weight on Milnor basis elements by3484adding up the terms in their 'height' - see3485:meth:`wall_height` for documentation. (When `p` is odd, the3486height of an element is not defined.)34873488REFERENCES:34893490- [May]: J. P. May, "The cohomology of restricted Lie algebras and of3491Hopf algebras; application to the Steenrod algebra." Thesis,3492Princeton Univ., 1964.34933494EXAMPLES::34953496sage: Sq(0).may_weight()349703498sage: a = Sq(4)3499sage: a.may_weight()350013501sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)3502sage: b.may_weight()350323504sage: Sq(2,1,5).wall_height()3505[2, 3, 2, 1, 1]3506sage: Sq(2,1,5).may_weight()350793508sage: A5 = SteenrodAlgebra(5)3509sage: a = A5.Q(1,2,4)3510sage: b = A5.P(1,2,1)3511sage: a.may_weight()3512103513sage: b.may_weight()351483515sage: (a * b).may_weight()3516183517sage: A5.P(0,0,1).may_weight()351833519"""3520from sage.rings.infinity import Infinity3521from sage.rings.all import Integer3522p = self.prime()3523if self == 0:3524return Infinity3525elif self.is_unit():3526return 03527elif p == 2:3528wt = Infinity3529for mono in self.milnor().monomials():3530wt = min(wt, sum(mono.wall_height()))3531return wt3532else: # p odd3533wt = Infinity3534for (mono1, mono2) in self.milnor().support():3535P_wt = 03536index = 13537for n in mono2:3538P_wt += index * sum(Integer(n).digits(p))3539index += 13540wt = min(wt, sum(mono1) + len(mono1) + P_wt)3541return wt35423543def is_decomposable(self):3544r"""3545Return True if element is decomposable, False otherwise.3546That is, if element is in the square of the augmentation ideal,3547return True; otherwise, return False.35483549OUTPUT: boolean35503551EXAMPLES::35523553sage: a = Sq(6)3554sage: a.is_decomposable()3555True3556sage: for i in range(9):3557... if not Sq(i).is_decomposable():3558... print Sq(i)355913560Sq(1)3561Sq(2)3562Sq(4)3563Sq(8)3564sage: A3 = SteenrodAlgebra(p=3, basis='adem')3565sage: [A3.P(n) for n in range(30) if not A3.P(n).is_decomposable()]3566[1, P^1, P^3, P^9, P^27]35673568TESTS:35693570These all test changing bases and printing in various bases::35713572sage: A = SteenrodAlgebra(basis='milnor')3573sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3574[1, Sq(1), Sq(2), Sq(4), Sq(8)]3575sage: A = SteenrodAlgebra(basis='wall_long')3576sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3577[1, Sq^1, Sq^2, Sq^4, Sq^8]3578sage: A = SteenrodAlgebra(basis='arnona_long')3579sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3580[1, Sq^1, Sq^2, Sq^4, Sq^8]3581sage: A = SteenrodAlgebra(basis='woodz')3582sage: [A.Sq(n) for n in range(20) if not A.Sq(n).is_decomposable()] # long time3583[1, Sq^1, Sq^2, Sq^4, Sq^8, Sq^16]3584sage: A = SteenrodAlgebra(basis='comm_long')3585sage: [A.Sq(n) for n in range(25) if not A.Sq(n).is_decomposable()] # long time3586[1, s_1, s_2, s_4, s_8, s_16]3587"""3588return self.may_weight() > 135893590def wall_height(self):3591r"""3592Wall's 'height' of element.35933594OUTPUT: list of non-negative integers35953596The height of an element of the mod 2 Steenrod algebra is a3597list of non-negative integers, defined as follows: if the3598element is a monomial in the generators `\text{Sq}(2^i)`, then3599the `i^{th}` entry in the list is the number of times3600`\text{Sq}(2^i)` appears. For an arbitrary element, write it3601as a sum of such monomials; then its height is the maximum,3602ordered right-lexicographically, of the heights of those3603monomials.36043605When `p` is odd, the height of an element is not defined.36063607According to Theorem 3 in [Wall], the height of the Milnor3608basis element `\text{Sq}(r_1, r_2, ...)` is obtained as3609follows: write each `r_i` in binary as `r_i = \sum_j 2^j3610r_{ij}`. Then each nonzero binary digit `r_{ij}` contributes 13611to the `k^{th}` entry in the height, for `j \leq k \leq3612i+j-1`.36133614REFERENCES:36153616- [Wall]: C. T. C. Wall, "Generators and relations for the Steenrod3617algebra," Ann. of Math. (2) **72** (1960), 429-444.36183619EXAMPLES::36203621sage: Sq(0).wall_height()3622[]3623sage: a = Sq(4)3624sage: a.wall_height()3625[0, 0, 1]3626sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)3627sage: b.wall_height()3628[0, 0, 1, 1]3629sage: Sq(0,0,3).wall_height()3630[1, 2, 2, 1]3631"""3632from sage.rings.all import Integer3633if self.prime() > 2:3634raise NotImplementedError("Wall height is not defined at odd primes.")3635if self == 0 or self == 1:3636return []3637result = []3638deg = self.parent().degree_on_basis3639for r in self.milnor().support():3640h = [0]*(1 + deg(r))3641i = 13642for x in r:3643if x > 0:3644for j in range(1+Integer(x).exact_log(2)):3645if (2**j & x) != 0:3646for k in range(j,i+j):3647h[k] += 13648i=i+13649h.reverse()3650result = max(h, result)3651result.reverse()3652while len(result) > 0 and result[-1] == 0:3653result = result[:-1]3654return result36553656def additive_order(self):3657"""3658The additive order of any nonzero element of the mod p3659Steenrod algebra is p.36603661OUTPUT: 1 (for the zero element) or p (for anything else)36623663EXAMPLES::36643665sage: z = Sq(4) + Sq(6) + 13666sage: z.additive_order()366723668sage: (Sq(3) + Sq(3)).additive_order()366913670"""3671if self == 0:3672return 13673return self.prime()36743675class SteenrodAlgebra_mod_two(SteenrodAlgebra_generic):3676"""3677The mod 2 Steenrod algebra.36783679Users should not call this, but use the function3680:func:`SteenrodAlgebra` instead. See that function for extensive3681documentation. (This differs from :class:`SteenrodAlgebra_generic`3682only in that it has a method :meth:`Sq` for defining elements.)3683"""3684def Sq(self, *nums):3685r"""3686Milnor element `\text{Sq}(a,b,c,...)`.36873688INPUT:36893690- ``a, b, c, ...`` - non-negative integers36913692OUTPUT: element of the Steenrod algebra36933694This returns the Milnor basis element3695`\text{Sq}(a, b, c, ...)`.36963697EXAMPLES::36983699sage: A = SteenrodAlgebra(2)3700sage: A.Sq(5)3701Sq(5)3702sage: A.Sq(5,0,2)3703Sq(5,0,2)37043705Entries must be non-negative integers; otherwise, an error3706results.3707"""3708if self.prime() == 2:3709return self.P(*nums)3710else:3711raise ValueError("Sq is only defined at the prime 2")37123713def SteenrodAlgebra(p=2, basis='milnor', **kwds):3714r"""3715The mod `p` Steenrod algebra37163717INPUT:37183719- ``p`` - positive prime integer (optional, default = 2)3720- ``basis`` - string (optional, default = 'milnor')3721- ``profile`` - a profile function in form specified below (optional, default ``None``)3722- ``truncation_type`` - 0 or `\infty` or 'auto' (optional, default 'auto')3723- ``precision`` - integer or ``None`` (optional, default ``None``)37243725OUTPUT: mod `p` Steenrod algebra or one of its sub-Hopf algebras,3726elements of which are printed using ``basis``37273728See below for information about ``basis``, ``profile``, etc.37293730EXAMPLES:37313732Some properties of the Steenrod algebra are available::37333734sage: A = SteenrodAlgebra(2)3735sage: A.order()3736+Infinity3737sage: A.is_finite()3738False3739sage: A.is_commutative()3740False3741sage: A.is_noetherian()3742False3743sage: A.is_integral_domain()3744False3745sage: A.is_field()3746False3747sage: A.is_division_algebra()3748False3749sage: A.category()3750Category of graded hopf algebras with basis over Finite Field of size 237513752There are methods for constructing elements of the Steenrod3753algebra::37543755sage: A2 = SteenrodAlgebra(2); A23756mod 2 Steenrod algebra, milnor basis3757sage: A2.Sq(1,2,6)3758Sq(1,2,6)3759sage: A2.Q(3,4) # product of Milnor primitives Q_3 and Q_43760Sq(0,0,0,1,1)3761sage: A2.pst(2,3) # Margolis pst element3762Sq(0,0,4)3763sage: A5 = SteenrodAlgebra(5); A53764mod 5 Steenrod algebra, milnor basis3765sage: A5.P(1,2,6)3766P(1,2,6)3767sage: A5.Q(3,4)3768Q_3 Q_43769sage: A5.Q(3,4) * A5.P(1,2,6)3770Q_3 Q_4 P(1,2,6)3771sage: A5.pst(2,3)3772P(0,0,25)37733774You can test whether elements are contained in the Steenrod3775algebra::37763777sage: w = Sq(2) * Sq(4)3778sage: w in SteenrodAlgebra(2)3779True3780sage: w in SteenrodAlgebra(17)3781False37823783.. rubric:: Different bases for the Steenrod algebra:37843785There are two standard vector space bases for the mod `p` Steenrod3786algebra: the Milnor basis and the Serre-Cartan basis. When `p=2`,3787there are also several other, less well-known, bases. See the3788documentation for this module (type3789``sage.algebras.steenrod.steenrod_algebra?``) and the function3790:func:`steenrod_algebra_basis3791<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_algebra_basis_>`3792for full descriptions of each of the implemented bases.37933794This module implements the following bases at all primes:37953796- 'milnor': Milnor basis.37973798- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.37993800- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz': various3801`P^s_t`-bases.38023803- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz', or3804these with '_long' appended: various commutator bases.38053806It implements the following bases when `p=2`:38073808- 'wood_y': Wood's Y basis.38093810- 'wood_z': Wood's Z basis.38113812- 'wall', 'wall_long': Wall's basis.38133814- 'arnon_a', 'arnon_a_long': Arnon's A basis.38153816- 'arnon_c': Arnon's C basis.38173818When defining a Steenrod algebra, you can specify a basis. Then3819elements of that Steenrod algebra are printed in that basis::38203821sage: adem = SteenrodAlgebra(2, 'adem')3822sage: x = adem.Sq(2,1) # Sq(-) always means a Milnor basis element3823sage: x3824Sq^4 Sq^1 + Sq^53825sage: y = Sq(0,1) # unadorned Sq defines elements w.r.t. Milnor basis3826sage: y3827Sq(0,1)3828sage: adem(y)3829Sq^2 Sq^1 + Sq^33830sage: adem5 = SteenrodAlgebra(5, 'serre-cartan')3831sage: adem5.P(0,2)3832P^10 P^2 + 4 P^11 P^1 + P^1238333834If you add or multiply elements defined using different bases, the3835left-hand factor determines the form of the output::38363837sage: SteenrodAlgebra(basis='adem').Sq(3) + SteenrodAlgebra(basis='pst').Sq(0,1)3838Sq^2 Sq^13839sage: SteenrodAlgebra(basis='pst').Sq(3) + SteenrodAlgebra(basis='milnor').Sq(0,1)3840P^0_1 P^1_1 + P^0_23841sage: SteenrodAlgebra(basis='milnor').Sq(2) * SteenrodAlgebra(basis='arnonc').Sq(2)3842Sq(1,1)38433844You can get a list of basis elements in a given dimension::38453846sage: A3 = SteenrodAlgebra(3, 'milnor')3847sage: A3.basis(13)3848Family (Q_1 P(2), Q_0 P(3))38493850Algebras defined over different bases are not equal::38513852sage: SteenrodAlgebra(basis='milnor') == SteenrodAlgebra(basis='pst')3853False38543855Bases have various synonyms, and in general Sage tries to figure3856out what basis you meant::38573858sage: SteenrodAlgebra(basis='MiLNOr')3859mod 2 Steenrod algebra, milnor basis3860sage: SteenrodAlgebra(basis='MiLNOr') == SteenrodAlgebra(basis='milnor')3861True3862sage: SteenrodAlgebra(basis='adem')3863mod 2 Steenrod algebra, serre-cartan basis3864sage: SteenrodAlgebra(basis='adem').basis_name()3865'serre-cartan'3866sage: SteenrodAlgebra(basis='wood---z---').basis_name()3867'woodz'38683869As noted above, several of the bases ('arnon_a', 'wall', 'comm')3870have alternate, sometimes longer, representations. These provide3871ways of expressing elements of the Steenrod algebra in terms of3872the `\text{Sq}^{2^n}`.38733874::38753876sage: A_long = SteenrodAlgebra(2, 'arnon_a_long')3877sage: A_long(Sq(6))3878Sq^1 Sq^2 Sq^1 Sq^2 + Sq^2 Sq^43879sage: SteenrodAlgebra(2, 'wall_long')(Sq(6))3880Sq^2 Sq^1 Sq^2 Sq^1 + Sq^2 Sq^43881sage: SteenrodAlgebra(2, 'comm_deg_long')(Sq(6))3882s_1 s_2 s_12 + s_2 s_438833884.. rubric:: Sub-Hopf algebras of the Steenrod algebra:38853886These are specified using the argument ``profile``, along with,3887optionally, ``truncation_type`` and ``precision``. The3888``profile`` argument specifies the profile function for this3889algebra. Any sub-Hopf algebra of the Steenrod algebra is3890determined by its *profile function*. When `p=2`, this is a map `e`3891from the positive integers to the set of non-negative integers,3892plus `\infty`, corresponding to the sub-Hopf algebra dual to this3893quotient of the dual Steenrod algebra:38943895.. math::38963897\GF{2} [\xi_1, \xi_2, \xi_3, ...] / (\xi_1^{2^{e(1)}}, \xi_2^{2^{e(2)}}, \xi_3^{2^{e(3)}}, ...).38983899The profile function `e` must satisfy the condition39003901- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.39023903This is specified via ``profile``, and optionally ``precision``3904and ``truncation_type``. First, ``profile`` must have one of the3905following forms:39063907- a list or tuple, e.g., ``[3,2,1]``, corresponding to the3908function sending 1 to 3, 2 to 2, 3 to 1, and all other integers3909to the value of ``truncation_type``.3910- a function from positive integers to non-negative integers (and3911`\infty`), e.g., ``lambda n: n+2``.3912- ``None`` or ``Infinity`` - use this for the profile function for3913the whole Steenrod algebra.39143915In the first and third cases, ``precision`` is ignored. In the3916second case, this function is converted to a tuple of length one3917less than ``precision``, which has default value 100. The3918function is truncated at this point, and all remaining values are3919set to the value of ``truncation_type``.39203921``truncation_type`` may be 0, `\infty`, or 'auto'. If it's3922'auto', then it gets converted to 0 in the first case above (when3923``profile`` is a list), and otherwise (when ``profile`` is a3924function, ``None``, or ``Infinity``) it gets converted to `\infty`.39253926For example, the sub-Hopf algebra `A(2)` has profile function3927``[3,2,1,0,0,0,...]``, so it can be defined by any of the3928following::39293930sage: A2 = SteenrodAlgebra(profile=[3,2,1])3931sage: B2 = SteenrodAlgebra(profile=[3,2,1,0,0]) # trailing 0's ignored3932sage: A2 == B23933True3934sage: C2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=0)3935sage: A2 == C23936True39373938In the following case, the profile function is specified by a3939function and ``truncation_type`` isn't specified, so it defaults3940to `\infty`; therefore this gives a different sub-Hopf algebra::39413942sage: D2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0))3943sage: A2 == D23944False3945sage: D2.is_finite()3946False3947sage: E2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=Infinity)3948sage: D2 == E23949True39503951The argument ``precision`` only needs to be specified if the3952profile function is defined by a function and you want to control3953when the profile switches from the given function to the3954truncation type. For example::39553956sage: D3 = SteenrodAlgebra(profile=lambda n: n, precision=3)3957sage: D33958sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, +Infinity, +Infinity, +Infinity, ...]3959sage: D4 = SteenrodAlgebra(profile=lambda n: n, precision=4); D43960sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, 3, +Infinity, +Infinity, +Infinity, ...]3961sage: D3 == D43962False39633964When `p` is odd, ``profile`` is a pair of functions `e` and `k`,3965corresponding to the quotient39663967.. math::39683969\GF{p} [\xi_1, \xi_2, \xi_3, ...] \otimes \Lambda (\tau_0,3970\tau_1, ...) / (\xi_1^{p^{e_1}}, \xi_2^{p^{e_2}}, ...;3971\tau_0^{k_0}, \tau_1^{k_1}, ...).39723973Together, the functions `e` and `k` must satisfy the conditions39743975- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`,39763977- if `k(i+j) = 1`, then either `e(i) \leq j` or `k(j) = 1` for all `i3978\geq 1`, `j \geq 0`.39793980Therefore ``profile`` must have one of the following forms:39813982- a pair of lists or tuples, the second of which takes values in3983the set `\{1,2\}`, e.g., ``([3,2,1,1], [1,1,2,2,1])``.39843985- a pair of functions, one from the positive integers to3986non-negative integers (and `\infty`), one from the non-negative3987integers to the set `\{1,2\}`, e.g., ``(lambda n: n+2, lambda n:39881 if n<3 else 2)``.39893990- ``None`` or ``Infinity`` - use this for the profile function for3991the whole Steenrod algebra.39923993You can also mix and match the first two, passing a pair with3994first entry a list and second entry a function, for instance. The3995values of ``precision`` and ``truncation_type`` are determined by3996the first entry.39973998More examples::39994000sage: E = SteenrodAlgebra(profile=lambda n: 0 if n<3 else 3, truncation_type=0)4001sage: E.is_commutative()4002True40034004sage: A2 = SteenrodAlgebra(profile=[3,2,1]) # the algebra A(2)4005sage: Sq(7,3,1) in A24006True4007sage: Sq(8) in A24008False4009sage: Sq(8) in SteenrodAlgebra().basis(8)4010True4011sage: Sq(8) in A2.basis(8)4012False4013sage: A2.basis(8)4014Family (Sq(1,0,1), Sq(2,2), Sq(5,1))40154016sage: A5 = SteenrodAlgebra(p=5)4017sage: A51 = SteenrodAlgebra(p=5, profile=([1], [2,2]))4018sage: A5.Q(0,1) * A5.P(4) in A514019True4020sage: A5.Q(2) in A514021False4022sage: A5.P(5) in A514023False40244025For sub-Hopf algebras of the Steenrod algebra, only the Milnor4026basis or the various `P^s_t`-bases may be used. ::40274028sage: SteenrodAlgebra(profile=[1,2,1,1], basis='adem')4029Traceback (most recent call last):4030...4031NotImplementedError: For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.40324033TESTS:40344035Testing unique parents::40364037sage: S0 = SteenrodAlgebra(2)4038sage: S1 = SteenrodAlgebra(2)4039sage: S0 is S14040True4041sage: S2 = SteenrodAlgebra(2, basis='adem')4042sage: S0 is S24043False4044sage: S0 == S24045False4046sage: A1 = SteenrodAlgebra(profile=[2,1])4047sage: B1 = SteenrodAlgebra(profile=[2,1,0,0])4048sage: A1 is B14049True4050"""4051if p == 2:4052return SteenrodAlgebra_mod_two(p=2, basis=basis, **kwds)4053else:4054return SteenrodAlgebra_generic(p=p, basis=basis, **kwds)405540564057def AA(n=None, p=2):4058r"""4059This returns the Steenrod algebra `A` or its sub-Hopf algebra `A(n)`.40604061INPUT:40624063- `n` - non-negative integer, optional (default None)4064- `p` - prime number, optional (default 2)40654066OUTPUT: If `n` is None, then return the full Steenrod algebra.4067Otherwise, return `A(n)`.40684069When `p=2`, `A(n)` is the sub-Hopf algebra generated by the4070elements `\text{Sq}^i` for `i \leq 2^n`. Its profile function is4071`(n+1, n, n-1, ...)`. When `p` is odd, `A(n)` is the sub-Hopf4072algebra generated by the elements `Q_0` and `\mathcal{P}^i` for `i4073\leq p^{n-1}`. Its profile function is `e=(n, n-1, n-2, ...)`4074and `k=(2, 2, ..., 2)` (length `n+1`).40754076EXAMPLES::40774078sage: from sage.algebras.steenrod.steenrod_algebra import AA as A4079sage: A()4080mod 2 Steenrod algebra, milnor basis4081sage: A(2)4082sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]4083sage: A(2, p=5)4084sub-Hopf algebra of mod 5 Steenrod algebra, milnor basis, profile function ([2, 1], [2, 2, 2])4085"""4086if n is None:4087return SteenrodAlgebra(p=p)4088if p == 2:4089return SteenrodAlgebra(p=p, profile=range(n+1, 0, -1))4090return SteenrodAlgebra(p=p, profile=(range(n, 0, -1), [2]*(n+1)))40914092def Sq(*nums):4093r"""4094Milnor element Sq(a,b,c,...).40954096INPUT:40974098- ``a, b, c, ...`` - non-negative integers40994100OUTPUT: element of the Steenrod algebra41014102This returns the Milnor basis element4103`\text{Sq}(a, b, c, ...)`.41044105EXAMPLES::41064107sage: Sq(5)4108Sq(5)4109sage: Sq(5) + Sq(2,1) + Sq(5) # addition is mod 2:4110Sq(2,1)4111sage: (Sq(4,3) + Sq(7,2)).degree()41121341134114Entries must be non-negative integers; otherwise, an error4115results.41164117This function is a good way to define elements of the Steenrod4118algebra.4119"""4120return SteenrodAlgebra(p=2).Sq(*nums)412141224123