Source code for surface_dynamics.misc.group_representation

# coding: utf-8
r"""
Some utility function for representations of finite groups

Most of the functions are just GAP wrappers.
"""
# *************************************************************************
# Copyright (C) 2015-2016 Charles Fougeron <charlesfougeron@gmail.com>
#                         Vincent Delecroix <20100.delecroix@gmail.com>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#  as published by the Free Software Foundation; either version 2 of
#  the License, or (at your option) any later version.
#                  https://www.gnu.org/licenses/
# *************************************************************************

from sage.rings.all import ZZ
from sage.libs.gap.libgap import libgap
import numpy as np


[docs] def real_characters(G): r""" Return a pair ``(table of characters, character degrees)`` for the group ``G``. OUTPUT: - table of characters - a list of characters. Each character is represented as the list of its values on conjugacy classes. The order of conjugacy classes is the same as the one returned by GAP. - degrees - the list of degrees of the characters EXAMPLES:: sage: from surface_dynamics.misc.group_representation import real_characters sage: T, deg = real_characters(AlternatingGroup(5)) sage: T [(1, 1, 1, 1, 1), (3, -1, 0, -E(5) - E(5)^4, -E(5)^2 - E(5)^3), (3, -1, 0, -E(5)^2 - E(5)^3, -E(5) - E(5)^4), (4, 0, 1, -1, -1), (5, 1, -1, 0, 0)] sage: set(parent(x) for chi in T for x in chi) {Universal Cyclotomic Field} sage: deg [1, 3, 3, 4, 5] sage: T, deg = real_characters(CyclicPermutationGroup(6)) sage: T [(1, 1, 1, 1, 1, 1), (1, -1, 1, -1, 1, -1), (2, -1, -1, 2, -1, -1), (2, 1, -1, -2, -1, 1)] sage: deg [1, 1, 1, 1] """ from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField G = libgap(G) UCF = UniversalCyclotomicField() n = G.ConjugacyClasses().Length() Tgap = G.CharacterTable().Irr() degrees = [chi.Degree().sage() for chi in Tgap] Tgap = [tuple(UCF(chi[j]) for j in range(n)) for chi in Tgap] real_T = [] real_degrees = [] seen = set() for i, chi in enumerate(Tgap): if chi in seen: continue real_degrees.append(degrees[i]) if all(x.is_real() for x in chi): real_T.append(chi) seen.add(chi) else: seen.add(chi) chi_bar = tuple(z.conjugate() for z in chi) seen.add(chi_bar) real_T.append(tuple(chi[j] + chi[j].conjugate() for j in range(n))) return (real_T, real_degrees)
[docs] def conjugacy_class_matrix(cl, d): r""" Return the matrix associated to a given conjugacy class of a permutation group. The result is a `d \times d` matrix that is invariant under conjugation by the group action on `\ZZ^d`. It is used to produce the projection matrices on the isotypic subspaces. EXAMPLES:: sage: from surface_dynamics.misc.group_representation import conjugacy_class_matrix sage: G = QuaternionGroup() sage: Ggap = libgap(G) sage: cls = Ggap.ConjugacyClasses() sage: m = conjugacy_class_matrix(cls[2], 8) sage: m [0 0 1 0 0 0 0 0] [0 0 0 1 0 0 0 0] [1 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0] [0 0 0 0 0 0 1 0] [0 0 0 0 0 0 0 1] [0 0 0 0 1 0 0 0] [0 0 0 0 0 1 0 0] sage: for cl in cls: ....: m = conjugacy_class_matrix(cl, 8) ....: for g in G: ....: g = g.matrix() ....: assert g*m*~g == m sage: A = AlternatingGroup(5) sage: Agap = libgap(A) sage: Ggap = Agap.Action(Agap) sage: gens = libgap.GeneratorsOfGroup(Ggap).sage() sage: G = PermutationGroup(gens) sage: cls = Ggap.ConjugacyClasses() sage: for cl in cls: ....: m = conjugacy_class_matrix(cls[2], 60) ....: for _ in range(20): ....: g = G.random_element().matrix() ....: assert g*m*~g == m """ res = [[0] * d for _ in range(d)] for p in cl.AsList(): p = [i - 1 for i in libgap.ListPerm(p, d).sage()] for k in range(d): res[k][p[k]] += 1 from sage.matrix.constructor import matrix return matrix(ZZ, res)
[docs] def regular_conjugacy_class_matrix(cl, G): r""" Return the matrix of the conjugacy class ``cl`` associated to the regular representation of ``G``. """ Glist = G.AsList() d = G.Size().sage() res = [[0]*d for _ in range(d)] for p in cl.AsList(): p = libgap.Permutation(p, Glist, libgap.OnRight) p = libgap.ListPerm(p, d) for k in range(d): res[k][p[k] - 1] += 1 from sage.matrix.constructor import matrix return matrix(ZZ, res)
[docs] def isotypic_projection_matrix(G, d, chi, deg, conj_mats=None, floating_point=False): r""" Return an isotypic projection matrix INPUT: - ``G`` -- a permutation group - ``d`` -- (integer) the domain of the group is `\{1, 2, \ldots, d\}` - ``chi`` -- (tuple) real or complex character - ``deg`` -- (integer) degree of the character - ``conj_mats`` -- (optional list) matrices of the conjugacy classes - ``floating_point`` -- whether to return matrices with floating point entries instead of elements in the cyclotomic field Recall the formula for the projection as given in Theorem 8 in [Ser67]_. If `G` is a permutation group, then .. MATH:: \pi_\chi = \sum_{g \in G} \overline_{\chi(g)} g EXAMPLES:: sage: from surface_dynamics.misc.group_representation import real_characters, isotypic_projection_matrix sage: G = AlternatingGroup(5) sage: T,deg = real_characters(G) sage: isotypic_projection_matrix(G, 5, T[0], deg[0]) [1/5 1/5 1/5 1/5 1/5] [1/5 1/5 1/5 1/5 1/5] [1/5 1/5 1/5 1/5 1/5] [1/5 1/5 1/5 1/5 1/5] [1/5 1/5 1/5 1/5 1/5] sage: isotypic_projection_matrix(G, 5, T[1], deg[1]) [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] sage: isotypic_projection_matrix(G, 5, T[2], deg[2]) [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] sage: isotypic_projection_matrix(G, 5, T[3], deg[3]) [ 4/5 -1/5 -1/5 -1/5 -1/5] [-1/5 4/5 -1/5 -1/5 -1/5] [-1/5 -1/5 4/5 -1/5 -1/5] [-1/5 -1/5 -1/5 4/5 -1/5] [-1/5 -1/5 -1/5 -1/5 4/5] sage: isotypic_projection_matrix(G, 5, T[3], deg[3], floating_point=True) array([[ 0.8, -0.2, -0.2, -0.2, -0.2], [-0.2, 0.8, -0.2, -0.2, -0.2], [-0.2, -0.2, 0.8, -0.2, -0.2], [-0.2, -0.2, -0.2, 0.8, -0.2], [-0.2, -0.2, -0.2, -0.2, 0.8]]) sage: isotypic_projection_matrix(G, 5, T[4], deg[4]) [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] sage: sum(isotypic_projection_matrix(G, 5, T[i], deg[i]) for i in range(5)).is_one() True """ from sage.matrix.special import zero_matrix res = zero_matrix(d) Ggap = libgap(G) for t, cl in enumerate(Ggap.ConjugacyClasses()): if conj_mats is None: m = conjugacy_class_matrix(cl, d) else: m = conj_mats[t] if floating_point: res += np.float64(chi[t]) * m.numpy() else: res += chi[t] * m if floating_point: return int(deg) * res / int(G.cardinality()) else: return deg * res / G.cardinality()
[docs] def real_isotypic_projection_matrices(G, d): r""" Return the real projections EXAMPLES:: sage: from surface_dynamics.misc.group_representation import real_isotypic_projection_matrices sage: G = AlternatingGroup(5) sage: mats = real_isotypic_projection_matrices(G, 6) sage: sum(mats).is_one() True sage: G = CyclicPermutationGroup(6) sage: mats = real_isotypic_projection_matrices(G, 6) sage: sum(mats).is_one() True sage: G = QuaternionGroup() sage: mats = real_isotypic_projection_matrices(G, 8) sage: sum(mats).is_one() True """ return [isotypic_projection_matrix(G, d, chi, deg) for chi, deg in zip(*real_characters(G))]