Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Finite complex reflection groups ----------------------------------
Let `V` be a finite-dimensional complex vector space. A reflection of `V` is an operator `r \in \operatorname{GL}(V)` that has finite order and fixes pointwise a hyperplane in `V`.
For more definitions and classification types of finite complex reflection groups, see :wikipedia:`Complex_reflection_group`.
The point of entry to work with reflection groups is :func:`~sage.combinat.root_system.reflection_group_real.ReflectionGroup` which can be used with finite Cartan-Killing types::
sage: ReflectionGroup(['A',2]) # optional - gap3 Irreducible real reflection group of rank 2 and type A2 sage: ReflectionGroup(['F',4]) # optional - gap3 Irreducible real reflection group of rank 4 and type F4 sage: ReflectionGroup(['H',3]) # optional - gap3 Irreducible real reflection group of rank 3 and type H3
or with Shephard-Todd types::
sage: ReflectionGroup((1,1,3)) # optional - gap3 Irreducible real reflection group of rank 2 and type A2 sage: ReflectionGroup((2,1,3)) # optional - gap3 Irreducible real reflection group of rank 3 and type B3 sage: ReflectionGroup((3,1,3)) # optional - gap3 Irreducible complex reflection group of rank 3 and type G(3,1,3) sage: ReflectionGroup((4,2,3)) # optional - gap3 Irreducible complex reflection group of rank 3 and type G(4,2,3) sage: ReflectionGroup(4) # optional - gap3 Irreducible complex reflection group of rank 2 and type ST4 sage: ReflectionGroup(31) # optional - gap3 Irreducible complex reflection group of rank 4 and type ST31
Also reducible types are allowed using concatenation::
sage: ReflectionGroup(['A',3],(4,2,3)) # optional - gap3 Reducible complex reflection group of rank 6 and type A3 x G(4,2,3)
Some special cases also occur, among them are::
sage: W = ReflectionGroup((2,2,2)); W # optional - gap3 Reducible real reflection group of rank 2 and type A1 x A1 sage: W = ReflectionGroup((2,2,3)); W # optional - gap3 Irreducible real reflection group of rank 3 and type A3
.. WARNING:: Uses the GAP3 package *Chevie* which is available as an experimental package (installed by ``sage -i gap3``) or to download by hand from `Jean Michel's website <http://webusers.imj-prg.fr/~jean.michel/gap3/>`_.
A guided tour -------------
We start with the example type `B_2`::
sage: W = ReflectionGroup(['B',2]); W # optional - gap3 Irreducible real reflection group of rank 2 and type B2
Most importantly, observe that the group elements are usually represented by permutations of the roots::
sage: for w in W: print(w) # optional - gap3 () (1,3)(2,6)(5,7) (1,5)(2,4)(6,8) (1,7,5,3)(2,4,6,8) (1,3,5,7)(2,8,6,4) (2,8)(3,7)(4,6) (1,7)(3,5)(4,8) (1,5)(2,6)(3,7)(4,8)
This has the drawback that one can hardly see anything. Usually, one would look at elements with either of the following methods::
sage: for w in W: w.reduced_word() # optional - gap3 [] [2] [1] [1, 2] [2, 1] [2, 1, 2] [1, 2, 1] [1, 2, 1, 2]
sage: for w in W: w.reduced_word_in_reflections() # optional - gap3 [] [2] [1] [1, 2] [1, 4] [3] [4] [1, 3]
sage: for w in W: w.reduced_word(); w.to_matrix(); print("") # optional - gap3 [] [1 0] [0 1] <BLANKLINE> [2] [ 1 1] [ 0 -1] <BLANKLINE> [1] [-1 0] [ 2 1] <BLANKLINE> [1, 2] [-1 -1] [ 2 1] <BLANKLINE> [2, 1] [ 1 1] [-2 -1] <BLANKLINE> [2, 1, 2] [ 1 0] [-2 -1] <BLANKLINE> [1, 2, 1] [-1 -1] [ 0 1] <BLANKLINE> [1, 2, 1, 2] [-1 0] [ 0 -1] <BLANKLINE>
The standard references for actions of complex reflection groups have the matrices acting on the right, so::
sage: W.simple_reflection(1).to_matrix() # optional - gap3 [-1 0] [ 2 1]
sends the simple root `\alpha_0`, or ``(1,0)`` in vector notation, to its negative, while sending `\alpha_1` to `2\alpha_0+\alpha_1`.
.. TODO::
- properly provide root systems for real reflection groups - element class should be unique to be able to work with large groups without creating elements multiple times - ``is_shephard_group``, ``is_generalized_coxeter_group`` - exponents and coexponents - coinvariant ring:
* fake degrees from Torsten Hoge * operation of linear characters on all characters * harmonic polynomials
- linear forms for hyperplanes - field of definition - intersection lattice and characteristic polynomial::
X = [ alpha(t) for t in W.distinguished_reflections() ] X = Matrix(CF,X).transpose() Y = Matroid(X)
- linear characters - permutation pi on irreducibles - hyperplane orbits (76.13 in Gap Manual) - improve invariant_form with a code similar to the one in ``reflection_group_real.py`` - add a method ``reflection_to_root`` or ``distinguished_reflection_to_positive_root`` - diagrams in ASCII-art (76.15) - standard (BMR) presentations - character table directly from Chevie - ``GenericOrder`` (76.20), ``TorusOrder`` (76.21) - correct fundamental invariants for `G_34`, check the others - copy hardcoded data (degrees, invariants, braid relations...) into sage - add other hardcoded data from the tables in chevie (location is SAGEDIR/local/gap3/gap-jm5-2015-02-01/gap3/pkg/chevie/tbl): basic derivations, discriminant, ... - transfer code for ``reduced_word_in_reflections`` into Gap4 or Sage - list of reduced words for an element - list of reduced words in reflections for an element - Hurwitz action? - :meth:`is_crystallographic` should be hardcoded
AUTHORS:
- Christian Stump (2015): initial version """ #***************************************************************************** # Copyright (C) 2011-2016 Christian Stump <christian.stump at gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function
#from six.moves import range
from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.categories.category import Category from sage.categories.permutation_groups import PermutationGroups from sage.categories.complex_reflection_groups import ComplexReflectionGroups from sage.categories.coxeter_groups import CoxeterGroups from sage.combinat.root_system.reflection_group_element import ComplexReflectionGroupElement, _gap_return from sage.sets.family import Family from sage.structure.unique_representation import UniqueRepresentation from sage.groups.perm_gps.permgroup import PermutationGroup_generic from sage.rings.all import ZZ, QQ from sage.matrix.all import Matrix, identity_matrix from sage.structure.element import is_Matrix from sage.interfaces.gap3 import gap3 from sage.rings.universal_cyclotomic_field import E from sage.modules.free_module_element import vector from sage.combinat.root_system.cartan_matrix import CartanMatrix
from sage.misc.sage_eval import sage_eval
class ComplexReflectionGroup(UniqueRepresentation, PermutationGroup_generic): """ A complex reflection group given as a permutation group.
.. SEEALSO::
:func:`ReflectionGroup` """ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflection_index_set=None): r""" TESTS::
sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups sage: W = ComplexReflectionGroups().example() # optional - gap3 sage: TestSuite(W).run() # optional - gap3 """ W_components = [] reflection_type = [] for W_type in W_types: if W_type == (1,1,1): raise ValueError("the one element group is not considered a reflection group") elif W_type in ZZ: call_str = 'ComplexReflectionGroup(%s)'%W_type elif isinstance(W_type,CartanMatrix): call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._M._gap_())) elif is_Matrix(W_type): call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._gap_())) elif W_type in ZZ or ( isinstance(W_type, tuple) and len(W_type) == 3 ): call_str = 'ComplexReflectionGroup%s'%str(W_type) else: if W_type[0] == "I": call_str = 'CoxeterGroup("I",2,%s)'%W_type[1] else: call_str = 'CoxeterGroup("%s",%s)'%W_type
W_components.append(gap3(call_str)) X = list(W_components[-1].ReflectionType()) if len(X) > 1: raise ValueError("input data %s is invalid"%W_type) X = X[0] type_dict = {} type_dict["series"] = X.series.sage() type_dict["rank"] = X.rank.sage() type_dict["indices"] = X.indices.sage() if hasattr(X.ST,"sage"): type_dict["ST"] = X.ST.sage() elif hasattr(X.p,"sage") and hasattr(X.q,"sage"): type_dict["ST"] = ( X.p.sage(), X.q.sage(), X.rank.sage() ) elif hasattr(X.bond,"sage"): type_dict["bond"] = X.bond.sage() if type_dict["series"] == "B" and (X.cartanType.sage() == 1 or X.indices.sage() == [2,1]): type_dict["series"] = "C" reflection_type.append( type_dict )
self._type = reflection_type self._gap_group = prod(W_components) generators = [str(x) for x in self._gap_group.generators] self._index_set = index_set self._hyperplane_index_set = hyperplane_index_set self._reflection_index_set = reflection_index_set
self._conjugacy_classes = {} self._conjugacy_classes_representatives = None self._reflection_representation = None
self._rank = self._gap_group.rank.sage() if len(generators) == self._rank: category = ComplexReflectionGroups().Finite().WellGenerated() if all(str(W_comp).find('CoxeterGroup') >= 0 for W_comp in W_components): category = Category.join([category,CoxeterGroups()]) else: category = ComplexReflectionGroups().Finite() if len(self._type) == 1: category = category.Irreducible()
category = Category.join([category,PermutationGroups()]).Finite()
PermutationGroup_generic.__init__(self, gens=generators, canonicalize=False, category=category)
l_set = list(range(1, len(self.gens()) + 1)) if self._index_set is None: self._index_set = tuple(l_set) else: if len(self._index_set) != len(l_set): raise ValueError("the given index set (= %s) does not have the right size"%self._index_set.values()) self._index_set_inverse = {i: ii for ii,i in enumerate(self._index_set)} Nstar_set = list(range(1, self.number_of_reflection_hyperplanes() + 1)) if self._hyperplane_index_set is None: self._hyperplane_index_set = tuple(Nstar_set) else: if len(self._hyperplane_index_set) != len(Nstar_set): raise ValueError("the given hyperplane index set (= %s) does not have the right size"%self._index_set.values()) self._hyperplane_index_set_inverse = {i: ii for ii,i in enumerate(self._hyperplane_index_set)}
N_set = list(range(1, self.number_of_reflections() + 1)) if self._reflection_index_set is None: self._reflection_index_set = tuple(N_set) else: if len(self._reflection_index_set) != len(N_set): raise ValueError("the given reflection index set (= %s) does not have the right size"%self._index_set.values()) self._reflection_index_set_inverse = {i: ii for ii,i in enumerate(self._reflection_index_set)}
def _irrcomp_repr_(self,W_type): r""" Return the string representation of an irreducible component of ``self``.
TESTS::
sage: W = ReflectionGroup(25,[4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) sage: for W_type in W._type: print(W._irrcomp_repr_(W_type)) # optional - gap3 ST25 G(4,1,4) A3 I2(5) """ type_str = '' if "ST" in W_type: if W_type["ST"] in ZZ: type_str += "ST" + str(W_type["ST"]) else: type_str += 'G' + str(W_type["ST"]).replace(' ','') else: type_str += str(W_type["series"]) if W_type["series"] == "I": type_str += '2(' + str(W_type["bond"]) + ')' else: type_str += str(W_type["rank"]) return type_str
def _repr_(self): r""" Return the string representation of ``self``.
EXAMPLES::
sage: W = ReflectionGroup(25, [4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) """ type_str = '' for W_type in self._type: type_str += self._irrcomp_repr_(W_type) type_str += ' x ' type_str = type_str[:-3] return 'Reducible complex reflection group of rank %s and type %s'%(self._rank,type_str)
def __iter__(self): r""" Return an iterator going through all elements in ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: w # optional - gap3 () (1,3)(2,5)(4,6) (1,4)(2,3)(5,6) (1,6,2)(3,5,4) (1,2,6)(3,4,5) (1,5)(2,4)(3,6) """ from sage.combinat.root_system.reflection_group_c import iterator_tracking_words for w,word in iterator_tracking_words(self): w._reduced_word = word yield w
@cached_method def index_set(self): r""" Return the index set of the simple reflections of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: W.index_set() # optional - gap3 (1, 2, 3) sage: W = ReflectionGroup((1,1,4), index_set=[1,3,'asdf']) # optional - gap3 sage: W.index_set() # optional - gap3 (1, 3, 'asdf') sage: W = ReflectionGroup((1,1,4), index_set=('a', 'b', 'c')) # optional - gap3 sage: W.index_set() # optional - gap3 ('a', 'b', 'c') """ return self._index_set
def simple_reflection(self, i): r""" Return the ``i``-th simple reflection of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.simple_reflection(1) # optional - gap3 (1,4)(2,3)(5,6) sage: W.simple_reflections() # optional - gap3 Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6)} """ return self.gens()[self._index_set_inverse[i]]
def series(self): r""" Return the series of the classification type to which ``self`` belongs.
For real reflection groups, these are the Cartan-Killing classification types "A","B","C","D","E","F","G","H","I", and for complx non-real reflection groups these are the Shephard-Todd classification type "ST".
EXAMPLES::
sage: ReflectionGroup((1,1,3)).series() # optional - gap3 ['A'] sage: ReflectionGroup((3,1,3)).series() # optional - gap3 ['ST'] """ return [self._type[i]['series'] for i in range(len(self._type))]
@cached_method def hyperplane_index_set(self): r""" Return the index set of the hyperplanes of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: W.hyperplane_index_set() # optional - gap3 (1, 2, 3, 4, 5, 6) sage: W = ReflectionGroup((1,1,4), hyperplane_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 sage: W.hyperplane_index_set() # optional - gap3 (1, 3, 'asdf', 7, 9, 11) sage: W = ReflectionGroup((1,1,4),hyperplane_index_set=('a','b','c','d','e','f')) # optional - gap3 sage: W.hyperplane_index_set() # optional - gap3 ('a', 'b', 'c', 'd', 'e', 'f') """ return self._hyperplane_index_set
@cached_method def distinguished_reflections(self): r""" Return a finite family containing the distinguished reflections of ``self`` indexed by :meth:`hyperplane_index_set`.
These are the reflections in ``self`` acting on the complement of the fixed hyperplane `H` as `\operatorname{exp}(2 \pi i / n)`, where `n` is the order of the reflection subgroup fixing `H`.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)}
sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)}
sage: W = ReflectionGroup((3,1,1)) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 Finite family {1: (1,2,3)}
sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), 5: (1,7)(2,6)(5,8), 6: (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26), 7: (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29), 8: (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28)} """ # makes sure that the simple reflections come first gens = self.gens() R = [t for t in gens] # Then import all distinguished reflections from gap, # the Set is used as every such appears multiple times. for r in self._gap_group.Reflections(): t = self(str(r)) if t not in R: R.append(t) return Family(self._hyperplane_index_set, lambda i: R[self._hyperplane_index_set_inverse[i]])
def distinguished_reflection(self, i): r""" Return the ``i``-th distinguished reflection of ``self``.
These are the reflections in ``self`` acting on the complement of the fixed hyperplane `H` as `\operatorname{exp}(2 \pi i / n)`, where `n` is the order of the reflection subgroup fixing `H`.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.distinguished_reflection(1) # optional - gap3 (1,4)(2,3)(5,6) sage: W.distinguished_reflection(2) # optional - gap3 (1,3)(2,5)(4,6) sage: W.distinguished_reflection(3) # optional - gap3 (1,5)(2,4)(3,6)
sage: W = ReflectionGroup((3,1,1),hyperplane_index_set=['a']) # optional - gap3 sage: W.distinguished_reflection('a') # optional - gap3 (1,2,3)
sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 sage: for i in range(W.number_of_reflection_hyperplanes()): # optional - gap3 ....: W.distinguished_reflection(i+1) # optional - gap3 (1,6)(2,5)(7,8) (1,5)(2,7)(6,8) (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30) (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30) (1,7)(2,6)(5,8) (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26) (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29) (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28) """ return self.distinguished_reflections()[i]
@cached_method def reflection_hyperplanes(self, as_linear_functionals=False): r""" Return the list of all reflection hyperplanes of ``self``, either as a codimension 1 space, or as its linear functional.
INPUT:
- ``as_linear_functionals`` -- (default:``False``) flag whether to return the hyperplane or its linear functional in the basis dual to the given root basis
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for H in W.reflection_hyperplanes(): H # optional - gap3 Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 2] Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [ 1 1/2] Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [ 1 -1]
sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 (1, -1/2) (1, -2) (1, 1)
sage: W = ReflectionGroup((2,1,2)) # optional - gap3 sage: for H in W.reflection_hyperplanes(): H # optional - gap3 Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 1] Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [ 1 1/2] Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0] Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [0 1]
sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 (1, -1) (1, -2) (0, 1) (1, 0) """ Hs = [] for r in self.distinguished_reflections(): mat = (r.to_matrix().transpose() - identity_matrix(self.rank())) if as_linear_functionals: Hs.append( mat.row_space().gen() ) else: Hs.append( mat.right_kernel() ) return Family(self._hyperplane_index_set, lambda i: Hs[self._hyperplane_index_set_inverse[i]])
def reflection_hyperplane(self, i, as_linear_functional=False): r""" Return the ``i``-th reflection hyperplane of ``self``.
The ``i``-th reflection hyperplane corresponds to the ``i`` distinguished reflection.
INPUT:
- ``i`` -- an index in the index set - ``as_linear_functionals`` -- (default:``False``) flag whether to return the hyperplane or its linear functional in the basis dual to the given root basis
EXAMPLES::
sage: W = ReflectionGroup((2,1,2)) # optional - gap3 sage: W.reflection_hyperplane(3) # optional - gap3 Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0]
One can ask for the result as a linear form::
sage: W.reflection_hyperplane(3, True) # optional - gap3 (0, 1) """ return self.reflection_hyperplanes(as_linear_functionals=as_linear_functional)[i]
@cached_method def reflection_index_set(self): r""" Return the index set of the reflections of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: W.reflection_index_set() # optional - gap3 (1, 2, 3, 4, 5, 6) sage: W = ReflectionGroup((1,1,4), reflection_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 sage: W.reflection_index_set() # optional - gap3 (1, 3, 'asdf', 7, 9, 11) sage: W = ReflectionGroup((1,1,4), reflection_index_set=('a','b','c','d','e','f')) # optional - gap3 sage: W.reflection_index_set() # optional - gap3 ('a', 'b', 'c', 'd', 'e', 'f') """ return self._reflection_index_set
@cached_method def reflections(self): r""" Return a finite family containing the reflections of ``self``, indexed by :meth:`self.reflection_index_set`.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.reflections() # optional - gap3 Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)}
sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) # optional - gap3 sage: W.reflections() # optional - gap3 Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)}
sage: W = ReflectionGroup((3,1,1)) # optional - gap3 sage: W.reflections() # optional - gap3 Finite family {1: (1,2,3), 2: (1,3,2)}
sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 sage: W.reflections() # optional - gap3 Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), 5: (1,7)(2,6)(5,8), 6: (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26), 7: (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29), 8: (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28), 9: (3,15,9)(4,16,10)(12,23,17)(14,24,18)(20,29,25)(21,26,22)(27,30,28), 10: (4,27,21)(10,28,22)(11,19,13)(12,20,14)(16,30,26)(17,25,18)(23,29,24)} """ T = self.distinguished_reflections().values() for i in range(self.number_of_reflection_hyperplanes()): for j in range(2, T[i].order()): T.append(T[i]**j) return Family(self._reflection_index_set, lambda i: T[self._reflection_index_set_inverse[i]])
def reflection(self,i): r""" Return the ``i``-th reflection of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.reflection(1) # optional - gap3 (1,4)(2,3)(5,6) sage: W.reflection(2) # optional - gap3 (1,3)(2,5)(4,6) sage: W.reflection(3) # optional - gap3 (1,5)(2,4)(3,6)
sage: W = ReflectionGroup((3,1,1),reflection_index_set=['a','b']) # optional - gap3 sage: W.reflection('a') # optional - gap3 (1,2,3) sage: W.reflection('b') # optional - gap3 (1,3,2) """ return self.reflections()[i]
def reflection_character(self): r""" Return the reflection characters of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.reflection_character() # optional - gap3 [2, 0, -1] """ return self._gap_group.ReflectionCharacter().sage()
def is_crystallographic(self): r""" Return ``True`` if self is crystallographic.
This is, if the field of definition is the rational field.
.. TODO::
Make this more robust and do not use the matrix representation of the simple reflections.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 Irreducible real reflection group of rank 2 and type A2 sage: W.is_crystallographic() # optional - gap3 True
sage: W = ReflectionGroup((2,1,3)); W # optional - gap3 Irreducible real reflection group of rank 3 and type B3 sage: W.is_crystallographic() # optional - gap3 True
sage: W = ReflectionGroup(23); W # optional - gap3 Irreducible real reflection group of rank 3 and type H3 sage: W.is_crystallographic() # optional - gap3 False
sage: W = ReflectionGroup((3,1,3)); W # optional - gap3 Irreducible complex reflection group of rank 3 and type G(3,1,3) sage: W.is_crystallographic() # optional - gap3 False
sage: W = ReflectionGroup((4,2,2)); W # optional - gap3 Irreducible complex reflection group of rank 2 and type G(4,2,2) sage: W.is_crystallographic() # optional - gap3 False """ return self.is_real() and all(t.to_matrix().base_ring() is QQ for t in self.simple_reflections())
def _element_class(self): r""" A temporary workaround for compatibility with Sage's permutation groups.
TESTS::
sage: W = ReflectionGroup(23) # optional - gap3 sage: W._element_class() is W.element_class # optional - gap3 True """ return self.element_class
def number_of_irreducible_components(self): r""" Return the number of irreducible components of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.number_of_irreducible_components() # optional - gap3 1
sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 sage: W.number_of_irreducible_components() # optional - gap3 2 """ return len(self._type)
def irreducible_components(self): r""" Return a list containing the irreducible components of ``self`` as finite reflection groups.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.irreducible_components() # optional - gap3 [Irreducible real reflection group of rank 2 and type A2]
sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 sage: W.irreducible_components() # optional - gap3 [Irreducible real reflection group of rank 2 and type A2, Irreducible real reflection group of rank 3 and type B3] """ from sage.combinat.root_system.reflection_group_real import ReflectionGroup irr_comps = [] for W_type in self._type: if W_type["series"] in ["A","B","D","E","F","G","H","I"]: W_str = (W_type["series"],W_type["rank"]) elif "ST" in W_type: W_str = W_type["ST"] irr_comps.append(ReflectionGroup(W_str)) return irr_comps
@cached_method def conjugacy_classes_representatives(self): r""" Return the shortest representatives of the conjugacy classes of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 [[], [1], [1, 2]]
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 [[], [1], [1, 3], [1, 2], [1, 3, 2]]
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 [[], [1], [1, 1], [2, 1, 2, 1], [2, 1, 2, 1, 1], [2, 1, 1, 2, 1, 1], [2], [1, 2], [1, 1, 2]]
sage: W = ReflectionGroup(23) # optional - gap3 sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 [[], [1], [1, 2], [1, 3], [2, 3], [1, 2, 3], [1, 2, 1, 2], [1, 2, 1, 2, 3], [1, 2, 1, 2, 3, 2, 1, 2, 3], [1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3]] """ # This can be converted to usual GAP S = str(gap3('List(ConjugacyClasses(%s),Representative)'%self._gap_group._name)) exec('_conjugacy_classes_representatives=' + _gap_return(S)) return _conjugacy_classes_representatives
def conjugacy_classes(self): r""" Return the conjugacy classes of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for C in W.conjugacy_classes(): sorted(C) # optional - gap3 [()] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] [(1,2,6)(3,4,5), (1,6,2)(3,5,4)]
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 True
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 True
sage: W = ReflectionGroup(23) # optional - gap3 sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 True """ return Family(self.conjugacy_classes_representatives(), lambda w: w.conjugacy_class())
def rank(self): r""" Return the rank of ``self``.
This is the dimension of the underlying vector space.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.rank() # optional - gap3 2 sage: W = ReflectionGroup((2,1,3)) # optional - gap3 sage: W.rank() # optional - gap3 3 sage: W = ReflectionGroup((4,1,3)) # optional - gap3 sage: W.rank() # optional - gap3 3 sage: W = ReflectionGroup((4,2,3)) # optional - gap3 sage: W.rank() # optional - gap3 3 """ return self._rank
@cached_method def degrees(self): r""" Return the degrees of ``self`` ordered within each irreducible component of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: W.degrees() # optional - gap3 (2, 3, 4)
sage: W = ReflectionGroup((2,1,4)) # optional - gap3 sage: W.degrees() # optional - gap3 (2, 4, 6, 8)
sage: W = ReflectionGroup((4,1,4)) # optional - gap3 sage: W.degrees() # optional - gap3 (4, 8, 12, 16)
sage: W = ReflectionGroup((4,2,4)) # optional - gap3 sage: W.degrees() # optional - gap3 (4, 8, 8, 12)
sage: W = ReflectionGroup((4,4,4)) # optional - gap3 sage: W.degrees() # optional - gap3 (4, 4, 8, 12)
Examples of reducible types::
sage: W = ReflectionGroup((1,1,4), (3,1,2)); W # optional - gap3 Reducible complex reflection group of rank 5 and type A3 x G(3,1,2) sage: W.degrees() # optional - gap3 (2, 3, 4, 3, 6)
sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 sage: W.degrees() # optional - gap3 (2, 3, 4, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 2, 6, 10) """ if self.is_irreducible(): try: return tuple(sorted(self._gap_group.degrees.sage())) except AttributeError: return tuple(sorted(self._gap_group.ReflectionDegrees().sage())) else: return sum([comp.degrees() for comp in self.irreducible_components()],tuple())
@cached_method def codegrees(self): r""" Return the codegrees of ``self`` ordered within each irreducible component of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,4)) # optional - gap3 sage: W.codegrees() # optional - gap3 (2, 1, 0)
sage: W = ReflectionGroup((2,1,4)) # optional - gap3 sage: W.codegrees() # optional - gap3 (6, 4, 2, 0)
sage: W = ReflectionGroup((4,1,4)) # optional - gap3 sage: W.codegrees() # optional - gap3 (12, 8, 4, 0)
sage: W = ReflectionGroup((4,2,4)) # optional - gap3 sage: W.codegrees() # optional - gap3 (12, 8, 4, 0)
sage: W = ReflectionGroup((4,4,4)) # optional - gap3 sage: W.codegrees() # optional - gap3 (8, 8, 4, 0)
sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 sage: W.codegrees() # optional - gap3 (2, 1, 0, 3, 0)
sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 sage: W.codegrees() # optional - gap3 (2, 1, 0, 66, 60, 54, 48, 42, 36, 30, 24, 18, 12, 6, 0, 8, 4, 0) """ if self.is_irreducible(): if self.is_well_generated(): h = self.coxeter_number() return tuple([h-d for d in self.degrees()]) else: return tuple(sorted(self._gap_group.ReflectionCoDegrees().sage(), reverse=True)) else: return sum([comp.codegrees() for comp in self.irreducible_components()],tuple())
@cached_method def reflection_eigenvalues_family(self): r""" Return the reflection eigenvalues of ``self`` as a finite family indexed by the class representatives of ``self``.
OUTPUT:
- list with entries `k/n` representing the eigenvalue `\zeta_n^k`.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.reflection_eigenvalues_family() # optional - gap3 Finite family {(): [0, 0], (1,4)(2,3)(5,6): [1/2, 0], (1,6,2)(3,5,4): [1/3, 2/3]}
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) # optional - gap3 () [0, 0] (1,3,9)(2,4,10)(6,11,17)(8,12,18)(14,19,23)(15,16,20)(21,22,24) [1/3, 0] (1,3,9)(2,16,24)(4,20,21)(5,7,13)(6,12,23)(8,19,17)(10,15,22)(11,18,14) [1/3, 1/3] (1,5)(2,6)(3,7)(4,8)(9,13)(10,14)(11,15)(12,16)(17,21)(18,22)(19,20)(23,24) [1/2, 0] (1,7,3,13,9,5)(2,8,16,19,24,17)(4,14,20,11,21,18)(6,15,12,22,23,10) [1/6, 2/3] (1,9,3)(2,10,4)(6,17,11)(8,18,12)(14,23,19)(15,20,16)(21,24,22) [2/3, 0] (1,9,3)(2,20,22)(4,15,24)(5,7,13)(6,18,19)(8,23,11)(10,16,21)(12,14,17) [1/3, 2/3] (1,9,3)(2,24,16)(4,21,20)(5,13,7)(6,23,12)(8,17,19)(10,22,15)(11,14,18) [2/3, 2/3] (1,13,9,7,3,5)(2,14,24,18,16,11)(4,6,21,23,20,12)(8,22,17,15,19,10) [1/3, 5/6]
sage: W = ReflectionGroup(23) # optional - gap3 sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) # optional - gap3 () [0, 0, 0] (1,8,4)(2,21,3)(5,10,11)(6,18,17)(7,9,12)(13,14,15)(16,23,19)(20,25,26)(22,24,27)(28,29,30) [1/3, 2/3, 0] (1,16)(2,5)(4,7)(6,9)(8,10)(11,13)(12,14)(17,20)(19,22)(21,24)(23,25)(26,28)(27,29) [1/2, 0, 0] (1,16)(2,9)(3,18)(4,10)(5,6)(7,8)(11,14)(12,13)(17,24)(19,25)(20,21)(22,23)(26,29)(27,28) [1/2, 1/2, 0] (1,16)(2,17)(3,18)(4,19)(5,20)(6,21)(7,22)(8,23)(9,24)(10,25)(11,26)(12,27)(13,28)(14,29)(15,30) [1/2, 1/2, 1/2] (1,19,20,2,7)(3,6,11,13,9)(4,5,17,22,16)(8,12,15,14,10)(18,21,26,28,24)(23,27,30,29,25) [1/5, 4/5, 0] (1,20,7,19,2)(3,11,9,6,13)(4,17,16,5,22)(8,15,10,12,14)(18,26,24,21,28)(23,30,25,27,29) [2/5, 3/5, 0] (1,23,26,29,22,16,8,11,14,7)(2,10,4,9,18,17,25,19,24,3)(5,21,27,30,28,20,6,12,15,13) [1/10, 1/2, 9/10] (1,24,17,16,9,2)(3,12,13,18,27,28)(4,21,29,19,6,14)(5,25,26,20,10,11)(7,23,30,22,8,15) [1/6, 1/2, 5/6] (1,29,8,7,26,16,14,23,22,11)(2,9,25,3,4,17,24,10,18,19)(5,30,6,13,27,20,15,21,28,12) [3/10, 1/2, 7/10] """ class_representatives = self.conjugacy_classes_representatives() Ev_list = self._gap_group.ReflectionEigenvalues().sage() return Family(class_representatives, lambda w: Ev_list[class_representatives.index(w)])
@cached_method def reflection_eigenvalues(self, w, is_class_representative=False): r""" Return the reflection eigenvalue of ``w`` in ``self``.
INPUT:
- ``is_class_representative`` -- boolean (default ``True``) whether to compute instead on the conjugacy class representative.
.. SEEALSO:: :meth:`reflection_eigenvalues_family`
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: # optional - gap3 ....: print('%s %s'%(w.reduced_word(), W.reflection_eigenvalues(w))) # optional - gap3 [] [0, 0] [2] [1/2, 0] [1] [1/2, 0] [1, 2] [1/3, 2/3] [2, 1] [1/3, 2/3] [1, 2, 1] [1/2, 0] """ if not is_class_representative: w = w.conjugacy_class_representative() return self.reflection_eigenvalues_family()[w]
@cached_method def simple_roots(self): r""" Return the simple roots of ``self``.
These are the roots corresponding to the simple reflections.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.simple_roots() # optional - gap3 Finite family {1: (1, 0), 2: (0, 1)}
sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 sage: W.simple_roots() # optional - gap3 Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, 0, 1)}
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W.simple_roots() # optional - gap3 Finite family {1: (1, 0), 2: (-1, 1)}
sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 sage: W.simple_roots() # optional - gap3 Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, -1, 1)} """ from sage.sets.family import Family return Family({ind:self.roots()[i] for i,ind in enumerate(self._index_set)})
def simple_root(self, i): r""" Return the simple root with index ``i``.
EXAMPLES::
sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.simple_root(1) # optional - gap3 (1, 0, 0) sage: W.simple_root(2) # optional - gap3 (0, 1, 0) sage: W.simple_root(3) # optional - gap3 (0, 0, 1)
TESTS::
sage: W.simple_root(0) # optional - gap3 Traceback (most recent call last): ... KeyError: 0 """ return self.simple_roots()[i]
@cached_method def simple_coroots(self): r""" Return the simple coroots of ``self``.
These are the coroots corresponding to the simple reflections.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.simple_coroots() # optional - gap3 Finite family {1: (2, -1), 2: (-1, 2)}
sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 sage: W.simple_coroots() # optional - gap3 Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, 2, -2), 5: (0, 0, 0, -1, 2)}
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W.simple_coroots() # optional - gap3 Finite family {1: (-2*E(3) - E(3)^2, 0), 2: (-1, 1)}
sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 sage: W.simple_coroots() # optional - gap3 Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, -2*E(3) - E(3)^2, 0), 5: (0, 0, 0, -1, 1)} """ from sage.sets.family import Family coroots = self._gap_group.simpleCoroots.sage() for i,coroot in enumerate(coroots): coroot = vector(coroot) coroot.set_immutable() coroots[i] = coroot return Family({ind:coroots[i] for i,ind in enumerate(self.index_set())})
def simple_coroot(self, i): r""" Return the simple root with index ``i``.
EXAMPLES::
sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.simple_coroot(1) # optional - gap3 (2, -1, 0) """ return self.simple_coroots()[i]
@cached_method def independent_roots(self): r""" Return a collection of simple roots generating the underlying vector space of ``self``.
For well-generated groups, these are all simple roots. Otherwise, a linearly independent subset of the simple roots is chosen.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.independent_roots() # optional - gap3 Finite family {1: (1, 0), 2: (0, 1)}
sage: W = ReflectionGroup((4,2,3)) # optional - gap3 sage: W.simple_roots() # optional - gap3 Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 3: (-1, 1, 0), 4: (0, -1, 1)} sage: W.independent_roots() # optional - gap3 Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 4: (0, -1, 1)} """ Delta = self.simple_roots() if self.is_well_generated(): return Delta
from sage.sets.family import Family basis = {} for ind in self._index_set: vec = Delta[ind] if Matrix(basis.values()+[vec]).rank() == len(basis) + 1: basis[ind] = vec return Family(basis)
@cached_method def roots(self): r""" Return all roots corresponding to all reflections of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.roots() # optional - gap3 [(1, 0), (0, 1), (1, 1), (-1, 0), (0, -1), (-1, -1)]
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W.roots() # optional - gap3 [(1, 0), (-1, 1), (E(3), 0), (-E(3), 1), (0, 1), (1, -1), (0, E(3)), (1, -E(3)), (E(3)^2, 0), (-E(3)^2, 1), (E(3), -1), (E(3), -E(3)), (0, E(3)^2), (1, -E(3)^2), (-1, E(3)), (-E(3), E(3)), (E(3)^2, -1), (E(3)^2, -E(3)), (E(3), -E(3)^2), (-E(3)^2, E(3)), (-1, E(3)^2), (-E(3), E(3)^2), (E(3)^2, -E(3)^2), (-E(3)^2, E(3)^2)]
sage: W = ReflectionGroup((4,2,2)) # optional - gap3 sage: W.roots() # optional - gap3 [(1, 0), (-E(4), 1), (-1, 1), (-1, 0), (E(4), 1), (1, 1), (0, -E(4)), (E(4), -1), (E(4), E(4)), (0, E(4)), (E(4), -E(4)), (0, 1), (1, -E(4)), (1, -1), (0, -1), (1, E(4)), (-E(4), 0), (-1, E(4)), (E(4), 0), (-E(4), E(4)), (-E(4), -1), (-E(4), -E(4)), (-1, -E(4)), (-1, -1)]
sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 sage: W.roots() # optional - gap3 [(1, 0, 0, 0, 0), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0), (0, 0, 0, 1, 0), (0, 0, 0, -1, 1), (1, 1, 0, 0, 0), (0, 1, 1, 0, 0), (1, 1, 1, 0, 0), (-1, 0, 0, 0, 0), (0, -1, 0, 0, 0), (0, 0, -1, 0, 0), (-1, -1, 0, 0, 0), (0, -1, -1, 0, 0), (-1, -1, -1, 0, 0), (0, 0, 0, E(3), 0), (0, 0, 0, -E(3), 1), (0, 0, 0, 0, 1), (0, 0, 0, 1, -1), (0, 0, 0, 0, E(3)), (0, 0, 0, 1, -E(3)), (0, 0, 0, E(3)^2, 0), (0, 0, 0, -E(3)^2, 1), (0, 0, 0, E(3), -1), (0, 0, 0, E(3), -E(3)), (0, 0, 0, 0, E(3)^2), (0, 0, 0, 1, -E(3)^2), (0, 0, 0, -1, E(3)), (0, 0, 0, -E(3), E(3)), (0, 0, 0, E(3)^2, -1), (0, 0, 0, E(3)^2, -E(3)), (0, 0, 0, E(3), -E(3)^2), (0, 0, 0, -E(3)^2, E(3)), (0, 0, 0, -1, E(3)^2), (0, 0, 0, -E(3), E(3)^2), (0, 0, 0, E(3)^2, -E(3)^2), (0, 0, 0, -E(3)^2, E(3)^2)] """ roots = [vector(sage_eval(str(root).replace("^","**"))) for root in self._gap_group.roots] for v in roots: v.set_immutable() return roots
@cached_method def braid_relations(self): r""" Return the braid relations of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 [[[2, 1, 2], [1, 2, 1]]]
sage: W = ReflectionGroup((2,1,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 [[[2, 1, 2, 1], [1, 2, 1, 2]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]]
sage: W = ReflectionGroup((2,2,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 [[[2, 1, 2], [1, 2, 1]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]] """ return self._gap_group.BraidRelations().sage()
def fundamental_invariants(self): r""" Return the fundamental invariants of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.fundamental_invariants() # optional - gap3 (-2*x0^2 + 2*x0*x1 - 2*x1^2, 6*x0^2*x1 - 6*x0*x1^2)
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W.fundamental_invariants() # optional - gap3 (x0^3 + x1^3, x0^3*x1^3) """ import re from sage.rings.polynomial.all import PolynomialRing
if not self.is_irreducible(): return sum([W.fundamental_invariants() for W in self.irreducible_components() ],tuple())
I = [ str(p) for p in gap3('List(Invariants(%s),x->ApplyFunc(x,List([0..%s],i->Mvp(SPrint("x",i)))))'%(self._gap_group._name,self.rank()-1)) ] P = PolynomialRing(QQ,['x%s'%i for i in range(0,self.rank())]) x = P.gens() for i in range(len(I)): I[i] = I[i].replace('^','**') I[i] = re.compile('E(\d\d*)').sub(r'E(\1)', I[i]) I[i] = re.compile('(\d)E\(').sub(r'\1*E(', I[i]) for j in range(len(x)): I[i] = I[i].replace('x%s'%j,'*x[%s]'%j) I[i] = I[i].replace("+*","+").replace("-*","-").replace("ER(5)","*(E(5)-E(5)**2-E(5)**3+E(5)**4)").lstrip("*") # sage_eval is used since eval kills the rational entries! I = [sage_eval(p, locals={'x': x}) for p in I] return tuple(sorted(I, key=lambda f: f.degree()))
def cartan_matrix(self): r""" Return the Cartan matrix associated with ``self``.
If ``self`` is crystallographic, the returned Cartan matrix is an instance of :class:`CartanMatrix`, and a normal matrix otherwise.
Let `s_1, \ldots, s_n` be a set of reflections which generate ``self`` with associated simple roots `s_1,\ldots,s_n` and simple coroots `s^\vee_i`. Then the Cartan matrix `C = (c_{ij})` is given by `s^\vee_i(s_j)`. The Cartan matrix completely determines the reflection representation if the `s_i` are linearly independent.
EXAMPLES::
sage: ReflectionGroup(['A',4]).cartan_matrix() # optional - gap3 [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2]
sage: ReflectionGroup(['H',4]).cartan_matrix() # optional - gap3 [ 2 E(5)^2 + E(5)^3 0 0] [E(5)^2 + E(5)^3 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2]
sage: ReflectionGroup(4).cartan_matrix() # optional - gap3 [-2*E(3) - E(3)^2 E(3)^2] [ -E(3)^2 -2*E(3) - E(3)^2]
sage: ReflectionGroup((4,2,2)).cartan_matrix() # optional - gap3 [ 2 -2*E(4) -2] [ E(4) 2 1 - E(4)] [ -1 1 + E(4) 2] """ # an alternative implementation is # Matrix(tuple(W.simple_coroots()))*Matrix(tuple(W.simple_roots())).transpose() # this should be implemented once we get the simple roots in an easy way if self.is_crystallographic(): from sage.combinat.root_system.cartan_matrix import CartanMatrix as CartanMat else: from sage.matrix.all import Matrix as CartanMat return CartanMat(self._gap_group.CartanMat().sage())
def invariant_form(self, brute_force=False): r""" Return the form that is invariant under the action of ``self``.
This is unique only up to a global scalar on the irreducible components.
INPUT:
- ``brute_force`` -- if ``True``, the computation is done by applying the Reynolds operator; this is, the invariant form of `e_i` and `e_j` is computed as the sum `\langle w(e_i), w(e_j)\rangle`, where `\langle \cdot, \cdot\rangle` is the standard scalar product
EXAMPLES::
sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: F = W.invariant_form(); F # optional - gap3 [ 1 -1/2 0] [-1/2 1 -1/2] [ 0 -1/2 1]
To check that this is indeed the invariant form, see::
sage: S = W.simple_reflections() # optional - gap3 sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 True
sage: W = ReflectionGroup(['B',3]) # optional - gap3 sage: F = W.invariant_form(); F # optional - gap3 [ 1 -1 0] [-1 2 -1] [ 0 -1 2] sage: w = W.an_element().to_matrix() # optional - gap3 sage: w * F * w.transpose().conjugate() == F # optional - gap3 True
sage: S = W.simple_reflections() # optional - gap3 sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 True
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: F = W.invariant_form(); F # optional - gap3 [1 0] [0 1]
sage: S = W.simple_reflections() # optional - gap3 sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 True
It also worked for badly generated groups::
sage: W = ReflectionGroup(7) # optional - gap3 sage: W.is_well_generated() # optional - gap3 False
sage: F = W.invariant_form(); F # optional - gap3 [1 0] [0 1] sage: S = W.simple_reflections() # optional - gap3 sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 True
And also for reducible types::
sage: W = ReflectionGroup(['B',3],(4,2,3),4,7); W # optional - gap3 Reducible complex reflection group of rank 10 and type B3 x G(4,2,3) x ST4 x ST7 sage: F = W.invariant_form(); S = W.simple_reflections() # optional - gap3 sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 True
TESTS::
sage: tests = [['A',3],['B',3],['F',4],(4,2,2),4,7] # optional - gap3 sage: for ty in tests: # optional - gap3 ....: W = ReflectionGroup(ty) # optional - gap3 ....: A = W.invariant_form() # optional - gap3 ....: B = W.invariant_form(brute_force=True) # optional - gap3 ....: print("{} {}".format(ty, A == B/B[0,0])) # optional - gap3 ['A', 3] True ['B', 3] True ['F', 4] True (4, 2, 2) True 4 True 7 True """ if brute_force: return self._invariant_form_brute_force()
n = self.rank() from sage.matrix.constructor import zero_matrix
if self.is_crystallographic(): ring = QQ else: from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField ring = UniversalCyclotomicField()
form = zero_matrix(ring, n, n)
C = self.cartan_matrix() if not self.is_well_generated(): indep_inds = sorted(self._index_set_inverse[key] for key in self.independent_roots().keys()) C = C.matrix_from_rows_and_columns(indep_inds,indep_inds)
for j in range(n): for i in range(j): if C[j,i] != 0: form[j,j] = form[i,i] * \ ( C[i,j] * C[j,j].conjugate() ) / \ ( C[j,i].conjugate() * C[i,i] ) if form[j,j] == 0: form[j,j] = ring.one() for j in range(n): for i in range(j): form[j, i] = C[i, j] * form[i, i] / C[i,i] form[i, j] = form[j, i].conjugate()
B = self.base_change_matrix() form = B*form*B.conjugate().transpose() form /= form[0,0] form.set_immutable() return form
def _invariant_form_brute_force(self): r""" Return the form that is invariant under the action of ``self``.
This brute force algorithm is only kept for possible testing.
EXAMPLES::
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W._invariant_form_brute_force() # optional - gap3 [1 0] [0 1] """ from sage.misc.cachefunc import cached_function
base_change = self.base_change_matrix() Delta = tuple(self.independent_roots()) basis_is_Delta = base_change.is_one() if not basis_is_Delta: Delta = [beta * base_change for beta in Delta]
S = self.simple_reflections() n = self.rank()
def action_on_root(w, beta): if basis_is_Delta: return w.action_on_root(beta) else: return beta * w.to_matrix()
@cached_function def invariant_value(i,j): if i > j: return invariant_value(j,i).conjugate() val = sum(action_on_root(w, Delta[i]) * action_on_root(w, Delta[j]).conjugate() for w in self) if val in QQ: val = QQ(val) return val
coeffs = [] for i in self.index_set(): coeff = 1-E(S[i].order()) if coeff in QQ: coeff = QQ(coeff) coeffs.append(coeff)
return Matrix([[invariant_value(i,j) / self.cardinality() for j in range(n)] for i in range(n)])
def set_reflection_representation(self,refl_repr=None): r""" Set the reflection representation of ``self``.
INPUT:
- ``refl_repr`` -- a dictionary representing the matrices of the generators of ``self`` with keys given by the index set, or ``None`` to reset to the default reflection representation
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 [1 0] [0 1] ----- [ 1 1] [ 0 -1] ----- [-1 0] [ 1 1] ----- [-1 -1] [ 1 0] ----- [ 0 1] [-1 -1] ----- [ 0 -1] [-1 0] -----
sage: W.set_reflection_representation({1: matrix([[0,1,0],[1,0,0],[0,0,1]]), 2: matrix([[1,0,0],[0,0,1],[0,1,0]])}) # optional - gap3 sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 [1 0 0] [0 1 0] [0 0 1] ----- [1 0 0] [0 0 1] [0 1 0] ----- [0 1 0] [1 0 0] [0 0 1] ----- [0 0 1] [1 0 0] [0 1 0] ----- [0 1 0] [0 0 1] [1 0 0] ----- [0 0 1] [0 1 0] [1 0 0] ----- sage: W.set_reflection_representation() # optional - gap3 """ if refl_repr is None or set(refl_repr) == set(self.index_set()): self._reflection_representation = refl_repr else: raise ValueError("the reflection representation must be defined for the complete index set")
class Element(ComplexReflectionGroupElement): #@cached_in_parent_method def conjugacy_class_representative(self): r""" Return a representative of the conjugacy class of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: # optional - gap3 ....: print('%s %s'%(w.reduced_word(), w.conjugacy_class_representative().reduced_word())) # optional - gap3 [] [] [2] [1] [1] [1] [1, 2] [1, 2] [2, 1] [1, 2] [1, 2, 1] [1] """ W = self.parent() for w in W._conjugacy_classes: if self in W._conjugacy_classes[w]: return w return W.conjugacy_classes_representatives()[ gap3("PositionClass(%s,%s)"%(W._gap_group._name,self)).sage()-1 ]
def conjugacy_class(self): r""" Return the conjugacy class of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: sorted(w.conjugacy_class()) # optional - gap3 [()] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] """ W = self.parent() if self not in W.conjugacy_classes_representatives(): self = self.conjugacy_class_representative() if self in W._conjugacy_classes: return W._conjugacy_classes[self] gens = W.simple_reflections() count = 0 orbit = [self] orbit_set = set(orbit) while count < len(orbit): w = orbit[count] count += 1 for s in gens: w_new = s*w*s**-1 if w_new not in orbit_set: orbit.append(w_new) orbit_set.add(w_new) orbit_set = frozenset(orbit_set) W._conjugacy_classes[self] = orbit_set return orbit_set
#@cached_in_parent_method def reflection_length(self, in_unitary_group=False): r""" Return the reflection length of ``self``.
This is the minimal numbers of reflections needed to obtain ``self``.
INPUT:
- ``in_unitary_group`` -- (default: ``False``) if ``True``, the reflection length is computed in the unitary group which is the dimension of the move space of ``self``
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: sorted([t.reflection_length() for t in W]) # optional - gap3 [0, 1, 1, 1, 2, 2]
sage: W = ReflectionGroup((2,1,2)) # optional - gap3 sage: sorted([t.reflection_length() for t in W]) # optional - gap3 [0, 1, 1, 1, 1, 2, 2, 2]
sage: W = ReflectionGroup((2,2,2)) # optional - gap3 sage: sorted([t.reflection_length() for t in W]) # optional - gap3 [0, 1, 1, 2]
sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: sorted([t.reflection_length() for t in W]) # optional - gap3 [0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ W = self.parent() if self in W.conjugacy_classes_representatives(): if in_unitary_group or W.is_real(): return W.rank() - self.reflection_eigenvalues(is_class_representative=True).count(0) else: return len(self.reduced_word_in_reflections()) else: w = self.conjugacy_class_representative() # the following assert a possible implementation bug and # is hopefully never needed assert w in self.parent().conjugacy_classes_representatives() return w.reflection_length(in_unitary_group=in_unitary_group)
class IrreducibleComplexReflectionGroup(ComplexReflectionGroup):
def _repr_(self): r""" Return the string representation of ``self``.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 Irreducible real reflection group of rank 2 and type A2 sage: W = ReflectionGroup((3,1,4)); W # optional - gap3 Irreducible complex reflection group of rank 4 and type G(3,1,4) """ type_str = self._irrcomp_repr_(self._type[0]) return 'Irreducible complex reflection group of rank %s and type %s'%(self._rank,type_str)
class Element(ComplexReflectionGroup.Element):
# TODO: lift to ComplexReflectionGroups.Finite # this method can be defined for well-generated, finite, # irreducible complex reflection group. The current # implementation uses this particular connection to chevie. #@cached_in_parent_method def is_coxeter_element(self, which_primitive=1, is_class_representative=False): r""" Return ``True`` if ``self`` is a Coxeter element.
This is, whether ``self`` has an eigenvalue that is a primitive `h`-th root of unity.
INPUT:
- ``which_primitive`` -- (default:``1``) for which power of the first primitive ``h``-th root of unity to look as a reflection eigenvalue for a regular element
- ``is_class_representative`` -- boolean (default ``True``) whether to compute instead on the conjugacy class representative
.. SEEALSO::
:meth:`~IrreducibleComplexReflectionGroup.coxeter_element` :meth:`~sage.categories.finite_complex_reflection_groups.coxeter_elements`
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: # optional - gap3 ....: print('%s %s'%(w.reduced_word(), w.is_coxeter_element())) # optional - gap3 [] False [2] False [1] False [1, 2] True [2, 1] True [1, 2, 1] False """ if not self.parent().is_irreducible() or not self.parent().is_well_generated(): raise ValueError("this method is available for elements in irreducible, well-generated complex reflection groups") h = self.parent().coxeter_number() # to check regularity for a Coxeter number h, we get that an eigenvector is regular for free return any(QQ(ev).denom() == h and QQ(ev).numer() == which_primitive for ev in self.reflection_eigenvalues(is_class_representative=is_class_representative))
#@cached_in_parent_method def is_h_regular(self, is_class_representative=False): r""" Return whether ``self`` is regular.
This is if ``self`` has an eigenvector with eigenvalue `h` and which does not lie in any reflection hyperplane. Here, `h` denotes the Coxeter number.
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: for w in W: # optional - gap3 ....: print('%s %s'%(w.reduced_word(), w.is_h_regular())) # optional - gap3 [] False [2] False [1] False [1, 2] True [2, 1] True [1, 2, 1] False """ if not self.parent().is_irreducible() or not self.parent().is_well_generated(): raise ValueError("This method is available for elements in irreducible, well-generated complex reflection groups") h = self.parent().coxeter_number() # to check regularity for a Coxeter number h, we get that an eigenvector is regular for free return any(QQ(ev).denom() == h for ev in self.reflection_eigenvalues(is_class_representative=is_class_representative))
#@cached_in_parent_method def is_regular(self, h, is_class_representative=False): r""" Return whether ``self`` is regular.
This is, if ``self`` has an eigenvector with eigenvalue ``h`` and which does not lie in any reflection hyperplane.
- ``is_class_representative`` -- boolean (default ``True``) whether to compute instead on the conjugacy class representative
EXAMPLES::
sage: W = ReflectionGroup((1,1,3)); h = W.coxeter_number() # optional - gap3 sage: for w in W: # optional - gap3 ....: print("{} {}".format(w.reduced_word(), w.is_regular(h))) [] False [2] False [1] False [1, 2] True [2, 1] True [1, 2, 1] False
sage: W = ReflectionGroup(23); h = W.coxeter_number() # optional - gap3 sage: for w in W: # optional - gap3 ....: if w.is_regular(h): # optional - gap3 ....: w.reduced_word() # optional - gap3 [1, 2, 3] [2, 1, 3] [1, 3, 2] [3, 2, 1] [2, 1, 2, 3, 2] [2, 3, 2, 1, 2] [1, 2, 1, 2, 3, 2, 1] [1, 2, 3, 2, 1, 2, 1] [1, 2, 1, 2, 3, 2, 1, 2, 3] [2, 1, 2, 1, 3, 2, 1, 2, 3] [2, 1, 2, 3, 2, 1, 2, 1, 3] [1, 2, 3, 2, 1, 2, 1, 3, 2] [3, 2, 1, 2, 1, 3, 2, 1, 2] [1, 2, 1, 2, 1, 3, 2, 1, 2] [2, 3, 2, 1, 2, 1, 3, 2, 1] [2, 1, 2, 1, 3, 2, 1, 2, 1] [2, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] [1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] [1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3] [1, 2, 1, 2, 3, 2, 1, 2, 1, 3, 2] [1, 2, 3, 2, 1, 2, 1, 3, 2, 1, 2] [2, 1, 2, 3, 2, 1, 2, 1, 3, 2, 1] [2, 1, 2, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] [1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] """ evs = self.reflection_eigenvalues(is_class_representative=is_class_representative) I = identity_matrix(self.parent().rank()) for ev in evs: ev = QQ(ev) if h == ev.denom(): M = self.to_matrix() - E(ev.denom(),ev.numer()) * I V = M.right_kernel() if all(not V.is_subspace(H) for H in self.parent().reflection_hyperplanes()): return True return False
|