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