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