def basis_of_short_vectors(self, show_lengths=False, safe_flag=True): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). The safe_flag allows us to select whether we want a copy of the output, or the original output. By default safe_flag = True, so we return a copy of the cached information. If this is set to False, then the routine is much faster but the return values are vulnerable to being corrupted by the user. OUTPUT: a list of vectors, and optionally a list of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)] sage: Q.basis_of_short_vectors(True) ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7]) """ ## Try to use the cached results try: ## Return the appropriate result if show_lengths: if safe_flag: return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths) else: return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths else: if safe_flag: return deepcopy(self.__basis_of_short_vectors) else: return deepcopy(self.__basis_of_short_vectors) except Exception: pass ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() #MS = MatrixSpace(QQ, n) M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: ## DIAGONSTIC #print #print "Starting with vec_len = ", vec_len #print "M_str = ", M_str vec_len += 1 gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose() number_of_vecs = ZZ(gp_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): #print "List at", i, ":", list(gp_mat[i+1,]) new_vec = vector([ZZ(x) for x in list(gp_mat[i+1,])]) vector_list.append(new_vec) ## DIAGNOSTIC #print "number_of_vecs = ", number_of_vecs #print "vector_list = ", vector_list ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## DIAGNOSTIC #print "matrix of vectors = \n", M1 #print "rank of the matrix = ", M1.rank() #print " vec_len = ", vec_len #print M1 ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len // 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = [sorted_matrix.column(i) for i in pivots] pivot_lengths = [self(v) for v in basis] ## DIAGNOSTIC #print "basis = ", basis #print "pivot_lengths = ", pivot_lengths ## Cache the result self.__basis_of_short_vectors = basis self.__basis_of_short_vectors_lenghts = pivot_lengths ## Return the appropriate result if show_lengths: return basis, pivot_lengths else: return basis
def basis_of_short_vectors(self, show_lengths=False, safe_flag=True): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). The safe_flag allows us to select whether we want a copy of the output, or the original output. By default safe_flag = True, so we return a copy of the cached information. If this is set to False, then the routine is much faster but the return values are vulnerable to being corrupted by the user. OUTPUT: a list of vectors, and optionally a list of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)] sage: Q.basis_of_short_vectors(True) ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7]) """ ## Try to use the cached results try: ## Return the appropriate result if show_lengths: if safe_flag: return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths) else: return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths else: if safe_flag: return deepcopy(self.__basis_of_short_vectors) else: return deepcopy(self.__basis_of_short_vectors) except Exception: pass ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() # MS = MatrixSpace(QQ, n) M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: ## DIAGONSTIC # print # print "Starting with vec_len = ", vec_len # print "M_str = ", M_str vec_len += 1 gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose() number_of_vecs = ZZ(gp_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): # print "List at", i, ":", list(gp_mat[i+1,]) new_vec = vector([ZZ(x) for x in list(gp_mat[i + 1,])]) vector_list.append(new_vec) ## DIAGNOSTIC # print "number_of_vecs = ", number_of_vecs # print "vector_list = ", vector_list ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## DIAGNOSTIC # print "matrix of vectors = \n", M1 # print "rank of the matrix = ", M1.rank() # print " vec_len = ", vec_len # print M1 ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len / 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = [sorted_matrix.column(i) for i in pivots] pivot_lengths = [self(v) for v in basis] ## DIAGNOSTIC # print "basis = ", basis # print "pivot_lengths = ", pivot_lengths ## Cache the result self.__basis_of_short_vectors = basis self.__basis_of_short_vectors_lenghts = pivot_lengths ## Return the appropriate result if show_lengths: return basis, pivot_lengths else: return basis
def find_linear_relation(vs, p, M): #This is the old version. The new one is _find_linear_relation r""" Finds a linear relation between a given list of vectors over Z/p^MZ. If they are LI, returns an empty list. INPUT: - ``vs`` -- a list of vectors over Z/p^MZ - ``p`` -- a prime - ``M`` -- positive integer OUTPUT: - A list of p-adic numbers describing the linear relation of the vs """ d = len(vs) R = Qp(p, M) V = R**d if d == 1: z = True for r in range(len(vs[0])): if vs[0][r] != 0: z = False if z: return [R(1, M)] else: return [] # Would be better to use the library as follows. Unfortunately, matsolvemod # is not included in the gen class!! #from sage.libs.pari.gen import pari #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)] #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift())) #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col() #v = A.matsolvemod(p ** M, aug_col) ## Stupid hack here to deal with the fact that I can't get ## matsolvemod to work if B=0 z = True for r in range(len(vs[d-1])): if vs[d-1][r] != 0: z = False if z: return [R(0, M) for a in range(len(vs)-1)] + [1] s = '[' for c in range(d-1): v = vs[c] for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' if c < d - 2: s += ';' s = s + ']' Verbose("s = %s"%(s)) A = gp(s) Verbose("A = %s"%(A)) if len(vs) == 2: A = A.Mat() s = '[' v = vs[d-1] for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' s += ']~' Verbose("s = %s"%(s)) B = gp(s) Verbose("B = %s"%(B)) v = A.mattranspose().matsolvemod(p**M,B) Verbose("v = %s"%(v)) if v == 0: return [] else: ## Move back to SAGE from Pari v = [R(v[a], M) for a in range(1,len(v)+1)] return v + [R(-1, M)]
def _find_linear_relation(Alist, B, p, M): #new! r""" Finds a linear relation between a given list of vectors over Z/p^MZ. If they are LI, returns an empty list. INPUT: - ``vs`` -- a list of vectors over Z/p^MZ - ``p`` -- a prime - ``M`` -- positive integer OUTPUT: - A list of p-adic numbers describing the linear relation of the vs TESTS:: sage: from sage.modular.pollack_stevens.modsym_OMS_families_space import _find_linear_relation sage: R = ZpCA(3, 4) sage: List = [Sequence([O(3^4), 2*3 + 3^2 + 3^3 + O(3^4), 2 + 3 + 3^2 + 2*3^3 + O(3^4), O(3^4), 1 + 3 + 3^2 + O(3^4), 2 + O(3^4), 1 + 3^2 + 3^3 + O(3^4), 2 + O(3^4), 1 + 3 + 3^2 + O(3^4)], universe=R), Sequence([O(3^4), 1 + 3^2 + O(3^4), 2 + 3 + 3^3 + O(3^4), O(3^4), 1 + 3 + 2*3^2 + 3^3 + O(3^4), 1 + 2*3^2 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 1 + 2*3^2 + O(3^4), 1 + 3 + 2*3^2 + 3^3 + O(3^4)], universe=R), Sequence([O(3^4), 2 + O(3^4), 3 + 3^2 + 3^3 + O(3^4), O(3^4), 2*3 + 3^2 + 3^3 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 3^3 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 2*3 + 3^2 + 3^3 + O(3^4)], universe=R)] sage: _find_linear_relation(List[:-1], List[-1], 3, 4) ([1 + 3^2 + O(3^4), 2 + 3 + 2*3^2 + O(3^4)], 2 + 2*3 + 2*3^2 + 2*3^3 + O(3^4)) sage: _find_linear_relation(List[:-1], List[-1], 3, 3) ([1 + 3^2 + O(3^3), 2 + 3 + 2*3^2 + O(3^3)], 2 + 2*3 + 2*3^2 + O(3^3)) sage: List[-1][1] -= 1 sage: _find_linear_relation(List[:-1], List[-1], 3, 4) ([], None) sage: List[-1][1] += 1 + 3^3 sage: _find_linear_relation(List[:-1], List[-1], 3, 4) ([], None) sage: _find_linear_relation(List[:-1], List[-1], 3, 3) ([1 + 3^2 + O(3^3), 2 + 3 + 2*3^2 + O(3^3)], 2 + 2*3 + 2*3^2 + O(3^3)) """ vs = list(Alist) + [B] d = len(vs) R = Qp(p,M) V = R**d if d == 1: z = True for r in range(len(vs[0])): if vs[0][r] != 0: z = False if z: return [R(1, M)] else: return [] # Would be better to use the library as follows. Unfortunately, matsolvemod # is not included in the gen class!! #from sage.libs.pari.gen import pari #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)] #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift())) #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col() #v = A.matsolvemod(p ** M, aug_col) ## Stupid hack here to deal with the fact that I can't get ## matsolvemod to work if B=0 z = True for r in range(len(vs[d-1])): if vs[d-1][r] != 0: z = False if z: return [R(0, M) for a in range(len(vs)-1)], 1 s = '[' for c in range(d-1): v = vs[c] for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' if c < d - 2: s += ';' s = s + ']' Verbose("s = %s"%(s)) A = gp(s) Verbose("A = %s"%(A)) if len(vs) == 2: A = A.Mat() s = '[' v = vs[d-1] for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' s += ']~' Verbose("s = %s"%(s)) B = gp(s) Verbose("B = %s"%(B)) v = A.mattranspose().matsolvemod(p**M,B) Verbose("v = %s"%(v)) if v == 0: return [], None else: ## Move back to SAGE from Pari v = [R(v[a], M) for a in range(1,len(v)+1)] return v, R(-1, M)
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): """ Return a list of lists of short vectors `v`, sorted by length, with Q(`v`) < len_bound. The list in output `[i]` indexes all vectors of length `i`. If the up_to_sign_flag is set to True, then only one of the vectors of the pair `[v, -v]` is listed. Note: This processes the PARI/GP output to always give elements of type `ZZ`. OUTPUT: a list of lists of vectors. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.short_vector_list_up_to_length(3) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], []] sage: Q.short_vector_list_up_to_length(4) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], [], [(0, 1, 0, 0), (0, -1, 0, 0)]] sage: Q.short_vector_list_up_to_length(5) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], [], [(0, 1, 0, 0), (0, -1, 0, 0)], [(1, 1, 0, 0), (-1, -1, 0, 0), (-1, 1, 0, 0), (1, -1, 0, 0), (2, 0, 0, 0), (-2, 0, 0, 0)]] sage: Q.short_vector_list_up_to_length(5, True) [[(0, 0, 0, 0)], [(1, 0, 0, 0)], [], [(0, 1, 0, 0)], [(1, 1, 0, 0), (-1, 1, 0, 0), (2, 0, 0, 0)]] sage: Q = QuadraticForm(matrix(6, [2, 1, 1, 1, -1, -1, 1, 2, 1, 1, -1, -1, 1, 1, 2, 0, -1, -1, 1, 1, 0, 2, 0, -1, -1, -1, -1, 0, 2, 1, -1, -1, -1, -1, 1, 2])) sage: vs = Q.short_vector_list_up_to_length(40) #long time The cases of ``len_bound < 2`` led to exception or infinite runtime before. :: sage: Q.short_vector_list_up_to_length(0) [] sage: Q.short_vector_list_up_to_length(1) [[(0, 0, 0, 0, 0, 0)]] In the case of quadratic forms that are not positive definite an error is raised. :: sage: QuadraticForm(matrix(2, [2, 0, 0, -2])).short_vector_list_up_to_length(3) Traceback (most recent call last): ... ValueError: Quadratic form must be positive definite in order to enumerate short vectors Sometimes, Pari does not compute short vectors correctly. It returns too long vectors. :: sage: mat = matrix(2, [72, 12, 12, 120]) #long time sage: len_bound = 22953421 #long time sage: gp_mat = gp.qfminim(str(gp(mat)), 2 * len_bound - 2)[3] #long time sage: rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ] #long time sage: vec_list = map(vector, zip(*rows)) #long time sage: eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(2) ), " + ".join( "{coeff} * a{i} * a{j}".format(coeff = mat[i,j], i = i, j = j) for i in range(2) for j in range(2) ) ) #long time sage: any( eval_v_cython(*v) == 2 * 22955664 for v in vec_list ) # 22955664 > 22953421 = len_bound #long time True """ if not self.is_positive_definite(): raise ValueError( "Quadratic form must be positive definite in order to enumerate short vectors" ) ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) if len_bound <= 0: return list() elif len_bound == 1: return [[(vector([ZZ(0) for _ in range(self.dim())]))]] ## Generate the short vectors gp_mat = gp.qfminim(M_str, 2 * len_bound - 2)[3] ## We read all n-th entries at once so that not too many sage[...] variables are ## used. This is important when to many vectors are returned. rows = [ map(ZZ, str(gp_mat[i, ])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ] vec_list = map(vector, zip(*rows)) if len(vec_list) > 500: eval_v_cython = cython_lambda( ", ".join("int a{0}".format(i) for i in range(self.dim())), " + ".join( "{coeff} * a{i} * a{j}".format(coeff=self[i, j], i=i, j=j) for i in range(self.dim()) for j in range(i, self.dim()))) eval_v = lambda v: eval_v_cython(*v) else: eval_v = self ## Sort the vectors into lists by their length vec_sorted_list = [list() for i in range(len_bound)] for v in vec_list: v_evaluated = eval_v(v) try: vec_sorted_list[v_evaluated].append(v) if not up_to_sign_flag: vec_sorted_list[v_evaluated].append(-v) except IndexError: ## We deal with a Pari but, that returns longer vectors that requested. ## E.g. : self.matrix() == matrix(2, [72, 12, 12, 120]) ## len_bound = 22953421 ## gives maximal length 22955664 pass ## Add the zero vector by hand zero_vec = vector([ZZ(0) for _ in range(self.dim())]) vec_sorted_list[0].append(zero_vec) ## Return the sorted list return vec_sorted_list
def linear_relation(self, List): r""" Finds a linear relation between the given list of OMSs. If they are LI, returns a list of all 0's. INPUT: - ``List`` -- a list of OMSs OUTPUT: - A list of p-adic numbers describing the linear relation of the list of OMSs """ for Phi in List: assert Phi.valuation() >= 0, "Symbols must be integral" R = self.base() ## NEED A COMMAND FOR RELATIVE PRECISION OF OMS M = List[0].precision_relative() p = self.prime() d = len(List) V = R**d if d == 1: if List[0].is_zero(): return [R(1)] else: return [R(0)] # Would be better to use the library as follows. Unfortunately, matsolvemod # is not included in the gen class!! #from sage.libs.pari.gen import pari #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)] #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift())) #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col() #v = A.matsolvemod(p ** M, aug_col) s = '[' for c in range(d - 1): v = List[c].list_of_total_measures() for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' if c < d - 2: s += ';' s = s + ']' verbose("s = %s" % (s)) A = gp(s) verbose("A = %s" % (A)) if len(List) == 2: A = A.Mat() s = '[' v = List[d - 1].list_of_total_measures() for r in range(len(v)): s += str(ZZ(v[r])) if r < len(v) - 1: s += ',' s += ']~' verbose("s = %s" % (s)) B = gp(s) verbose("B = %s" % (B)) v = A.mattranspose().matsolvemod(p**M, B) verbose("v = %s" % (v)) if v == 0: return [R(0) for a in range(len(v))] else: ## Move back to SAGE from Pari v = [R(v[a]) for a in range(1, len(v) + 1)] return v + [R(-1)]
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): """ Return a list of lists of short vectors `v`, sorted by length, with Q(`v`) < len_bound. The list in output `[i]` indexes all vectors of length `i`. If the up_to_sign_flag is set to True, then only one of the vectors of the pair `[v, -v]` is listed. Note: This processes the PARI/GP output to always give elements of type `ZZ`. OUTPUT: a list of lists of vectors. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.short_vector_list_up_to_length(3) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], []] sage: Q.short_vector_list_up_to_length(4) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], [], [(0, 1, 0, 0), (0, -1, 0, 0)]] sage: Q.short_vector_list_up_to_length(5) [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], [], [(0, 1, 0, 0), (0, -1, 0, 0)], [(1, 1, 0, 0), (-1, -1, 0, 0), (-1, 1, 0, 0), (1, -1, 0, 0), (2, 0, 0, 0), (-2, 0, 0, 0)]] sage: Q.short_vector_list_up_to_length(5, True) [[(0, 0, 0, 0)], [(1, 0, 0, 0)], [], [(0, 1, 0, 0)], [(1, 1, 0, 0), (-1, 1, 0, 0), (2, 0, 0, 0)]] sage: Q = QuadraticForm(matrix(6, [2, 1, 1, 1, -1, -1, 1, 2, 1, 1, -1, -1, 1, 1, 2, 0, -1, -1, 1, 1, 0, 2, 0, -1, -1, -1, -1, 0, 2, 1, -1, -1, -1, -1, 1, 2])) sage: vs = Q.short_vector_list_up_to_length(40) #long time The cases of ``len_bound < 2`` led to exception or infinite runtime before. :: sage: Q.short_vector_list_up_to_length(0) [] sage: Q.short_vector_list_up_to_length(1) [[(0, 0, 0, 0, 0, 0)]] In the case of quadratic forms that are not positive definite an error is raised. :: sage: QuadraticForm(matrix(2, [2, 0, 0, -2])).short_vector_list_up_to_length(3) Traceback (most recent call last): ... ValueError: Quadratic form must be positive definite in order to enumerate short vectors Sometimes, Pari does not compute short vectors correctly. It returns too long vectors. :: sage: mat = matrix(2, [72, 12, 12, 120]) #long time sage: len_bound = 22953421 #long time sage: gp_mat = gp.qfminim(str(gp(mat)), 2 * len_bound - 2)[3] #long time sage: rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ] #long time sage: vec_list = map(vector, zip(*rows)) #long time sage: eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(2) ), " + ".join( "{coeff} * a{i} * a{j}".format(coeff = mat[i,j], i = i, j = j) for i in range(2) for j in range(2) ) ) #long time sage: any( eval_v_cython(*v) == 2 * 22955664 for v in vec_list ) # 22955664 > 22953421 = len_bound #long time True """ if not self.is_positive_definite() : raise ValueError( "Quadratic form must be positive definite in order to enumerate short vectors" ) ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) if len_bound <= 0 : return list() elif len_bound == 1 : return [ [(vector([ZZ(0) for _ in range(self.dim())]))] ] ## Generate the short vectors gp_mat = gp.qfminim(M_str, 2*len_bound - 2)[3] ## We read all n-th entries at once so that not too many sage[...] variables are ## used. This is important when to many vectors are returned. rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ] vec_list = map(vector, zip(*rows)) if len(vec_list) > 500 : eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(self.dim()) ), " + ".join( "{coeff} * a{i} * a{j}".format(coeff = self[i,j], i = i, j = j) for i in range(self.dim()) for j in range(i, self.dim()) ) ) eval_v = lambda v: eval_v_cython(*v) else : eval_v = self ## Sort the vectors into lists by their length vec_sorted_list = [list() for i in range(len_bound)] for v in vec_list: v_evaluated = eval_v(v) try : vec_sorted_list[v_evaluated].append(v) if not up_to_sign_flag : vec_sorted_list[v_evaluated].append(-v) except IndexError : ## We deal with a Pari but, that returns longer vectors that requested. ## E.g. : self.matrix() == matrix(2, [72, 12, 12, 120]) ## len_bound = 22953421 ## gives maximal length 22955664 pass ## Add the zero vector by hand zero_vec = vector([ZZ(0) for _ in range(self.dim())]) vec_sorted_list[0].append(zero_vec) ## Return the sorted list return vec_sorted_list
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): """ Return a list of lists of short vectors `v`, sorted by length, with Q(`v`) < len_bound. The list in output `[i]` indexes all vectors of length `i`. If the up_to_sign_flag is set to True, then only one of the vectors of the pair `[v, -v]` is listed. Note: This processes the PARI/GP output to always give elements of type `ZZ`. OUTPUT: a list of lists of vectors. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.short_vector_list_up_to_length(3) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], []] sage: Q.short_vector_list_up_to_length(4) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], [], [(0, 1, 0, 0), [0, -1, 0, 0]]] sage: Q.short_vector_list_up_to_length(5) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], [], [(0, 1, 0, 0), [0, -1, 0, 0]], [(1, 1, 0, 0), [-1, -1, 0, 0], (-1, 1, 0, 0), [1, -1, 0, 0], (2, 0, 0, 0), [-2, 0, 0, 0]]] sage: Q.short_vector_list_up_to_length(5, True) [[(0, 0, 0, 0)], [(1, 0, 0, 0)], [], [(0, 1, 0, 0)], [(1, 1, 0, 0), (-1, 1, 0, 0), (2, 0, 0, 0)]] """ ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Generate the short vectors gp_mat = gp.qfminim(M_str, 2 * len_bound - 2, Max_number_of_vectors)[3].mattranspose() number_of_rows = gp_mat.matsize()[1] gp_mat_vector_list = [ vector([ZZ(x) for x in gp_mat[i + 1, ]]) for i in range(number_of_rows) ] ## Sort the vectors into lists by their length vec_list = [[] for i in range(len_bound)] for v in gp_mat_vector_list: vec_list[self(v)].append(v) if up_to_sign_flag == False: vec_list[self(v)].append([-x for x in v]) ## Add the zero vector by hand zero_vec = vector([ZZ(0) for _ in range(self.dim())]) vec_list[0].append(zero_vec) ## Return the sorted list return vec_list
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): """ Return a list of lists of short vectors `v`, sorted by length, with Q(`v`) < len_bound. The list in output `[i]` indexes all vectors of length `i`. If the up_to_sign_flag is set to True, then only one of the vectors of the pair `[v, -v]` is listed. Note: This processes the PARI/GP output to always give elements of type `ZZ`. OUTPUT: a list of lists of vectors. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.short_vector_list_up_to_length(3) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], []] sage: Q.short_vector_list_up_to_length(4) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], [], [(0, 1, 0, 0), [0, -1, 0, 0]]] sage: Q.short_vector_list_up_to_length(5) [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], [], [(0, 1, 0, 0), [0, -1, 0, 0]], [(1, 1, 0, 0), [-1, -1, 0, 0], (-1, 1, 0, 0), [1, -1, 0, 0], (2, 0, 0, 0), [-2, 0, 0, 0]]] sage: Q.short_vector_list_up_to_length(5, True) [[(0, 0, 0, 0)], [(1, 0, 0, 0)], [], [(0, 1, 0, 0)], [(1, 1, 0, 0), (-1, 1, 0, 0), (2, 0, 0, 0)]] """ ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Generate the short vectors gp_mat = gp.qfminim(M_str, 2*len_bound-2, Max_number_of_vectors)[3].mattranspose() number_of_rows = gp_mat.matsize()[1] gp_mat_vector_list = [vector([ZZ(x) for x in gp_mat[i+1,]]) for i in range(number_of_rows)] ## Sort the vectors into lists by their length vec_list = [[] for i in range(len_bound)] for v in gp_mat_vector_list: vec_list[self(v)].append(v) if up_to_sign_flag == False: vec_list[self(v)].append([-x for x in v]) ## Add the zero vector by hand zero_vec = vector([ZZ(0) for _ in range(self.dim())]) vec_list[0].append(zero_vec) ## Return the sorted list return vec_list