aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kummer_degree.sage825
1 files changed, 825 insertions, 0 deletions
diff --git a/kummer_degree.sage b/kummer_degree.sage
new file mode 100644
index 0000000..5800559
--- /dev/null
+++ b/kummer_degree.sage
@@ -0,0 +1,825 @@
1# Computes the "adelic Kummer failure", i.e. the degrees of the intersection
2# of the the Kummer extension Q(\sqrt{2^n}{G}) with the M-th cyclotomic field
3# over Q_{2^n}.
4#
5# Input: a good basis B for the torsion-free group G, organized as a list of
6# lists, and a non negative integer d. They have to satisfy the following:
7# 1. Each list B[i] contains all basis elements of 2-divisibility i.
8# 2. The basis given by B is 2-maximal, that is to say it satisfies Theorem 14
9# of Debry-Perucca; i.e. each element of B[i] is, up to plus or minus 1, the
10# 2^i-th power of a strongly 2-indivisible rational.
11# 3. For i != d every element of B[i] is positive, and B[d][0] is the only
12# negative element of B[d] (if d=-1, then there is no negative element).
13# 3'. Notice that the existence on negative elements in B[0] does not influence
14# the correctness of the algorithm; in fact, the function adjust_sign
15# produces a basis that may have negative elements of divisibility 0, and
16# this basis is given as input for adelic_failure_gb.
17#
18# Output: a table ad_fail as described below. Call M0 the smallest positive
19# integer such that the intersection of Q(\sqrt{2^n}{G}) with Q_\infty is
20# contained in Q_M0.
21# The table ad_fail has N rows, where N is defined below.
22# Each row R=ad_fail[i] contains a variable number of pairs (d,r), where d is a
23# divisor of M0 and r is the degree of Q(\sqrt{2^{i+1}}{G}) \cap Q_d over
24# Q_{2^n}.
25# Each divisor of M appears at most once on each row, and the last element of
26# the last row is of the form (M0,r0).
27def adelic_failure_gb( B, d ):
28
29 # The table to be returned (or printed at the end), as described above.
30 ad_fail = []
31
32 # N is such that for every n > N the adelic failure of Q(\sqrt{2^n}{G}) is
33 # the same as that of Q(\sqrt{2^N}{G}).
34 # We always have to include n=3, because of problem with sqrt(2) in Q_8 (in
35 # theory, this is not necessary in some cases, e.g. if 2 does not divide
36 # any element of G).
37 # If the negative generator is on the last level, we need to increase N by
38 # 1, because it would contribute to the shortlist in the next level (by
39 # taking the root of an even power).
40 if d == len(B)-1:
41 N = max(3,len(B)+1)
42 else:
43 N = max(3,len(B))
44
45 # The intersection is given by adding the square roots of the elements of
46 # this shortlist (and a "special element", not always of the form sqrt(d),
47 # coming from taking a suitable root of a negative generator; this special
48 # element is dealt with later). The shortlist grows at each step, so we
49 # declare it before starting to loop over n and we build it incrementally.
50 # The "special element" is of the form \zeta_{2^n}\sqrt{b}, which we encode
51 # as (n,b). We use the value (1,1) (special element -1) to say that there
52 # isno special element at this level.
53 shortlist = []
54 special_element = (1,1)
55
56 # The integers M, giving the smallest cyclotomic field in which lies the
57 # whole intersection with Q_\infty, also grows with n. As with the
58 # shortlist, we declare it here and increase it appropriately at each step.
59 M = 1
60
61 for n in range( 1, N+1 ): # 1 \leq n \leq N
62
63 # We add the new elements to the shortlist, modifying M if needed.
64 # This is not done in case we are in the extra "fake" level (this case
65 # dealt with immediately below).
66 if n-1 < len(B):
67 for g in B[n-1]:
68 # Case of negative g
69 if g < 0 and n > 1:
70 # Special element of the form \zeta_{2^{n+1}}\sqrt(b).
71 # It is contained in Q_{lcm(2^{n+1},cyc_emb(b))}, except in
72 # case n=2 and b=2, in which case it is contained in Q_4.
73 # We store it as as pair ( n+1, b ).
74 special_element = ( n+1, abs(g)^(1/(2^(n-1))) )
75 M = lcm( M, special_embed( special_element ) )
76 else:
77 b = g^(1/(2^(n-1))) # b is 2-indivisible
78 shortlist.append( b )
79 M = lcm( M, cyc_embed(b) )
80
81 # We add a root of an even power of the negative generator, as soon as
82 # we are beyond its level.
83 if d != -1 and n == d+2:
84 b = abs(B[d][0])^(1/2^d)
85 shortlist.append( b )
86 M = lcm( M, cyc_embed(b) )
87
88 M = lcm(M,2^n)
89
90 # Here we account for the extra 2^{n+1} root of unity (in case the
91 # negative element has enough divisibility).
92 if n <= d:
93 M = lcm( M, 2^(n+1) )
94
95 # We have to add -1 to the shortlist (see example [12,36]) and
96 # remove it later.
97 if n == 1 and d >= 1:
98 shortlist.append(-1)
99 if n > 1 and -1 in shortlist:
100 shortlist.remove(-1)
101
102 # For each divisor dM of M, compute the degree of the intersection of
103 # Q(\sqrt{2^n}{G}) with Q_dM over Q_{2^n}. We need to compute the
104 # number r of elements in the subgroup of G generated by the shortlist
105 # that lie in Q_dM. This is going to be a power of 2. The sought degree
106 # will be r, up to considering some special cases (described below).
107 #
108 # This algorithm could be inefficient for groups of big rank. It can be
109 # improved by precomputing subgroups of G/G^2 and the cyclotomic fields
110 # containing them.
111
112 aux = [] # Next line of ad_fail table
113
114 for dM in divisors( M ):
115 # We only care of the intersection with Q_dM if it contains the 2^n
116 # roots of unity.
117 if dM % (2^n) != 0:
118 continue
119
120 # The following line gives, without repetitions, the list of m's
121 # such that that some element in the subgroup of G generated by
122 # the shortlist embeds in Q_m.
123 S = [ product(s) for s in subsets( shortlist ) ]
124 H = [ cyc_embed( s ) for s in S ]
125 r = len( [ b for b in H if dM % b == 0 ] )
126
127 # We double n in case a new roots of unity enters the cyclotomic
128 # due to the negative negerator. In case n=1, this is accounted
129 # by having -1 in the shortlist.
130 if n <= d and dM % (2^(n+1)) == 0 and n > 1:
131 r *= 2
132
133 # We loose a factor of 2 if we have sqrt(2) in Q_8 or \zeta_8 2 in
134 # Q_4.
135 if 8 in H and dM % 8 == 0 and (n >= 3 or (n == 2 and n <= d)):
136 r = r/2
137
138 # If we have a special element in this level, we consider it.
139 # We have to consider all possible special elements arising from
140 # multiplying the given element with the other elements of the
141 # shortlist.
142 # If any of them is of the form \zeta_8 2q for q a square, there is
143 # nothing to do: in fact \zeta_8 2 embeds in Q_4, which coincides
144 # with Q_{2^n} (n must be 2); so we would double the degree because
145 # of the existence of the special element, butthen we would loose
146 # another factor of 2 because of this.
147 if special_element != (1,1) and special_element[0] == n+1:
148 nothing_to_do = False
149 intersecting_QdM = False
150 for s in S:
151 new_special = ( n+1, special_element[1] * s )
152 m = special_embed( new_special )
153 if n == 2 and m == 4: # \zeta_8 times 2 times square
154 nothing_to_do = True
155 if dM % m == 0:
156 intersecting_QdM = True
157 if intersecting_QdM and not nothing_to_do:
158 r *= 2
159
160 aux.append( (dM,r) )
161
162 ad_fail.append(aux)
163
164 return ad_fail
165
166# Returns the smallest m such that \sqrt(b) is in the m-th cyclotomic field.
167def cyc_embed( b ):
168 m = squarefree_part(b)
169 if m%4 != 1:
170 m *= 4
171 return abs(m)
172
173# Computes the minimal cyclotomic field containing \zeta_{2^n}\sqrt(b).
174def special_embed( (n,b) ):
175 m = squarefree_part(b)
176 if n == 3 and m % 2 == 0:
177 return 4 * cyc_embed(m/2)
178 else:
179 return lcm( 2^n, cyc_embed(b) )
180
181# Computes the "l-adic failure", i.e. the ratio between l^{nr} and the degree
182# of Q_{l^m}(\sqrt[2^n](G)) over Q_{l^m} for all possible l.
183# Input: any basis for G
184# Returns a pair (L,T) where:
185# - L is a list of primes l that do not have maximal l-adic part
186# - T is an array of tables, where T[i][m][n] is the l-valuation of the
187# degree of Q_{l^m,l^n} over Q_{l^m}, where l = L[i]
188def total_l_adic_failure( B ):
189
190 bp = bad_primes( B )
191 ret = ( bp, [] )
192
193 for l in bp:
194 ret[1].append( l_adic_failure( B, l ) )
195
196 return ret
197
198# Computes the "bad primes", i.e. the ones for which the l-adic part is not
199# maximal. Used by l_adic_degree and total_l_adic_failure.
200def bad_primes( B ):
201 M = exponent_matrix( B )
202 (a,b) = M.dimensions()
203 if a > b or M.rank() < a:
204 print "This is not a basis"
205 return
206
207 # Compute which primes l divide all minors of the exponent matrix
208 ms = M.minors( a )
209 d = ms[0]
210 for m in ms:
211 d = gcd( d, m )
212 bad_primes = list( prime_factors( d ) )
213 if 2 not in bad_primes:
214 bad_primes += [2] # 2 is always bad
215 bad_primes.sort() # Ensures 2 is always first
216
217 return bad_primes
218
219# Computes the l-adic failure for a specific l. Returns a "table" as described
220# above "total_l_adic_failure".
221# B is any basis for G.
222def l_adic_failure( B, l ):
223
224 r = len(B)
225 GB = make_good_basis( B, l )
226
227 # Computes the parameters over Q4. For l odd, they are the same as over Q.
228 p = parameters_Q4( GB, l )
229 maxM = max( [ sum(x) for x in p ] )
230 maxN = max( maxM, len(GB)-1 )
231
232 maxM = max( 1, maxM )
233 if l == 2:
234 maxM = max( 2, maxM ) # For computing parameters over Q4
235 maxN = max( 1, maxN )
236
237 tabel = []
238 max_failure = 0
239 for m in range( 1, maxM+1 ):
240 row = []
241 for n in range( 1, max( m+1, maxN ) ):
242 vl = -1
243 if m >= n:
244 if l==2 and n==1 and m==1:
245 vl = compute_vl( p, 1, 2, r )
246 if adjust_sign( GB )[1] >= 1:
247 vl += 1
248 else:
249 vl = compute_vl( p, n, m, r )
250 failure = r*n - vl
251 row.append(vl)
252 max_failure = max( max_failure, failure )
253 tabel.append((row,max_failure))
254 return tabel
255
256# Returns a power of l that is the l-adic failure at M, N.
257# tablel must be the output table of l_adic_failure.
258def l_adic_failure_from_data( B, l, tablel, M, N ):
259
260 m = valuation( M, l )
261 n = valuation( N, l )
262 if m < n:
263 # inconsistent choice of M and N
264 return 0
265 if n == 0:
266 return 1
267 r = len(B)
268
269 # Basically, the "dual" of what we do in l_adic_degree.
270 if m > len(tablel):
271 if n > len(tablel):
272 return l^tablel[-1][1]
273 else:
274 return l^(r*n-tablel[-1][0][n-1])
275
276 return l^(r*n-tablel[m-1][0][n-1])
277
278# Computes the l-divisibility parameters of G over Q4, given a good basis b
279# over Q for G. Returns a list of pairs (di,hi).
280# If l is odd it just uses the good basis given to compute the parameters.
281def parameters_Q4( gb, l ):
282 # Converts from "good basis format" to simple list
283 b = []
284 for x in gb:
285 b += x
286 ret = []
287
288 if l != 2:
289 for i in range( len( gb ) ):
290 for j in gb[i]:
291 ret.append( (i,0) )
292 return ret
293 else:
294 R.<y> = PolynomialRing( QQ )
295 pol = R(y^2+1)
296 Q4.<eye> = NumberField( pol ) # I already use i for other things
297
298 # Factorize basis elements over Q4 and so on.
299 d = []
300 B = []
301 h = []
302 ideals_list = set()
303 M = [] # Exponent matrix of the Bi's
304
305 # Pre-process to find all ideals appearing in the factorization and fix
306 # a chosen generator for each of them. This is important in order to
307 # compute the "sign" (h-parameter) of an element with respect to it Bi.
308 for g in b:
309 factorization_list = list( Q4.ideal(g).factor() )
310 ideals_list |= set( [ x[0] for x in factorization_list ] )
311 ideals_list = list( ideals_list )
312 # Chooses a generator of each principal ideal in the list
313 irreducibles_list = [ J.gens_reduced()[0] for J in ideals_list ]
314
315 # Compute the Q4-parameters of the given basis b. Also computes the
316 # exponent matrix of the Bi's
317 for g in b:
318 factorization_list = list( Q4.ideal(g).factor() )
319 exps = [ x[1] for x in factorization_list ]
320 d.append( divisibility( exps, l ) )
321 Bg = 1
322 for j in range(len(factorization_list)):
323 a = 0
324 for i in range( len( ideals_list ) ):
325 if ideals_list[i] == factorization_list[j][0]:
326 a = irreducibles_list[i]
327 break
328 Bg *= a ^ (exps[j]/(l^d[-1]))
329 B.append(Bg)
330 u = g / (Bg^(l^d[-1]))
331 if not u.is_unit():
332 print "Error: g is not the right power of the computed Bg."
333 print "g:", g, ", Bg:", Bg, ", exponent:", l^d[-1]
334 if u == 1:
335 h.append( 0 )
336 elif u == -1:
337 h.append( 1 )
338 else:
339 h.append( 2 )
340
341 # Make the exponent matrix M (for now as a list of rows)
342 for g in B:
343 row = [0] * len(ideals_list)
344 for i in range(len(ideals_list)):
345 I = ideals_list[i]
346 ee = 1
347 while (I^ee).divides(g):
348 ee += 1
349 row[i] = ee-1
350 M.append(row)
351
352 # If the Bi's are not strongly independent, apply the algorithm (only
353 # once) to produce a new basis. The new basis has maximal parameters.
354 coeffs = find_combination( matrix(M), l )
355 if coeffs != []:
356
357 maxi = -1
358 maxd = -1
359 for i in range(len(d)):
360 if d[i] > maxd and coeffs[i] != 0:
361 maxd = d[i]
362 maxi = i
363
364 x = [(a/coeffs[maxi]).lift() for a in coeffs] # Now a vector of int
365
366 new_element = 1
367 for i in range(len(d)):
368 new_element *= b[i]^( x[i] * l^(d[maxi]-d[i]) )
369
370 b[maxi] = new_element
371
372 # Compute new B, d and so on.
373 factorization_list = list( Q4.ideal(b[maxi]).factor() )
374 exps = [ x[1] for x in factorization_list ]
375 d[maxi] = divisibility( exps, l )
376 Bg = 1
377
378 for j in range(len(factorization_list)):
379 a = 0
380 for i in range( len( ideals_list ) ):
381 if ideals_list[i] == factorization_list[j][0]:
382 a = irreducibles_list[i]
383 break
384 Bg *= a ^ (exps[j]/(l^d[maxi]))
385 B[maxi] = Bg
386 M[maxi] = [ x[1] for x in list( Q4.ideal(Bg).factor() ) ]
387 u = b[maxi] / (Bg^(l^d[maxi]))
388 if not u.is_unit():
389 print "Error: new element is not the right power of B."
390 print "New el.:", b[maxi], ", B:", Bg, ", exponent:", l^d[maxi]
391 if u == 1:
392 h[maxi] = 0
393 elif u == -1:
394 h[maxi] = 1
395 else:
396 h[maxi] = 2
397
398 return [(d[i],h[i]) for i in range(len(d))]
399
400# Uses Theorem 18 to compute the degree of Kummer extensions.
401def compute_vl( p, n, m, r ):
402 h = [ x[1] for x in p ]
403 ni = [ min( n, x[0] ) for x in p ]
404 M = max( m, max( [ h[i] + ni[i] for i in range( len( p ) ) ] ) )
405
406 return M - m + r*n - sum( ni )
407
408# Given any basis b of a group G computes an l-good basis for G. This is done
409# using the algorithm outlined in the proof of Theorem 14 of (Debry-Perucca).
410def make_good_basis( b, l ):
411 M = exponent_matrix( b )
412 d = []
413 B = []
414 for i in range(len(b)):
415 di = divisibility( M[i], l )
416 d.append( di )
417 B.append( abs(b[i])^(1/(l^di)) )
418
419 # Computes the coeffiecients of a linear combination of the rows of M
420 # that is zero modulo l. These coefficients are elements of F_l.
421 coeffs = find_combination( exponent_matrix( B ), l )
422
423 while coeffs != []:
424
425 # Computes which basis element (with non-zero coefficient in the linear
426 # combination above) has maximal divisibility.
427 maxi = -1
428 maxd = -1
429 for i in range(len(d)):
430 if d[i] > maxd and coeffs[i] != 0:
431 maxd = d[i]
432 maxi = i
433
434 x = [ (a/coeffs[maxi]).lift() for a in coeffs ] # Now a vector of ints
435
436 new_element = 1
437 for i in range(len(d)):
438 new_element *= b[i]^( x[i] * l^(d[maxi]-d[i]) )
439
440 b[maxi] = new_element
441 M = exponent_matrix( b )
442 d[maxi] = divisibility( M[maxi], l )
443 B[maxi] = abs(b[maxi])^(1/(l^d[maxi]))
444
445 coeffs = find_combination( exponent_matrix( B ), l )
446
447 GB = [[]]
448 for i in range(len(d)):
449 while( len(GB) <= d[i] ):
450 GB.append([])
451 GB[d[i]].append(b[i])
452 return GB
453
454# Takes a good basis B and adjusts the sign of the elements so that there is at
455# most one negative generator (of positive divisibility). The input is a good
456# basis in the format returned by make_good_basis.
457# Returns a pair (B,d), where B is the updated basis and d is the divisibility
458# parameter of the only negative element remained.
459# The sign of the d=0 elements is just ignored in the other steps of the
460# algorithm, so we keep them negative.
461def adjust_sign( B ):
462 neg = 0
463 dret = -1
464 for d in range( len(B)-1, 0, -1 ):
465 for i in range(len(B[d])):
466 if B[d][i] < 0:
467 if neg == 0:
468 neg = B[d][i]
469 dret = d
470 else:
471 B[d][i] *= neg
472 return (B,dret)
473
474# Given the exponent matrix M of a list of rational numbers B, returns the
475# coefficients of a linear combination that is weakly l-divisible, or [] if
476# the B[i] are strongly l-independent.
477def find_combination( M, l ):
478 M = M.change_ring( GF( l ) )
479 if M.rank() != min( M.dimensions() ):
480 return M.kernel().basis()[0]
481 else:
482 return []
483
484# A rational number must be given as a list of exponents of primes in its
485# factorization.
486# Returns the minimal l-valuation of the exponents.
487def divisibility( A, l ):
488 if len(A) == 0:
489 print "Warning: computing the divisibility of a torsion element.",
490 print "Returning +Infinity."
491 return +Infinity
492 return min( [ valuation( x, l ) for x in A ] )
493
494# For a given set of non-zero rationals B, computes the "exponent matrix"
495def exponent_matrix( B ):
496 prime_list = set()
497 for g in B:
498 prime_list |= set( prime_factors( g ) )
499 prime_list = list( prime_list )
500 np = len( prime_list )
501 rows = []
502 for g in B:
503 rowg = [0] * np
504 for f in list( factor( g ) ):
505 for i in range( np ):
506 if f[0] == prime_list[i]:
507 rowg[i] = f[1]
508 rows.append( rowg )
509 return matrix( rows )
510
511# Computing the exponent matrix, but keeping an extra column for the signs.
512# Returns a pair (M,L) where M is the modified exponent matrix and L is the
513# list of primes appearing in the factorization.
514def exponent_matrix_with_sign_and_primes( B ):
515 prime_list = set()
516 for g in B:
517 prime_list |= set( prime_factors( g ) )
518 prime_list = list( prime_list )
519 np = len( prime_list )
520 rows = []
521 for g in B:
522 rowg = [0] * np
523 for f in list( factor( g ) ):
524 for i in range( np ):
525 if f[0] == prime_list[i]:
526 rowg[i] = f[1]
527 s = 0
528 if sgn(g) == -1:
529 s = 1
530 rowg.append( s )
531 rows.append( rowg )
532 return ( matrix( rows ), prime_list )
533
534# This is a wrapper function for total_kummer_failure( G, True ), see below.
535def TotalKummerFailure( G ):
536 total_kummer_failure( G, True )
537
538# Input: any set of generators for a subgroup G of Q*.
539# If output=False, returns a 4-uple (t,MM,NN,D):
540# - t is a pair, where t[0] is the rank of G and t[1] is either True (if G has
541# torsion) or False (is it does not).
542# - MM is the pair (M0,divisors(M0))
543# - NN is the pair (N0,divisors(N0))
544# - D is a table F, where F[j][i] is the ration between phi(m)n^r and the
545# degree of Q_{m,n} (i.e., the Kummer failure at m,n, where m is the i-th
546# divisor of M0 and n is the j-th divisor of N0 (in the computed list))
547# computed for a torsion-free part of G.
548# If output is True, outputs this data in a human-readable way and does not
549# return any value.
550def total_kummer_failure( G, output ):
551
552 # Computing a basis.
553 (BM,BM_primes) = exponent_matrix_with_sign_and_primes( G )
554 BM = BM.echelon_form()
555 BM_primes.append(-1)
556 B = []
557 torsion = False
558
559 for r in BM.rows():
560 gr = product( [ BM_primes[i]^r[i] for i in range(len(r)) ] )
561 if gr == -1:
562 torsion = True
563 break
564 elif gr == 1:
565 break
566 else:
567 B.append(gr)
568
569 if len(B) == 0:
570 print "G is torsion. The extension is cyclotomic. Stopping."
571 return False
572
573 r = len(B) # Rank of G
574
575 # Compute l-adic data (straightforward)
576 ( bad_primes, l_adic_failure_table ) = total_l_adic_failure( B )
577
578 # Compute adelic data.
579 (GB,d) = adjust_sign( make_good_basis( B, 2 ) )
580 adelic_failure_table = adelic_failure_gb( GB, d )
581
582 # Computing the bounds M0 and N0
583 N0 = 1
584 for i in range(len( bad_primes )):
585 N0 *= bad_primes[i] ^ len( l_adic_failure_table[i][-1][0] )
586 # Extra factors of 2 may come from the adelic failure
587 N0 = lcm( N0, 2^len( adelic_failure_table ) )
588 divs_N0 = divisors(N0)
589
590 # greatest M appearing in the adelic failure table
591 M0 = adelic_failure_table[-1][-1][0]
592 divs_M0 = divisors(M0)
593
594 # Failure Table
595 FT = [ [ 1 for d1 in divisors(M0) ] for d2 in divisors(N0) ]
596
597 # Takes the adelic failure from the the relative table
598 for i in range(1,len(adelic_failure_table)+1):
599 for pp in adelic_failure_table[i-1]:
600 # Runs through all divisors of M0 and N0 that may have this
601 # failure (the table is not "complete").
602 for l in range(len(divs_M0)):
603 for j in range(len(divs_N0)):
604 dM = divs_M0[l]
605 dN = divs_N0[j]
606 if dN%(2^i)==0:
607 if dN%(2^(i+1))!=0 or i==len(adelic_failure_table):
608 if dM % pp[0] == 0:
609 FT[j][l] = lcm( FT[j][l], pp[1] )
610
611 # Adding l-adic failure to the table
612 for i in range( len( bad_primes ) ):
613 l = bad_primes[i]
614 for j in range(len(divs_N0)):
615 dN = divs_N0[j]
616 fl = l_adic_failure_from_data(B,l,l_adic_failure_table[i],dN,dN)
617 for h in range(len(divs_M0)):
618 FT[j][h] *= fl
619
620 ret = ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT )
621
622 if output:
623 print_total_table( ret )
624 # Uncomment following line for case list description.
625 #print_case_list( ret )
626 return
627 else:
628 return ret
629
630# For the torsion tables, recall that when -1 is in G, the failure is defined
631# as the ration between 2^eN^r and the degree of Q_{M,N} over Q_M, where e=1
632# if N is even and e=0 otherwise.
633
634# Makes the failure table for the torsion case when M/N is even. In this case,
635# an entry of the table is doubled if the corresponding value of N (actually,
636# of gcd(N,N0) ) is even, and is kept the same otherwise.
637# The expected degree (over Q) 2^e * phi(M) * N^r, where e=1 if N is even and
638# e=0 otherwise.
639def torsion_table_even( data ):
640
641 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
642
643 new_FT = [ [ 1 for d1 in divs_M0 ] for d2 in divs_N0 ]
644 for i in range(len(divs_N0)):
645 for j in range(len(divs_M0)):
646 if divs_N0[i] % 2 == 0:
647 new_FT[i][j] = 2 * FT[i][j]
648
649 return new_FT
650
651# Makes the failure table for the torsion case when M/N is odd. In this case
652# the entry at (M,N) is taken from the torsion-free entry at (2M,N).
653# In other words, the expected degree (over Q) 2^e * phi(M) * N^r, where e=1 if
654# N is even and e=0 otherwise.
655def torsion_table_odd( data ):
656
657 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
658
659 new_FT = [ [ 1 for d1 in divs_M0 ] for d2 in divs_N0 ]
660 new_data = ( ( r, False ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT )
661 for i in range(len(divs_N0)):
662 for j in range(len(divs_M0)):
663 dN = divs_N0[i]
664 dM = divs_M0[j]
665 # The following lines just copy the failure from (2M,N) in the
666 # torsion-free case. It is easier to write it like this, although
667 # it is not necessary in theory to compute the degree.
668 exp_deg = euler_phi(2*dM) * dN^r
669 deg = kummer_degree_from_total_table( 2*dM, dN, new_data )
670 new_FT[i][j] = exp_deg / deg
671
672 return new_FT
673
674def print_total_table( data ):
675
676 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
677
678 print "M_0 =", M0
679 print "N_0 =", N0
680 print ""
681 print "The following table shows the total failure of Kummer degrees",
682 if torsion:
683 print "in\n case the quotient M/N is EVEN."
684 else:
685 print "."
686 print "Columns correspond to values of M, rows to values of N"
687 print ""
688 print "The degree of the Kummer extension (M,N) can be extracted by taking"
689 print "the value f (failure) of the entry at (gcd(N,N0),gcd(M,M0)) and"
690 print "simply computing ed(M,N) / f, where ed(M,N) is the expected degree"
691 print "of the Kummer extension."
692 if torsion:
693 print "In this case (-1 is in G), we have ed(M,N) = 2^e*phi(M)*N^r,"
694 print "where e=1 if N is even and e=0 if N is odd."
695 FT1 = torsion_table_even( data )
696 else:
697 print "In this case (G is torsion-free) we have ed(M,N) = phi(M)*N^r,"
698 FT1 = FT
699 print "where r is the rank of G."
700 print ""
701
702 tt = [ ["","|"] + divs_M0 ]
703 tt.append( "-" * (len(divs_M0)+2) )
704 for i in range(len(divs_N0)):
705 tt.append( [ divs_N0[i] ] + ["|"] + FT1[i] )
706 print table(tt)
707 print ""
708
709 if torsion:
710 print "The following table shows the total failure of Kummer degrees in"
711 print "case the quotient M/N is ODD."
712 print "This table can be read exactly as the first one."
713 print ""
714
715 # A good strategy is the following:
716 # A little translation exercise: the failure at (M,N) in the torsion
717 # case is either the same as that for (2M,N) in the torsion-free case
718 # (if M is even) or its double (if M is odd).
719 # However, due to problems in reading the table for bigger M, it is
720 # easier to just compute the degree every time, and then deduce the
721 # failure. This is not too inefficient, since we can use the data that
722 # we have already computed via kummer_degree_from_total_table.
723 new_FT = torsion_table_odd( data )
724 # Printing the new table
725 tt = [ ["","|"] + divs_M0 ]
726 tt.append( "-" * (len(divs_M0)+2) )
727 for i in range(len(divs_N0)):
728 tt.append( [ divs_N0[i] ] + ["|"] + new_FT[i] )
729 print table(tt)
730 print ""
731
732def print_case_list( data ):
733
734 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
735
736 FT_odd = torsion_table_odd( data )
737 # FT1 is either FT or FT_even in the torsion case
738 FT1 = FT
739 if torsion:
740 FT1 = torsion_table_even( data )
741 pf = []
742 for row in FT1:
743 pf += row
744 if torsion:
745 for row in FT_odd:
746 pf += row
747 pf = list(set(pf))
748 pf.sort()
749 for f in pf:
750 print "Failure is", f, "if",
751 if torsion:
752 print "M/N is EVEN and",
753 print "(gcd(M,M0),gcd(N,N0)) is one of the following:"
754 lijst = []
755 for i in range(len(divs_N0)):
756 for j in range(len(divs_M0)):
757 if FT1[i][j] == f:
758 lijst.append( ( divs_M0[j], divs_N0[i] ) )
759 print lijst
760 if torsion:
761 print "or if M/N is ODD and (gcd(M,M0)),gcd(N,N0)) is one of the",
762 print "following:"
763
764 lijst_odd = []
765 for i in range(len(divs_N0)):
766 for j in range(len(divs_M0)):
767 if FT_odd[i][j] == f:
768 lijst_odd.append( ( divs_M0[j], divs_N0[i] ) )
769 print lijst_odd
770
771 print ""
772
773# Extracts a specific value of failure from the total table.
774def kummer_failure_from_total_table( M, N, data ):
775 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
776 FT1 = FT
777 if torsion:
778 if (M/N) % 2 == 0:
779 FT1 = torsion_table_even( data )
780 else:
781 FT1 = torsion_table_odd( data )
782
783 i = divs_N0.index( gcd( N, N0 ) )
784 j = divs_M0.index( gcd( M, M0 ) )
785 return FT1[i][j]
786
787# Computes the degree of the Kummer extension (M,N), by taking as input the
788# table computed by TotalKummerFailure.
789def kummer_degree_from_total_table( M, N, data ):
790
791 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
792
793 exp_deg = euler_phi(M) * N^r
794 if torsion and N % 2 == 0:
795 exp_deg *= 2
796 return exp_deg / kummer_failure_from_total_table( M, N, data )
797
798# Given a set of generators for a finitely generated subgroup G of the
799# multiplicative group of Q, returns the degree of Q_{M,N} over Q.
800# M must be a multiple of N.
801def KummerDegree( G, M, N ):
802 if M % N != 0:
803 print "M is not a multiple of N"
804 return -1
805
806 data = total_kummer_failure(G,False)
807 ((r,torsion),(M0,divs_M0),(N0,divs_N0),FT) = data
808
809 exp_deg = euler_phi(M) * N^r
810
811 if torsion and N % 2 == 0:
812 exp_deg *= 2
813
814 j = divs_M0.index(gcd(M,M0))
815 i = divs_N0.index(gcd(N,N0))
816
817 if torsion:
818 if (M/N)%2 == 0:
819 failure = torsion_table_even( data )
820 else:
821 failure = torsion_table_odd( data )
822 else:
823 failure = FT
824
825 return exp_deg / failure[i][j]

Generated with cgit - Back to sebastiano.tronto.net