def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision='sturm', check_local_equivalence=True): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If return_matrix is True, then we also return the transformation matrix M so that self(M) == other. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.(Q1) # optional -- souvigner True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) # optional -- souvigner sage: Q(MM) == Q1 # optional -- souvigner True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) # optional -- souvigner False sage: Q1.is_globally_equivalent_to(Q3) # optional -- souvigner True sage: M = Q1.is_globally_equivalent_to(Q3, True) ; M # optional -- souvigner [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 # optional -- souvigner True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() """ ## only for definite forms if not self.is_definite(): raise ValueError( "not a definite form in QuadraticForm.is_globally_equivalent_to()") ## Check that other is a QuadraticForm #if not isinstance(other, QuadraticForm): if not is_QuadraticForm(other): raise TypeError( "Oops! You must compare two quadratic forms, but the argument is not a quadratic form. =(" ) ## Now use the Souvigner code by default! =) return other.is_globally_equivalent__souvigner( self, return_matrix ) ## Note: We switch this because the Souvigner code has the opposite mapping convention to us. (It takes the second argument to the first!) ## ---------------------------------- Unused Code below --------------------------------------------------------- ## Check if the forms are locally equivalent if (check_local_equivalence == True): if not self.is_locally_equivalent_to(other): return False ## Check that the forms have the same theta function up to the desired precision (this can be set so that it determines the cusp form) if check_theta_to_precision is not None: if self.theta_series( check_theta_to_precision, var_str='', safe_flag=False) != other.theta_series( check_theta_to_precision, var_str='', safe_flag=False): return False ## Make all possible matrices which give an isomorphism -- can we do this more intelligently? ## ------------------------------------------------------------------------------------------ ## Find a basis of short vectors for one form, and try to match them with vectors of that length in the other one. basis_for_self, self_lengths = self.basis_of_short_vectors( show_lengths=True) max_len = max(self_lengths) short_vectors_of_other = other.short_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis_for_self).transpose() Q2 = A.transpose() * self.matrix( ) * A ## This is the matrix of 'self' in the new basis Q3 = other.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] ## DIAGNOSTIC #print "n = " + str(n) #print "pivot_lengths = " + str(pivot_lengths) #print "vector_list_by_length = " + str(vector_list_by_length) #print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange( [len(short_vectors_of_other[self_lengths[i]]) for i in range(n)]): M = Matrix([ short_vectors_of_other[self_lengths[i]][index_vec[i]] for i in range(n) ]).transpose() if M.transpose() * Q3 * M == Q2: if return_matrix: return A * M.inverse() else: return True ## If we got here, then there is no isomorphism return False
def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision=None, check_local_equivalence=None): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If ``return_matrix`` is True, then we return the transformation matrix `M` so that ``self(M) == other``. INPUT: - ``self``, ``other`` -- positive definite integral quadratic forms - ``return_matrix`` -- (boolean, default ``False``) return the transformation matrix instead of a boolean OUTPUT: - if ``return_matrix`` is ``False``: a boolean - if ``return_matrix`` is ``True``: either ``False`` or the transformation matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.is_globally_equivalent_to(Q1) True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) sage: Q(MM) == Q1 True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) False sage: Q1.is_globally_equivalent_to(Q2, return_matrix=True) False sage: Q1.is_globally_equivalent_to(Q3) True sage: M = Q1.is_globally_equivalent_to(Q3, True); M [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() ALGORITHM: this uses the PARI function ``qfisom()``, implementing an algorithm by Plesken and Souvignier. """ if check_theta_to_precision is not None: from sage.misc.superseded import deprecation deprecation( 19111, "The check_theta_to_precision argument is deprecated and ignored") if check_local_equivalence is not None: from sage.misc.superseded import deprecation deprecation( 19111, "The check_local_equivalence argument is deprecated and ignored") ## Check that other is a QuadraticForm if not is_QuadraticForm(other): raise TypeError( "you must compare two quadratic forms, but the argument is not a quadratic form" ) ## only for definite forms if not self.is_definite() or not other.is_definite(): raise ValueError( "not a definite form in QuadraticForm.is_globally_equivalent_to()") mat = other._pari_().qfisom(self) if not mat: return False if return_matrix: return mat.sage() else: return True
def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision='sturm', check_local_equivalence=True): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If return_matrix is True, then we also return the transformation matrix M so that self(M) == other. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.(Q1) # optional -- souvigner True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) # optional -- souvigner sage: Q(MM) == Q1 # optional -- souvigner True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) # optional -- souvigner False sage: Q1.is_globally_equivalent_to(Q3) # optional -- souvigner True sage: M = Q1.is_globally_equivalent_to(Q3, True) ; M # optional -- souvigner [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 # optional -- souvigner True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() """ ## only for definite forms if not self.is_definite(): raise ValueError, "not a definite form in QuadraticForm.is_globally_equivalent_to()" ## Check that other is a QuadraticForm #if not isinstance(other, QuadraticForm): if not is_QuadraticForm(other): raise TypeError, "Oops! You must compare two quadratic forms, but the argument is not a quadratic form. =(" ## Now use the Souvigner code by default! =) return other.is_globally_equivalent__souvigner(self, return_matrix) ## Note: We switch this because the Souvigner code has the opposite mapping convention to us. (It takes the second argument to the first!) ## ---------------------------------- Unused Code below --------------------------------------------------------- ## Check if the forms are locally equivalent if (check_local_equivalence == True): if not self.is_locally_equivalent_to(other): return False ## Check that the forms have the same theta function up to the desired precision (this can be set so that it determines the cusp form) if check_theta_to_precision != None: if self.theta_series(check_theta_to_precision, var_str='', safe_flag=False) != other.theta_series(check_theta_to_precision, var_str='', safe_flag=False): return False ## Make all possible matrices which give an isomorphism -- can we do this more intelligently? ## ------------------------------------------------------------------------------------------ ## Find a basis of short vectors for one form, and try to match them with vectors of that length in the other one. basis_for_self, self_lengths = self.basis_of_short_vectors(show_lengths=True) max_len = max(self_lengths) short_vectors_of_other = other.short_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis_for_self).transpose() Q2 = A.transpose() * self.matrix() * A ## This is the matrix of 'self' in the new basis Q3 = other.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] ## DIAGNOSTIC #print "n = " + str(n) #print "pivot_lengths = " + str(pivot_lengths) #print "vector_list_by_length = " + str(vector_list_by_length) #print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange([len(short_vectors_of_other[self_lengths[i]]) for i in range(n)]): M = Matrix([short_vectors_of_other[self_lengths[i]][index_vec[i]] for i in range(n)]).transpose() if M.transpose() * Q3 * M == Q2: if return_matrix: return A * M.inverse() else: return True ## If we got here, then there is no isomorphism return False
def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision=None, check_local_equivalence=None): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If ``return_matrix`` is True, then we return the transformation matrix `M` so that ``self(M) == other``. INPUT: - ``self``, ``other`` -- positive definite integral quadratic forms - ``return_matrix`` -- (boolean, default ``False``) return the transformation matrix instead of a boolean OUTPUT: - if ``return_matrix`` is ``False``: a boolean - if ``return_matrix`` is ``True``: either ``False`` or the transformation matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.is_globally_equivalent_to(Q1) True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) sage: Q(MM) == Q1 True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) False sage: Q1.is_globally_equivalent_to(Q2, return_matrix=True) False sage: Q1.is_globally_equivalent_to(Q3) True sage: M = Q1.is_globally_equivalent_to(Q3, True); M [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() ALGORITHM: this uses the PARI function ``qfisom()``, implementing an algorithm by Plesken and Souvignier. """ if check_theta_to_precision is not None: from sage.misc.superseded import deprecation deprecation(19111, "The check_theta_to_precision argument is deprecated and ignored") if check_local_equivalence is not None: from sage.misc.superseded import deprecation deprecation(19111, "The check_local_equivalence argument is deprecated and ignored") ## Check that other is a QuadraticForm if not is_QuadraticForm(other): raise TypeError("you must compare two quadratic forms, but the argument is not a quadratic form") ## only for definite forms if not self.is_definite() or not other.is_definite(): raise ValueError("not a definite form in QuadraticForm.is_globally_equivalent_to()") mat = other._pari_().qfisom(self) if not mat: return False if return_matrix: return mat.sage() else: return True