Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/algebras/steenrod/steenrod_algebra.py
4159 views
1
r"""
2
The Steenrod algebra
3
4
AUTHORS:
5
6
- John H. Palmieri (2008-07-30): version 0.9: Initial implementation.
7
- John H. Palmieri (2010-06-30): version 1.0: Implemented sub-Hopf
8
algebras and profile functions; direct multiplication of admissible
9
sequences (rather than conversion to the Milnor basis); implemented
10
the Steenrod algebra using CombinatorialFreeModule; improved the
11
test suite.
12
13
This module defines the mod `p` Steenrod algebra `\mathcal{A}_p`, some
14
of its properties, and ways to define elements of it.
15
16
From a topological point of view, `\mathcal{A}_p` is the algebra of
17
stable cohomology operations on mod `p` cohomology; thus for any
18
topological space `X`, its mod `p` cohomology algebra
19
`H^*(X,\mathbf{F}_p)` is a module over `\mathcal{A}_p`.
20
21
From an algebraic point of view, `\mathcal{A}_p` is an
22
`\mathbf{F}_p`-algebra; when `p=2`, it is generated by elements
23
`\text{Sq}^i` for `i\geq 0` (the *Steenrod squares*), and when `p` is
24
odd, it is generated by elements `\mathcal{P}^i` for `i \geq 0` (the
25
*Steenrod reduced pth powers*) along with an element `\beta` (the *mod
26
p Bockstein*). The Steenrod algebra is graded: `\text{Sq}^i` is in
27
degree `i` for each `i`, `\beta` is in degree 1, and `\mathcal{P}^i`
28
is in degree `2(p-1)i`.
29
30
The unit element is `\text{Sq}^0` when `p=2` and
31
`\mathcal{P}^0` when `p` is odd. The generating
32
elements also satisfy the *Adem relations*. At the prime 2, these
33
have the form
34
35
.. math::
36
37
\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.
38
39
At odd primes, they are a bit more complicated; see Steenrod and
40
Epstein [SE] or :mod:`sage.algebras.steenrod.steenrod_algebra_bases`
41
for full details. These relations lead to the existence of the
42
*Serre-Cartan* basis for `\mathcal{A}_p`.
43
44
The mod `p` Steenrod algebra has the structure of a Hopf
45
algebra, and Milnor [Mil] has a beautiful description of the dual,
46
leading to a construction of the *Milnor basis* for
47
`\mathcal{A}_p`. In this module, elements in the Steenrod
48
algebra are represented, by default, using the Milnor basis.
49
50
.. rubric:: Bases for the Steenrod algebra
51
52
There are a handful of other bases studied in the literature; the
53
paper by Monks is a good reference. Here is a quick summary:
54
55
- The *Milnor basis*. When `p=2`, the Milnor basis consists of symbols
56
of the form `\text{Sq}(m_1, m_2, ..., m_t)`, where each `m_i` is a
57
non-negative integer and if `t>1`, then the last entry `m_t > 0`.
58
When `p` is odd, the Milnor basis consists of symbols of the form
59
`Q_{e_1} Q_{e_2} ... \mathcal{P}(m_1, m_2, ..., m_t)`, where `0 \leq
60
e_1 < e_2 < ...`, each `m_i` is a non-negative integer, and if
61
`t>1`, then the last entry `m_t > 0`.
62
63
When `p=2`, it can be convenient to use the notation
64
`\mathcal{P}(-)` to mean `\text{Sq}(-)`, so that there is consistent
65
notation for all primes.
66
67
- The *Serre-Cartan basis*. This basis consists of 'admissible
68
monomials' in the Steenrod operations. Thus at the prime 2, it
69
consists of monomials `\text{Sq}^{m_1} \text{Sq}^{m_2}
70
... \text{Sq}^{m_t}` with `m_i \geq 2m_{i+1}` for each `i`. At odd
71
primes, this basis consists of monomials `\beta^{\epsilon_0}
72
\mathcal{P}^{s_1} \beta^{\epsilon_1} \mathcal{P}^{s_2} ...
73
\mathcal{P}^{s_k} \beta^{\epsilon_k}` with each `\epsilon_i` either
74
0 or 1, `s_i \geq p s_{i+1} + \epsilon_i`, and `s_k \geq 1`.
75
76
Most of the rest of the bases are only defined when `p=2`. The only
77
exceptions are the `P^s_t`-bases and the commutator bases, which are
78
defined at all primes.
79
80
- *Wood's Y basis*. For pairs of non-negative integers `(m,k)`, let
81
`w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`. Wood's `Y` basis consists of
82
monomials `w(m_0,k_0) ... w(m_t, k_t)` with `(m_i,k_i) >
83
(m_{i+1},k_{i+1})`, in left lex order.
84
85
- *Wood's Z basis*. For pairs of non-negative integers `(m,k)`, let
86
`w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`. Wood's `Z` basis consists of
87
monomials `w(m_0,k_0) ... w(m_t, k_t)` with `(m_i+k_i,m_i) >
88
(m_{i+1}+k_{i+1},m_{i+1})`, in left lex order.
89
90
- *Wall's basis*. For any pair of integers `(m,k)` with `m \geq k \geq
91
0`, let `Q^m_k = \text{Sq}^{2^k} \text{Sq}^{2^{k+1}}
92
... \text{Sq}^{2^m}`. The elements of Wall's basis are monomials
93
`Q^{m_0}_{k_0} ... Q^{m_t}_{k_t}` with `(m_i, k_i) > (m_{i+1},
94
k_{i+1})`, ordered left lexicographically.
95
96
(Note that `Q^m_k` is the reverse of the element `X^m_k` used in
97
defining Arnon's A basis.)
98
99
- *Arnon's A basis*. For any pair of integers `(m,k)` with `m \geq k
100
\geq 0`, let `X^m_k = \text{Sq}^{2^m} \text{Sq}^{2^{m-1}}
101
... \text{Sq}^{2^k}`. The elements of Arnon's A basis are monomials
102
`X^{m_0}_{k_0} ... X^{m_t}_{k_t}` with `(m_i, k_i) < (m_{i+1},
103
k_{i+1})`, ordered left lexicographically.
104
105
(Note that `X^m_k` is the reverse of the element `Q^m_k` used in
106
defining Wall's basis.)
107
108
- *Arnon's C basis*. The elements of Arnon's C basis are monomials of
109
the form `\text{Sq}^{t_1} ... \text{Sq}^{t_m}` where for each `i`,
110
we have `t_i \leq 2t_{i+1}` and `2^i | t_{m-i}`.
111
112
- `P^s_t` *bases*. Let `p=2`. For integers `s \geq 0` and `t > 0`,
113
the element `P^s_t` is the Milnor basis element `\mathcal{P}(0, ...,
114
0, p^s, 0, ...)`, with the nonzero entry in position `t`. To obtain
115
a `P^s_t`-basis, for each set `\{P^{s_1}_{t_1}, ...,
116
P^{s_k}_{t_k}\}` of (distinct) `P^s_t`'s, one chooses an ordering
117
and forms the monomials
118
119
.. math::
120
121
(P^{s_1}_{t_1})^{i_1} ... (P^{s_k}_{t_k})^{i_k}
122
123
for all exponents `i_j` with `0 < i_j < p`. When `p=2`, the set of
124
all such monomials then forms a basis, and when `p` is odd, if one
125
multiplies each such monomial on the left by products of the form
126
`Q_{e_1} Q_{e_2} ...` with `0 \leq e_1 < e_2 < ...`, one obtains a
127
basis.
128
129
Thus one gets a basis by choosing an ordering on each set of
130
`P^s_t`'s. There are infinitely many orderings possible, and we
131
have implemented four of them:
132
133
- 'rlex': right lexicographic ordering
134
135
- 'llex': left lexicographic ordering
136
137
- 'deg': ordered by degree, which is the same as left lexicographic
138
ordering on the pair `(s+t,t)`
139
140
- 'revz': left lexicographic ordering on the pair `(s+t,s)`, which
141
is the reverse of the ordering used (on elements in the same
142
degrees as the `P^s_t`'s) in Wood's Z basis: 'revz' stands for
143
'reversed Z'. This is the default: 'pst' is the same as
144
'pst_revz'.
145
146
- *Commutator bases*. Let `c_{i,1} = \mathcal{P}(p^i)`, let `c_{i,2}
147
= [c_{i+1,1}, c_{i,1}]`, and inductively define `c_{i,k} =
148
[c_{i+k-1,1}, c_{i,k-1}]`. Thus `c_{i,k}` is a `k`-fold iterated
149
commutator of the elements `\mathcal{P}(p^i)`, ...,
150
`\mathcal{P}(p^{i+k-1})`. Note that `\dim c_{i,k} = \dim P^i_k`.
151
152
Commutator bases are obtained in much the same way as `P^s_t`-bases:
153
for each set `\{c_{s_1,t_1}, ..., c_{s_k,t_k}\}` of (distinct)
154
`c_{s,t}`'s, one chooses an ordering and forms the resulting
155
monomials
156
157
.. math::
158
159
c_{s_1, t_1}^{i_1} ... c_{s_k,t_k}^{i_k}
160
161
for all exponents `i_j` with `0 < i_j < p`. When `p` is odd, one
162
also needs to left-multiply by products of the `Q_i`'s. As for
163
`P^s_t`-bases, every ordering on each set of iterated commutators
164
determines a basis, and the same four orderings have been defined
165
for these bases as for the `P^s_t` bases: 'rlex', 'llex', 'deg',
166
'revz'.
167
168
.. rubric:: Sub-Hopf algebras of the Steenrod algebra
169
170
The sub-Hopf algebras of the Steenrod algebra have been
171
classified. Milnor proved that at the prime 2, the dual of the
172
Steenrod algebra `A_*` is isomorphic to a polynomial algebra
173
174
.. math::
175
176
A_* \cong \GF{2} [\xi_1, \xi_2, \xi_3, ...].
177
178
The Milnor basis is dual to the monomial basis. Furthermore, any sub-Hopf
179
algebra corresponds to a quotient of this of the form
180
181
.. math::
182
183
A_* /(\xi_1^{2^{e_1}}, \xi_2^{2^{e_2}}, \xi_3^{2^{e_3}}, ...).
184
185
The list of exponents `(e_1, e_2, ...)` may be considered a function
186
`e` from the positive integers to the extended non-negative integers
187
(the non-negative integers and `\infty`); this is called the *profile
188
function* for the sub-Hopf algebra. The profile function must satisfy
189
the condition
190
191
- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.
192
193
At odd primes, the situation is similar: the dual is isomorphic to the
194
tensor product of a polynomial algebra and an exterior algebra,
195
196
.. math::
197
198
A_* = \GF{p} [\xi_1, \xi_2, \xi_3, ...] \otimes \Lambda (\tau_0, \tau_1, ...),
199
200
and any sub-Hopf algebra corresponds to a quotient of this of the form
201
202
.. math::
203
204
A_* / (\xi_1^{p^{e_1}}, \xi_2^{p^{e_2}}, ...; \tau_0^{k_0}, \tau_1^{k_1}, ...).
205
206
Here the profile function has two pieces, `e` as at the prime 2, and
207
`k`, which maps the non-negative integers to the set `\{1, 2\}`.
208
These must satisfy the following conditions:
209
210
- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.
211
212
- if `k(i+j) = 1`, then either `e(i) \leq j` or `k(j) = 1` for all `i
213
\geq 1`, `j \geq 0`.
214
215
(See Adams-Margolis, for example, for these results on profile
216
functions.)
217
218
This module allows one to construct the Steenrod algebra or any of its
219
sub-Hopf algebras, at any prime. When defining a sub-Hopf algebra,
220
you must work with the Milnor basis or a `P^s_t`-basis.
221
222
.. rubric:: Elements of the Steenrod algebra
223
224
Basic arithmetic, `p=2`. To construct an element of the mod 2 Steenrod
225
algebra, use the function ``Sq``::
226
227
sage: a = Sq(1,2)
228
sage: b = Sq(4,1)
229
sage: z = a + b
230
sage: z
231
Sq(1,2) + Sq(4,1)
232
sage: Sq(4) * Sq(1,2)
233
Sq(1,1,1) + Sq(2,3) + Sq(5,2)
234
sage: z**2 # non-negative exponents work as they should
235
Sq(1,2,1) + Sq(4,1,1)
236
sage: z**0
237
1
238
239
Basic arithmetic, `p>2`. To construct an element of the mod `p`
240
Steenrod algebra when `p` is odd, you should first define a Steenrod
241
algebra, using the ``SteenrodAlgebra`` command::
242
243
sage: A3 = SteenrodAlgebra(3)
244
245
Having done this, the newly created algebra ``A3`` has methods ``Q``
246
and ``P`` which construct elements of ``A3``::
247
248
sage: c = A3.Q(1,3,6); c
249
Q_1 Q_3 Q_6
250
sage: d = A3.P(2,0,1); d
251
P(2,0,1)
252
sage: c * d
253
Q_1 Q_3 Q_6 P(2,0,1)
254
sage: e = A3.P(3)
255
sage: d * e
256
P(5,0,1)
257
sage: e * d
258
P(1,1,1) + P(5,0,1)
259
sage: c * c
260
0
261
sage: e ** 3
262
2 P(1,2)
263
264
Note that one can construct an element like ``c`` above in one step,
265
without first constructing the algebra::
266
267
sage: c = SteenrodAlgebra(3).Q(1,3,6)
268
sage: c
269
Q_1 Q_3 Q_6
270
271
And of course, you can do similar constructions with the mod 2
272
Steenrod algebra::
273
274
sage: A = SteenrodAlgebra(2); A
275
mod 2 Steenrod algebra, milnor basis
276
sage: A.Sq(2,3,5)
277
Sq(2,3,5)
278
sage: A.P(2,3,5) # when p=2, P = Sq
279
Sq(2,3,5)
280
sage: A.Q(1,4) # when p=2, this gives a product of Milnor primitives
281
Sq(0,1,0,0,1)
282
283
Associated to each element is its prime (the characteristic of the
284
underlying base field) and its basis (the basis for the Steenrod
285
algebra in which it lies)::
286
287
sage: a = SteenrodAlgebra(basis='milnor').Sq(1,2,1)
288
sage: a.prime()
289
2
290
sage: a.basis_name()
291
'milnor'
292
sage: a.degree()
293
14
294
295
It can be viewed in other bases::
296
297
sage: a.milnor() # same as a
298
Sq(1,2,1)
299
sage: a.change_basis('adem')
300
Sq^9 Sq^4 Sq^1 + Sq^11 Sq^2 Sq^1 + Sq^13 Sq^1
301
sage: a.change_basis('adem').change_basis('milnor')
302
Sq(1,2,1)
303
304
Regardless of the prime, each element has an ``excess``, and if the
305
element is homogeneous, a ``degree``. The excess of
306
`\text{Sq}(i_1,i_2,i_3,...)` is `i_1 + i_2 + i_3 + ...`; when `p` is
307
odd, the excess of `Q_{0}^{e_0} Q_{1}^{e_1} ... \mathcal{P}(r_1, r_2,
308
...)` is `\sum e_i + 2 \sum r_i`. The excess of a linear combination
309
of Milnor basis elements is the minimum of the excesses of those basis
310
elements.
311
312
The degree of `\text{Sq}(i_1,i_2,i_3,...)` is `sum (2^n-1) i_n`, and
313
when `p` is odd, the degree of `Q_{0}^{\epsilon_0} Q_{1}^{\epsilon_1}
314
... \mathcal{P}(r_1, r_2, ...)` is `\sum \epsilon_i (2p^i - 1) + \sum
315
r_j (2p^j - 2)`. The degree of a linear combination of such terms is
316
only defined if the terms all have the same degree.
317
318
Here are some simple examples::
319
320
sage: z = Sq(1,2) + Sq(4,1)
321
sage: z.degree()
322
7
323
sage: (Sq(0,0,1) + Sq(5,3)).degree()
324
Traceback (most recent call last):
325
...
326
ValueError: Element is not homogeneous.
327
sage: Sq(7,2,1).excess()
328
10
329
sage: z.excess()
330
3
331
sage: B = SteenrodAlgebra(3)
332
sage: x = B.Q(1,4)
333
sage: y = B.P(1,2,3)
334
sage: x.degree()
335
166
336
sage: x.excess()
337
2
338
sage: y.excess()
339
12
340
341
Elements have a ``weight`` in the May filtration, which (when `p=2`)
342
is related to the ``height`` function defined by Wall::
343
344
sage: Sq(2,1,5).may_weight()
345
9
346
sage: Sq(2,1,5).wall_height()
347
[2, 3, 2, 1, 1]
348
sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)
349
sage: b.may_weight()
350
2
351
sage: b.wall_height()
352
[0, 0, 1, 1]
353
354
Odd primary May weights::
355
356
sage: A5 = SteenrodAlgebra(5)
357
sage: a = A5.Q(1,2,4)
358
sage: b = A5.P(1,2,1)
359
sage: a.may_weight()
360
10
361
sage: b.may_weight()
362
8
363
sage: (a * b).may_weight()
364
18
365
sage: A5.P(0,0,1).may_weight()
366
3
367
368
Since the Steenrod algebra is a Hopf algebra, every element has a
369
coproduct and an antipode.
370
371
::
372
373
sage: Sq(5).coproduct()
374
1 # Sq(5) + Sq(1) # Sq(4) + Sq(2) # Sq(3) + Sq(3) # Sq(2) + Sq(4) # Sq(1) + Sq(5) # 1
375
sage: Sq(5).antipode()
376
Sq(2,1) + Sq(5)
377
sage: d = Sq(0,0,1); d
378
Sq(0,0,1)
379
sage: d.antipode()
380
Sq(0,0,1)
381
sage: Sq(4).antipode()
382
Sq(1,1) + Sq(4)
383
sage: (Sq(4) * Sq(2)).antipode()
384
Sq(6)
385
sage: SteenrodAlgebra(7).P(3,1).antipode()
386
P(3,1)
387
388
Applying the antipode twice returns the original element::
389
390
sage: y = Sq(8)*Sq(4)
391
sage: y == (y.antipode()).antipode()
392
True
393
394
Internal representation: you can use any element as an iterator (``for
395
x in a: ...``), and the method :meth:`monomial_coefficients` returns a
396
dictionary with keys tuples representing basis elements and with
397
corresponding value representing the coefficient of that term::
398
399
sage: c = Sq(5).antipode(); c
400
Sq(2,1) + Sq(5)
401
sage: for mono, coeff in c: print coeff, mono
402
1 (5,)
403
1 (2, 1)
404
sage: c.monomial_coefficients()
405
{(5,): 1, (2, 1): 1}
406
sage: c.monomials()
407
[Sq(2,1), Sq(5)]
408
sage: c.support()
409
[(2, 1), (5,)]
410
sage: Adem = SteenrodAlgebra(basis='adem')
411
sage: (Adem.Sq(10) + Adem.Sq(9) * Adem.Sq(1)).monomials()
412
[Sq^9 Sq^1, Sq^10]
413
414
sage: A7 = SteenrodAlgebra(p=7)
415
sage: a = A7.P(1) * A7.P(1); a
416
2 P(2)
417
sage: a.leading_coefficient()
418
2
419
sage: a.leading_monomial()
420
P(2)
421
sage: a.leading_term()
422
2 P(2)
423
sage: a.change_basis('adem').monomial_coefficients()
424
{(0, 2, 0): 2}
425
426
The tuple in the previous output stands for the element `\beta^0
427
P^2 \beta^0`, i.e., `P^2`. Going in the other direction, if you
428
want to specify a basis element by giving the corresponding tuple,
429
you can use the :meth:`monomial` method on the algebra::
430
431
sage: SteenrodAlgebra(p=7, basis='adem').monomial((0, 2, 0))
432
P^2
433
sage: 10 * SteenrodAlgebra(p=7, basis='adem').monomial((0, 2, 0))
434
3 P^2
435
436
In the following example, elements in Wood's Z basis are certain
437
products of the elements `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.
438
Internally, each `w(m,k)` is represented by the pair `(m,k)`, and
439
products of them are represented by tuples of such pairs. ::
440
441
sage: A = SteenrodAlgebra(basis='wood_z')
442
sage: t = ((2, 0), (0, 0))
443
sage: A.monomial(t)
444
Sq^4 Sq^1
445
446
See the documentation for :func:`SteenrodAlgebra` for more details and
447
examples.
448
449
REFERENCES:
450
451
- [AM] J. F. Adams, and H. R. Margolis, "Sub-Hopf-algebras of the
452
Steenrod algebra," Proc. Cambridge Philos. Soc. 76 (1974), 45-52.
453
454
- [Mil] J. W. Milnor, "The Steenrod algebra and its dual," Ann. of
455
Math. (2) 67 (1958), 150-171.
456
457
- [Mon] K. G. Monks, "Change of basis, monomial relations, and
458
`P^s_t` bases for the Steenrod algebra," J. Pure Appl.
459
Algebra 125 (1998), no. 1-3, 235-260.
460
461
- [SE] N. E. Steenrod and D. B. A. Epstein, Cohomology operations,
462
Ann. of Math. Stud. 50 (Princeton University Press, 1962).
463
"""
464
465
#*****************************************************************************
466
# Copyright (C) 2008-2010 John H. Palmieri <[email protected]>
467
# Distributed under the terms of the GNU General Public License (GPL)
468
#*****************************************************************************
469
470
from sage.combinat.free_module import CombinatorialFreeModule, \
471
CombinatorialFreeModuleElement
472
from sage.misc.lazy_attribute import lazy_attribute
473
from sage.categories.all import ModulesWithBasis, tensor, Hom
474
475
######################################################
476
# the main class
477
######################################################
478
479
class SteenrodAlgebra_generic(CombinatorialFreeModule):
480
r"""
481
The mod `p` Steenrod algebra.
482
483
Users should not call this, but use the function
484
:func:`SteenrodAlgebra` instead. See that function for
485
extensive documentation.
486
487
EXAMPLES::
488
489
sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic()
490
mod 2 Steenrod algebra, milnor basis
491
sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5)
492
mod 5 Steenrod algebra, milnor basis
493
sage: sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic(5, 'adem')
494
mod 5 Steenrod algebra, serre-cartan basis
495
"""
496
@staticmethod
497
def __classcall__(self, p=2, basis='milnor', **kwds):
498
"""
499
This normalizes the basis name and the profile, to make unique
500
representation work properly.
501
502
EXAMPLES::
503
504
sage: SteenrodAlgebra(basis='adem') is SteenrodAlgebra(basis='serre-cartan')
505
True
506
sage: SteenrodAlgebra(profile=[3,2,1,0]) is SteenrodAlgebra(profile=lambda n: max(4-n,0), truncation_type=0)
507
True
508
"""
509
from steenrod_algebra_misc import get_basis_name, normalize_profile
510
profile = kwds.get('profile', None)
511
precision = kwds.get('precision', None)
512
truncation_type = kwds.get('truncation_type', 'auto')
513
514
std_basis = get_basis_name(basis, p)
515
std_profile, std_type = normalize_profile(profile, precision=precision, truncation_type=truncation_type, p=p)
516
return super(SteenrodAlgebra_generic, self).__classcall__(self, p=p, basis=std_basis, profile=std_profile, truncation_type=std_type)
517
518
def __init__(self, p=2, basis='milnor', **kwds):
519
r"""
520
INPUT:
521
522
- ``p`` - positive prime integer (optional, default 2)
523
- ``basis`` - string (optional, default = 'milnor')
524
- ``profile`` - profile function (optional, default ``None``)
525
- ``truncation_type`` - (optional, default 'auto')
526
- ``precision`` - (optional, default ``None``)
527
528
OUTPUT: mod `p` Steenrod algebra with basis, or a sub-Hopf
529
algebra of the mod `p` Steenrod algebra defined by the given
530
profile function.
531
532
See :func:`SteenrodAlgebra` for full documentation.
533
534
EXAMPLES::
535
536
sage: SteenrodAlgebra() # 2 is the default prime
537
mod 2 Steenrod algebra, milnor basis
538
sage: SteenrodAlgebra(5)
539
mod 5 Steenrod algebra, milnor basis
540
sage: SteenrodAlgebra(2, 'milnor').Sq(0,1)
541
Sq(0,1)
542
sage: SteenrodAlgebra(2, 'adem').Sq(0,1)
543
Sq^2 Sq^1 + Sq^3
544
545
TESTS::
546
547
sage: TestSuite(SteenrodAlgebra()).run()
548
sage: TestSuite(SteenrodAlgebra(profile=[4,3,2,2,1])).run()
549
sage: TestSuite(SteenrodAlgebra(basis='adem')).run()
550
sage: TestSuite(SteenrodAlgebra(basis='wall')).run()
551
sage: TestSuite(SteenrodAlgebra(basis='arnonc')).run() # long time
552
sage: TestSuite(SteenrodAlgebra(basis='woody')).run() # long time
553
sage: A3 = SteenrodAlgebra(3)
554
sage: A3.category()
555
Category of graded hopf algebras with basis over Finite Field of size 3
556
sage: TestSuite(A3).run()
557
sage: TestSuite(SteenrodAlgebra(basis='adem', p=3)).run()
558
sage: TestSuite(SteenrodAlgebra(basis='pst_llex', p=7)).run() # long time
559
sage: TestSuite(SteenrodAlgebra(basis='comm_deg', p=5)).run() # long time
560
"""
561
from sage.rings.arith import is_prime
562
from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis
563
from sage.rings.infinity import Infinity
564
from sage.sets.family import Family
565
from sage.sets.non_negative_integers import NonNegativeIntegers
566
from functools import partial
567
from steenrod_algebra_bases import steenrod_algebra_basis
568
from sage.rings.all import GF
569
profile = kwds.get('profile', None)
570
truncation_type = kwds.get('truncation_type', 'auto')
571
572
if not is_prime(p):
573
raise ValueError("%s is not prime." % p)
574
self._prime = p
575
base_ring = GF(p)
576
self._profile = profile
577
self._truncation_type = truncation_type
578
NN = NonNegativeIntegers()
579
if ((p==2 and ((len(profile) > 0 and profile[0] < Infinity)))
580
or (p>2 and profile != ((), ()) and len(profile[0]) > 0
581
and profile[0][0] < Infinity)
582
or (truncation_type < Infinity)):
583
if basis != 'milnor' and basis.find('pst') == -1:
584
raise NotImplementedError("For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.")
585
self._basis_name = basis
586
self._basis_fcn = partial(steenrod_algebra_basis,
587
p=p,
588
basis=basis,
589
profile=profile,
590
truncation_type=truncation_type)
591
# name the basis function so different Steenrod algebras have
592
# different basis functions... (see __eq__ for LazyFamily?)
593
self._basis_fcn.func_name = "basis_%s_%s_%s_%s" % (p, basis, profile, truncation_type)
594
CombinatorialFreeModule.__init__(self,
595
base_ring,
596
Family(NN, self._basis_fcn),
597
prefix=self._basis_name,
598
element_class=self.Element,
599
category = GradedHopfAlgebrasWithBasis(base_ring),
600
scalar_mult = ' ')
601
602
def prime(self):
603
r"""
604
The prime associated to self.
605
606
EXAMPLES::
607
608
sage: SteenrodAlgebra(p=2, profile=[1,1]).prime()
609
2
610
sage: SteenrodAlgebra(p=7).prime()
611
7
612
"""
613
return self.base_ring().characteristic()
614
615
def basis_name(self):
616
r"""
617
The basis name associated to self.
618
619
EXAMPLES::
620
621
sage: SteenrodAlgebra(p=2, profile=[1,1]).basis_name()
622
'milnor'
623
sage: SteenrodAlgebra(basis='serre-cartan').basis_name()
624
'serre-cartan'
625
sage: SteenrodAlgebra(basis='adem').basis_name()
626
'serre-cartan'
627
"""
628
return self.prefix()
629
630
def _has_nontrivial_profile(self):
631
r"""
632
True if the profile function for this algebra seems to be that
633
for a proper sub-Hopf algebra of the Steenrod algebra.
634
635
EXAMPLES::
636
637
sage: SteenrodAlgebra()._has_nontrivial_profile()
638
False
639
sage: SteenrodAlgebra(p=3)._has_nontrivial_profile()
640
False
641
sage: SteenrodAlgebra(profile=[3,2,1])._has_nontrivial_profile()
642
True
643
sage: SteenrodAlgebra(profile=([1], [2, 2]), p=3)._has_nontrivial_profile()
644
True
645
646
Check that a bug in #11832 has been fixed::
647
648
sage: P3 = SteenrodAlgebra(p=3, profile=(lambda n: Infinity, lambda n: 1))
649
sage: P3._has_nontrivial_profile()
650
True
651
"""
652
from sage.rings.infinity import Infinity
653
profile = self._profile
654
trunc = self._truncation_type
655
if self.prime() == 2:
656
return ((len(profile) > 0 and len(profile) > 0
657
and profile[0] < Infinity)
658
or (trunc < Infinity))
659
return ((profile != ((), ()) and
660
((len(profile[0]) > 0 and profile[0][0] < Infinity)
661
or (len(profile[1]) > 0 and min(profile[1]) == 1)))
662
or (trunc < Infinity))
663
664
def _repr_(self):
665
r"""
666
Printed representation of the Steenrod algebra.
667
668
EXAMPLES::
669
670
sage: SteenrodAlgebra(3)
671
mod 3 Steenrod algebra, milnor basis
672
sage: SteenrodAlgebra(2, basis='adem')
673
mod 2 Steenrod algebra, serre-cartan basis
674
sage: B = SteenrodAlgebra(2003)
675
sage: B._repr_()
676
'mod 2003 Steenrod algebra, milnor basis'
677
678
sage: SteenrodAlgebra(profile=(3,2,1,0))
679
sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]
680
sage: SteenrodAlgebra(profile=lambda n: 4)
681
sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [4, 4, 4, ..., 4, 4, +Infinity, +Infinity, +Infinity, ...]
682
sage: SteenrodAlgebra(p=5, profile=(lambda n: 4, lambda n: 1))
683
sub-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, ...])
684
"""
685
def abridge_list(l):
686
"""
687
String rep for list ``l`` if ``l`` is short enough;
688
otherwise print the first few terms and the last few
689
terms, with an ellipsis in between.
690
"""
691
if len(l) < 8:
692
l_str = str(l)
693
else:
694
l_str = str(l[:3]).rstrip("]") + ", ..., " + str(l[-2:]).lstrip("[")
695
return l_str
696
697
from sage.rings.infinity import Infinity
698
profile = self._profile
699
trunc = self._truncation_type
700
p = self.prime()
701
if self._has_nontrivial_profile():
702
if p == 2:
703
pro_str = abridge_list(list(profile))
704
if trunc != 0:
705
pro_str = pro_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"
706
else:
707
e_str = abridge_list(list(profile[0]))
708
k_str = abridge_list(list(profile[1]))
709
if trunc != 0:
710
e_str = e_str.rstrip("]") + ", " + str([Infinity] * 3).strip("[]") + ", ...]"
711
k_str = k_str.rstrip("]") + ", " + str([2] * 2).strip("[]") + ", ...]"
712
pro_str = "(%s, %s)" % (e_str, k_str)
713
return "sub-Hopf algebra of mod %d Steenrod algebra, %s basis, profile function %s" % (self.prime(), self._basis_name, pro_str)
714
return "mod %d Steenrod algebra, %s basis" % (self.prime(), self._basis_name)
715
716
def _latex_(self):
717
r"""
718
LaTeX representation of the Steenrod algebra.
719
720
EXAMPLES::
721
722
sage: C = SteenrodAlgebra(3)
723
sage: C
724
mod 3 Steenrod algebra, milnor basis
725
sage: C._latex_()
726
'\\mathcal{A}_{3}'
727
"""
728
return "\\mathcal{A}_{%s}" % self.prime()
729
730
def _repr_term(self, t):
731
r"""
732
String representation of the monomial specified by the tuple ``t``.
733
734
INPUT:
735
736
- ``t`` - tuple, representing basis element in the current basis.
737
738
OUTPUT: string
739
740
This is tested in many places: any place elements are printed
741
is essentially a doctest for this method. Also, each basis
742
has its own method for printing monomials, and those are
743
doctested individually. We give a few doctests here, in
744
addition.
745
746
EXAMPLES::
747
748
sage: SteenrodAlgebra()._repr_term((3,2))
749
'Sq(3,2)'
750
sage: SteenrodAlgebra(p=7)._repr_term(((0,2), (3,2)))
751
'Q_0 Q_2 P(3,2)'
752
sage: SteenrodAlgebra(basis='adem')._repr_term((14,2))
753
'Sq^14 Sq^2'
754
sage: SteenrodAlgebra(basis='adem', p=3)._repr_term((1,3,0))
755
'beta P^3'
756
sage: SteenrodAlgebra(basis='pst')._repr_term(((0,2), (1,3)))
757
'P^0_2 P^1_3'
758
sage: SteenrodAlgebra(basis='arnon_a')._repr_term(((0,2), (1,3)))
759
'X^0_2 X^1_3'
760
761
sage: A7 = SteenrodAlgebra(7)
762
sage: x = A7.Q(0,3) * A7.P(2,2)
763
sage: x._repr_()
764
'Q_0 Q_3 P(2,2)'
765
sage: x
766
Q_0 Q_3 P(2,2)
767
sage: a = SteenrodAlgebra().Sq(0,0,2)
768
sage: a
769
Sq(0,0,2)
770
sage: A2_adem = SteenrodAlgebra(2,'admissible')
771
sage: A2_adem(a)
772
Sq^8 Sq^4 Sq^2 + Sq^9 Sq^4 Sq^1 + Sq^10 Sq^3 Sq^1 +
773
Sq^10 Sq^4 + Sq^11 Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^13 Sq^1
774
+ Sq^14
775
sage: SteenrodAlgebra(2, 'woodz')(a)
776
Sq^6 Sq^7 Sq^1 + Sq^14 + Sq^4 Sq^7 Sq^3 + Sq^4 Sq^7
777
Sq^2 Sq^1 + Sq^12 Sq^2 + Sq^8 Sq^6 + Sq^8 Sq^4 Sq^2
778
sage: SteenrodAlgebra(2, 'arnonc')(a)
779
Sq^4 Sq^2 Sq^8 + Sq^4 Sq^4 Sq^6 + Sq^4 Sq^6 Sq^4 +
780
Sq^6 Sq^8 + Sq^8 Sq^4 Sq^2 + Sq^8 Sq^6
781
sage: SteenrodAlgebra(2, 'pst_llex')(a)
782
P^1_3
783
sage: SteenrodAlgebra(2, 'comm_revz')(a)
784
c_0,1 c_1,1 c_0,3 c_2,1 + c_0,2 c_0,3 c_2,1 + c_1,3
785
"""
786
from steenrod_algebra_misc import milnor_mono_to_string, \
787
serre_cartan_mono_to_string, wood_mono_to_string, \
788
wall_mono_to_string, wall_long_mono_to_string, \
789
arnonA_mono_to_string, arnonA_long_mono_to_string, \
790
serre_cartan_mono_to_string, pst_mono_to_string, \
791
comm_long_mono_to_string, comm_mono_to_string
792
p = self.prime()
793
basis = self.basis_name()
794
if basis == 'milnor':
795
s = milnor_mono_to_string(t, p=p)
796
elif basis == 'serre-cartan':
797
s = serre_cartan_mono_to_string(t, p=p)
798
elif basis.find('wood') >= 0:
799
s = wood_mono_to_string(t)
800
elif basis == 'wall':
801
s = wall_mono_to_string(t)
802
elif basis == 'wall_long':
803
s = wall_long_mono_to_string(t)
804
elif basis == 'arnona':
805
s = arnonA_mono_to_string(t)
806
elif basis == 'arnona_long':
807
s = arnonA_long_mono_to_string(t)
808
elif basis == 'arnonc':
809
s = serre_cartan_mono_to_string(t)
810
elif basis.find('pst') >= 0:
811
s = pst_mono_to_string(t, p=p)
812
elif basis.find('comm') >= 0 and basis.find('long') >= 0:
813
s = comm_long_mono_to_string(t, p=p)
814
elif basis.find('comm') >= 0:
815
s = comm_mono_to_string(t, p=p)
816
s = s.translate(None, "{}")
817
return s
818
819
def _latex_term(self, t):
820
"""
821
LaTeX representation of the monomial specified by the tuple ``t``.
822
823
INPUT:
824
825
- ``t`` - tuple, representing basis element in the current basis.
826
827
OUTPUT: string
828
829
The string depends on the basis over which the element is defined.
830
831
EXAMPLES::
832
833
sage: A7 = SteenrodAlgebra(7)
834
sage: A7._latex_term(((0, 3), (2,2)))
835
'Q_{0} Q_{3} \\mathcal{P}(2,2)'
836
sage: x = A7.Q(0,3) * A7.P(2,2)
837
sage: x._latex_() # indirect doctest
838
'Q_{0} Q_{3} \\mathcal{P}(2,2)'
839
sage: latex(x)
840
Q_{0} Q_{3} \mathcal{P}(2,2)
841
sage: b = Sq(0,2)
842
sage: b.change_basis('adem')._latex_()
843
'\\text{Sq}^{4} \\text{Sq}^{2} + \\text{Sq}^{5} \\text{Sq}^{1} +
844
\\text{Sq}^{6}'
845
sage: b.change_basis('woody')._latex_()
846
'\\text{Sq}^{2} \\text{Sq}^{3} \\text{Sq}^{1} + \\text{Sq}^{6} +
847
\\text{Sq}^{4} \\text{Sq}^{2}'
848
sage: SteenrodAlgebra(2, 'arnona')(b)._latex_()
849
'X^{1}_{1} X^{2}_{2} + X^{2}_{1}'
850
sage: SteenrodAlgebra(p=3, basis='serre-cartan').Q(0)._latex_()
851
'\\beta'
852
sage: latex(Sq(2).change_basis('adem').coproduct())
853
1 \otimes \text{Sq}^{2} + \text{Sq}^{1} \otimes \text{Sq}^{1} + \text{Sq}^{2} \otimes 1
854
"""
855
import re
856
s = self._repr_term(t)
857
s = re.sub(r"\^([0-9]*)", r"^{\1}", s)
858
s = re.sub("_([0-9,]*)", r"_{\1}", s)
859
s = s.replace("Sq", "\\text{Sq}")
860
s = s.replace("P", "\\mathcal{P}")
861
s = s.replace("beta", "\\beta")
862
return s
863
864
def __eq__(self, right):
865
r"""
866
Two Steenrod algebras are equal iff their associated primes,
867
bases, and profile functions (if present) are equal. Because
868
this class inherits from :class:`UniqueRepresentation`, this
869
means that they are equal if and only they are identical: ``A
870
== B`` is True if and only if ``A is B`` is True.
871
872
EXAMPLES::
873
874
sage: A = SteenrodAlgebra(2)
875
sage: B = SteenrodAlgebra(2, 'adem')
876
sage: A == B
877
False
878
sage: C = SteenrodAlgebra(17)
879
sage: A == C
880
False
881
882
sage: A1 = SteenrodAlgebra(2, profile=[2,1])
883
sage: A1 == A
884
False
885
sage: A1 == SteenrodAlgebra(2, profile=[2,1,0])
886
True
887
sage: A1 == SteenrodAlgebra(2, profile=[2,1], basis='pst')
888
False
889
"""
890
return self is right
891
892
def __ne__(self, right):
893
r"""
894
The negation of the method ``__eq__``.
895
896
EXAMPLES::
897
898
sage: SteenrodAlgebra(p=2) != SteenrodAlgebra(p=2, profile=[2,1])
899
True
900
"""
901
return not self.__eq__(right)
902
903
def profile(self, i, component=0):
904
r"""
905
Profile function for this algebra.
906
907
INPUT:
908
909
- `i` - integer
910
- ``component`` - either 0 or 1, optional (default 0)
911
912
OUTPUT: integer or `\infty`
913
914
See the documentation for
915
:mod:`sage.algebras.steenrod.steenrod_algebra` and
916
:func:`SteenrodAlgebra` for information on profile functions.
917
918
This applies the profile function to the integer `i`. Thus
919
when `p=2`, `i` must be a positive integer. When `p` is odd,
920
there are two profile functions, `e` and `k` (in the notation
921
of the aforementioned documentation), corresponding,
922
respectively to ``component=0`` and ``component=1``. So when
923
`p` is odd and ``component`` is 0, `i` must be positive, while
924
when ``component`` is 1, `i` must be non-negative.
925
926
EXAMPLES::
927
928
sage: SteenrodAlgebra().profile(3)
929
+Infinity
930
sage: SteenrodAlgebra(profile=[3,2,1]).profile(1)
931
3
932
sage: SteenrodAlgebra(profile=[3,2,1]).profile(2)
933
2
934
935
When the profile is specified by a list, the default behavior
936
is to return zero values outside the range of the list. This
937
can be overridden if the algebra is created with an infinite
938
``truncation_type``::
939
940
sage: SteenrodAlgebra(profile=[3,2,1]).profile(9)
941
0
942
sage: SteenrodAlgebra(profile=[3,2,1], truncation_type=Infinity).profile(9)
943
+Infinity
944
945
sage: B = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1))
946
sage: B.profile(3)
947
3
948
sage: B.profile(3, component=1)
949
1
950
"""
951
# determine the tuple t to use
952
p = self.prime()
953
if p == 2:
954
t = self._profile
955
elif component == 0:
956
t = self._profile[0]
957
else:
958
t = self._profile[1]
959
# case 1: exponents of the xi's
960
if p == 2 or component == 0:
961
if i <= 0:
962
return 0
963
try:
964
return t[i-1]
965
except IndexError:
966
return self._truncation_type
967
else:
968
# case 2: exponents of the tau's
969
if i < 0:
970
return 1
971
try:
972
return t[i]
973
except IndexError:
974
if self._truncation_type > 0:
975
return 2
976
else:
977
return 1
978
979
def homogeneous_component(self, n):
980
"""
981
Return the nth homogeneous piece of the Steenrod algebra.
982
983
INPUT:
984
985
- `n` - integer
986
987
OUTPUT: a vector space spanned by the basis for this algebra
988
in dimension `n`
989
990
EXAMPLES::
991
992
sage: A = SteenrodAlgebra()
993
sage: A.homogeneous_component(4)
994
Vector space spanned by (Sq(1,1), Sq(4)) over Finite Field of size 2
995
sage: SteenrodAlgebra(profile=[2,1,0]).homogeneous_component(4)
996
Vector space spanned by (Sq(1,1),) over Finite Field of size 2
997
998
The notation A[n] may also be used::
999
1000
sage: A[5]
1001
Vector space spanned by (Sq(2,1), Sq(5)) over Finite Field of size 2
1002
sage: SteenrodAlgebra(basis='wall')[4]
1003
Vector space spanned by (Q^1_0 Q^0_0, Q^2_2) over Finite Field of size 2
1004
sage: SteenrodAlgebra(p=5)[17]
1005
Vector space spanned by (Q_1 P(1), Q_0 P(2)) over Finite Field of size 5
1006
1007
Note that A[n] is just a vector space, not a Hopf algebra, so
1008
its elements don't have products, coproducts, or antipodes
1009
defined on them. If you want to use operations like this on
1010
elements of some A[n], then convert them back to elements of A::
1011
1012
sage: A[5].basis()
1013
Finite family {(5,): milnor[(5,)], (2, 1): milnor[(2, 1)]}
1014
sage: a = list(A[5].basis())[1]
1015
sage: a # not in A, doesn't print like an element of A
1016
milnor[(5,)]
1017
sage: A(a) # in A
1018
Sq(5)
1019
sage: A(a) * A(a)
1020
Sq(7,1)
1021
sage: a * A(a) # only need to convert one factor
1022
Sq(7,1)
1023
sage: a.antipode() # not defined
1024
Traceback (most recent call last):
1025
...
1026
AttributeError: 'CombinatorialFreeModule_with_category.element_class' object has no attribute 'antipode'
1027
sage: A(a).antipode() # convert to elt of A, then compute antipode
1028
Sq(2,1) + Sq(5)
1029
1030
sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')
1031
1032
TESTS:
1033
1034
The following sort of thing is also tested by the function
1035
:func:`steenrod_basis_error_check
1036
<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_basis_error_check>`::
1037
1038
sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])
1039
sage: G = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst')
1040
sage: max([H[n].dimension() - G[n].dimension() for n in range(100)])
1041
0
1042
"""
1043
from sage.rings.all import GF
1044
basis = self._basis_fcn(n)
1045
M = CombinatorialFreeModule(GF(self.prime()), basis,
1046
element_class=self.Element,
1047
prefix=self._basis_name)
1048
M._name = "Vector space spanned by %s"%(tuple([self.monomial(a) for a in basis]),)
1049
return M
1050
1051
__getitem__ = homogeneous_component
1052
1053
def one_basis(self):
1054
"""
1055
The index of the element 1 in the basis for the Steenrod algebra.
1056
1057
EXAMPLES::
1058
1059
sage: SteenrodAlgebra(p=2).one_basis()
1060
()
1061
sage: SteenrodAlgebra(p=7).one_basis()
1062
((), ())
1063
"""
1064
p = self.prime()
1065
basis = self.basis_name()
1066
if basis == 'serre-cartan' or basis == 'arnonc':
1067
return (0,)
1068
if p == 2:
1069
return ()
1070
return ((), ())
1071
1072
def product_on_basis(self, t1, t2):
1073
"""
1074
The product of two basis elements of this algebra
1075
1076
INPUT:
1077
1078
- ``t1``, ``t2`` -- tuples, the indices of two basis elements of self
1079
1080
OUTPUT: the product of the two corresponding basis elements,
1081
as an element of self
1082
1083
ALGORITHM: If the two elements are represented in the Milnor
1084
basis, use Milnor multiplication as implemented in
1085
:mod:`sage.algebras.steenrod.steenrod_algebra_mult`. If the two
1086
elements are represented in the Serre-Cartan basis, then
1087
multiply them using Adem relations (also implemented in
1088
:mod:`sage.algebras.steenrod.steenrod_algebra_mult`). This
1089
provides a good way of checking work -- multiply Milnor
1090
elements, then convert them to Adem elements and multiply
1091
those, and see if the answers correspond.
1092
1093
If the two elements are represented in some other basis, then
1094
convert them both to the Milnor basis and multiply.
1095
1096
EXAMPLES::
1097
1098
sage: Milnor = SteenrodAlgebra()
1099
sage: Milnor.product_on_basis((2,), (2,))
1100
Sq(1,1)
1101
sage: Adem = SteenrodAlgebra(basis='adem')
1102
sage: Adem.Sq(2) * Adem.Sq(2) # indirect doctest
1103
Sq^3 Sq^1
1104
1105
When multiplying elements from different bases, the left-hand
1106
factor determines the form of the output::
1107
1108
sage: Adem.Sq(2) * Milnor.Sq(2)
1109
Sq^3 Sq^1
1110
sage: Milnor.Sq(2) * Adem.Sq(2)
1111
Sq(1,1)
1112
1113
TESTS::
1114
1115
sage: all([Adem(Milnor.Sq(n) ** 3)._repr_() == (Adem.Sq(n) ** 3)._repr_() for n in range(10)])
1116
True
1117
sage: Wall = SteenrodAlgebra(basis='wall')
1118
sage: Wall(Adem.Sq(4,4) * Milnor.Sq(4)) == Adem(Wall.Sq(4,4) * Milnor.Sq(4))
1119
True
1120
1121
sage: A3 = SteenrodAlgebra(p=3, basis='adem')
1122
sage: M3 = SteenrodAlgebra(p=3, basis='milnor')
1123
sage: 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)])
1124
True
1125
"""
1126
p = self.prime()
1127
basis = self.basis_name()
1128
if basis == 'milnor':
1129
if p == 2:
1130
from steenrod_algebra_mult import milnor_multiplication
1131
d = milnor_multiplication(t1, t2)
1132
else:
1133
from steenrod_algebra_mult import milnor_multiplication_odd
1134
d = milnor_multiplication_odd(t1, t2, p)
1135
return self._from_dict(d, coerce=True)
1136
elif basis == 'serre-cartan':
1137
from steenrod_algebra_mult import make_mono_admissible
1138
if p > 2:
1139
# make sure output has an odd number of terms. if both t1
1140
# and t2 have an odd number, concatenate them, adding the
1141
# middle term...
1142
#
1143
# if either t1 or t2 has an even number of terms, append a
1144
# 0.
1145
if (len(t1) % 2) == 0:
1146
t1 = t1 + (0,)
1147
if (len(t2) % 2) == 0:
1148
t2 = t2 + (0,)
1149
if t1[-1] + t2[0] == 2:
1150
return self.zero()
1151
mono = t1[:-1] + (t1[-1] + t2[0],) + t2[1:]
1152
d = make_mono_admissible(mono, p)
1153
else: # p=2
1154
mono = t1 + t2
1155
while len(mono) > 1 and mono[-1] == 0:
1156
mono = mono[:-1]
1157
d = make_mono_admissible(mono)
1158
return self._from_dict(d, coerce=True)
1159
else:
1160
x = self({t1: 1})
1161
y = self({t2: 1})
1162
A = SteenrodAlgebra(basis='milnor', p=p)
1163
return self(A(x) * A(y))
1164
1165
def coproduct_on_basis(self, t, algorithm=None):
1166
r"""
1167
The coproduct of a basis element of this algebra
1168
1169
INPUT:
1170
1171
- ``t`` -- tuple, the index of a basis element of self
1172
1173
- ``algorithm`` -- None or a string, either 'milnor' or
1174
'serre-cartan' (or anything which will be converted to one
1175
of these by the function :func:`get_basis_name
1176
<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.
1177
If None, default to 'milnor' unless current basis is
1178
'serre-cartan', in which case use 'serre-cartan'.
1179
1180
ALGORITHM: The coproduct on a Milnor basis element `P(n_1,
1181
n_2, ...)` is `\sum P(i_1, i_2, ...) \otimes P(j_1, j_2,
1182
...)`, summed over all `i_k + j_k = n_k` for each `k`. At odd
1183
primes, each element `Q_n` is primitive: its coproduct is `Q_n
1184
\otimes 1 + 1 \otimes Q_n`.
1185
1186
One can deduce a coproduct formula for the Serre-Cartan basis
1187
from this: the coproduct on each `P^n` is `\sum P^i \otimes
1188
P^{n-i}` and at odd primes `\beta` is primitive. Since the
1189
coproduct is an algebra map, one can then compute the
1190
coproduct on any Serre-Cartan basis element.
1191
1192
Which of these methods is used is controlled by whether
1193
``algorithm`` is 'milnor' or 'serre-cartan'.
1194
1195
OUTPUT: the coproduct of the corresponding basis element,
1196
as an element of self tensor self.
1197
1198
EXAMPLES::
1199
1200
sage: A = SteenrodAlgebra()
1201
sage: A.coproduct_on_basis((3,))
1202
1 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 1
1203
1204
TESTS::
1205
1206
sage: all([A.coproduct_on_basis((n,1), algorithm='milnor') == A.coproduct_on_basis((n,1), algorithm='adem') for n in range(9)]) # long time
1207
True
1208
sage: A7 = SteenrodAlgebra(p=7, basis='adem')
1209
sage: 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 time
1210
True
1211
"""
1212
def coprod_list(t):
1213
"""
1214
if t = (n0, n1, ...), then return list of terms (i0, i1,
1215
...) where ik <= nk for each k. From each such term, can
1216
recover the second factor in the coproduct.
1217
"""
1218
if len(t) == 0:
1219
return [()]
1220
if len(t) == 1:
1221
return [[a] for a in range(t[0] + 1)]
1222
ans = []
1223
for i in range(t[0] + 1):
1224
ans.extend([[i] + x for x in coprod_list(t[1:])])
1225
return ans
1226
1227
from steenrod_algebra_misc import get_basis_name
1228
p = self.prime()
1229
basis = self.basis_name()
1230
if algorithm is None:
1231
if basis == 'serre-cartan':
1232
algorithm = 'serre-cartan'
1233
else:
1234
algorithm = 'milnor'
1235
else:
1236
algorithm = get_basis_name(algorithm, p)
1237
if basis == algorithm:
1238
if basis == 'milnor':
1239
if p == 2:
1240
left = coprod_list(t)
1241
right = [[x-y for (x,y) in zip(t, m)] for m in left]
1242
old = list(left)
1243
left = []
1244
# trim trailing zeros:
1245
for a in old:
1246
while len(a) > 0 and a[-1] == 0:
1247
a = a[:-1]
1248
left.append(tuple(a))
1249
old = list(right)
1250
right = []
1251
for a in old:
1252
while len(a) > 0 and a[-1] == 0:
1253
a = a[:-1]
1254
right.append(tuple(a))
1255
tens = dict().fromkeys(zip(left, right), 1)
1256
return self.tensor_square()._from_dict(tens)
1257
else: # p odd
1258
from sage.combinat.permutation import Permutation
1259
from steenrod_algebra_misc import convert_perm
1260
from sage.sets.set import Set
1261
left_p = coprod_list(t[1])
1262
right_p = [[x-y for (x,y) in zip(t[1], m)] for m in left_p]
1263
old = list(left_p)
1264
left_p = []
1265
# trim trailing zeros:
1266
for a in old:
1267
while len(a) > 0 and a[-1] == 0:
1268
a = a[:-1]
1269
left_p.append(tuple(a))
1270
old = list(right_p)
1271
right_p = []
1272
for a in old:
1273
while len(a) > 0 and a[-1] == 0:
1274
a = a[:-1]
1275
right_p.append(tuple(a))
1276
all_q = Set(t[0])
1277
tens_q = {}
1278
for a in all_q.subsets():
1279
left_q = sorted(list(a))
1280
right_q = sorted(list(all_q - a))
1281
sign = Permutation(convert_perm(left_q + right_q)).signature()
1282
tens_q[(tuple(left_q), tuple(right_q))] = sign
1283
tens = {}
1284
for l, r in zip(left_p, right_p):
1285
for q in tens_q:
1286
tens[((q[0], l), (q[1], r))] = tens_q[q]
1287
return self.tensor_square()._from_dict(tens)
1288
elif basis == 'serre-cartan':
1289
from sage.categories.tensor import tensor
1290
result = self.tensor_square().one()
1291
if p == 2:
1292
for n in t:
1293
s = self.tensor_square().zero()
1294
for i in range(0, n+1):
1295
s += tensor((self.Sq(i), self.Sq(n-i)))
1296
result = result * s
1297
return result
1298
else:
1299
bockstein = True
1300
for n in t:
1301
if bockstein:
1302
if n != 0:
1303
s = tensor((self.Q(0), self.one())) + tensor((self.one(), self.Q(0)))
1304
else:
1305
s = self.tensor_square().one()
1306
bockstein = False
1307
else:
1308
s = self.tensor_square().zero()
1309
for i in range(0, n+1):
1310
s += tensor((self.P(i), self.P(n-i)))
1311
bockstein = True
1312
result = result * s
1313
return result
1314
else:
1315
from sage.categories.tensor import tensor
1316
A = SteenrodAlgebra(p=p, basis=algorithm)
1317
x = A(self._change_basis_on_basis(t, algorithm)).coproduct(algorithm=algorithm)
1318
result = self.tensor_square().zero()
1319
for (a,b), coeff in x:
1320
result += coeff * tensor((A._change_basis_on_basis(a, basis),
1321
A._change_basis_on_basis(b, basis)))
1322
return result
1323
1324
def coproduct(self, x, algorithm='milnor'):
1325
r"""
1326
Return the coproduct of an element ``x`` of this algebra.
1327
1328
INPUT:
1329
1330
- ``x`` -- element of self
1331
1332
- ``algorithm`` -- None or a string, either 'milnor' or
1333
'serre-cartan' (or anything which will be converted to one
1334
of these by the function :func:`get_basis_name
1335
<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`.
1336
If None, default to 'serre-cartan' if current basis is
1337
'serre-cartan'; otherwise use 'milnor'.
1338
1339
This calls :meth:`coproduct_on_basis` on the summands of ``x``
1340
and extends linearly.
1341
1342
EXAMPLES::
1343
1344
sage: SteenrodAlgebra().Sq(3).coproduct()
1345
1 # Sq(3) + Sq(1) # Sq(2) + Sq(2) # Sq(1) + Sq(3) # 1
1346
1347
The element `\text{Sq}(0,1)` is primitive::
1348
1349
sage: SteenrodAlgebra(basis='adem').Sq(0,1).coproduct()
1350
1 # Sq^2 Sq^1 + 1 # Sq^3 + Sq^2 Sq^1 # 1 + Sq^3 # 1
1351
sage: SteenrodAlgebra(basis='pst').Sq(0,1).coproduct()
1352
1 # P^0_2 + P^0_2 # 1
1353
1354
sage: SteenrodAlgebra(p=3).P(4).coproduct()
1355
1 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 1
1356
sage: SteenrodAlgebra(p=3).P(4).coproduct(algorithm='serre-cartan')
1357
1 # P(4) + P(1) # P(3) + P(2) # P(2) + P(3) # P(1) + P(4) # 1
1358
sage: SteenrodAlgebra(p=3, basis='serre-cartan').P(4).coproduct()
1359
1 # P^4 + P^1 # P^3 + P^2 # P^2 + P^3 # P^1 + P^4 # 1
1360
sage: SteenrodAlgebra(p=11, profile=((), (2,1,2))).Q(0,2).coproduct()
1361
1 # Q_0 Q_2 + Q_0 # Q_2 + Q_0 Q_2 # 1 - Q_2 # Q_0
1362
"""
1363
# taken from categories.coalgebras_with_basis, then modified
1364
# to allow the use of the "algorithm" keyword
1365
coprod = lambda x: self.coproduct_on_basis(x, algorithm)
1366
return Hom(self, tensor([self, self]), ModulesWithBasis(self.base_ring()))(on_basis = coprod)(x)
1367
1368
def antipode_on_basis(self, t):
1369
r"""
1370
The antipode of a basis element of this algebra
1371
1372
INPUT:
1373
1374
- ``t`` -- tuple, the index of a basis element of self
1375
1376
OUTPUT: the antipode of the corresponding basis element,
1377
as an element of self.
1378
1379
ALGORITHM: according to a result of Milnor's, the antipode of
1380
`\text{Sq}(n)` is the sum of all of the Milnor basis elements
1381
in dimension `n`. So: convert the element to the Serre-Cartan
1382
basis, thus writing it as a sum of products of elements
1383
`\text{Sq}(n)`, and use Milnor's formula for the antipode of
1384
`\text{Sq}(n)`, together with the fact that the antipode is an
1385
antihomomorphism: if we call the antipode `c`, then `c(ab) =
1386
c(b) c(a)`.
1387
1388
At odd primes, a similar method is used: the antipode of
1389
`P(n)` is the sum of the Milnor P basis elements in dimension
1390
`n*2(p-1)`, multiplied by `(-1)^n`, and the antipode of `\beta
1391
= Q_0` is `-Q_0`. So convert to the Serre-Cartan basis, as in
1392
the `p=2` case.
1393
1394
EXAMPLES::
1395
1396
sage: A = SteenrodAlgebra()
1397
sage: A.antipode_on_basis((4,))
1398
Sq(1,1) + Sq(4)
1399
sage: A.Sq(4).antipode()
1400
Sq(1,1) + Sq(4)
1401
sage: Adem = SteenrodAlgebra(basis='adem')
1402
sage: Adem.Sq(4).antipode()
1403
Sq^3 Sq^1 + Sq^4
1404
sage: SteenrodAlgebra(basis='pst').Sq(3).antipode()
1405
P^0_1 P^1_1 + P^0_2
1406
sage: a = SteenrodAlgebra(basis='wall_long').Sq(10)
1407
sage: a.antipode()
1408
Sq^1 Sq^2 Sq^4 Sq^1 Sq^2 + Sq^2 Sq^4 Sq^1 Sq^2 Sq^1 + Sq^8 Sq^2
1409
sage: a.antipode().antipode() == a
1410
True
1411
1412
sage: SteenrodAlgebra(p=3).P(6).antipode()
1413
P(2,1) + P(6)
1414
sage: SteenrodAlgebra(p=3).P(6).antipode().antipode()
1415
P(6)
1416
1417
TESTS::
1418
1419
sage: Milnor = SteenrodAlgebra()
1420
sage: all([x.antipode().antipode() == x for x in Milnor.basis(11)]) # long time
1421
True
1422
sage: A5 = SteenrodAlgebra(p=5, basis='adem')
1423
sage: all([x.antipode().antipode() == x for x in A5.basis(25)])
1424
True
1425
sage: H = SteenrodAlgebra(profile=[2,2,1])
1426
sage: H.Sq(1,2).antipode() in H
1427
True
1428
"""
1429
p = self.prime()
1430
if self.basis_name() == 'serre-cartan':
1431
antipode = self.one()
1432
if p == 2:
1433
for n in t:
1434
antipode = self(sum(SteenrodAlgebra().basis(n))) * antipode
1435
else:
1436
from sage.misc.functional import is_even
1437
for index, n in enumerate(t):
1438
if is_even(index):
1439
if n != 0:
1440
antipode = -self.Q(0) * antipode
1441
else:
1442
B = SteenrodAlgebra(p=p).basis(n * 2 * (p-1))
1443
s = self(0)
1444
for b in B:
1445
if len(b.leading_support()[0]) == 0:
1446
s += self(b)
1447
antipode = (-1)**n * s * antipode
1448
return antipode
1449
return self(self._change_basis_on_basis(t, 'serre-cartan').antipode())
1450
1451
def _milnor_on_basis(self, t):
1452
"""
1453
Convert the tuple t in the current basis to an element in the
1454
Milnor basis.
1455
1456
INPUT:
1457
1458
- t - tuple, representing basis element in the current basis.
1459
1460
OUTPUT: element of the Steenrod algebra with the Milnor basis
1461
1462
ALGORITHM: there is a simple conversion from each basis to the
1463
Milnor basis, so use that. In more detail:
1464
1465
- If the current basis is the Milnor basis, just return the
1466
corresponding element.
1467
1468
- If the current basis is the Serre-Cartan basis: when `p=2`,
1469
the element `\text{Sq}^a` equals the Milnor element
1470
`\text{Sq}(a)`; when `p` is odd, `\mathcal{P}^a =
1471
\mathcal{P}(a)` and `\beta = Q_0`. Hence for any
1472
Serre-Cartan basis element, represent it in the
1473
Milnor basis by computing an appropriate product using
1474
Milnor multiplication.
1475
1476
- The same goes for Arnon's C basis, since the elements are
1477
monomials in the Steenrod squares.
1478
1479
- If the current basis is Wood's Y or Z bases, then each basis
1480
element is a monomial in the classes `w(m,k) =
1481
\text{Sq}^{2^m (2^{k+1}-1)}`. So again, multiply the
1482
corresponding Milnor elements together.
1483
1484
- The Wall basis: each basis element is a monomial in the
1485
elements `Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.
1486
1487
- Arnon's A basis: each basis element is a monomial in the
1488
elements `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`.
1489
1490
- The `P^s_t` bases: when `p=2`, each basis element is a
1491
monomial in the elements `P^s_t`. When `p` is odd, each
1492
basis element is a product of elements `Q_i` and a monomial
1493
in the elements `(P^s_t)^n` where `0 < n < p`.
1494
1495
- The commutator bases: when `p=2`, each basis element is a
1496
monomial in the iterated commutators `c_{i,j}`, defined by
1497
`c_{i,1} = \text{Sq}(2^i)` and `c_{i,j} = [c_{i,j-1},
1498
\text{Sq}(2^{i+j-1})]`. When `p` is odd, each basis element
1499
is a product of elements `Q_i` and a monomial in the
1500
elements `c_{i,j}^n` where `0 < n < p`, `c_{i,1} =
1501
P(p^i)` and `c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}]`.
1502
1503
EXAMPLES::
1504
1505
sage: Adem = SteenrodAlgebra(basis='serre-cartan')
1506
sage: Adem._milnor_on_basis((2,1)) # Sq^2 Sq^1
1507
Sq(0,1) + Sq(3)
1508
sage: Pst = SteenrodAlgebra(basis='pst')
1509
sage: Pst._milnor_on_basis(((0,1), (1,1), (2,1)))
1510
Sq(7)
1511
"""
1512
basis = self.basis_name()
1513
p = self.prime()
1514
A = SteenrodAlgebra(p=p)
1515
# milnor
1516
if basis == 'milnor':
1517
return A({t: 1})
1518
1519
ans = A(1)
1520
# serre-cartan, arnonc
1521
if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):
1522
for j in t:
1523
ans = ans * A.Sq(j)
1524
1525
elif p > 2 and basis == 'serre-cartan':
1526
bockstein = True
1527
for j in t:
1528
if bockstein:
1529
if j != 0:
1530
ans = ans * A.Q(0)
1531
bockstein = False
1532
else:
1533
ans = ans * A.P(j)
1534
bockstein = True
1535
# wood_y:
1536
elif basis == 'woody' or basis == 'woodz':
1537
# each entry in t is a pair (m,k), corresponding to w(m,k), defined by
1538
# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.
1539
for (m,k) in t:
1540
ans = ans * A.Sq(2**m * (2**(k+1) - 1))
1541
1542
# wall[_long]
1543
elif basis.find('wall') >= 0:
1544
# each entry in t is a pair (m,k), corresponding to Q^m_k, defined by
1545
#`Q^m_k = Sq(2^k) Sq(2^{k+1}) ... Sq(2^m)`.
1546
for (m,k) in t:
1547
exponent = 2**k
1548
ans = ans * A.Sq(exponent)
1549
for i in range(m-k):
1550
exponent = exponent * 2
1551
ans = ans * A.Sq(exponent)
1552
1553
# pst...
1554
elif basis.find('pst') >= 0:
1555
if p == 2:
1556
# each entry in t is a pair (i,j), corresponding to P^i_j
1557
for (i,j) in t:
1558
ans = ans * A.pst(i,j)
1559
else:
1560
# t = (Q, P) where Q is the tuple of Q_i's, and P is a
1561
# tuple with entries of the form ((i,j), n),
1562
# corresponding to (P^i_j)^n
1563
if len(t[0]) > 0:
1564
ans = ans * A.Q(*t[0])
1565
for ((i, j), n) in t[1]:
1566
ans = ans * (A.pst(i,j))**n
1567
1568
# arnona[_long]
1569
elif basis.find('arnona') >= 0:
1570
# each entry in t is a pair (m,k), corresponding to X^m_k, defined by
1571
# `X^m_k = Sq(2^m) ... Sq(2^{k+1}) Sq(2^k)`
1572
for (m,k) in t:
1573
exponent = 2**k
1574
X = A.Sq(exponent)
1575
for i in range(m-k):
1576
exponent = exponent * 2
1577
X = A.Sq(exponent) * X
1578
ans = ans * X
1579
1580
# comm...[_long]
1581
elif basis.find('comm') >= 0:
1582
if p == 2:
1583
# each entry in t is a pair (i,j), corresponding to
1584
# c_{i,j}, the iterated commutator defined by c_{i,1}
1585
# = Sq(2^i) and c_{i,j} = [c_{i,j-1}, Sq(2^{i+j-1})].
1586
for (i,j) in t:
1587
comm = A.Sq(2**i)
1588
for k in range(2, j+1):
1589
y = A.Sq(2**(i+k-1))
1590
comm = comm * y + y * comm
1591
ans = ans * comm
1592
else:
1593
# t = (Q, P) where Q is the tuple of Q_i's, and P is a
1594
# tuple with entries of the form ((i,j), n),
1595
# corresponding to (c_{i,j})^n. Here c_{i,j} is the
1596
# iterated commutator defined by c_{i,1} = P(p^i) and
1597
# c_{i,j} = [P(p^{i+j-1}), c_{i,j-1}].
1598
if len(t[0]) > 0:
1599
ans = ans * A.Q(*t[0])
1600
for ((i, j), n) in t[1]:
1601
comm = A.P(p**i)
1602
for k in range(2, j+1):
1603
y = A.P(p**(i+k-1))
1604
comm = y * comm - comm * y
1605
ans = ans * comm**n
1606
return ans
1607
1608
@lazy_attribute
1609
def milnor(self):
1610
"""
1611
Convert an element of this algebra to the Milnor basis
1612
1613
INPUT:
1614
1615
- x - an element of this algebra
1616
1617
OUTPUT: x converted to the Milnor basis
1618
1619
ALGORITHM: use the method ``_milnor_on_basis`` and linearity.
1620
1621
EXAMPLES::
1622
1623
sage: Adem = SteenrodAlgebra(basis='adem')
1624
sage: a = Adem.Sq(2) * Adem.Sq(1)
1625
sage: Adem.milnor(a)
1626
Sq(0,1) + Sq(3)
1627
"""
1628
A = SteenrodAlgebra(p=self.prime(), basis='milnor')
1629
return self._module_morphism(self._milnor_on_basis, codomain=A)
1630
1631
def _change_basis_on_basis(self, t, basis='milnor'):
1632
"""
1633
Convert the tuple t to the named basis.
1634
1635
INPUT:
1636
1637
- ``t`` - tuple, representing basis element in the current basis.
1638
1639
- ``basis`` - string, the basis to which to convert, optional
1640
(default 'milnor')
1641
1642
OUTPUT: an element of the Steenrod algebra with basis ``basis``.
1643
1644
ALGORITHM: it's straightforward to convert to the Milnor basis
1645
(using :meth:`milnor` or :meth:`_milnor_on_basis`), so it's
1646
straightforward to produce a matrix representing this
1647
conversion in any degree. The function
1648
:func:`convert_from_milnor_matrix
1649
<steenrod_algebra_bases.convert_from_milnor_matrix>` provides
1650
the inverse operation.
1651
1652
So: convert from the current basis to the Milnor basis, then
1653
from the Milnor basis to the new basis.
1654
1655
EXAMPLES::
1656
1657
sage: Adem = SteenrodAlgebra(basis='adem')
1658
sage: a = Adem({(2,1): 1}); a
1659
Sq^2 Sq^1
1660
sage: a.change_basis('adem') # indirect doctest
1661
Sq^2 Sq^1
1662
sage: a.change_basis('milnor')
1663
Sq(0,1) + Sq(3)
1664
sage: a.change_basis('pst')
1665
P^0_1 P^1_1 + P^0_2
1666
sage: a.change_basis('milnor').change_basis('adem').change_basis('adem')
1667
Sq^2 Sq^1
1668
sage: a.change_basis('wall') == a.change_basis('woody')
1669
True
1670
1671
TESTS::
1672
1673
sage: a = sum(SteenrodAlgebra(basis='comm').basis(10))
1674
sage: a.change_basis('adem').change_basis('wall').change_basis('comm')._repr_() == a._repr_()
1675
True
1676
sage: a.change_basis('pst').change_basis('milnor').change_basis('comm')._repr_() == a._repr_()
1677
True
1678
sage: a.change_basis('woody').change_basis('arnona').change_basis('comm')._repr_() == a._repr_()
1679
True
1680
1681
sage: b = sum(SteenrodAlgebra(p=3).basis(41))
1682
sage: b.change_basis('adem').change_basis('adem').change_basis('milnor')._repr_() == b._repr_()
1683
True
1684
"""
1685
from sage.matrix.constructor import matrix
1686
from sage.rings.all import GF
1687
from steenrod_algebra_bases import steenrod_algebra_basis,\
1688
convert_from_milnor_matrix
1689
from steenrod_algebra_misc import get_basis_name
1690
basis = get_basis_name(basis, self.prime())
1691
if basis == self.basis_name():
1692
return self({t: 1})
1693
a = self._milnor_on_basis(t)
1694
if basis == 'milnor':
1695
return a
1696
d = a.monomial_coefficients()
1697
p = self.prime()
1698
deg = a.degree()
1699
A = SteenrodAlgebra(basis=basis, p=p)
1700
if deg == 0:
1701
return A(a.leading_coefficient())
1702
Bnew = steenrod_algebra_basis(deg, basis, p)
1703
Bmil = steenrod_algebra_basis(deg, 'milnor', p)
1704
v = []
1705
for a in Bmil:
1706
v.append(d.get(a, 0))
1707
out = (matrix(GF(p), 1, len(v), v) *
1708
convert_from_milnor_matrix(deg, basis, p))
1709
new_d = dict(zip(Bnew, out[0]))
1710
return A(new_d)
1711
1712
def _change_basis(self, x, basis='milnor'):
1713
"""
1714
Convert an element of this algebra to the specified basis
1715
1716
INPUT:
1717
1718
- ``x`` - an element of this algebra.
1719
1720
- ``basis`` - string, the basis to which to convert, optional
1721
(default 'milnor')
1722
1723
OUTPUT: an element of the Steenrod algebra with basis ``basis``.
1724
1725
ALGORITHM: use :meth:`_change_basis_on_basis` and linearity
1726
1727
EXAMPLES::
1728
1729
sage: Adem = SteenrodAlgebra(basis='adem')
1730
sage: a = Adem({(2,1): 1}); a
1731
Sq^2 Sq^1
1732
sage: a.change_basis('adem') # indirect doctest
1733
Sq^2 Sq^1
1734
sage: a.change_basis('milnor')
1735
Sq(0,1) + Sq(3)
1736
sage: a.change_basis('pst')
1737
P^0_1 P^1_1 + P^0_2
1738
"""
1739
if basis == 'milnor':
1740
return x.milnor()
1741
A = SteenrodAlgebra(p=self.prime(), basis=basis)
1742
change = lambda y: self._change_basis_on_basis(y, basis)
1743
f = self._module_morphism(change, codomain=A)
1744
return f(x)
1745
1746
def degree_on_basis(self, t):
1747
r"""
1748
The degree of the monomial specified by the tuple ``t``.
1749
1750
INPUT:
1751
1752
- ``t`` - tuple, representing basis element in the current basis.
1753
1754
OUTPUT: integer, the degree of the corresponding element.
1755
1756
The degree of `\text{Sq}(i_1,i_2,i_3,...)` is
1757
1758
.. math::
1759
1760
i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....
1761
1762
At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the
1763
degree of `\mathcal{P}(i_1, i_2, ...)` is
1764
1765
.. math::
1766
1767
\sum_{k \geq 0} 2(p^k - 1) i_k.
1768
1769
ALGORITHM: Each basis element is represented in terms relevant
1770
to the particular basis: 'milnor' basis elements (at the prime
1771
2) are given by tuples ``(a,b,c,...)`` corresponding to the
1772
element `\text{Sq}(a,b,c,...)`, while 'pst' basis elements are
1773
given by tuples of pairs ``((a, b), (c, d), ...)``,
1774
corresponding to the product `P^a_b P^c_d ...`. The other
1775
bases have similar descriptions. The degree of each basis
1776
element is computed from this data, rather than converting the
1777
element to the Milnor basis, for example, and then computing
1778
the degree.
1779
1780
EXAMPLES::
1781
1782
sage: SteenrodAlgebra().degree_on_basis((0,0,1))
1783
7
1784
sage: Sq(7).degree()
1785
7
1786
1787
sage: A11 = SteenrodAlgebra(p=11)
1788
sage: A11.degree_on_basis(((), (1,1)))
1789
260
1790
sage: A11.degree_on_basis(((2,), ()))
1791
241
1792
"""
1793
def p_degree(m, mult=1, prime=2):
1794
"""
1795
For m=(n_1, n_2, n_3, ...), Sum_i (mult) * n_i * (p^i - 1)
1796
"""
1797
i = 0
1798
deg = 0
1799
for n in m:
1800
i += 1
1801
deg += n*mult*(prime**i - 1)
1802
return deg
1803
1804
def q_degree(m, prime=3):
1805
"""
1806
For m=(n_0, n_1, n_2, ...), Sum_i 2*p^(n_i) - 1
1807
"""
1808
deg = 0
1809
for n in m:
1810
deg += 2*prime**n - 1
1811
return deg
1812
1813
p = self.prime()
1814
basis = self.basis_name()
1815
# milnor
1816
if basis == 'milnor':
1817
if p == 2:
1818
return p_degree(t)
1819
else:
1820
return q_degree(t[0], prime=p) + p_degree(t[1], prime=p, mult=2)
1821
# serre-cartan, arnonc
1822
if p == 2 and (basis == 'serre-cartan' or basis == 'arnonc'):
1823
return sum(t)
1824
if p > 2 and basis == 'serre-cartan':
1825
bockstein = True
1826
n = 0
1827
for j in t:
1828
if bockstein:
1829
if j != 0:
1830
n += 1
1831
bockstein = False
1832
else:
1833
n += 2 * j * (p - 1)
1834
bockstein = True
1835
return n
1836
1837
# wood_y:
1838
if basis == 'woody' or basis == 'woodz':
1839
# each entry in t is a pair (m,k), corresponding to w(m,k), defined by
1840
# `w(m,k) = \text{Sq}^{2^m (2^{k+1}-1)}`.
1841
return sum(2**m * (2**(k+1)-1) for (m,k) in t)
1842
1843
# wall, arnon_a
1844
if basis.find('wall') >= 0 or basis.find('arnona') >= 0:
1845
# Wall: each entry in t is a pair (m,k), corresponding to
1846
# Q^m_k, defined by `Q^m_k = Sq(2^k) Sq(2^{k+1})
1847
# ... Sq(2^m)`.
1848
#
1849
# Arnon A: each entry in t is a pair (m,k), corresponding
1850
# to X^m_k, defined by `X^m_k = Sq(2^m) ... Sq(2^{k+1})
1851
# Sq(2^k)`
1852
return sum(2**k * (2**(m-k+1)-1) for (m,k) in t)
1853
1854
# pst, comm
1855
if basis.find('pst') >= 0 or basis.find('comm') >= 0:
1856
if p == 2:
1857
# Pst: each entry in t is a pair (i,j), corresponding to P^i_j
1858
#
1859
# Comm: each entry in t is a pair (i,j), corresponding
1860
# to c_{i,j}, the iterated commutator defined by
1861
# c_{i,1} = Sq(2^i) and c_{i,j} = [c_{i,j-1},
1862
# Sq(2^{i+j-1})].
1863
return sum(2**m * (2**k - 1) for (m,k) in t)
1864
# p odd:
1865
#
1866
# Pst: have pair (Q, P) where Q is a tuple of Q's, as in
1867
# the Milnor basis, and P is a tuple of terms of the form
1868
# ((i,j), n), corresponding to (P^i_j)^n.
1869
#
1870
# Comm: similarly (Q, C) with Q as above and C a tuple
1871
# with each entry in t is of the form ((s,t), n),
1872
# corresponding to c_{s,t}^n. here c_{s,t} is the the
1873
# iterated commutator defined by c_{s,1} = P(p^s) and
1874
# c_{s,t} = [P(p^{s+t-1}), c_{s,t-1}].
1875
q_deg = q_degree(t[0], prime=p)
1876
p_deg = sum(2 * n * p**s * (p**t - 1) for ((s,t), n) in t[1])
1877
return q_deg + p_deg
1878
1879
# coercion methods:
1880
1881
def _coerce_map_from_(self, S):
1882
r"""
1883
True if there is a coercion from ``S`` to ``self``, False otherwise.
1884
1885
INPUT:
1886
1887
- ``S`` - a Sage object.
1888
1889
The algebras that coerce into the mod p Steenrod algebra are:
1890
1891
- the mod p Steenrod algebra `A`
1892
- its sub-Hopf algebras
1893
- its homogeneous components
1894
- its base field `GF(p)`
1895
- `ZZ`
1896
1897
Similarly, a sub-Hopf algebra `B` of `A` coerces into another
1898
sub-Hopf algebra `C` if and only if the profile function for
1899
`B` is less than or equal to that of `C`, pointwise.
1900
1901
EXAMPLES::
1902
1903
sage: A = SteenrodAlgebra()
1904
sage: A1 = SteenrodAlgebra(profile=[2,1])
1905
sage: A2 = SteenrodAlgebra(profile=[3,2,1])
1906
sage: B = SteenrodAlgebra(profile=[1,2,1])
1907
sage: A._coerce_map_from_(A1)
1908
True
1909
sage: A2._coerce_map_from_(A1)
1910
True
1911
sage: A1._coerce_map_from_(A)
1912
False
1913
sage: A1._coerce_map_from_(B)
1914
False
1915
sage: B._coerce_map_from_(A1)
1916
False
1917
1918
sage: A._coerce_map_from_(A[12])
1919
True
1920
1921
sage: A3 = SteenrodAlgebra(p=3)
1922
sage: A31 = SteenrodAlgebra(p=3, profile=([1], [2, 2]))
1923
sage: B3 = SteenrodAlgebra(p=3, profile=([1, 2, 1], [1]))
1924
sage: A3._coerce_map_from_(A31)
1925
True
1926
sage: A31._coerce_map_from_(A3)
1927
False
1928
sage: A31._coerce_map_from_(B3)
1929
False
1930
sage: B3._coerce_map_from_(A31)
1931
False
1932
"""
1933
from sage.rings.all import ZZ, GF
1934
from sage.rings.infinity import Infinity
1935
p = self.prime()
1936
if S == ZZ or S == GF(p):
1937
return True
1938
if (isinstance(S, SteenrodAlgebra_generic) and p == S.prime()):
1939
# deal with profiles.
1940
if p == 2:
1941
self_prec = len(self._profile)
1942
S_prec = len(S._profile)
1943
return all([self.profile(i) >= S.profile(i)
1944
for i in range(1, max(self_prec, S_prec)+1)])
1945
self_prec = len(self._profile[0])
1946
S_prec = len(S._profile[0])
1947
return (all([self.profile(i) >= S.profile(i)
1948
for i in range(1, max(self_prec, S_prec)+1)])
1949
and all([self.profile(i, 1) >= S.profile(i, 1)
1950
for i in range(1, max(self_prec, S_prec)+1)]))
1951
if (isinstance(S, CombinatorialFreeModule)
1952
and S.dimension() < Infinity and p == S.base_ring().characteristic()):
1953
from steenrod_algebra_misc import get_basis_name
1954
try:
1955
get_basis_name(S.prefix(), S.base_ring().characteristic())
1956
# return all([a in self for a in S.basis()])
1957
return True
1958
except ValueError:
1959
return False
1960
return False
1961
1962
def _element_constructor_(self, x):
1963
r"""
1964
Try to turn ``x`` into an element of ``self``.
1965
1966
INPUT:
1967
1968
- ``x`` - an element of some Steenrod algebra or an element of
1969
`\ZZ` or `\GF{p}` or a dict
1970
1971
OUTPUT: ``x`` as a member of ``self``.
1972
1973
If ``x`` is a dict, then call :meth:`_from_dict` on it,
1974
coercing the coefficients into the base field. That is, treat
1975
it as having entries of the form ``tuple: coeff``, where
1976
``tuple`` is a tuple representing a basis element and
1977
``coeff`` is the coefficient of that element.
1978
1979
EXAMPLES::
1980
1981
sage: A1 = SteenrodAlgebra(profile=[2,1])
1982
sage: A1(Sq(2)) # indirect doctest
1983
Sq(2)
1984
sage: A1._element_constructor_(Sq(2))
1985
Sq(2)
1986
sage: A1(3) # map integer into A1
1987
1
1988
sage: A1._element_constructor_(Sq(4)) # Sq(4) not in A1
1989
Traceback (most recent call last):
1990
...
1991
ValueError: Element does not lie in this Steenrod algebra
1992
sage: A1({(2,): 1, (1,): 13})
1993
Sq(1) + Sq(2)
1994
"""
1995
from sage.rings.all import ZZ, GF
1996
if x in GF(self.prime()) or x in ZZ:
1997
return self.from_base_ring_from_one_basis(x)
1998
1999
if isinstance(x, dict):
2000
A = SteenrodAlgebra(p=self.prime(), basis=self.basis_name())
2001
x = A._from_dict(x, coerce=True)
2002
if x in self:
2003
if x.basis_name() == self.basis_name():
2004
if x.parent() is self:
2005
return x
2006
return self._from_dict(x.monomial_coefficients(), coerce=True)
2007
else:
2008
a = x.milnor()
2009
if self.basis_name() == 'milnor':
2010
return a
2011
return a.change_basis(self.basis_name())
2012
raise ValueError("Element does not lie in this Steenrod algebra")
2013
2014
def __contains__(self, x):
2015
r"""
2016
True if self contains x.
2017
2018
EXAMPLES::
2019
2020
sage: Sq(3,1,1) in SteenrodAlgebra()
2021
True
2022
sage: Sq(3,1,1) in SteenrodAlgebra(p=5)
2023
False
2024
2025
sage: A1 = SteenrodAlgebra(profile=[2,1])
2026
sage: Sq(3) in A1
2027
True
2028
sage: Sq(4) in A1
2029
False
2030
sage: Sq(0,2) in A1
2031
False
2032
2033
sage: A_3 = SteenrodAlgebra(p=3)
2034
sage: B_3 = SteenrodAlgebra(p=3, profile=([1], [2,2,1,1]))
2035
sage: A_3.P(2) in B_3
2036
True
2037
sage: A_3.P(3) in B_3
2038
False
2039
sage: A_3.Q(1) in B_3
2040
True
2041
sage: A_3.P(1) * A_3.Q(2) in B_3
2042
False
2043
"""
2044
from sage.rings.all import GF
2045
p = self.prime()
2046
if (GF(p).__contains__(x)):
2047
return True
2048
if (isinstance(x, self.Element)
2049
and x.prime() == p):
2050
A = SteenrodAlgebra(p=p, basis=self.basis_name())
2051
if self._has_nontrivial_profile():
2052
return all([self._check_profile_on_basis(mono)
2053
for mono in A(x).support()])
2054
return True # trivial profile, so True
2055
return False
2056
2057
def basis(self, d=None):
2058
"""
2059
Returns basis for self, either the whole basis or the basis in
2060
degree `d`.
2061
2062
INPUT:
2063
2064
- `d` - integer or None, optional (default None)
2065
2066
OUTPUT: If `d` is None, then return a basis of the algebra.
2067
Otherwise, return the basis in degree `d`.
2068
2069
EXAMPLES::
2070
2071
sage: A3 = SteenrodAlgebra(3)
2072
sage: A3.basis(13)
2073
Family (Q_1 P(2), Q_0 P(3))
2074
sage: SteenrodAlgebra(2, 'adem').basis(12)
2075
Family (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)
2076
2077
sage: A = SteenrodAlgebra(profile=[1,2,1])
2078
sage: A.basis(2)
2079
Family ()
2080
sage: A.basis(3)
2081
Family (Sq(0,1),)
2082
sage: SteenrodAlgebra().basis(3)
2083
Family (Sq(0,1), Sq(3))
2084
sage: A_pst = SteenrodAlgebra(profile=[1,2,1], basis='pst')
2085
sage: A_pst.basis(3)
2086
Family (P^0_2,)
2087
2088
sage: A7 = SteenrodAlgebra(p=7)
2089
sage: B = SteenrodAlgebra(p=7, profile=([1,2,1], [1]))
2090
sage: A7.basis(84)
2091
Family (P(7),)
2092
sage: B.basis(84)
2093
Family ()
2094
sage: C = SteenrodAlgebra(p=7, profile=([1], [2,2]))
2095
sage: A7.Q(0,1) in C.basis(14)
2096
True
2097
sage: A7.Q(2) in A7.basis(97)
2098
True
2099
sage: A7.Q(2) in C.basis(97)
2100
False
2101
2102
With no arguments, return the basis of the whole algebra.
2103
This doesn't print in a very helpful way, unfortunately::
2104
2105
sage: A7.basis()
2106
Lazy 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}}
2107
"""
2108
from sage.sets.family import Family
2109
if d is None:
2110
return Family(self._basis_keys, self.monomial)
2111
else:
2112
return Family([self.monomial(tuple(a)) for a in self._basis_fcn(d)])
2113
2114
def _check_profile_on_basis(self, t):
2115
"""
2116
True if the element specified by the tuple ``t`` is in this
2117
algebra.
2118
2119
INPUT:
2120
2121
- ``t`` - tuple of ...
2122
2123
2124
EXAMPLES::
2125
2126
sage: A = SteenrodAlgebra(profile=[1,2,1])
2127
sage: A._check_profile_on_basis((0,0,1))
2128
True
2129
sage: A._check_profile_on_basis((0,0,2))
2130
False
2131
sage: A5 = SteenrodAlgebra(p=5, profile=([3,2,1], [2,2,2,2,2]))
2132
sage: A5._check_profile_on_basis(((), (1,5)))
2133
True
2134
sage: A5._check_profile_on_basis(((1,1,1), (1,5)))
2135
True
2136
sage: A5._check_profile_on_basis(((1,1,1), (1,5,5)))
2137
False
2138
"""
2139
if self.basis_name() != 'milnor':
2140
A = SteenrodAlgebra(p=self.prime(),
2141
profile=self._profile,
2142
truncation_type=self._truncation_type)
2143
return all([A._check_profile_on_basis(a[0])
2144
for a in self._milnor_on_basis(t)])
2145
2146
from sage.rings.infinity import Infinity
2147
p = self.prime()
2148
if not self._has_nontrivial_profile():
2149
return True
2150
profile = self.profile
2151
if p == 2:
2152
return all([self.profile(i+1) == Infinity
2153
or t[i] < 2**self.profile(i+1)
2154
for i in range(len(t))])
2155
# p odd:
2156
if any([self.profile(i,1) != 2 for i in t[0]]):
2157
return False
2158
return all([self.profile(i+1,0) == Infinity
2159
or t[1][i] < p**self.profile(i+1,0)
2160
for i in range(len(t[1]))])
2161
2162
def P(self, *nums):
2163
r"""
2164
The element `P(a, b, c, ...)`
2165
2166
INPUT:
2167
2168
- ``a, b, c, ...`` - non-negative integers
2169
2170
OUTPUT: element of the Steenrod algebra given by the Milnor
2171
single basis element `P(a, b, c, ...)`
2172
2173
Note that at the prime 2, this is the same element as
2174
`\text{Sq}(a, b, c, ...)`.
2175
2176
EXAMPLES::
2177
2178
sage: A = SteenrodAlgebra(2)
2179
sage: A.P(5)
2180
Sq(5)
2181
sage: B = SteenrodAlgebra(3)
2182
sage: B.P(5,1,1)
2183
P(5,1,1)
2184
sage: B.P(1,1,-12,1)
2185
Traceback (most recent call last):
2186
...
2187
TypeError: entries must be non-negative integers
2188
2189
sage: SteenrodAlgebra(basis='serre-cartan').P(0,1)
2190
Sq^2 Sq^1 + Sq^3
2191
"""
2192
from sage.rings.all import Integer
2193
if self.basis_name() != 'milnor':
2194
return self(SteenrodAlgebra(p=self.prime()).P(*nums))
2195
while len(nums) > 0 and nums[-1] == 0:
2196
nums = nums[:-1]
2197
if len(nums) == 0 or (len(nums) == 1 and nums[0] == 0):
2198
return self.one()
2199
for i in nums:
2200
try:
2201
assert Integer(i) >= 0
2202
except (TypeError, AssertionError):
2203
raise TypeError("entries must be non-negative integers")
2204
2205
if self.prime() == 2:
2206
t = nums
2207
else:
2208
t = ((), nums)
2209
if self._check_profile_on_basis(t):
2210
A = SteenrodAlgebra_generic(p=self.prime())
2211
a = A.monomial(t)
2212
return self(a)
2213
raise ValueError("Element not in this algebra")
2214
2215
def Q_exp(self, *nums):
2216
r"""
2217
The element `Q_0^{e_0} Q_1^{e_1} ...` , given by
2218
specifying the exponents.
2219
2220
INPUT:
2221
2222
- ``e0, e1, ...`` - sequence of 0s and 1s
2223
2224
OUTPUT: The element `Q_0^{e_0} Q_1^{e_1} ...`
2225
2226
Note that at the prime 2, `Q_n` is the element
2227
`\text{Sq}(0,0,...,1)` , where the 1 is in the
2228
`(n+1)^{st}` position.
2229
2230
Compare this to the method :meth:`Q`, which defines a similar
2231
element, but by specifying the tuple of subscripts of terms
2232
with exponent 1.
2233
2234
EXAMPLES::
2235
2236
sage: A2 = SteenrodAlgebra(2)
2237
sage: A5 = SteenrodAlgebra(5)
2238
sage: A2.Q_exp(0,0,1,1,0)
2239
Sq(0,0,1,1)
2240
sage: A5.Q_exp(0,0,1,1,0)
2241
Q_2 Q_3
2242
sage: A5.Q(2,3)
2243
Q_2 Q_3
2244
sage: A5.Q_exp(0,0,1,1,0) == A5.Q(2,3)
2245
True
2246
"""
2247
if not set(nums).issubset(set((0,1))):
2248
raise ValueError("The tuple %s should consist " % (nums,) + \
2249
"only of 0's and 1's")
2250
else:
2251
if self.basis_name() != 'milnor':
2252
return self(SteenrodAlgebra(p=self.prime()).Q_exp(*nums))
2253
while nums[-1] == 0:
2254
nums = nums[:-1]
2255
if self.prime() == 2:
2256
return self.P(*nums)
2257
else:
2258
mono = ()
2259
index = 0
2260
for e in nums:
2261
if e == 1:
2262
mono = mono + (index,)
2263
index += 1
2264
return self.Q(*mono)
2265
2266
def Q(self, *nums):
2267
r"""
2268
The element `Q_{n0} Q_{n1} ...` , given by specifying the
2269
subscripts.
2270
2271
INPUT:
2272
2273
- ``n0, n1, ...`` - non-negative integers
2274
2275
OUTPUT: The element `Q_{n0} Q_{n1} ...`
2276
2277
Note that at the prime 2, `Q_n` is the element
2278
`\text{Sq}(0,0,...,1)` , where the 1 is in the
2279
`(n+1)^{st}` position.
2280
2281
Compare this to the method :meth:`Q_exp`, which defines a
2282
similar element, but by specifying the tuple of exponents.
2283
2284
EXAMPLES::
2285
2286
sage: A2 = SteenrodAlgebra(2)
2287
sage: A2.Q(2,3)
2288
Sq(0,0,1,1)
2289
sage: A5 = SteenrodAlgebra(5)
2290
sage: A5.Q(1,4)
2291
Q_1 Q_4
2292
sage: A5.Q(1,4) == A5.Q_exp(0,1,0,0,1)
2293
True
2294
sage: H = SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]])
2295
sage: H.Q(2)
2296
Q_2
2297
sage: H.Q(4)
2298
Traceback (most recent call last):
2299
...
2300
ValueError: Element not in this algebra
2301
"""
2302
if len(nums) != len(set(nums)):
2303
return self(0)
2304
else:
2305
if self.basis_name() != 'milnor':
2306
return self(SteenrodAlgebra(p=self.prime()).Q(*nums))
2307
if self.prime() == 2:
2308
if len(nums) == 0:
2309
return self.one()
2310
else:
2311
list = (1+max(nums)) * [0]
2312
for i in nums:
2313
list[i] = 1
2314
return self.Sq(*tuple(list))
2315
else:
2316
answer = self.one()
2317
for i in nums:
2318
answer = answer * self.monomial(((i,), ()))
2319
t = answer.leading_support()
2320
if self._check_profile_on_basis(t):
2321
return answer
2322
raise ValueError("Element not in this algebra")
2323
2324
def an_element(self):
2325
"""
2326
An element of this Steenrod algebra. The element depends on
2327
the basis and whether there is a nontrivial profile function.
2328
(This is used by the automatic test suite, so having different
2329
elements in different bases may help in discovering bugs.)
2330
2331
EXAMPLES::
2332
2333
sage: SteenrodAlgebra().an_element()
2334
Sq(2,1)
2335
sage: SteenrodAlgebra(basis='adem').an_element()
2336
Sq^4 Sq^2 Sq^1
2337
sage: SteenrodAlgebra(p=5).an_element()
2338
4 Q_1 Q_3 P(2,1)
2339
sage: SteenrodAlgebra(basis='pst').an_element()
2340
P^3_1
2341
sage: SteenrodAlgebra(basis='pst', profile=[3,2,1]).an_element()
2342
P^0_1
2343
"""
2344
from sage.rings.all import GF
2345
basis = self.basis_name()
2346
p = self.prime()
2347
2348
if self._has_nontrivial_profile():
2349
if self.ngens() > 0:
2350
return self.gen(0)
2351
else:
2352
return self.one()
2353
2354
if basis == 'milnor' and p == 2:
2355
return self.monomial((2,1))
2356
if basis == 'milnor' and p > 2:
2357
return self.term(((1,3), (2,1)), GF(p)(p-1))
2358
if basis == 'serre-cartan' and p == 2:
2359
return self.monomial((4,2,1))
2360
if basis == 'serre-cartan' and p > 2:
2361
return self.term((1,p,0,1,0), GF(p)(p-1))
2362
if basis == 'woody' or basis == 'woodz':
2363
return self._from_dict({((3,0),): 1, ((1, 1), (1, 0)): 1}, coerce=True)
2364
if basis.find('wall') >= 0:
2365
return self._from_dict({((1,1), (1,0)): 1, ((2, 2), (0, 0)): 1}, coerce=True)
2366
if basis.find('arnona') >= 0:
2367
return self._from_dict({((3,3),): 1, ((1, 1), (2, 1)): 1}, coerce=True)
2368
if basis == 'arnonc':
2369
return self._from_dict({(8,): 1, (4, 4): 1}, coerce=True)
2370
if basis.find('pst') >= 0:
2371
if p == 2:
2372
return self.monomial(((3, 1),))
2373
return self.term(((1,), (((1,1), 2),)), GF(p)(p-1))
2374
if basis.find('comm') >= 0:
2375
if p == 2:
2376
return self.monomial(((1, 2),))
2377
return self.term(((), (((1,2), 1),)), GF(p)(p-1))
2378
2379
def pst(self,s,t):
2380
r"""
2381
The Margolis element `P^s_t`.
2382
2383
INPUT:
2384
2385
- ``s`` - non-negative integer
2386
2387
- ``t`` - positive integer
2388
2389
- ``p`` - positive prime number
2390
2391
OUTPUT: element of the Steenrod algebra
2392
2393
This returns the Margolis element `P^s_t` of the mod
2394
`p` Steenrod algebra: the element equal to
2395
`P(0,0,...,0,p^s)`, where the `p^s` is in position
2396
`t`.
2397
2398
EXAMPLES::
2399
2400
sage: A2 = SteenrodAlgebra(2)
2401
sage: A2.pst(3,5)
2402
Sq(0,0,0,0,8)
2403
sage: A2.pst(1,2) == Sq(4)*Sq(2) + Sq(2)*Sq(4)
2404
True
2405
sage: SteenrodAlgebra(5).pst(3,5)
2406
P(0,0,0,0,125)
2407
"""
2408
from sage.rings.all import Integer
2409
if self.basis_name() != 'milnor':
2410
return self(SteenrodAlgebra(p=self.prime()).pst(s,t))
2411
if not isinstance(s, (Integer, int)) and s >= 0:
2412
raise ValueError("%s is not a non-negative integer" % s)
2413
if not isinstance(t, (Integer, int)) and t > 0:
2414
raise ValueError("%s is not a positive integer" % t)
2415
nums = (0,)*(t-1) + (self.prime()**s,)
2416
return self.P(*nums)
2417
2418
def ngens(self):
2419
r"""
2420
Number of generators of self.
2421
2422
OUTPUT: number or Infinity
2423
2424
The Steenrod algebra is infinitely generated. A sub-Hopf
2425
algebra may be finitely or infinitely generated; in general,
2426
it is not clear what a minimal generating set is, nor the
2427
cardinality of that set. So: if the algebra is
2428
infinite-dimensional, this returns Infinity. If the algebra
2429
is finite-dimensional and is equal to one of the sub-Hopf
2430
algebras `A(n)`, then their minimal generating set is known,
2431
and this returns the cardinality of that set. Otherwise, any
2432
sub-Hopf algebra is (not necessarily minimally) generated by
2433
the `P^s_t`'s that it contains (along with the `Q_n`'s it
2434
contains, at odd primes), so this returns the number of
2435
`P^s_t`'s and `Q_n`'s in the algebra.
2436
2437
EXAMPLES::
2438
2439
sage: A = SteenrodAlgebra(3)
2440
sage: A.ngens()
2441
+Infinity
2442
sage: SteenrodAlgebra(profile=lambda n: n).ngens()
2443
+Infinity
2444
sage: SteenrodAlgebra(profile=[3,2,1]).ngens() # A(2)
2445
3
2446
sage: SteenrodAlgebra(profile=[3,2,1], basis='pst').ngens()
2447
3
2448
sage: SteenrodAlgebra(p=3, profile=[[3,2,1], [2,2,2,2]]).ngens() # A(3) at p=3
2449
4
2450
sage: SteenrodAlgebra(profile=[1,2,1,1]).ngens()
2451
5
2452
"""
2453
from sage.rings.infinity import Infinity
2454
if self._truncation_type == Infinity:
2455
return Infinity
2456
n = self.profile(1)
2457
p = self.prime()
2458
if p == 2 and self._profile == AA(n-1, p=p)._profile:
2459
return n
2460
if p > 2 and self._profile == AA(n, p=p)._profile:
2461
return n+1
2462
if p == 2:
2463
return sum(self._profile)
2464
return sum(self._profile[0]) + len([a for a in self._profile[1] if a == 2])
2465
2466
def gens(self):
2467
r"""
2468
Family of generators for this algebra.
2469
2470
OUTPUT: family of elements of this algebra
2471
2472
At the prime 2, the Steenrod algebra is generated by the
2473
elements `\text{Sq}^{2^i}` for `i \geq 0`. At odd primes, it
2474
is generated by the elements `Q_0` and `\mathcal{P}^{p^i}` for
2475
`i \geq 0`. So if this algebra is the entire Steenrod
2476
algebra, return an infinite family made up of these elements.
2477
2478
For sub-Hopf algebras of the Steenrod algebra, it is not
2479
always clear what a minimal generating set is. The sub-Hopf
2480
algebra `A(n)` is minimally generated by the elements
2481
`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At
2482
odd primes, `A(n)` is minimally generated by `Q_0` along with
2483
`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this
2484
algebra is `A(n)`, return the appropriate list of generators.
2485
2486
For other sub-Hopf algebras: return a non-minimal generating
2487
set: the family of `P^s_t`'s and `Q_n`'s contained in the
2488
algebra.
2489
2490
EXAMPLES::
2491
2492
sage: A3 = SteenrodAlgebra(3, 'adem')
2493
sage: A3.gens()
2494
Lazy family (<bound method SteenrodAlgebra_generic_with_category.gen of mod 3 Steenrod algebra, serre-cartan basis>(i))_{i in Non negative integers}
2495
sage: A3.gens()[0]
2496
beta
2497
sage: A3.gens()[1]
2498
P^1
2499
sage: A3.gens()[2]
2500
P^3
2501
sage: SteenrodAlgebra(profile=[3,2,1]).gens()
2502
Family (Sq(1), Sq(2), Sq(4))
2503
2504
In the following case, return a non-minimal generating set.
2505
(It is not minimal because `\text{Sq}(0,0,1)` is the
2506
commutator of `\text{Sq}(1)` and `\text{Sq}(0,2)`.) ::
2507
2508
sage: SteenrodAlgebra(profile=[1,2,1]).gens()
2509
Family (Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1))
2510
sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).gens()
2511
Family (Q_0, P(1), P(5))
2512
sage: SteenrodAlgebra(profile=lambda n: n).gens()
2513
Lazy 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}
2514
2515
You may also use ``algebra_generators`` instead of ``gens``::
2516
2517
sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).algebra_generators()
2518
Family (Q_0, P(1), P(5))
2519
"""
2520
from sage.sets.family import Family
2521
from sage.sets.non_negative_integers import NonNegativeIntegers
2522
from sage.rings.infinity import Infinity
2523
n = self.ngens()
2524
if n < Infinity:
2525
return Family([self.gen(i) for i in range(n)])
2526
return Family(NonNegativeIntegers(), self.gen)
2527
2528
algebra_generators = gens
2529
2530
def gen(self, i=0):
2531
r"""
2532
The ith generator of this algebra.
2533
2534
INPUT:
2535
2536
- ``i`` - non-negative integer
2537
2538
OUTPUT: the ith generator of this algebra
2539
2540
For the full Steenrod algebra, the `i^{th}` generator is
2541
`\text{Sq}(2^i)` at the prime 2; when `p` is odd, the 0th generator
2542
is `\beta = Q(0)`, and for `i>0`, the `i^{th}` generator is
2543
`P(p^{i-1})`.
2544
2545
For sub-Hopf algebras of the Steenrod algebra, it is not
2546
always clear what a minimal generating set is. The sub-Hopf
2547
algebra `A(n)` is minimally generated by the elements
2548
`\text{Sq}^{2^i}` for `0 \leq i \leq n` at the prime 2. At
2549
odd primes, `A(n)` is minimally generated by `Q_0` along with
2550
`\mathcal{P}^{p^i}` for `0 \leq i \leq n-1`. So if this
2551
algebra is `A(n)`, return the appropriate generator.
2552
2553
For other sub-Hopf algebras: they are generated (but not
2554
necessarily minimally) by the `P^s_t`'s (and `Q_n`'s, if `p`
2555
is odd) that they contain. So order the `P^s_t`'s (and
2556
`Q_n`'s) in the algebra by degree and return the `i`-th one.
2557
2558
EXAMPLES::
2559
2560
sage: A = SteenrodAlgebra(2)
2561
sage: A.gen(4)
2562
Sq(16)
2563
sage: A.gen(200)
2564
Sq(1606938044258990275541962092341162602522202993782792835301376)
2565
sage: SteenrodAlgebra(2, basis='adem').gen(2)
2566
Sq^4
2567
sage: SteenrodAlgebra(2, basis='pst').gen(2)
2568
P^2_1
2569
sage: B = SteenrodAlgebra(5)
2570
sage: B.gen(0)
2571
Q_0
2572
sage: B.gen(2)
2573
P(5)
2574
2575
sage: SteenrodAlgebra(profile=[2,1]).gen(1)
2576
Sq(2)
2577
sage: SteenrodAlgebra(profile=[1,2,1]).gen(1)
2578
Sq(0,1)
2579
sage: SteenrodAlgebra(profile=[1,2,1]).gen(5)
2580
Traceback (most recent call last):
2581
...
2582
ValueError: This algebra only has 4 generators, so call gen(i) with 0 <= i < 4
2583
2584
sage: D = SteenrodAlgebra(profile=lambda n: n)
2585
sage: [D.gen(n) for n in range(5)]
2586
[Sq(1), Sq(0,1), Sq(0,2), Sq(0,0,1), Sq(0,0,2)]
2587
sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 2))
2588
sage: [D3.gen(n) for n in range(9)]
2589
[Q_0, P(1), Q_1, P(0,1), Q_2, P(0,3), P(0,0,1), Q_3, P(0,0,3)]
2590
sage: D3 = SteenrodAlgebra(p=3, profile=(lambda n: n, lambda n: 1 if n<1 else 2))
2591
sage: [D3.gen(n) for n in range(9)]
2592
[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)]
2593
sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]], basis='pst').gen(2)
2594
P^1_1
2595
"""
2596
from sage.rings.infinity import Infinity
2597
from sage.rings.all import Integer
2598
p = self.prime()
2599
if not isinstance(i, (Integer, int)) and i >= 0:
2600
raise ValueError("%s is not a non-negative integer" % i)
2601
num = self.ngens()
2602
if num < Infinity:
2603
if i >= num:
2604
raise ValueError("This algebra only has %s generators, so call gen(i) with 0 <= i < %s" % (num, num))
2605
# check to see if equal to A(n) for some n.
2606
n = self.profile(1)
2607
if p == 2 and self._profile == AA(n-1, p=p)._profile:
2608
return self.pst(i,1)
2609
if p > 2 and self._profile == AA(n, p=p)._profile:
2610
if i == 0:
2611
return self.Q(0)
2612
return self.pst(i-1, 1)
2613
# if not A(n), return list of P^s_t's in algebra, along with Q's if p is odd
2614
idx = -1
2615
if p == 2:
2616
last_t = len(self._profile)
2617
else:
2618
last_t = max(len(self._profile[0]), len(self._profile[1]))
2619
last_s = self.profile(last_t)
2620
for j in range(1, last_s + last_t + 1):
2621
if p > 2 and self.profile(j-1, 1) == 2:
2622
guess = self.Q(j-1)
2623
idx += 1
2624
if idx == i:
2625
elt = guess
2626
break
2627
for t in range(1, min(j, last_t) + 1):
2628
s = j - t
2629
if self.profile(t) > s:
2630
guess = self.pst(s,t)
2631
idx += 1
2632
if idx == i:
2633
elt = guess
2634
break
2635
return elt
2636
2637
# entire Steenrod algebra:
2638
if self.profile(1) == Infinity:
2639
if p == 2:
2640
return self.Sq(p**i)
2641
elif self.profile(0,1) == 2:
2642
if i == 0:
2643
return self.Q(0)
2644
else:
2645
return self.P(p**(i-1))
2646
2647
# infinite-dimensional sub-Hopf algebra
2648
idx = -1
2649
tot = 1
2650
found = False
2651
A = SteenrodAlgebra(p=p)
2652
while not found:
2653
if p > 2:
2654
test = A.Q(tot-1)
2655
if test in self:
2656
idx += 1
2657
if idx == i:
2658
found = True
2659
break
2660
for t in range(1, tot+1):
2661
s = tot - t
2662
test = A.pst(s,t)
2663
if test in self:
2664
idx += 1
2665
if idx == i:
2666
found = True
2667
break
2668
tot += 1
2669
return test
2670
2671
def is_commutative(self):
2672
r"""
2673
True if ``self`` is graded commutative, as determined by the
2674
profile function. In particular, a sub-Hopf algebra of the
2675
mod 2 Steenrod algebra is commutative if and only if there is
2676
an integer `n>0` so that its profile function `e` satisfies
2677
2678
- `e(i) = 0` for `i < n`,
2679
- `e(i) \leq n` for `i \geq n`.
2680
2681
When `p` is odd, there must be an integer `n \geq 0` so that
2682
the profile functions `e` and `k` satisfy
2683
2684
- `e(i) = 0` for `i < n`,
2685
- `e(i) \leq n` for `i \geq n`.
2686
- `k(i) = 1` for `i < n`.
2687
2688
EXAMPLES::
2689
2690
sage: A = SteenrodAlgebra(p=3)
2691
sage: A.is_commutative()
2692
False
2693
sage: SteenrodAlgebra(profile=[2,1]).is_commutative()
2694
False
2695
sage: SteenrodAlgebra(profile=[0,2,2,1]).is_commutative()
2696
True
2697
2698
Note that if the profile function is specified by a function,
2699
then by default it has infinite truncation type: the profile
2700
function is assumed to be infinite after the 100th term. ::
2701
2702
sage: SteenrodAlgebra(profile=lambda n: 1).is_commutative()
2703
False
2704
sage: SteenrodAlgebra(profile=lambda n: 1, truncation_type=0).is_commutative()
2705
True
2706
2707
sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [])).is_commutative()
2708
True
2709
sage: SteenrodAlgebra(p=5, profile=([0,2,2,1], [1,1,2])).is_commutative()
2710
True
2711
sage: SteenrodAlgebra(p=5, profile=([0,2,1], [1,2,2,2])).is_commutative()
2712
False
2713
"""
2714
if not self._has_nontrivial_profile() or self._truncation_type > 0:
2715
return False
2716
p = self.prime()
2717
if p == 2:
2718
n = max(self._profile)
2719
return all([self.profile(i) == 0 for i in range(1, n)])
2720
n = max(self._profile[0])
2721
return (all([self.profile(i,0) == 0 for i in range(1, n)])
2722
and all([self.profile(i,1) == 1 for i in range(n)]))
2723
2724
def is_finite(self):
2725
r"""
2726
True if this algebra is finite-dimensional.
2727
2728
Therefore true if the profile function is finite, and in
2729
particular the ``truncation_type`` must be finite.
2730
2731
EXAMPLES::
2732
2733
sage: A = SteenrodAlgebra(p=3)
2734
sage: A.is_finite()
2735
False
2736
sage: SteenrodAlgebra(profile=[3,2,1]).is_finite()
2737
True
2738
sage: SteenrodAlgebra(profile=lambda n: n).is_finite()
2739
False
2740
"""
2741
return self._has_nontrivial_profile() and self._truncation_type == 0
2742
2743
def dimension(self):
2744
r"""
2745
The dimension of this algebra as a vector space over `\GF{p}`.
2746
2747
If the algebra is infinite, return ``+Infinity``. Otherwise,
2748
the profile function must be finite. In this case, at the
2749
prime 2, its dimension is `2^s`, where `s` is the sum of the
2750
entries in the profile function. At odd primes, the dimension
2751
is `p^s * 2^t` where `s` is the sum of the `e` component of
2752
the profile function and `t` is the number of 2's in the `k`
2753
component of the profile function.
2754
2755
EXAMPLES::
2756
2757
sage: SteenrodAlgebra(p=7).dimension()
2758
+Infinity
2759
sage: SteenrodAlgebra(profile=[3,2,1]).dimension()
2760
64
2761
sage: SteenrodAlgebra(p=3, profile=([1,1], [])).dimension()
2762
9
2763
sage: SteenrodAlgebra(p=5, profile=([1], [2,2])).dimension()
2764
20
2765
"""
2766
from sage.rings.infinity import Infinity
2767
if not self.is_finite():
2768
return Infinity
2769
p = self.prime()
2770
if p == 2:
2771
return 2**sum(self._profile)
2772
return p**sum(self._profile[0]) * 2**len([a for a in self._profile[1] if a == 2])
2773
2774
def order(self):
2775
r"""
2776
The order of this algebra.
2777
2778
This is computed by computing its vector space dimension `d`
2779
and then returning `p^d`.
2780
2781
EXAMPLES::
2782
2783
sage: SteenrodAlgebra(p=7).order()
2784
+Infinity
2785
sage: SteenrodAlgebra(profile=[2,1]).dimension()
2786
8
2787
sage: SteenrodAlgebra(profile=[2,1]).order()
2788
256
2789
sage: SteenrodAlgebra(p=3, profile=([1], [])).dimension()
2790
3
2791
sage: SteenrodAlgebra(p=3, profile=([1], [])).order()
2792
27
2793
sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).dimension()
2794
4
2795
sage: SteenrodAlgebra(p=5, profile=([], [2, 2])).order() == 5**4
2796
True
2797
"""
2798
from sage.rings.infinity import Infinity
2799
if not self.is_finite():
2800
return Infinity
2801
return self.prime() ** self.dimension()
2802
2803
def is_division_algebra(self):
2804
r"""
2805
The only way this algebra can be a division algebra is if it
2806
is the ground field `\GF{p}`.
2807
2808
EXAMPLES::
2809
2810
sage: SteenrodAlgebra(11).is_division_algebra()
2811
False
2812
sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_division_algebra()
2813
True
2814
"""
2815
return self.is_field()
2816
2817
def is_field(self, proof = True):
2818
r"""
2819
The only way this algebra can be a field is if it is the
2820
ground field `\GF{p}`.
2821
2822
EXAMPLES::
2823
2824
sage: SteenrodAlgebra(11).is_field()
2825
False
2826
sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_field()
2827
True
2828
"""
2829
return self.dimension() == 1
2830
2831
def is_integral_domain(self, proof = True):
2832
r"""
2833
The only way this algebra can be an integral domain is if it
2834
is the ground field `\GF{p}`.
2835
2836
EXAMPLES::
2837
2838
sage: SteenrodAlgebra(11).is_integral_domain()
2839
False
2840
sage: SteenrodAlgebra(profile=lambda n: 0, truncation_type=0).is_integral_domain()
2841
True
2842
"""
2843
return self.is_field()
2844
2845
def is_noetherian(self):
2846
"""
2847
This algebra is noetherian if and only if it is finite.
2848
2849
EXAMPLES::
2850
2851
sage: SteenrodAlgebra(3).is_noetherian()
2852
False
2853
sage: SteenrodAlgebra(profile=[1,2,1]).is_noetherian()
2854
True
2855
sage: SteenrodAlgebra(profile=lambda n: n+2).is_noetherian()
2856
False
2857
"""
2858
return self.is_finite()
2859
2860
######################################################
2861
# element class
2862
######################################################
2863
2864
class Element(CombinatorialFreeModuleElement):
2865
r"""
2866
Class for elements of the Steenrod algebra. Since the
2867
Steenrod algebra class is based on
2868
:class:`CombinatorialFreeModule
2869
<sage.combinat.free_module.CombinatorialFreeModule>`, this is
2870
based on :class:`CombinatorialFreeModuleElement
2871
<sage.combinat.free_module.CombinatorialFreeModuleElement>`.
2872
It has new methods reflecting its role, like :meth:`degree`
2873
for computing the degree of an element.
2874
2875
EXAMPLES:
2876
2877
Since this class inherits from
2878
:class:`CombinatorialFreeModuleElement
2879
<sage.combinat.free_module.CombinatorialFreeModuleElement>`,
2880
elements can be used as iterators, and there are other useful
2881
methods::
2882
2883
sage: c = Sq(5).antipode(); c
2884
Sq(2,1) + Sq(5)
2885
sage: for mono, coeff in c: print coeff, mono
2886
1 (5,)
2887
1 (2, 1)
2888
sage: c.monomial_coefficients()
2889
{(5,): 1, (2, 1): 1}
2890
sage: c.monomials()
2891
[Sq(2,1), Sq(5)]
2892
sage: c.support()
2893
[(2, 1), (5,)]
2894
2895
See the documentation for this module (type
2896
``sage.algebras.steenrod.steenrod_algebra?``) for more
2897
information about elements of the Steenrod algebra.
2898
"""
2899
def prime(self):
2900
"""
2901
The prime associated to self.
2902
2903
EXAMPLES::
2904
2905
sage: a = SteenrodAlgebra().Sq(3,2,1)
2906
sage: a.prime()
2907
2
2908
sage: a.change_basis('adem').prime()
2909
2
2910
sage: b = SteenrodAlgebra(p=7).basis(36)[0]
2911
sage: b.prime()
2912
7
2913
sage: SteenrodAlgebra(p=3, basis='adem').one().prime()
2914
3
2915
"""
2916
return self.base_ring().characteristic()
2917
2918
def basis_name(self):
2919
"""
2920
The basis name associated to self.
2921
2922
EXAMPLES::
2923
2924
sage: a = SteenrodAlgebra().Sq(3,2,1)
2925
sage: a.basis_name()
2926
'milnor'
2927
sage: a.change_basis('adem').basis_name()
2928
'serre-cartan'
2929
sage: a.change_basis('wood____y').basis_name()
2930
'woody'
2931
sage: b = SteenrodAlgebra(p=7).basis(36)[0]
2932
sage: b.basis_name()
2933
'milnor'
2934
sage: a.change_basis('adem').basis_name()
2935
'serre-cartan'
2936
"""
2937
return self.parent().prefix()
2938
2939
def is_homogeneous(self):
2940
"""
2941
Return True iff this element is homogeneous.
2942
2943
EXAMPLES::
2944
2945
sage: (Sq(0,0,1) + Sq(7)).is_homogeneous()
2946
True
2947
sage: (Sq(0,0,1) + Sq(2)).is_homogeneous()
2948
False
2949
"""
2950
monos = self.support()
2951
if len(monos) <= 1:
2952
return True
2953
degree = None
2954
deg = self.parent().degree_on_basis
2955
for mono in monos:
2956
if degree is None:
2957
degree = deg(mono)
2958
elif deg(mono) != degree:
2959
return False
2960
return True
2961
2962
def degree(self):
2963
r"""
2964
The degree of self.
2965
2966
The degree of `\text{Sq}(i_1,i_2,i_3,...)` is
2967
2968
.. math::
2969
2970
i_1 + 3i_2 + 7i_3 + ... + (2^k - 1) i_k + ....
2971
2972
At an odd prime `p`, the degree of `Q_k` is `2p^k - 1` and the
2973
degree of `\mathcal{P}(i_1, i_2, ...)` is
2974
2975
.. math::
2976
2977
\sum_{k \geq 0} 2(p^k - 1) i_k.
2978
2979
ALGORITHM: If :meth:`is_homogeneous` returns True, call
2980
:meth:`SteenrodAlgebra_generic.degree_on_basis` on the leading
2981
summand.
2982
2983
EXAMPLES::
2984
2985
sage: Sq(0,0,1).degree()
2986
7
2987
sage: (Sq(0,0,1) + Sq(7)).degree()
2988
7
2989
sage: (Sq(0,0,1) + Sq(2)).degree()
2990
Traceback (most recent call last):
2991
...
2992
ValueError: Element is not homogeneous.
2993
2994
sage: A11 = SteenrodAlgebra(p=11)
2995
sage: A11.P(1).degree()
2996
20
2997
sage: A11.P(1,1).degree()
2998
260
2999
sage: A11.Q(2).degree()
3000
241
3001
3002
TESTS::
3003
3004
sage: all([x.degree() == 10 for x in SteenrodAlgebra(basis='woody').basis(10)])
3005
True
3006
sage: all([x.degree() == 11 for x in SteenrodAlgebra(basis='woodz').basis(11)])
3007
True
3008
sage: all([x.degree() == x.milnor().degree() for x in SteenrodAlgebra(basis='wall').basis(11)])
3009
True
3010
sage: a = SteenrodAlgebra(basis='pst').basis(10)[0]
3011
sage: a.degree() == a.change_basis('arnonc').degree()
3012
True
3013
sage: b = SteenrodAlgebra(basis='comm').basis(12)[1]
3014
sage: b.degree() == b.change_basis('adem').change_basis('arnona').degree()
3015
True
3016
sage: all([x.degree() == 9 for x in SteenrodAlgebra(basis='comm').basis(9)])
3017
True
3018
sage: all([x.degree() == 8 for x in SteenrodAlgebra(basis='adem').basis(8)])
3019
True
3020
sage: all([x.degree() == 7 for x in SteenrodAlgebra(basis='milnor').basis(7)])
3021
True
3022
sage: all([x.degree() == 24 for x in SteenrodAlgebra(p=3).basis(24)])
3023
True
3024
sage: all([x.degree() == 40 for x in SteenrodAlgebra(p=5, basis='serre-cartan').basis(40)])
3025
True
3026
"""
3027
if len(self.support()) == 0:
3028
raise ValueError("The zero element does not have a well-defined degree.")
3029
try:
3030
assert self.is_homogeneous()
3031
return self.parent().degree_on_basis(self.leading_support())
3032
except AssertionError:
3033
raise ValueError("Element is not homogeneous.")
3034
3035
def milnor(self):
3036
"""
3037
Return this element in the Milnor basis; that is, as an
3038
element of the appropriate Steenrod algebra.
3039
3040
This just calls the method
3041
:meth:`SteenrodAlgebra_generic.milnor`.
3042
3043
EXAMPLES::
3044
3045
sage: Adem = SteenrodAlgebra(basis='adem')
3046
sage: a = Adem.basis(4)[1]; a
3047
Sq^3 Sq^1
3048
sage: a.milnor()
3049
Sq(1,1)
3050
"""
3051
A = self.parent()
3052
return A.milnor(self)
3053
3054
def change_basis(self, basis='milnor'):
3055
r"""
3056
Representation of element with respect to basis.
3057
3058
INPUT:
3059
3060
- ``basis`` - string, basis in which to work.
3061
3062
OUTPUT: representation of self in given basis
3063
3064
The choices for ``basis`` are:
3065
3066
- 'milnor' for the Milnor basis.
3067
- 'serre-cartan', 'serre_cartan', 'sc', 'adem', 'admissible'
3068
for the Serre-Cartan basis.
3069
- 'wood_y' for Wood's Y basis.
3070
- 'wood_z' for Wood's Z basis.
3071
- 'wall' for Wall's basis.
3072
- 'wall_long' for Wall's basis, alternate representation
3073
- 'arnon_a' for Arnon's A basis.
3074
- 'arnon_a_long' for Arnon's A basis, alternate representation.
3075
- 'arnon_c' for Arnon's C basis.
3076
- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz' for
3077
various `P^s_t`-bases.
3078
- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz'
3079
for various commutator bases.
3080
- 'comm_long', 'comm_rlex_long', etc., for commutator bases,
3081
alternate representations.
3082
3083
See documentation for this module (by browsing the
3084
reference manual or by typing
3085
``sage.algebras.steenrod.steenrod_algebra?``) for
3086
descriptions of the different bases.
3087
3088
EXAMPLES::
3089
3090
sage: c = Sq(2) * Sq(1)
3091
sage: c.change_basis('milnor')
3092
Sq(0,1) + Sq(3)
3093
sage: c.change_basis('serre-cartan')
3094
Sq^2 Sq^1
3095
sage: d = Sq(0,0,1)
3096
sage: d.change_basis('arnonc')
3097
Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^7
3098
"""
3099
A = self.parent()
3100
return A._change_basis(self, basis)
3101
3102
def _basis_dictionary(self,basis):
3103
r"""
3104
Convert self to ``basis``, returning a dictionary of terms of
3105
the form (mono: coeff), where mono is a monomial in the given
3106
basis.
3107
3108
INPUT:
3109
3110
- ``basis`` - string, basis in which to work
3111
3112
OUTPUT: dictionary
3113
3114
This just calls :meth:`change_basis` to get an element of the
3115
Steenrod algebra with the new basis, and then calls
3116
:meth:`monomial_coefficients` on this element to produce its
3117
dictionary representation.
3118
3119
EXAMPLES::
3120
3121
sage: c = Sq(2) * Sq(1)
3122
sage: c._basis_dictionary('milnor')
3123
{(0, 1): 1, (3,): 1}
3124
sage: c
3125
Sq(0,1) + Sq(3)
3126
sage: c._basis_dictionary('serre-cartan')
3127
{(2, 1): 1}
3128
sage: c.change_basis('serre-cartan')
3129
Sq^2 Sq^1
3130
sage: d = Sq(0,0,1)
3131
sage: d._basis_dictionary('arnonc')
3132
{(7,): 1, (2, 5): 1, (4, 3): 1, (4, 2, 1): 1}
3133
sage: d.change_basis('arnonc')
3134
Sq^2 Sq^5 + Sq^4 Sq^2 Sq^1 + Sq^4 Sq^3 + Sq^7
3135
3136
At odd primes::
3137
3138
sage: e = 2 * SteenrodAlgebra(3).P(1,2)
3139
sage: e._basis_dictionary('milnor')
3140
{((), (1, 2)): 2}
3141
sage: e
3142
2 P(1,2)
3143
sage: e._basis_dictionary('serre-cartan')
3144
{(0, 7, 0, 2, 0): 2, (0, 8, 0, 1, 0): 2}
3145
sage: e.change_basis('adem')
3146
2 P^7 P^2 + 2 P^8 P^1
3147
"""
3148
a = self.change_basis(basis)
3149
return a.monomial_coefficients()
3150
3151
def basis(self, basis):
3152
r"""
3153
Representation of element with respect to basis.
3154
3155
INPUT:
3156
3157
- ``basis`` - string, basis in which to work.
3158
3159
OUTPUT: Representation of self in given basis
3160
3161
.. warning::
3162
3163
Deprecated (December 2010). Use :meth:`change_basis` instead.
3164
3165
EXAMPLES::
3166
3167
sage: c = Sq(2) * Sq(1)
3168
sage: c.basis('milnor')
3169
doctest:...: DeprecationWarning: (Since Sage 4.6.2) The .basis() method is deprecated. Use .change_basis() instead.
3170
Sq(0,1) + Sq(3)
3171
"""
3172
from sage.misc.misc import deprecation
3173
deprecation('The .basis() method is deprecated. Use .change_basis() instead.',
3174
version='Sage 4.6.2')
3175
return self.change_basis(basis)
3176
3177
def coproduct(self, algorithm='milnor'):
3178
"""
3179
The coproduct of this element.
3180
3181
INPUT:
3182
3183
- ``algorithm`` -- None or a string, either 'milnor' or
3184
'serre-cartan' (or anything which will be converted to
3185
one of these by the function :func:`get_basis_name
3186
<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`).
3187
If None, default to 'serre-cartan' if current basis is
3188
'serre-cartan'; otherwise use 'milnor'.
3189
3190
See :meth:`SteenrodAlgebra_generic.coproduct_on_basis` for
3191
more information on computing the coproduct.
3192
3193
EXAMPLES::
3194
3195
sage: a = Sq(2)
3196
sage: a.coproduct()
3197
1 # Sq(2) + Sq(1) # Sq(1) + Sq(2) # 1
3198
sage: b = Sq(4)
3199
sage: (a*b).coproduct() == (a.coproduct()) * (b.coproduct())
3200
True
3201
3202
sage: c = a.change_basis('adem'); c.coproduct(algorithm='milnor')
3203
1 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 1
3204
sage: c = a.change_basis('adem'); c.coproduct(algorithm='adem')
3205
1 # Sq^2 + Sq^1 # Sq^1 + Sq^2 # 1
3206
3207
sage: d = a.change_basis('comm_long'); d.coproduct()
3208
1 # s_2 + s_1 # s_1 + s_2 # 1
3209
3210
sage: A7 = SteenrodAlgebra(p=7)
3211
sage: a = A7.Q(1) * A7.P(1); a
3212
Q_1 P(1)
3213
sage: a.coproduct()
3214
1 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 1
3215
sage: a.coproduct(algorithm='adem')
3216
1 # Q_1 P(1) + P(1) # Q_1 + Q_1 # P(1) + Q_1 P(1) # 1
3217
"""
3218
A = self.parent()
3219
return A.coproduct(self, algorithm=algorithm)
3220
3221
def excess(self):
3222
r"""
3223
Excess of element.
3224
3225
OUTPUT: ``excess`` - non-negative integer
3226
3227
The excess of a Milnor basis element `\text{Sq}(a,b,c,...)` is
3228
`a + b + c + ...`. When `p` is odd, the excess of `Q_{0}^{e_0}
3229
Q_{1}^{e_1} ... P(r_1, r_2, ...)` is `\sum e_i + 2 \sum r_i`.
3230
The excess of a linear combination of Milnor basis elements is
3231
the minimum of the excesses of those basis elements.
3232
3233
See [Kra] for the proofs of these assertions.
3234
3235
REFERENCES:
3236
3237
- [Kra] D. Kraines, "On excess in the Milnor basis," Bull. London
3238
Math. Soc. 3 (1971), 363-365.
3239
3240
EXAMPLES::
3241
3242
sage: a = Sq(1,2,3)
3243
sage: a.excess()
3244
6
3245
sage: (Sq(0,0,1) + Sq(4,1) + Sq(7)).excess()
3246
1
3247
sage: [m.excess() for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]
3248
[1, 5, 7]
3249
sage: [m for m in (Sq(0,0,1) + Sq(4,1) + Sq(7)).monomials()]
3250
[Sq(0,0,1), Sq(4,1), Sq(7)]
3251
sage: B = SteenrodAlgebra(7)
3252
sage: a = B.Q(1,2,5)
3253
sage: b = B.P(2,2,3)
3254
sage: a.excess()
3255
3
3256
sage: b.excess()
3257
14
3258
sage: (a + b).excess()
3259
3
3260
sage: (a * b).excess()
3261
17
3262
"""
3263
def excess_odd(mono):
3264
"""
3265
Excess of mono, where mono has the form ((s0, s1, ...), (r1, r2,
3266
...)).
3267
3268
Returns the length of the first component, since that is the number
3269
of factors, plus twice the sum of the terms in the second
3270
component.
3271
"""
3272
if len(mono) == 0:
3273
return 0
3274
else:
3275
return len(mono[0]) + 2 * sum(mono[1])
3276
3277
p = self.prime()
3278
a = self.milnor()
3279
if p == 2:
3280
excesses = [sum(mono) for mono in a.support()]
3281
else:
3282
excesses = [excess_odd(mono) for mono in a.support()]
3283
return min(excesses)
3284
3285
def is_unit(self):
3286
"""
3287
True if element has a nonzero scalar multiple of P(0) as a summand,
3288
False otherwise.
3289
3290
EXAMPLES::
3291
3292
sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)
3293
sage: z.is_unit()
3294
False
3295
sage: u = Sq(0) + Sq(3,1)
3296
sage: u == 1 + Sq(3,1)
3297
True
3298
sage: u.is_unit()
3299
True
3300
sage: A5 = SteenrodAlgebra(5)
3301
sage: v = A5.P(0)
3302
sage: (v + v + v).is_unit()
3303
True
3304
"""
3305
return self.parent().one() in self.monomials()
3306
3307
def is_nilpotent(self):
3308
"""
3309
True if element is not a unit, False otherwise.
3310
3311
EXAMPLES::
3312
3313
sage: z = Sq(4,2) + Sq(7,1) + Sq(3,0,1)
3314
sage: z.is_nilpotent()
3315
True
3316
sage: u = 1 + Sq(3,1)
3317
sage: u == 1 + Sq(3,1)
3318
True
3319
sage: u.is_nilpotent()
3320
False
3321
"""
3322
return not self.is_unit()
3323
3324
def may_weight(self):
3325
r"""
3326
May's 'weight' of element.
3327
3328
OUTPUT: ``weight`` - non-negative integer
3329
3330
If we let `F_* (A)` be the May filtration of the Steenrod
3331
algebra, the weight of an element `x` is the integer `k` so
3332
that `x` is in `F_k(A)` and not in `F_{k+1}(A)`. According to
3333
Theorem 2.6 in May's thesis [May], the weight of a Milnor
3334
basis element is computed as follows: first, to compute the
3335
weight of `P(r_1,r_2, ...)`, write each `r_i` in base `p` as
3336
`r_i = \sum_j p^j r_{ij}`. Then each nonzero binary digit
3337
`r_{ij}` contributes `i` to the weight: the weight is
3338
`\sum_{i,j} i r_{ij}`. When `p` is odd, the weight of `Q_i` is
3339
`i+1`, so the weight of a product `Q_{i_1} Q_{i_2} ...` equals
3340
`(i_1+1) + (i_2+1) + ...`. Then the weight of `Q_{i_1} Q_{i_2}
3341
...P(r_1,r_2, ...)` is the sum of `(i_1+1) + (i_2+1) + ...`
3342
and `\sum_{i,j} i r_{ij}`.
3343
3344
The weight of a sum of Milnor basis elements is the minimum of
3345
the weights of the summands.
3346
3347
When `p=2`, we compute the weight on Milnor basis elements by
3348
adding up the terms in their 'height' - see
3349
:meth:`wall_height` for documentation. (When `p` is odd, the
3350
height of an element is not defined.)
3351
3352
REFERENCES:
3353
3354
- [May]: J. P. May, "The cohomology of restricted Lie algebras and of
3355
Hopf algebras; application to the Steenrod algebra." Thesis,
3356
Princeton Univ., 1964.
3357
3358
EXAMPLES::
3359
3360
sage: Sq(0).may_weight()
3361
0
3362
sage: a = Sq(4)
3363
sage: a.may_weight()
3364
1
3365
sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)
3366
sage: b.may_weight()
3367
2
3368
sage: Sq(2,1,5).wall_height()
3369
[2, 3, 2, 1, 1]
3370
sage: Sq(2,1,5).may_weight()
3371
9
3372
sage: A5 = SteenrodAlgebra(5)
3373
sage: a = A5.Q(1,2,4)
3374
sage: b = A5.P(1,2,1)
3375
sage: a.may_weight()
3376
10
3377
sage: b.may_weight()
3378
8
3379
sage: (a * b).may_weight()
3380
18
3381
sage: A5.P(0,0,1).may_weight()
3382
3
3383
"""
3384
from sage.rings.infinity import Infinity
3385
from sage.rings.all import Integer
3386
p = self.prime()
3387
if self == 0:
3388
return Infinity
3389
elif self.is_unit():
3390
return 0
3391
elif p == 2:
3392
wt = Infinity
3393
for mono in self.milnor().monomials():
3394
wt = min(wt, sum(mono.wall_height()))
3395
return wt
3396
else: # p odd
3397
wt = Infinity
3398
for (mono1, mono2) in self.milnor().support():
3399
P_wt = 0
3400
index = 1
3401
for n in mono2:
3402
P_wt += index * sum(Integer(n).digits(p))
3403
index += 1
3404
wt = min(wt, sum(mono1) + len(mono1) + P_wt)
3405
return wt
3406
3407
def is_decomposable(self):
3408
r"""
3409
Return True if element is decomposable, False otherwise.
3410
That is, if element is in the square of the augmentation ideal,
3411
return True; otherwise, return False.
3412
3413
OUTPUT: boolean
3414
3415
EXAMPLES::
3416
3417
sage: a = Sq(6)
3418
sage: a.is_decomposable()
3419
True
3420
sage: for i in range(9):
3421
... if not Sq(i).is_decomposable():
3422
... print Sq(i)
3423
1
3424
Sq(1)
3425
Sq(2)
3426
Sq(4)
3427
Sq(8)
3428
sage: A3 = SteenrodAlgebra(p=3, basis='adem')
3429
sage: [A3.P(n) for n in range(30) if not A3.P(n).is_decomposable()]
3430
[1, P^1, P^3, P^9, P^27]
3431
3432
TESTS:
3433
3434
These all test changing bases and printing in various bases::
3435
3436
sage: A = SteenrodAlgebra(basis='milnor')
3437
sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]
3438
[1, Sq(1), Sq(2), Sq(4), Sq(8)]
3439
sage: A = SteenrodAlgebra(basis='wall_long')
3440
sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]
3441
[1, Sq^1, Sq^2, Sq^4, Sq^8]
3442
sage: A = SteenrodAlgebra(basis='arnona_long')
3443
sage: [A.Sq(n) for n in range(9) if not A.Sq(n).is_decomposable()]
3444
[1, Sq^1, Sq^2, Sq^4, Sq^8]
3445
sage: A = SteenrodAlgebra(basis='woodz')
3446
sage: [A.Sq(n) for n in range(20) if not A.Sq(n).is_decomposable()] # long time
3447
[1, Sq^1, Sq^2, Sq^4, Sq^8, Sq^16]
3448
sage: A = SteenrodAlgebra(basis='comm_long')
3449
sage: [A.Sq(n) for n in range(25) if not A.Sq(n).is_decomposable()] # long time
3450
[1, s_1, s_2, s_4, s_8, s_16]
3451
"""
3452
return self.may_weight() > 1
3453
3454
def wall_height(self):
3455
r"""
3456
Wall's 'height' of element.
3457
3458
OUTPUT: list of non-negative integers
3459
3460
The height of an element of the mod 2 Steenrod algebra is a
3461
list of non-negative integers, defined as follows: if the
3462
element is a monomial in the generators `\text{Sq}(2^i)`, then
3463
the `i^{th}` entry in the list is the number of times
3464
`\text{Sq}(2^i)` appears. For an arbitrary element, write it
3465
as a sum of such monomials; then its height is the maximum,
3466
ordered right-lexicographically, of the heights of those
3467
monomials.
3468
3469
When `p` is odd, the height of an element is not defined.
3470
3471
According to Theorem 3 in [Wall], the height of the Milnor
3472
basis element `\text{Sq}(r_1, r_2, ...)` is obtained as
3473
follows: write each `r_i` in binary as `r_i = \sum_j 2^j
3474
r_{ij}`. Then each nonzero binary digit `r_{ij}` contributes 1
3475
to the `k^{th}` entry in the height, for `j \leq k \leq
3476
i+j-1`.
3477
3478
REFERENCES:
3479
3480
- [Wall]: C. T. C. Wall, "Generators and relations for the Steenrod
3481
algebra," Ann. of Math. (2) **72** (1960), 429-444.
3482
3483
EXAMPLES::
3484
3485
sage: Sq(0).wall_height()
3486
[]
3487
sage: a = Sq(4)
3488
sage: a.wall_height()
3489
[0, 0, 1]
3490
sage: b = Sq(4)*Sq(8) + Sq(8)*Sq(4)
3491
sage: b.wall_height()
3492
[0, 0, 1, 1]
3493
sage: Sq(0,0,3).wall_height()
3494
[1, 2, 2, 1]
3495
"""
3496
from sage.rings.all import Integer
3497
if self.prime() > 2:
3498
raise NotImplementedError("Wall height is not defined at odd primes.")
3499
if self == 0 or self == 1:
3500
return []
3501
result = []
3502
deg = self.parent().degree_on_basis
3503
for r in self.milnor().support():
3504
h = [0]*(1 + deg(r))
3505
i = 1
3506
for x in r:
3507
if x > 0:
3508
for j in range(1+Integer(x).exact_log(2)):
3509
if (2**j & x) != 0:
3510
for k in range(j,i+j):
3511
h[k] += 1
3512
i=i+1
3513
h.reverse()
3514
result = max(h, result)
3515
result.reverse()
3516
while len(result) > 0 and result[-1] == 0:
3517
result = result[:-1]
3518
return result
3519
3520
def additive_order(self):
3521
"""
3522
The additive order of any nonzero element of the mod p
3523
Steenrod algebra is p.
3524
3525
OUTPUT: 1 (for the zero element) or p (for anything else)
3526
3527
EXAMPLES::
3528
3529
sage: z = Sq(4) + Sq(6) + 1
3530
sage: z.additive_order()
3531
2
3532
sage: (Sq(3) + Sq(3)).additive_order()
3533
1
3534
"""
3535
if self == 0:
3536
return 1
3537
return self.prime()
3538
3539
class SteenrodAlgebra_mod_two(SteenrodAlgebra_generic):
3540
"""
3541
The mod 2 Steenrod algebra.
3542
3543
Users should not call this, but use the function
3544
:func:`SteenrodAlgebra` instead. See that function for extensive
3545
documentation. (This differs from :class:`SteenrodAlgebra_generic`
3546
only in that it has a method :meth:`Sq` for defining elements.)
3547
"""
3548
def Sq(self, *nums):
3549
r"""
3550
Milnor element `\text{Sq}(a,b,c,...)`.
3551
3552
INPUT:
3553
3554
- ``a, b, c, ...`` - non-negative integers
3555
3556
OUTPUT: element of the Steenrod algebra
3557
3558
This returns the Milnor basis element
3559
`\text{Sq}(a, b, c, ...)`.
3560
3561
EXAMPLES::
3562
3563
sage: A = SteenrodAlgebra(2)
3564
sage: A.Sq(5)
3565
Sq(5)
3566
sage: A.Sq(5,0,2)
3567
Sq(5,0,2)
3568
3569
Entries must be non-negative integers; otherwise, an error
3570
results.
3571
"""
3572
if self.prime() == 2:
3573
return self.P(*nums)
3574
else:
3575
raise ValueError("Sq is only defined at the prime 2")
3576
3577
def SteenrodAlgebra(p=2, basis='milnor', **kwds):
3578
r"""
3579
The mod `p` Steenrod algebra
3580
3581
INPUT:
3582
3583
- ``p`` - positive prime integer (optional, default = 2)
3584
- ``basis`` - string (optional, default = 'milnor')
3585
- ``profile`` - a profile function in form specified below (optional, default ``None``)
3586
- ``truncation_type`` - 0 or `\infty` or 'auto' (optional, default 'auto')
3587
- ``precision`` - integer or ``None`` (optional, default ``None``)
3588
3589
OUTPUT: mod `p` Steenrod algebra or one of its sub-Hopf algebras,
3590
elements of which are printed using ``basis``
3591
3592
See below for information about ``basis``, ``profile``, etc.
3593
3594
EXAMPLES:
3595
3596
Some properties of the Steenrod algebra are available::
3597
3598
sage: A = SteenrodAlgebra(2)
3599
sage: A.order()
3600
+Infinity
3601
sage: A.is_finite()
3602
False
3603
sage: A.is_commutative()
3604
False
3605
sage: A.is_noetherian()
3606
False
3607
sage: A.is_integral_domain()
3608
False
3609
sage: A.is_field()
3610
False
3611
sage: A.is_division_algebra()
3612
False
3613
sage: A.category()
3614
Category of graded hopf algebras with basis over Finite Field of size 2
3615
3616
There are methods for constructing elements of the Steenrod
3617
algebra::
3618
3619
sage: A2 = SteenrodAlgebra(2); A2
3620
mod 2 Steenrod algebra, milnor basis
3621
sage: A2.Sq(1,2,6)
3622
Sq(1,2,6)
3623
sage: A2.Q(3,4) # product of Milnor primitives Q_3 and Q_4
3624
Sq(0,0,0,1,1)
3625
sage: A2.pst(2,3) # Margolis pst element
3626
Sq(0,0,4)
3627
sage: A5 = SteenrodAlgebra(5); A5
3628
mod 5 Steenrod algebra, milnor basis
3629
sage: A5.P(1,2,6)
3630
P(1,2,6)
3631
sage: A5.Q(3,4)
3632
Q_3 Q_4
3633
sage: A5.Q(3,4) * A5.P(1,2,6)
3634
Q_3 Q_4 P(1,2,6)
3635
sage: A5.pst(2,3)
3636
P(0,0,25)
3637
3638
You can test whether elements are contained in the Steenrod
3639
algebra::
3640
3641
sage: w = Sq(2) * Sq(4)
3642
sage: w in SteenrodAlgebra(2)
3643
True
3644
sage: w in SteenrodAlgebra(17)
3645
False
3646
3647
.. rubric:: Different bases for the Steenrod algebra:
3648
3649
There are two standard vector space bases for the mod `p` Steenrod
3650
algebra: the Milnor basis and the Serre-Cartan basis. When `p=2`,
3651
there are also several other, less well-known, bases. See the
3652
documentation for this module (type
3653
``sage.algebras.steenrod.steenrod_algebra?``) and the function
3654
:func:`steenrod_algebra_basis
3655
<sage.algebras.steenrod.steenrod_algebra_bases.steenrod_algebra_basis_>`
3656
for full descriptions of each of the implemented bases.
3657
3658
This module implements the following bases at all primes:
3659
3660
- 'milnor': Milnor basis.
3661
3662
- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.
3663
3664
- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz': various
3665
`P^s_t`-bases.
3666
3667
- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz', or
3668
these with '_long' appended: various commutator bases.
3669
3670
It implements the following bases when `p=2`:
3671
3672
- 'wood_y': Wood's Y basis.
3673
3674
- 'wood_z': Wood's Z basis.
3675
3676
- 'wall', 'wall_long': Wall's basis.
3677
3678
- 'arnon_a', 'arnon_a_long': Arnon's A basis.
3679
3680
- 'arnon_c': Arnon's C basis.
3681
3682
When defining a Steenrod algebra, you can specify a basis. Then
3683
elements of that Steenrod algebra are printed in that basis::
3684
3685
sage: adem = SteenrodAlgebra(2, 'adem')
3686
sage: x = adem.Sq(2,1) # Sq(-) always means a Milnor basis element
3687
sage: x
3688
Sq^4 Sq^1 + Sq^5
3689
sage: y = Sq(0,1) # unadorned Sq defines elements w.r.t. Milnor basis
3690
sage: y
3691
Sq(0,1)
3692
sage: adem(y)
3693
Sq^2 Sq^1 + Sq^3
3694
sage: adem5 = SteenrodAlgebra(5, 'serre-cartan')
3695
sage: adem5.P(0,2)
3696
P^10 P^2 + 4 P^11 P^1 + P^12
3697
3698
If you add or multiply elements defined using different bases, the
3699
left-hand factor determines the form of the output::
3700
3701
sage: SteenrodAlgebra(basis='adem').Sq(3) + SteenrodAlgebra(basis='pst').Sq(0,1)
3702
Sq^2 Sq^1
3703
sage: SteenrodAlgebra(basis='pst').Sq(3) + SteenrodAlgebra(basis='milnor').Sq(0,1)
3704
P^0_1 P^1_1 + P^0_2
3705
sage: SteenrodAlgebra(basis='milnor').Sq(2) * SteenrodAlgebra(basis='arnonc').Sq(2)
3706
Sq(1,1)
3707
3708
You can get a list of basis elements in a given dimension::
3709
3710
sage: A3 = SteenrodAlgebra(3, 'milnor')
3711
sage: A3.basis(13)
3712
Family (Q_1 P(2), Q_0 P(3))
3713
3714
Algebras defined over different bases are not equal::
3715
3716
sage: SteenrodAlgebra(basis='milnor') == SteenrodAlgebra(basis='pst')
3717
False
3718
3719
Bases have various synonyms, and in general Sage tries to figure
3720
out what basis you meant::
3721
3722
sage: SteenrodAlgebra(basis='MiLNOr')
3723
mod 2 Steenrod algebra, milnor basis
3724
sage: SteenrodAlgebra(basis='MiLNOr') == SteenrodAlgebra(basis='milnor')
3725
True
3726
sage: SteenrodAlgebra(basis='adem')
3727
mod 2 Steenrod algebra, serre-cartan basis
3728
sage: SteenrodAlgebra(basis='adem').basis_name()
3729
'serre-cartan'
3730
sage: SteenrodAlgebra(basis='wood---z---').basis_name()
3731
'woodz'
3732
3733
As noted above, several of the bases ('arnon_a', 'wall', 'comm')
3734
have alternate, sometimes longer, representations. These provide
3735
ways of expressing elements of the Steenrod algebra in terms of
3736
the `\text{Sq}^{2^n}`.
3737
3738
::
3739
3740
sage: A_long = SteenrodAlgebra(2, 'arnon_a_long')
3741
sage: A_long(Sq(6))
3742
Sq^1 Sq^2 Sq^1 Sq^2 + Sq^2 Sq^4
3743
sage: SteenrodAlgebra(2, 'wall_long')(Sq(6))
3744
Sq^2 Sq^1 Sq^2 Sq^1 + Sq^2 Sq^4
3745
sage: SteenrodAlgebra(2, 'comm_deg_long')(Sq(6))
3746
s_1 s_2 s_12 + s_2 s_4
3747
3748
.. rubric:: Sub-Hopf algebras of the Steenrod algebra:
3749
3750
These are specified using the argument ``profile``, along with,
3751
optionally, ``truncation_type`` and ``precision``. The
3752
``profile`` argument specifies the profile function for this
3753
algebra. Any sub-Hopf algebra of the Steenrod algebra is
3754
determined by its *profile function*. When `p=2`, this is a map `e`
3755
from the positive integers to the set of non-negative integers,
3756
plus `\infty`, corresponding to the sub-Hopf algebra dual to this
3757
quotient of the dual Steenrod algebra:
3758
3759
.. math::
3760
3761
\GF{2} [\xi_1, \xi_2, \xi_3, ...] / (\xi_1^{2^{e(1)}}, \xi_2^{2^{e(2)}}, \xi_3^{2^{e(3)}}, ...).
3762
3763
The profile function `e` must satisfy the condition
3764
3765
- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`.
3766
3767
This is specified via ``profile``, and optionally ``precision``
3768
and ``truncation_type``. First, ``profile`` must have one of the
3769
following forms:
3770
3771
- a list or tuple, e.g., ``[3,2,1]``, corresponding to the
3772
function sending 1 to 3, 2 to 2, 3 to 1, and all other integers
3773
to the value of ``truncation_type``.
3774
- a function from positive integers to non-negative integers (and
3775
`\infty`), e.g., ``lambda n: n+2``.
3776
- ``None`` or ``Infinity`` - use this for the profile function for
3777
the whole Steenrod algebra.
3778
3779
In the first and third cases, ``precision`` is ignored. In the
3780
second case, this function is converted to a tuple of length one
3781
less than ``precision``, which has default value 100. The
3782
function is truncated at this point, and all remaining values are
3783
set to the value of ``truncation_type``.
3784
3785
``truncation_type`` may be 0, `\infty`, or 'auto'. If it's
3786
'auto', then it gets converted to 0 in the first case above (when
3787
``profile`` is a list), and otherwise (when ``profile`` is a
3788
function, ``None``, or ``Infinity``) it gets converted to `\infty`.
3789
3790
For example, the sub-Hopf algebra `A(2)` has profile function
3791
``[3,2,1,0,0,0,...]``, so it can be defined by any of the
3792
following::
3793
3794
sage: A2 = SteenrodAlgebra(profile=[3,2,1])
3795
sage: B2 = SteenrodAlgebra(profile=[3,2,1,0,0]) # trailing 0's ignored
3796
sage: A2 == B2
3797
True
3798
sage: C2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=0)
3799
sage: A2 == C2
3800
True
3801
3802
In the following case, the profile function is specified by a
3803
function and ``truncation_type`` isn't specified, so it defaults
3804
to `\infty`; therefore this gives a different sub-Hopf algebra::
3805
3806
sage: D2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0))
3807
sage: A2 == D2
3808
False
3809
sage: D2.is_finite()
3810
False
3811
sage: E2 = SteenrodAlgebra(profile=lambda n: max(4-n, 0), truncation_type=Infinity)
3812
sage: D2 == E2
3813
True
3814
3815
The argument ``precision`` only needs to be specified if the
3816
profile function is defined by a function and you want to control
3817
when the profile switches from the given function to the
3818
truncation type. For example::
3819
3820
sage: D3 = SteenrodAlgebra(profile=lambda n: n, precision=3)
3821
sage: D3
3822
sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, +Infinity, +Infinity, +Infinity, ...]
3823
sage: D4 = SteenrodAlgebra(profile=lambda n: n, precision=4); D4
3824
sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [1, 2, 3, +Infinity, +Infinity, +Infinity, ...]
3825
sage: D3 == D4
3826
False
3827
3828
When `p` is odd, ``profile`` is a pair of functions `e` and `k`,
3829
corresponding to the quotient
3830
3831
.. math::
3832
3833
\GF{p} [\xi_1, \xi_2, \xi_3, ...] \otimes \Lambda (\tau_0,
3834
\tau_1, ...) / (\xi_1^{p^{e_1}}, \xi_2^{p^{e_2}}, ...;
3835
\tau_0^{k_0}, \tau_1^{k_1}, ...).
3836
3837
Together, the functions `e` and `k` must satisfy the conditions
3838
3839
- `e(r) \geq \min( e(r-i) - i, e(i))` for all `0 < i < r`,
3840
3841
- if `k(i+j) = 1`, then either `e(i) \leq j` or `k(j) = 1` for all `i
3842
\geq 1`, `j \geq 0`.
3843
3844
Therefore ``profile`` must have one of the following forms:
3845
3846
- a pair of lists or tuples, the second of which takes values in
3847
the set `\{1,2\}`, e.g., ``([3,2,1,1], [1,1,2,2,1])``.
3848
3849
- a pair of functions, one from the positive integers to
3850
non-negative integers (and `\infty`), one from the non-negative
3851
integers to the set `\{1,2\}`, e.g., ``(lambda n: n+2, lambda n:
3852
1 if n<3 else 2)``.
3853
3854
- ``None`` or ``Infinity`` - use this for the profile function for
3855
the whole Steenrod algebra.
3856
3857
You can also mix and match the first two, passing a pair with
3858
first entry a list and second entry a function, for instance. The
3859
values of ``precision`` and ``truncation_type`` are determined by
3860
the first entry.
3861
3862
More examples::
3863
3864
sage: E = SteenrodAlgebra(profile=lambda n: 0 if n<3 else 3, truncation_type=0)
3865
sage: E.is_commutative()
3866
True
3867
3868
sage: A2 = SteenrodAlgebra(profile=[3,2,1]) # the algebra A(2)
3869
sage: Sq(7,3,1) in A2
3870
True
3871
sage: Sq(8) in A2
3872
False
3873
sage: Sq(8) in SteenrodAlgebra().basis(8)
3874
True
3875
sage: Sq(8) in A2.basis(8)
3876
False
3877
sage: A2.basis(8)
3878
Family (Sq(1,0,1), Sq(2,2), Sq(5,1))
3879
3880
sage: A5 = SteenrodAlgebra(p=5)
3881
sage: A51 = SteenrodAlgebra(p=5, profile=([1], [2,2]))
3882
sage: A5.Q(0,1) * A5.P(4) in A51
3883
True
3884
sage: A5.Q(2) in A51
3885
False
3886
sage: A5.P(5) in A51
3887
False
3888
3889
For sub-Hopf algebras of the Steenrod algebra, only the Milnor
3890
basis or the various `P^s_t`-bases may be used. ::
3891
3892
sage: SteenrodAlgebra(profile=[1,2,1,1], basis='adem')
3893
Traceback (most recent call last):
3894
...
3895
NotImplementedError: For sub-Hopf algebras of the Steenrod algebra, only the Milnor basis and the pst bases are implemented.
3896
3897
TESTS:
3898
3899
Testing unique parents::
3900
3901
sage: S0 = SteenrodAlgebra(2)
3902
sage: S1 = SteenrodAlgebra(2)
3903
sage: S0 is S1
3904
True
3905
sage: S2 = SteenrodAlgebra(2, basis='adem')
3906
sage: S0 is S2
3907
False
3908
sage: S0 == S2
3909
False
3910
sage: A1 = SteenrodAlgebra(profile=[2,1])
3911
sage: B1 = SteenrodAlgebra(profile=[2,1,0,0])
3912
sage: A1 is B1
3913
True
3914
"""
3915
if p == 2:
3916
return SteenrodAlgebra_mod_two(p=2, basis=basis, **kwds)
3917
else:
3918
return SteenrodAlgebra_generic(p=p, basis=basis, **kwds)
3919
3920
3921
def AA(n=None, p=2):
3922
r"""
3923
This returns the Steenrod algebra `A` or its sub-Hopf algebra `A(n)`.
3924
3925
INPUT:
3926
3927
- `n` - non-negative integer, optional (default None)
3928
- `p` - prime number, optional (default 2)
3929
3930
OUTPUT: If `n` is None, then return the full Steenrod algebra.
3931
Otherwise, return `A(n)`.
3932
3933
When `p=2`, `A(n)` is the sub-Hopf algebra generated by the
3934
elements `\text{Sq}^i` for `i \leq 2^n`. Its profile function is
3935
`(n+1, n, n-1, ...)`. When `p` is odd, `A(n)` is the sub-Hopf
3936
algebra generated by the elements `Q_0` and `\mathcal{P}^i` for `i
3937
\leq p^{n-1}`. Its profile function is `e=(n, n-1, n-2, ...)`
3938
and `k=(2, 2, ..., 2)` (length `n+1`).
3939
3940
EXAMPLES::
3941
3942
sage: from sage.algebras.steenrod.steenrod_algebra import AA as A
3943
sage: A()
3944
mod 2 Steenrod algebra, milnor basis
3945
sage: A(2)
3946
sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]
3947
sage: A(2, p=5)
3948
sub-Hopf algebra of mod 5 Steenrod algebra, milnor basis, profile function ([2, 1], [2, 2, 2])
3949
"""
3950
if n is None:
3951
return SteenrodAlgebra(p=p)
3952
if p == 2:
3953
return SteenrodAlgebra(p=p, profile=range(n+1, 0, -1))
3954
return SteenrodAlgebra(p=p, profile=(range(n, 0, -1), [2]*(n+1)))
3955
3956
def Sq(*nums):
3957
r"""
3958
Milnor element Sq(a,b,c,...).
3959
3960
INPUT:
3961
3962
- ``a, b, c, ...`` - non-negative integers
3963
3964
OUTPUT: element of the Steenrod algebra
3965
3966
This returns the Milnor basis element
3967
`\text{Sq}(a, b, c, ...)`.
3968
3969
EXAMPLES::
3970
3971
sage: Sq(5)
3972
Sq(5)
3973
sage: Sq(5) + Sq(2,1) + Sq(5) # addition is mod 2:
3974
Sq(2,1)
3975
sage: (Sq(4,3) + Sq(7,2)).degree()
3976
13
3977
3978
Entries must be non-negative integers; otherwise, an error
3979
results.
3980
3981
This function is a good way to define elements of the Steenrod
3982
algebra.
3983
"""
3984
return SteenrodAlgebra(p=2).Sq(*nums)
3985
3986