Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/algebras/steenrod/steenrod_algebra_bases.py
4156 views
1
"""
2
Steenrod algebra bases
3
4
AUTHORS:
5
6
- John H. Palmieri (2008-07-30): version 0.9
7
- John H. Palmieri (2010-06-30): version 1.0
8
- Simon King (2011-10-25): Fix the use of cached functions
9
10
This package defines functions for computing various bases of the
11
Steenrod algebra, and for converting between the Milnor basis and
12
any other basis.
13
14
This packages implements a number of different bases, at least at
15
the prime 2. The Milnor and Serre-Cartan bases are the most
16
familiar and most standard ones, and all of the others are defined
17
in terms of one of these. The bases are described in the
18
documentation for the function
19
:func:`steenrod_algebra_basis`; also see the papers by
20
Monks [M] and Wood [W] for more information about them. For
21
commutator bases, see the preprint by Palmieri and Zhang [PZ].
22
23
- 'milnor': Milnor basis.
24
25
- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.
26
27
Most of the rest of the bases are only defined when `p=2`. The only
28
exceptions are the `P^s_t`-bases and the commutator bases, which are
29
defined at all primes.
30
31
- 'wood_y': Wood's Y basis.
32
33
- 'wood_z': Wood's Z basis.
34
35
- 'wall', 'wall_long': Wall's basis.
36
37
- 'arnon_a', 'arnon_a_long': Arnon's A basis.
38
39
- 'arnon_c': Arnon's C basis.
40
41
- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz':
42
various `P^s_t`-bases.
43
44
- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz',
45
or these with '_long' appended: various commutator bases.
46
47
The main functions provided here are
48
49
- :func:`steenrod_algebra_basis`. This computes a tuple representing
50
basis elements for the Steenrod algebra in a given degree, at a
51
given prime, with respect to a given basis. It is a cached function.
52
53
- :func:`convert_to_milnor_matrix`. This returns the change-of-basis
54
matrix, in a given degree, from any basis to the Milnor basis. It is
55
a cached function.
56
57
- :func:`convert_from_milnor_matrix`. This returns the inverse of the
58
previous matrix.
59
60
INTERNAL DOCUMENTATION:
61
62
If you want to implement a new basis for the Steenrod algebra:
63
64
In the file :file:`steenrod_algebra.py`:
65
66
For the class :class:`SteenrodAlgebra_generic
67
<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic>`, add functionality to the
68
methods:
69
70
- :meth:`_repr_term <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic._repr_term>`
71
72
- :meth:`degree_on_basis <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.degree_on_basis>`
73
74
- :meth:`_milnor_on_basis <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic._milnor_on_basis>`
75
76
- :meth:`an_element <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.an_element>`
77
78
In the file :file:`steenrod_algebra_misc.py`:
79
80
- add functionality to :func:`get_basis_name
81
<sage.algebras.steenrod.steenrod_algebra_misc.get_basis_name>`: this
82
should accept as input various synonyms for the basis, and its
83
output should be a canonical name for the basis.
84
85
- add a function ``BASIS_mono_to_string`` like
86
:func:`milnor_mono_to_string
87
<sage.algebras.steenrod.steenrod_algebra_misc.milnor_mono_to_string>`
88
or one of the other similar functions.
89
90
In this file :file:`steenrod_algebra_bases.py`:
91
92
- add appropriate lines to :func:`steenrod_algebra_basis`.
93
94
- add a function to compute the basis in a given dimension (to be
95
called by :func:`steenrod_algebra_basis`).
96
97
- modify :func:`steenrod_basis_error_check` so it checks the new
98
basis.
99
100
If the basis has an intrinsic way of defining a product, implement it
101
in the file :file:`steenrod_algebra_mult.py` and also in the
102
:meth:`product_on_basis
103
<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic.product_on_basis>`
104
method for :class:`SteenrodAlgebra_generic
105
<sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra_generic>` in
106
:file:`steenrod_algebra.py`.
107
108
REFERENCES:
109
110
- [M] K. G. Monks, "Change of basis, monomial relations, and
111
`P^s_t` bases for the Steenrod algebra," J. Pure Appl.
112
Algebra 125 (1998), no. 1-3, 235-260.
113
114
- [PZ] J. H. Palmieri and J. J. Zhang, "Commutators in the Steenrod
115
algebra," preprint (2008)
116
117
- [W] R. M. W. Wood, "Problems in the Steenrod algebra," Bull. London
118
Math. Soc. 30 (1998), no. 5, 449-517.
119
"""
120
121
#*****************************************************************************
122
# Copyright (C) 2008-2010 John H. Palmieri <[email protected]>
123
# Distributed under the terms of the GNU General Public License (GPL)
124
#*****************************************************************************
125
126
from sage.misc.cachefunc import cached_function
127
128
@cached_function
129
def convert_to_milnor_matrix(n, basis, p=2):
130
r"""
131
Change-of-basis matrix, 'basis' to Milnor, in dimension
132
`n`, at the prime `p`.
133
134
INPUT:
135
136
- ``n`` - non-negative integer, the dimension
137
- ``basis`` - string, the basis from which to convert
138
- ``p`` - positive prime number (optional, default 2)
139
140
OUTPUT:
141
142
``matrix`` - change-of-basis matrix, a square matrix over ``GF(p)``
143
144
.. note::
145
146
This is called internally. It is not intended for casual
147
users, so no error checking is made on the integer `n`, the
148
basis name, or the prime. Also, users should call
149
:func:`convert_to_milnor_matrix` instead of this function
150
(which has a trailing underscore in its name), because the
151
former is the cached version of the latter.
152
153
EXAMPLES::
154
155
sage: from sage.algebras.steenrod.steenrod_algebra_bases import convert_to_milnor_matrix
156
sage: convert_to_milnor_matrix(5, 'adem') # indirect doctest
157
[0 1]
158
[1 1]
159
sage: convert_to_milnor_matrix(45, 'milnor')
160
111 x 111 dense matrix over Finite Field of size 2
161
sage: convert_to_milnor_matrix(12,'wall')
162
[1 0 0 1 0 0 0]
163
[1 1 0 0 0 1 0]
164
[0 1 0 1 0 0 0]
165
[0 0 0 1 0 0 0]
166
[1 1 0 0 1 0 0]
167
[0 0 1 1 1 0 1]
168
[0 0 0 0 1 0 1]
169
170
The function takes an optional argument, the prime `p` over
171
which to work::
172
173
sage: convert_to_milnor_matrix(17,'adem',3)
174
[0 0 1 1]
175
[0 0 0 1]
176
[1 1 1 1]
177
[0 1 0 1]
178
sage: convert_to_milnor_matrix(48,'adem',5)
179
[0 1]
180
[1 1]
181
sage: convert_to_milnor_matrix(36,'adem',3)
182
[0 0 1]
183
[0 1 0]
184
[1 2 0]
185
"""
186
from sage.matrix.constructor import matrix
187
from sage.rings.all import GF
188
from steenrod_algebra import SteenrodAlgebra
189
if n == 0:
190
return matrix(GF(p), 1, 1, [[1]])
191
milnor_base = steenrod_algebra_basis(n,'milnor',p)
192
rows = []
193
A = SteenrodAlgebra(basis=basis, p=p)
194
for poly in A.basis(n):
195
d = poly.milnor().monomial_coefficients()
196
for v in milnor_base:
197
entry = d.get(v, 0)
198
rows = rows + [entry]
199
d = len(milnor_base)
200
return matrix(GF(p),d,d,rows)
201
202
def convert_from_milnor_matrix(n, basis, p=2):
203
r"""
204
Change-of-basis matrix, Milnor to 'basis', in dimension
205
`n`.
206
207
INPUT:
208
209
- ``n`` - non-negative integer, the dimension
210
211
- ``basis`` - string, the basis to which to convert
212
213
- ``p`` - positive prime number (optional, default 2)
214
215
OUTPUT: ``matrix`` - change-of-basis matrix, a square matrix over
216
GF(p)
217
218
.. note::
219
220
This is called internally. It is not intended for casual
221
users, so no error checking is made on the integer `n`, the
222
basis name, or the prime.
223
224
EXAMPLES::
225
226
sage: from sage.algebras.steenrod.steenrod_algebra_bases import convert_from_milnor_matrix, convert_to_milnor_matrix
227
sage: convert_from_milnor_matrix(12,'wall')
228
[1 0 0 1 0 0 0]
229
[0 0 1 1 0 0 0]
230
[0 0 0 1 0 1 1]
231
[0 0 0 1 0 0 0]
232
[1 0 1 0 1 0 0]
233
[1 1 1 0 0 0 0]
234
[1 0 1 0 1 0 1]
235
sage: convert_from_milnor_matrix(38,'serre_cartan')
236
72 x 72 dense matrix over Finite Field of size 2
237
sage: x = convert_to_milnor_matrix(20,'wood_y')
238
sage: y = convert_from_milnor_matrix(20,'wood_y')
239
sage: x*y
240
[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
241
[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
242
[0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
243
[0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0]
244
[0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
245
[0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]
246
[0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0]
247
[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]
248
[0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0]
249
[0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
250
[0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0]
251
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]
252
[0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]
253
[0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]
254
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0]
255
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0]
256
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
257
258
The function takes an optional argument, the prime `p` over
259
which to work::
260
261
sage: convert_from_milnor_matrix(17,'adem',3)
262
[2 1 1 2]
263
[0 2 0 1]
264
[1 2 0 0]
265
[0 1 0 0]
266
"""
267
mat = convert_to_milnor_matrix(n,basis,p)
268
if mat.nrows() != 0:
269
return convert_to_milnor_matrix(n,basis,p).inverse()
270
else:
271
return mat
272
273
@cached_function
274
def steenrod_algebra_basis(n, basis='milnor', p=2, **kwds):
275
r"""
276
Basis for the Steenrod algebra in degree `n`.
277
278
INPUT:
279
280
- ``n`` - non-negative integer
281
- ``basis`` - string, which basis to use (optional, default = 'milnor')
282
- ``p`` - positive prime number (optional, default = 2)
283
- ``profile`` - profile function (optional, default None). This
284
is just passed on to the functions :func:`milnor_basis` and
285
:func:`pst_basis`.
286
- ``truncation_type`` - truncation type, either 0 or Infinity
287
(optional, default Infinity if no profile function is specified,
288
0 otherwise). This is just passed on to the function
289
:func:`milnor_basis`.
290
291
OUTPUT:
292
293
Tuple of objects representing basis elements for the Steenrod algebra
294
in dimension n.
295
296
.. note::
297
298
Users should use :func:`steenrod_algebra_basis` instead of
299
this function (which has a trailing underscore in its name):
300
:func:`steenrod_algebra_basis` is the cached version of this
301
one, and so will be faster.
302
303
The choices for the string ``basis`` are as follows; see the
304
documentation for :mod:`sage.algebras.steenrod.steenrod_algebra`
305
for details on each basis:
306
307
- 'milnor': Milnor basis.
308
- 'serre-cartan' or 'adem' or 'admissible': Serre-Cartan basis.
309
- 'pst', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz':
310
various `P^s_t`-bases.
311
- 'comm', 'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz', or
312
any of these with '_long' appended: various commutator bases.
313
314
The rest of these bases are only defined when `p=2`.
315
316
- 'wood_y': Wood's Y basis.
317
- 'wood_z': Wood's Z basis.
318
- 'wall' or 'wall_long': Wall's basis.
319
- 'arnon_a' or 'arnon_a_long': Arnon's A basis.
320
- 'arnon_c': Arnon's C basis.
321
322
EXAMPLES::
323
324
sage: from sage.algebras.steenrod.steenrod_algebra_bases import steenrod_algebra_basis
325
sage: steenrod_algebra_basis(7,'milnor') # indirect doctest
326
((0, 0, 1), (1, 2), (4, 1), (7,))
327
sage: steenrod_algebra_basis(5) # milnor basis is the default
328
((2, 1), (5,))
329
330
Bases in negative dimensions are empty::
331
332
sage: steenrod_algebra_basis(-2, 'wall')
333
()
334
335
The third (optional) argument to 'steenrod_algebra_basis' is the
336
prime p::
337
338
sage: steenrod_algebra_basis(9, 'milnor', p=3)
339
(((1,), (1,)), ((0,), (2,)))
340
sage: steenrod_algebra_basis(9, 'milnor', 3)
341
(((1,), (1,)), ((0,), (2,)))
342
sage: steenrod_algebra_basis(17, 'milnor', 3)
343
(((2,), ()), ((1,), (3,)), ((0,), (0, 1)), ((0,), (4,)))
344
345
Other bases::
346
347
sage: steenrod_algebra_basis(7,'admissible')
348
((7,), (6, 1), (4, 2, 1), (5, 2))
349
sage: steenrod_algebra_basis(13,'admissible',p=3)
350
((1, 3, 0), (0, 3, 1))
351
sage: steenrod_algebra_basis(5,'wall')
352
(((2, 2), (0, 0)), ((1, 1), (1, 0)))
353
sage: steenrod_algebra_basis(5,'wall_long')
354
(((2, 2), (0, 0)), ((1, 1), (1, 0)))
355
sage: steenrod_algebra_basis(5,'pst-rlex')
356
(((0, 1), (2, 1)), ((1, 1), (0, 2)))
357
"""
358
from steenrod_algebra_misc import get_basis_name
359
try:
360
if n < 0 or int(n) != n:
361
return ()
362
except TypeError:
363
return ()
364
365
basis_name = get_basis_name(basis, p)
366
if basis_name.find('long') >= 0:
367
long = True
368
basis_name = basis_name.rsplit('_', 1)[0]
369
else:
370
long = False
371
372
profile = kwds.get("profile", None)
373
if (profile is not None and profile != () and profile != ((), ())
374
and basis != 'milnor' and basis.find('pst') == -1):
375
raise ValueError("Profile functions may only be used with the Milnor or pst bases")
376
377
## Milnor basis
378
if basis_name == 'milnor':
379
return milnor_basis(n,p, **kwds)
380
## Serre-Cartan basis
381
elif basis_name == 'serre-cartan':
382
return serre_cartan_basis(n,p)
383
## Atomic bases, p odd:
384
elif p > 2 and (basis_name.find('pst') >= 0
385
or basis_name.find('comm') >= 0):
386
return atomic_basis_odd(n, basis_name, p, **kwds)
387
## Atomic bases, p=2
388
elif p == 2 and (basis_name == 'woody' or basis_name == 'woodz'
389
or basis_name == 'wall' or basis_name == 'arnona'
390
or basis_name.find('pst') >= 0
391
or basis_name.find('comm') >= 0):
392
return atomic_basis(n, basis_name, **kwds)
393
## Arnon 'C' basis
394
elif p == 2 and basis == 'arnonc':
395
return arnonC_basis(n)
396
else:
397
raise ValueError("Unknown basis: %s at the prime %s" % (basis, p))
398
399
# helper functions for producing bases
400
401
def restricted_partitions(n, l, no_repeats=False):
402
"""
403
List of 'restricted' partitions of n: partitions with parts taken
404
from list.
405
406
INPUT:
407
408
- ``n`` - non-negative integer
409
- ``l`` - list of positive integers
410
- ``no_repeats`` - boolean (optional, default = False), if True,
411
only return partitions with no repeated parts
412
413
OUTPUT: list of lists
414
415
One could also use ``Partitions(n, parts_in=l)``, but this
416
function may be faster. Also, while ``Partitions(n, parts_in=l,
417
max_slope=-1)`` should in theory return the partitions of `n` with
418
parts in ``l`` with no repetitions, the ``max_slope=-1`` argument
419
is ignored, so it doesn't work. (At the moment, the
420
``no_repeats=True`` case is the only one used in the code.)
421
422
EXAMPLES::
423
424
sage: from sage.algebras.steenrod.steenrod_algebra_bases import restricted_partitions
425
sage: restricted_partitions(10, [7,5,1])
426
[[7, 1, 1, 1], [5, 5], [5, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
427
sage: restricted_partitions(10, [6,5,4,3,2,1], no_repeats=True)
428
[[6, 4], [6, 3, 1], [5, 4, 1], [5, 3, 2], [4, 3, 2, 1]]
429
sage: restricted_partitions(10, [6,4,2])
430
[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]
431
sage: restricted_partitions(10, [6,4,2], no_repeats=True)
432
[[6, 4]]
433
434
'l' may have repeated elements. If 'no_repeats' is False, this
435
has no effect. If 'no_repeats' is True, and if the repeated
436
elements appear consecutively in 'l', then each element may be
437
used only as many times as it appears in 'l'::
438
439
sage: restricted_partitions(10, [6,4,2,2], no_repeats=True)
440
[[6, 4], [6, 2, 2]]
441
sage: restricted_partitions(10, [6,4,2,2,2], no_repeats=True)
442
[[6, 4], [6, 2, 2], [4, 2, 2, 2]]
443
444
(If the repeated elements don't appear consecutively, the results
445
are likely meaningless, containing several partitions more than
446
once, for example.)
447
448
In the following examples, 'no_repeats' is False::
449
450
sage: restricted_partitions(10, [6,4,2])
451
[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]
452
sage: restricted_partitions(10, [6,4,2,2,2])
453
[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]
454
sage: restricted_partitions(10, [6,4,4,4,2,2,2,2,2,2])
455
[[6, 4], [6, 2, 2], [4, 4, 2], [4, 2, 2, 2], [2, 2, 2, 2, 2]]
456
"""
457
if n < 0:
458
return []
459
elif n == 0:
460
return [[]]
461
else:
462
results = []
463
if no_repeats:
464
index = 1
465
else:
466
index = 0
467
old_i = 0
468
for i in l:
469
if old_i != i:
470
for sigma in restricted_partitions(n-i, l[index:], no_repeats):
471
results.append([i] + sigma)
472
index += 1
473
old_i = i
474
return results
475
476
def xi_degrees(n,p=2, reverse=True):
477
r"""
478
Decreasing list of degrees of the xi_i's, starting in degree n.
479
480
INPUT:
481
482
- `n` - integer
483
- `p` - prime number, optional (default 2)
484
- ``reverse`` - bool, optional (default True)
485
486
OUTPUT: ``list`` - list of integers
487
488
When `p=2`: decreasing list of the degrees of the `\xi_i`'s with
489
degree at most n.
490
491
At odd primes: decreasing list of these degrees, each divided by
492
`2(p-1)`.
493
494
If ``reverse`` is False, then return an increasing list rather
495
than a decreasing one.
496
497
EXAMPLES::
498
499
sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17)
500
[15, 7, 3, 1]
501
sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17, reverse=False)
502
[1, 3, 7, 15]
503
sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(17,p=3)
504
[13, 4, 1]
505
sage: sage.algebras.steenrod.steenrod_algebra_bases.xi_degrees(400,p=17)
506
[307, 18, 1]
507
"""
508
from sage.rings.all import Integer
509
if n <= 0: return []
510
N = Integer(n*(p-1) + 1)
511
l = [int((p**d-1)/(p-1)) for d in range(1,N.exact_log(p)+1)]
512
if not reverse:
513
return l
514
l.reverse()
515
return l
516
517
########################################################
518
# Functions for defining bases.
519
520
# These should each return a tuple of tuples of the appropriate form
521
# for the basis. For example, at the prime 2, the Milnor basis
522
# element Sq(a,b,c,...) corresponds to the tuple (a, b, c, ...), while
523
# at odd primes, the element Q_i Q_j ... P(a, b, ...) corresponds to
524
# the pair ((i, j, ...), (a, b, ...)). See each function for more
525
# information.
526
527
def milnor_basis(n, p=2, **kwds):
528
r"""
529
Milnor basis in dimension `n` with profile function ``profile``.
530
531
INPUT:
532
533
- ``n`` - non-negative integer
534
535
- ``p`` - positive prime number (optional, default 2)
536
537
- ``profile`` - profile function (optional, default None).
538
Together with ``truncation_type``, specify the profile function
539
to be used; None means the profile function for the entire
540
Steenrod algebra. See
541
:mod:`sage.algebras.steenrod.steenrod_algebra` and
542
:func:`SteenrodAlgebra <sage.algebras.steenrod.steenrod_algebra.SteenrodAlgebra>`
543
for information on profile functions.
544
545
- ``truncation_type`` - truncation type, either 0 or Infinity
546
(optional, default Infinity if no profile function is specified,
547
0 otherwise)
548
549
OUTPUT: tuple of mod p Milnor basis elements in dimension n
550
551
At the prime 2, the Milnor basis consists of symbols of the form
552
`\text{Sq}(m_1, m_2, ..., m_t)`, where each
553
`m_i` is a non-negative integer and if `t>1`, then
554
`m_t \neq 0`. At odd primes, it consists of symbols of the
555
form `Q_{e_1} Q_{e_2} ... P(m_1, m_2, ..., m_t)`,
556
where `0 \leq e_1 < e_2 < ...`, each `m_i` is a
557
non-negative integer, and if `t>1`, then
558
`m_t \neq 0`.
559
560
EXAMPLES::
561
562
sage: from sage.algebras.steenrod.steenrod_algebra_bases import milnor_basis
563
sage: milnor_basis(7)
564
((0, 0, 1), (1, 2), (4, 1), (7,))
565
sage: milnor_basis(7, 2)
566
((0, 0, 1), (1, 2), (4, 1), (7,))
567
sage: milnor_basis(4, 2)
568
((1, 1), (4,))
569
sage: milnor_basis(4, 2, profile=[2,1])
570
((1, 1),)
571
sage: milnor_basis(4, 2, profile=(), truncation_type=0)
572
()
573
sage: milnor_basis(4, 2, profile=(), truncation_type=Infinity)
574
((1, 1), (4,))
575
sage: milnor_basis(9, 3)
576
(((1,), (1,)), ((0,), (2,)))
577
sage: milnor_basis(17, 3)
578
(((2,), ()), ((1,), (3,)), ((0,), (0, 1)), ((0,), (4,)))
579
sage: milnor_basis(48, p=5)
580
(((), (0, 1)), ((), (6,)))
581
sage: len(milnor_basis(100,3))
582
13
583
sage: len(milnor_basis(200,7))
584
0
585
sage: len(milnor_basis(240,7))
586
3
587
sage: len(milnor_basis(240,7, profile=((),()), truncation_type=Infinity))
588
3
589
sage: len(milnor_basis(240,7, profile=((),()), truncation_type=0))
590
0
591
"""
592
if n == 0:
593
if p == 2:
594
return ((),)
595
else:
596
return (((), ()),)
597
598
from sage.rings.infinity import Infinity
599
from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
600
profile = kwds.get("profile", None)
601
trunc = kwds.get("truncation_type", None)
602
if trunc is None:
603
if profile is not None:
604
trunc = 0
605
else:
606
trunc = Infinity
607
608
result = []
609
if p == 2:
610
for mono in WeightedIntegerVectors(n, xi_degrees(n, reverse=False)):
611
exponents = list(mono)
612
while len(exponents) > 0 and exponents[-1] == 0:
613
exponents.pop(-1)
614
# check profile:
615
okay = True
616
if profile is not None and len(profile) > 0:
617
for i in range(len(exponents)):
618
if ((len(profile) > i and exponents[i] >= 2**profile[i])
619
or (len(profile) <= i and trunc < Infinity
620
and exponents[i] >= 2**trunc)):
621
okay = False
622
break
623
else:
624
# profile is empty
625
okay = (trunc == Infinity)
626
if okay:
627
result.append(tuple(exponents))
628
else: # p odd
629
# first find the P part of each basis element.
630
# in this part of the code (the P part), all dimensions are
631
# divided by 2(p-1).
632
for dim in range(n/(2*(p-1)) + 1):
633
if dim == 0:
634
P_result = [[0]]
635
else:
636
P_result = []
637
for mono in WeightedIntegerVectors(dim, xi_degrees(dim, p=p, reverse=False)):
638
p_mono = list(mono)
639
while len(p_mono) > 0 and p_mono[-1] == 0:
640
p_mono.pop(-1)
641
if len(p_mono) > 0:
642
P_result.append(p_mono)
643
# now find the Q part of the basis element.
644
# dimensions here are back to normal.
645
for p_mono in P_result:
646
deg = n - 2*dim*(p-1)
647
q_degrees = [1+2*(p-1)*d for d in
648
xi_degrees(int((deg - 1)/(2*(p-1))), p)] + [1]
649
q_degrees_decrease = q_degrees
650
q_degrees.reverse()
651
if deg % (2*(p-1)) <= len(q_degrees):
652
# if this inequality fails, no way to have a partition
653
# with distinct parts.
654
for sigma in restricted_partitions(deg,
655
q_degrees_decrease,
656
no_repeats = True):
657
index = 0
658
q_mono = []
659
for q in q_degrees:
660
if q in sigma:
661
q_mono.append(index)
662
index += 1
663
# check profile:
664
okay = True
665
if profile is not None and (len(profile[0]) > 0
666
or len(profile[1]) > 0):
667
# check profile function for q_mono
668
for i in q_mono:
669
if ((len(profile[1]) > i and profile[1][i] == 1)
670
or (len(profile[1]) <= i and trunc == 0)):
671
okay = False
672
break
673
# check profile function for p_mono
674
for i in range(len(p_mono)):
675
if okay and ((len(profile[0]) > i and p_mono[i] >= p**profile[0][i])
676
or (len(profile[0]) <= i and trunc < Infinity
677
and p_mono[i] >= p**trunc)):
678
okay = False
679
break
680
else:
681
# profile is empty
682
okay = (trunc == Infinity)
683
if okay:
684
if list(p_mono) == [0]:
685
p_mono = []
686
result.append((tuple(q_mono), tuple(p_mono)))
687
return tuple(result)
688
689
def serre_cartan_basis(n, p=2, bound=1):
690
r"""
691
Serre-Cartan basis in dimension `n`.
692
693
INPUT:
694
695
- ``n`` - non-negative integer
696
- ``bound`` - positive integer (optional)
697
- ``prime`` - positive prime number (optional, default 2)
698
699
OUTPUT: tuple of mod p Serre-Cartan basis elements in dimension n
700
701
The Serre-Cartan basis consists of 'admissible monomials in the
702
Steenrod squares'. Thus at the prime 2, it consists of monomials
703
`\text{Sq}^{m_1} \text{Sq}^{m_2} ... \text{Sq}^{m_t}` with `m_i
704
\geq 2m_{i+1}` for each `i`. At odd primes, it consists of
705
monomials `\beta^{e_0} P^{s_1} \beta^{e_1} P^{s_2} ... P^{s_k}
706
\beta^{e_k}` with each `e_i` either 0 or 1, `s_i \geq p s_{i+1} +
707
e_i` for all `i`, and `s_k \geq 1`.
708
709
EXAMPLES::
710
711
sage: from sage.algebras.steenrod.steenrod_algebra_bases import serre_cartan_basis
712
sage: serre_cartan_basis(7)
713
((7,), (6, 1), (4, 2, 1), (5, 2))
714
sage: serre_cartan_basis(13,3)
715
((1, 3, 0), (0, 3, 1))
716
sage: serre_cartan_basis(50,5)
717
((1, 5, 0, 1, 1), (1, 6, 1))
718
719
If optional argument ``bound`` is present, include only those monomials
720
whose last term is at least ``bound`` (when p=2), or those for which
721
`s_k - e_k \geq bound` (when p is odd). ::
722
723
sage: serre_cartan_basis(7, bound=2)
724
((7,), (5, 2))
725
sage: serre_cartan_basis(13, 3, bound=3)
726
((1, 3, 0),)
727
"""
728
if n == 0:
729
return ((),)
730
else:
731
if p == 2:
732
# Build basis recursively. last = last term.
733
# last is >= bound, and we will append (last,) to the end of
734
# elements from serre_cartan_basis (n - last, bound=2 * last).
735
# This means that 2 last <= n - last, or 3 last <= n.
736
result = [(n,)]
737
for last in range(bound, 1+n/3):
738
for vec in serre_cartan_basis(n - last, bound = 2*last):
739
new = vec + (last,)
740
result.append(new)
741
else: # p odd
742
if n % (2 * (p-1)) == 0 and n/(2 * (p-1)) >= bound:
743
result = [(0, int(n/(2 * (p-1))), 0)]
744
elif n == 1:
745
result = [(1,)]
746
else:
747
result = []
748
# 2 cases: append P^{last}, or append P^{last} beta
749
# case 1: append P^{last}
750
for last in range(bound, 1+n/(2*(p - 1))):
751
if n - 2*(p-1)*last > 0:
752
for vec in serre_cartan_basis(n - 2*(p-1)*last,
753
p, p*last):
754
result.append(vec + (last,0))
755
# case 2: append P^{last} beta
756
if bound == 1:
757
bound = 0
758
for last in range(bound+1, 1+n/(2*(p - 1))):
759
basis = serre_cartan_basis(n - 2*(p-1)*last - 1,
760
p, p*last)
761
for vec in basis:
762
if vec == ():
763
vec = (0,)
764
new = vec + (last, 1)
765
result.append(new)
766
return tuple(result)
767
768
def atomic_basis(n, basis, **kwds):
769
r"""
770
Basis for dimension `n` made of elements in 'atomic' degrees:
771
degrees of the form `2^i (2^j - 1)`.
772
773
This works at the prime 2 only.
774
775
INPUT:
776
777
- ``n`` - non-negative integer
778
- ``basis`` - string, the name of the basis
779
780
- ``profile`` - profile function (optional, default None).
781
Together with ``truncation_type``, specify the profile function
782
to be used; None means the profile function for the entire
783
Steenrod algebra. See
784
:mod:`sage.algebras.steenrod.steenrod_algebra` and
785
:func:`SteenrodAlgebra` for information on profile functions.
786
787
- ``truncation_type`` - truncation type, either 0 or Infinity
788
(optional, default Infinity if no profile function is specified,
789
0 otherwise).
790
791
OUTPUT: tuple of basis elements in dimension n
792
793
The atomic bases include Wood's Y and Z bases, Wall's basis,
794
Arnon's A basis, the `P^s_t`-bases, and the commutator
795
bases. (All of these bases are constructed similarly, hence their
796
constructions have been consolidated into a single function. Also,
797
see the documentation for 'steenrod_algebra_basis' for
798
descriptions of them.) For `P^s_t`-bases, you may also specify a
799
profile function and truncation type; profile functions are ignored
800
for the other bases.
801
802
EXAMPLES::
803
804
sage: from sage.algebras.steenrod.steenrod_algebra_bases import atomic_basis
805
sage: atomic_basis(6,'woody')
806
(((1, 0), (0, 1), (0, 0)), ((2, 0), (1, 0)), ((1, 1),))
807
sage: atomic_basis(8,'woodz')
808
(((2, 0), (0, 1), (0, 0)), ((0, 2), (0, 0)), ((1, 1), (1, 0)), ((3, 0),))
809
sage: atomic_basis(6,'woodz') == atomic_basis(6, 'woody')
810
True
811
sage: atomic_basis(9,'woodz') == atomic_basis(9, 'woody')
812
False
813
814
Wall's basis::
815
816
sage: atomic_basis(8,'wall')
817
(((2, 2), (1, 0), (0, 0)), ((2, 0), (0, 0)), ((2, 1), (1, 1)), ((3, 3),))
818
819
Arnon's A basis::
820
821
sage: atomic_basis(7,'arnona')
822
(((0, 0), (1, 1), (2, 2)), ((0, 0), (2, 1)), ((1, 0), (2, 2)), ((2, 0),))
823
824
`P^s_t`-bases::
825
826
sage: atomic_basis(7,'pst_rlex')
827
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((2, 1), (0, 2)), ((0, 3),))
828
sage: atomic_basis(7,'pst_llex')
829
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
830
sage: atomic_basis(7,'pst_deg')
831
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
832
sage: atomic_basis(7,'pst_revz')
833
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
834
835
Commutator bases::
836
837
sage: atomic_basis(7,'comm_rlex')
838
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((2, 1), (0, 2)), ((0, 3),))
839
sage: atomic_basis(7,'comm_llex')
840
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
841
sage: atomic_basis(7,'comm_deg')
842
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
843
sage: atomic_basis(7,'comm_revz')
844
(((0, 1), (1, 1), (2, 1)), ((0, 1), (1, 2)), ((0, 2), (2, 1)), ((0, 3),))
845
"""
846
def degree_dictionary(n, basis):
847
"""
848
Dictionary of atomic degrees for basis up to degree n.
849
850
The keys for the dictionary are the atomic degrees - the numbers of
851
the form 2^i (2^j - 1) - which are less than or equal to n. The value
852
associated to such a degree depends on basis; it has the form
853
(s,t), where (s,t) is a pair of integers which indexes the
854
corresponding element.
855
"""
856
dict = {}
857
if basis.find('wood') >= 0:
858
k=0
859
m=0
860
deg = 2**m * (2**(k+1) - 1)
861
while deg <= n:
862
dict[deg] = (m,k)
863
if m>0:
864
m = m - 1
865
k = k + 1
866
else:
867
m = k + 1
868
k = 0
869
deg = 2**m * (2**(k+1) - 1)
870
elif basis.find('wall') >= 0 or basis.find('arnon') >= 0:
871
k=0
872
m=0
873
deg = 2**k * (2**(m-k+1) - 1)
874
while deg <= n:
875
dict[deg] = (m,k)
876
if k == 0:
877
m = m + 1
878
k = m
879
else:
880
k = k - 1
881
deg = 2**k * (2**(m-k+1) - 1)
882
elif basis.find('pst') >= 0 or basis.find('comm') >= 0:
883
s=0
884
t=1
885
deg = 2**s * (2**t - 1)
886
while deg <= n:
887
if basis.find('pst') >= 0:
888
dict[deg] = (s,t)
889
else: # comm
890
dict[deg] = (s,t)
891
if s == 0:
892
s = t
893
t = 1
894
else:
895
s = s - 1
896
t = t + 1
897
deg = 2**s * (2**t - 1)
898
return dict
899
900
def sorting_pair(s,t,basis): # pair used for sorting the basis
901
if basis.find('wood') >= 0 and basis.find('z') >= 0:
902
return (-s-t,-s)
903
elif basis.find('wood') >= 0 or basis.find('wall') >= 0 or \
904
basis.find('arnon') >= 0:
905
return (-s,-t)
906
elif basis.find('rlex') >= 0:
907
return (t,s)
908
elif basis.find('llex') >= 0:
909
return (s,t)
910
elif basis.find('deg') >= 0:
911
return (s+t,t)
912
elif basis.find('revz') >= 0:
913
return (s+t,s)
914
915
from sage.misc.misc import prod
916
from sage.rings.infinity import Infinity
917
profile = kwds.get("profile", None)
918
trunc = kwds.get("truncation_type", None)
919
if profile is not None and trunc is None:
920
trunc = 0
921
922
if n == 0:
923
return ((),)
924
else:
925
result = []
926
degrees_etc = degree_dictionary(n, basis)
927
degrees = degrees_etc.keys()
928
for sigma in restricted_partitions(n, degrees, no_repeats=True):
929
big_list = [degrees_etc[part] for part in sigma]
930
big_list.sort(cmp = lambda x, y: cmp(sorting_pair(x[0], x[1], basis),
931
sorting_pair(y[0], y[1], basis)))
932
# reverse = True)
933
# arnon: sort like wall, then reverse end result
934
if basis.find('arnon') >= 0:
935
big_list.reverse()
936
937
# check profile:
938
okay = True
939
if basis.find('pst') >= 0:
940
if profile is not None and len(profile) > 0:
941
for (s,t) in big_list:
942
if ((len(profile) > t-1 and profile[t-1] <= s)
943
or (len(profile) <= t-1 and trunc < Infinity)):
944
okay = False
945
break
946
if okay:
947
result.append(tuple(big_list))
948
return tuple(result)
949
950
def arnonC_basis(n,bound=1):
951
r"""
952
Arnon's C basis in dimension `n`.
953
954
INPUT:
955
956
- ``n`` - non-negative integer
957
958
- ``bound`` - positive integer (optional)
959
960
OUTPUT: tuple of basis elements in dimension n
961
962
The elements of Arnon's C basis are monomials of the form
963
`\text{Sq}^{t_1} ... \text{Sq}^{t_m}` where for each
964
`i`, we have `t_i \leq 2t_{i+1}` and
965
`2^i | t_{m-i}`.
966
967
EXAMPLES::
968
969
sage: from sage.algebras.steenrod.steenrod_algebra_bases import arnonC_basis
970
sage: arnonC_basis(7)
971
((7,), (2, 5), (4, 3), (4, 2, 1))
972
973
If optional argument ``bound`` is present, include only those monomials
974
whose first term is at least as large as ``bound``::
975
976
sage: arnonC_basis(7,3)
977
((7,), (4, 3), (4, 2, 1))
978
"""
979
if n == 0:
980
return ((),)
981
else:
982
# Build basis recursively. first = first term.
983
# first is >= bound, and we will prepend (first,) to the
984
# elements from arnonC_basis (n - first, first / 2).
985
# first also must be divisible by 2**(len(old-basis-elt))
986
# This means that 3 first <= 2 n.
987
result = [(n,)]
988
for first in range(bound,1+2*n/3):
989
for vec in arnonC_basis(n - first, max(first/2,1)):
990
if first % 2**len(vec) == 0:
991
result.append((first,) + vec)
992
return tuple(result)
993
994
def atomic_basis_odd(n, basis, p, **kwds):
995
r"""
996
`P^s_t`-bases and commutator basis in dimension `n` at odd primes.
997
998
This function is called ``atomic_basis_odd`` in analogy with
999
:func:`atomic_basis`.
1000
1001
INPUT:
1002
1003
- ``n`` - non-negative integer
1004
- ``basis`` - string, the name of the basis
1005
- ``p`` - positive prime number
1006
1007
- ``profile`` - profile function (optional, default None).
1008
Together with ``truncation_type``, specify the profile function
1009
to be used; None means the profile function for the entire
1010
Steenrod algebra. See
1011
:mod:`sage.algebras.steenrod.steenrod_algebra` and
1012
:func:`SteenrodAlgebra` for information on profile functions.
1013
1014
- ``truncation_type`` - truncation type, either 0 or Infinity
1015
(optional, default Infinity if no profile function is specified,
1016
0 otherwise).
1017
1018
OUTPUT: tuple of basis elements in dimension n
1019
1020
The only possible difference in the implementations for `P^s_t`
1021
bases and commutator bases is that the former make sense, and
1022
require filtering, if there is a nontrivial profile function.
1023
This function is called by :func:`steenrod_algebra_basis`, and it
1024
will not be called for commutator bases if there is a profile
1025
function, so we treat the two bases exactly the same.
1026
1027
EXAMPLES::
1028
1029
sage: from sage.algebras.steenrod.steenrod_algebra_bases import atomic_basis_odd
1030
sage: atomic_basis_odd(8, 'pst_rlex', 3)
1031
(((), (((0, 1), 2),)),)
1032
1033
sage: atomic_basis_odd(18, 'pst_rlex', 3)
1034
(((0, 2), ()), ((0, 1), (((1, 1), 1),)))
1035
sage: atomic_basis_odd(18, 'pst_rlex', 3, profile=((), (2,2,2)))
1036
(((0, 2), ()),)
1037
"""
1038
def sorting_pair(s,t,basis): # pair used for sorting the basis
1039
if basis.find('rlex') >= 0:
1040
return (t,s)
1041
elif basis.find('llex') >= 0:
1042
return (s,t)
1043
elif basis.find('deg') >= 0:
1044
return (s+t,t)
1045
elif basis.find('revz') >= 0:
1046
return (s+t,s)
1047
1048
if n == 0:
1049
if p == 2:
1050
return ((),)
1051
else:
1052
return (((), ()),)
1053
from sage.misc.misc import prod
1054
from sage.rings.all import Integer
1055
from sage.rings.infinity import Infinity
1056
from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
1057
profile = kwds.get("profile", None)
1058
trunc = kwds.get("truncation_type", 0)
1059
1060
result = []
1061
for dim in range(n/(2*p-2) + 1):
1062
P_result = []
1063
for v in WeightedIntegerVectors(dim, xi_degrees(dim, p=p, reverse=False)):
1064
mono = []
1065
for t, a in enumerate(v):
1066
for s, pow in enumerate(Integer(a).digits(p)):
1067
if pow > 0:
1068
mono.append(((s, t+1), pow))
1069
P_result.append(mono)
1070
for p_mono in P_result:
1071
p_mono.sort(cmp = lambda x, y: cmp(sorting_pair(x[0][0], x[0][1], basis),
1072
sorting_pair(y[0][0], y[0][1], basis)))
1073
deg = n - 2*dim*(p-1)
1074
q_degrees = [1+2*(p-1)*d for d in
1075
xi_degrees(int((deg - 1)/(2*(p-1))), p)] + [1]
1076
q_degrees_decrease = q_degrees
1077
q_degrees.reverse()
1078
if deg % (2*(p-1)) <= len(q_degrees):
1079
# if this inequality fails, no way to have a partition
1080
# with distinct parts.
1081
for sigma in restricted_partitions(deg,
1082
q_degrees_decrease,
1083
no_repeats = True):
1084
index = 0
1085
q_mono = []
1086
for q in q_degrees:
1087
if q in sigma:
1088
q_mono.append(index)
1089
index += 1
1090
# check profile:
1091
okay = True
1092
if profile is not None and profile != ((), ()):
1093
# check profile function for q_mono
1094
for i in q_mono:
1095
if ((len(profile[1]) > i and profile[1][i] == 1)
1096
or (len(profile[1]) <= i and trunc == 0)):
1097
okay = False
1098
break
1099
1100
for ((s,t), exp) in p_mono:
1101
if ((len(profile[0]) > t-1 and profile[0][t-1] <= s)
1102
or (len(profile[0]) <= t-1 and trunc < Infinity)):
1103
okay = False
1104
break
1105
1106
if okay:
1107
if list(p_mono) == [0]:
1108
p_mono = []
1109
result.append((tuple(q_mono), tuple(p_mono)))
1110
return tuple(result)
1111
1112
#############################################################################
1113
def steenrod_basis_error_check(dim, p):
1114
"""
1115
This performs crude error checking.
1116
1117
INPUT:
1118
1119
- ``dim`` - non-negative integer
1120
- ``p`` - positive prime number
1121
1122
OUTPUT: None
1123
1124
This checks to see if the different bases have the same length, and
1125
if the change-of-basis matrices are invertible. If something goes
1126
wrong, an error message is printed.
1127
1128
This function checks at the prime ``p`` as the dimension goes up
1129
from 0 to ``dim``.
1130
1131
If you set the Sage verbosity level to a positive integer (using
1132
``set_verbose(n)``), then some extra messages will be printed.
1133
1134
EXAMPLES::
1135
1136
sage: from sage.algebras.steenrod.steenrod_algebra_bases import steenrod_basis_error_check
1137
sage: steenrod_basis_error_check(15,2) # long time
1138
sage: steenrod_basis_error_check(40,3) # long time
1139
sage: steenrod_basis_error_check(80,5) # long time
1140
"""
1141
import sage.misc.misc as misc
1142
1143
# Apparently, in this test function, we don't want to benefit from caching.
1144
# Hence, the uncached version of steenrod_algebra_basis and of
1145
# convert_to-milnor_matrix are used.
1146
if p == 2:
1147
bases = ('adem','woody', 'woodz', 'wall', 'arnona', 'arnonc',
1148
'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz',
1149
'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz')
1150
else:
1151
bases = ('adem',
1152
'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz',
1153
'comm_rlex', 'comm_llex', 'comm_deg', 'comm_revz')
1154
1155
for i in range(dim):
1156
if i % 5 == 0:
1157
misc.verbose("up to dimension %s"%i)
1158
milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p))
1159
for B in bases:
1160
if milnor_dim != len(steenrod_algebra_basis.f(i,B,p)):
1161
print "problem with milnor/" + B + " in dimension ", i
1162
mat = convert_to_milnor_matrix.f(i,B,p)
1163
if mat.nrows() != 0 and not mat.is_invertible():
1164
print "%s invertibility problem in dim %s at p=%s" % (B, i, p)
1165
1166
misc.verbose("done checking, no profiles")
1167
1168
bases = ('pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz')
1169
if p == 2:
1170
profiles = [(4,3,2,1), (2,2,3,1,1), (0,0,0,2)]
1171
else:
1172
profiles = [((3,2,1), ()), ((), (2,1,2)), ((3,2,1), (2,2,2,2))]
1173
1174
for i in range(dim):
1175
if i % 5 == 0:
1176
misc.verbose("up to dimension %s"%i)
1177
for pro in profiles:
1178
milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p,profile=pro))
1179
for B in bases:
1180
if milnor_dim != len(steenrod_algebra_basis.f(i,B,p,profile=pro)):
1181
print "problem with milnor/%s in dimension %s with profile %s"%(B, i, pro)
1182
1183
misc.verbose("done checking with profiles")
1184
1185