Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/algebras/steenrod/steenrod_algebra_mult.py
4145 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
This uses the same algorithm Monks does in his Maple package to
441
iterate through the possible matrices: see
442
http://mathweb.scranton.edu/monks/software/Steenrod/steen.html.
443
"""
444
from sage.rings.all import GF
445
F = GF(p)
446
(f,s) = m2
447
# First compute Q_e0 Q_e1 ... P(r1, r2, ...) Q_f0 Q_f1 ...
448
# Store results (as dictionary of pairs of tuples) in 'answer'.
449
answer = {m1: F(1)}
450
for k in f:
451
old_answer = answer
452
answer = {}
453
for mono in old_answer:
454
if k not in mono[0]:
455
q_mono = set(mono[0])
456
if len(q_mono) > 0:
457
ind = len(q_mono.intersection(range(k,1+max(q_mono))))
458
else:
459
ind = 0
460
coeff = (-1)**ind * old_answer[mono]
461
lst = list(mono[0])
462
if ind == 0:
463
lst.append(k)
464
else:
465
lst.insert(-ind,k)
466
q_mono = tuple(lst)
467
p_mono = mono[1]
468
answer[(q_mono, p_mono)] = F(coeff)
469
for i in range(1,1+len(mono[1])):
470
if (k+i not in mono[0]) and (p**k <= mono[1][i-1]):
471
q_mono = set(mono[0])
472
if len(q_mono) > 0:
473
ind = len(q_mono.intersection(range(k+i,1+max(q_mono))))
474
else:
475
ind = 0
476
coeff = (-1)**ind
477
lst = list(mono[0])
478
if ind == 0:
479
lst.append(k+i)
480
else:
481
lst.insert(-ind,k+i)
482
q_mono = tuple(lst)
483
p_mono = list(mono[1])
484
p_mono[i-1] = p_mono[i-1] - p**k
485
486
# The next two lines were added so that p_mono won't
487
# have trailing zeros. This makes p_mono uniquely
488
# determined by P(*p_mono).
489
490
while len(p_mono)>0 and p_mono[-1] == 0:
491
p_mono.pop()
492
493
answer[(q_mono, tuple(p_mono))] = F(coeff)
494
# Now for the Milnor matrices. For each entry '(e,r): coeff' in answer,
495
# multiply r with s. Record coefficient for matrix and multiply by coeff.
496
# Store in 'result'.
497
if len(s) == 0:
498
result = answer
499
else:
500
result = {}
501
for (e, r) in answer:
502
old_coeff = answer[(e,r)]
503
# Milnor multiplication for r and s
504
rows = len(r) + 1
505
cols = len(s) + 1
506
diags = len(r) + len(s)
507
# initialize matrix
508
M = range(rows)
509
for i in range(rows):
510
M[i] = [0]*cols
511
for j in range(1,cols):
512
M[0][j] = s[j-1]
513
for i in range(1,rows):
514
M[i][0] = r[i-1]
515
for j in range(1,cols):
516
M[i][j] = 0
517
found = True
518
while found:
519
# check diagonals
520
n = 1
521
coeff = old_coeff
522
diagonal = [0]*diags
523
while n <= diags and coeff != 0:
524
nth_diagonal = [M[i][n-i] for i in range(max(0,n-cols+1), min(1+n,rows))]
525
coeff = coeff * multinomial_odd(nth_diagonal,p)
526
diagonal[n-1] = sum(nth_diagonal)
527
n = n + 1
528
if F(coeff) != 0:
529
i = diags - 1
530
while i >= 0 and diagonal[i] == 0:
531
i = i - 1
532
t = tuple(diagonal[:i+1])
533
if (e,t) in result:
534
result[(e,t)] = F(coeff + result[(e,t)])
535
else:
536
result[(e,t)] = F(coeff)
537
# now look for new matrices:
538
found = False
539
i = 1
540
while not found and i < rows:
541
temp_sum = M[i][0]
542
j = 1
543
while not found and j < cols:
544
# check to see if column index j is small enough
545
if temp_sum >= p**j:
546
# now check to see if there's anything above this entry
547
# to add to it
548
temp_col_sum = 0
549
for k in range(i):
550
temp_col_sum += M[k][j]
551
if temp_col_sum != 0:
552
found = True
553
for row in range(1,i):
554
M[row][0] = r[row-1]
555
for col in range(1,cols):
556
M[0][col] = M[0][col] + M[row][col]
557
M[row][col] = 0
558
for col in range(1,j):
559
M[0][col] = M[0][col] + M[i][col]
560
M[i][col] = 0
561
M[0][j] = M[0][j] - 1
562
M[i][j] = M[i][j] + 1
563
M[i][0] = temp_sum - p**j
564
else:
565
temp_sum += M[i][j] * p**j
566
else:
567
temp_sum += M[i][j] * p**j
568
j = j + 1
569
i = i + 1
570
return result
571
572
def multinomial_odd(list,p):
573
r"""
574
Multinomial coefficient of list, mod p.
575
576
INPUT:
577
578
- list - list of integers
579
- p - a prime number
580
581
OUTPUT:
582
583
Associated multinomial coefficient, mod p
584
585
Given the input $[n_1, n_2, n_3, ...]$, this computes the
586
multinomial coefficient $(n_1 + n_2 + n_3 + ...)! / (n_1! n_2!
587
n_3! ...)$, mod $p$. The method is this: expand each $n_i$ in
588
base $p$: $n_i = \sum_j p^j n_{ij}$. Do the same for the sum of
589
the $n_i$'s, which we call $m$: $m = \sum_j p^j m_j$. Then the
590
multinomial coefficient is congruent, mod $p$, to the product of
591
the multinomial coefficients $m_j! / (n_{1j}! n_{2j}! ...)$.
592
593
Furthermore, any multinomial coefficient $m! / (n_1! n_2! ...)$
594
can be computed as a product of binomial coefficients: it equals
595
596
.. math::
597
598
\binom{n_1}{n_1} \binom{n_1 + n_2}{n_2} \binom{n_1 + n_2 + n_3}{n_3} ...
599
600
This is convenient because Sage's binomial function returns
601
integers, not rational numbers (as would be produced just by
602
dividing factorials).
603
604
EXAMPLES::
605
606
sage: from sage.algebras.steenrod.steenrod_algebra_mult import multinomial_odd
607
sage: multinomial_odd([1,2,4], 2)
608
1
609
sage: multinomial_odd([1,2,4], 7)
610
0
611
sage: multinomial_odd([1,2,4], 11)
612
6
613
sage: multinomial_odd([1,2,4], 101)
614
4
615
sage: multinomial_odd([1,2,4], 107)
616
105
617
"""
618
from sage.rings.all import GF, Integer
619
from sage.rings.arith import binomial
620
n = sum(list)
621
answer = 1
622
F = GF(p)
623
n_expansion = Integer(n).digits(p)
624
list_expansion = [Integer(k).digits(p) for k in list]
625
index = 0
626
while answer != 0 and index < len(n_expansion):
627
multi = F(1)
628
partial_sum = 0
629
for exp in list_expansion:
630
if index < len(exp):
631
partial_sum = partial_sum + exp[index]
632
multi = F(multi * binomial(partial_sum, exp[index]))
633
answer = F(answer * multi)
634
index += 1
635
return answer
636
637
# adem relations, serre-cartan basis, admissible sequences
638
639
def binomial_mod2(n,k):
640
r"""
641
The binomial coefficient `\binom{n}{k}`, computed mod 2.
642
643
INPUT:
644
645
- `n`, `k` - integers
646
647
OUTPUT:
648
649
`n` choose `k`, mod 2
650
651
EXAMPLES::
652
653
sage: from sage.algebras.steenrod.steenrod_algebra_mult import binomial_mod2
654
sage: binomial_mod2(4,2)
655
0
656
sage: binomial_mod2(5,4)
657
1
658
sage: binomial_mod2(3 * 32768, 32768)
659
1
660
sage: binomial_mod2(4 * 32768, 32768)
661
0
662
"""
663
if n < k:
664
return 0
665
elif ((n-k) & k) == 0:
666
return 1
667
else:
668
return 0
669
670
def binomial_modp(n,k,p):
671
r"""
672
The binomial coefficient `\binom{n}{k}`, computed mod `p`.
673
674
INPUT:
675
676
- `n`, `k` - integers
677
- `p` - prime number
678
679
OUTPUT:
680
681
`n` choose `k`, mod `p`
682
683
EXAMPLES::
684
685
sage: from sage.algebras.steenrod.steenrod_algebra_mult import binomial_modp
686
sage: binomial_modp(5,2,3)
687
1
688
sage: binomial_modp(6,2,11) # 6 choose 2 = 15
689
4
690
"""
691
if n < k:
692
return 0
693
return multinomial_odd([n-k, k], p)
694
695
@cached_function
696
def adem(a, b, c=0, p=2):
697
r"""
698
The mod `p` Adem relations
699
700
INPUT:
701
702
- `a`, `b`, `c` (optional) - nonnegative integers, corresponding
703
to either `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`
704
- `p` - positive prime number (optional, default 2)
705
706
OUTPUT:
707
708
a dictionary representing the mod `p` Adem relations
709
applied to `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`.
710
711
.. note::
712
713
Users should use :func:`adem` instead of this function (which
714
has a trailing underscore in its name): :func:`adem`
715
is the cached version of this one, and so will be faster.
716
717
The mod `p` Adem relations for the mod `p` Steenrod algebra are as
718
follows: if `p=2`, then if `a < 2b`,
719
720
.. math::
721
722
\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
723
724
If `p` is odd, then if `a < pb`,
725
726
.. math::
727
728
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
729
730
Also for `p` odd, if `a \leq pb`,
731
732
.. math::
733
734
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
735
+ \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
736
737
EXAMPLES:
738
739
If two arguments (`a` and `b`) are given, then computations are
740
done mod 2. If `a \geq 2b`, then the dictionary {(a,b): 1} is
741
returned. Otherwise, the right side of the mod 2 Adem relation
742
for `\text{Sq}^a \text{Sq}^b` is returned. For example, since
743
`\text{Sq}^2 \text{Sq}^2 = \text{Sq}^3 \text{Sq}^1`, we have::
744
745
sage: from sage.algebras.steenrod.steenrod_algebra_mult import adem
746
sage: adem(2,2) # indirect doctest
747
{(3, 1): 1}
748
sage: adem(4,2)
749
{(4, 2): 1}
750
sage: adem(4,4)
751
{(6, 2): 1, (7, 1): 1}
752
753
If `p` is given and is odd, then with two inputs `a` and `b`, the
754
Adem relation for `P^a P^b` is computed. With three inputs `a`,
755
`b`, `c`, the Adem relation for `P^a \beta^b P^c` is computed.
756
In either case, the keys in the output are all tuples of odd length,
757
with ``(i_1, i_2, ..., i_m)`` representing
758
759
.. math::
760
761
\beta^{i_1} P^{i_2} \beta^{i_3} P^{i_4} ... \beta^{i_m}
762
763
For instance::
764
765
sage: adem(3,1, p=3)
766
{(0, 3, 0, 1, 0): 1}
767
sage: adem(3,0,1, p=3)
768
{(0, 3, 0, 1, 0): 1}
769
sage: adem(1,0,1, p=7)
770
{(0, 2, 0): 2}
771
sage: adem(1,1,1, p=5)
772
{(1, 2, 0): 1, (0, 2, 1): 1}
773
sage: adem(1,1,2, p=5)
774
{(1, 3, 0): 2, (0, 3, 1): 1}
775
"""
776
if p == 2:
777
if b == 0: return {(a,): 1}
778
elif a == 0: return {(b,): 1}
779
elif a >= 2*b: return {(a,b): 1}
780
result = {}
781
for c in range(1+a/2):
782
if binomial_mod2(b-c-1, a-2*c) == 1:
783
if c == 0:
784
result[(a+b,)] = 1
785
else:
786
result[(a+b-c,c)] = 1
787
return result
788
# p odd
789
if a == 0 and b == 0:
790
return {(c,): 1}
791
if c == 0:
792
bockstein = 0
793
A = a
794
B = b
795
else:
796
A = a
797
B = c
798
bockstein = b # should be 0 or 1
799
if A == 0:
800
return {(bockstein, B, 0): 1}
801
if B == 0:
802
return {(0, A, bockstein): 1}
803
if bockstein == 0:
804
if A >= p*B: # admissible
805
return {(0,A,0,B,0): 1}
806
result = {}
807
for j in range(1 + int(a/p)):
808
coeff = (-1)**(A+j) * binomial_modp((B-j) * (p-1) - 1, A - p*j, p)
809
if coeff % p != 0:
810
if j == 0:
811
result[(0,A+B,0)] = coeff
812
else:
813
result[(0,A+B-j,0,j,0)] = coeff
814
else:
815
if A >= p*B + 1: # admissible
816
return {(0,A,1,B,0): 1}
817
result = {}
818
for j in range(1 + int(a/p)):
819
coeff = (-1)**(A+j) * binomial_modp((B-j) * (p-1), A - p*j, p)
820
if coeff % p != 0:
821
if j == 0:
822
result[(1,A+B,0)] = coeff
823
else:
824
result[(1,A+B-j,0,j,0)] = coeff
825
for j in range(1 + int((a-1)/p)):
826
coeff = (-1)**(A+j-1) * binomial_modp((B-j) * (p-1) - 1, A - p*j - 1, p)
827
if coeff % p != 0:
828
if j == 0:
829
result[(0,A+B,1)] = coeff
830
else:
831
result[(0,A+B-j,1,j,0)] = coeff
832
return result
833
834
@cached_function
835
def make_mono_admissible(mono, p=2):
836
r"""
837
Given a tuple ``mono``, view it as a product of Steenrod
838
operations, and return a dictionary giving data equivalent to
839
writing that product as a linear combination of admissible
840
monomials.
841
842
When `p=2`, the sequence (and hence the corresponding monomial)
843
`(i_1, i_2, ...)` is admissible if `i_j \geq 2 i_{j+1}` for all
844
`j`.
845
846
When `p` is odd, the sequence `(e_1, i_1, e_2, i_2, ...)` is
847
admissible if `i_j \geq e_{j+1} + p i_{j+1}` for all `j`.
848
849
INPUT:
850
851
- ``mono`` - a tuple of non-negative integers
852
- `p` - prime number, optional (default 2)
853
854
OUTPUT:
855
856
Dictionary of terms of the form (tuple: coeff), where
857
'tuple' is an admissible tuple of non-negative integers and
858
'coeff' is its coefficient. This corresponds to a linear
859
combination of admissible monomials. When `p` is odd, each tuple
860
must have an odd length: it should be of the form `(e_1, i_1, e_2,
861
i_2, ..., e_k)` where each `e_j` is either 0 or 1 and each `i_j`
862
is a positive integer: this corresponds to the admissible monomial
863
864
.. math::
865
866
\beta^{e_1} \mathcal{P}^{i_2} \beta^{e_2} \mathcal{P}^{i_2} ...
867
\mathcal{P}^{i_k} \beta^{e_k}
868
869
ALGORITHM:
870
871
Given `(i_1, i_2, i_3, ...)`, apply the Adem relations to the first
872
pair (or triple when `p` is odd) where the sequence is inadmissible,
873
and then apply this function recursively to each of the resulting
874
tuples `(i_1, ..., i_{j-1}, NEW, i_{j+2}, ...)`, keeping track of
875
the coefficients.
876
877
.. note::
878
879
Users should use :func:`make_mono_admissible` instead of this
880
function (which has a trailing underscore in its name):
881
:func:`make_mono_admissible` is the cached version of this
882
one, and so will be faster.
883
884
EXAMPLES::
885
886
sage: from sage.algebras.steenrod.steenrod_algebra_mult import make_mono_admissible
887
sage: make_mono_admissible((12,)) # already admissible, indirect doctest
888
{(12,): 1}
889
sage: make_mono_admissible((2,1)) # already admissible
890
{(2, 1): 1}
891
sage: make_mono_admissible((2,2))
892
{(3, 1): 1}
893
sage: make_mono_admissible((2, 2, 2))
894
{(5, 1): 1}
895
sage: make_mono_admissible((0, 2, 0, 1, 0), p=7)
896
{(0, 3, 0): 3}
897
"""
898
from sage.rings.all import GF
899
if len(mono) == 1:
900
return {mono: 1}
901
if p==2 and len(mono) == 2:
902
return adem(*mono, p=p)
903
if p == 2:
904
# check to see if admissible:
905
admissible = True
906
for j in range(len(mono)-1):
907
if mono[j] < 2*mono[j+1]:
908
admissible = False
909
break
910
if admissible:
911
return {mono: 1}
912
# else j is the first index where admissibility fails
913
ans = {}
914
y = adem(mono[j], mono[j+1])
915
for x in y:
916
new = mono[:j] + x + mono[j+2:]
917
new = make_mono_admissible(new)
918
for m in new:
919
if m in ans:
920
ans[m] = ans[m] + y[x] * new[m]
921
if F(ans[m]) == 0:
922
del ans[m]
923
else:
924
ans[m] = y[x] * new[m]
925
return ans
926
# p odd
927
# check to see if admissible:
928
admissible = True
929
for j in range(1, len(mono)-2, 2):
930
if mono[j] < mono[j+1] + p*mono[j+2]:
931
admissible = False
932
break
933
if admissible:
934
return {mono: 1}
935
# else j is the first index where admissibility fails
936
ans = {}
937
y = adem(*mono[j:j+3], p=p)
938
for x in y:
939
new_x = list(x)
940
new_x[0] = mono[j-1] + x[0]
941
if len(mono) >= j+3:
942
new_x[-1] = mono[j+3] + x[-1]
943
if new_x[0] <= 1 and new_x[-1] <= 1:
944
new = mono[:j-1] + tuple(new_x) + mono[j+4:]
945
new = make_mono_admissible(new, p)
946
for m in new:
947
if m in ans:
948
ans[m] = ans[m] + y[x] * new[m]
949
if F(ans[m]) == 0:
950
del ans[m]
951
else:
952
ans[m] = y[x] * new[m]
953
return ans
954
955