def __call__(self, x): """ EXAMPLES:: sage: W = WeylGroup(['A',2]) sage: W(1) [1 0 0] [0 1 0] [0 0 1] :: sage: W(2) Traceback (most recent call last): ... TypeError: no way to coerce element into self. sage: W2 = WeylGroup(['A',3]) sage: W(1) in W2 # indirect doctest False """ if isinstance(x, self.element_class) and x.parent() is self: return x from sage.matrix.matrix import is_Matrix if not (x in ZZ or is_Matrix(x)): # this should be handled by self.matrix_space()(x) raise TypeError, "no way to coerce element into self" M = self.matrix_space()(x) # This is really bad, especially for infinite groups! # TODO: compute the image of rho, compose by s_i until back in # the fundamental chamber. Return True iff the matrix is the identity g = self._element_constructor_(M) if not gap(g) in gap(self): raise TypeError, "no way to coerce element into self." return g
def __init__(self, homset, imgsH, check=True): MatrixGroupMorphism.__init__(self, homset) # sets the parent G = homset.domain() H = homset.codomain() gaplist_gens = [gap(x) for x in G.gens()] gaplist_imgs = [gap(x) for x in imgsH] genss = '[%s]'%(','.join(str(v) for v in gaplist_gens)) imgss = '[%s]'%(','.join(str(v) for v in gaplist_imgs)) args = '%s, %s, %s, %s'%(G._gap_init_(), H._gap_init_(), genss, imgss) self._gap_str = 'GroupHomomorphismByImages(%s)'%args phi0 = gap(self) if gap.eval("IsGroupHomomorphism(%s)"%phi0.name())!="true": raise ValueError,"The map "+str(gensG)+"-->"+str(imgsH)+" isn't a homomorphism."
def __init__(self, homset, imgsH, check=True): MatrixGroupMorphism.__init__(self, homset) # sets the parent G = homset.domain() H = homset.codomain() gaplist_gens = [gap(x) for x in G.gens()] gaplist_imgs = [gap(x) for x in imgsH] genss = '[%s]' % (','.join(str(v) for v in gaplist_gens)) imgss = '[%s]' % (','.join(str(v) for v in gaplist_imgs)) args = '%s, %s, %s, %s' % (G._gap_init_(), H._gap_init_(), genss, imgss) self._gap_str = 'GroupHomomorphismByImages(%s)' % args phi0 = gap(self) if gap.eval("IsGroupHomomorphism(%s)" % phi0.name()) != "true": raise ValueError, "The map " + str(gensG) + "-->" + str( imgsH) + " isn't a homomorphism."
def pushforward(self, J, *args, **kwds): """ The image of an element or a subgroup. INPUT: ``J`` -- a subgroup or an element of the domain of ``self``. OUTPUT: The image of ``J`` under ``self`` NOTE: ``pushforward`` is the method that is used when a map is called on anything that is not an element of its domain. For historical reasons, we keep the alias ``image()`` for this method. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: F.multiplicative_generator() 3 sage: G = MatrixGroup([MS([3,0,0,1])]) sage: a = G.gens()[0]^2 sage: phi = G.hom([a]) sage: phi.image(G.gens()[0]) # indirect doctest [2 0] [0 1] sage: H = MatrixGroup([MS(a.list())]) sage: H Matrix group over Finite Field of size 7 with 1 generators: [[[2, 0], [0, 1]]] The following tests against trac ticket #10659:: sage: phi(H) # indirect doctestest Matrix group over Finite Field of size 7 with 1 generators: [[[4, 0], [0, 1]]] """ phi = gap(self) F = self.codomain().base_ring() from sage.all import MatrixGroup gapJ = gap(J) if gap.eval("IsGroup(%s)" % gapJ.name()) == "true": return MatrixGroup( [x._matrix_(F) for x in phi.Image(gapJ).GeneratorsOfGroup()]) return phi.Image(gapJ)._matrix_(F)
def invariant_quadratic_form(self): r""" Return the quadratic form `q(v) = v Q v^t` on the space on which this group `G` that satisfies the equation `q(v) = q(v M)` for all `v \in V` and `M \in G`. .. note:: Uses GAP's command InvariantQuadraticForm. OUTPUT: - ``Q`` - matrix that defines the invariant quadratic form. EXAMPLES:: sage: G = SO( 4, GF(7), 1) sage: G.invariant_quadratic_form() [0 1 0 0] [0 0 0 0] [0 0 3 0] [0 0 0 1] """ F = self.base_ring() I = gap(self).InvariantQuadraticForm() Q = I.attribute("matrix") return Q._matrix_(F)
def invariant_quadratic_form(self): """ This wraps GAP's command "InvariantQuadraticForm". From the GAP documentation: INPUT: - ``self`` - a matrix group G OUTPUT: - ``Q`` - the matrix satisfying the property: The quadratic form q on the natural vector space V on which G acts is given by `q(v) = v Q v^t`, and the invariance under G is given by the equation `q(v) = q(v M)` for all `v \in V` and `M \in G`. EXAMPLES:: sage: G = GO( 4, GF(7), 1) sage: G.invariant_quadratic_form() [0 1 0 0] [0 0 0 0] [0 0 3 0] [0 0 0 1] """ F = self.base_ring() G = self._gap_init_() cmd = "r := InvariantQuadraticForm("+G+")" gap.eval(cmd) cmd = "r.matrix" Q = gap(cmd) return Q._matrix_(F)
def __call__(self, g): """ Evaluate the character on the group element `g`. Return an error if `g` is not in `G`. EXAMPLES:: sage: G = GL(2,7) sage: values = G.gap().CharacterTable().Irr()[2].List().sage() sage: chi = ClassFunction(G, values) sage: z = G([[3,0],[0,3]]); z [3 0] [0 3] sage: chi(z) zeta3 sage: G = GL(2,3) sage: chi = G.irreducible_characters()[3] sage: g = G.conjugacy_classes_representatives()[6] sage: chi(g) zeta8^3 + zeta8 sage: G = SymmetricGroup(3) sage: h = G((2,3)) sage: triv = G.trivial_character() sage: triv(h) 1 """ return self._base_ring(gap(g)._operation("^", self._gap_classfunction))
def character_table(self): """ Returns the character table as a matrix Each row is an irreducible character. For larger tables you may preface this with a command such as gap.eval("SizeScreen([120,40])") in order to widen the screen. EXAMPLES:: sage: WeylGroup(['A',3]).character_table() CT1 <BLANKLINE> 2 3 2 2 . 3 3 1 . . 1 . <BLANKLINE> 1a 4a 2a 3a 2b <BLANKLINE> X.1 1 -1 -1 1 1 X.2 3 1 -1 . -1 X.3 2 . . -1 2 X.4 3 -1 1 . -1 X.5 1 1 1 1 1 """ gens_str = ', '.join(str(g.gap()) for g in self.gens()) ctbl = gap('CharacterTable(Group({0}))'.format(gens_str)) return ctbl.Display()
def cardinality(self): """ Implements :meth:`EnumeratedSets.ParentMethods.cardinality`. EXAMPLES:: sage: G = Sp(4,GF(3)) sage: G.cardinality() 51840 sage: G = SL(4,GF(3)) sage: G.cardinality() 12130560 sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) sage: G.cardinality() 480 sage: G = MatrixGroup([matrix(ZZ,2,[1,1,0,1])]) sage: G.cardinality() +Infinity """ g = self._gap_() if g.IsFinite().bool(): return integer.Integer(gap(self).Size()) return infinity
def random_element(self): """ Return a random element of this group. EXAMPLES:: sage: G = Sp(4,GF(3)) sage: G.random_element() # random [2 1 1 1] [1 0 2 1] [0 1 1 0] [1 0 0 1] sage: G.random_element() in G True :: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) sage: G.random_element() # random [1 3] [0 3] sage: G.random_element() in G True """ # Note: even with fixed random seed, the Random() element # returned by gap does depend on execution order and # architecture. Presumably due to different memory loctions. current_randstate().set_seed_gap() F = self.field_of_definition() return self.element_class(gap(self).Random()._matrix_(F), self, check=False)
def gens(self): """ Return generators for this matrix group. EXAMPLES:: sage: G = GO(3,GF(5)) sage: G.gens() [ [2 0 0] [0 3 0] [0 0 1], [0 1 0] [1 4 4] [0 2 1] ] """ try: return self.__gens except AttributeError: pass F = self.field_of_definition() gap_gens = list(gap(self).GeneratorsOfGroup()) gens = Sequence([self.element_class(g._matrix_(F), self, check=False) for g in gap_gens], cr=True, universe=self, check=False) self.__gens = gens return gens
def gens(self): """ Return generators for this matrix group. EXAMPLES:: sage: G = GO(3,GF(5)) sage: G.gens() [ [2 0 0] [0 3 0] [0 0 1], [0 1 0] [1 4 4] [0 2 1] ] """ try: return self.__gens except AttributeError: pass F = self.field_of_definition() gap_gens = list(gap(self).GeneratorsOfGroup()) gens = Sequence([ self.element_class(g._matrix_(F), self, check=False) for g in gap_gens ], cr=True, universe=self, check=False) self.__gens = gens return gens
def invariant_quadratic_form(self): r""" Return the quadratic form `q(v) = v Q v^t` on the space on which this group `G` that satisfies the equation `q(v) = q(v M)` for all `v \in V` and `M \in G`. .. note:: Uses GAP's command InvariantQuadraticForm. OUTPUT: - ``Q`` - matrix that defines the invariant quadratic form. EXAMPLES:: sage: G = SO( 4, GF(7), 1) sage: G.invariant_quadratic_form() [0 1 0 0] [0 0 0 0] [0 0 3 0] [0 0 0 1] """ F = self.base_ring() I = gap(self).InvariantQuadraticForm() Q = I.attribute('matrix') return Q._matrix_(F)
def pushforward(self, J, *args,**kwds): """ The image of an element or a subgroup. INPUT: ``J`` -- a subgroup or an element of the domain of ``self``. OUTPUT: The image of ``J`` under ``self`` NOTE: ``pushforward`` is the method that is used when a map is called on anything that is not an element of its domain. For historical reasons, we keep the alias ``image()`` for this method. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: F.multiplicative_generator() 3 sage: G = MatrixGroup([MS([3,0,0,1])]) sage: a = G.gens()[0]^2 sage: phi = G.hom([a]) sage: phi.image(G.gens()[0]) # indirect doctest [2 0] [0 1] sage: H = MatrixGroup([MS(a.list())]) sage: H Matrix group over Finite Field of size 7 with 1 generators: [[[2, 0], [0, 1]]] The following tests against trac ticket #10659:: sage: phi(H) # indirect doctestest Matrix group over Finite Field of size 7 with 1 generators: [[[4, 0], [0, 1]]] """ phi = gap(self) F = self.codomain().base_ring() from sage.all import MatrixGroup gapJ = gap(J) if gap.eval("IsGroup(%s)"%gapJ.name()) == "true": return MatrixGroup([x._matrix_(F) for x in phi.Image(gapJ).GeneratorsOfGroup()]) return phi.Image(gapJ)._matrix_(F)
def module_composition_factors(self, algorithm=None): r""" Returns a list of triples consisting of [base field, dimension, irreducibility], for each of the Meataxe composition factors modules. The algorithm="verbose" option returns more information, but in Meataxe notation. EXAMPLES:: sage: F=GF(3);MS=MatrixSpace(F,4,4) sage: M=MS(0) sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1 sage: G = MatrixGroup([M]) sage: G.module_composition_factors() [(Finite Field of size 3, 1, True), (Finite Field of size 3, 1, True), (Finite Field of size 3, 2, True)] sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.module_composition_factors() [(Finite Field of size 7, 2, True)] Type "G.module_composition_factors(algorithm='verbose')" to get a more verbose version. For more on MeatAxe notation, see http://www.gap-system.org/Manuals/doc/htm/ref/CHAP067.htm """ from sage.misc.sage_eval import sage_eval F = self.base_ring() if not (F.is_finite()): raise NotImplementedError, "Base ring must be finite." q = F.cardinality() gens = self.gens() n = self.degree() MS = MatrixSpace(F, n, n) mats = [] # initializing list of mats by which the gens act on self W = self.matrix_space().row_space() for g in gens: p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("M:=GModuleByMats(" + mats_str + ", GF(" + str(q) + "))") gap.eval("MCFs := MTX.CompositionFactors( M )") N = eval(gap.eval("Length(MCFs)")) if algorithm == "verbose": print gap.eval('MCFs') + "\n" L = [] for i in range(1, N + 1): gap.eval("MCF := MCFs[%s]" % i) L.append( tuple([ sage_eval(gap.eval("MCF.field")), eval(gap.eval("MCF.dimension")), sage_eval(gap.eval("MCF.IsIrreducible")) ])) return sorted(L)
def module_composition_factors(self, algorithm=None): r""" Return a list of triples consisting of [base field, dimension, irreducibility], for each of the Meataxe composition factors modules. The ``algorithm="verbose"`` option returns more information, but in Meataxe notation. EXAMPLES:: sage: F=GF(3);MS=MatrixSpace(F,4,4) sage: M=MS(0) sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1 sage: G = MatrixGroup([M]) sage: G.module_composition_factors() [(Finite Field of size 3, 1, True), (Finite Field of size 3, 1, True), (Finite Field of size 3, 2, True)] sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.module_composition_factors() [(Finite Field of size 7, 2, True)] Type ``G.module_composition_factors(algorithm='verbose')`` to get a more verbose version. For more on MeatAxe notation, see http://www.gap-system.org/Manuals/doc/ref/chap69.html """ from sage.misc.sage_eval import sage_eval F = self.base_ring() if not(F.is_finite()): raise NotImplementedError("Base ring must be finite.") q = F.cardinality() gens = self.gens() n = self.degree() MS = MatrixSpace(F,n,n) mats = [] # initializing list of mats by which the gens act on self W = self.matrix_space().row_space() for g in gens: p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))") gap.eval("MCFs := MTX.CompositionFactors( M )") N = eval(gap.eval("Length(MCFs)")) if algorithm == "verbose": print(gap.eval('MCFs') + "\n") L = [] for i in range(1,N+1): gap.eval("MCF := MCFs[%s]"%i) L.append(tuple([sage_eval(gap.eval("MCF.field")), eval(gap.eval("MCF.dimension")), sage_eval(gap.eval("MCF.IsIrreducible")) ])) return sorted(L)
def __call__(self, x): """ EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G.matrix_space() Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 5 sage: G(1) [1 0] [0 1] """ if isinstance(x, self.element_class) and x.parent() is self: return x M = self.matrix_space()(x) g = self.element_class(M, self) if not gap(g) in gap(self): raise TypeError, "no way to coerce element to self." return g
def __contains__(self, x): """ Return True if `x` is an element of this abelian group. EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G Matrix group over Finite Field of size 5 with 2 generators: [[[1, 0], [0, 1]], [[1, 2], [3, 4]]] sage: G.cardinality() 8 sage: G(1) [1 0] [0 1] sage: G.1 in G True sage: 1 in G True sage: [1,2,3,4] in G True sage: matrix(GF(5),2,[1,2,3,5]) in G False sage: G(matrix(GF(5),2,[1,2,3,5])) Traceback (most recent call last): ... TypeError: no way to coerce element to self. """ if isinstance(x, self.element_class): if x.parent() == self: return True return gap(x) in gap(self) try: self(x) return True except TypeError: return False
def cardinality(self): """ EXAMPLES:: sage: G = Sp(4,GF(3)) sage: G.cardinality() 51840 sage: G = SL(4,GF(3)) sage: G.cardinality() 12130560 sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) sage: G.cardinality() 480 sage: G = MatrixGroup([matrix(ZZ,2,[1,1,0,1])]) sage: G.cardinality() +Infinity """ return integer.Integer(gap(self).Size())
def kernel(self): """ Return the kernel of ``self``, i.e., a matrix group. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: F.multiplicative_generator() 3 sage: G = MatrixGroup([MS([3,0,0,1])]) sage: a = G.gens()[0]^2 sage: phi = G.hom([a]) sage: phi.kernel() Matrix group over Finite Field of size 7 with 1 generators: [[[6, 0], [0, 1]]] """ gap_ker = gap(self).Kernel() from sage.all import MatrixGroup F = self.domain().base_ring() return MatrixGroup([x._matrix_(F) for x in gap_ker.GeneratorsOfGroup()])
def kernel(self): """ Return the kernel of ``self``, i.e., a matrix group. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: F.multiplicative_generator() 3 sage: G = MatrixGroup([MS([3,0,0,1])]) sage: a = G.gens()[0]^2 sage: phi = G.hom([a]) sage: phi.kernel() Matrix group over Finite Field of size 7 with 1 generators: [[[6, 0], [0, 1]]] """ gap_ker = gap(self).Kernel() from sage.all import MatrixGroup F = self.domain().base_ring() return MatrixGroup( [x._matrix_(F) for x in gap_ker.GeneratorsOfGroup()])
def character_table(self): """ Returns the GAP character table as a string. For larger tables you may preface this with a command such as gap.eval("SizeScreen([120,40])") in order to widen the screen. EXAMPLES:: sage: print WeylGroup(['A',3]).character_table() CT1 <BLANKLINE> 2 3 2 2 . 3 3 1 . . 1 . <BLANKLINE> 1a 4a 2a 3a 2b <BLANKLINE> X.1 1 -1 -1 1 1 X.2 3 1 -1 . -1 X.3 2 . . -1 2 X.4 3 -1 1 . -1 X.5 1 1 1 1 1 """ return gap.eval("Display(CharacterTable(%s))" % gap(self).name())
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree begin determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError, "Group must be finite." n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group(" + mats_str + "))") if algorithm == "smaller": gap.eval( "small:= SmallerDegreePermutationRepresentation( Image( iso ) );" ) C = gap("Image( small )") else: C = gap("Image( iso )") return PermutationGroup(gap_group=C)
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree begin determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. TESTS:: sage: A= matrix(QQ, 2, [0, 1, 1, 0]) sage: B= matrix(QQ, 2, [1, 0, 0, 1]) sage: a, b= MatrixGroup([A, B]).as_permutation_group().gens() sage: a.order(), b.order() (2, 1) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError("Group must be finite.") n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group("+mats_str+"))") if algorithm == "smaller": gap.eval("small:= SmallerDegreePermutationRepresentation( Image( iso ) );") C = gap("Image( small )") else: C = gap("Image( iso )") return PermutationGroup(gap_group=C, canonicalize=False)
def word_problem(self, gens=None): r""" Write this group element in terms of the elements of the list ``gens``, or the standard generators of the parent of self if ``gens`` is None. INPUT: - ``gens`` - a list of elements that can be coerced into the parent of self, or None. ALGORITHM: Use GAP, which has optimized algorithms for solving the word problem (the GAP functions EpimorphismFromFreeGroup and PreImagesRepresentative). EXAMPLE:: sage: G = GL(2,5); G General Linear Group of degree 2 over Finite Field of size 5 sage: G.gens() [ [2 0] [0 1], [4 1] [4 0] ] sage: G(1).word_problem([G.1, G.0]) 1 Next we construct a more complicated element of the group from the generators:: sage: s,t = G.0, G.1 sage: a = (s * t * s); b = a.word_problem(); b ([2 0] [0 1]) * ([4 1] [4 0]) * ([2 0] [0 1]) sage: b.prod() == a True We solve the word problem using some different generators:: sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) sage: a.word_problem([s,t,u]) ([2 0] [0 1])^-1 * ([1 1] [0 1])^-1 * ([0 4] [1 0]) * ([2 0] [0 1])^-1 We try some elements that don't actually generate the group:: sage: a.word_problem([t,u]) Traceback (most recent call last): ... ValueError: Could not solve word problem AUTHORS: - David Joyner and William Stein - David Loeffler (2010): fixed some bugs """ G = self.parent() gg = gap(G) if not gens: gens = gg.GeneratorsOfGroup() else: gens = gap([G(x) for x in gens]) H = gens.Group() hom = H.EpimorphismFromFreeGroup() in_terms_of_gens = str(hom.PreImagesRepresentative(self)) if 'identity' in in_terms_of_gens: return Factorization([]) elif in_terms_of_gens == 'fail': raise ValueError, "Could not solve word problem" v = list(H.GeneratorsOfGroup()) F = G.field_of_definition() w = [G(x._matrix_(F)) for x in v] factors = [Factorization([(x,1)]) for x in w] d = dict([('x%s'%(i+1),factors[i]) for i in range(len(factors))]) from sage.misc.sage_eval import sage_eval F = sage_eval(str(in_terms_of_gens), d) F._set_cr(True) return F
def _call_(self, g): """ Some python code for wrapping GAP's Images function for a matrix group G. Returns an error if g is not in G. EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: g = MS([1,1,0,1]) sage: G = MatrixGroup([g]) sage: phi = G.hom(G.gens()) sage: phi(G.0) [1 1] [0 1] sage: phi(G(g^2)) [1 2] [0 1] :: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([1,2, -1,1]),MS([1,1, 0,1])] sage: G = MatrixGroup(gens) sage: phi = G.hom(G.gens()) sage: phi(G.0) [1 2] [4 1] sage: phi(G.1) [1 1] [0 1] TEST: The following tests that the call method was successfully improved in trac ticket #10659:: sage: O = WeylGroup(['D',6]) sage: r = prod(O.gens()) sage: r_ = r^-1 sage: f = O.hom([r*x*r_ for x in O.gens()]) # long time (19s on sage.math, 2011) sage: [f(x) for x in O.gens()] # long time [ [1 0 0 0 0 0] [1 0 0 0 0 0] [1 0 0 0 0 0] [ 0 0 0 0 -1 0] [0 0 1 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] [ 0 1 0 0 0 0] [0 1 0 0 0 0] [0 0 0 1 0 0] [0 0 1 0 0 0] [ 0 0 1 0 0 0] [0 0 0 1 0 0] [0 0 1 0 0 0] [0 0 0 0 1 0] [ 0 0 0 1 0 0] [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 1 0 0] [-1 0 0 0 0 0] [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1], [ 0 0 0 0 0 1], <BLANKLINE> [0 0 0 0 0 1] [ 0 0 0 0 0 -1] [0 1 0 0 0 0] [ 0 1 0 0 0 0] [0 0 1 0 0 0] [ 0 0 1 0 0 0] [0 0 0 1 0 0] [ 0 0 0 1 0 0] [0 0 0 0 1 0] [ 0 0 0 0 1 0] [1 0 0 0 0 0], [-1 0 0 0 0 0] ] sage: f(O) # long time Matrix group over Rational Field with 6 generators: [[[1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]], [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]], [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1]], [[0, 0, 0, 0, -1, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [-1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1]], [[0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, -1], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [-1, 0, 0, 0, 0, 0]]] """ phi = gap(self) G = self.domain() F = G.base_ring() h = gap(g) return phi.Image(h)._matrix_(F)
def __cmp__(self, H): if not isinstance(H, MatrixGroup_gap): return cmp(type(self), type(H)) return cmp(gap(self), gap(H))
def invariant_generators(self): """ Wraps Singular's invariant_algebra_reynolds and invariant_ring in finvar.lib, with help from Simon King and Martin Albrecht. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where G in GL(n,F) is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of G-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of G-invariant polynomials. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[-1,1]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() # long time (67s on sage.math, 2012) [x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20, x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20] sage: F=CyclotomicField(8) sage: z=F.gen() sage: a=z+1/z sage: b=z^2 sage: MS=MatrixSpace(F,2,2) sage: g1=MS([[1/a,1/a],[1/a,-1/a]]) sage: g2=MS([[1,0],[0,b]]) sage: g3=MS([[b,0],[0,1]]) sage: G=MatrixGroup([g1,g2,g3]) sage: G.invariant_generators() # long time (12s on sage.math, 2011) [x1^8 + 14*x1^4*x2^4 + x2^8, x1^24 + 10626/1025*x1^20*x2^4 + 735471/1025*x1^16*x2^8 + 2704156/1025*x1^12*x2^12 + 735471/1025*x1^8*x2^16 + 10626/1025*x1^4*x2^20 + x2^24] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - B. Sturmfels, "Algorithms in invariant theory", Springer-Verlag, 1993. - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", arXiv:math.AC/0703035 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() #len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() ## test if the field is admissible if F.gen() == 1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F, 'polynomial'): # we got an algebraic extension if len(F.gens()) > 1: raise NotImplementedError, "can only deal with finite fields and (simple algebraic extensions of) the rationals" FieldStr = '(%d,%s)' % (F.characteristic(), str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)' % (F.characteristic(), ','.join( [str(p) for p in F.gens()])) ## Setting Singular's variable names ## We need to make sure that field generator and variables get different names. if str(F.gen())[0] == 'x': VarStr = 'y' else: VarStr = 'x' VarNames = '(' + ','.join( (VarStr + str(i + 1) for i in range(n))) + ')' R = singular.ring(FieldStr, VarNames, 'dp') if hasattr(F, 'polynomial') and F.gen() != 1: # we have to define minpoly singular.eval('minpoly = ' + str(F.polynomial()).replace('x', str(F.gen()))) A = [singular.matrix(n, n, str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F, n, [VarStr + str(i) for i in range(1, n + 1)]) if q == 0 or (q > 0 and self.cardinality() % q != 0): from sage.all import Integer, Matrix try: gapself = gap(self) # test whether the backwards transformation works as well: for i in range(self.ngens()): if Matrix(gapself.gen(i + 1), F) != self.gen(i).matrix(): raise ValueError except (TypeError, ValueError): gapself is None if gapself is not None: ReyName = 't' + singular._next_var_name() singular.eval('matrix %s[%d][%d]' % (ReyName, self.cardinality(), n)) En = gapself.Enumerator() for i in range(1, self.cardinality() + 1): M = Matrix(En[i], F) D = [{} for foobar in range(self.degree())] for x, y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' % (ReyName, i, row + 1, ReyName, i, row + 1, repr(t[1]), t[0] + 1)) foobar = singular(ReyName) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)' % (IRName, ReyName)) else: ReyName = 't' + singular._next_var_name() singular.eval('list %s=group_reynolds((%s))' % (ReyName, Lgens)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])' % (IRName, ReyName)) OUT = [ singular.eval(IRName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + IRName + ')')) ] return [PR(gen) for gen in OUT] if self.cardinality() % q == 0: PName = 't' + singular._next_var_name() SName = 't' + singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)' % (PName, SName, Lgens)) OUT = [ singular.eval(PName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + PName + ')')) ] + [ singular.eval(SName + '[1,%d]' % (j)) for j in range(2, 1 + singular('ncols(' + SName + ')')) ] return [PR(gen) for gen in OUT]
def word_problem(self, gens=None): r""" Write this group element in terms of the elements of the list ``gens``, or the standard generators of the parent of self if ``gens`` is None. INPUT: - ``gens`` - a list of elements that can be coerced into the parent of self, or None. ALGORITHM: Use GAP, which has optimized algorithms for solving the word problem (the GAP functions EpimorphismFromFreeGroup and PreImagesRepresentative). EXAMPLE:: sage: G = GL(2,5); G General Linear Group of degree 2 over Finite Field of size 5 sage: G.gens() [ [2 0] [0 1], [4 1] [4 0] ] sage: G(1).word_problem([G.1, G.0]) 1 Next we construct a more complicated element of the group from the generators:: sage: s,t = G.0, G.1 sage: a = (s * t * s); b = a.word_problem(); b ([2 0] [0 1]) * ([4 1] [4 0]) * ([2 0] [0 1]) sage: b.prod() == a True We solve the word problem using some different generators:: sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) sage: a.word_problem([s,t,u]) ([2 0] [0 1])^-1 * ([1 1] [0 1])^-1 * ([0 4] [1 0]) * ([2 0] [0 1])^-1 We try some elements that don't actually generate the group:: sage: a.word_problem([t,u]) Traceback (most recent call last): ... ValueError: Could not solve word problem AUTHORS: - David Joyner and William Stein - David Loeffler (2010): fixed some bugs """ G = self.parent() gg = gap(G) if not gens: gens = gg.GeneratorsOfGroup() else: gens = gap([G(x) for x in gens]) H = gens.Group() hom = H.EpimorphismFromFreeGroup() in_terms_of_gens = str(hom.PreImagesRepresentative(self)) if "identity" in in_terms_of_gens: return Factorization([]) elif in_terms_of_gens == "fail": raise ValueError, "Could not solve word problem" v = list(H.GeneratorsOfGroup()) F = G.field_of_definition() w = [G(x._matrix_(F)) for x in v] factors = [Factorization([(x, 1)]) for x in w] d = dict([("x%s" % (i + 1), factors[i]) for i in range(len(factors))]) from sage.misc.sage_eval import sage_eval F = sage_eval(str(in_terms_of_gens), d) F._set_cr(True) return F
def cycle_index(self, parent = None): r""" INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method (default: the symmetric functions over the rational field in the p basis) Returns the *cycle index* of `G`, which is a gadget counting the elements of `G` by cycle type, averaged over the group: .. math:: P = \frac{1}{|G|} \sum_{g\in G} p_{ \operatorname{cycle\ type}(g) } EXAMPLES: Among the permutations of the symmetric group `S_4`, there is the identity, 6 cycles of length 2, 3 products of two cycles of length 2, 8 cycles of length 3, and 6 cycles of length 4:: sage: S4 = SymmetricGroup(4) sage: P = S4.cycle_index() sage: 24 * P p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 8*p[3, 1] + 6*p[4] If `l = (l_1,\dots,l_k)` is a partition, ``|G| P[l]`` is the number of elements of `G` with cycles of length `(p_1,\dots,p_k)`:: sage: 24 * P[ Partition([3,1]) ] 8 The cycle index plays an important role in the enumeration of objects modulo the action of a group (Polya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: sage: P.parent() Symmetric Functions over Rational Field in the powersum basis This symmetric function can have some nice properties; for example, for the symmetric group `S_n`, we get the complete symmetric function `h_n`:: sage: S = SymmetricFunctions(QQ); h = S.h() sage: h( P ) h[4] TODO: add some simple examples of Polya enumeration, once it will be easy to expand symmetric functions on any alphabet. Here are the cycle indices of some permutation groups:: sage: 6 * CyclicPermutationGroup(6).cycle_index() p[1, 1, 1, 1, 1, 1] + p[2, 2, 2] + 2*p[3, 3] + 2*p[6] sage: 60 * AlternatingGroup(5).cycle_index() p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] sage: for G in TransitiveGroups(5): # optional - database_gap # long time ... G.cardinality() * G.cycle_index() p[1, 1, 1, 1, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 10*p[4, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) sage: P = CyclicPermutationGroup(6).cycle_index(parent = F) sage: 6 * P B[[1, 1, 1, 1, 1, 1]] + B[[2, 2, 2]] + 2*B[[3, 3]] + 2*B[[6]] sage: P.parent() is F True This parent should have a ``term`` and ``sum`` method:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... AssertionError: `parent` should be (or behave as) a free module with basis indexed by partitions REFERENCES: .. [Ker1991] A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. BI-Wissenschaftsverlag, Mannheim, 1991. AUTHORS: - Nicolas Borie and Nicolas M. Thiery TESTS:: sage: P = PermutationGroup([]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] sage: P = PermutationGroup([[(1)]]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] """ from sage.combinat.permutation import Permutation if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() else: assert hasattr(parent, "term") and hasattr(parent, "sum"), \ "`parent` should be (or behave as) a free module with basis indexed by partitions" base_ring = parent.base_ring() # TODO: use self.conjugacy_classes() once available from sage.interfaces.gap import gap CC = ([Permutation(self(C.Representative())).cycle_type(), base_ring(C.Size())] for C in gap(self).ConjugacyClasses()) return parent.sum( parent.term( partition, coeff ) for (partition, coeff) in CC)/self.cardinality()
def invariant_generators(self): """ Wraps Singular's invariant_algebra_reynolds and invariant_ring in finvar.lib, with help from Simon King and Martin Albrecht. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where G in GL(n,F) is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of G-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of G-invariant polynomials. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[-1,1]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() # long time (64s on sage.math, 2011) [x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20, x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20] sage: F=CyclotomicField(8) sage: z=F.gen() sage: a=z+1/z sage: b=z^2 sage: MS=MatrixSpace(F,2,2) sage: g1=MS([[1/a,1/a],[1/a,-1/a]]) sage: g2=MS([[1,0],[0,b]]) sage: g3=MS([[b,0],[0,1]]) sage: G=MatrixGroup([g1,g2,g3]) sage: G.invariant_generators() # long time (12s on sage.math, 2011) [x1^8 + 14*x1^4*x2^4 + x2^8, x1^24 + 10626/1025*x1^20*x2^4 + 735471/1025*x1^16*x2^8 + 2704156/1025*x1^12*x2^12 + 735471/1025*x1^8*x2^16 + 10626/1025*x1^4*x2^20 + x2^24] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - B. Sturmfels, "Algorithms in invariant theory", Springer-Verlag, 1993. - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", arXiv:math.AC/0703035 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() #len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() ## test if the field is admissible if F.gen()==1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F,'polynomial'): # we got an algebraic extension if len(F.gens())>1: raise NotImplementedError, "can only deal with finite fields and (simple algebraic extensions of) the rationals" FieldStr = '(%d,%s)'%(F.characteristic(),str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)'%(F.characteristic(),','.join([str(p) for p in F.gens()])) ## Setting Singular's variable names ## We need to make sure that field generator and variables get different names. if str(F.gen())[0]=='x': VarStr = 'y' else: VarStr = 'x' VarNames='('+','.join((VarStr+str(i+1) for i in range(n)))+')' R=singular.ring(FieldStr,VarNames,'dp') if hasattr(F,'polynomial') and F.gen()!=1: # we have to define minpoly singular.eval('minpoly = '+str(F.polynomial()).replace('x',str(F.gen()))) A = [singular.matrix(n,n,str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F,n,[VarStr+str(i) for i in range(1,n+1)]) if q == 0 or (q > 0 and self.cardinality()%q != 0): from sage.all import Integer, Matrix try: gapself = gap(self) # test whether the backwards transformation works as well: for i in range(self.ngens()): if Matrix(gapself.gen(i+1),F) != self.gen(i).matrix(): raise ValueError except (TypeError,ValueError): gapself is None if gapself is not None: ReyName = 't'+singular._next_var_name() singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n)) En = gapself.Enumerator() for i in range(1,self.cardinality()+1): M = Matrix(En[i],F) D = [{} for foobar in range(self.degree())] for x,y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)'%(ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1)) foobar = singular(ReyName) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)'%(IRName,ReyName)) else: ReyName = 't'+singular._next_var_name() singular.eval('list %s=group_reynolds((%s))'%(ReyName,Lgens)) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])'%(IRName,ReyName)) OUT = [singular.eval(IRName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+IRName+')'))] return [PR(gen) for gen in OUT] if self.cardinality()%q == 0: PName = 't'+singular._next_var_name() SName = 't'+singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)'%(PName,SName,Lgens)) OUT = [singular.eval(PName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+PName+')'))] + [singular.eval(SName+'[1,%d]'%(j)) for j in range(2,1+singular('ncols('+SName+')'))] return [PR(gen) for gen in OUT]
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree being determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. TESTS:: sage: A= matrix(QQ, 2, [0, 1, 1, 0]) sage: B= matrix(QQ, 2, [1, 0, 0, 1]) sage: a, b= MatrixGroup([A, B]).as_permutation_group().gens() sage: a.order(), b.order() (2, 1) Check that ``_permutation_group_morphism`` works (:trac:`25706`):: sage: MG = GU(3,2).as_matrix_group() sage: PG = MG.as_permutation_group() # this constructs the morphism sage: mg = MG.an_element() sage: MG._permutation_group_morphism(mg) (1,2,6,19,35,33)(3,9,26,14,31,23)(4,13,5)(7,22,17)(8,24,12)(10,16,32,27,20,28)(11,30,18)(15,25,36,34,29,21) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError("Group must be finite.") n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group("+mats_str+"))") gap_permutation_map = gap("iso;") if algorithm == "smaller": gap.eval("small:= SmallerDegreePermutationRepresentation( Image( iso ) );") C = gap("Image( small )") else: C = gap("Image( iso )") PG = PermutationGroup(gap_group=C, canonicalize=False) def permutation_group_map(element): return PG(gap_permutation_map.ImageElm(element.gap())) from sage.categories.homset import Hom self._permutation_group_morphism = Hom(self, PG)(permutation_group_map) return PG
def _call_( self, g ): """ Some python code for wrapping GAP's Images function for a matrix group G. Returns an error if g is not in G. EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: g = MS([1,1,0,1]) sage: G = MatrixGroup([g]) sage: phi = G.hom(G.gens()) sage: phi(G.0) [1 1] [0 1] sage: phi(G(g^2)) [1 2] [0 1] :: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([1,2, -1,1]),MS([1,1, 0,1])] sage: G = MatrixGroup(gens) sage: phi = G.hom(G.gens()) sage: phi(G.0) [1 2] [4 1] sage: phi(G.1) [1 1] [0 1] TEST: The following tests that the call method was successfully improved in trac ticket #10659:: sage: O = WeylGroup(['D',6]) sage: r = prod(O.gens()) sage: r_ = r^-1 sage: f = O.hom([r*x*r_ for x in O.gens()]) # long time (19s on sage.math, 2011) sage: [f(x) for x in O.gens()] # long time [ [1 0 0 0 0 0] [1 0 0 0 0 0] [1 0 0 0 0 0] [ 0 0 0 0 -1 0] [0 0 1 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] [ 0 1 0 0 0 0] [0 1 0 0 0 0] [0 0 0 1 0 0] [0 0 1 0 0 0] [ 0 0 1 0 0 0] [0 0 0 1 0 0] [0 0 1 0 0 0] [0 0 0 0 1 0] [ 0 0 0 1 0 0] [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 1 0 0] [-1 0 0 0 0 0] [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1], [ 0 0 0 0 0 1], <BLANKLINE> [0 0 0 0 0 1] [ 0 0 0 0 0 -1] [0 1 0 0 0 0] [ 0 1 0 0 0 0] [0 0 1 0 0 0] [ 0 0 1 0 0 0] [0 0 0 1 0 0] [ 0 0 0 1 0 0] [0 0 0 0 1 0] [ 0 0 0 0 1 0] [1 0 0 0 0 0], [-1 0 0 0 0 0] ] sage: f(O) # long time Matrix group over Rational Field with 6 generators: [[[1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]], [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]], [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1]], [[0, 0, 0, 0, -1, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [-1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1]], [[0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, -1], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [-1, 0, 0, 0, 0, 0]]] """ phi = gap(self) G = self.domain() F = G.base_ring() h = gap(g) return phi.Image(h)._matrix_(F)