def c3_func(SUK, prec=106): r""" Return the constant `c_3` from Smart's 1995 TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``c3``, as a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import c3_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: c3_func(SUK) # abs tol 1e-29 0.4257859134798034746197327286726 .. NOTE:: The numerator should be as close to 1 as possible, especially as the rank of the `S`-units grows large REFERENCES: - [Sma1995]_ p. 823 """ R = RealField(prec) all_places = list(SUK.primes()) + SUK.number_field().places(prec) Possible_U = Combinations(all_places, SUK.rank()) c1 = R(0) for U in Possible_U: # first, build the matrix C_{i,U} columns_of_C = [] for unit in SUK.fundamental_units(): columns_of_C.append(column_Log(SUK, unit, U, prec)) C = Matrix(SUK.rank(), SUK.rank(), columns_of_C) # Is it invertible? if abs(C.determinant()) > 10**(-10): poss_c1 = C.inverse().apply_map(abs).norm(Infinity) c1 = R(max(poss_c1, c1)) return R(0.9999999) / (c1*SUK.rank())
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses the PARI/GP function ``qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : x^2 - y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : -1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point is None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]] par = ([Q(pol(x/y)*y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def automorphisms(self): """ Return a list of the automorphisms of the quadratic form. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.number_of_automorphisms() # optional -- souvigner 48 sage: 2^3 * factorial(3) 48 sage: len(Q.automorphisms()) 48 :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() # optional -- souvigner 16 sage: aut = Q.automorphisms() sage: len(aut) 16 sage: print([Q(M) == Q for M in aut]) [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q.automorphisms() [ [1 0 0] [-1 0 0] [0 1 0] [ 0 -1 0] [0 0 1], [ 0 0 -1] ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.automorphisms() Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.automorphisms() """ ## only for definite forms if not self.is_definite(): raise ValueError("not a definite form in QuadraticForm.automorphisms()") ## Check for a cached value try: return self.__automorphisms except AttributeError: pass ## Find a basis of short vectors, and their lengths basis, pivot_lengths = self.basis_of_short_vectors(show_lengths=True) ## List the relevant vectors by length max_len = max(pivot_lengths) vector_list_by_length = self.short_primitive_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis).transpose() Ainv = A.inverse() # A1 = A.inverse() * A.det() # Q1 = A1.transpose() * self.matrix() * A1 ## This is the matrix of Q # Q = self.matrix() * A.det()**2 Q2 = A.transpose() * self.matrix() * A ## This is the matrix of Q in the new basis Q3 = self.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] # ct = 0 ## 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(vector_list_by_length[pivot_lengths[i]]) for i in range(n)]): M = Matrix([vector_list_by_length[pivot_lengths[i]][index_vec[i]] for i in range(n)]).transpose() # Q1 = self.matrix() # if self(M) == self: # ct += 1 # print "ct = ", ct, " M = " # print M # print if M.transpose() * Q3 * M == Q2: ## THIS DOES THE SAME THING! =( Auto_list.append(M * Ainv) ## Cache the answer and return the list self.__automorphisms = Auto_list self.__number_of_automorphisms = len(Auto_list) return Auto_list
def automorphisms(self): """ Return a list of the automorphisms of the quadratic form. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.number_of_automorphisms() # optional -- souvigner 48 sage: 2^3 * factorial(3) 48 sage: len(Q.automorphisms()) 48 :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() # optional -- souvigner 16 sage: aut = Q.automorphisms() sage: len(aut) 16 sage: print([Q(M) == Q for M in aut]) [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q.automorphisms() [ [1 0 0] [-1 0 0] [0 1 0] [ 0 -1 0] [0 0 1], [ 0 0 -1] ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.automorphisms() Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.automorphisms() """ ## only for definite forms if not self.is_definite(): raise ValueError("not a definite form in QuadraticForm.automorphisms()") ## Check for a cached value try: return self.__automorphisms except AttributeError: pass ## Find a basis of short vectors, and their lengths basis, pivot_lengths = self.basis_of_short_vectors(show_lengths=True) ## List the relevant vectors by length max_len = max(pivot_lengths) vector_list_by_length = self.short_primitive_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis).transpose() Ainv = A.inverse() #A1 = A.inverse() * A.det() #Q1 = A1.transpose() * self.matrix() * A1 ## This is the matrix of Q #Q = self.matrix() * A.det()**2 Q2 = A.transpose() * self.matrix() * A ## This is the matrix of Q in the new basis Q3 = self.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] #ct = 0 ## 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(vector_list_by_length[pivot_lengths[i]]) for i in range(n)]): M = Matrix([vector_list_by_length[pivot_lengths[i]][index_vec[i]] for i in range(n)]).transpose() #Q1 = self.matrix() #if self(M) == self: #ct += 1 #print "ct = ", ct, " M = " #print M #print if M.transpose() * Q3 * M == Q2: ## THIS DOES THE SAME THING! =( Auto_list.append(M * Ainv) ## Cache the answer and return the list self.__automorphisms = Auto_list self.__number_of_automorphisms = len(Auto_list) return Auto_list
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses the PARI/GP function ``qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : x^2 - y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : -1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError( "The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point is None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([t.denominator() for t in M.list()]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i, j] * gens[j] for j in range(3)]) for i in [1, 0]] par = ([Q(pol(x / y) * y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0], self), self.Hom(P1)(par[1], check=False)
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='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