Path: blob/master/sage/algebras/steenrod/steenrod_algebra.py
4159 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.categories.all import ModulesWithBasis, tensor, Hom473474######################################################475# the main class476######################################################477478class SteenrodAlgebra_generic(CombinatorialFreeModule):479r"""480The mod `p` Steenrod algebra.481482Users should not call this, but use the function483:func:`SteenrodAlgebra` instead. See that function for484extensive documentation.485486EXAMPLES::487488sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic()489mod 2 Steenrod algebra, milnor basis490sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5)491mod 5 Steenrod algebra, milnor basis492sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5, 'adem')493mod 5 Steenrod algebra, serre-cartan basis494"""495@staticmethod496def __classcall__(self, p=2, basis='milnor', **kwds):497"""498This normalizes the basis name and the profile, to make unique499representation work properly.500501EXAMPLES::502503sage: SteenrodAlgebra(basis='adem') is SteenrodAlgebra(basis='serre-cartan')504True505sage: SteenrodAlgebra(profile=[3,2,1,0]) is SteenrodAlgebra(profile=lambda n: max(4-n,0), truncation_type=0)506True507"""508from steenrod_algebra_misc import get_basis_name, normalize_profile509profile = kwds.get('profile', None)510precision = kwds.get('precision', None)511truncation_type = kwds.get('truncation_type', 'auto')512513std_basis = get_basis_name(basis, p)514std_profile, std_type = normalize_profile(profile, precision=precision, truncation_type=truncation_type, p=p)515return super(SteenrodAlgebra_generic, self).__classcall__(self, p=p, basis=std_basis, profile=std_profile, truncation_type=std_type)516517def __init__(self, p=2, basis='milnor', **kwds):518r"""519INPUT:520521- ``p`` - positive prime integer (optional, default 2)522- ``basis`` - string (optional, default = 'milnor')523- ``profile`` - profile function (optional, default ``None``)524- ``truncation_type`` - (optional, default 'auto')525- ``precision`` - (optional, default ``None``)526527OUTPUT: mod `p` Steenrod algebra with basis, or a sub-Hopf528algebra of the mod `p` Steenrod algebra defined by the given529profile function.530531See :func:`SteenrodAlgebra` for full documentation.532533EXAMPLES::534535sage: SteenrodAlgebra() # 2 is the default prime536mod 2 Steenrod algebra, milnor basis537sage: SteenrodAlgebra(5)538mod 5 Steenrod algebra, milnor basis539sage: SteenrodAlgebra(2, 'milnor').Sq(0,1)540Sq(0,1)541sage: SteenrodAlgebra(2, 'adem').Sq(0,1)542Sq^2 Sq^1 + Sq^3543544TESTS::545546sage: TestSuite(SteenrodAlgebra()).run()547sage: TestSuite(SteenrodAlgebra(profile=[4,3,2,2,1])).run()548sage: TestSuite(SteenrodAlgebra(basis='adem')).run()549sage: TestSuite(SteenrodAlgebra(basis='wall')).run()550sage: TestSuite(SteenrodAlgebra(basis='arnonc')).run() # long time551sage: TestSuite(SteenrodAlgebra(basis='woody')).run() # long time552sage: A3 = SteenrodAlgebra(3)553sage: A3.category()554Category of graded hopf algebras with basis over Finite Field of size 3555sage: TestSuite(A3).run()556sage: TestSuite(SteenrodAlgebra(basis='adem', p=3)).run()557sage: TestSuite(SteenrodAlgebra(basis='pst_llex', p=7)).run() # long time558sage: TestSuite(SteenrodAlgebra(basis='comm_deg', p=5)).run() # long time559"""560from sage.rings.arith import is_prime561from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis562from sage.rings.infinity import Infinity563from sage.sets.family import Family564from sage.sets.non_negative_integers import NonNegativeIntegers565from functools import partial566from steenrod_algebra_bases import steenrod_algebra_basis567from sage.rings.all import GF568profile = kwds.get('profile', None)569truncation_type = kwds.get('truncation_type', 'auto')570571if not is_prime(p):572raise ValueError("%s is not prime." % p)573self._prime = p574base_ring = GF(p)575self._profile = profile576self._truncation_type = truncation_type577NN = NonNegativeIntegers()578if ((p==2 and ((len(profile) > 0 and profile[0] < Infinity)))579or (p>2 and profile != ((), ()) and len(profile[0]) > 0580and profile[0][0] < Infinity)581or (truncation_type < Infinity)):582if basis != 'milnor' and basis.find('pst') == -1:583raise NotImplementedError("For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.")584self._basis_name = basis585self._basis_fcn = partial(steenrod_algebra_basis,586p=p,587basis=basis,588profile=profile,589truncation_type=truncation_type)590# name the basis function so different Steenrod algebras have591# different basis functions... (see __eq__ for LazyFamily?)592self._basis_fcn.func_name = "basis_%s_%s_%s_%s" % (p, basis, profile, truncation_type)593CombinatorialFreeModule.__init__(self,594base_ring,595Family(NN, self._basis_fcn),596prefix=self._basis_name,597element_class=self.Element,598category = GradedHopfAlgebrasWithBasis(base_ring),599scalar_mult = ' ')600601def prime(self):602r"""603The prime associated to self.604605EXAMPLES::606607sage: SteenrodAlgebra(p=2, profile=[1,1]).prime()6082609sage: SteenrodAlgebra(p=7).prime()6107611"""612return self.base_ring().characteristic()613614def basis_name(self):615r"""616The basis name associated to self.617618EXAMPLES::619620sage: SteenrodAlgebra(p=2, profile=[1,1]).basis_name()621'milnor'622sage: SteenrodAlgebra(basis='serre-cartan').basis_name()623'serre-cartan'624sage: SteenrodAlgebra(basis='adem').basis_name()625'serre-cartan'626"""627return self.prefix()628629def _has_nontrivial_profile(self):630r"""631True if the profile function for this algebra seems to be that632for a proper sub-Hopf algebra of the Steenrod algebra.633634EXAMPLES::635636sage: SteenrodAlgebra()._has_nontrivial_profile()637False638sage: SteenrodAlgebra(p=3)._has_nontrivial_profile()639False640sage: SteenrodAlgebra(profile=[3,2,1])._has_nontrivial_profile()641True642sage: SteenrodAlgebra(profile=([1], [2, 2]), p=3)._has_nontrivial_profile()643True644645Check that a bug in #11832 has been fixed::646647sage: P3 = SteenrodAlgebra(p=3, profile=(lambda n: Infinity, lambda n: 1))648sage: P3._has_nontrivial_profile()649True650"""651from sage.rings.infinity import Infinity652profile = self._profile653trunc = self._truncation_type654if self.prime() == 2:655return ((len(profile) > 0 and len(profile) > 0656and profile[0] < Infinity)657or (trunc < Infinity))658return ((profile != ((), ()) and659((len(profile[0]) > 0 and profile[0][0] < Infinity)660or (len(profile[1]) > 0 and min(profile[1]) == 1)))661or (trunc < Infinity))662663def _repr_(self):664r"""665Printed representation of the Steenrod algebra.666667EXAMPLES::668669sage: SteenrodAlgebra(3)670mod 3 Steenrod algebra, milnor basis671sage: SteenrodAlgebra(2, basis='adem')672mod 2 Steenrod algebra, serre-cartan basis673sage: B = SteenrodAlgebra(2003)674sage: B._repr_()675'mod 2003 Steenrod algebra, milnor basis'676677sage: SteenrodAlgebra(profile=(3,2,1,0))678sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]679sage: SteenrodAlgebra(profile=lambda n: 4)680sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [4, 4, 4, ..., 4, 4, +Infinity, +Infinity, +Infinity, ...]681sage: SteenrodAlgebra(p=5, profile=(lambda n: 4, lambda n: 1))682sub-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, ...])683"""684def abridge_list(l):685"""686String rep for list ``l`` if ``l`` is short enough;687otherwise print the first few terms and the last few688terms, with an ellipsis in between.689"""690if len(l) < 8:691l_str = str(l)692else:693l_str = str(l[:3]).rstrip("]") + ", ..., " + str(l[-2:]).lstrip("[")694return l_str695696from sage.rings.infinity import Infinity697profile = self._profile698trunc = self._truncation_type699p = self.prime()700if self._has_nontrivial_profile():701if p == 2:702pro_str = abridge_list(list(profile))703if trunc != 0:704pro_str = pro_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"705else:706e_str = abridge_list(list(profile[0]))707k_str = abridge_list(list(profile[1]))708if trunc != 0:709e_str = e_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"710k_str = k_str.rstrip("]") + ", " + str([2] * 2).strip("[]") + ", ...]"711pro_str = "(%s, %s)" % (e_str, k_str)712return "sub-Hopf algebra of mod %d Steenrod algebra, %s basis, profile function %s" % (self.prime(), self._basis_name, pro_str)713return "mod %d Steenrod algebra, %s basis" % (self.prime(), self._basis_name)714715def _latex_(self):716r"""717LaTeX representation of the Steenrod algebra.718719EXAMPLES::720721sage: C = SteenrodAlgebra(3)722sage: C723mod 3 Steenrod algebra, milnor basis724sage: C._latex_()725'\\mathcal{A}_{3}'726"""727return "\\mathcal{A}_{%s}" % self.prime()728729def _repr_term(self, t):730r"""731String representation of the monomial specified by the tuple ``t``.732733INPUT:734735- ``t`` - tuple, representing basis element in the current basis.736737OUTPUT: string738739This is tested in many places: any place elements are printed740is essentially a doctest for this method. Also, each basis741has its own method for printing monomials, and those are742doctested individually. We give a few doctests here, in743addition.744745EXAMPLES::746747sage: SteenrodAlgebra()._repr_term((3,2))748'Sq(3,2)'749sage: SteenrodAlgebra(p=7)._repr_term(((0,2), (3,2)))750'Q_0 Q_2 P(3,2)'751sage: SteenrodAlgebra(basis='adem')._repr_term((14,2))752'Sq^14 Sq^2'753sage: SteenrodAlgebra(basis='adem', p=3)._repr_term((1,3,0))754'beta P^3'755sage: SteenrodAlgebra(basis='pst')._repr_term(((0,2), (1,3)))756'P^0_2 P^1_3'757sage: SteenrodAlgebra(basis='arnon_a')._repr_term(((0,2), (1,3)))758'X^0_2 X^1_3'759760sage: A7 = SteenrodAlgebra(7)761sage: x = A7.Q(0,3) * A7.P(2,2)762sage: x._repr_()763'Q_0 Q_3 P(2,2)'764sage: x765Q_0 Q_3 P(2,2)766sage: a = SteenrodAlgebra().Sq(0,0,2)767sage: a768Sq(0,0,2)769sage: A2_adem = SteenrodAlgebra(2,'admissible')770sage: A2_adem(a)771Sq^8 Sq^4 Sq^2 + Sq^9 Sq^4 Sq^1 + Sq^10 Sq^3 Sq^1 +772Sq^10 Sq^4 + Sq^11 Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^13 Sq^1773+ Sq^14774sage: SteenrodAlgebra(2, 'woodz')(a)775Sq^6 Sq^7 Sq^1 + Sq^14 + Sq^4 Sq^7 Sq^3 + Sq^4 Sq^7776Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^8 Sq^6 + Sq^8 Sq^4 Sq^2777sage: SteenrodAlgebra(2, 'arnonc')(a)778Sq^4 Sq^2 Sq^8 + Sq^4 Sq^4 Sq^6 + Sq^4 Sq^6 Sq^4 +779Sq^6 Sq^8 + Sq^8 Sq^4 Sq^2 + Sq^8 Sq^6780sage: SteenrodAlgebra(2, 'pst_llex')(a)781P^1_3782sage: SteenrodAlgebra(2, 'comm_revz')(a)783c_0,1 c_1,1 c_0,3 c_2,1 + c_0,2 c_0,3 c_2,1 + c_1,3784"""785from steenrod_algebra_misc import milnor_mono_to_string, \786serre_cartan_mono_to_string, wood_mono_to_string, \787wall_mono_to_string, wall_long_mono_to_string, \788arnonA_mono_to_string, arnonA_long_mono_to_string, \789serre_cartan_mono_to_string, pst_mono_to_string, \790comm_long_mono_to_string, comm_mono_to_string791p = self.prime()792basis = self.basis_name()793if basis == 'milnor':794s = milnor_mono_to_string(t, p=p)795elif basis == 'serre-cartan':796s = serre_cartan_mono_to_string(t, p=p)797elif basis.find('wood') >= 0:798s = wood_mono_to_string(t)799elif basis == 'wall':800s = wall_mono_to_string(t)801elif basis == 'wall_long':802s = wall_long_mono_to_string(t)803elif basis == 'arnona':804s = arnonA_mono_to_string(t)805elif basis == 'arnona_long':806s = arnonA_long_mono_to_string(t)807elif basis == 'arnonc':808s = serre_cartan_mono_to_string(t)809elif basis.find('pst') >= 0:810s = pst_mono_to_string(t, p=p)811elif basis.find('comm') >= 0 and basis.find('long') >= 0:812s = comm_long_mono_to_string(t, p=p)813elif basis.find('comm') >= 0:814s = comm_mono_to_string(t, p=p)815s = s.translate(None, "{}")816return s817818def _latex_term(self, t):819"""820LaTeX representation of the monomial specified by the tuple ``t``.821822INPUT:823824- ``t`` - tuple, representing basis element in the current basis.825826OUTPUT: string827828The string depends on the basis over which the element is defined.829830EXAMPLES::831832sage: A7 = SteenrodAlgebra(7)833sage: A7._latex_term(((0, 3), (2,2)))834'Q_{0} Q_{3} \\mathcal{P}(2,2)'835sage: x = A7.Q(0,3) * A7.P(2,2)836sage: x._latex_() # indirect doctest837'Q_{0} Q_{3} \\mathcal{P}(2,2)'838sage: latex(x)839Q_{0} Q_{3} \mathcal{P}(2,2)840sage: b = Sq(0,2)841sage: b.change_basis('adem')._latex_()842'\\text{Sq}^{4} \\text{Sq}^{2} + \\text{Sq}^{5} \\text{Sq}^{1} +843\\text{Sq}^{6}'844sage: b.change_basis('woody')._latex_()845'\\text{Sq}^{2} \\text{Sq}^{3} \\text{Sq}^{1} + \\text{Sq}^{6} +846\\text{Sq}^{4} \\text{Sq}^{2}'847sage: SteenrodAlgebra(2, 'arnona')(b)._latex_()848'X^{1}_{1} X^{2}_{2} + X^{2}_{1}'849sage: SteenrodAlgebra(p=3, basis='serre-cartan').Q(0)._latex_()850'\\beta'851sage: latex(Sq(2).change_basis('adem').coproduct())8521 \otimes \text{Sq}^{2} + \text{Sq}^{1} \otimes \text{Sq}^{1} + \text{Sq}^{2} \otimes 1853"""854import re855s = self._repr_term(t)856s = re.sub(r"\^([0-9]*)", r"^{\1}", s)857s = re.sub("_([0-9,]*)", r"_{\1}", s)858s = s.replace("Sq", "\\text{Sq}")859s = s.replace("P", "\\mathcal{P}")860s = s.replace("beta", "\\beta")861return s862863def __eq__(self, right):864r"""865Two Steenrod algebras are equal iff their associated primes,866bases, and profile functions (if present) are equal. Because867this class inherits from :class:`UniqueRepresentation`, this868means that they are equal if and only they are identical: ``A869== B`` is True if and only if ``A is B`` is True.870871EXAMPLES::872873sage: A = SteenrodAlgebra(2)874sage: B = SteenrodAlgebra(2, 'adem')875sage: A == B876False877sage: C = SteenrodAlgebra(17)878sage: A == C879False880881sage: A1 = SteenrodAlgebra(2, profile=[2,1])882sage: A1 == A883False884sage: A1 == SteenrodAlgebra(2, profile=[2,1,0])885True886sage: A1 == SteenrodAlgebra(2, profile=[2,1], basis='pst')887False888"""889return self is right890891def __ne__(self, right):892r"""893The negation of the method ``__eq__``.894895EXAMPLES::896897sage: SteenrodAlgebra(p=2) != SteenrodAlgebra(p=2, profile=[2,1])898True899"""900return not self.__eq__(right)901902def profile(self, i, component=0):903r"""904Profile function for this algebra.905906INPUT:907908- `i` - integer909- ``component`` - either 0 or 1, optional (default 0)910911OUTPUT: integer or `\infty`912913See the documentation for914:mod:`sage.algebras.steenrod.steenrod_algebra` and915:func:`SteenrodAlgebra` for information on profile functions.916917This applies the profile function to the integer `i`. Thus918when `p=2`, `i` must be a positive integer. When `p` is odd,919there are two profile functions, `e` and `k` (in the notation920of the aforementioned documentation), corresponding,921respectively to ``component=0`` and ``component=1``. So when922`p` is odd and ``component`` is 0, `i` must be positive, while923when ``component`` is 1, `i` must be non-negative.924925EXAMPLES::926927sage: SteenrodAlgebra().profile(3)928+Infinity929sage: SteenrodAlgebra(profile=[3,2,1]).profile(1)9303931sage: SteenrodAlgebra(profile=[3,2,1]).profile(2)9322933934When the profile is specified by a list, the default behavior935is to return zero values outside the range of the list. This936can be overridden if the algebra is created with an infinite937``truncation_type``::938939sage: SteenrodAlgebra(profile=[3,2,1]).profile(9)9400941sage: SteenrodAlgebra(profile=[3,2,1], truncation_type=Infinity).profile(9)942+Infinity943944sage: B = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1))945sage: B.profile(3)9463947sage: B.profile(3, component=1)9481949"""950# determine the tuple t to use951p = self.prime()952if p == 2:953t = self._profile954elif component == 0:955t = self._profile[0]956else:957t = self._profile[1]958# case 1: exponents of the xi's959if p == 2 or component == 0:960if i <= 0:961return 0962try:963return t[i-1]964except IndexError:965return self._truncation_type966else:967# case 2: exponents of the tau's968if i < 0:969return 1970try:971return t[i]972except IndexError:973if self._truncation_type > 0:974return 2975else:976return 1977978def homogeneous_component(self, n):979"""980Return the nth homogeneous piece of the Steenrod algebra.981982INPUT:983984- `n` - integer985986OUTPUT: a vector space spanned by the basis for this algebra987in dimension `n`988989EXAMPLES::990991sage: A = SteenrodAlgebra()992sage: A.homogeneous_component(4)993Vector space spanned by (Sq(1,1), Sq(4)) over Finite Field of size 2994sage: SteenrodAlgebra(profile=[2,1,0]).homogeneous_component(4)995Vector space spanned by (Sq(1,1),) over Finite Field of size 2996997The notation A[n] may also be used::998999sage: A[5]1000Vector space spanned by (Sq(2,1), Sq(5)) over Finite Field of size 21001sage: SteenrodAlgebra(basis='wall')[4]1002Vector space spanned by (Q^1_0 Q^0_0, Q^2_2) over Finite Field of size 21003sage: SteenrodAlgebra(p=5)[17]1004Vector space spanned by (Q_1 P(1), Q_0 P(2)) over Finite Field of size 510051006Note that A[n] is just a vector space, not a Hopf algebra, so1007its elements don't have products, coproducts, or antipodes1008defined on them. If you want to use operations like this on1009elements of some A[n], then convert them back to elements of A::10101011sage: A[5].basis()1012Finite family {(5,): milnor[(5,)], (2, 1): milnor[(2, 1)]}1013sage: a = list(A[5].basis())[1]1014sage: a # not in A, doesn't print like an element of A1015milnor[(5,)]1016sage: A(a) # in A1017Sq(5)1018sage: A(a) * A(a)1019Sq(7,1)1020sage: a * A(a) # only need to convert one factor1021Sq(7,1)1022sage: a.antipode() # not defined1023Traceback (most recent call last):1024...1025AttributeError: 'CombinatorialFreeModule_with_category.element_class' object has no attribute 'antipode'1026sage: A(a).antipode() # convert to elt of A, then compute antipode1027Sq(2,1) + Sq(5)10281029sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')10301031TESTS:10321033The following sort of thing is also tested by the function1034:func:`steenrod_basis_error_check1035<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_basis_error_check>`::10361037sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])1038sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')1039sage: max([H[n].dimension() - G[n].dimension() for n in range(100)])104001041"""1042from sage.rings.all import GF1043basis = self._basis_fcn(n)1044M = CombinatorialFreeModule(GF(self.prime()), basis,1045element_class=self.Element,1046prefix=self._basis_name)1047M._name = "Vector space spanned by %s"%(tuple([self.monomial(a) for a in basis]),)1048return M10491050__getitem__ = homogeneous_component10511052def one_basis(self):1053"""1054The index of the element 1 in the basis for the Steenrod algebra.10551056EXAMPLES::10571058sage: SteenrodAlgebra(p=2).one_basis()1059()1060sage: SteenrodAlgebra(p=7).one_basis()1061((), ())1062"""1063p = self.prime()1064basis = self.basis_name()1065if basis == 'serre-cartan' or basis == 'arnonc':1066return (0,)1067if p == 2:1068return ()1069return ((), ())10701071def product_on_basis(self, t1, t2):1072"""1073The product of two basis elements of this algebra10741075INPUT:10761077- ``t1``, ``t2`` -- tuples, the indices of two basis elements of self10781079OUTPUT: the product of the two corresponding basis elements,1080as an element of self10811082ALGORITHM: If the two elements are represented in the Milnor1083basis, use Milnor multiplication as implemented in1084:mod:`sage.algebras.steenrod.steenrod_algebra_mult`. If the two1085elements are represented in the Serre-Cartan basis, then1086multiply them using Adem relations (also implemented in1087:mod:`sage.algebras.steenrod.steenrod_algebra_mult`). This1088provides a good way of checking work -- multiply Milnor1089elements, then convert them to Adem elements and multiply1090those, and see if the answers correspond.10911092If the two elements are represented in some other basis, then1093convert them both to the Milnor basis and multiply.10941095EXAMPLES::10961097sage: Milnor = SteenrodAlgebra()1098sage: Milnor.product_on_basis((2,), (2,))1099Sq(1,1)1100sage: Adem = SteenrodAlgebra(basis='adem')1101sage: Adem.Sq(2) * Adem.Sq(2) # indirect doctest1102Sq^3 Sq^111031104When multiplying elements from different bases, the left-hand1105factor determines the form of the output::11061107sage: Adem.Sq(2) * Milnor.Sq(2)1108Sq^3 Sq^11109sage: Milnor.Sq(2) * Adem.Sq(2)1110Sq(1,1)11111112TESTS::11131114sage: all([Adem(Milnor.Sq(n) ** 3)._repr_() == (Adem.Sq(n) ** 3)._repr_() for n in range(10)])1115True1116sage: Wall = SteenrodAlgebra(basis='wall')1117sage: Wall(Adem.Sq(4,4) * Milnor.Sq(4)) == Adem(Wall.Sq(4,4) * Milnor.Sq(4))1118True11191120sage: A3 = SteenrodAlgebra(p=3, basis='adem')1121sage: M3 = SteenrodAlgebra(p=3, basis='milnor')1122sage: 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)])1123True1124"""1125p = self.prime()1126basis = self.basis_name()1127if basis == 'milnor':1128if p == 2:1129from steenrod_algebra_mult import milnor_multiplication1130d = milnor_multiplication(t1, t2)1131else:1132from steenrod_algebra_mult import milnor_multiplication_odd1133d = milnor_multiplication_odd(t1, t2, p)1134return self._from_dict(d, coerce=True)1135elif basis == 'serre-cartan':1136from steenrod_algebra_mult import make_mono_admissible1137if p > 2:1138# make sure output has an odd number of terms. if both t11139# and t2 have an odd number, concatenate them, adding the1140# middle term...1141#1142# if either t1 or t2 has an even number of terms, append a1143# 0.1144if (len(t1) % 2) == 0:1145t1 = t1 + (0,)1146if (len(t2) % 2) == 0:1147t2 = t2 + (0,)1148if t1[-1] + t2[0] == 2:1149return self.zero()1150mono = t1[:-1] + (t1[-1] + t2[0],) + t2[1:]1151d = make_mono_admissible(mono, p)1152else: # p=21153mono = t1 + t21154while len(mono) > 1 and mono[-1] == 0:1155mono = mono[:-1]1156d = make_mono_admissible(mono)1157return self._from_dict(d, coerce=True)1158else:1159x = self({t1: 1})1160y = self({t2: 1})1161A = SteenrodAlgebra(basis='milnor', p=p)1162return self(A(x) * A(y))11631164def coproduct_on_basis(self, t, algorithm=None):1165r"""1166The coproduct of a basis element of this algebra11671168INPUT:11691170- ``t`` -- tuple, the index of a basis element of self11711172- ``algorithm`` -- None or a string, either 'milnor' or1173'serre-cartan' (or anything which will be converted to one1174of these by the function :func:`get_basis_name1175<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.1176If None, default to 'milnor' unless current basis is1177'serre-cartan', in which case use 'serre-cartan'.11781179ALGORITHM: The coproduct on a Milnor basis element `P(n_1,1180n_2, ...)` is `\sum P(i_1, i_2, ...) \otimes P(j_1, j_2,1181...)`, summed over all `i_k + j_k = n_k` for each `k`. At odd1182primes, each element `Q_n` is primitive: its coproduct is `Q_n1183\otimes 1 + 1 \otimes Q_n`.11841185One can deduce a coproduct formula for the Serre-Cartan basis1186from this: the coproduct on each `P^n` is `\sum P^i \otimes1187P^{n-i}` and at odd primes `\beta` is primitive. Since the1188coproduct is an algebra map, one can then compute the1189coproduct on any Serre-Cartan basis element.11901191Which of these methods is used is controlled by whether1192``algorithm`` is 'milnor' or 'serre-cartan'.11931194OUTPUT: the coproduct of the corresponding basis element,1195as an element of self tensor self.11961197EXAMPLES::11981199sage: A = SteenrodAlgebra()1200sage: A.coproduct_on_basis((3,))12011 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 112021203TESTS::12041205sage: all([A.coproduct_on_basis((n,1), algorithm='milnor') == A.coproduct_on_basis((n,1), algorithm='adem') for n in range(9)]) # long time1206True1207sage: A7 = SteenrodAlgebra(p=7, basis='adem')1208sage: 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 time1209True1210"""1211def coprod_list(t):1212"""1213if t = (n0, n1, ...), then return list of terms (i0, i1,1214...) where ik <= nk for each k. From each such term, can1215recover the second factor in the coproduct.1216"""1217if len(t) == 0:1218return [()]1219if len(t) == 1:1220return [[a] for a in range(t[0] + 1)]1221ans = []1222for i in range(t[0] + 1):1223ans.extend([[i] + x for x in coprod_list(t[1:])])1224return ans12251226from steenrod_algebra_misc import get_basis_name1227p = self.prime()1228basis = self.basis_name()1229if algorithm is None:1230if basis == 'serre-cartan':1231algorithm = 'serre-cartan'1232else:1233algorithm = 'milnor'1234else:1235algorithm = get_basis_name(algorithm, p)1236if basis == algorithm:1237if basis == 'milnor':1238if p == 2:1239left = coprod_list(t)1240right = [[x-y for (x,y) in zip(t, m)] for m in left]1241old = list(left)1242left = []1243# trim trailing zeros:1244for a in old:1245while len(a) > 0 and a[-1] == 0:1246a = a[:-1]1247left.append(tuple(a))1248old = list(right)1249right = []1250for a in old:1251while len(a) > 0 and a[-1] == 0:1252a = a[:-1]1253right.append(tuple(a))1254tens = dict().fromkeys(zip(left, right), 1)1255return self.tensor_square()._from_dict(tens)1256else: # p odd1257from sage.combinat.permutation import Permutation1258from steenrod_algebra_misc import convert_perm1259from sage.sets.set import Set1260left_p = coprod_list(t[1])1261right_p = [[x-y for (x,y) in zip(t[1], m)] for m in left_p]1262old = list(left_p)1263left_p = []1264# trim trailing zeros:1265for a in old:1266while len(a) > 0 and a[-1] == 0:1267a = a[:-1]1268left_p.append(tuple(a))1269old = list(right_p)1270right_p = []1271for a in old:1272while len(a) > 0 and a[-1] == 0:1273a = a[:-1]1274right_p.append(tuple(a))1275all_q = Set(t[0])1276tens_q = {}1277for a in all_q.subsets():1278left_q = sorted(list(a))1279right_q = sorted(list(all_q - a))1280sign = Permutation(convert_perm(left_q + right_q)).signature()1281tens_q[(tuple(left_q), tuple(right_q))] = sign1282tens = {}1283for l, r in zip(left_p, right_p):1284for q in tens_q:1285tens[((q[0], l), (q[1], r))] = tens_q[q]1286return self.tensor_square()._from_dict(tens)1287elif basis == 'serre-cartan':1288from sage.categories.tensor import tensor1289result = self.tensor_square().one()1290if p == 2:1291for n in t:1292s = self.tensor_square().zero()1293for i in range(0, n+1):1294s += tensor((self.Sq(i), self.Sq(n-i)))1295result = result * s1296return result1297else:1298bockstein = True1299for n in t:1300if bockstein:1301if n != 0:1302s = tensor((self.Q(0), self.one())) + tensor((self.one(), self.Q(0)))1303else:1304s = self.tensor_square().one()1305bockstein = False1306else:1307s = self.tensor_square().zero()1308for i in range(0, n+1):1309s += tensor((self.P(i), self.P(n-i)))1310bockstein = True1311result = result * s1312return result1313else:1314from sage.categories.tensor import tensor1315A = SteenrodAlgebra(p=p, basis=algorithm)1316x = A(self._change_basis_on_basis(t, algorithm)).coproduct(algorithm=algorithm)1317result = self.tensor_square().zero()1318for (a,b), coeff in x:1319result += coeff * tensor((A._change_basis_on_basis(a, basis),1320A._change_basis_on_basis(b, basis)))1321return result13221323def coproduct(self, x, algorithm='milnor'):1324r"""1325Return the coproduct of an element ``x`` of this algebra.13261327INPUT:13281329- ``x`` -- element of self13301331- ``algorithm`` -- None or a string, either 'milnor' or1332'serre-cartan' (or anything which will be converted to one1333of these by the function :func:`get_basis_name1334<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.1335If None, default to 'serre-cartan' if current basis is1336'serre-cartan'; otherwise use 'milnor'.13371338This calls :meth:`coproduct_on_basis` on the summands of ``x``1339and extends linearly.13401341EXAMPLES::13421343sage: SteenrodAlgebra().Sq(3).coproduct()13441 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 113451346The element `\text{Sq}(0,1)` is primitive::13471348sage: SteenrodAlgebra(basis='adem').Sq(0,1).coproduct()13491 # Sq^2 Sq^1 + 1 # Sq^3 + Sq^2 Sq^1 # 1 + Sq^3 # 11350sage: SteenrodAlgebra(basis='pst').Sq(0,1).coproduct()13511 # P^0_2 + P^0_2 # 113521353sage: SteenrodAlgebra(p=3).P(4).coproduct()13541 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 11355sage: SteenrodAlgebra(p=3).P(4).coproduct(algorithm='serre-cartan')13561 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 11357sage: SteenrodAlgebra(p=3, basis='serre-cartan').P(4).coproduct()13581 # P^4 + P^1 # P^3 + P^2 # P^2 + P^3 # P^1 + P^4 # 11359sage: SteenrodAlgebra(p=11, profile=((), (2,1,2))).Q(0,2).coproduct()13601 # Q_0 Q_2 + Q_0 # Q_2 + Q_0 Q_2 # 1 - Q_2 # Q_01361"""1362# taken from categories.coalgebras_with_basis, then modified1363# to allow the use of the "algorithm" keyword1364coprod = lambda x: self.coproduct_on_basis(x, algorithm)1365return Hom(self, tensor([self, self]), ModulesWithBasis(self.base_ring()))(on_basis = coprod)(x)13661367def antipode_on_basis(self, t):1368r"""1369The antipode of a basis element of this algebra13701371INPUT:13721373- ``t`` -- tuple, the index of a basis element of self13741375OUTPUT: the antipode of the corresponding basis element,1376as an element of self.13771378ALGORITHM: according to a result of Milnor's, the antipode of1379`\text{Sq}(n)` is the sum of all of the Milnor basis elements1380in dimension `n`. So: convert the element to the Serre-Cartan1381basis, thus writing it as a sum of products of elements1382`\text{Sq}(n)`, and use Milnor's formula for the antipode of1383`\text{Sq}(n)`, together with the fact that the antipode is an1384antihomomorphism: if we call the antipode `c`, then `c(ab) =1385c(b) c(a)`.13861387At odd primes, a similar method is used: the antipode of1388`P(n)` is the sum of the Milnor P basis elements in dimension1389`n*2(p-1)`, multiplied by `(-1)^n`, and the antipode of `\beta1390= Q_0` is `-Q_0`. So convert to the Serre-Cartan basis, as in1391the `p=2` case.13921393EXAMPLES::13941395sage: A = SteenrodAlgebra()1396sage: A.antipode_on_basis((4,))1397Sq(1,1) + Sq(4)1398sage: A.Sq(4).antipode()1399Sq(1,1) + Sq(4)1400sage: Adem = SteenrodAlgebra(basis='adem')1401sage: Adem.Sq(4).antipode()1402Sq^3 Sq^1 + Sq^41403sage: SteenrodAlgebra(basis='pst').Sq(3).antipode()1404P^0_1 P^1_1 + P^0_21405sage: a = SteenrodAlgebra(basis='wall_long').Sq(10)1406sage: a.antipode()1407Sq^1 Sq^2 Sq^4 Sq^1 Sq^2 + Sq^2 Sq^4 Sq^1 Sq^2 Sq^1 + Sq^8 Sq^21408sage: a.antipode().antipode() == a1409True14101411sage: SteenrodAlgebra(p=3).P(6).antipode()1412P(2,1) + P(6)1413sage: SteenrodAlgebra(p=3).P(6).antipode().antipode()1414P(6)14151416TESTS::14171418sage: Milnor = SteenrodAlgebra()1419sage: all([x.antipode().antipode() == x for x in Milnor.basis(11)]) # long time1420True1421sage: A5 = SteenrodAlgebra(p=5, basis='adem')1422sage: all([x.antipode().antipode() == x for x in A5.basis(25)])1423True1424sage: H = SteenrodAlgebra(profile=[2,2,1])1425sage: H.Sq(1,2).antipode() in H1426True1427"""1428p = self.prime()1429if self.basis_name() == 'serre-cartan':1430antipode = self.one()1431if p == 2:1432for n in t:1433antipode = self(sum(SteenrodAlgebra().basis(n))) * antipode1434else:1435from sage.misc.functional import is_even1436for index, n in enumerate(t):1437if is_even(index):1438if n != 0:1439antipode = -self.Q(0) * antipode1440else:1441B = SteenrodAlgebra(p=p).basis(n * 2 * (p-1))1442s = self(0)1443for b in B:1444if len(b.leading_support()[0]) == 0:1445s += self(b)1446antipode = (-1)**n * s * antipode1447return antipode1448return self(self._change_basis_on_basis(t, 'serre-cartan').antipode())14491450def _milnor_on_basis(self, t):1451"""1452Convert the tuple t in the current basis to an element in the1453Milnor basis.14541455INPUT:14561457- t - tuple, representing basis element in the current basis.14581459OUTPUT: element of the Steenrod algebra with the Milnor basis14601461ALGORITHM: there is a simple conversion from each basis to the1462Milnor basis, so use that. In more detail:14631464- If the current basis is the Milnor basis, just return the1465corresponding element.14661467- If the current basis is the Serre-Cartan basis: when `p=2`,1468the element `\text{Sq}^a` equals the Milnor element1469`\text{Sq}(a)`; when `p` is odd, `\mathcal{P}^a =1470\mathcal{P}(a)` and `\beta = Q_0`. Hence for any1471Serre-Cartan basis element, represent it in the1472Milnor basis by computing an appropriate product using1473Milnor multiplication.14741475- The same goes for Arnon's C basis, since the elements are1476monomials in the Steenrod squares.14771478- If the current basis is Wood's Y or Z bases, then each basis1479element is a monomial in the classes `w(m,k) =1480\text{Sq}^{2^m (2^{k+1}-1)}`. So again, multiply the1481corresponding Milnor elements together.14821483- The Wall basis: each basis element is a monomial in the1484elements `Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.14851486- Arnon's A basis: each basis element is a monomial in the1487elements `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`.14881489- The `P^s_t` bases: when `p=2`, each basis element is a1490monomial in the elements `P^s_t`. When `p` is odd, each1491basis element is a product of elements `Q_i` and a monomial1492in the elements `(P^s_t)^n` where `0 < n < p`.14931494- The commutator bases: when `p=2`, each basis element is a1495monomial in the iterated commutators `c_{i,j}`, defined by1496`c_{i,1} = \text{Sq}(2^i)` and `c_{i,j} = [c_{i,j-1},1497\text{Sq}(2^{i+j-1})]`. When `p` is odd, each basis element1498is a product of elements `Q_i` and a monomial in the1499elements `c_{i,j}^n` where `0 < n < p`, `c_{i,1} =1500P(p^i)` and `c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}]`.15011502EXAMPLES::15031504sage: Adem = SteenrodAlgebra(basis='serre-cartan')1505sage: Adem._milnor_on_basis((2,1)) # Sq^2 Sq^11506Sq(0,1) + Sq(3)1507sage: Pst = SteenrodAlgebra(basis='pst')1508sage: Pst._milnor_on_basis(((0,1), (1,1), (2,1)))1509Sq(7)1510"""1511basis = self.basis_name()1512p = self.prime()1513A = SteenrodAlgebra(p=p)1514# milnor1515if basis == 'milnor':1516return A({t: 1})15171518ans = A(1)1519# serre-cartan, arnonc1520if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):1521for j in t:1522ans = ans * A.Sq(j)15231524elif p > 2 and basis == 'serre-cartan':1525bockstein = True1526for j in t:1527if bockstein:1528if j != 0:1529ans = ans * A.Q(0)1530bockstein = False1531else:1532ans = ans * A.P(j)1533bockstein = True1534# wood_y:1535elif basis == 'woody' or basis == 'woodz':1536# each entry in t is a pair (m,k), corresponding to w(m,k), defined by1537# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.1538for (m,k) in t:1539ans = ans * A.Sq(2**m * (2**(k+1) - 1))15401541# wall[_long]1542elif basis.find('wall') >= 0:1543# each entry in t is a pair (m,k), corresponding to Q^m_k, defined by1544#`Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.1545for (m,k) in t:1546exponent = 2**k1547ans = ans * A.Sq(exponent)1548for i in range(m-k):1549exponent = exponent * 21550ans = ans * A.Sq(exponent)15511552# pst...1553elif basis.find('pst') >= 0:1554if p == 2:1555# each entry in t is a pair (i,j), corresponding to P^i_j1556for (i,j) in t:1557ans = ans * A.pst(i,j)1558else:1559# t = (Q, P) where Q is the tuple of Q_i's, and P is a1560# tuple with entries of the form ((i,j), n),1561# corresponding to (P^i_j)^n1562if len(t[0]) > 0:1563ans = ans * A.Q(*t[0])1564for ((i, j), n) in t[1]:1565ans = ans * (A.pst(i,j))**n15661567# arnona[_long]1568elif basis.find('arnona') >= 0:1569# each entry in t is a pair (m,k), corresponding to X^m_k, defined by1570# `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`1571for (m,k) in t:1572exponent = 2**k1573X = A.Sq(exponent)1574for i in range(m-k):1575exponent = exponent * 21576X = A.Sq(exponent) * X1577ans = ans * X15781579# comm...[_long]1580elif basis.find('comm') >= 0:1581if p == 2:1582# each entry in t is a pair (i,j), corresponding to1583# c_{i,j}, the iterated commutator defined by c_{i,1}1584# = Sq(2^i) and c_{i,j} = [c_{i,j-1}, Sq(2^{i+j-1})].1585for (i,j) in t:1586comm = A.Sq(2**i)1587for k in range(2, j+1):1588y = A.Sq(2**(i+k-1))1589comm = comm * y + y * comm1590ans = ans * comm1591else:1592# t = (Q, P) where Q is the tuple of Q_i's, and P is a1593# tuple with entries of the form ((i,j), n),1594# corresponding to (c_{i,j})^n. Here c_{i,j} is the1595# iterated commutator defined by c_{i,1} = P(p^i) and1596# c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}].1597if len(t[0]) > 0:1598ans = ans * A.Q(*t[0])1599for ((i, j), n) in t[1]:1600comm = A.P(p**i)1601for k in range(2, j+1):1602y = A.P(p**(i+k-1))1603comm = y * comm - comm * y1604ans = ans * comm**n1605return ans16061607@lazy_attribute1608def milnor(self):1609"""1610Convert an element of this algebra to the Milnor basis16111612INPUT:16131614- x - an element of this algebra16151616OUTPUT: x converted to the Milnor basis16171618ALGORITHM: use the method ``_milnor_on_basis`` and linearity.16191620EXAMPLES::16211622sage: Adem = SteenrodAlgebra(basis='adem')1623sage: a = Adem.Sq(2) * Adem.Sq(1)1624sage: Adem.milnor(a)1625Sq(0,1) + Sq(3)1626"""1627A = SteenrodAlgebra(p=self.prime(), basis='milnor')1628return self._module_morphism(self._milnor_on_basis, codomain=A)16291630def _change_basis_on_basis(self, t, basis='milnor'):1631"""1632Convert the tuple t to the named basis.16331634INPUT:16351636- ``t`` - tuple, representing basis element in the current basis.16371638- ``basis`` - string, the basis to which to convert, optional1639(default 'milnor')16401641OUTPUT: an element of the Steenrod algebra with basis ``basis``.16421643ALGORITHM: it's straightforward to convert to the Milnor basis1644(using :meth:`milnor` or :meth:`_milnor_on_basis`), so it's1645straightforward to produce a matrix representing this1646conversion in any degree. The function1647:func:`convert_from_milnor_matrix1648<steenrod_algebra_bases.convert_from_milnor_matrix>` provides1649the inverse operation.16501651So: convert from the current basis to the Milnor basis, then1652from the Milnor basis to the new basis.16531654EXAMPLES::16551656sage: Adem = SteenrodAlgebra(basis='adem')1657sage: a = Adem({(2,1): 1}); a1658Sq^2 Sq^11659sage: a.change_basis('adem') # indirect doctest1660Sq^2 Sq^11661sage: a.change_basis('milnor')1662Sq(0,1) + Sq(3)1663sage: a.change_basis('pst')1664P^0_1 P^1_1 + P^0_21665sage: a.change_basis('milnor').change_basis('adem').change_basis('adem')1666Sq^2 Sq^11667sage: a.change_basis('wall') == a.change_basis('woody')1668True16691670TESTS::16711672sage: a = sum(SteenrodAlgebra(basis='comm').basis(10))1673sage: a.change_basis('adem').change_basis('wall').change_basis('comm')._repr_() == a._repr_()1674True1675sage: a.change_basis('pst').change_basis('milnor').change_basis('comm')._repr_() == a._repr_()1676True1677sage: a.change_basis('woody').change_basis('arnona').change_basis('comm')._repr_() == a._repr_()1678True16791680sage: b = sum(SteenrodAlgebra(p=3).basis(41))1681sage: b.change_basis('adem').change_basis('adem').change_basis('milnor')._repr_() == b._repr_()1682True1683"""1684from sage.matrix.constructor import matrix1685from sage.rings.all import GF1686from steenrod_algebra_bases import steenrod_algebra_basis,\1687convert_from_milnor_matrix1688from steenrod_algebra_misc import get_basis_name1689basis = get_basis_name(basis, self.prime())1690if basis == self.basis_name():1691return self({t: 1})1692a = self._milnor_on_basis(t)1693if basis == 'milnor':1694return a1695d = a.monomial_coefficients()1696p = self.prime()1697deg = a.degree()1698A = SteenrodAlgebra(basis=basis, p=p)1699if deg == 0:1700return A(a.leading_coefficient())1701Bnew = steenrod_algebra_basis(deg, basis, p)1702Bmil = steenrod_algebra_basis(deg, 'milnor', p)1703v = []1704for a in Bmil:1705v.append(d.get(a, 0))1706out = (matrix(GF(p), 1, len(v), v) *1707convert_from_milnor_matrix(deg, basis, p))1708new_d = dict(zip(Bnew, out[0]))1709return A(new_d)17101711def _change_basis(self, x, basis='milnor'):1712"""1713Convert an element of this algebra to the specified basis17141715INPUT:17161717- ``x`` - an element of this algebra.17181719- ``basis`` - string, the basis to which to convert, optional1720(default 'milnor')17211722OUTPUT: an element of the Steenrod algebra with basis ``basis``.17231724ALGORITHM: use :meth:`_change_basis_on_basis` and linearity17251726EXAMPLES::17271728sage: Adem = SteenrodAlgebra(basis='adem')1729sage: a = Adem({(2,1): 1}); a1730Sq^2 Sq^11731sage: a.change_basis('adem') # indirect doctest1732Sq^2 Sq^11733sage: a.change_basis('milnor')1734Sq(0,1) + Sq(3)1735sage: a.change_basis('pst')1736P^0_1 P^1_1 + P^0_21737"""1738if basis == 'milnor':1739return x.milnor()1740A = SteenrodAlgebra(p=self.prime(), basis=basis)1741change = lambda y: self._change_basis_on_basis(y, basis)1742f = self._module_morphism(change, codomain=A)1743return f(x)17441745def degree_on_basis(self, t):1746r"""1747The degree of the monomial specified by the tuple ``t``.17481749INPUT:17501751- ``t`` - tuple, representing basis element in the current basis.17521753OUTPUT: integer, the degree of the corresponding element.17541755The degree of `\text{Sq}(i_1,i_2,i_3,...)` is17561757.. math::17581759i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....17601761At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the1762degree of `\mathcal{P}(i_1, i_2, ...)` is17631764.. math::17651766\sum_{k \geq 0} 2(p^k - 1) i_k.17671768ALGORITHM: Each basis element is represented in terms relevant1769to the particular basis: 'milnor' basis elements (at the prime17702) are given by tuples ``(a,b,c,...)`` corresponding to the1771element `\text{Sq}(a,b,c,...)`, while 'pst' basis elements are1772given by tuples of pairs ``((a, b), (c, d), ...)``,1773corresponding to the product `P^a_b P^c_d ...`. The other1774bases have similar descriptions. The degree of each basis1775element is computed from this data, rather than converting the1776element to the Milnor basis, for example, and then computing1777the degree.17781779EXAMPLES::17801781sage: SteenrodAlgebra().degree_on_basis((0,0,1))178271783sage: Sq(7).degree()1784717851786sage: A11 = SteenrodAlgebra(p=11)1787sage: A11.degree_on_basis(((), (1,1)))17882601789sage: A11.degree_on_basis(((2,), ()))17902411791"""1792def p_degree(m, mult=1, prime=2):1793"""1794For m=(n_1, n_2, n_3, ...), Sum_i (mult) * n_i * (p^i - 1)1795"""1796i = 01797deg = 01798for n in m:1799i += 11800deg += n*mult*(prime**i - 1)1801return deg18021803def q_degree(m, prime=3):1804"""1805For m=(n_0, n_1, n_2, ...), Sum_i 2*p^(n_i) - 11806"""1807deg = 01808for n in m:1809deg += 2*prime**n - 11810return deg18111812p = self.prime()1813basis = self.basis_name()1814# milnor1815if basis == 'milnor':1816if p == 2:1817return p_degree(t)1818else:1819return q_degree(t[0], prime=p) + p_degree(t[1], prime=p, mult=2)1820# serre-cartan, arnonc1821if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):1822return sum(t)1823if p > 2 and basis == 'serre-cartan':1824bockstein = True1825n = 01826for j in t:1827if bockstein:1828if j != 0:1829n += 11830bockstein = False1831else:1832n += 2 * j * (p - 1)1833bockstein = True1834return n18351836# wood_y:1837if basis == 'woody' or basis == 'woodz':1838# each entry in t is a pair (m,k), corresponding to w(m,k), defined by1839# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.1840return sum(2**m * (2**(k+1)-1) for (m,k) in t)18411842# wall, arnon_a1843if basis.find('wall') >= 0 or basis.find('arnona') >= 0:1844# Wall: each entry in t is a pair (m,k), corresponding to1845# Q^m_k, defined by `Q^m_k = Sq(2^k) Sq(2^{k+1})1846# ... Sq(2^m)`.1847#1848# Arnon A: each entry in t is a pair (m,k), corresponding1849# to X^m_k, defined by `X^m_k = Sq(2^m) ... Sq(2^{k+1})1850# Sq(2^k)`1851return sum(2**k * (2**(m-k+1)-1) for (m,k) in t)18521853# pst, comm1854if basis.find('pst') >= 0 or basis.find('comm') >= 0:1855if p == 2:1856# Pst: each entry in t is a pair (i,j), corresponding to P^i_j1857#1858# Comm: each entry in t is a pair (i,j), corresponding1859# to c_{i,j}, the iterated commutator defined by1860# c_{i,1} = Sq(2^i) and c_{i,j} = [c_{i,j-1},1861# Sq(2^{i+j-1})].1862return sum(2**m * (2**k - 1) for (m,k) in t)1863# p odd:1864#1865# Pst: have pair (Q, P) where Q is a tuple of Q's, as in1866# the Milnor basis, and P is a tuple of terms of the form1867# ((i,j), n), corresponding to (P^i_j)^n.1868#1869# Comm: similarly (Q, C) with Q as above and C a tuple1870# with each entry in t is of the form ((s,t), n),1871# corresponding to c_{s,t}^n. here c_{s,t} is the the1872# iterated commutator defined by c_{s,1} = P(p^s) and1873# c_{s,t} = [P(p^{s+t-1}), c_{s,t-1}].1874q_deg = q_degree(t[0], prime=p)1875p_deg = sum(2 * n * p**s * (p**t - 1) for ((s,t), n) in t[1])1876return q_deg + p_deg18771878# coercion methods:18791880def _coerce_map_from_(self, S):1881r"""1882True if there is a coercion from ``S`` to ``self``, False otherwise.18831884INPUT:18851886- ``S`` - a Sage object.18871888The algebras that coerce into the mod p Steenrod algebra are:18891890- the mod p Steenrod algebra `A`1891- its sub-Hopf algebras1892- its homogeneous components1893- its base field `GF(p)`1894- `ZZ`18951896Similarly, a sub-Hopf algebra `B` of `A` coerces into another1897sub-Hopf algebra `C` if and only if the profile function for1898`B` is less than or equal to that of `C`, pointwise.18991900EXAMPLES::19011902sage: A = SteenrodAlgebra()1903sage: A1 = SteenrodAlgebra(profile=[2,1])1904sage: A2 = SteenrodAlgebra(profile=[3,2,1])1905sage: B = SteenrodAlgebra(profile=[1,2,1])1906sage: A._coerce_map_from_(A1)1907True1908sage: A2._coerce_map_from_(A1)1909True1910sage: A1._coerce_map_from_(A)1911False1912sage: A1._coerce_map_from_(B)1913False1914sage: B._coerce_map_from_(A1)1915False19161917sage: A._coerce_map_from_(A[12])1918True19191920sage: A3 = SteenrodAlgebra(p=3)1921sage: A31 = SteenrodAlgebra(p=3, profile=([1], [2, 2]))1922sage: B3 = SteenrodAlgebra(p=3, profile=([1, 2, 1], [1]))1923sage: A3._coerce_map_from_(A31)1924True1925sage: A31._coerce_map_from_(A3)1926False1927sage: A31._coerce_map_from_(B3)1928False1929sage: B3._coerce_map_from_(A31)1930False1931"""1932from sage.rings.all import ZZ, GF1933from sage.rings.infinity import Infinity1934p = self.prime()1935if S == ZZ or S == GF(p):1936return True1937if (isinstance(S, SteenrodAlgebra_generic) and p == S.prime()):1938# deal with profiles.1939if p == 2:1940self_prec = len(self._profile)1941S_prec = len(S._profile)1942return all([self.profile(i) >= S.profile(i)1943for i in range(1, max(self_prec, S_prec)+1)])1944self_prec = len(self._profile[0])1945S_prec = len(S._profile[0])1946return (all([self.profile(i) >= S.profile(i)1947for i in range(1, max(self_prec, S_prec)+1)])1948and all([self.profile(i, 1) >= S.profile(i, 1)1949for i in range(1, max(self_prec, S_prec)+1)]))1950if (isinstance(S, CombinatorialFreeModule)1951and S.dimension() < Infinity and p == S.base_ring().characteristic()):1952from steenrod_algebra_misc import get_basis_name1953try:1954get_basis_name(S.prefix(), S.base_ring().characteristic())1955# return all([a in self for a in S.basis()])1956return True1957except ValueError:1958return False1959return False19601961def _element_constructor_(self, x):1962r"""1963Try to turn ``x`` into an element of ``self``.19641965INPUT:19661967- ``x`` - an element of some Steenrod algebra or an element of1968`\ZZ` or `\GF{p}` or a dict19691970OUTPUT: ``x`` as a member of ``self``.19711972If ``x`` is a dict, then call :meth:`_from_dict` on it,1973coercing the coefficients into the base field. That is, treat1974it as having entries of the form ``tuple: coeff``, where1975``tuple`` is a tuple representing a basis element and1976``coeff`` is the coefficient of that element.19771978EXAMPLES::19791980sage: A1 = SteenrodAlgebra(profile=[2,1])1981sage: A1(Sq(2)) # indirect doctest1982Sq(2)1983sage: A1._element_constructor_(Sq(2))1984Sq(2)1985sage: A1(3) # map integer into A1198611987sage: A1._element_constructor_(Sq(4)) # Sq(4) not in A11988Traceback (most recent call last):1989...1990ValueError: Element does not lie in this Steenrod algebra1991sage: A1({(2,): 1, (1,): 13})1992Sq(1) + Sq(2)1993"""1994from sage.rings.all import ZZ, GF1995if x in GF(self.prime()) or x in ZZ:1996return self.from_base_ring_from_one_basis(x)19971998if isinstance(x, dict):1999A = SteenrodAlgebra(p=self.prime(), basis=self.basis_name())2000x = A._from_dict(x, coerce=True)2001if x in self:2002if x.basis_name() == self.basis_name():2003if x.parent() is self:2004return x2005return self._from_dict(x.monomial_coefficients(), coerce=True)2006else:2007a = x.milnor()2008if self.basis_name() == 'milnor':2009return a2010return a.change_basis(self.basis_name())2011raise ValueError("Element does not lie in this Steenrod algebra")20122013def __contains__(self, x):2014r"""2015True if self contains x.20162017EXAMPLES::20182019sage: Sq(3,1,1) in SteenrodAlgebra()2020True2021sage: Sq(3,1,1) in SteenrodAlgebra(p=5)2022False20232024sage: A1 = SteenrodAlgebra(profile=[2,1])2025sage: Sq(3) in A12026True2027sage: Sq(4) in A12028False2029sage: Sq(0,2) in A12030False20312032sage: A_3 = SteenrodAlgebra(p=3)2033sage: B_3 = SteenrodAlgebra(p=3, profile=([1], [2,2,1,1]))2034sage: A_3.P(2) in B_32035True2036sage: A_3.P(3) in B_32037False2038sage: A_3.Q(1) in B_32039True2040sage: A_3.P(1) * A_3.Q(2) in B_32041False2042"""2043from sage.rings.all import GF2044p = self.prime()2045if (GF(p).__contains__(x)):2046return True2047if (isinstance(x, self.Element)2048and x.prime() == p):2049A = SteenrodAlgebra(p=p, basis=self.basis_name())2050if self._has_nontrivial_profile():2051return all([self._check_profile_on_basis(mono)2052for mono in A(x).support()])2053return True # trivial profile, so True2054return False20552056def basis(self, d=None):2057"""2058Returns basis for self, either the whole basis or the basis in2059degree `d`.20602061INPUT:20622063- `d` - integer or None, optional (default None)20642065OUTPUT: If `d` is None, then return a basis of the algebra.2066Otherwise, return the basis in degree `d`.20672068EXAMPLES::20692070sage: A3 = SteenrodAlgebra(3)2071sage: A3.basis(13)2072Family (Q_1 P(2), Q_0 P(3))2073sage: SteenrodAlgebra(2, 'adem').basis(12)2074Family (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)20752076sage: A = SteenrodAlgebra(profile=[1,2,1])2077sage: A.basis(2)2078Family ()2079sage: A.basis(3)2080Family (Sq(0,1),)2081sage: SteenrodAlgebra().basis(3)2082Family (Sq(0,1), Sq(3))2083sage: A_pst = SteenrodAlgebra(profile=[1,2,1], basis='pst')2084sage: A_pst.basis(3)2085Family (P^0_2,)20862087sage: A7 = SteenrodAlgebra(p=7)2088sage: B = SteenrodAlgebra(p=7, profile=([1,2,1], [1]))2089sage: A7.basis(84)2090Family (P(7),)2091sage: B.basis(84)2092Family ()2093sage: C = SteenrodAlgebra(p=7, profile=([1], [2,2]))2094sage: A7.Q(0,1) in C.basis(14)2095True2096sage: A7.Q(2) in A7.basis(97)2097True2098sage: A7.Q(2) in C.basis(97)2099False21002101With no arguments, return the basis of the whole algebra.2102This doesn't print in a very helpful way, unfortunately::21032104sage: A7.basis()2105Lazy family (Term map from Lazy family (<functools.partial object at ...>(i))_{i in Non negative integers} to mod 7 Steenrod algebra, milnor basis(i))_{i in Lazy family (<functools.partial object at ...>(i))_{i in Non negative integers}}2106"""2107from sage.sets.family import Family2108if d is None:2109return Family(self._basis_keys, self.monomial)2110else:2111return Family([self.monomial(tuple(a)) for a in self._basis_fcn(d)])21122113def _check_profile_on_basis(self, t):2114"""2115True if the element specified by the tuple ``t`` is in this2116algebra.21172118INPUT:21192120- ``t`` - tuple of ...212121222123EXAMPLES::21242125sage: A = SteenrodAlgebra(profile=[1,2,1])2126sage: A._check_profile_on_basis((0,0,1))2127True2128sage: A._check_profile_on_basis((0,0,2))2129False2130sage: A5 = SteenrodAlgebra(p=5, profile=([3,2,1], [2,2,2,2,2]))2131sage: A5._check_profile_on_basis(((), (1,5)))2132True2133sage: A5._check_profile_on_basis(((1,1,1), (1,5)))2134True2135sage: A5._check_profile_on_basis(((1,1,1), (1,5,5)))2136False2137"""2138if self.basis_name() != 'milnor':2139A = SteenrodAlgebra(p=self.prime(),2140profile=self._profile,2141truncation_type=self._truncation_type)2142return all([A._check_profile_on_basis(a[0])2143for a in self._milnor_on_basis(t)])21442145from sage.rings.infinity import Infinity2146p = self.prime()2147if not self._has_nontrivial_profile():2148return True2149profile = self.profile2150if p == 2:2151return all([self.profile(i+1) == Infinity2152or t[i] < 2**self.profile(i+1)2153for i in range(len(t))])2154# p odd:2155if any([self.profile(i,1) != 2 for i in t[0]]):2156return False2157return all([self.profile(i+1,0) == Infinity2158or t[1][i] < p**self.profile(i+1,0)2159for i in range(len(t[1]))])21602161def P(self, *nums):2162r"""2163The element `P(a, b, c, ...)`21642165INPUT:21662167- ``a, b, c, ...`` - non-negative integers21682169OUTPUT: element of the Steenrod algebra given by the Milnor2170single basis element `P(a, b, c, ...)`21712172Note that at the prime 2, this is the same element as2173`\text{Sq}(a, b, c, ...)`.21742175EXAMPLES::21762177sage: A = SteenrodAlgebra(2)2178sage: A.P(5)2179Sq(5)2180sage: B = SteenrodAlgebra(3)2181sage: B.P(5,1,1)2182P(5,1,1)2183sage: B.P(1,1,-12,1)2184Traceback (most recent call last):2185...2186TypeError: entries must be non-negative integers21872188sage: SteenrodAlgebra(basis='serre-cartan').P(0,1)2189Sq^2 Sq^1 + Sq^32190"""2191from sage.rings.all import Integer2192if self.basis_name() != 'milnor':2193return self(SteenrodAlgebra(p=self.prime()).P(*nums))2194while len(nums) > 0 and nums[-1] == 0:2195nums = nums[:-1]2196if len(nums) == 0 or (len(nums) == 1 and nums[0] == 0):2197return self.one()2198for i in nums:2199try:2200assert Integer(i) >= 02201except (TypeError, AssertionError):2202raise TypeError("entries must be non-negative integers")22032204if self.prime() == 2:2205t = nums2206else:2207t = ((), nums)2208if self._check_profile_on_basis(t):2209A = SteenrodAlgebra_generic(p=self.prime())2210a = A.monomial(t)2211return self(a)2212raise ValueError("Element not in this algebra")22132214def Q_exp(self, *nums):2215r"""2216The element `Q_0^{e_0} Q_1^{e_1} ...` , given by2217specifying the exponents.22182219INPUT:22202221- ``e0, e1, ...`` - sequence of 0s and 1s22222223OUTPUT: The element `Q_0^{e_0} Q_1^{e_1} ...`22242225Note that at the prime 2, `Q_n` is the element2226`\text{Sq}(0,0,...,1)` , where the 1 is in the2227`(n+1)^{st}` position.22282229Compare this to the method :meth:`Q`, which defines a similar2230element, but by specifying the tuple of subscripts of terms2231with exponent 1.22322233EXAMPLES::22342235sage: A2 = SteenrodAlgebra(2)2236sage: A5 = SteenrodAlgebra(5)2237sage: A2.Q_exp(0,0,1,1,0)2238Sq(0,0,1,1)2239sage: A5.Q_exp(0,0,1,1,0)2240Q_2 Q_32241sage: A5.Q(2,3)2242Q_2 Q_32243sage: A5.Q_exp(0,0,1,1,0) == A5.Q(2,3)2244True2245"""2246if not set(nums).issubset(set((0,1))):2247raise ValueError("The tuple %s should consist " % (nums,) + \2248"only of 0's and 1's")2249else:2250if self.basis_name() != 'milnor':2251return self(SteenrodAlgebra(p=self.prime()).Q_exp(*nums))2252while nums[-1] == 0:2253nums = nums[:-1]2254if self.prime() == 2:2255return self.P(*nums)2256else:2257mono = ()2258index = 02259for e in nums:2260if e == 1:2261mono = mono + (index,)2262index += 12263return self.Q(*mono)22642265def Q(self, *nums):2266r"""2267The element `Q_{n0} Q_{n1} ...` , given by specifying the2268subscripts.22692270INPUT:22712272- ``n0, n1, ...`` - non-negative integers22732274OUTPUT: The element `Q_{n0} Q_{n1} ...`22752276Note that at the prime 2, `Q_n` is the element2277`\text{Sq}(0,0,...,1)` , where the 1 is in the2278`(n+1)^{st}` position.22792280Compare this to the method :meth:`Q_exp`, which defines a2281similar element, but by specifying the tuple of exponents.22822283EXAMPLES::22842285sage: A2 = SteenrodAlgebra(2)2286sage: A2.Q(2,3)2287Sq(0,0,1,1)2288sage: A5 = SteenrodAlgebra(5)2289sage: A5.Q(1,4)2290Q_1 Q_42291sage: A5.Q(1,4) == A5.Q_exp(0,1,0,0,1)2292True2293sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])2294sage: H.Q(2)2295Q_22296sage: H.Q(4)2297Traceback (most recent call last):2298...2299ValueError: Element not in this algebra2300"""2301if len(nums) != len(set(nums)):2302return self(0)2303else:2304if self.basis_name() != 'milnor':2305return self(SteenrodAlgebra(p=self.prime()).Q(*nums))2306if self.prime() == 2:2307if len(nums) == 0:2308return self.one()2309else:2310list = (1+max(nums)) * [0]2311for i in nums:2312list[i] = 12313return self.Sq(*tuple(list))2314else:2315answer = self.one()2316for i in nums:2317answer = answer * self.monomial(((i,), ()))2318t = answer.leading_support()2319if self._check_profile_on_basis(t):2320return answer2321raise ValueError("Element not in this algebra")23222323def an_element(self):2324"""2325An element of this Steenrod algebra. The element depends on2326the basis and whether there is a nontrivial profile function.2327(This is used by the automatic test suite, so having different2328elements in different bases may help in discovering bugs.)23292330EXAMPLES::23312332sage: SteenrodAlgebra().an_element()2333Sq(2,1)2334sage: SteenrodAlgebra(basis='adem').an_element()2335Sq^4 Sq^2 Sq^12336sage: SteenrodAlgebra(p=5).an_element()23374 Q_1 Q_3 P(2,1)2338sage: SteenrodAlgebra(basis='pst').an_element()2339P^3_12340sage: SteenrodAlgebra(basis='pst', profile=[3,2,1]).an_element()2341P^0_12342"""2343from sage.rings.all import GF2344basis = self.basis_name()2345p = self.prime()23462347if self._has_nontrivial_profile():2348if self.ngens() > 0:2349return self.gen(0)2350else:2351return self.one()23522353if basis == 'milnor' and p == 2:2354return self.monomial((2,1))2355if basis == 'milnor' and p > 2:2356return self.term(((1,3), (2,1)), GF(p)(p-1))2357if basis == 'serre-cartan' and p == 2:2358return self.monomial((4,2,1))2359if basis == 'serre-cartan' and p > 2:2360return self.term((1,p,0,1,0), GF(p)(p-1))2361if basis == 'woody' or basis == 'woodz':2362return self._from_dict({((3,0),): 1, ((1, 1), (1, 0)): 1}, coerce=True)2363if basis.find('wall') >= 0:2364return self._from_dict({((1,1), (1,0)): 1, ((2, 2), (0, 0)): 1}, coerce=True)2365if basis.find('arnona') >= 0:2366return self._from_dict({((3,3),): 1, ((1, 1), (2, 1)): 1}, coerce=True)2367if basis == 'arnonc':2368return self._from_dict({(8,): 1, (4, 4): 1}, coerce=True)2369if basis.find('pst') >= 0:2370if p == 2:2371return self.monomial(((3, 1),))2372return self.term(((1,), (((1,1), 2),)), GF(p)(p-1))2373if basis.find('comm') >= 0:2374if p == 2:2375return self.monomial(((1, 2),))2376return self.term(((), (((1,2), 1),)), GF(p)(p-1))23772378def pst(self,s,t):2379r"""2380The Margolis element `P^s_t`.23812382INPUT:23832384- ``s`` - non-negative integer23852386- ``t`` - positive integer23872388- ``p`` - positive prime number23892390OUTPUT: element of the Steenrod algebra23912392This returns the Margolis element `P^s_t` of the mod2393`p` Steenrod algebra: the element equal to2394`P(0,0,...,0,p^s)`, where the `p^s` is in position2395`t`.23962397EXAMPLES::23982399sage: A2 = SteenrodAlgebra(2)2400sage: A2.pst(3,5)2401Sq(0,0,0,0,8)2402sage: A2.pst(1,2) == Sq(4)*Sq(2) + Sq(2)*Sq(4)2403True2404sage: SteenrodAlgebra(5).pst(3,5)2405P(0,0,0,0,125)2406"""2407from sage.rings.all import Integer2408if self.basis_name() != 'milnor':2409return self(SteenrodAlgebra(p=self.prime()).pst(s,t))2410if not isinstance(s, (Integer, int)) and s >= 0:2411raise ValueError("%s is not a non-negative integer" % s)2412if not isinstance(t, (Integer, int)) and t > 0:2413raise ValueError("%s is not a positive integer" % t)2414nums = (0,)*(t-1) + (self.prime()**s,)2415return self.P(*nums)24162417def ngens(self):2418r"""2419Number of generators of self.24202421OUTPUT: number or Infinity24222423The Steenrod algebra is infinitely generated. A sub-Hopf2424algebra may be finitely or infinitely generated; in general,2425it is not clear what a minimal generating set is, nor the2426cardinality of that set. So: if the algebra is2427infinite-dimensional, this returns Infinity. If the algebra2428is finite-dimensional and is equal to one of the sub-Hopf2429algebras `A(n)`, then their minimal generating set is known,2430and this returns the cardinality of that set. Otherwise, any2431sub-Hopf algebra is (not necessarily minimally) generated by2432the `P^s_t`'s that it contains (along with the `Q_n`'s it2433contains, at odd primes), so this returns the number of2434`P^s_t`'s and `Q_n`'s in the algebra.24352436EXAMPLES::24372438sage: A = SteenrodAlgebra(3)2439sage: A.ngens()2440+Infinity2441sage: SteenrodAlgebra(profile=lambda n: n).ngens()2442+Infinity2443sage: SteenrodAlgebra(profile=[3,2,1]).ngens() # A(2)244432445sage: SteenrodAlgebra(profile=[3,2,1], basis='pst').ngens()244632447sage: SteenrodAlgebra(p=3, profile=[[3,2,1], [2,2,2,2]]).ngens() # A(3) at p=3244842449sage: SteenrodAlgebra(profile=[1,2,1,1]).ngens()245052451"""2452from sage.rings.infinity import Infinity2453if self._truncation_type == Infinity:2454return Infinity2455n = self.profile(1)2456p = self.prime()2457if p == 2 and self._profile == AA(n-1, p=p)._profile:2458return n2459if p > 2 and self._profile == AA(n, p=p)._profile:2460return n+12461if p == 2:2462return sum(self._profile)2463return sum(self._profile[0]) + len([a for a in self._profile[1] if a == 2])24642465def gens(self):2466r"""2467Family of generators for this algebra.24682469OUTPUT: family of elements of this algebra24702471At the prime 2, the Steenrod algebra is generated by the2472elements `\text{Sq}^{2^i}` for `i \geq 0`. At odd primes, it2473is generated by the elements `Q_0` and `\mathcal{P}^{p^i}` for2474`i \geq 0`. So if this algebra is the entire Steenrod2475algebra, return an infinite family made up of these elements.24762477For sub-Hopf algebras of the Steenrod algebra, it is not2478always clear what a minimal generating set is. The sub-Hopf2479algebra `A(n)` is minimally generated by the elements2480`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At2481odd primes, `A(n)` is minimally generated by `Q_0` along with2482`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this2483algebra is `A(n)`, return the appropriate list of generators.24842485For other sub-Hopf algebras: return a non-minimal generating2486set: the family of `P^s_t`'s and `Q_n`'s contained in the2487algebra.24882489EXAMPLES::24902491sage: A3 = SteenrodAlgebra(3, 'adem')2492sage: A3.gens()2493Lazy family (<bound method SteenrodAlgebra_generic_with_category.gen of mod 3 Steenrod algebra, serre-cartan basis>(i))_{i in Non negative integers}2494sage: A3.gens()[0]2495beta2496sage: A3.gens()[1]2497P^12498sage: A3.gens()[2]2499P^32500sage: SteenrodAlgebra(profile=[3,2,1]).gens()2501Family (Sq(1), Sq(2), Sq(4))25022503In the following case, return a non-minimal generating set.2504(It is not minimal because `\text{Sq}(0,0,1)` is the2505commutator of `\text{Sq}(1)` and `\text{Sq}(0,2)`.) ::25062507sage: SteenrodAlgebra(profile=[1,2,1]).gens()2508Family (Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1))2509sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).gens()2510Family (Q_0, P(1), P(5))2511sage: SteenrodAlgebra(profile=lambda n: n).gens()2512Lazy 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}25132514You may also use ``algebra_generators`` instead of ``gens``::25152516sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).algebra_generators()2517Family (Q_0, P(1), P(5))2518"""2519from sage.sets.family import Family2520from sage.sets.non_negative_integers import NonNegativeIntegers2521from sage.rings.infinity import Infinity2522n = self.ngens()2523if n < Infinity:2524return Family([self.gen(i) for i in range(n)])2525return Family(NonNegativeIntegers(), self.gen)25262527algebra_generators = gens25282529def gen(self, i=0):2530r"""2531The ith generator of this algebra.25322533INPUT:25342535- ``i`` - non-negative integer25362537OUTPUT: the ith generator of this algebra25382539For the full Steenrod algebra, the `i^{th}` generator is2540`\text{Sq}(2^i)` at the prime 2; when `p` is odd, the 0th generator2541is `\beta = Q(0)`, and for `i>0`, the `i^{th}` generator is2542`P(p^{i-1})`.25432544For sub-Hopf algebras of the Steenrod algebra, it is not2545always clear what a minimal generating set is. The sub-Hopf2546algebra `A(n)` is minimally generated by the elements2547`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At2548odd primes, `A(n)` is minimally generated by `Q_0` along with2549`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this2550algebra is `A(n)`, return the appropriate generator.25512552For other sub-Hopf algebras: they are generated (but not2553necessarily minimally) by the `P^s_t`'s (and `Q_n`'s, if `p`2554is odd) that they contain. So order the `P^s_t`'s (and2555`Q_n`'s) in the algebra by degree and return the `i`-th one.25562557EXAMPLES::25582559sage: A = SteenrodAlgebra(2)2560sage: A.gen(4)2561Sq(16)2562sage: A.gen(200)2563Sq(1606938044258990275541962092341162602522202993782792835301376)2564sage: SteenrodAlgebra(2, basis='adem').gen(2)2565Sq^42566sage: SteenrodAlgebra(2, basis='pst').gen(2)2567P^2_12568sage: B = SteenrodAlgebra(5)2569sage: B.gen(0)2570Q_02571sage: B.gen(2)2572P(5)25732574sage: SteenrodAlgebra(profile=[2,1]).gen(1)2575Sq(2)2576sage: SteenrodAlgebra(profile=[1,2,1]).gen(1)2577Sq(0,1)2578sage: SteenrodAlgebra(profile=[1,2,1]).gen(5)2579Traceback (most recent call last):2580...2581ValueError: This algebra only has 4 generators, so call gen(i) with 0 <= i < 425822583sage: D = SteenrodAlgebra(profile=lambda n: n)2584sage: [D.gen(n) for n in range(5)]2585[Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1), Sq(0,0,2)]2586sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 2))2587sage: [D3.gen(n) for n in range(9)]2588[Q_0, P(1), Q_1, P(0,1), Q_2, P(0,3), P(0,0,1), Q_3, P(0,0,3)]2589sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1 if n<1 else 2))2590sage: [D3.gen(n) for n in range(9)]2591[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)]2592sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst').gen(2)2593P^1_12594"""2595from sage.rings.infinity import Infinity2596from sage.rings.all import Integer2597p = self.prime()2598if not isinstance(i, (Integer, int)) and i >= 0:2599raise ValueError("%s is not a non-negative integer" % i)2600num = self.ngens()2601if num < Infinity:2602if i >= num:2603raise ValueError("This algebra only has %s generators, so call gen(i) with 0 <= i < %s" % (num, num))2604# check to see if equal to A(n) for some n.2605n = self.profile(1)2606if p == 2 and self._profile == AA(n-1, p=p)._profile:2607return self.pst(i,1)2608if p > 2 and self._profile == AA(n, p=p)._profile:2609if i == 0:2610return self.Q(0)2611return self.pst(i-1, 1)2612# if not A(n), return list of P^s_t's in algebra, along with Q's if p is odd2613idx = -12614if p == 2:2615last_t = len(self._profile)2616else:2617last_t = max(len(self._profile[0]), len(self._profile[1]))2618last_s = self.profile(last_t)2619for j in range(1, last_s + last_t + 1):2620if p > 2 and self.profile(j-1, 1) == 2:2621guess = self.Q(j-1)2622idx += 12623if idx == i:2624elt = guess2625break2626for t in range(1, min(j, last_t) + 1):2627s = j - t2628if self.profile(t) > s:2629guess = self.pst(s,t)2630idx += 12631if idx == i:2632elt = guess2633break2634return elt26352636# entire Steenrod algebra:2637if self.profile(1) == Infinity:2638if p == 2:2639return self.Sq(p**i)2640elif self.profile(0,1) == 2:2641if i == 0:2642return self.Q(0)2643else:2644return self.P(p**(i-1))26452646# infinite-dimensional sub-Hopf algebra2647idx = -12648tot = 12649found = False2650A = SteenrodAlgebra(p=p)2651while not found:2652if p > 2:2653test = A.Q(tot-1)2654if test in self:2655idx += 12656if idx == i:2657found = True2658break2659for t in range(1, tot+1):2660s = tot - t2661test = A.pst(s,t)2662if test in self:2663idx += 12664if idx == i:2665found = True2666break2667tot += 12668return test26692670def is_commutative(self):2671r"""2672True if ``self`` is graded commutative, as determined by the2673profile function. In particular, a sub-Hopf algebra of the2674mod 2 Steenrod algebra is commutative if and only if there is2675an integer `n>0` so that its profile function `e` satisfies26762677- `e(i) = 0` for `i < n`,2678- `e(i) \leq n` for `i \geq n`.26792680When `p` is odd, there must be an integer `n \geq 0` so that2681the profile functions `e` and `k` satisfy26822683- `e(i) = 0` for `i < n`,2684- `e(i) \leq n` for `i \geq n`.2685- `k(i) = 1` for `i < n`.26862687EXAMPLES::26882689sage: A = SteenrodAlgebra(p=3)2690sage: A.is_commutative()2691False2692sage: SteenrodAlgebra(profile=[2,1]).is_commutative()2693False2694sage: SteenrodAlgebra(profile=[0,2,2,1]).is_commutative()2695True26962697Note that if the profile function is specified by a function,2698then by default it has infinite truncation type: the profile2699function is assumed to be infinite after the 100th term. ::27002701sage: SteenrodAlgebra(profile=lambda n: 1).is_commutative()2702False2703sage: SteenrodAlgebra(profile=lambda n: 1, truncation_type=0).is_commutative()2704True27052706sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [])).is_commutative()2707True2708sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [1,1,2])).is_commutative()2709True2710sage: SteenrodAlgebra(p=5, profile=([0,2,1], [1,2,2,2])).is_commutative()2711False2712"""2713if not self._has_nontrivial_profile() or self._truncation_type > 0:2714return False2715p = self.prime()2716if p == 2:2717n = max(self._profile)2718return all([self.profile(i) == 0 for i in range(1, n)])2719n = max(self._profile[0])2720return (all([self.profile(i,0) == 0 for i in range(1, n)])2721and all([self.profile(i,1) == 1 for i in range(n)]))27222723def is_finite(self):2724r"""2725True if this algebra is finite-dimensional.27262727Therefore true if the profile function is finite, and in2728particular the ``truncation_type`` must be finite.27292730EXAMPLES::27312732sage: A = SteenrodAlgebra(p=3)2733sage: A.is_finite()2734False2735sage: SteenrodAlgebra(profile=[3,2,1]).is_finite()2736True2737sage: SteenrodAlgebra(profile=lambda n: n).is_finite()2738False2739"""2740return self._has_nontrivial_profile() and self._truncation_type == 027412742def dimension(self):2743r"""2744The dimension of this algebra as a vector space over `\GF{p}`.27452746If the algebra is infinite, return ``+Infinity``. Otherwise,2747the profile function must be finite. In this case, at the2748prime 2, its dimension is `2^s`, where `s` is the sum of the2749entries in the profile function. At odd primes, the dimension2750is `p^s * 2^t` where `s` is the sum of the `e` component of2751the profile function and `t` is the number of 2's in the `k`2752component of the profile function.27532754EXAMPLES::27552756sage: SteenrodAlgebra(p=7).dimension()2757+Infinity2758sage: SteenrodAlgebra(profile=[3,2,1]).dimension()2759642760sage: SteenrodAlgebra(p=3, profile=([1,1], [])).dimension()276192762sage: SteenrodAlgebra(p=5, profile=([1], [2,2])).dimension()2763202764"""2765from sage.rings.infinity import Infinity2766if not self.is_finite():2767return Infinity2768p = self.prime()2769if p == 2:2770return 2**sum(self._profile)2771return p**sum(self._profile[0]) * 2**len([a for a in self._profile[1] if a == 2])27722773def order(self):2774r"""2775The order of this algebra.27762777This is computed by computing its vector space dimension `d`2778and then returning `p^d`.27792780EXAMPLES::27812782sage: SteenrodAlgebra(p=7).order()2783+Infinity2784sage: SteenrodAlgebra(profile=[2,1]).dimension()278582786sage: SteenrodAlgebra(profile=[2,1]).order()27872562788sage: SteenrodAlgebra(p=3, profile=([1], [])).dimension()278932790sage: SteenrodAlgebra(p=3, profile=([1], [])).order()2791272792sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).dimension()279342794sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).order() == 5**42795True2796"""2797from sage.rings.infinity import Infinity2798if not self.is_finite():2799return Infinity2800return self.prime() ** self.dimension()28012802def is_division_algebra(self):2803r"""2804The only way this algebra can be a division algebra is if it2805is the ground field `\GF{p}`.28062807EXAMPLES::28082809sage: SteenrodAlgebra(11).is_division_algebra()2810False2811sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_division_algebra()2812True2813"""2814return self.is_field()28152816def is_field(self, proof = True):2817r"""2818The only way this algebra can be a field is if it is the2819ground field `\GF{p}`.28202821EXAMPLES::28222823sage: SteenrodAlgebra(11).is_field()2824False2825sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_field()2826True2827"""2828return self.dimension() == 128292830def is_integral_domain(self, proof = True):2831r"""2832The only way this algebra can be an integral domain is if it2833is the ground field `\GF{p}`.28342835EXAMPLES::28362837sage: SteenrodAlgebra(11).is_integral_domain()2838False2839sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_integral_domain()2840True2841"""2842return self.is_field()28432844def is_noetherian(self):2845"""2846This algebra is noetherian if and only if it is finite.28472848EXAMPLES::28492850sage: SteenrodAlgebra(3).is_noetherian()2851False2852sage: SteenrodAlgebra(profile=[1,2,1]).is_noetherian()2853True2854sage: SteenrodAlgebra(profile=lambda n: n+2).is_noetherian()2855False2856"""2857return self.is_finite()28582859######################################################2860# element class2861######################################################28622863class Element(CombinatorialFreeModuleElement):2864r"""2865Class for elements of the Steenrod algebra. Since the2866Steenrod algebra class is based on2867:class:`CombinatorialFreeModule2868<sage.combinat.free_module.CombinatorialFreeModule>`, this is2869based on :class:`CombinatorialFreeModuleElement2870<sage.combinat.free_module.CombinatorialFreeModuleElement>`.2871It has new methods reflecting its role, like :meth:`degree`2872for computing the degree of an element.28732874EXAMPLES:28752876Since this class inherits from2877:class:`CombinatorialFreeModuleElement2878<sage.combinat.free_module.CombinatorialFreeModuleElement>`,2879elements can be used as iterators, and there are other useful2880methods::28812882sage: c = Sq(5).antipode(); c2883Sq(2,1) + Sq(5)2884sage: for mono, coeff in c: print coeff, mono28851 (5,)28861 (2, 1)2887sage: c.monomial_coefficients()2888{(5,): 1, (2, 1): 1}2889sage: c.monomials()2890[Sq(2,1), Sq(5)]2891sage: c.support()2892[(2, 1), (5,)]28932894See the documentation for this module (type2895``sage.algebras.steenrod.steenrod_algebra?``) for more2896information about elements of the Steenrod algebra.2897"""2898def prime(self):2899"""2900The prime associated to self.29012902EXAMPLES::29032904sage: a = SteenrodAlgebra().Sq(3,2,1)2905sage: a.prime()290622907sage: a.change_basis('adem').prime()290822909sage: b = SteenrodAlgebra(p=7).basis(36)[0]2910sage: b.prime()291172912sage: SteenrodAlgebra(p=3, basis='adem').one().prime()291332914"""2915return self.base_ring().characteristic()29162917def basis_name(self):2918"""2919The basis name associated to self.29202921EXAMPLES::29222923sage: a = SteenrodAlgebra().Sq(3,2,1)2924sage: a.basis_name()2925'milnor'2926sage: a.change_basis('adem').basis_name()2927'serre-cartan'2928sage: a.change_basis('wood____y').basis_name()2929'woody'2930sage: b = SteenrodAlgebra(p=7).basis(36)[0]2931sage: b.basis_name()2932'milnor'2933sage: a.change_basis('adem').basis_name()2934'serre-cartan'2935"""2936return self.parent().prefix()29372938def is_homogeneous(self):2939"""2940Return True iff this element is homogeneous.29412942EXAMPLES::29432944sage: (Sq(0,0,1) + Sq(7)).is_homogeneous()2945True2946sage: (Sq(0,0,1) + Sq(2)).is_homogeneous()2947False2948"""2949monos = self.support()2950if len(monos) <= 1:2951return True2952degree = None2953deg = self.parent().degree_on_basis2954for mono in monos:2955if degree is None:2956degree = deg(mono)2957elif deg(mono) != degree:2958return False2959return True29602961def degree(self):2962r"""2963The degree of self.29642965The degree of `\text{Sq}(i_1,i_2,i_3,...)` is29662967.. math::29682969i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....29702971At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the2972degree of `\mathcal{P}(i_1, i_2, ...)` is29732974.. math::29752976\sum_{k \geq 0} 2(p^k - 1) i_k.29772978ALGORITHM: If :meth:`is_homogeneous` returns True, call2979:meth:`SteenrodAlgebra_generic.degree_on_basis` on the leading2980summand.29812982EXAMPLES::29832984sage: Sq(0,0,1).degree()298572986sage: (Sq(0,0,1) + Sq(7)).degree()298772988sage: (Sq(0,0,1) + Sq(2)).degree()2989Traceback (most recent call last):2990...2991ValueError: Element is not homogeneous.29922993sage: A11 = SteenrodAlgebra(p=11)2994sage: A11.P(1).degree()2995202996sage: A11.P(1,1).degree()29972602998sage: A11.Q(2).degree()299924130003001TESTS::30023003sage: all([x.degree() == 10 for x in SteenrodAlgebra(basis='woody').basis(10)])3004True3005sage: all([x.degree() == 11 for x in SteenrodAlgebra(basis='woodz').basis(11)])3006True3007sage: all([x.degree() == x.milnor().degree() for x in SteenrodAlgebra(basis='wall').basis(11)])3008True3009sage: a = SteenrodAlgebra(basis='pst').basis(10)[0]3010sage: a.degree() == a.change_basis('arnonc').degree()3011True3012sage: b = SteenrodAlgebra(basis='comm').basis(12)[1]3013sage: b.degree() == b.change_basis('adem').change_basis('arnona').degree()3014True3015sage: all([x.degree() == 9 for x in SteenrodAlgebra(basis='comm').basis(9)])3016True3017sage: all([x.degree() == 8 for x in SteenrodAlgebra(basis='adem').basis(8)])3018True3019sage: all([x.degree() == 7 for x in SteenrodAlgebra(basis='milnor').basis(7)])3020True3021sage: all([x.degree() == 24 for x in SteenrodAlgebra(p=3).basis(24)])3022True3023sage: all([x.degree() == 40 for x in SteenrodAlgebra(p=5, basis='serre-cartan').basis(40)])3024True3025"""3026if len(self.support()) == 0:3027raise ValueError("The zero element does not have a well-defined degree.")3028try:3029assert self.is_homogeneous()3030return self.parent().degree_on_basis(self.leading_support())3031except AssertionError:3032raise ValueError("Element is not homogeneous.")30333034def milnor(self):3035"""3036Return this element in the Milnor basis; that is, as an3037element of the appropriate Steenrod algebra.30383039This just calls the method3040:meth:`SteenrodAlgebra_generic.milnor`.30413042EXAMPLES::30433044sage: Adem = SteenrodAlgebra(basis='adem')3045sage: a = Adem.basis(4)[1]; a3046Sq^3 Sq^13047sage: a.milnor()3048Sq(1,1)3049"""3050A = self.parent()3051return A.milnor(self)30523053def change_basis(self, basis='milnor'):3054r"""3055Representation of element with respect to basis.30563057INPUT:30583059- ``basis`` - string, basis in which to work.30603061OUTPUT: representation of self in given basis30623063The choices for ``basis`` are:30643065- 'milnor' for the Milnor basis.3066- 'serre-cartan', 'serre_cartan', 'sc', 'adem', 'admissible'3067for the Serre-Cartan basis.3068- 'wood_y' for Wood's Y basis.3069- 'wood_z' for Wood's Z basis.3070- 'wall' for Wall's basis.3071- 'wall_long' for Wall's basis, alternate representation3072- 'arnon_a' for Arnon's A basis.3073- 'arnon_a_long' for Arnon's A basis, alternate representation.3074- 'arnon_c' for Arnon's C basis.3075- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz' for3076various `P^s_t`-bases.3077- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz'3078for various commutator bases.3079- 'comm_long', 'comm_rlex_long', etc., for commutator bases,3080alternate representations.30813082See documentation for this module (by browsing the3083reference manual or by typing3084``sage.algebras.steenrod.steenrod_algebra?``) for3085descriptions of the different bases.30863087EXAMPLES::30883089sage: c = Sq(2) * Sq(1)3090sage: c.change_basis('milnor')3091Sq(0,1) + Sq(3)3092sage: c.change_basis('serre-cartan')3093Sq^2 Sq^13094sage: d = Sq(0,0,1)3095sage: d.change_basis('arnonc')3096Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^73097"""3098A = self.parent()3099return A._change_basis(self, basis)31003101def _basis_dictionary(self,basis):3102r"""3103Convert self to ``basis``, returning a dictionary of terms of3104the form (mono: coeff), where mono is a monomial in the given3105basis.31063107INPUT:31083109- ``basis`` - string, basis in which to work31103111OUTPUT: dictionary31123113This just calls :meth:`change_basis` to get an element of the3114Steenrod algebra with the new basis, and then calls3115:meth:`monomial_coefficients` on this element to produce its3116dictionary representation.31173118EXAMPLES::31193120sage: c = Sq(2) * Sq(1)3121sage: c._basis_dictionary('milnor')3122{(0, 1): 1, (3,): 1}3123sage: c3124Sq(0,1) + Sq(3)3125sage: c._basis_dictionary('serre-cartan')3126{(2, 1): 1}3127sage: c.change_basis('serre-cartan')3128Sq^2 Sq^13129sage: d = Sq(0,0,1)3130sage: d._basis_dictionary('arnonc')3131{(7,): 1, (2, 5): 1, (4, 3): 1, (4, 2, 1): 1}3132sage: d.change_basis('arnonc')3133Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^731343135At odd primes::31363137sage: e = 2 * SteenrodAlgebra(3).P(1,2)3138sage: e._basis_dictionary('milnor')3139{((), (1, 2)): 2}3140sage: e31412 P(1,2)3142sage: e._basis_dictionary('serre-cartan')3143{(0, 7, 0, 2, 0): 2, (0, 8, 0, 1, 0): 2}3144sage: e.change_basis('adem')31452 P^7 P^2 + 2 P^8 P^13146"""3147a = self.change_basis(basis)3148return a.monomial_coefficients()31493150def basis(self, basis):3151r"""3152Representation of element with respect to basis.31533154INPUT:31553156- ``basis`` - string, basis in which to work.31573158OUTPUT: Representation of self in given basis31593160.. warning::31613162Deprecated (December 2010). Use :meth:`change_basis` instead.31633164EXAMPLES::31653166sage: c = Sq(2) * Sq(1)3167sage: c.basis('milnor')3168doctest:...: DeprecationWarning: (Since Sage 4.6.2) The .basis() method is deprecated. Use .change_basis() instead.3169Sq(0,1) + Sq(3)3170"""3171from sage.misc.misc import deprecation3172deprecation('The .basis() method is deprecated. Use .change_basis() instead.',3173version='Sage 4.6.2')3174return self.change_basis(basis)31753176def coproduct(self, algorithm='milnor'):3177"""3178The coproduct of this element.31793180INPUT:31813182- ``algorithm`` -- None or a string, either 'milnor' or3183'serre-cartan' (or anything which will be converted to3184one of these by the function :func:`get_basis_name3185<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`).3186If None, default to 'serre-cartan' if current basis is3187'serre-cartan'; otherwise use 'milnor'.31883189See :meth:`SteenrodAlgebra_generic.coproduct_on_basis` for3190more information on computing the coproduct.31913192EXAMPLES::31933194sage: a = Sq(2)3195sage: a.coproduct()31961 # Sq(2) + Sq(1) # Sq(1) + Sq(2) # 13197sage: b = Sq(4)3198sage: (a*b).coproduct() == (a.coproduct()) * (b.coproduct())3199True32003201sage: c = a.change_basis('adem'); c.coproduct(algorithm='milnor')32021 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 13203sage: c = a.change_basis('adem'); c.coproduct(algorithm='adem')32041 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 132053206sage: d = a.change_basis('comm_long'); d.coproduct()32071 # s_2 + s_1 # s_1 + s_2 # 132083209sage: A7 = SteenrodAlgebra(p=7)3210sage: a = A7.Q(1) * A7.P(1); a3211Q_1 P(1)3212sage: a.coproduct()32131 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 13214sage: a.coproduct(algorithm='adem')32151 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 13216"""3217A = self.parent()3218return A.coproduct(self, algorithm=algorithm)32193220def excess(self):3221r"""3222Excess of element.32233224OUTPUT: ``excess`` - non-negative integer32253226The excess of a Milnor basis element `\text{Sq}(a,b,c,...)` is3227`a + b + c + ...`. When `p` is odd, the excess of `Q_{0}^{e_0}3228Q_{1}^{e_1} ... P(r_1, r_2, ...)` is `\sum e_i + 2 \sum r_i`.3229The excess of a linear combination of Milnor basis elements is3230the minimum of the excesses of those basis elements.32313232See [Kra] for the proofs of these assertions.32333234REFERENCES:32353236- [Kra] D. Kraines, "On excess in the Milnor basis," Bull. London3237Math. Soc. 3 (1971), 363-365.32383239EXAMPLES::32403241sage: a = Sq(1,2,3)3242sage: a.excess()324363244sage: (Sq(0,0,1) + Sq(4,1) + Sq(7)).excess()324513246sage: [m.excess() for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]3247[1, 5, 7]3248sage: [m for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]3249[Sq(0,0,1), Sq(4,1), Sq(7)]3250sage: B = SteenrodAlgebra(7)3251sage: a = B.Q(1,2,5)3252sage: b = B.P(2,2,3)3253sage: a.excess()325433255sage: b.excess()3256143257sage: (a + b).excess()325833259sage: (a * b).excess()3260173261"""3262def excess_odd(mono):3263"""3264Excess of mono, where mono has the form ((s0, s1, ...), (r1, r2,3265...)).32663267Returns the length of the first component, since that is the number3268of factors, plus twice the sum of the terms in the second3269component.3270"""3271if len(mono) == 0:3272return 03273else:3274return len(mono[0]) + 2 * sum(mono[1])32753276p = self.prime()3277a = self.milnor()3278if p == 2:3279excesses = [sum(mono) for mono in a.support()]3280else:3281excesses = [excess_odd(mono) for mono in a.support()]3282return min(excesses)32833284def is_unit(self):3285"""3286True if element has a nonzero scalar multiple of P(0) as a summand,3287False otherwise.32883289EXAMPLES::32903291sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)3292sage: z.is_unit()3293False3294sage: u = Sq(0) + Sq(3,1)3295sage: u == 1 + Sq(3,1)3296True3297sage: u.is_unit()3298True3299sage: A5 = SteenrodAlgebra(5)3300sage: v = A5.P(0)3301sage: (v + v + v).is_unit()3302True3303"""3304return self.parent().one() in self.monomials()33053306def is_nilpotent(self):3307"""3308True if element is not a unit, False otherwise.33093310EXAMPLES::33113312sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)3313sage: z.is_nilpotent()3314True3315sage: u = 1 + Sq(3,1)3316sage: u == 1 + Sq(3,1)3317True3318sage: u.is_nilpotent()3319False3320"""3321return not self.is_unit()33223323def may_weight(self):3324r"""3325May's 'weight' of element.33263327OUTPUT: ``weight`` - non-negative integer33283329If we let `F_* (A)` be the May filtration of the Steenrod3330algebra, the weight of an element `x` is the integer `k` so3331that `x` is in `F_k(A)` and not in `F_{k+1}(A)`. According to3332Theorem 2.6 in May's thesis [May], the weight of a Milnor3333basis element is computed as follows: first, to compute the3334weight of `P(r_1,r_2, ...)`, write each `r_i` in base `p` as3335`r_i = \sum_j p^j r_{ij}`. Then each nonzero binary digit3336`r_{ij}` contributes `i` to the weight: the weight is3337`\sum_{i,j} i r_{ij}`. When `p` is odd, the weight of `Q_i` is3338`i+1`, so the weight of a product `Q_{i_1} Q_{i_2} ...` equals3339`(i_1+1) + (i_2+1) + ...`. Then the weight of `Q_{i_1} Q_{i_2}3340...P(r_1,r_2, ...)` is the sum of `(i_1+1) + (i_2+1) + ...`3341and `\sum_{i,j} i r_{ij}`.33423343The weight of a sum of Milnor basis elements is the minimum of3344the weights of the summands.33453346When `p=2`, we compute the weight on Milnor basis elements by3347adding up the terms in their 'height' - see3348:meth:`wall_height` for documentation. (When `p` is odd, the3349height of an element is not defined.)33503351REFERENCES:33523353- [May]: J. P. May, "The cohomology of restricted Lie algebras and of3354Hopf algebras; application to the Steenrod algebra." Thesis,3355Princeton Univ., 1964.33563357EXAMPLES::33583359sage: Sq(0).may_weight()336003361sage: a = Sq(4)3362sage: a.may_weight()336313364sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)3365sage: b.may_weight()336623367sage: Sq(2,1,5).wall_height()3368[2, 3, 2, 1, 1]3369sage: Sq(2,1,5).may_weight()337093371sage: A5 = SteenrodAlgebra(5)3372sage: a = A5.Q(1,2,4)3373sage: b = A5.P(1,2,1)3374sage: a.may_weight()3375103376sage: b.may_weight()337783378sage: (a * b).may_weight()3379183380sage: A5.P(0,0,1).may_weight()338133382"""3383from sage.rings.infinity import Infinity3384from sage.rings.all import Integer3385p = self.prime()3386if self == 0:3387return Infinity3388elif self.is_unit():3389return 03390elif p == 2:3391wt = Infinity3392for mono in self.milnor().monomials():3393wt = min(wt, sum(mono.wall_height()))3394return wt3395else: # p odd3396wt = Infinity3397for (mono1, mono2) in self.milnor().support():3398P_wt = 03399index = 13400for n in mono2:3401P_wt += index * sum(Integer(n).digits(p))3402index += 13403wt = min(wt, sum(mono1) + len(mono1) + P_wt)3404return wt34053406def is_decomposable(self):3407r"""3408Return True if element is decomposable, False otherwise.3409That is, if element is in the square of the augmentation ideal,3410return True; otherwise, return False.34113412OUTPUT: boolean34133414EXAMPLES::34153416sage: a = Sq(6)3417sage: a.is_decomposable()3418True3419sage: for i in range(9):3420... if not Sq(i).is_decomposable():3421... print Sq(i)342213423Sq(1)3424Sq(2)3425Sq(4)3426Sq(8)3427sage: A3 = SteenrodAlgebra(p=3, basis='adem')3428sage: [A3.P(n) for n in range(30) if not A3.P(n).is_decomposable()]3429[1, P^1, P^3, P^9, P^27]34303431TESTS:34323433These all test changing bases and printing in various bases::34343435sage: A = SteenrodAlgebra(basis='milnor')3436sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3437[1, Sq(1), Sq(2), Sq(4), Sq(8)]3438sage: A = SteenrodAlgebra(basis='wall_long')3439sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3440[1, Sq^1, Sq^2, Sq^4, Sq^8]3441sage: A = SteenrodAlgebra(basis='arnona_long')3442sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]3443[1, Sq^1, Sq^2, Sq^4, Sq^8]3444sage: A = SteenrodAlgebra(basis='woodz')3445sage: [A.Sq(n) for n in range(20) if not A.Sq(n).is_decomposable()] # long time3446[1, Sq^1, Sq^2, Sq^4, Sq^8, Sq^16]3447sage: A = SteenrodAlgebra(basis='comm_long')3448sage: [A.Sq(n) for n in range(25) if not A.Sq(n).is_decomposable()] # long time3449[1, s_1, s_2, s_4, s_8, s_16]3450"""3451return self.may_weight() > 134523453def wall_height(self):3454r"""3455Wall's 'height' of element.34563457OUTPUT: list of non-negative integers34583459The height of an element of the mod 2 Steenrod algebra is a3460list of non-negative integers, defined as follows: if the3461element is a monomial in the generators `\text{Sq}(2^i)`, then3462the `i^{th}` entry in the list is the number of times3463`\text{Sq}(2^i)` appears. For an arbitrary element, write it3464as a sum of such monomials; then its height is the maximum,3465ordered right-lexicographically, of the heights of those3466monomials.34673468When `p` is odd, the height of an element is not defined.34693470According to Theorem 3 in [Wall], the height of the Milnor3471basis element `\text{Sq}(r_1, r_2, ...)` is obtained as3472follows: write each `r_i` in binary as `r_i = \sum_j 2^j3473r_{ij}`. Then each nonzero binary digit `r_{ij}` contributes 13474to the `k^{th}` entry in the height, for `j \leq k \leq3475i+j-1`.34763477REFERENCES:34783479- [Wall]: C. T. C. Wall, "Generators and relations for the Steenrod3480algebra," Ann. of Math. (2) **72** (1960), 429-444.34813482EXAMPLES::34833484sage: Sq(0).wall_height()3485[]3486sage: a = Sq(4)3487sage: a.wall_height()3488[0, 0, 1]3489sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)3490sage: b.wall_height()3491[0, 0, 1, 1]3492sage: Sq(0,0,3).wall_height()3493[1, 2, 2, 1]3494"""3495from sage.rings.all import Integer3496if self.prime() > 2:3497raise NotImplementedError("Wall height is not defined at odd primes.")3498if self == 0 or self == 1:3499return []3500result = []3501deg = self.parent().degree_on_basis3502for r in self.milnor().support():3503h = [0]*(1 + deg(r))3504i = 13505for x in r:3506if x > 0:3507for j in range(1+Integer(x).exact_log(2)):3508if (2**j & x) != 0:3509for k in range(j,i+j):3510h[k] += 13511i=i+13512h.reverse()3513result = max(h, result)3514result.reverse()3515while len(result) > 0 and result[-1] == 0:3516result = result[:-1]3517return result35183519def additive_order(self):3520"""3521The additive order of any nonzero element of the mod p3522Steenrod algebra is p.35233524OUTPUT: 1 (for the zero element) or p (for anything else)35253526EXAMPLES::35273528sage: z = Sq(4) + Sq(6) + 13529sage: z.additive_order()353023531sage: (Sq(3) + Sq(3)).additive_order()353213533"""3534if self == 0:3535return 13536return self.prime()35373538class SteenrodAlgebra_mod_two(SteenrodAlgebra_generic):3539"""3540The mod 2 Steenrod algebra.35413542Users should not call this, but use the function3543:func:`SteenrodAlgebra` instead. See that function for extensive3544documentation. (This differs from :class:`SteenrodAlgebra_generic`3545only in that it has a method :meth:`Sq` for defining elements.)3546"""3547def Sq(self, *nums):3548r"""3549Milnor element `\text{Sq}(a,b,c,...)`.35503551INPUT:35523553- ``a, b, c, ...`` - non-negative integers35543555OUTPUT: element of the Steenrod algebra35563557This returns the Milnor basis element3558`\text{Sq}(a, b, c, ...)`.35593560EXAMPLES::35613562sage: A = SteenrodAlgebra(2)3563sage: A.Sq(5)3564Sq(5)3565sage: A.Sq(5,0,2)3566Sq(5,0,2)35673568Entries must be non-negative integers; otherwise, an error3569results.3570"""3571if self.prime() == 2:3572return self.P(*nums)3573else:3574raise ValueError("Sq is only defined at the prime 2")35753576def SteenrodAlgebra(p=2, basis='milnor', **kwds):3577r"""3578The mod `p` Steenrod algebra35793580INPUT:35813582- ``p`` - positive prime integer (optional, default = 2)3583- ``basis`` - string (optional, default = 'milnor')3584- ``profile`` - a profile function in form specified below (optional, default ``None``)3585- ``truncation_type`` - 0 or `\infty` or 'auto' (optional, default 'auto')3586- ``precision`` - integer or ``None`` (optional, default ``None``)35873588OUTPUT: mod `p` Steenrod algebra or one of its sub-Hopf algebras,3589elements of which are printed using ``basis``35903591See below for information about ``basis``, ``profile``, etc.35923593EXAMPLES:35943595Some properties of the Steenrod algebra are available::35963597sage: A = SteenrodAlgebra(2)3598sage: A.order()3599+Infinity3600sage: A.is_finite()3601False3602sage: A.is_commutative()3603False3604sage: A.is_noetherian()3605False3606sage: A.is_integral_domain()3607False3608sage: A.is_field()3609False3610sage: A.is_division_algebra()3611False3612sage: A.category()3613Category of graded hopf algebras with basis over Finite Field of size 236143615There are methods for constructing elements of the Steenrod3616algebra::36173618sage: A2 = SteenrodAlgebra(2); A23619mod 2 Steenrod algebra, milnor basis3620sage: A2.Sq(1,2,6)3621Sq(1,2,6)3622sage: A2.Q(3,4) # product of Milnor primitives Q_3 and Q_43623Sq(0,0,0,1,1)3624sage: A2.pst(2,3) # Margolis pst element3625Sq(0,0,4)3626sage: A5 = SteenrodAlgebra(5); A53627mod 5 Steenrod algebra, milnor basis3628sage: A5.P(1,2,6)3629P(1,2,6)3630sage: A5.Q(3,4)3631Q_3 Q_43632sage: A5.Q(3,4) * A5.P(1,2,6)3633Q_3 Q_4 P(1,2,6)3634sage: A5.pst(2,3)3635P(0,0,25)36363637You can test whether elements are contained in the Steenrod3638algebra::36393640sage: w = Sq(2) * Sq(4)3641sage: w in SteenrodAlgebra(2)3642True3643sage: w in SteenrodAlgebra(17)3644False36453646.. rubric:: Different bases for the Steenrod algebra:36473648There are two standard vector space bases for the mod `p` Steenrod3649algebra: the Milnor basis and the Serre-Cartan basis. When `p=2`,3650there are also several other, less well-known, bases. See the3651documentation for this module (type3652``sage.algebras.steenrod.steenrod_algebra?``) and the function3653:func:`steenrod_algebra_basis3654<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_algebra_basis_>`3655for full descriptions of each of the implemented bases.36563657This module implements the following bases at all primes:36583659- 'milnor': Milnor basis.36603661- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.36623663- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz': various3664`P^s_t`-bases.36653666- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz', or3667these with '_long' appended: various commutator bases.36683669It implements the following bases when `p=2`:36703671- 'wood_y': Wood's Y basis.36723673- 'wood_z': Wood's Z basis.36743675- 'wall', 'wall_long': Wall's basis.36763677- 'arnon_a', 'arnon_a_long': Arnon's A basis.36783679- 'arnon_c': Arnon's C basis.36803681When defining a Steenrod algebra, you can specify a basis. Then3682elements of that Steenrod algebra are printed in that basis::36833684sage: adem = SteenrodAlgebra(2, 'adem')3685sage: x = adem.Sq(2,1) # Sq(-) always means a Milnor basis element3686sage: x3687Sq^4 Sq^1 + Sq^53688sage: y = Sq(0,1) # unadorned Sq defines elements w.r.t. Milnor basis3689sage: y3690Sq(0,1)3691sage: adem(y)3692Sq^2 Sq^1 + Sq^33693sage: adem5 = SteenrodAlgebra(5, 'serre-cartan')3694sage: adem5.P(0,2)3695P^10 P^2 + 4 P^11 P^1 + P^1236963697If you add or multiply elements defined using different bases, the3698left-hand factor determines the form of the output::36993700sage: SteenrodAlgebra(basis='adem').Sq(3) + SteenrodAlgebra(basis='pst').Sq(0,1)3701Sq^2 Sq^13702sage: SteenrodAlgebra(basis='pst').Sq(3) + SteenrodAlgebra(basis='milnor').Sq(0,1)3703P^0_1 P^1_1 + P^0_23704sage: SteenrodAlgebra(basis='milnor').Sq(2) * SteenrodAlgebra(basis='arnonc').Sq(2)3705Sq(1,1)37063707You can get a list of basis elements in a given dimension::37083709sage: A3 = SteenrodAlgebra(3, 'milnor')3710sage: A3.basis(13)3711Family (Q_1 P(2), Q_0 P(3))37123713Algebras defined over different bases are not equal::37143715sage: SteenrodAlgebra(basis='milnor') == SteenrodAlgebra(basis='pst')3716False37173718Bases have various synonyms, and in general Sage tries to figure3719out what basis you meant::37203721sage: SteenrodAlgebra(basis='MiLNOr')3722mod 2 Steenrod algebra, milnor basis3723sage: SteenrodAlgebra(basis='MiLNOr') == SteenrodAlgebra(basis='milnor')3724True3725sage: SteenrodAlgebra(basis='adem')3726mod 2 Steenrod algebra, serre-cartan basis3727sage: SteenrodAlgebra(basis='adem').basis_name()3728'serre-cartan'3729sage: SteenrodAlgebra(basis='wood---z---').basis_name()3730'woodz'37313732As noted above, several of the bases ('arnon_a', 'wall', 'comm')3733have alternate, sometimes longer, representations. These provide3734ways of expressing elements of the Steenrod algebra in terms of3735the `\text{Sq}^{2^n}`.37363737::37383739sage: A_long = SteenrodAlgebra(2, 'arnon_a_long')3740sage: A_long(Sq(6))3741Sq^1 Sq^2 Sq^1 Sq^2 + Sq^2 Sq^43742sage: SteenrodAlgebra(2, 'wall_long')(Sq(6))3743Sq^2 Sq^1 Sq^2 Sq^1 + Sq^2 Sq^43744sage: SteenrodAlgebra(2, 'comm_deg_long')(Sq(6))3745s_1 s_2 s_12 + s_2 s_437463747.. rubric:: Sub-Hopf algebras of the Steenrod algebra:37483749These are specified using the argument ``profile``, along with,3750optionally, ``truncation_type`` and ``precision``. The3751``profile`` argument specifies the profile function for this3752algebra. Any sub-Hopf algebra of the Steenrod algebra is3753determined by its *profile function*. When `p=2`, this is a map `e`3754from the positive integers to the set of non-negative integers,3755plus `\infty`, corresponding to the sub-Hopf algebra dual to this3756quotient of the dual Steenrod algebra:37573758.. math::37593760\GF{2} [\xi_1, \xi_2, \xi_3, ...] / (\xi_1^{2^{e(1)}}, \xi_2^{2^{e(2)}}, \xi_3^{2^{e(3)}}, ...).37613762The profile function `e` must satisfy the condition37633764- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.37653766This is specified via ``profile``, and optionally ``precision``3767and ``truncation_type``. First, ``profile`` must have one of the3768following forms:37693770- a list or tuple, e.g., ``[3,2,1]``, corresponding to the3771function sending 1 to 3, 2 to 2, 3 to 1, and all other integers3772to the value of ``truncation_type``.3773- a function from positive integers to non-negative integers (and3774`\infty`), e.g., ``lambda n: n+2``.3775- ``None`` or ``Infinity`` - use this for the profile function for3776the whole Steenrod algebra.37773778In the first and third cases, ``precision`` is ignored. In the3779second case, this function is converted to a tuple of length one3780less than ``precision``, which has default value 100. The3781function is truncated at this point, and all remaining values are3782set to the value of ``truncation_type``.37833784``truncation_type`` may be 0, `\infty`, or 'auto'. If it's3785'auto', then it gets converted to 0 in the first case above (when3786``profile`` is a list), and otherwise (when ``profile`` is a3787function, ``None``, or ``Infinity``) it gets converted to `\infty`.37883789For example, the sub-Hopf algebra `A(2)` has profile function3790``[3,2,1,0,0,0,...]``, so it can be defined by any of the3791following::37923793sage: A2 = SteenrodAlgebra(profile=[3,2,1])3794sage: B2 = SteenrodAlgebra(profile=[3,2,1,0,0]) # trailing 0's ignored3795sage: A2 == B23796True3797sage: C2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=0)3798sage: A2 == C23799True38003801In the following case, the profile function is specified by a3802function and ``truncation_type`` isn't specified, so it defaults3803to `\infty`; therefore this gives a different sub-Hopf algebra::38043805sage: D2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0))3806sage: A2 == D23807False3808sage: D2.is_finite()3809False3810sage: E2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=Infinity)3811sage: D2 == E23812True38133814The argument ``precision`` only needs to be specified if the3815profile function is defined by a function and you want to control3816when the profile switches from the given function to the3817truncation type. For example::38183819sage: D3 = SteenrodAlgebra(profile=lambda n: n, precision=3)3820sage: D33821sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, +Infinity, +Infinity, +Infinity, ...]3822sage: D4 = SteenrodAlgebra(profile=lambda n: n, precision=4); D43823sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, 3, +Infinity, +Infinity, +Infinity, ...]3824sage: D3 == D43825False38263827When `p` is odd, ``profile`` is a pair of functions `e` and `k`,3828corresponding to the quotient38293830.. math::38313832\GF{p} [\xi_1, \xi_2, \xi_3, ...] \otimes \Lambda (\tau_0,3833\tau_1, ...) / (\xi_1^{p^{e_1}}, \xi_2^{p^{e_2}}, ...;3834\tau_0^{k_0}, \tau_1^{k_1}, ...).38353836Together, the functions `e` and `k` must satisfy the conditions38373838- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`,38393840- if `k(i+j) = 1`, then either `e(i) \leq j` or `k(j) = 1` for all `i3841\geq 1`, `j \geq 0`.38423843Therefore ``profile`` must have one of the following forms:38443845- a pair of lists or tuples, the second of which takes values in3846the set `\{1,2\}`, e.g., ``([3,2,1,1], [1,1,2,2,1])``.38473848- a pair of functions, one from the positive integers to3849non-negative integers (and `\infty`), one from the non-negative3850integers to the set `\{1,2\}`, e.g., ``(lambda n: n+2, lambda n:38511 if n<3 else 2)``.38523853- ``None`` or ``Infinity`` - use this for the profile function for3854the whole Steenrod algebra.38553856You can also mix and match the first two, passing a pair with3857first entry a list and second entry a function, for instance. The3858values of ``precision`` and ``truncation_type`` are determined by3859the first entry.38603861More examples::38623863sage: E = SteenrodAlgebra(profile=lambda n: 0 if n<3 else 3, truncation_type=0)3864sage: E.is_commutative()3865True38663867sage: A2 = SteenrodAlgebra(profile=[3,2,1]) # the algebra A(2)3868sage: Sq(7,3,1) in A23869True3870sage: Sq(8) in A23871False3872sage: Sq(8) in SteenrodAlgebra().basis(8)3873True3874sage: Sq(8) in A2.basis(8)3875False3876sage: A2.basis(8)3877Family (Sq(1,0,1), Sq(2,2), Sq(5,1))38783879sage: A5 = SteenrodAlgebra(p=5)3880sage: A51 = SteenrodAlgebra(p=5, profile=([1], [2,2]))3881sage: A5.Q(0,1) * A5.P(4) in A513882True3883sage: A5.Q(2) in A513884False3885sage: A5.P(5) in A513886False38873888For sub-Hopf algebras of the Steenrod algebra, only the Milnor3889basis or the various `P^s_t`-bases may be used. ::38903891sage: SteenrodAlgebra(profile=[1,2,1,1], basis='adem')3892Traceback (most recent call last):3893...3894NotImplementedError: For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.38953896TESTS:38973898Testing unique parents::38993900sage: S0 = SteenrodAlgebra(2)3901sage: S1 = SteenrodAlgebra(2)3902sage: S0 is S13903True3904sage: S2 = SteenrodAlgebra(2, basis='adem')3905sage: S0 is S23906False3907sage: S0 == S23908False3909sage: A1 = SteenrodAlgebra(profile=[2,1])3910sage: B1 = SteenrodAlgebra(profile=[2,1,0,0])3911sage: A1 is B13912True3913"""3914if p == 2:3915return SteenrodAlgebra_mod_two(p=2, basis=basis, **kwds)3916else:3917return SteenrodAlgebra_generic(p=p, basis=basis, **kwds)391839193920def AA(n=None, p=2):3921r"""3922This returns the Steenrod algebra `A` or its sub-Hopf algebra `A(n)`.39233924INPUT:39253926- `n` - non-negative integer, optional (default None)3927- `p` - prime number, optional (default 2)39283929OUTPUT: If `n` is None, then return the full Steenrod algebra.3930Otherwise, return `A(n)`.39313932When `p=2`, `A(n)` is the sub-Hopf algebra generated by the3933elements `\text{Sq}^i` for `i \leq 2^n`. Its profile function is3934`(n+1, n, n-1, ...)`. When `p` is odd, `A(n)` is the sub-Hopf3935algebra generated by the elements `Q_0` and `\mathcal{P}^i` for `i3936\leq p^{n-1}`. Its profile function is `e=(n, n-1, n-2, ...)`3937and `k=(2, 2, ..., 2)` (length `n+1`).39383939EXAMPLES::39403941sage: from sage.algebras.steenrod.steenrod_algebra import AA as A3942sage: A()3943mod 2 Steenrod algebra, milnor basis3944sage: A(2)3945sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]3946sage: A(2, p=5)3947sub-Hopf algebra of mod 5 Steenrod algebra, milnor basis, profile function ([2, 1], [2, 2, 2])3948"""3949if n is None:3950return SteenrodAlgebra(p=p)3951if p == 2:3952return SteenrodAlgebra(p=p, profile=range(n+1, 0, -1))3953return SteenrodAlgebra(p=p, profile=(range(n, 0, -1), [2]*(n+1)))39543955def Sq(*nums):3956r"""3957Milnor element Sq(a,b,c,...).39583959INPUT:39603961- ``a, b, c, ...`` - non-negative integers39623963OUTPUT: element of the Steenrod algebra39643965This returns the Milnor basis element3966`\text{Sq}(a, b, c, ...)`.39673968EXAMPLES::39693970sage: Sq(5)3971Sq(5)3972sage: Sq(5) + Sq(2,1) + Sq(5) # addition is mod 2:3973Sq(2,1)3974sage: (Sq(4,3) + Sq(7,2)).degree()39751339763977Entries must be non-negative integers; otherwise, an error3978results.39793980This function is a good way to define elements of the Steenrod3981algebra.3982"""3983return SteenrodAlgebra(p=2).Sq(*nums)398439853986