def modform_cusp_info(calc, S, l, precLimit): """ This goes through all the cusps and compares the space given by `(f|R)[S]` with the space of Elliptic modular forms expansion at those cusps. """ assert l == S.det() assert list(calc.curlS) == [S] D = calc.D HermWeight = calc.HermWeight reducedCurlFSize = calc.matrixColumnCount herm_modform_fe_expannsion = FreeModule(QQ, reducedCurlFSize) if not Integer(l).is_squarefree(): # The calculation of the cusp expansion space takes very long here, thus # we skip them for now. return None for cusp in Gamma0(l).cusps(): if cusp == Infinity: continue M = cusp_matrix(cusp) try: gamma, R, tM = solveR(M, S, space=CurlO(D)) except Exception: print (M, S) raise R.set_immutable() # for caching, we need it hashable herm_modforms = herm_modform_fe_expannsion.echelonized_basis_matrix().transpose() ell_R_denom, ell_R_order, M_R = calcMatrixTrans(calc, R) CycloDegree_R = CyclotomicField(ell_R_order).degree() print "M_R[0] nrows, ell_R_denom, ell_R_order, Cyclo degree:", \ M_R[0].nrows(), ell_R_denom, ell_R_order, CycloDegree_R # The maximum precision we can use is M_R[0].nrows(). # However, that can be quite huge (e.g. 600). ce_prec = min(precLimit, M_R[0].nrows()) ce = cuspExpansions(level=l, weight=2*HermWeight, prec=ce_prec) ell_M_denom, ell_M = ce.expansion_at(SL2Z(M)) print "ell_M_denom, ell_M nrows:", ell_M_denom, ell_M.nrows() ell_M_order = ell_R_order # not sure here. just try the one from R. toCyclPowerBase would fail if this doesn't work # CyclotomicField(l / prod(l.prime_divisors())) should also work. # Transform to same denom. denom_lcm = int(lcm(ell_R_denom, ell_M_denom)) ell_M = addRows(ell_M, denom_lcm / ell_M_denom) M_R = [addRows(M_R_i, denom_lcm / ell_R_denom) for M_R_i in M_R] ell_R_denom = ell_M_denom = denom_lcm print "new denom:", denom_lcm assert ell_R_denom == ell_M_denom # ell_M rows are the elliptic FE. M_R[i] columns are the elliptic FE. # We expect that M_R gives a higher precision for the ell FE. I'm not sure # if this is always true but we expect it here (maybe not needed, though). print "precision of M_R[0], ell_M, wanted:", M_R[0].nrows(), ell_M.ncols(), ce_prec assert ell_M.ncols() >= ce_prec prec = min(M_R[0].nrows(), ell_M.ncols()) # cut to have same precision M_R = [M_R_i[:prec,:] for M_R_i in M_R] ell_M = ell_M[:,:prec] assert ell_M.ncols() == M_R[0].nrows() == prec print "M_R[0] rank, herm rank, mult rank:", \ M_R[0].rank(), herm_modforms.rank(), (M_R[0] * herm_modforms).rank() ell_R = [M_R_i * herm_modforms for M_R_i in M_R] # I'm not sure on this. Seems to be true and it simplifies things in the following. assert ell_M_order <= ell_R_order, "{0}".format((ell_M_order, ell_R_order)) assert ell_R_order % ell_M_order == 0, "{0}".format((ell_M_order, ell_R_order)) # Transform to same Cyclomotic Field in same power base. ell_M2 = toCyclPowerBase(ell_M, ell_M_order) ell_R2 = toLowerCyclBase(ell_R, ell_R_order, ell_M_order) # We must work with the matrix. maybe we should transform hf_M instead to a # higher order field instead, if this ever fails (I'm not sure). assert ell_R2 is not None assert len(ell_M2) == len(ell_R2) # They should have the same power base & same degree now. print "ell_M2[0], ell_R2[0] rank with order %i:" % ell_M_order, ell_M2[0].rank(), ell_R2[0].rank() assert len(M_R) == len(ell_M2) for i in range(len(ell_M2)): ell_M_space = ell_M2[i].row_space() ell_R_space = ell_R2[i].column_space() merged = ell_M_space.intersection(ell_R_space) herm_modform_fe_expannsion_Ci = M_R[i].solve_right( merged.basis_matrix().transpose() ) herm_modform_fe_expannsion_Ci_module = herm_modform_fe_expannsion_Ci.column_module() herm_modform_fe_expannsion_Ci_module += M_R[i].right_kernel() extra_check_on_herm_superspace( vs=herm_modform_fe_expannsion_Ci_module, D=D, B_cF=calc.B_cF, HermWeight=HermWeight ) herm_modform_fe_expannsion = herm_modform_fe_expannsion.intersection( herm_modform_fe_expannsion_Ci_module ) print "power", i, merged.dimension(), herm_modform_fe_expannsion_Ci_module.dimension(), \ herm_modform_fe_expannsion.dimension() current_dimension = herm_modform_fe_expannsion.dimension() return herm_modform_fe_expannsion
def is_finite(self): r""" Check whether the group is finite. A group is finite if and only if it is conjugate to a (finite) subgroup of O(2). This is actually also true in higher dimensions. EXAMPLES:: sage: from flatsurf.geometry.finitely_generated_matrix_group import FinitelyGenerated2x2MatrixGroup sage: G = FinitelyGenerated2x2MatrixGroup([identity_matrix(2)]) sage: G.is_finite() True sage: t = matrix(2, [2,1,1,1]) sage: m1 = matrix([[0,1],[-1,0]]) sage: m2 = matrix([[1,-1],[1,0]]) sage: FinitelyGenerated2x2MatrixGroup([m1]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m2]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m1,m2]).is_finite() False sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t,t*m2*~t]).is_finite() False sage: from flatsurf.geometry.polygon import number_field_elements_from_algebraics sage: c5 = QQbar.zeta(5).real() sage: s5 = QQbar.zeta(5).imag() sage: K, (c5,s5) = number_field_elements_from_algebraics([c5,s5]) sage: r = matrix(K, 2, [c5,-s5,s5,c5]) sage: FinitelyGenerated2x2MatrixGroup([m1,r]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t,t*r*~t]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m2,r]).is_finite() False sage: FinitelyGenerated2x2MatrixGroup([t*m2*~t, t*r*~t]).is_finite() False """ # determinant and trace tests # (the code actually check that each generator is of finite order) for m in self._generators: if (m.det() != 1 and m.det() != -1) or \ m.trace().abs() > 2 or \ (m.trace().abs() == 2 and (m[0,1] or m[1,0])): return False gens = [g for g in self._generators if not g.is_scalar()] if len(gens) <= 1: return True # now we try to find a non-trivial invariant quadratic form from sage.modules.free_module import FreeModule V = FreeModule(self._matrix_space.base_ring(), 3) for g in gens: V = V.intersection(invariant_quadratic_forms(g)) if not contains_definite_form(V): return False return True
def maximal_grading(L): r""" Return a maximal grading of a Lie algebra defined over an algebraically closed field. A maximal grading of a Lie algebra `\mathfrak{g}` is the finest possible grading of `\mathfrak{g}` over torsion free abelian groups. If `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^k} \mathfrak{g}_n` is a maximal grading, then there exists no other grading `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` over a torsion free abelian group `A` such that every `\mathfrak{g}_a` is contained in some `\mathfrak{g}_n`. EXAMPLES: A maximal grading of an abelian Lie algebra puts each basis element into an independent layer:: sage: import sys, pathlib sage: sys.path.append(str(pathlib.Path().absolute())) sage: from lie_gradings.gradings.grading import maximal_grading sage: L = LieAlgebra(QQbar, 4, abelian=True) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z + Z of Abelian Lie algebra on 4 generators (L[0], L[1], L[2], L[3]) over Algebraic Field with nonzero layers (1, 0, 0, 0) : (L[3],) (0, 1, 0, 0) : (L[2],) (0, 0, 1, 0) : (L[1],) (0, 0, 0, 1) : (L[0],) A maximal grading of a free nilpotent Lie algebra decomposes the Lie algebra based on how many times each generator appears in the defining Lie bracket:: sage: L = LieAlgebra(QQbar, 3, step=3) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z of Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Algebraic Field with nonzero layers (1, 0, 0) : (X_1,) (0, 1, 0) : (X_2,) (1, 1, 0) : (X_12,) (1, 2, 0) : (X_122,) (2, 1, 0) : (X_112,) (0, 0, 1) : (X_3,) (0, 1, 1) : (X_23,) (0, 1, 2) : (X_233,) (0, 2, 1) : (X_223,) (1, 0, 1) : (X_13,) (1, 0, 2) : (X_133,) (1, 1, 1) : (X_123, X_132) (2, 0, 1) : (X_113,) """ # define utilities to convert from matrices to vectors and back R = L.base_ring() n = L.dimension() MS = MatrixSpace(R, n, n) def matrix_to_vec(A): return vector(R, sum((list(Ar) for Ar in A.rows()), [])) db = L.derivations_basis() def derivation_lincomb(vec): return sum((vk * dk for vk, dk in zip(vec, db)), MS.zero()) # iteratively construct larger and larger tori of derivations t = [] while True: # compute the centralizer of the torus in the derivation algebra ker = FreeModule(R, len(db)) for der in t: # form the matrix of ad(der) with rows # the images of basis derivations A = matrix([matrix_to_vec(der * X - X * der) for X in db]) ker = ker.intersection(A.left_kernel()) cb = [derivation_lincomb(v) for v in ker.basis()] # check the basis of the centralizer for semisimple parts outside of t gl = FreeModule(R, n * n) t_submodule = gl.submodule([matrix_to_vec(der) for der in t]) for A in cb: As, An = jordan_decomposition(A) if matrix_to_vec(As) not in t_submodule: # extend the torus by As t.append(As) break else: # no new elements found, so the torus is maximal break # compute the eigenspace intersections to get the concrete grading common_eigenspaces = [([], FreeModule(R, n))] for A in t: new_eigenspaces = [] eig = A.right_eigenspaces() for ev, V in common_eigenspaces: for ew, W in eig: VW = V.intersection(W) if VW.dimension() > 0: new_eigenspaces.append((ev + [ew], VW)) common_eigenspaces = new_eigenspaces if not t: # zero dimensional maximal torus # the only grading is the trivial grading magma = AdditiveAbelianGroup([]) layers = {magma.zero(): L.basis().list()} return grading(L, layers, magma=magma) # define a grading with layers indexed by tuples of eigenvalues cm = get_coercion_model() all_eigenvalues = sum((ev for ev, V in common_eigenspaces), []) k = len(common_eigenspaces[0][0]) evR = cm.common_parent(*all_eigenvalues) layers = { tuple(ev): [L.from_vector(v) for v in V.basis()] for ev, V in common_eigenspaces } magma = evR.cartesian_product(*[evR] * (k - 1)) gr = grading(L, layers, magma=magma) # convert to a grading over Z^k return gr.universal_realization()
def is_finite(self): r""" Check whether the group is finite. A group is finite if and only if it is conjugate to a (finite) subgroup of O(2). This is actually also true in higher dimensions. EXAMPLES:: sage: from flatsurf.geometry.finitely_generated_matrix_group import FinitelyGenerated2x2MatrixGroup sage: G = FinitelyGenerated2x2MatrixGroup([identity_matrix(2)]) sage: G.is_finite() True sage: t = matrix(2, [2,1,1,1]) sage: m1 = matrix([[0,1],[-1,0]]) sage: m2 = matrix([[1,-1],[1,0]]) sage: FinitelyGenerated2x2MatrixGroup([m1]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m2]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m1,m2]).is_finite() False sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t,t*m2*~t]).is_finite() False sage: from flatsurf.geometry.polygon import number_field_elements_from_algebraics sage: c5 = QQbar.zeta(5).real() sage: s5 = QQbar.zeta(5).imag() sage: K, (c5,s5) = number_field_elements_from_algebraics([c5,s5]) sage: r = matrix(K, 2, [c5,-s5,s5,c5]) sage: FinitelyGenerated2x2MatrixGroup([m1,r]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([t*m1*~t,t*r*~t]).is_finite() True sage: FinitelyGenerated2x2MatrixGroup([m2,r]).is_finite() False sage: FinitelyGenerated2x2MatrixGroup([t*m2*~t, t*r*~t]).is_finite() False """ # determinant and trace tests # (the code actually check that each generator is of finite order) for m in self._generators: if (m.det() != 1 and m.det() != -1) or \ m.trace().abs() > 2 or \ (m.trace().abs() == 2 and (m[0,1] or m[1,0])): return False gens = [g for g in self._generators if not g.is_scalar()] if len(gens) <= 1: return True # now we try to find a non-trivial invariant quadratic form from sage.modules.free_module import FreeModule V = FreeModule(self._matrix_space.base_ring(), 3) for g in gens: V = V.intersection(invariant_quadratic_forms(g)) if not contains_definite_form(V): return False return True