def compute_definiteness_string_by_determinants(self): """ Compute the (positive) definiteness of a quadratic form by looking at the signs of all of its upper-left subdeterminants. See also self.compute_definiteness() for more documentation. INPUT: None OUTPUT: string describing the definiteness EXAMPLES: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1,1]) sage: Q.compute_definiteness_string_by_determinants() 'pos_def' :: sage: Q = DiagonalQuadraticForm(ZZ, []) sage: Q.compute_definiteness_string_by_determinants() 'zero' :: sage: Q = DiagonalQuadraticForm(ZZ, [1,0,-1]) sage: Q.compute_definiteness_string_by_determinants() 'degenerate' :: sage: Q = DiagonalQuadraticForm(ZZ, [1,-1]) sage: Q.compute_definiteness_string_by_determinants() 'indefinite' :: sage: Q = DiagonalQuadraticForm(ZZ, [-1,-1]) sage: Q.compute_definiteness_string_by_determinants() 'neg_def' """ ## Sanity Check if not ((self.base_ring() == ZZ) or (self.base_ring() == QQ) or (self.base_ring() == RR)): raise NotImplementedError( "Oops! We can only check definiteness over ZZ, QQ, and RR for now." ) ## Some useful variables n = self.dim() M = self.matrix() ## Deal with the zero-diml form if n == 0: return "zero" ## Deal with degenerate forms if self.det() == 0: return "degenerate" ## Check the sign of the ratios of consecutive determinants of the upper triangular r x r submatrices first_coeff = self[0, 0] for r in range(1, n + 1): I = list(range(r)) new_det = M.matrix_from_rows_and_columns(I, I).det() ## Check for a (non-degenerate) zero -- so it's indefinite if new_det == 0: return "indefinite" ## Check for a change of signs in the upper r x r submatrix -- so it's indefinite if sgn(first_coeff)**r != sgn(new_det): return "indefinite" ## Here all ratios of determinants have the correct sign, so the matrix is (pos or neg) definite. if first_coeff > 0: return "pos_def" else: return "neg_def"
def compute_definiteness_string_by_determinants(self): """ Compute the (positive) definiteness of a quadratic form by looking at the signs of all of its upper-left subdeterminants. See also self.compute_definiteness() for more documentation. INPUT: None OUTPUT: string describing the definiteness EXAMPLES: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1,1]) sage: Q.compute_definiteness_string_by_determinants() 'pos_def' :: sage: Q = DiagonalQuadraticForm(ZZ, []) sage: Q.compute_definiteness_string_by_determinants() 'zero' :: sage: Q = DiagonalQuadraticForm(ZZ, [1,0,-1]) sage: Q.compute_definiteness_string_by_determinants() 'degenerate' :: sage: Q = DiagonalQuadraticForm(ZZ, [1,-1]) sage: Q.compute_definiteness_string_by_determinants() 'indefinite' :: sage: Q = DiagonalQuadraticForm(ZZ, [-1,-1]) sage: Q.compute_definiteness_string_by_determinants() 'neg_def' """ ## Sanity Check if not ((self.base_ring() == ZZ) or (self.base_ring() == QQ) or (self.base_ring() == RR)): raise NotImplementedError, "Oops! We can only check definiteness over ZZ, QQ, and RR for now." ## Some useful variables n = self.dim() M = self.matrix() ## Deal with the zero-diml form if n == 0: return "zero" ## Deal with degenerate forms if self.det() == 0: return "degenerate" ## Check the sign of the ratios of consecutive determinants of the upper triangular r x r submatrices first_coeff = self[0,0] for r in range(1,n+1): I = range(r) new_det = M.matrix_from_rows_and_columns(I, I).det() ## Check for a (non-degenerate) zero -- so it's indefinite if new_det == 0: return "indefinite" ## Check for a change of signs in the upper r x r submatrix -- so it's indefinite if sgn(first_coeff)**r != sgn(new_det): return "indefinite" ## Here all ratios of determinants have the correct sign, so the matrix is (pos or neg) definite. if first_coeff > 0: return "pos_def" else: return "neg_def"
def Watson_mass_at_2(self): """ Returns the local mass of the quadratic form when `p=2`, according to Watson's Theorem 1 of "The 2-adic density of a quadratic form" in Mathematika 23 (1976), pp 94--106. INPUT: none OUTPUT: a rational number EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.Watson_mass_at_2() ## WARNING: WE NEED TO CHECK THIS CAREFULLY! 384 """ ## Make a 0-dim'l quadratic form (for initialization purposes) Null_Form = copy.deepcopy(self) Null_Form.__init__(ZZ, 0) ## Step 0: Compute Jordan blocks and bounds of the scales to keep track of Jordan_Blocks = self.jordan_blocks_by_scale_and_unimodular(2) scale_list = [B[0] for B in Jordan_Blocks] s_min = min(scale_list) s_max = max(scale_list) ## Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale diag_dict = dict( (i, Null_Form) for i in range(s_min - 2, s_max + 4)) ## Initialize with the zero form dim2_dict = dict( (i, Null_Form) for i in range(s_min, s_max + 4)) ## Initialize with the zero form for (s, L) in Jordan_Blocks: i = 0 while (i < L.dim() - 1) and (L[i, i + 1] == 0): ## Find where the 2x2 blocks start i = i + 1 if i < (L.dim() - 1): diag_dict[s] = L.extract_variables(range(i)) ## Diagonal Form dim2_dict[s + 1] = L.extract_variables(range( i, L.dim())) ## Non-diagonal Form else: diag_dict[s] = L #print "diag_dict = ", diag_dict #print "dim2_dict = ", dim2_dict #print "Jordan_Blocks = ", Jordan_Blocks ## Step 2: Compute three dictionaries of invariants (for n_j, m_j, nu_j) n_dict = dict((j, 0) for j in range(s_min + 1, s_max + 2)) m_dict = dict((j, 0) for j in range(s_min, s_max + 4)) for (s, L) in Jordan_Blocks: n_dict[s + 1] = L.dim() if diag_dict[s].dim() == 0: m_dict[s + 1] = ZZ.one() / ZZ(2) * L.dim() else: m_dict[s + 1] = floor(ZZ(L.dim() - 1) / ZZ(2)) #print " ==>", ZZ(L.dim() - 1) / ZZ(2), floor(ZZ(L.dim() - 1) / ZZ(2)) nu_dict = dict((j, n_dict[j + 1] - 2 * m_dict[j + 1]) for j in range(s_min, s_max + 1)) nu_dict[s_max + 1] = 0 #print "n_dict = ", n_dict #print "m_dict = ", m_dict #print "nu_dict = ", nu_dict ## Step 3: Compute the e_j dictionary eps_dict = {} for j in range(s_min, s_max + 3): two_form = (diag_dict[j - 2] + diag_dict[j] + dim2_dict[j]).scale_by_factor(2) j_form = (two_form + diag_dict[j - 1]).base_change_to( IntegerModRing(4)) if j_form.dim() == 0: eps_dict[j] = 1 else: iter_vec = [4] * j_form.dim() alpha = sum([True for x in mrange(iter_vec) if j_form(x) == 0]) beta = sum([True for x in mrange(iter_vec) if j_form(x) == 2]) if alpha > beta: eps_dict[j] = 1 elif alpha == beta: eps_dict[j] = 0 else: eps_dict[j] = -1 #print "eps_dict = ", eps_dict ## Step 4: Compute the quantities nu, q, P, E for the local mass at 2 nu = sum([j * n_dict[j] * (ZZ(n_dict[j] + 1) / ZZ(2) + \ sum([n_dict[r] for r in range(j+1, s_max+2)])) for j in range(s_min+1, s_max+2)]) q = sum([ sgn(nu_dict[j - 1] * (n_dict[j] + sgn(nu_dict[j]))) for j in range(s_min + 1, s_max + 2) ]) P = prod([ prod([1 - QQ(4)**(-i) for i in range(1, m_dict[j] + 1)]) for j in range(s_min + 1, s_max + 2) ]) E = prod([ ZZ(1) / ZZ(2) * (1 + eps_dict[j] * QQ(2)**(-m_dict[j])) for j in range(s_min, s_max + 3) ]) #print "\nFinal Summary:" #print "nu =", nu #print "q = ", q #print "P = ", P #print "E = ", E ## Step 5: Compute the local mass for the prime 2. mass_at_2 = QQ(2)**(nu - q) * P / E return mass_at_2
def Watson_mass_at_2(self): """ Returns the local mass of the quadratic form when `p=2`, according to Watson's Theorem 1 of "The 2-adic density of a quadratic form" in Mathematika 23 (1976), pp 94--106. INPUT: none OUTPUT: a rational number EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.Watson_mass_at_2() ## WARNING: WE NEED TO CHECK THIS CAREFULLY! 384 """ ## Make a 0-dim'l quadratic form (for initialization purposes) Null_Form = copy.deepcopy(self) Null_Form.__init__(ZZ, 0) ## Step 0: Compute Jordan blocks and bounds of the scales to keep track of Jordan_Blocks = self.jordan_blocks_by_scale_and_unimodular(2) scale_list = [B[0] for B in Jordan_Blocks] s_min = min(scale_list) s_max = max(scale_list) ## Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale diag_dict = dict((i, Null_Form) for i in range(s_min-2, s_max + 4)) ## Initialize with the zero form dim2_dict = dict((i, Null_Form) for i in range(s_min, s_max + 4)) ## Initialize with the zero form for (s,L) in Jordan_Blocks: i = 0 while (i < L.dim()-1) and (L[i,i+1] == 0): ## Find where the 2x2 blocks start i = i + 1 if i < (L.dim() - 1): diag_dict[s] = L.extract_variables(range(i)) ## Diagonal Form dim2_dict[s+1] = L.extract_variables(range(i, L.dim())) ## Non-diagonal Form else: diag_dict[s] = L #print "diag_dict = ", diag_dict #print "dim2_dict = ", dim2_dict #print "Jordan_Blocks = ", Jordan_Blocks ## Step 2: Compute three dictionaries of invariants (for n_j, m_j, nu_j) n_dict = dict((j,0) for j in range(s_min+1, s_max+2)) m_dict = dict((j,0) for j in range(s_min, s_max+4)) for (s,L) in Jordan_Blocks: n_dict[s+1] = L.dim() if diag_dict[s].dim() == 0: m_dict[s+1] = ZZ(1)/ZZ(2) * L.dim() else: m_dict[s+1] = floor(ZZ(L.dim() - 1) / ZZ(2)) #print " ==>", ZZ(L.dim() - 1) / ZZ(2), floor(ZZ(L.dim() - 1) / ZZ(2)) nu_dict = dict((j,n_dict[j+1] - 2*m_dict[j+1]) for j in range(s_min, s_max+1)) nu_dict[s_max+1] = 0 #print "n_dict = ", n_dict #print "m_dict = ", m_dict #print "nu_dict = ", nu_dict ## Step 3: Compute the e_j dictionary eps_dict = {} for j in range(s_min, s_max+3): two_form = (diag_dict[j-2] + diag_dict[j] + dim2_dict[j]).scale_by_factor(2) j_form = (two_form + diag_dict[j-1]).base_change_to(IntegerModRing(4)) if j_form.dim() == 0: eps_dict[j] = 1 else: iter_vec = [4] * j_form.dim() alpha = sum([True for x in mrange(iter_vec) if j_form(x) == 0]) beta = sum([True for x in mrange(iter_vec) if j_form(x) == 2]) if alpha > beta: eps_dict[j] = 1 elif alpha == beta: eps_dict[j] = 0 else: eps_dict[j] = -1 #print "eps_dict = ", eps_dict ## Step 4: Compute the quantities nu, q, P, E for the local mass at 2 nu = sum([j * n_dict[j] * (ZZ(n_dict[j] + 1) / ZZ(2) + \ sum([n_dict[r] for r in range(j+1, s_max+2)])) for j in range(s_min+1, s_max+2)]) q = sum([sgn(nu_dict[j-1] * (n_dict[j] + sgn(nu_dict[j]))) for j in range(s_min+1, s_max+2)]) P = prod([ prod([1 - QQ(4)**(-i) for i in range(1, m_dict[j]+1)]) for j in range(s_min+1, s_max+2)]) E = prod([ZZ(1)/ZZ(2) * (1 + eps_dict[j] * QQ(2)**(-m_dict[j])) for j in range(s_min, s_max+3)]) #print "\nFinal Summary:" #print "nu =", nu #print "q = ", q #print "P = ", P #print "E = ", E ## Step 5: Compute the local mass for the prime 2. mass_at_2 = QQ(2)**(nu - q) * P / E return mass_at_2