Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/algebras/steenrod/steenrod_algebra_mult.py
8822 views
1
r"""
2
Multiplication for elements of the Steenrod algebra
3
4
AUTHORS:
5
6
- John H. Palmieri (2008-07-30: version 0.9) initial version: Milnor
7
multiplication.
8
- John H. Palmieri (2010-06-30: version 1.0) multiplication of
9
Serre-Cartan basis elements using the Adem relations.
10
- Simon King (2011-10-25): Fix the use of cached functions.
11
12
.. rubric:: Milnor multiplication, `p=2`
13
14
See Milnor's paper [Mil] for proofs, etc.
15
16
To multiply Milnor basis elements $\text{Sq}(r_1, r_2, ...)$ and
17
$\text{Sq}(s_1, s_2,...)$ at the prime 2, form all possible matrices
18
$M$ with rows and columns indexed starting at 0, with position (0,0)
19
deleted (or ignored), with $s_i$ equal to the sum of column $i$ for
20
each $i$, and with $r_j$ equal to the 'weighted' sum of row $j$. The
21
weights are as follows: elements from column $i$ are multiplied by
22
$2^i$. For example, to multiply $\text{Sq}(2)$ and $\text{Sq}(1,1)$,
23
form the matrices
24
25
.. math::
26
27
\begin{Vmatrix}
28
* & 1 & 1 \\
29
2 & 0 & 0
30
\end{Vmatrix}
31
\quad \text{and} \quad
32
\begin{Vmatrix}
33
* & 0 & 1 \\
34
0 & 1 & 0
35
\end{Vmatrix}
36
37
(The $*$ is the ignored (0,0)-entry of the matrix.) For each such
38
matrix $M$, compute a multinomial coefficient, mod 2: for each
39
diagonal $\{m_{ij}: i+j=n\}$, compute $(\sum m_{i,j}!) / (m_{0,n}!
40
m_{1,n-1}! ... m_{n,0}!)$. Multiply these together for all $n$. (To
41
compute this mod 2, view the entries of the matrix as their base 2
42
expansions; then this coefficient is zero if and only if there is some
43
diagonal containing two numbers which have a summand in common in
44
their base 2 expansion. For example, if 3 and 10 are in the same
45
diagonal, the coefficient is zero, because $3=1+2$ and $10=2+8$: they
46
both have a summand of 2.)
47
48
Now, for each matrix with multinomial coefficient 1, let $t_n$ be
49
the sum of the nth diagonal in the matrix; then
50
51
.. math::
52
53
\text{Sq}(r_1, r_2, ...) \text{Sq}(s_1, s_2, ...) = \sum \text{Sq}(t_1, t_2, ...)
54
55
The function :func:`milnor_multiplication` takes as input two tuples
56
of non-negative integers, $r$ and $s$, which represent
57
$\text{Sq}(r)=\text{Sq}(r_1, r_2, ...)$ and
58
$\text{Sq}(s)=\text{Sq}(s_1, s_2, ...)$; it returns as output a
59
dictionary whose keys are tuples $t=(t_1, t_2, ...)$ of non-negative
60
integers, and for each tuple the associated value is the coefficient
61
of $\text{Sq}(t)$ in the product formula. (Since we are working mod 2,
62
this coefficient is 1 -- if it is zero, the the element is omitted from
63
the dictionary altogether).
64
65
.. rubric:: Milnor multiplication, odd primes
66
67
As for the `p=2` case, see Milnor's paper [Mil] for proofs.
68
69
Fix an odd prime $p$. There are three steps to multiply Milnor basis
70
elements $Q_{f_1} Q_{f_2} ... \mathcal{P}(q_1, q_2, ...)$ and
71
$Q_{g_1} Q_{g_2} ... \mathcal{P}(s_1, s_2,...)$: first, use the formula
72
73
.. math::
74
75
\mathcal{P}(q_1, q_2, ...) Q_k = Q_k \mathcal{P}(q_1, q_2, ...)
76
+ Q_{k+1} \mathcal{P}(q_1 - p^k, q_2, ...)
77
+ Q_{k+2} \mathcal{P}(q_1, q_2 - p^k, ...)
78
+ ...
79
80
Second, use the fact that the $Q_k$'s form an exterior algebra: $Q_k^2 =
81
0$ for all $k$, and if $i \neq j$, then $Q_i$ and $Q_j$ anticommute:
82
$Q_i Q_j = -Q_j Q_i$. After these two steps, the product is a linear
83
combination of terms of the form
84
85
.. math::
86
87
Q_{e_1} Q_{e_2} ... \mathcal{P}(r_1, r_2, ...) \mathcal{P}(s_1, s_2, ...).
88
89
Finally, use Milnor matrices to multiply the pairs of
90
$\mathcal{P}(...)$ terms, as at the prime 2: form all possible
91
matrices $M$ with rows and columns indexed starting at 0, with
92
position (0,0) deleted (or ignored), with $s_i$ equal to the sum of
93
column $i$ for each $i$, and with $r_j$ equal to the weighted sum of
94
row $j$: elements from column $i$ are multiplied by $p^i$. For
95
example when $p=5$, to multiply $\mathcal{P}(5)$ and
96
$\mathcal{P}(1,1)$, form the matrices
97
98
.. math::
99
100
\begin{Vmatrix}
101
* & 1 & 1 \\
102
5 & 0 & 0
103
\end{Vmatrix}
104
\quad \text{and} \quad
105
\begin{Vmatrix}
106
* & 0 & 1 \\
107
0 & 1 & 0
108
\end{Vmatrix}
109
110
For each such matrix $M$, compute a multinomial coefficient, mod $p$:
111
for each diagonal $\{m_{ij}: i+j=n\}$, compute $(\sum m_{i,j}!) /
112
(m_{0,n}! m_{1,n-1}! ... m_{n,0}!)$. Multiply these together for
113
all $n$.
114
115
Now, for each matrix with nonzero multinomial coefficient $b_M$, let
116
$t_n$ be the sum of the $n$-th diagonal in the matrix; then
117
118
.. math::
119
120
\mathcal{P}(r_1, r_2, ...) \mathcal{P}(s_1, s_2, ...)
121
= \sum b_M \mathcal{P}(t_1, t_2, ...)
122
123
For example when $p=5$, we have
124
125
.. math::
126
127
\mathcal{P}(5) \mathcal{P}(1,1) = \mathcal{P}(6,1) + 2 \mathcal{P}(0,2).
128
129
The function :func:`milnor_multiplication` takes as input two pairs of
130
tuples of non-negative integers, $(g,q)$ and $(f,s)$, which represent
131
$Q_{g_1} Q_{g_2} ... \mathcal{P}(q_1, q_2, ...)$ and
132
$Q_{f_1} Q_{f_2} ... \mathcal{P}(s_1, s_2, ...)$. It returns as output a
133
dictionary whose keys are pairs of tuples $(e,t)$ of non-negative
134
integers, and for each tuple the associated value is the coefficient
135
in the product formula.
136
137
.. rubric:: The Adem relations and admissible sequences
138
139
If `p=2`, then the mod 2 Steenrod algebra is generated by Steenrod
140
squares `\text{Sq}^a` for `a \geq 0` (equal to the Milnor basis element
141
`\text{Sq}(a)`). The *Adem relations* are as follows: if `a < 2b`,
142
143
.. math::
144
145
\text{Sq}^a \text{Sq}^b = \sum_{j=0}^{a/2} \binom{b-j-1}{a-2j} \text{Sq}^{a+b-j} \text{Sq}^j
146
147
A monomial `\text{Sq}^{i_1} \text{Sq}^{i_2} ... \text{Sq}^{i_n}` is called *admissible* if
148
`i_k \geq 2 i_{k+1}` for all `k`. One can use the Adem relations to
149
show that the admissible monomials span the Steenrod algebra, as a
150
vector space; with more work, one can show that the admissible
151
monomials are also linearly independent. They form the *Serre-Cartan*
152
basis for the Steenrod algebra. To multiply a collection of
153
admissible monomials, concatenate them and see if the result is
154
admissible. If it is, you're done. If not, find the first pair `\text{Sq}^a
155
\text{Sq}^b` where it fails to be admissible and apply the Adem relations
156
there. Repeat with the resulting terms. One can prove that this
157
process terminates in a finite number of steps, and therefore gives a
158
procedure for multiplying elements of the Serre-Cartan basis.
159
160
At an odd prime `p`, the Steenrod algebra is generated by the pth
161
power operations `\mathcal{P}^a` (the same as `\mathcal{P}(a)` in the
162
Milnor basis) and the Bockstein operation `\beta` (= `Q_0` in the
163
Milnor basis). The odd primary *Adem relations* are as follows: if `a
164
< pb`,
165
166
.. math::
167
168
\mathcal{P}^a \mathcal{P}^b = \sum_{j=0}^{a/p} (-1)^{a+j}
169
\binom{(b-j)(p-1)-1}{a-pj} \mathcal{P}^{a+b-j} \mathcal{P}^j
170
171
Also, if `a \leq pb`,
172
173
.. math::
174
175
\mathcal{P}^a \beta \mathcal{P}^b = \sum_{j=0}^{a/p} (-1)^{a+j}
176
\binom{(b-j)(p-1)}{a-pj} \beta \mathcal{P}^{a+b-j} \mathcal{P}^j +
177
\sum_{j=0}^{a/p} (-1)^{a+j-1} \binom{(b-j)(p-1)-1}{a-pj-1}
178
\mathcal{P}^{a+b-j} \beta \mathcal{P}^j
179
180
The *admissible* monomials at an odd prime are products of the form
181
182
.. math::
183
184
\beta^{\epsilon_0} \mathcal{P}^{s_1} \beta^{\epsilon_1}
185
\mathcal{P}^{s_2} ... \mathcal{P}^{s_n} \beta^{\epsilon_n}
186
187
where `s_k \geq \epsilon_{k+1} + p s_{k+1}` for all `k`. As at the
188
prime 2, these form a basis for the Steenrod algebra.
189
190
The main function for this is :func:`make_mono_admissible_` (and in
191
practice, one should use the cached version,
192
``make_mono_admissible``), which converts a product of Steenrod
193
squares or pth power operations and Bocksteins into a dictionary
194
representing a sum of admissible monomials.
195
196
REFERENCES:
197
198
- [Mil] J. W. Milnor, "The Steenrod algebra and its dual", Ann. of Math.
199
(2) 67 (1958), 150--171.
200
201
- [SE] N. E. Steenrod, "Cohomology operations (Lectures by
202
N. E. Steenrod written and revised by D. B. A. Epstein)". Annals of
203
Mathematics Studies, No. 50, 1962, Princeton University Press.
204
"""
205
206
#*****************************************************************************
207
# Copyright (C) 2008-2010 John H. Palmieri <[email protected]>
208
# Distributed under the terms of the GNU General Public License (GPL)
209
#*****************************************************************************
210
211
from sage.misc.cachefunc import cached_function
212
213
# Milnor, p=2
214
215
def milnor_multiplication(r,s):
216
r"""
217
Product of Milnor basis elements r and s at the prime 2.
218
219
INPUT:
220
221
- r - tuple of non-negative integers
222
- s - tuple of non-negative integers
223
224
OUTPUT:
225
226
Dictionary of terms of the form (tuple: coeff), where
227
'tuple' is a tuple of non-negative integers and 'coeff' is 1.
228
229
This computes Milnor matrices for the product of $\text{Sq}(r)$
230
and $\text{Sq}(s)$, computes their multinomial coefficients, and
231
for each matrix whose coefficient is 1, add $\text{Sq}(t)$ to the
232
output, where $t$ is the tuple formed by the diagonals sums from
233
the matrix.
234
235
EXAMPLES::
236
237
sage: from sage.algebras.steenrod.steenrod_algebra_mult import milnor_multiplication
238
sage: milnor_multiplication((2,), (1,))
239
{(0, 1): 1, (3,): 1}
240
sage: milnor_multiplication((4,), (2,1))
241
{(6, 1): 1, (0, 3): 1, (2, 0, 1): 1}
242
sage: milnor_multiplication((2,4), (0,1))
243
{(2, 5): 1, (2, 0, 0, 1): 1}
244
245
These examples correspond to the following product computations:
246
247
.. math::
248
249
\text{Sq}(2) \text{Sq}(1) = \text{Sq}(0,1) + \text{Sq}(3)
250
251
\text{Sq}(4) \text{Sq}(2,1) = \text{Sq}(6,1) + \text{Sq}(0,3) + \text{Sq}(2,0,1)
252
253
\text{Sq}(2,4) \text{Sq}(0,1) = \text{Sq}(2, 5) + \text{Sq}(2, 0, 0, 1)
254
255
This uses the same algorithm Monks does in his Maple package: see
256
http://mathweb.scranton.edu/monks/software/Steenrod/steen.html.
257
"""
258
result = {}
259
rows = len(r) + 1
260
cols = len(s) + 1
261
diags = len(r) + len(s)
262
# initialize matrix
263
M = range(rows)
264
for i in range(rows):
265
M[i] = [0]*cols
266
for j in range(1,cols):
267
M[0][j] = s[j-1]
268
for i in range(1,rows):
269
M[i][0] = r[i-1]
270
for j in range(1,cols):
271
M[i][j] = 0
272
found = True
273
while found:
274
# check diagonals
275
n = 1
276
okay = 1
277
diagonal = [0]*diags
278
while n <= diags and okay is not None:
279
nth_diagonal = [M[i][n-i] for i in range(max(0,n-cols+1), min(1+n,rows))]
280
okay = multinomial(nth_diagonal)
281
diagonal[n-1] = okay
282
n = n + 1
283
if okay is not None:
284
i = diags - 1
285
while i >= 0 and diagonal[i] == 0:
286
i = i - 1
287
t = tuple(diagonal[:i+1])
288
# reduce mod two:
289
if t in result:
290
del result[t]
291
else:
292
result[t] = 1
293
# now look for new matrices:
294
found = False
295
i = 1
296
while not found and i < rows:
297
sum = M[i][0]
298
j = 1
299
while not found and j < cols:
300
# check to see if column index j is small enough
301
if sum >= 2**j:
302
# now check to see if there's anything above this entry
303
# to add to it
304
temp_col_sum = 0
305
for k in range(i):
306
temp_col_sum += M[k][j]
307
if temp_col_sum != 0:
308
found = True
309
for row in range(1,i):
310
M[row][0] = r[row-1]
311
for col in range(1,cols):
312
M[0][col] = M[0][col] + M[row][col]
313
M[row][col] = 0
314
for col in range(1,j):
315
M[0][col] = M[0][col] + M[i][col]
316
M[i][col] = 0
317
M[0][j] = M[0][j] - 1
318
M[i][j] = M[i][j] + 1
319
M[i][0] = sum - 2**j
320
else:
321
sum = sum + M[i][j] * 2**j
322
else:
323
sum = sum + M[i][j] * 2**j
324
j = j + 1
325
i = i + 1
326
return result
327
328
def multinomial(list):
329
r"""
330
Multinomial coefficient of list, mod 2.
331
332
INPUT:
333
334
- list - list of integers
335
336
OUTPUT:
337
338
None if the multinomial coefficient is 0, or sum of list if it is 1
339
340
Given the input $[n_1, n_2, n_3, ...]$, this computes the
341
multinomial coefficient $(n_1 + n_2 + n_3 + ...)! / (n_1! n_2!
342
n_3! ...)$, mod 2. The method is roughly this: expand each
343
$n_i$ in binary. If there is a 1 in the same digit for any $n_i$
344
and $n_j$ with $i\neq j$, then the coefficient is 0; otherwise, it
345
is 1.
346
347
EXAMPLES::
348
349
sage: from sage.algebras.steenrod.steenrod_algebra_mult import multinomial
350
sage: multinomial([1,2,4])
351
7
352
sage: multinomial([1,2,5])
353
sage: multinomial([1,2,12,192,256])
354
463
355
356
This function does not compute any factorials, so the following are
357
actually reasonable to do::
358
359
sage: multinomial([1,65536])
360
65537
361
sage: multinomial([4,65535])
362
sage: multinomial([32768,65536])
363
98304
364
"""
365
old_sum = list[0]
366
okay = True
367
i = 1
368
while okay and i < len(list):
369
j = 1
370
while okay and j <= min(old_sum, list[i]):
371
if j & old_sum == j:
372
okay = (j & list[i] == 0)
373
j = j << 1
374
old_sum = old_sum + list[i]
375
i = i + 1
376
if okay:
377
return old_sum
378
else:
379
return None
380
381
# Milnor, p odd
382
383
def milnor_multiplication_odd(m1,m2,p):
384
r"""
385
Product of Milnor basis elements defined by m1 and m2 at the odd prime p.
386
387
INPUT:
388
389
- m1 - pair of tuples (e,r), where e is an increasing tuple of
390
non-negative integers and r is a tuple of non-negative integers
391
- m2 - pair of tuples (f,s), same format as m1
392
- p - odd prime number
393
394
OUTPUT:
395
396
Dictionary of terms of the form (tuple: coeff), where 'tuple' is
397
a pair of tuples, as for r and s, and 'coeff' is an integer mod p.
398
399
This computes the product of the Milnor basis elements
400
$Q_{e_1} Q_{e_2} ... P(r_1, r_2, ...)$ and
401
$Q_{f_1} Q_{f_2} ... P(s_1, s_2, ...)$.
402
403
EXAMPLES::
404
405
sage: from sage.algebras.steenrod.steenrod_algebra_mult import milnor_multiplication_odd
406
sage: milnor_multiplication_odd(((0,2),(5,)), ((1,),(1,)), 5)
407
{((0, 1, 2), (0, 1)): 4, ((0, 1, 2), (6,)): 4}
408
sage: milnor_multiplication_odd(((0,2,4),()), ((1,3),()), 7)
409
{((0, 1, 2, 3, 4), ()): 6}
410
sage: milnor_multiplication_odd(((0,2,4),()), ((1,5),()), 7)
411
{((0, 1, 2, 4, 5), ()): 1}
412
sage: milnor_multiplication_odd(((),(6,)), ((),(2,)), 3)
413
{((), (4, 1)): 1, ((), (8,)): 1, ((), (0, 2)): 1}
414
415
These examples correspond to the following product computations:
416
417
.. math::
418
419
p=5: \quad Q_0 Q_2 \mathcal{P}(5) Q_1 \mathcal{P}(1) = 4 Q_0 Q_1 Q_2 \mathcal{P}(0,1) + 4 Q_0 Q_1 Q_2 \mathcal{P}(6)
420
421
p=7: \quad (Q_0 Q_2 Q_4) (Q_1 Q_3) = 6 Q_0 Q_1 Q_2 Q_3 Q_4
422
423
p=7: \quad (Q_0 Q_2 Q_4) (Q_1 Q_5) = Q_0 Q_1 Q_2 Q_3 Q_5
424
425
p=3: \quad \mathcal{P}(6) \mathcal{P}(2) = \mathcal{P}(0,2) + \mathcal{P}(4,1) + \mathcal{P}(8)
426
427
The following used to fail until the trailing zeroes were
428
eliminated in p_mono::
429
430
sage: A = SteenrodAlgebra(3)
431
sage: a = A.P(0,3); b = A.P(12); c = A.Q(1,2)
432
sage: (a+b)*c == a*c + b*c
433
True
434
435
Test that the bug reported in #7212 has been fixed::
436
437
sage: A.P(36,6)*A.P(27,9,81)
438
2 P(13,21,83) + P(14,24,82) + P(17,20,83) + P(25,18,83) + P(26,21,82) + P(36,15,80,1) + P(49,12,83) + 2 P(50,15,82) + 2 P(53,11,83) + 2 P(63,15,81)
439
440
Associativity once failed because of a sign error::
441
442
sage: a,b,c = A.Q_exp(0,1), A.P(3), A.Q_exp(1,1)
443
sage: (a*b)*c == a*(b*c)
444
True
445
446
This uses the same algorithm Monks does in his Maple package to
447
iterate through the possible matrices: see
448
http://mathweb.scranton.edu/monks/software/Steenrod/steen.html.
449
"""
450
from sage.rings.all import GF
451
F = GF(p)
452
(f,s) = m2
453
# First compute Q_e0 Q_e1 ... P(r1, r2, ...) Q_f0 Q_f1 ...
454
# Store results (as dictionary of pairs of tuples) in 'answer'.
455
answer = {m1: F(1)}
456
for k in f:
457
old_answer = answer
458
answer = {}
459
for mono in old_answer:
460
if k not in mono[0]:
461
q_mono = set(mono[0])
462
if len(q_mono) > 0:
463
ind = len(q_mono.intersection(range(k,1+max(q_mono))))
464
else:
465
ind = 0
466
coeff = (-1)**ind * old_answer[mono]
467
lst = list(mono[0])
468
if ind == 0:
469
lst.append(k)
470
else:
471
lst.insert(-ind,k)
472
q_mono = tuple(lst)
473
p_mono = mono[1]
474
answer[(q_mono, p_mono)] = F(coeff)
475
for i in range(1,1+len(mono[1])):
476
if (k+i not in mono[0]) and (p**k <= mono[1][i-1]):
477
q_mono = set(mono[0])
478
if len(q_mono) > 0:
479
ind = len(q_mono.intersection(range(k+i,1+max(q_mono))))
480
else:
481
ind = 0
482
coeff = (-1)**ind * old_answer[mono]
483
lst = list(mono[0])
484
if ind == 0:
485
lst.append(k+i)
486
else:
487
lst.insert(-ind,k+i)
488
q_mono = tuple(lst)
489
p_mono = list(mono[1])
490
p_mono[i-1] = p_mono[i-1] - p**k
491
492
# The next two lines were added so that p_mono won't
493
# have trailing zeros. This makes p_mono uniquely
494
# determined by P(*p_mono).
495
496
while len(p_mono)>0 and p_mono[-1] == 0:
497
p_mono.pop()
498
499
answer[(q_mono, tuple(p_mono))] = F(coeff)
500
# Now for the Milnor matrices. For each entry '(e,r): coeff' in answer,
501
# multiply r with s. Record coefficient for matrix and multiply by coeff.
502
# Store in 'result'.
503
if len(s) == 0:
504
result = answer
505
else:
506
result = {}
507
for (e, r) in answer:
508
old_coeff = answer[(e,r)]
509
# Milnor multiplication for r and s
510
rows = len(r) + 1
511
cols = len(s) + 1
512
diags = len(r) + len(s)
513
# initialize matrix
514
M = range(rows)
515
for i in range(rows):
516
M[i] = [0]*cols
517
for j in range(1,cols):
518
M[0][j] = s[j-1]
519
for i in range(1,rows):
520
M[i][0] = r[i-1]
521
for j in range(1,cols):
522
M[i][j] = 0
523
found = True
524
while found:
525
# check diagonals
526
n = 1
527
coeff = old_coeff
528
diagonal = [0]*diags
529
while n <= diags and coeff != 0:
530
nth_diagonal = [M[i][n-i] for i in range(max(0,n-cols+1), min(1+n,rows))]
531
coeff = coeff * multinomial_odd(nth_diagonal,p)
532
diagonal[n-1] = sum(nth_diagonal)
533
n = n + 1
534
if F(coeff) != 0:
535
i = diags - 1
536
while i >= 0 and diagonal[i] == 0:
537
i = i - 1
538
t = tuple(diagonal[:i+1])
539
if (e,t) in result:
540
result[(e,t)] = F(coeff + result[(e,t)])
541
else:
542
result[(e,t)] = F(coeff)
543
# now look for new matrices:
544
found = False
545
i = 1
546
while not found and i < rows:
547
temp_sum = M[i][0]
548
j = 1
549
while not found and j < cols:
550
# check to see if column index j is small enough
551
if temp_sum >= p**j:
552
# now check to see if there's anything above this entry
553
# to add to it
554
temp_col_sum = 0
555
for k in range(i):
556
temp_col_sum += M[k][j]
557
if temp_col_sum != 0:
558
found = True
559
for row in range(1,i):
560
M[row][0] = r[row-1]
561
for col in range(1,cols):
562
M[0][col] = M[0][col] + M[row][col]
563
M[row][col] = 0
564
for col in range(1,j):
565
M[0][col] = M[0][col] + M[i][col]
566
M[i][col] = 0
567
M[0][j] = M[0][j] - 1
568
M[i][j] = M[i][j] + 1
569
M[i][0] = temp_sum - p**j
570
else:
571
temp_sum += M[i][j] * p**j
572
else:
573
temp_sum += M[i][j] * p**j
574
j = j + 1
575
i = i + 1
576
return result
577
578
def multinomial_odd(list,p):
579
r"""
580
Multinomial coefficient of list, mod p.
581
582
INPUT:
583
584
- list - list of integers
585
- p - a prime number
586
587
OUTPUT:
588
589
Associated multinomial coefficient, mod p
590
591
Given the input $[n_1, n_2, n_3, ...]$, this computes the
592
multinomial coefficient $(n_1 + n_2 + n_3 + ...)! / (n_1! n_2!
593
n_3! ...)$, mod $p$. The method is this: expand each $n_i$ in
594
base $p$: $n_i = \sum_j p^j n_{ij}$. Do the same for the sum of
595
the $n_i$'s, which we call $m$: $m = \sum_j p^j m_j$. Then the
596
multinomial coefficient is congruent, mod $p$, to the product of
597
the multinomial coefficients $m_j! / (n_{1j}! n_{2j}! ...)$.
598
599
Furthermore, any multinomial coefficient $m! / (n_1! n_2! ...)$
600
can be computed as a product of binomial coefficients: it equals
601
602
.. math::
603
604
\binom{n_1}{n_1} \binom{n_1 + n_2}{n_2} \binom{n_1 + n_2 + n_3}{n_3} ...
605
606
This is convenient because Sage's binomial function returns
607
integers, not rational numbers (as would be produced just by
608
dividing factorials).
609
610
EXAMPLES::
611
612
sage: from sage.algebras.steenrod.steenrod_algebra_mult import multinomial_odd
613
sage: multinomial_odd([1,2,4], 2)
614
1
615
sage: multinomial_odd([1,2,4], 7)
616
0
617
sage: multinomial_odd([1,2,4], 11)
618
6
619
sage: multinomial_odd([1,2,4], 101)
620
4
621
sage: multinomial_odd([1,2,4], 107)
622
105
623
"""
624
from sage.rings.all import GF, Integer
625
from sage.rings.arith import binomial
626
n = sum(list)
627
answer = 1
628
F = GF(p)
629
n_expansion = Integer(n).digits(p)
630
list_expansion = [Integer(k).digits(p) for k in list]
631
index = 0
632
while answer != 0 and index < len(n_expansion):
633
multi = F(1)
634
partial_sum = 0
635
for exp in list_expansion:
636
if index < len(exp):
637
partial_sum = partial_sum + exp[index]
638
multi = F(multi * binomial(partial_sum, exp[index]))
639
answer = F(answer * multi)
640
index += 1
641
return answer
642
643
# adem relations, serre-cartan basis, admissible sequences
644
645
def binomial_mod2(n,k):
646
r"""
647
The binomial coefficient `\binom{n}{k}`, computed mod 2.
648
649
INPUT:
650
651
- `n`, `k` - integers
652
653
OUTPUT:
654
655
`n` choose `k`, mod 2
656
657
EXAMPLES::
658
659
sage: from sage.algebras.steenrod.steenrod_algebra_mult import binomial_mod2
660
sage: binomial_mod2(4,2)
661
0
662
sage: binomial_mod2(5,4)
663
1
664
sage: binomial_mod2(3 * 32768, 32768)
665
1
666
sage: binomial_mod2(4 * 32768, 32768)
667
0
668
"""
669
if n < k:
670
return 0
671
elif ((n-k) & k) == 0:
672
return 1
673
else:
674
return 0
675
676
def binomial_modp(n,k,p):
677
r"""
678
The binomial coefficient `\binom{n}{k}`, computed mod `p`.
679
680
INPUT:
681
682
- `n`, `k` - integers
683
- `p` - prime number
684
685
OUTPUT:
686
687
`n` choose `k`, mod `p`
688
689
EXAMPLES::
690
691
sage: from sage.algebras.steenrod.steenrod_algebra_mult import binomial_modp
692
sage: binomial_modp(5,2,3)
693
1
694
sage: binomial_modp(6,2,11) # 6 choose 2 = 15
695
4
696
"""
697
if n < k:
698
return 0
699
return multinomial_odd([n-k, k], p)
700
701
@cached_function
702
def adem(a, b, c=0, p=2):
703
r"""
704
The mod `p` Adem relations
705
706
INPUT:
707
708
- `a`, `b`, `c` (optional) - nonnegative integers, corresponding
709
to either `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`
710
- `p` - positive prime number (optional, default 2)
711
712
OUTPUT:
713
714
a dictionary representing the mod `p` Adem relations
715
applied to `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`.
716
717
.. note::
718
719
Users should use :func:`adem` instead of this function (which
720
has a trailing underscore in its name): :func:`adem`
721
is the cached version of this one, and so will be faster.
722
723
The mod `p` Adem relations for the mod `p` Steenrod algebra are as
724
follows: if `p=2`, then if `a < 2b`,
725
726
.. math::
727
728
\text{Sq}^a \text{Sq}^b = \sum_{j=0}^{a/2} \binom{b-j-1}{a-2j} \text{Sq}^{a+b-j} \text{Sq}^j
729
730
If `p` is odd, then if `a < pb`,
731
732
.. math::
733
734
P^a P^b = \sum_{j=0}^{a/p} (-1)^{a+j} \binom{(b-j)(p-1)-1}{a-pj} P^{a+b-j} P^j
735
736
Also for `p` odd, if `a \leq pb`,
737
738
.. math::
739
740
P^a \beta P^b = \sum_{j=0}^{a/p} (-1)^{a+j} \binom{(b-j)(p-1)}{a-pj} \beta P^{a+b-j} P^j
741
+ \sum_{j=0}^{a/p} (-1)^{a+j-1} \binom{(b-j)(p-1)-1}{a-pj-1} P^{a+b-j} \beta P^j
742
743
EXAMPLES:
744
745
If two arguments (`a` and `b`) are given, then computations are
746
done mod 2. If `a \geq 2b`, then the dictionary {(a,b): 1} is
747
returned. Otherwise, the right side of the mod 2 Adem relation
748
for `\text{Sq}^a \text{Sq}^b` is returned. For example, since
749
`\text{Sq}^2 \text{Sq}^2 = \text{Sq}^3 \text{Sq}^1`, we have::
750
751
sage: from sage.algebras.steenrod.steenrod_algebra_mult import adem
752
sage: adem(2,2) # indirect doctest
753
{(3, 1): 1}
754
sage: adem(4,2)
755
{(4, 2): 1}
756
sage: adem(4,4)
757
{(6, 2): 1, (7, 1): 1}
758
759
If `p` is given and is odd, then with two inputs `a` and `b`, the
760
Adem relation for `P^a P^b` is computed. With three inputs `a`,
761
`b`, `c`, the Adem relation for `P^a \beta^b P^c` is computed.
762
In either case, the keys in the output are all tuples of odd length,
763
with ``(i_1, i_2, ..., i_m)`` representing
764
765
.. math::
766
767
\beta^{i_1} P^{i_2} \beta^{i_3} P^{i_4} ... \beta^{i_m}
768
769
For instance::
770
771
sage: adem(3,1, p=3)
772
{(0, 3, 0, 1, 0): 1}
773
sage: adem(3,0,1, p=3)
774
{(0, 3, 0, 1, 0): 1}
775
sage: adem(1,0,1, p=7)
776
{(0, 2, 0): 2}
777
sage: adem(1,1,1, p=5)
778
{(1, 2, 0): 1, (0, 2, 1): 1}
779
sage: adem(1,1,2, p=5)
780
{(1, 3, 0): 2, (0, 3, 1): 1}
781
"""
782
if p == 2:
783
if b == 0: return {(a,): 1}
784
elif a == 0: return {(b,): 1}
785
elif a >= 2*b: return {(a,b): 1}
786
result = {}
787
for c in range(1+a/2):
788
if binomial_mod2(b-c-1, a-2*c) == 1:
789
if c == 0:
790
result[(a+b,)] = 1
791
else:
792
result[(a+b-c,c)] = 1
793
return result
794
# p odd
795
if a == 0 and b == 0:
796
return {(c,): 1}
797
if c == 0:
798
bockstein = 0
799
A = a
800
B = b
801
else:
802
A = a
803
B = c
804
bockstein = b # should be 0 or 1
805
if A == 0:
806
return {(bockstein, B, 0): 1}
807
if B == 0:
808
return {(0, A, bockstein): 1}
809
if bockstein == 0:
810
if A >= p*B: # admissible
811
return {(0,A,0,B,0): 1}
812
result = {}
813
for j in range(1 + int(a/p)):
814
coeff = (-1)**(A+j) * binomial_modp((B-j) * (p-1) - 1, A - p*j, p)
815
if coeff % p != 0:
816
if j == 0:
817
result[(0,A+B,0)] = coeff
818
else:
819
result[(0,A+B-j,0,j,0)] = coeff
820
else:
821
if A >= p*B + 1: # admissible
822
return {(0,A,1,B,0): 1}
823
result = {}
824
for j in range(1 + int(a/p)):
825
coeff = (-1)**(A+j) * binomial_modp((B-j) * (p-1), A - p*j, p)
826
if coeff % p != 0:
827
if j == 0:
828
result[(1,A+B,0)] = coeff
829
else:
830
result[(1,A+B-j,0,j,0)] = coeff
831
for j in range(1 + int((a-1)/p)):
832
coeff = (-1)**(A+j-1) * binomial_modp((B-j) * (p-1) - 1, A - p*j - 1, p)
833
if coeff % p != 0:
834
if j == 0:
835
result[(0,A+B,1)] = coeff
836
else:
837
result[(0,A+B-j,1,j,0)] = coeff
838
return result
839
840
@cached_function
841
def make_mono_admissible(mono, p=2):
842
r"""
843
Given a tuple ``mono``, view it as a product of Steenrod
844
operations, and return a dictionary giving data equivalent to
845
writing that product as a linear combination of admissible
846
monomials.
847
848
When `p=2`, the sequence (and hence the corresponding monomial)
849
`(i_1, i_2, ...)` is admissible if `i_j \geq 2 i_{j+1}` for all
850
`j`.
851
852
When `p` is odd, the sequence `(e_1, i_1, e_2, i_2, ...)` is
853
admissible if `i_j \geq e_{j+1} + p i_{j+1}` for all `j`.
854
855
INPUT:
856
857
- ``mono`` - a tuple of non-negative integers
858
- `p` - prime number, optional (default 2)
859
860
OUTPUT:
861
862
Dictionary of terms of the form (tuple: coeff), where
863
'tuple' is an admissible tuple of non-negative integers and
864
'coeff' is its coefficient. This corresponds to a linear
865
combination of admissible monomials. When `p` is odd, each tuple
866
must have an odd length: it should be of the form `(e_1, i_1, e_2,
867
i_2, ..., e_k)` where each `e_j` is either 0 or 1 and each `i_j`
868
is a positive integer: this corresponds to the admissible monomial
869
870
.. math::
871
872
\beta^{e_1} \mathcal{P}^{i_2} \beta^{e_2} \mathcal{P}^{i_2} ...
873
\mathcal{P}^{i_k} \beta^{e_k}
874
875
ALGORITHM:
876
877
Given `(i_1, i_2, i_3, ...)`, apply the Adem relations to the first
878
pair (or triple when `p` is odd) where the sequence is inadmissible,
879
and then apply this function recursively to each of the resulting
880
tuples `(i_1, ..., i_{j-1}, NEW, i_{j+2}, ...)`, keeping track of
881
the coefficients.
882
883
.. note::
884
885
Users should use :func:`make_mono_admissible` instead of this
886
function (which has a trailing underscore in its name):
887
:func:`make_mono_admissible` is the cached version of this
888
one, and so will be faster.
889
890
EXAMPLES::
891
892
sage: from sage.algebras.steenrod.steenrod_algebra_mult import make_mono_admissible
893
sage: make_mono_admissible((12,)) # already admissible, indirect doctest
894
{(12,): 1}
895
sage: make_mono_admissible((2,1)) # already admissible
896
{(2, 1): 1}
897
sage: make_mono_admissible((2,2))
898
{(3, 1): 1}
899
sage: make_mono_admissible((2, 2, 2))
900
{(5, 1): 1}
901
sage: make_mono_admissible((0, 2, 0, 1, 0), p=7)
902
{(0, 3, 0): 3}
903
904
Test the fix from :trac:`13796`::
905
906
sage: SteenrodAlgebra(p=2, basis='adem').Q(2) * (Sq(6) * Sq(2)) # indirect doctest
907
Sq^10 Sq^4 Sq^1 + Sq^10 Sq^5 + Sq^12 Sq^3 + Sq^13 Sq^2
908
"""
909
from sage.rings.all import GF
910
F = GF(p)
911
if len(mono) == 1:
912
return {mono: 1}
913
if p==2 and len(mono) == 2:
914
return adem(*mono, p=p)
915
if p == 2:
916
# check to see if admissible:
917
admissible = True
918
for j in range(len(mono)-1):
919
if mono[j] < 2*mono[j+1]:
920
admissible = False
921
break
922
if admissible:
923
return {mono: 1}
924
# else j is the first index where admissibility fails
925
ans = {}
926
y = adem(mono[j], mono[j+1])
927
for x in y:
928
new = mono[:j] + x + mono[j+2:]
929
new = make_mono_admissible(new)
930
for m in new:
931
if m in ans:
932
ans[m] = ans[m] + y[x] * new[m]
933
if F(ans[m]) == 0:
934
del ans[m]
935
else:
936
ans[m] = y[x] * new[m]
937
return ans
938
# p odd
939
# check to see if admissible:
940
admissible = True
941
for j in range(1, len(mono)-2, 2):
942
if mono[j] < mono[j+1] + p*mono[j+2]:
943
admissible = False
944
break
945
if admissible:
946
return {mono: 1}
947
# else j is the first index where admissibility fails
948
ans = {}
949
y = adem(*mono[j:j+3], p=p)
950
for x in y:
951
new_x = list(x)
952
new_x[0] = mono[j-1] + x[0]
953
if len(mono) >= j+3:
954
new_x[-1] = mono[j+3] + x[-1]
955
if new_x[0] <= 1 and new_x[-1] <= 1:
956
new = mono[:j-1] + tuple(new_x) + mono[j+4:]
957
new = make_mono_admissible(new, p)
958
for m in new:
959
if m in ans:
960
ans[m] = ans[m] + y[x] * new[m]
961
if F(ans[m]) == 0:
962
del ans[m]
963
else:
964
ans[m] = y[x] * new[m]
965
return ans
966
967