def unimodular_transformation(self, u): r""" Apply the transformation `u` to the underlying lattice `L`: `u^\tr L u`. INPUT: - `u` -- A unimodular matrix. OUTPUT: - A pair of a discriminant group and a homomorphism from self to this group. """ n_disc = DiscriminantForm(u.transpose() * self._L * u) uinv = u.inverse() basis_images = [ sum(map(operator.mul, b.lift(), self._dual_basis.columns())) for b in self.smith_form_gens() ] basis_images = [ n_disc._dual_basis.solve_right(uinv * b).list() for b in basis_images ] coercion_hom = self.hom([ sum( map(operator.mul, map(ZZ, b), map(n_disc, FreeModule(ZZ, self._L.nrows()).gens()))) for b in basis_images ]) return (n_disc, coercion_hom)
def ambient_module(self): """ Return the ambient module. .. SEEALSO:: :meth:`ambient_vector_space` OUTPUT: The domain of the linear expressions as a free module over the base ring. EXAMPLES:: sage: from sage.geometry.linear_expression import LinearExpressionModule sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z')) sage: L.ambient_module() Vector space of dimension 3 over Rational Field sage: M = LinearExpressionModule(ZZ, ('r', 's')) sage: M.ambient_module() Ambient free module of rank 2 over the principal ideal domain Integer Ring sage: M.ambient_vector_space() Vector space of dimension 2 over Rational Field """ from sage.modules.all import FreeModule return FreeModule(self.base_ring(), self.ngens())
def TestExpansionAmbient_vv(A): """ TESTS:: sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import * sage: ea = TestExpansionAmbient_vv(QQ) """ return EquivariantMonoidPowerSeriesModule( NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation( "1", FreeModule(A, ModularFormTestType_vectorvalued.rank)))
def vector_space(self): """ Return the vector space of the underlying affine space. EXAMPLES:: sage: G = AffineGroup(3, GF(5)) sage: G.vector_space() Vector space of dimension 3 over Finite Field of size 5 """ return FreeModule(self.base_ring(), self.degree())
def _pow_int(self, n): r""" Returns the vector space of dimension `n` over ``self``. EXAMPLES:: sage: QQ^4 Vector space of dimension 4 over Rational Field """ from sage.modules.all import FreeModule return FreeModule(self, n)
def add_unimodular_lattice(self, L=None): r""" Add a unimodular lattice (which is not necessarily positive definite) to the underlying lattice and the resulting discriminant group and an isomorphism to ``self``. INPUT: - `L` -- The Gram matrix of a unimodular lattice or ``None`` (default: ``None``). If ``None``, `E_8` will be added. OUTPUT: - A pair of a discriminant group and a homomorphism from self to this group. """ if L is None: L = matrix(8, [ 2, -1, 0, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 0, 0, -1, 2, -1, 0, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, -1, 0, 0, 0, 0, 2 ]) else: if L.base_ring() is not ZZ or all(e % 2 == 0 for e in L.diagonal()) \ or L.det() != 1 : raise ValueError( "L must be the Gram matrix of an even unimodular lattice") nL = self._L.block_sum(L) n_disc = DiscriminantForm(nL) basis_images = [ sum(map(operator.mul, b.lift(), self._dual_basis.columns())) for b in self.smith_form_gens() ] basis_images = [ n_disc._dual_basis.solve_right( vector(QQ, b.list() + L.nrows() * [0])).list() for b in basis_images ] coercion_hom = self.hom([ sum( map(operator.mul, map(ZZ, b), map(n_disc, FreeModule(ZZ, nL.nrows()).gens()))) for b in basis_images ]) return (n_disc, coercion_hom)
def _split_hyperbolic(L): cur_cor = 2 Lcor = L + cur_cor * identity_matrix(L.nrows()) while not is_positive_definite(Lcor): cur_cor += 2 Lcor = L + cur_cor * identity_matrix(L.nrows()) a = FreeModule(ZZ, L.nrows()).gen(0) if a * L * a >= 0: Lcor_length_inc = max(3, a * L * a) cur_Lcor_length = Lcor_length_inc while True: short_vectors = flatten( QuadraticForm(Lcor).short_vector_list_up_to_length( a * Lcor * a)[cur_Lcor_length - Lcor_length_inc:cur_Lcor_length], max_level=1) for a in short_vectors: if a * L * a < 0: break else: continue break n = -a * L * a // 2 short_vectors = E8.short_vector_list_up_to_length(n + 1)[-1] for v in short_vectors: for w in short_vectors: if v * E8_gram * w == 2 * n - 1: LE8_mat = L.block_sum(E8_gram) v_form = vector(list(a) + list(v)) * LE8_mat w_form = vector(list(a) + list(w)) * LE8_mat Lred_basis = matrix( ZZ, [v_form, w_form ]).right_kernel().basis_matrix().transpose() Lred_basis = matrix(ZZ, Lred_basis) return Lred_basis.transpose() * LE8_mat * Lred_basis
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. INPUT: - ``len_bound`` -- bound for the length of the vectors. - ``up_to_sign_flag`` -- (default: ``False``) if set to True, then only one of the vectors of the pair `[v, -v]` is listed. OUTPUT: A list of lists of vectors such that entry `[i]` contains all vectors of length `i`. 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(8) sage: [len(vs[i]) for i in range(len(vs))] [1, 72, 270, 720, 936, 2160, 2214, 3600] sage: vs = Q.short_vector_list_up_to_length(30) # long time (28s on sage.math, 2014) sage: [len(vs[i]) for i in range(len(vs))] # long time [1, 72, 270, 720, 936, 2160, 2214, 3600, 4590, 6552, 5184, 10800, 9360, 12240, 13500, 17712, 14760, 25920, 19710, 26064, 28080, 36000, 25920, 47520, 37638, 43272, 45900, 59040, 46800, 75600] The cases of ``len_bound < 2`` led to exception or infinite runtime before. :: sage: Q.short_vector_list_up_to_length(-1) [] 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: Q = QuadraticForm(matrix(2, [72, 12, 12, 120])) sage: len_bound_pari = 2*22953421 - 2; len_bound_pari 45906840 sage: vs = list(Q._pari_().qfminim(len_bound_pari)[2]) # long time (18s on sage.math, 2014) sage: v = vs[0]; v # long time [-65, 623]~ sage: v.Vec() * Q._pari_() * v # long time 45907800 """ if not self.is_positive_definite() : raise ValueError( "Quadratic form must be positive definite in order to enumerate short vectors" ) if len_bound <= 0: return [] # Free module in which the vectors live V = FreeModule(ZZ, self.dim()) # Adjust length for PARI. We need to subtract 1 because PARI returns # returns vectors of length less than or equal to b, but we want # strictly less. We need to double because the matrix is doubled. len_bound_pari = 2*(len_bound - 1) # Call PARI's qfminim() parilist = self._pari_().qfminim(len_bound_pari)[2].Vec() # List of lengths parilens = pari(r"(M,v) -> vector(#v, i, (v[i]~ * M * v[i])\2)")(self, parilist) # Sort the vectors into lists by their length vec_sorted_list = [list() for i in range(len_bound)] for i in range(len(parilist)): length = ZZ(parilens[i]) # PARI can sometimes return longer vectors than requested. # E.g. : self.matrix() == matrix(2, [72, 12, 12, 120]) # len_bound = 22953421 # gives maximal length 22955664 if length < len_bound: v = parilist[i] sagevec = V(list(parilist[i])) vec_sorted_list[length].append(sagevec) if not up_to_sign_flag : vec_sorted_list[length].append(-sagevec) # Add the zero vector by hand vec_sorted_list[0].append(V.zero_vector()) return vec_sorted_list
def to_sage(self): """ EXAMPLES: sage: macaulay2(ZZ).to_sage() #optional Integer Ring sage: macaulay2(QQ).to_sage() #optional Rational Field sage: macaulay2(2).to_sage() #optional 2 sage: macaulay2(1/2).to_sage() #optional 1/2 sage: macaulay2(2/1).to_sage() #optional 2 sage: _.parent() #optional Rational Field sage: macaulay2([1,2,3]).to_sage() #optional [1, 2, 3] sage: m = matrix([[1,2],[3,4]]) sage: macaulay2(m).to_sage() #optional [1 2] [3 4] sage: macaulay2(QQ['x,y']).to_sage() #optional Multivariate Polynomial Ring in x, y over Rational Field sage: macaulay2(QQ['x']).to_sage() #optional Univariate Polynomial Ring in x over Rational Field sage: macaulay2(GF(7)['x,y']).to_sage() #optional Multivariate Polynomial Ring in x, y over Finite Field of size 7 sage: macaulay2(GF(7)).to_sage() #optional Finite Field of size 7 sage: macaulay2(GF(49, 'a')).to_sage() #optional Finite Field in a of size 7^2 sage: R.<x,y> = QQ[] sage: macaulay2(x^2+y^2+1).to_sage() #optional x^2 + y^2 + 1 sage: R = macaulay2("QQ[x,y]") #optional sage: I = macaulay2("ideal (x,y)") #optional sage: I.to_sage() #optional Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field sage: X = R/I #optional sage: X.to_sage() #optional Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x, y) sage: R = macaulay2("QQ^2") #optional sage: R.to_sage() #optional Vector space of dimension 2 over Rational Field sage: m = macaulay2('"hello"') #optional sage: m.to_sage() #optional 'hello' """ repr_str = str(self) cls_str = str(self.cls()) cls_cls_str = str(self.cls().cls()) if repr_str == "ZZ": from sage.rings.all import ZZ return ZZ elif repr_str == "QQ": from sage.rings.all import QQ return QQ if cls_cls_str == "Type": if cls_str == "List": return [entry.to_sage() for entry in self] elif cls_str == "Matrix": from sage.matrix.all import matrix base_ring = self.ring().to_sage() entries = self.entries().to_sage() return matrix(base_ring, entries) elif cls_str == "Ideal": parent = self.ring().to_sage() gens = self.gens().entries().flatten().to_sage() return parent.ideal(*gens) elif cls_str == "QuotientRing": #Handle the ZZ/n case if "ZZ" in repr_str and "--" in repr_str: from sage.rings.all import ZZ, GF external_string = self.external_string() zz, n = external_string.split("/") #Note that n must be prime since it is #coming from Macaulay 2 return GF(ZZ(n)) ambient = self.ambient().to_sage() ideal = self.ideal().to_sage() return ambient.quotient(ideal) elif cls_str == "PolynomialRing": from sage.rings.all import PolynomialRing from sage.rings.polynomial.term_order import inv_macaulay2_name_mapping #Get the base ring base_ring = self.coefficientRing().to_sage() #Get a string list of generators gens = str(self.gens())[1:-1] # Check that we are dealing with default degrees, i.e. 1's. if self.degrees().any("x -> x != {1}").to_sage(): raise ValueError, "cannot convert Macaulay2 polynomial ring with non-default degrees to Sage" #Handle the term order external_string = self.external_string() order = None if "MonomialOrder" not in external_string: order = "degrevlex" else: for order_name in inv_macaulay2_name_mapping: if order_name in external_string: order = inv_macaulay2_name_mapping[order_name] if len(gens) > 1 and order is None: raise ValueError, "cannot convert Macaulay2's term order to a Sage term order" return PolynomialRing(base_ring, order=order, names=gens) elif cls_str == "GaloisField": from sage.rings.all import ZZ, GF gf, n = repr_str.split(" ") n = ZZ(n) if n.is_prime(): return GF(n) else: gen = str(self.gens())[1:-1] return GF(n, gen) elif cls_str == "Boolean": if repr_str == "true": return True elif repr_str == "false": return False elif cls_str == "String": return str(repr_str) elif cls_str == "Module": from sage.modules.all import FreeModule if self.isFreeModule().to_sage(): ring = self.ring().to_sage() rank = self.rank().to_sage() return FreeModule(ring, rank) else: #Handle the integers and rationals separately if cls_str == "ZZ": from sage.rings.all import ZZ return ZZ(repr_str) elif cls_str == "QQ": from sage.rings.all import QQ repr_str = self.external_string() if "/" not in repr_str: repr_str = repr_str + "/1" return QQ(repr_str) m2_parent = self.cls() parent = m2_parent.to_sage() if cls_cls_str == "PolynomialRing": from sage.misc.sage_eval import sage_eval gens_dict = parent.gens_dict() return sage_eval(self.external_string(), gens_dict) from sage.misc.sage_eval import sage_eval try: return sage_eval(repr_str) except Exception: raise NotImplementedError, "cannot convert %s to a Sage object" % repr_str
def cohomology(self, degree=None, weight=None, dim=False): r""" Return the bundle cohomology groups. INPUT: - ``degree`` -- ``None`` (default) or an integer. The degree of the cohomology group. - ``weight`` -- ``None`` (default) or a tuple of integers or a `M`-lattice point. A point in the dual lattice of the fan defining a torus character. The weight of the cohomology group. - ``dim`` -- Boolean (default: ``False``). Whether to return vector spaces or only their dimension. OUTPUT: The cohomology group of given cohomological ``degree`` and torus ``weight``. * If no ``weight`` is specified, the unweighted group (sum over all weights) is returned. * If no ``degree`` is specified, a dictionary whose keys are integers and whose values are the cohomology groups is returned. If, in addition, ``dim=True``, then an integral vector of the dimensions is returned. EXAMPLES:: sage: V = toric_varieties.P2().sheaves.tangent_bundle() sage: V.cohomology(degree=0, weight=(0,0)) Vector space of dimension 2 over Rational Field sage: V.cohomology(weight=(0,0), dim=True) (2, 0, 0) sage: for i,j in cartesian_product((list(range(-2,3)), list(range(-2,3)))): ....: HH = V.cohomology(weight=(i,j), dim=True) ....: if HH.is_zero(): continue ....: print('H^*i(P^2, TP^2)_M({}, {}) = {}'.format(i,j,HH)) H^*i(P^2, TP^2)_M(-1, 0) = (1, 0, 0) H^*i(P^2, TP^2)_M(-1, 1) = (1, 0, 0) H^*i(P^2, TP^2)_M(0, -1) = (1, 0, 0) H^*i(P^2, TP^2)_M(0, 0) = (2, 0, 0) H^*i(P^2, TP^2)_M(0, 1) = (1, 0, 0) H^*i(P^2, TP^2)_M(1, -1) = (1, 0, 0) H^*i(P^2, TP^2)_M(1, 0) = (1, 0, 0) """ from sage.modules.all import FreeModule if weight is None: raise NotImplementedError('sum over weights is not implemented') else: weight = self.variety().fan().dual_lattice()(weight) weight.set_immutable() if degree is not None: return self.cohomology(weight=weight, dim=dim)[degree] C = self.cohomology_complex(weight) space_dim = self._variety.dimension() C_homology = C.homology() HH = dict() for d in range(space_dim + 1): try: HH[d] = C_homology[d] except KeyError: HH[d] = FreeModule(self.base_ring(), 0) if dim: HH = vector(ZZ, [HH[i].rank() for i in range(space_dim + 1)]) return HH
def _find_complete_set_of_restriction_vectors(L, R, additional_s=0, reduction_function=None): r""" Given a set R of elements in L^# (e.g. representatives for ( L^# / L ) / \pm 1) find a complete set of restriction vectors. (See [GKR]) INPUT: - `L` -- A quadratic form. - `R` -- A list of tuples or vectors in L \otimes QQ (with given coordinates). - ``additional_s`` -- A non-negative integer; Number of additional elements of `L` that should be returned. - ``reduction_function`` -- A function that takes a tuple representing an element in `L^\#` and returs a pair of a reduced element in `L^\#` and a sign. OUTPUT: - A set S of pairs, the first of which is a vector corresponding to an element in L, and the second of which is an integer. TESTS:: sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _find_complete_set_of_restriction_vectors sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import * sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2]))) sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives) [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)] sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives, reduction_function = indices.reduce_r) [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)] :: sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _local_restriction_matrix sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(4, [2,0,0,1, 0,2,0,1, 0,0,2,1, 1,1,1,2]))) sage: S = _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives) sage: _local_restriction_matrix(indices._r_representatives, S).rank() 4 """ R = [map(vector, rs) for rs in R] length_inc = 5 max_length = 5 cur_length = 1 short_vectors = L.short_vector_list_up_to_length(max_length, True) S = list() restriction_space = FreeModule(QQ, len(R)).span([]) while (len(S) < len(R) + additional_s): while len(short_vectors[cur_length]) == 0: cur_length += 1 if max_length >= cur_length: max_length += length_inc short_vectors = L.short_vector_list_up_to_length( max_length, True) s = vector(short_vectors[cur_length].pop()) rcands = Set([s.dot_product(r) for rs in R for r in rs]) for r in rcands: v = _eval_restriction_vector(R, s, r, reduction_function) if len(S) - restriction_space.rank() < additional_s \ or v not in restriction_space : S.append((s, r)) restriction_space = restriction_space + FreeModule( QQ, len(R)).span([v]) if len(S) == len(R) + additional_s: break return S
def gens(self): # FIXME: This is incorrect for almost all indices m return [(1, tuple(self.__L_size * [0]))] + [ (1, tuple(r)) for r in FreeModule(ZZ, self.__L_size).basis() ]
def split_off_unimodular_lattice(self, L_basis_matrix, check=True): r""" Split off a unimodular lattice that is an orthogonal summand of self. INPUT: - ``L_basis_matrix`` -- A matrix over `\ZZ` whose number of rows equals the size of the underlying lattice. - ``check`` -- A boolean (default: ``True``). If ``True`` it will be checked whether the given sublattice is a direct summand. OUTPUT: - A pair of a discriminant group and a homomorphism from self to this group. """ if check and L_basis_matrix.base_ring() is not ZZ: raise ValueError("L_basis_matrix must define a sublattice") pre_bases = matrix(ZZ, [ [ l * self._L * b for l in L_basis_matrix.columns()] for b in FreeModule(ZZ, self._L.nrows()).gens() ]) \ .augment(identity_matrix(ZZ, self._L.nrows())).echelon_form() if check and pre_bases[:L_basis_matrix.ncols(), :L_basis_matrix.ncols( )] != identity_matrix(ZZ, L_basis_matrix.ncols()): raise ValueError( "The sublattice defined by L_basis_matrix must be unimodular") K_basis_matrix = pre_bases[L_basis_matrix.ncols():, L_basis_matrix.ncols():].transpose() if check and L_basis_matrix.column_module( ) + K_basis_matrix.column_module() != FreeModule(ZZ, self._L.nrows()): raise ValueError( "The sublattice defined by L_basis_matrix must be an orthogonal summand" ) K = K_basis_matrix.transpose() * self._L * K_basis_matrix n_disc = DiscriminantForm(K) total_basis_matrix = L_basis_matrix.change_ring(QQ).augment( K_basis_matrix * n_disc._dual_basis) basis_images = [ sum(map(operator.mul, b.lift(), self._dual_basis.columns())) for b in self.smith_form_gens() ] basis_images = [ total_basis_matrix.solve_right(b)[-K_basis_matrix.ncols():] for b in basis_images ] coercion_hom = self.hom([ sum( map(operator.mul, map(ZZ, b), map(n_disc, FreeModule(ZZ, K.nrows()).gens()))) for b in basis_images ]) return (n_disc, coercion_hom)
def _find_complete_set_of_restriction_vectors(L, R, additional_s = 0, reduction_function = None) : r""" Given a set R of elements in L^# (e.g. representatives for ( L^# / L ) / \pm 1) find a complete set of restriction vectors. (See [GKR]) INPUT: - `L` -- A quadratic form. - `R` -- A list of tuples or vectors in L \otimes QQ (with given coordinates). - ``additional_s`` -- A non-negative integer; Number of additional elements of `L` that should be returned. - ``reduction_function`` -- A function that takes a tuple representing an element in `L^\#` and returs a pair of a reduced element in `L^\#` and a sign. OUTPUT: - A set S of pairs, the first of which is a vector corresponding to an element in L, and the second of which is an integer. TESTS:: sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _find_complete_set_of_restriction_vectors sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import * sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2]))) sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives) [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)] sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives, reduction_function = indices.reduce_r) [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)] :: sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _local_restriction_matrix sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(4, [2,0,0,1, 0,2,0,1, 0,0,2,1, 1,1,1,2]))) sage: S = _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives) sage: _local_restriction_matrix(indices._r_representatives, S).rank() 4 """ R = [map(vector, rs) for rs in R] length_inc = 5 max_length = 5 cur_length = 1 short_vectors = L.short_vector_list_up_to_length(max_length, True) S = list() restriction_space = FreeModule(QQ, len(R)).span([]) while (len(S) < len(R) + additional_s ) : while len(short_vectors[cur_length]) == 0 : cur_length += 1 if max_length >= cur_length : max_length += length_inc short_vectors = L.short_vector_list_up_to_length(max_length, True) s = vector( short_vectors[cur_length].pop() ) rcands = Set([ s.dot_product(r) for rs in R for r in rs ]) for r in rcands : v = _eval_restriction_vector(R, s, r, reduction_function) if len(S) - restriction_space.rank() < additional_s \ or v not in restriction_space : S.append((s, r)) restriction_space = restriction_space + FreeModule(QQ, len(R)).span([v]) if len(S) == len(R) + additional_s : break return S
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. INPUT: - ``len_bound`` -- bound for the length of the vectors. - ``up_to_sign_flag`` -- (default: ``False``) if set to True, then only one of the vectors of the pair `[v, -v]` is listed. OUTPUT: A list of lists of vectors such that entry `[i]` contains all vectors of length `i`. 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(8) sage: [len(vs[i]) for i in range(len(vs))] [1, 72, 270, 720, 936, 2160, 2214, 3600] sage: vs = Q.short_vector_list_up_to_length(30) # long time (28s on sage.math, 2014) sage: [len(vs[i]) for i in range(len(vs))] # long time [1, 72, 270, 720, 936, 2160, 2214, 3600, 4590, 6552, 5184, 10800, 9360, 12240, 13500, 17712, 14760, 25920, 19710, 26064, 28080, 36000, 25920, 47520, 37638, 43272, 45900, 59040, 46800, 75600] The cases of ``len_bound < 2`` led to exception or infinite runtime before. :: sage: Q.short_vector_list_up_to_length(-1) [] 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: Q = QuadraticForm(matrix(2, [72, 12, 12, 120])) sage: len_bound_pari = 2*22953421 - 2; len_bound_pari 45906840 sage: vs = list(Q._pari_().qfminim(len_bound_pari)[2]) # long time (18s on sage.math, 2014) sage: v = vs[0]; v # long time [-65, 623]~ sage: v.Vec() * Q._pari_() * v # long time 45907800 """ if not self.is_positive_definite(): raise ValueError("Quadratic form must be positive definite in order to enumerate short vectors") if len_bound <= 0: return [] # Free module in which the vectors live V = FreeModule(ZZ, self.dim()) # Adjust length for PARI. We need to subtract 1 because PARI returns # returns vectors of length less than or equal to b, but we want # strictly less. We need to double because the matrix is doubled. len_bound_pari = 2 * (len_bound - 1) # Call PARI's qfminim() parilist = self._pari_().qfminim(len_bound_pari)[2].Vec() # List of lengths parilens = pari(r"(M,v) -> vector(#v, i, (v[i]~ * M * v[i])\2)")(self, parilist) # Sort the vectors into lists by their length vec_sorted_list = [list() for i in range(len_bound)] for i in range(len(parilist)): length = ZZ(parilens[i]) # PARI can sometimes return longer vectors than requested. # E.g. : self.matrix() == matrix(2, [72, 12, 12, 120]) # len_bound = 22953421 # gives maximal length 22955664 if length < len_bound: v = parilist[i] sagevec = V(list(parilist[i])) vec_sorted_list[length].append(sagevec) if not up_to_sign_flag: vec_sorted_list[length].append(-sagevec) # Add the zero vector by hand vec_sorted_list[0].append(V.zero_vector()) return vec_sorted_list
def __init__(self, L, reduced=True, weak_forms=False): r""" INPUT: - `L` -- An even quadratic form, the index of the associated Jacobi forms. - ``reduced`` -- If ``True`` the reduction of Fourier indices with respect to the full Jacobi group will be considered. - ``weak_forms`` -- If ``False`` the condition `|L| L^{-1} r**2 <= 4 |L| n` will be imposed. NOTE: The Fourier expansion of a form is assumed to be indexed `\sum c(n,r) q^n \zeta^r` . The indices are pairs `(n, r)`. TESTS: sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import JacobiFormD1Indices sage: JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))._r_representatives [[(0, 0)], [(-1, -1), (0, 1), (1, 0)], [(1, 1), (0, -1), (-1, 0)]] sage: JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))._r_reduced_representatives [(0, 0), (1, 1)] """ self.__L = L ## This is two times the adjoint of L, which in all cases will be even self.__Ladj = QuadraticForm(2 * L.matrix().adjoint()) self.__L_size = L.matrix().nrows() self.__reduced = reduced self.__weak_forms = weak_forms # We fix representatives for ZZ^N / L ZZ^N with minimal norm. Ladj = self.__Ladj preliminary_reps = [ r.lift() for r in FreeModule(ZZ, L.matrix().nrows()) / L.matrix().row_module() ] max_norm = max(Ladj(r) for r in preliminary_reps) short_vectors = [ map(vector, rrs) for rrs in Ladj.short_vector_list_up_to_length(max_norm + 1) ] self.__L_span = L.matrix().row_space() representatives = list() for r in preliminary_reps: representatives.append(list()) for rrs in short_vectors: for rr in rrs: if r - rr in self.__L_span: representatives[-1].append(tuple(rr)) if len(representatives[-1]) != 0: break # This is a list of lists. All elements in the interior lists # have the same norm and the set of first elements of the interior # lists is a set of representatives for ZZ^N / L ZZ^N self._r_representatives = representatives representatives = list() for rs in self._r_representatives: if any(r in representatives or tuple(map(operator.neg, r)) in representatives for r in rs): continue for ri in rs[0]: if ri > 0: representatives.append(rs[0]) break if ri < 0: representatives.append(tuple(map(operator.neg, rs[0]))) break else: representatives.append(rs[0]) self._r_reduced_representatives = representatives
nmb_forms_coords = row_groups[-1][2] + row_groups[-1][3] ch1 = JacobiFormD1WeightCharacter(k) for (s, m, start, length) in row_groups: row_labels_dict = row_labels[m] for f in jacobi_forms[m].graded_submodule(None).basis(): f = f.fourier_expansion() v = vector(ZZ, len(row_labels_dict)) for (l, i) in row_labels_dict.iteritems(): v[i] = f[(ch1, l)] forms.append( vector(start * [0] + v.list() + (nmb_forms_coords - start - length) * [0])) if relation_precision == precision: restriction_expansion = FreeModule(QQ, nmb_forms_coords).span(forms) else: restriction_expansion_matrix__big = matrix(len(forms), nmb_forms_coords, forms).transpose() restriction_expansion_matrix = restriction_expansion_matrix__big.matrix_from_rows( row_indices__small) restriction_expansion = restriction_expansion_matrix.column_module() restriction_expansions = global_restriction_matrix.column_module( ).intersection(restriction_expansion) restriction_pullback = global_restriction_matrix.solve_right(restriction_expansions.basis_matrix().transpose()).column_space() \ + global_restriction_matrix.right_kernel().change_ring(QQ) jacobi_expansions = restriction_pullback.intersection( global_relation_matrix.right_kernel())