def _minimize_level(G): r""" Utility function. Given a matrix group `G` contained in `SL(2, \ZZ / N\ZZ)` for some `N`, test whether or not `G` is the preimage of a subgroup of smaller level, and if so, return that subgroup. The trivial group is handled specially: instead of returning a group, it returns an integer `N`, representing the trivial subgroup of `SL(2, \ZZ / N\ZZ)`. EXAMPLE:: sage: M = MatrixSpace(Zmod(9), 2, 2) sage: G = MatrixGroup([M(x) for x in [[1,1,0,1],[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G Matrix group over Ring of integers modulo 9 with 4 generators: [[[1, 1], [0, 1]], [[1, 3], [0, 1]], [[1, 0], [3, 1]], [[4, 0], [0, 7]]] sage: sage.modular.arithgroup.congroup_generic._minimize_level(G) Matrix group over Ring of integers modulo 3 with 1 generators: [[[1, 1], [0, 1]]] sage: G = MatrixGroup([M(x) for x in [[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G Matrix group over Ring of integers modulo 9 with 3 generators: [[[1, 3], [0, 1]], [[1, 0], [3, 1]], [[4, 0], [0, 7]]] sage: sage.modular.arithgroup.congroup_generic._minimize_level(G) 3 """ from congroup_gamma import Gamma_constructor as Gamma Glist = list(G) N = G.base_ring().characteristic() i = Gamma(N).index() for p in N.prime_divisors(): j = Gamma(N // p).index() k = len( [g for g in Glist if g.matrix().change_ring(Zmod(N // p)) == 1]) if k == i // j: if N // p == 1: return ZZ(1) H = MatrixGroup( [g.matrix().change_ring(Zmod(N // p)) for g in G.gens()]) return _minimize_level(H) # now sanitize the generators (remove duplicates and copies of the identity) new_gens = [x.matrix() for x in G.gens() if x.matrix() != 1] all([x.set_immutable() for x in new_gens]) new_gens = list(Set(new_gens)) if new_gens == []: return ZZ(G.base_ring().characteristic()) return MatrixGroup(new_gens)
def image_mod_n(self): r""" Return the image of this group in `SL(2, \ZZ / N\ZZ)`. EXAMPLE:: sage: Gamma0(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 2 generators: [[[2, 0], [0, 2]], [[1, 1], [0, 1]]] TEST:: sage: for n in [2..20]: ... for g in Gamma0(n).gamma_h_subgroups(): ... G = g.image_mod_n() ... assert G.order() == Gamma(n).index() / g.index() """ N = self.level() if N == 1: raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented" gens = [ matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1) / x]) for x in self._generators_for_H() ] gens += [matrix(Zmod(N), 2, [1, 1, 0, 1])] return MatrixGroup(gens)
def as_matrix_group(self): """ Return this group, but as a general matrix group, i.e., throw away the extra structure of general unitary group. EXAMPLES:: sage: G = SU(4,GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field in a of size 5^2 with 2 generators: [[[a, 0, 0, 0], [0, 2*a + 3, 0, 0], [0, 0, 4*a + 1, 0], [0, 0, 0, 3*a]], [[1, 0, 4*a + 3, 0], [1, 0, 0, 0], [0, 2*a + 4, 0, 1], [0, 3*a + 1, 0, 0]]] :: sage: G = GO(3,GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field of size 5 with 2 generators: [[[2, 0, 0], [0, 3, 0], [0, 0, 1]], [[0, 1, 0], [1, 4, 4], [0, 2, 1]]] """ from sage.groups.matrix_gps.matrix_group import MatrixGroup return MatrixGroup([g.matrix() for g in self.gens()])
def QuaternionMatrixGroupGF3(): r""" The quaternion group as a set of `2\times 2` matrices over `GF(3)`. OUTPUT: A matrix group consisting of `2\times 2` matrices with elements from the finite field of order 3. The group is the quaternion group, the nonabelian group of order 8 that is not isomorphic to the group of symmetries of a square (the dihedral group `D_4`). .. note:: This group is most easily available via ``groups.matrix.QuaternionGF3()``. EXAMPLES: The generators are the matrix representations of the elements commonly called `I` and `J`, while `K` is the product of `I` and `J`. :: sage: from sage.groups.misc_gps.misc_groups import QuaternionMatrixGroupGF3 sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 sage: aye = Q.gens()[0]; aye [1 1] [1 2] sage: jay = Q.gens()[1]; jay [2 1] [1 1] sage: kay = aye*jay; kay [0 2] [1 0] TESTS:: sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators: [[[1, 1], [1, 2]], [[2, 1], [1, 1]]] sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True sage: H = DihedralGroup(4) sage: H.order() 8 sage: QP.is_abelian(), H.is_abelian() (False, False) sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace from sage.groups.matrix_gps.matrix_group import MatrixGroup MS = MatrixSpace(FiniteField(3), 2) aye = MS([1,1,1,2]) jay = MS([2,1,1,1]) return MatrixGroup([aye, jay])
def image_mod_n(self): r""" Return the image of this group modulo `N`, as a subgroup of `SL(2, \ZZ / N\ZZ)`. This is just the trivial subgroup. EXAMPLE:: sage: Gamma(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 1 generators: [[[1, 0], [0, 1]]] """ return MatrixGroup([matrix(Zmod(self.level()), 2, 2, 1)])
def center(self): """ Return the center of this linear group as a matrix group. EXAMPLES:: sage: G = SU(3,GF(2)) sage: G.center() Matrix group over Finite Field in a of size 2^2 with 1 generators: [[[a, 0, 0], [0, a, 0], [0, 0, a]]] sage: GL(2,GF(3)).center() Matrix group over Finite Field of size 3 with 1 generators: [[[2, 0], [0, 2]]] sage: GL(3,GF(3)).center() Matrix group over Finite Field of size 3 with 1 generators: [[[2, 0, 0], [0, 2, 0], [0, 0, 2]]] sage: GU(3,GF(2)).center() Matrix group over Finite Field in a of size 2^2 with 1 generators: [[[a + 1, 0, 0], [0, a + 1, 0], [0, 0, a + 1]]] sage: A=Matrix(FiniteField(5), [[2,0,0], [0,3,0], [0,0,1]]) sage: B=Matrix(FiniteField(5), [[1,0,0], [0,1,0], [0,1,1]]) sage: MatrixGroup([A,B]).center() Matrix group over Finite Field of size 5 with 1 generators: [[[1, 0, 0], [0, 1, 0], [0, 0, 1]]] """ try: return self.__center except AttributeError: pass G = list(self._gap_().Center().GeneratorsOfGroup()) from sage.groups.matrix_gps.matrix_group import MatrixGroup if len(G) == 0: self.__center = MatrixGroup([self.one()]) else: F = self.field_of_definition() self.__center = MatrixGroup([g._matrix_(F) for g in G]) return self.__center
def to_even_subgroup(self): r""" Return the smallest even subgroup of `SL(2, \ZZ)` containing self. EXAMPLE:: sage: G = Gamma(3) sage: G.to_even_subgroup() Congruence subgroup of SL(2,Z) of level 3, preimage of: Matrix group over Ring of integers modulo 3 with 1 generators: [[[2, 0], [0, 2]]] """ if self.is_even(): return self else: G = self.image_mod_n() H = MatrixGroup(G.gens() + [G.matrix_space()(-1)]) return CongruenceSubgroup_constructor(H)
def CongruenceSubgroup_constructor(*args): r""" Attempt to create a congruence subgroup from the given data. The allowed inputs are as follows: - A :class:`~sage.groups.matrix_gps.matrix_group.MatrixGroup` object. This must be a group of matrices over `\ZZ / N\ZZ` for some `N`, with determinant 1, in which case the function will return the group of matrices in `SL(2, \ZZ)` whose reduction mod `N` is in the given group. - A list of matrices over `\ZZ / N\ZZ` for some `N`. The function will then compute the subgroup of `SL(2, \ZZ)` generated by these matrices, and proceed as above. - An integer `N` and a list of matrices (over any ring coercible to `\ZZ / N\ZZ`, e.g. over `\ZZ`). The matrices will then be coerced to `\ZZ / N\ZZ`. The function checks that the input G is valid. It then tests to see if `G` is the preimage mod `N` of some group of matrices modulo a proper divisor `M` of `N`, in which case it replaces `G` with this group before continuing. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroup_constructor as CS sage: CS(2, [[1,1,0,1]]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS([matrix(Zmod(2), 2, [1,1,0,1])]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(MatrixGroup([matrix(Zmod(2), 2, [1,1,0,1])])) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(SL(2, 2)) Modular Group SL(2,Z) Some invalid inputs:: sage: CS(SU(2, 7)) Traceback (most recent call last): ... TypeError: Ring of definition must be Z / NZ for some N """ if isinstance(args[0], MatrixGroup_generic): G = args[0] elif type(args[0]) == type([]): G = MatrixGroup(args[0]) elif args[0] in ZZ: M = MatrixSpace(Zmod(args[0]), 2) G = MatrixGroup([M(x) for x in args[1]]) R = G.matrix_space().base_ring() if not hasattr(R, "cover_ring") or R.cover_ring() != ZZ: raise TypeError, "Ring of definition must be Z / NZ for some N" if not all([x.matrix().det() == 1 for x in G.gens()]): raise ValueError, "Group must be contained in SL(2, Z / N)" GG = _minimize_level(G) if GG in ZZ: from all import Gamma return Gamma(GG) else: return CongruenceSubgroupFromGroup(GG)
def CongruenceSubgroup_constructor(*args): r""" Attempt to create a congruence subgroup from the given data. The allowed inputs are as follows: - A :class:`~sage.groups.matrix_gps.matrix_group.MatrixGroup` object. This must be a group of matrices over `\ZZ / N\ZZ` for some `N`, with determinant 1, in which case the function will return the group of matrices in `SL(2, \ZZ)` whose reduction mod `N` is in the given group. - A list of matrices over `\ZZ / N\ZZ` for some `N`. The function will then compute the subgroup of `SL(2, \ZZ)` generated by these matrices, and proceed as above. - An integer `N` and a list of matrices (over any ring coercible to `\ZZ / N\ZZ`, e.g. over `\ZZ`). The matrices will then be coerced to `\ZZ / N\ZZ`. The function checks that the input G is valid. It then tests to see if `G` is the preimage mod `N` of some group of matrices modulo a proper divisor `M` of `N`, in which case it replaces `G` with this group before continuing. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroup_constructor as CS sage: CS(2, [[1,1,0,1]]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS([matrix(Zmod(2), 2, [1,1,0,1])]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(MatrixGroup([matrix(Zmod(2), 2, [1,1,0,1])])) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(SL(2, 2)) Modular Group SL(2,Z) Some invalid inputs:: sage: CS(SU(2, 7)) Traceback (most recent call last): ... TypeError: Ring of definition must be Z / NZ for some N """ if isinstance(args[0], MatrixGroup_generic): G = args[0] elif type(args[0]) == type([]): G = MatrixGroup(args[0]) elif args[0] in ZZ: M = MatrixSpace(Zmod(args[0]), 2) G = MatrixGroup([M(x) for x in args[1]]) R = G.matrix_space().base_ring() if not hasattr(R, "cover_ring") or R.cover_ring() != ZZ: raise TypeError, "Ring of definition must be Z / NZ for some N" if not all([x.matrix().det() == 1 for x in G.gens()]): raise ValueError, "Group must be contained in SL(2, Z / N)" GG = _minimize_level(G) if GG in ZZ: from all import Gamma return Gamma(GG) else: return CongruenceSubgroupFromGroup(GG)