Path: blob/master/sage/quadratic_forms/genera/genus.py
4077 views
#*****************************************************************************1# Copyright (C) 2007 David Kohel <[email protected]>2# Gabriele Nebe <[email protected]>3#4# Distributed under the terms of the GNU General Public License (GPL)5#6# http://www.gnu.org/licenses/7#*****************************************************************************89import sage.misc.misc as misc10from sage.rings.arith import LCM11from sage.matrix.matrix_space import MatrixSpace12from sage.rings.integer_ring import IntegerRing13from sage.rings.rational_field import RationalField14from sage.rings.integer import Integer15from sage.rings.finite_rings.constructor import FiniteField1617def Genus(A):18"""19Given a nonsingular symmetric matrix A, return the genus of A.2021INPUT:22A -- a symmetric matrix with coefficients in ZZ2324OUTPUT:25A GenusSymbol_global_ring object, encoding the Conway-Sloane26genus symbol of the quadratic form whose Gram matrix is A.2728EXAMPLES:29sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring30sage: from sage.quadratic_forms.genera.genus import Genus3132sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])33sage: Genus(A)34Genus of [1 1]35[1 2]36"""37return GenusSymbol_global_ring(A)38394041def LocalGenusSymbol(A,p):42"""43Given a nonsingular symmetric matrix A, return the local symbol of A at the prime p.4445INPUT:46A -- a symmetric matrix with coefficients in ZZ47p -- an integer prime p > 04849OUTPUT:50A Genus_Symbol_p_adic_ring object, encoding the Conway-Sloane51genus symbol at p of the quadratic form whose Gram matrix is A.5253EXAMPLES:54sage: from sage.quadratic_forms.genera.genus import LocalGenusSymbol5556sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])57sage: LocalGenusSymbol(A, 2)58Genus symbol at 2 : [[0, 2, 1, 1, 2]]59sage: LocalGenusSymbol(A, 3)60Genus symbol at 3 : [[0, 2, 1]]6162sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])63sage: LocalGenusSymbol(A, 2)64Genus symbol at 2 : [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]65sage: LocalGenusSymbol(A, 3)66Genus symbol at 3 : [[0, 2, -1]]67"""68val = A.determinant().valuation(p)69symbol = p_adic_symbol(A, p, val = val)70return Genus_Symbol_p_adic_ring(p, symbol)71727374def is_GlobalGenus(G):75"""76Given a genus symbol G (specified by a collection of local symbols), return77True in G represents the genus of a global quadratic form or lattice.7879INPUT:80G -- GenusSymbol_global_ring object8182OUTPUT:83boolean8485EXAMPLES:86sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring87sage: from sage.quadratic_forms.genera.genus import Genus, is_GlobalGenus8889sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])90sage: G = Genus(A)91sage: is_GlobalGenus(G)92True93"""94D = G.determinant()95r, s = G.signature_pair_of_matrix()96oddity = r - s97for loc in G._local_symbols:98p = loc._prime99sym = loc._symbol100v = sum([ s[0]*s[1] for s in sym ])101a = D // (p**v)102b = Integer(misc.prod([ s[2] for s in sym ]))103if p == 2:104if not is_2_adic_genus(sym):105# print "False in is_2_adic_genus(sym)"106return False107if (a*b).kronecker(p) != 1:108# print "False in (%s*%s).kronecker(%s)"%(a,b,p)109return False110oddity -= loc.excess()111else:112if a.kronecker(p) != b:113# print "False in %s.kronecker(%s) != *%s"%(a,p,b)114return False115oddity += loc.excess()116if oddity%8 != 0:117# print "False in oddity"118return False119return True120121122123def is_2_adic_genus(genus_symbol_quintuple_list):124"""125Given a 2-adic local symbol (as the underlying list of quintuples)126check whether it is the 2-adic symbol of a 2-adic form.127128INPUT:129genus_symbol_quintuple_list -- a quintuple of integers (with certain130restrictions).131132OUTPUT:133boolean134135EXAMPLES:136sage: from sage.quadratic_forms.genera.genus import LocalGenusSymbol137138sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])139sage: G2 = LocalGenusSymbol(A, 2)140sage: is_2_adic_genus(G2.symbol_tuple_list())141True142143sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])144sage: G3 = LocalGenusSymbol(A, 3)145sage: is_2_adic_genus(G3.symbol_tuple_list()) ## This raises an error146Traceback (most recent call last):147...148TypeError: The genus symbols are not quintuples, so it's not a genus symbol for the prime p=2.149150sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])151sage: G2 = LocalGenusSymbol(A, 2)152sage: is_2_adic_genus(G2.symbol_tuple_list())153True154"""155## TO DO: Add explicit checking for the prime p here to ensure it's p=2... not just the quintuple checking below156157for s in genus_symbol_quintuple_list:158159## Check that we have a quintuple (i.e. that p=2 and not p >2)160if len(s) != 5:161raise TypeError, "The genus symbols are not quintuples, so it's not a genus symbol for the prime p=2."162163## Check the Conway-Sloane conditions164if s[1] == 1:165if s[3] == 0 or s[2] != s[4]:166return False167if s[1] == 2 and s[3] == 1:168if s[2] in (1,-1):169if not s[4] in (0,2,6):170return False171if s[2] in (3,-3):172if not s[4] in (2,4,6):173return False174if (s[1] - s[4])% 2 == 1:175return False176if s[3] == 0 and s[4] != 0:177return False178return True179180181182def canonical_2_adic_compartments(genus_symbol_quintuple_list):183"""184Given a 2-adic local symbol (as the underlying list of quintuples)185this returns a list of lists of indices of the186genus_symbol_quintuple_list which are in the same compartment. A187compartment is defined to be a maximal interval of Jordan188components all (scaled) of type I (i.e. odd).189190INPUT:191genus_symbol_quintuple_list -- a quintuple of integers (with certain192restrictions).193194OUTPUT:195a list of lists of integers.196197EXAMPLES:198sage: from sage.quadratic_forms.genera.genus import LocalGenusSymbol199sage: from sage.quadratic_forms.genera.genus import canonical_2_adic_compartments200201sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])202sage: G2 = LocalGenusSymbol(A, 2); G2203Genus symbol at 2 : [[0, 2, 1, 1, 2]]204sage: canonical_2_adic_compartments(G2.symbol_tuple_list())205[[0]]206207sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])208sage: G2 = LocalGenusSymbol(A, 2); G2209Genus symbol at 2 : [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]210sage: canonical_2_adic_compartments(G2.symbol_tuple_list())211[[0, 1]]212213sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()214sage: G2 = LocalGenusSymbol(A, 2); G2215Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]216sage: canonical_2_adic_compartments(G2.symbol_tuple_list())217[[0, 1, 2]]218219sage: A = Matrix(ZZ, 2, 2, [2,1,1,2])220sage: G2 = LocalGenusSymbol(A, 2); G2221Genus symbol at 2 : [[0, 2, 3, 0, 0]]222sage: canonical_2_adic_compartments(G2.symbol_tuple_list()) ## No compartments here!223[]224225NOTES:226See Conway-Sloane 3rd edition, pp. 381-382 for definitions and examples.227"""228symbol = genus_symbol_quintuple_list229compartments = []230i = 0231r = len(symbol)232while i < r:233s = symbol[i]234if s[3] == 1:235v = s[0]236c = []237while i < r and symbol[i][3] == 1 and symbol[i][0] == v:238c.append(i)239i += 1240v += 1241compartments.append(c)242else:243i += 1244return compartments245246247248249250def canonical_2_adic_trains(genus_symbol_quintuple_list, compartments=None):251"""252Given a 2-adic local symbol (as the underlying list of quintuples)253this returns a list of lists of indices of the254genus_symbol_quintuple_list which are in the same train. A train255is defined to be a maximal interval of Jordan components so that256at least one of each adjacent pair (allowing zero-dimensional257Jordan components) is (scaled) of type I (i.e. odd).258259INPUT:260genus_symbol_quintuple_list -- a quintuple of integers (with certain261restrictions).262compartments -- a list of lists of distinct integers (optional)263264OUTPUT:265a list of lists of distinct integers.266267EXAMPLES:268sage: from sage.quadratic_forms.genera.genus import LocalGenusSymbol269sage: from sage.quadratic_forms.genera.genus import canonical_2_adic_compartments270sage: from sage.quadratic_forms.genera.genus import canonical_2_adic_trains271272sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])273sage: G2 = LocalGenusSymbol(A, 2); G2274Genus symbol at 2 : [[0, 2, 1, 1, 2]]275sage: c = canonical_2_adic_compartments(G2.symbol_tuple_list()); c276[[0]]277sage: canonical_2_adic_trains(G2.symbol_tuple_list(), c)278[[0]]279280sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])281sage: G2 = LocalGenusSymbol(A, 2); G2282Genus symbol at 2 : [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]283sage: c = canonical_2_adic_compartments(G2.symbol_tuple_list()); c284[[0, 1]]285sage: canonical_2_adic_trains(G2.symbol_tuple_list(), c)286[[0, 1]]287sage: canonical_2_adic_trains(G2.symbol_tuple_list())288[[0, 1]]289290sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()291sage: G2 = LocalGenusSymbol(A, 2); G2292Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]293sage: c = canonical_2_adic_compartments(G2.symbol_tuple_list()); c294[[0, 1, 2]]295sage: canonical_2_adic_trains(G2.symbol_tuple_list())296[[0, 1, 2]]297298sage: A = Matrix(ZZ, 2, 2, [2,1,1,2])299sage: G2 = LocalGenusSymbol(A, 2); G2300Genus symbol at 2 : [[0, 2, 3, 0, 0]]301sage: c = canonical_2_adic_compartments(G2.symbol_tuple_list()); c ## No compartments here!302[]303sage: canonical_2_adic_trains(G2.symbol_tuple_list())304[]305306NOTES:307See Conway-Sloane 3rd edition, pp. 381-382 for definitions and examples.308309TO DO:310- Add a non-trivial example in the doctest here!311"""312## Recompute compartments if none are passed.313if compartments == None:314compartments = canonical_2_adic_compartments(genus_symbol_quintuple_list)315316symbol = genus_symbol_quintuple_list317trains = []318i = 0319while i < len(compartments):320flag = True321train = [ ]322while flag:323ci = compartments[i]324j = ci[0]325if j == 0 or symbol[j-1][0] != symbol[j][0] - 1:326train += ci327else:328train += [j-1] + ci329act = ci[len(ci)-1]+1330if i+1 < len(compartments):331ci_plus = compartments[i+1]332else:333if act < len(symbol) and symbol[act][0] == symbol[act-1][0] +1:334train += [act]335flag = False336if flag and symbol[ci[len(ci)-1]][0]+2 != symbol[ci_plus[0]][0]:337if act != ci_plus[0] and symbol[act][0] == symbol[act-1][0] +1:338train += [act]339flag = False340i += 1341trains.append(train)342return trains343344345346347def canonical_2_adic_reduction(genus_symbol_quintuple_list):348"""349Given a 2-adic local symbol (as the underlying list of quintuples)350this returns a canonical 2-adic symbol (again as a raw list of351quintuples of integers) which has at most one minus sign per train352and this sign appears on the smallest dimensional Jordan component353in each train. This results from applying the "sign-walking" and354"oddity fusion" equivalences.355356INPUT:357genus_symbol_quintuple_list -- a quintuple of integers (with certain358restrictions).359compartments -- a list of lists of distinct integers (optional)360361OUTPUT:362a list of lists of distinct integers.363364EXAMPLES:365sage: from sage.quadratic_forms.genera.genus import LocalGenusSymbol366sage: from sage.quadratic_forms.genera.genus import canonical_2_adic_reduction367368sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])369sage: G2 = LocalGenusSymbol(A, 2); G2370Genus symbol at 2 : [[0, 2, 1, 1, 2]]371sage: canonical_2_adic_reduction(G2.symbol_tuple_list())372[[0, 2, 1, 1, 2]]373374sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])375sage: G2 = LocalGenusSymbol(A, 2); G2376Genus symbol at 2 : [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]377sage: canonical_2_adic_reduction(G2.symbol_tuple_list()) ## Oddity fusion occurred here!378[[0, 1, 1, 1, 2], [1, 1, 1, 1, 0]]379380sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()381sage: G2 = LocalGenusSymbol(A, 2); G2382Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]383sage: canonical_2_adic_reduction(G2.symbol_tuple_list()) ## Oddity fusion occurred here!384[[1, 2, -1, 1, 6], [2, 1, 1, 1, 0], [3, 1, 1, 1, 0]]385386sage: A = Matrix(ZZ, 2, 2, [2,1,1,2])387sage: G2 = LocalGenusSymbol(A, 2); G2388Genus symbol at 2 : [[0, 2, 3, 0, 0]]389sage: canonical_2_adic_reduction(G2.symbol_tuple_list())390[[0, 2, -1, 0, 0]]391392NOTES:393See Conway-Sloane 3rd edition, pp. 381-382 for definitions and examples.394395TO DO:396- Add an example where sign walking occurs!397"""398canonical_symbol = genus_symbol_quintuple_list399# Canonical determinants:400for i in range(len(genus_symbol_quintuple_list)):401d = genus_symbol_quintuple_list[i][2]402if d in (1,7):403canonical_symbol[i][2] = 1404else:405canonical_symbol[i][2] = -1406# Oddity fusion:407compartments = canonical_2_adic_compartments(genus_symbol_quintuple_list)408for compart in compartments:409oddity = sum([ genus_symbol_quintuple_list[i][4] for i in compart ]) % 8410for i in compart:411genus_symbol_quintuple_list[i][4] = 0412genus_symbol_quintuple_list[compart[0]][4] = oddity413#print "End oddity fusion:", canonical_symbol414# Sign walking:415trains = canonical_2_adic_trains(genus_symbol_quintuple_list, compartments)416for train in trains:417t = len(train)418for i in range(t-1):419t1 = train[t-i-1]420if canonical_symbol[t1][2] == -1:421canonical_symbol[t1][2] = 1422canonical_symbol[t1-1][2] *= -1423for compart in compartments:424if t1-1 in compart or t1 in compart:425o = canonical_symbol[compart[0]][4]426canonical_symbol[compart[0]][4] = (o+4) % 8427#print "End sign walking:", canonical_symbol428return canonical_symbol429430431432433434def basis_complement(B):435"""436Given an echelonized basis matrix (over a field), calculate a437matrix whose rows form a basis complement (to the rows of B).438439INPUT:440B -- matrix over a field in row echelon form441442OUTPUT:443a rectangular matrix over a field444445EXAMPLES:446sage: from sage.quadratic_forms.genera.genus import basis_complement447448sage: A = Matrix(ZZ, 2, 2, [1,1,1,1])449sage: B = A.kernel().echelonized_basis_matrix(); B450[ 1 -1]451sage: basis_complement(B)452[0 1]453"""454F = B.parent().base_ring()455m = B.nrows()456n = B.ncols()457C = MatrixSpace(F,n-m,n,sparse=True)(0)458k = 0459l = 0460for i in range(m):461for j in range(k,n):462if B[i,j] == 0:463C[l,j] = 1464l += 1465else:466k = j+1467break468for j in range(k,n):469C[l+j-k,j] = 1470return C471472473474def signature_pair_of_matrix(A):475"""476Computes the signature pair (p, n) of a non-degenerate symmetric477matrix, where478p = number of positive eigenvalues of A479n = number of negative eigenvalues of A480481INPUT:482A -- symmetric matrix (assumed to be non-degenerate)483484OUTPUT:485a pair (tuple) of integers.486487EXAMPLES:488sage: from sage.quadratic_forms.genera.genus import signature_pair_of_matrix489490sage: A = Matrix(ZZ, 2, 2, [-1,0,0,3])491sage: signature_pair_of_matrix(A)492(1, 1)493494sage: A = Matrix(ZZ, 2, 2, [-1,1,1,7])495sage: signature_pair_of_matrix(A)496(1, 1)497498sage: A = Matrix(ZZ, 2, 2, [3,1,1,7])499sage: signature_pair_of_matrix(A)500(2, 0)501502sage: A = Matrix(ZZ, 2, 2, [-3,1,1,-11])503sage: signature_pair_of_matrix(A)504(0, 2)505506507sage: A = Matrix(ZZ, 2, 2, [1,1,1,1])508sage: signature_pair_of_matrix(A) ## Raises an error -- degenerate matrix509Traceback (most recent call last):510...511TypeError: A is assumed to be non-degenerate, but it's det = 0.512513"""514from sage.quadratic_forms.quadratic_form import QuadraticForm515s_vec = QuadraticForm(A.base_extend(A.base_ring().fraction_field())).signature_vector()516517## Check that the matrix is non-degenerate (i.e. no zero eigenvalues)518if s_vec[2] != 0:519raise TypeError, "A is assumed to be non-degenerate, but it's det = 0."520521## Return the pair (p,n)522return s_vec[:2]523524525526def p_adic_symbol(A, p, val):527"""528Given a symmetric matrix A and prime p, return the genus symbol at p.529530val = valuation of the maximal elementary divisor of A531needed to obtain enough precision532calculation is modulo p to the val+3533TODO: Some description of the definition of the genus symbol.534535INPUT:536A -- symmetric matrix with integer coefficients537p -- prime number > 0538val -- integer >= 0539540OUTPUT:541a list of lists of integers542543EXAMPLES:544sage: from sage.quadratic_forms.genera.genus import p_adic_symbol545546sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()547sage: p_adic_symbol(A, 2, 2)548[[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]549550sage: p_adic_symbol(A, 3, 1)551[[0, 3, 1], [1, 1, -1]]552553"""554if p % 2 == 0:555return two_adic_symbol(A, val)556m0 = min([ c.valuation(p) for c in A.list() ])557q = p**m0558n = A.nrows()559A = MatrixSpace(IntegerRing(),n,n)([ c // q for c in A.list() ])560A_p = MatrixSpace(FiniteField(p),n,n)(A)561B_p = A_p.kernel().echelonized_basis_matrix()562if B_p.nrows() == 0:563e0 = Integer(A_p.det()).kronecker(p)564n0 = A.nrows()565return [ [m0,n0,e0] ]566else:567C_p = basis_complement(B_p)568e0 = Integer((C_p*A_p*C_p.transpose()).det()).kronecker(p)569n0 = C_p.nrows()570sym = [ [0,n0,e0] ]571r = B_p.nrows()572B = MatrixSpace(IntegerRing(),r,n)(B_p)573C = MatrixSpace(IntegerRing(),n-r,n)(C_p)574# Construct the blocks for the Jordan decomposition [F,X;X,A_new]575F = MatrixSpace(RationalField(),n-r,n-r)(C*A*C.transpose())576U = F**-1577d = LCM([ c.denominator() for c in U.list() ])578R = IntegerRing().quotient_ring(Integer(p)**(val+3))579u = R(d)**-1580MatR = MatrixSpace(R,n-r,n-r)581MatZ = MatrixSpace(IntegerRing(),n-r,n-r)582U = MatZ(MatR(MatZ(U*d))*u)583# X = C*A*B.transpose()584# A = B*A*B.transpose() - X.transpose()*U*X585X = C*A586A = B*(A - X.transpose()*U*X)*B.transpose()587return [ [s[0]+m0] + s[1:] for s in sym + p_adic_symbol(A, p, val) ]588589590591def is_even_matrix(A):592"""593Determines if the integral symmetric matrix A is even594(i.e. represents only even numbers). If not, then it returns the595index of an odd diagonal entry. If it is even, then we return the596index -1.597598INPUT:599A -- symmetric integer matrix600601OUTPUT:602a pair of the form (boolean, integer)603604EXAMPLES:605sage: from sage.quadratic_forms.genera.genus import is_even_matrix606607sage: A = Matrix(ZZ, 2, 2, [1,1,1,1])608sage: is_even_matrix(A)609(False, 0)610611sage: A = Matrix(ZZ, 2, 2, [2,1,1,2])612sage: is_even_matrix(A)613(True, -1)614"""615for i in range(A.nrows()):616if A[i,i]%2 == 1:617return False, i618return True, -1619620621622def split_odd(A):623"""624Given a non-degenerate Gram matrix A (mod 8), return a splitting [u] + B625such that u is odd and B is not even.626627INPUT:628A -- an odd symmetric matrix with integer coefficients (which629admits a splitting as above).630631OUTPUT:632a pair (u, B) consisting of an odd integer u and an odd633integral symmetric matrix B.634635EXAMPLES:636sage: from sage.quadratic_forms.genera.genus import is_even_matrix637sage: from sage.quadratic_forms.genera.genus import split_odd638639sage: A = Matrix(ZZ, 2, 2, [1,2,2,3])640sage: is_even_matrix(A)641(False, 0)642sage: split_odd(A)643(1, [-1])644645sage: A = Matrix(ZZ, 2, 2, [1,2,2,5])646sage: split_odd(A)647(1, [1])648649sage: A = Matrix(ZZ, 2, 2, [1,1,1,1])650sage: is_even_matrix(A)651(False, 0)652sage: split_odd(A) ## This fails because no such splitting exists. =(653Traceback (most recent call last):654...655RuntimeError: The matrix A does not admit a non-even splitting.656657sage: A = Matrix(ZZ, 2, 2, [1,2,2,6])658sage: split_odd(A) ## This fails because no such splitting exists. =(659Traceback (most recent call last):660...661RuntimeError: The matrix A does not admit a non-even splitting.662663"""664n0 = A.nrows()665if n0 == 1:666return A[0,0], MatrixSpace(IntegerRing(),0,A.ncols())([])667even, i = is_even_matrix(A)668R = A.parent().base_ring()669C = MatrixSpace(R,n0-1,n0)(0)670u = A[i,i]671for j in range(n0-1):672if j < i:673C[j,j] = 1674C[j,i] = -A[j,i]*u675else:676C[j,j+1] = 1677C[j,i] = -A[j+1,i]*u678B = C*A*C.transpose()679even, j = is_even_matrix(B)680if even:681I = A.parent()(1)682# TODO: we could manually (re)construct the kernel here...683if i == 0:684I[1,0] = 1 - A[1,0]*u685i = 1686else:687I[0,i] = 1 - A[0,i]*u688i = 0689A = I*A*I.transpose()690u = A[i,i]691C = MatrixSpace(R,n0-1,n0)(0)692for j in range(n0-1):693if j < i:694C[j,j] = 1695C[j,i] = -A[j,i]*u696else:697C[j,j+1] = 1698C[j,i] = -A[j+1,i]*u699B = C*A*C.transpose()700even, j = is_even_matrix(B)701if even:702print "B:"703print B704raise RuntimeError, "The matrix A does not admit a non-even splitting."705return u, B706707708709def trace_diag_mod_8(A):710"""711Return the trace of the diagonalised form of A of an integral712symmetric matrix which is diagonalizable mod 8. (Note that since713the Jordan decomposition into blocks of size <= 2 is not unique714here, this is not the same as saying that A is always diagonal in715any 2-adic Jordan decomposition!)716717INPUT:718A -- symmetric matrix with coefficients in Z which is odd in719Z/2Z and has determinant not divisible by 8.720721OUTPUT:722an integer723724EXAMPLES:725sage: from sage.quadratic_forms.genera.genus import is_even_matrix726sage: from sage.quadratic_forms.genera.genus import split_odd727sage: from sage.quadratic_forms.genera.genus import trace_diag_mod_8728729sage: A = Matrix(ZZ, 2, 2, [1,2,2,3])730sage: is_even_matrix(A)731(False, 0)732sage: split_odd(A)733(1, [-1])734sage: trace_diag_mod_8(A)7350736737sage: A = Matrix(ZZ, 2, 2, [1,2,2,5])738sage: split_odd(A)739(1, [1])740sage: trace_diag_mod_8(A)7412742"""743tr = 0744while A.nrows() > 0:745u, A = split_odd(A)746tr += u747return IntegerRing()(tr)748749750751def two_adic_symbol(A, val):752"""753Given a symmetric matrix A and prime p, return the genus symbol at p.754755val = valuation of maximal 2-elementary divisor756757The genus symbol of a component 2^m*f is of the form (m,n,s,d[,o]),758where759m = valuation of the component760n = dimension of f761d = det(f) in {1,3,5,7}762s = 0 (or 1) if even (or odd)763o = oddity of f (= 0 if s = 0) in Z/8Z764765INPUT:766A -- symmetric matrix with integer coefficients767val -- integer >=0768769OUTPUT:770a list of lists of integers (representing a Conway-Sloane 2-adic symbol)771772EXAMPLES:773sage: from sage.quadratic_forms.genera.genus import two_adic_symbol774775sage: A = diagonal_matrix(ZZ, [1,2,3,4])776sage: two_adic_symbol(A, 2)777[[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]778779"""780m0 = min([ c.valuation(2) for c in A.list() ])781q = 2**m0782A = A.parent()([ c // q for c in A.list() ])783ZZ = IntegerRing()784n = A.nrows()785A_2 = MatrixSpace(FiniteField(2),n,n)(A)786K_2 = A_2.kernel()787R_8 = ZZ.quotient_ring(Integer(8))788789## Deal with the matrix being non-degenerate mod 2.790if K_2.dimension() == 0:791A_8 = MatrixSpace(R_8,n)(A)792n0 = A.nrows()793# d0 = ZZ(A_8.determinant()) # no determinant over Z/8Z794d0 = ZZ(R_8(MatrixSpace(ZZ,n)(A_8).determinant()))795if d0 == 0: ## SANITY CHECK: The mod 8 determinant shouldn't be zero.796print "A:"797print A798assert False799even, i = is_even_matrix(A_2) ## Determine whether the matrix is even or odd.800if even:801return [ [m0,n0,d0,0,0] ]802else:803tr8 = trace_diag_mod_8(A_8) ## Here we already know that A_8 is odd and diagonalizable mod 8.804return [ [m0,n0,d0,1,tr8] ]805806## Deal with the matrix being degenerate mod 2.807else:808B_2 = K_2.echelonized_basis_matrix()809C_2 = basis_complement(B_2)810n0 = C_2.nrows()811C = MatrixSpace(ZZ,n0,n)(C_2)812A_new = C*A*C.transpose()813# compute oddity modulo 8:814A_8 = MatrixSpace(R_8,n0,n0)(A_new)815# d0 = A_8.det() # no determinant over Z/8Z816d0 = ZZ(R_8(MatrixSpace(ZZ,n0,n0)(A_8).determinant()))817if d0 == 0:818print "A:"819print A_new820assert False821even, i = is_even_matrix(A_new)822if even:823sym = [ [0,n0,d0,0,0] ]824else:825tr8 = trace_diag_mod_8(A_8)826sym = [ [0,n0,d0,1,tr8] ]827r = B_2.nrows()828B = MatrixSpace(ZZ,r,n)(B_2)829C = MatrixSpace(IntegerRing(),n-r,n)(C_2)830F = MatrixSpace(RationalField(),n-r,n-r)(C*A*C.transpose())831U = F**-1832d = LCM([ c.denominator() for c in U.list() ])833R = IntegerRing().quotient_ring(Integer(2)**(val+3))834u = R(d)**-1835MatR = MatrixSpace(R,n-r,n-r)836MatZ = MatrixSpace(IntegerRing(),n-r,n-r)837U = MatZ(MatR(MatZ(U*d))*u)838X = C*A839A = B*(A - X.transpose()*U*X)*B.transpose()840return [ [s[0]+m0] + s[1:] for s in sym + two_adic_symbol(A, val) ]841842843844845846## Removed because it was unused and undocumented!847#848#def is_trivial_symbol(p, sym):849# """850# """851# if len(sym) != 1:852# return False853# if sym[0] != 0 or sym[2] != 1:854# return False855# if p != 2:856# return True857# return sym[3] == 1 and sym[1] % 8 == sym[4]858859860861862863864865866class Genus_Symbol_p_adic_ring(object):867"""868Local genus symbol over a p-adic ring.869"""870def __init__(self, prime, symbol, check = True):871"""872Create the local genus symbol of given prime and local invariants.873874The genus symbol of a component p^m*A for odd prime = p is of the875form (m,n,d), where876877m = valuation of the component878n = rank of A879d = det(A) in {1,u} for normalized quadratic non-residue u.880881The genus symbol of a component 2^m*A is of the form (m,n,s,d,o),882where883884m = valuation of the component885n = rank of A886d = det(A) in {1,3,5,7}887s = 0 (or 1) if even (or odd)888o = oddity of A (= 0 if s = 0) in Z/8Z889= the trace of the diagonalization of A890891The genus symbol is a list of such symbols (ordered by m) for each892of the Jordan blocks A_1,...,A_t.893894Reference: Conway and Sloane 3rd edition, Chapter 15, Section 7.895896897WARNING/NOTE: This normalization seems non-standard, and we898should review this entire class to make sure that we have our899doubling conventions straight throughout! This is especially900noticeable in the determinant and excess methods!!901902903INPUT:904prime -- a prime integer > 0905symbol -- the list of invariants for Jordan blocks906A_t,...,A_t given as a list of lists of integers907908OUTPUT:909None910911EXAMPLES:912sage: from sage.quadratic_forms.genera.genus import p_adic_symbol913sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring914915sage: A = diagonal_matrix(ZZ, [1,2,3,4])916sage: p = 2917sage: s2 = p_adic_symbol(A, p, 2); s2918[[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]919sage: G = Genus_Symbol_p_adic_ring(p,s2);G920Genus symbol at 2 : [[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]921sage: G == loads(dumps(G))922True923924sage: A = diagonal_matrix(ZZ, [1,2,3,4])925sage: p = 3926sage: s3 = p_adic_symbol(A, p, 1); s3927[[0, 3, -1], [1, 1, 1]]928sage: G = Genus_Symbol_p_adic_ring(p,s3);G929Genus symbol at 3 : [[0, 3, -1], [1, 1, 1]]930sage: G == loads(dumps(G))931True932933934"""935if check:936pass937self._prime = prime938self._symbol = symbol939self._canonical_symbol = None940941def __repr__(self):942"""943Gives a string representation for the p-adic genus symbol944945INPUT:946None947948OUTPUT:949a string950951EXAMPLES:952sage: from sage.quadratic_forms.genera.genus import two_adic_symbol953sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring954955sage: A = diagonal_matrix(ZZ, [1,2,3,4])956sage: s2 = two_adic_symbol(A, 2); s2957[[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]958sage: G = Genus_Symbol_p_adic_ring(2, s2)959sage: G.__repr__()960'Genus symbol at 2 : [[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]'961962"""963return "Genus symbol at %s : %s"%(self._prime, self._symbol)964965966def __eq__(self, other):967"""968Determines if two genus symbols are equal (not just equivalent!).969970INPUT:971a Genus_Symbol_p_adic_ring object972973OUTPUT:974boolean975976EXAMPLES:977sage: from sage.quadratic_forms.genera.genus import p_adic_symbol978sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring979980sage: A = diagonal_matrix(ZZ, [1,2,3,4])981sage: p = 2982sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2))983sage: p = 3984sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 1))985986sage: G2 == G3987False988sage: G3 == G2989False990sage: G2 == G2991True992sage: G3 == G3993True994995"""996p = self._prime997if p != other._prime:998return False999return self.canonical_symbol() == other.canonical_symbol()100010011002def __ne__(self, other):1003"""1004Determines if two genus symbols are unequal (not just inequivalent!).10051006INPUT:1007a Genus_Symbol_p_adic_ring object10081009OUTPUT:1010boolean10111012sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1013sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring10141015sage: A = diagonal_matrix(ZZ, [1,2,3,4])1016sage: p = 21017sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2))1018sage: p = 31019sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 1))10201021sage: G2 != G31022True1023sage: G3 != G21024True1025sage: G2 != G21026False1027sage: G3 != G31028False10291030"""1031return not self.__eq__(other)103210331034## Added these two methods to make this class iterable...1035#def __getitem__(self, i):1036# return self._symbol[i]1037#1038#def len(self):1039# return len(self._symbol)1040## ------------------------------------------------------104110421043def canonical_symbol(self):1044"""1045Return (and cache) the canonical p-adic genus symbol. This is1046only really affects the 2-adic symbol, since when p > 2 the1047symbol is already canonical.10481049INPUT:1050None10511052OUTPUT:1053a list of lists of integers10541055EXAMPLES:1056sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1057sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring10581059sage: A = Matrix(ZZ, 2, 2, [1,1,1,2])1060sage: p = 21061sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21062Genus symbol at 2 : [[0, 2, 1, 1, 2]]1063sage: G2.canonical_symbol()1064[[0, 2, 1, 1, 2]]10651066sage: A = Matrix(ZZ, 2, 2, [1,0,0,2])1067sage: p = 21068sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21069Genus symbol at 2 : [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]1070sage: G2.canonical_symbol() ## Oddity fusion occurred here!1071[[0, 1, 1, 1, 2], [1, 1, 1, 1, 0]]10721073sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1074sage: p = 21075sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21076Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1077sage: G2.canonical_symbol() ## Oddity fusion occurred here!1078[[1, 2, -1, 1, 6], [2, 1, 1, 1, 0], [3, 1, 1, 1, 0]]10791080sage: A = Matrix(ZZ, 2, 2, [2,1,1,2])1081sage: p = 21082sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21083Genus symbol at 2 : [[0, 2, 3, 0, 0]]1084sage: G2.canonical_symbol()1085[[0, 2, -1, 0, 0]]108610871088sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1089sage: p = 31090sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31091Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1092sage: G3.canonical_symbol()1093[[0, 3, 1], [1, 1, -1]]109410951096NOTES:1097See Conway-Sloane 3rd edition, pp. 381-382 for definitions and examples.10981099TO DO:1100- Add an example where sign walking occurs!1101"""1102symbol = self._symbol1103if self._prime == 2:1104if self._canonical_symbol is None:1105self._canonical_symbol = canonical_2_adic_reduction(symbol)1106return self._canonical_symbol1107else:1108return self._symbol1109111011111112def symbol_tuple_list(self):1113"""1114Returns the underlying list of lists of integers defining the genus symbol.11151116INPUT:1117None11181119OUTPUT:1120list of lists of integers11211122EXAMPLES:1123sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1124sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring11251126sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1127sage: p = 31128sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31129Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1130sage: G3.symbol_tuple_list()1131[[0, 3, 1], [1, 1, -1]]1132sage: type(G3.symbol_tuple_list())1133<type 'list'>11341135sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1136sage: p = 21137sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21138Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1139sage: G2.symbol_tuple_list()1140[[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1141sage: type(G2.symbol_tuple_list())1142<type 'list'>11431144"""1145return self._symbol1146114711481149def number_of_blocks(self):1150"""1151Returns the number of positive dimensional symbols/Jordan blocks11521153INPUT:1154None11551156OUTPUT:1157integer >= 011581159EXAMPLES:1160sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1161sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring11621163sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1164sage: p = 21165sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21166Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1167sage: G2.number_of_blocks()1168311691170sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1171sage: p = 31172sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31173Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1174sage: G3.number_of_blocks()1175211761177"""1178return len(self._symbol)117911801181def determinant(self):1182"""1183Returns the (p-part of the) determinant (square-class) of the1184Hessian matrix of the quadratic form (given by regarding the1185integral symmetric matrix which generated this genus symbol as1186the Gram matrix of Q) associated to this local genus symbol.11871188INPUT:1189None11901191OUTPUT:1192an integer11931194EXAMPLES:1195sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1196sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring11971198sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1199sage: p = 21200sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21201Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1202sage: G2.determinant()120312812041205sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1206sage: p = 31207sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31208Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1209sage: G3.determinant()121031211"""1212p = self._prime1213return misc.prod([ p**(s[0]*s[1]) for s in self._symbol ])121412151216def rank(self):1217"""1218Returns the dimension of a quadratic form associated to this genus symbol.12191220TO DO: DELETE THIS METHOD IN FAVOR OF THE dimension() METHOD BELOW!122112221223INPUT:1224None12251226OUTPUT:1227an integer >= 012281229EXAMPLES:1230sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1231sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring12321233sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1234sage: p = 21235sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21236Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1237sage: G2.rank()1238412391240sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1241sage: p = 31242sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31243Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1244sage: G3.rank()1245412461247"""1248return sum([ s[1] for s in self._symbol ])124912501251def dimension(self):1252"""1253Returns the dimension of a quadratic form associated to this genus symbol.12541255INPUT:1256None12571258OUTPUT:1259an integer >= 012601261EXAMPLES:1262sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1263sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring12641265sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1266sage: p = 21267sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21268Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1269sage: G2.dimension()1270412711272sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1273sage: p = 31274sage: G3 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G31275Genus symbol at 3 : [[0, 3, 1], [1, 1, -1]]1276sage: G3.dimension()1277412781279"""1280return self.rank()128112821283def excess(self):1284"""1285Returns the p-excess of the quadratic form whose Hessian1286matrix is the symmetric matrix A. When p = 2 the p-excess is1287called the oddity.12881289WARNING/NOTE: This normalization seems non-standard, and we1290should review this entire class to make sure that we have our1291doubling conventions straight throughout!12921293REFERENCE:1294Conway and Sloane Book, 3rd edition, pp 370-371.12951296INPUT:1297None12981299OUTPUT:1300an integer13011302EXAMPLES:1303sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1304sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring13051306sage: AC = diagonal_matrix(ZZ, [1,3,-3])1307sage: p=2; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()130811309sage: p=3; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()131001311sage: p=5; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()131201313sage: p=7; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()131401315sage: p=11; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()1316013171318sage: AC = 2 * diagonal_matrix(ZZ, [1,3,-3])1319sage: p=2; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()132011321sage: p=3; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()132201323sage: p=5; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()132401325sage: p=7; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()132601327sage: p=11; Genus_Symbol_p_adic_ring(p, p_adic_symbol(AC, p, 2)).excess()1328013291330sage: A = 2*diagonal_matrix(ZZ, [1,2,3,4])1331sage: p=2; Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)).excess()133221333sage: p=3; Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)).excess()133461335sage: p=5; Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)).excess()133601337sage: p=7; Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)).excess()133801339sage: p=11; Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)).excess()1340013411342"""1343p = self._prime1344if self._prime == 2:1345k = 01346for s in self._symbol:1347if s[0]%2 == 1 and s[2] in (3,5):1348k += 11349return Integer(sum([ s[4] for s in self._symbol ]) + 4*k).mod(8)1350else:1351k = 01352for s in self._symbol:1353if s[0]%2 == 1 and s[2] == -1:1354k += 11355return Integer(sum([ s[1]*(p**s[0]-1) for s in self._symbol ]) + 4*k).mod(8)1356135713581359def trains(self):1360"""1361Compute the indices for each of the trains in this local genus1362symbol if it is associated to the prime p=2 (and raise an1363error for all other primes).13641365INPUT:1366None13671368OUTPUT:1369a list of integers >= 013701371EXAMPLES:1372sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1373sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring13741375sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1376sage: p = 21377sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21378Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1379sage: G2.trains()1380[[0, 1, 2]]13811382"""1383## Check that p = 21384if self._prime != 2:1385raise TypeError, "trains() only makes sense when the prime of the p_adic_Genus_Symbol is p=2"1386symbol = self._symbol1387compartments = canonical_2_adic_compartments(symbol)1388return canonical_2_adic_trains(symbol, compartments)138913901391def compartments(self):1392"""1393Compute the indices for each of the compartments in this local genus1394symbol if it is associated to the prime p=2 (and raise an1395error for all other primes).13961397INPUT:1398None13991400OUTPUT:1401a list of integers >= 014021403EXAMPLES:1404sage: from sage.quadratic_forms.genera.genus import p_adic_symbol1405sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring14061407sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1408sage: p = 21409sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G21410Genus symbol at 2 : [[1, 2, 3, 1, 4], [2, 1, 1, 1, 1], [3, 1, 1, 1, 1]]1411sage: G2.compartments()1412[[0, 1, 2]]14131414"""1415## Check that p = 21416if self._prime != 2:1417raise TypeError, "compartments() only makes sense when the prime of the p_adic_Genus_Symbol is p=2"1418symbol = self._symbol1419return canonical_2_adic_compartments(symbol)1420142114221423142414251426class GenusSymbol_global_ring(object):1427"""1428This represents a collection of local genus symbols (at primes)1429and signature information which represent the genus of a1430non-degenerate integral lattice.1431"""14321433def __init__(self, A, max_elem_divisors=None):1434"""1435Initialize a global genus symbol from a non-degenerate1436integral gram matrix (and possibly information about its1437largest elementary divisors).14381439INPUT:1440A -- a symmetric matrix with integer coefficients1441max_elem_divisors -- the input precision for valuation of1442maximal p-elementary divisor. (OPTIONAL)14431444OUTPUT:1445None14461447EXAMPLES:1448sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring14491450sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1451sage: G = GenusSymbol_global_ring(A);G1452Genus of [2 0 0 0]1453[0 4 0 0]1454[0 0 6 0]1455[0 0 0 8]1456sage: G == loads(dumps(G))1457True14581459"""1460D = A.determinant()1461D = 2*D1462prms = [ p[0] for p in D.factor() ]1463self._representative = A1464self._signature = signature_pair_of_matrix(A)1465self._local_symbols = []1466for p in prms:1467if max_elem_divisors is None:1468val = D.valuation(p)1469symbol = p_adic_symbol(A, p, val = val)1470G = Genus_Symbol_p_adic_ring(p, symbol)1471self._local_symbols.append(G)147214731474def __repr__(self):1475"""1476Returns a string representing the global genus symbol.14771478INPUT:1479None14801481OUTPUT:1482a string14831484EXAMPLES:1485sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring14861487sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1488sage: GS = GenusSymbol_global_ring(A)1489sage: GS.__repr__()1490'Genus of [2 0 0 0]\n[0 4 0 0]\n[0 0 6 0]\n[0 0 0 8]'14911492"""1493return "Genus of %s"%self._representative1494149514961497def __eq__(self, other):1498"""1499Determines if two global genus symbols are equal (not just equivalent!).15001501INPUT:1502a GenusSymbol_global_ring object15031504OUTPUT:1505boolean15061507EXAMPLES:1508sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring15091510sage: A1 = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1511sage: GS1 = GenusSymbol_global_ring(A1)1512sage: A2 = DiagonalQuadraticForm(ZZ, [1,2,3,5]).Hessian_matrix()1513sage: GS2 = GenusSymbol_global_ring(A2)15141515sage: GS1 == GS21516False15171518sage: GS2 == GS11519False15201521sage: GS1 == GS11522True15231524sage: GS2 == GS21525True15261527"""1528if self is other:1529return True1530t = len(self._local_symbols)1531if t != len(other._local_symbols):1532return False1533for i in range(t):1534if self._local_symbols[i] != other._local_symbols[i]:1535return False1536return True1537153815391540def __ne__(self, other):1541"""1542Determines if two global genus symbols are unequal (not just inequivalent!).15431544INPUT:1545a GenusSymbol_global_ring object15461547OUTPUT:1548boolean15491550EXAMPLES:1551sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring15521553sage: A1 = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix()1554sage: GS1 = GenusSymbol_global_ring(A1)1555sage: A2 = DiagonalQuadraticForm(ZZ, [1,2,3,5]).Hessian_matrix()1556sage: GS2 = GenusSymbol_global_ring(A2)15571558sage: GS1 != GS21559True15601561sage: GS2 != GS11562True15631564sage: GS1 != GS11565False15661567sage: GS2 != GS21568False15691570"""1571return not self.__eq__(other)157215731574def signature_pair_of_matrix(self):1575"""1576Returns the signature pair (p, n) of the (non-degenerate)1577global genus symbol, where p is the number of positive1578eigenvalues and n is the number of negative eigenvalues.15791580INPUT:1581None15821583OUTPUT:1584a pair of integers (p, n) each >= 015851586EXAMPLES:1587sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring15881589sage: A = DiagonalQuadraticForm(ZZ, [1,-2,3,4,8,-11]).Hessian_matrix()1590sage: GS = GenusSymbol_global_ring(A)1591sage: GS.signature_pair_of_matrix()1592(4, 2)15931594"""1595return self._signature159615971598def determinant(self):1599"""1600Returns the determinant of this genus, where the determinant1601is the Hessian determinant of the quadratic form whose Gram1602matrix is the Gram matrix giving rise to this global genus1603symbol.16041605INPUT:1606None16071608OUTPUT:1609an integer16101611EXAMPLES:1612sage: from sage.quadratic_forms.genera.genus import GenusSymbol_global_ring16131614sage: A = DiagonalQuadraticForm(ZZ, [1,-2,3,4]).Hessian_matrix()1615sage: GS = GenusSymbol_global_ring(A)1616sage: GS.determinant()1617-38416181619"""1620r, s = self.signature_pair_of_matrix()1621return (-1)**s*misc.prod([ G.determinant() for G in self._local_symbols ])162216231624