diff options
Diffstat (limited to '')
| -rw-r--r-- | kummer_degree.sage | 825 |
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). | ||
| 27 | def 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. | ||
| 167 | def 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). | ||
| 174 | def 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] | ||
| 188 | def 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. | ||
| 200 | def 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. | ||
| 222 | def 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. | ||
| 258 | def 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. | ||
| 281 | def 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. | ||
| 401 | def 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). | ||
| 410 | def 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. | ||
| 461 | def 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. | ||
| 477 | def 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. | ||
| 487 | def 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" | ||
| 495 | def 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. | ||
| 514 | def 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. | ||
| 535 | def 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. | ||
| 550 | def 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. | ||
| 639 | def 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. | ||
| 655 | def 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 | |||
| 674 | def 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 | |||
| 732 | def 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. | ||
| 774 | def 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. | ||
| 789 | def 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. | ||
| 801 | def 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] | ||
