Path: blob/master/src/sage/groups/matrix_gps/coxeter_group.py
8815 views
"""1Coxeter Groups As Matrix Groups23This implements a general Coxeter group as a matrix group by using the4reflection representation.56AUTHORS:78- Travis Scrimshaw (2013-08-28): Initial version9"""1011##############################################################################12# Copyright (C) 2013 Travis Scrimshaw <tscrim at ucdavis.edu>13#14# Distributed under the terms of the GNU General Public License (GPL)15#16# The full text of the GPL is available at:17#18# http://www.gnu.org/licenses/19##############################################################################2021from sage.structure.unique_representation import UniqueRepresentation22from sage.categories.coxeter_groups import CoxeterGroups2324from sage.combinat.root_system.cartan_type import CartanType, CartanType_abstract25from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_generic26from sage.groups.matrix_gps.group_element import MatrixGroupElement_generic27from sage.graphs.graph import Graph28from sage.matrix.constructor import matrix29from sage.matrix.matrix_space import MatrixSpace30from sage.rings.all import ZZ31from sage.rings.infinity import infinity32from sage.rings.universal_cyclotomic_field.universal_cyclotomic_field import UniversalCyclotomicField333435class CoxeterMatrixGroup(FinitelyGeneratedMatrixGroup_generic, UniqueRepresentation):36r"""37A Coxeter group represented as a matrix group.3839Let `(W, S)` be a Coxeter system and we construct a vector space `V`40over `\RR` with a basis of `\{ \alpha_s \}_{s \in S}` and inner product4142.. MATH::4344B(\alpha_s, \alpha_t) = -\cos\left( \frac{\pi}{m_{st}} \right)4546where we have `B(\alpha_s, \alpha_t) = -1` if `m_{st} = \infty`. Next we47define a representation `\sigma_s : V \to V` by4849.. MATH::5051\sigma_s \lambda = \lambda - 2 B(\alpha_s, \lambda) \alpha_s.5253This representation is faithful so we can represent the Coxeter group `W`54by the set of matrices `\sigma_s` acting on `V`.5556INPUT:5758- ``data`` -- a Coxeter matrix or graph or a Cartan type59- ``base_ring`` -- (default: the universal cyclotomic field) the base60ring which contains all values `\cos(\pi/m_{ij})` where `(m_{ij})_{ij}`61is the Coxeter matrix62- ``index_set`` -- (optional) an indexing set for the generators6364For more on creating Coxeter groups, see65:meth:`~sage.combinat.root_system.coxeter_group.CoxeterGroup`.6667.. TODO::6869Currently the label `\infty` is implemented as `-1` in the Coxeter70matrix.7172EXAMPLES:7374We can create Coxeter groups from Coxeter matrices::7576sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]])77sage: W78Coxeter group over Universal Cyclotomic Field with Coxeter matrix:79[ 1 6 3]80[ 6 1 10]81[ 3 10 1]82sage: W.gens()83(84[ -1 -E(12)^7 + E(12)^11 1]85[ 0 1 0]86[ 0 0 1],87<BLANKLINE>88[ 1 0 0]89[-E(12)^7 + E(12)^11 -1 E(20) - E(20)^9]90[ 0 0 1],91<BLANKLINE>92[ 1 0 0]93[ 0 1 0]94[ 1 E(20) - E(20)^9 -1]95)96sage: m = matrix([[1,3,3,3], [3,1,3,2], [3,3,1,2], [3,2,2,1]])97sage: W = CoxeterGroup(m)98sage: W.gens()99(100[-1 1 1 1] [ 1 0 0 0] [ 1 0 0 0] [ 1 0 0 0]101[ 0 1 0 0] [ 1 -1 1 0] [ 0 1 0 0] [ 0 1 0 0]102[ 0 0 1 0] [ 0 0 1 0] [ 1 1 -1 0] [ 0 0 1 0]103[ 0 0 0 1], [ 0 0 0 1], [ 0 0 0 1], [ 1 0 0 -1]104)105sage: a,b,c,d = W.gens()106sage: (a*b*c)^3107[ 5 1 -5 7]108[ 5 0 -4 5]109[ 4 1 -4 4]110[ 0 0 0 1]111sage: (a*b)^3112[1 0 0 0]113[0 1 0 0]114[0 0 1 0]115[0 0 0 1]116sage: b*d == d*b117True118sage: a*c*a == c*a*c119True120121We can create the matrix representation over different base rings and with122different index sets. Note that the base ring must contain all123`2*\cos(\pi/m_{ij})` where `(m_{ij})_{ij}` is the Coxeter matrix::124125sage: W = CoxeterGroup(m, base_ring=RR, index_set=['a','b','c','d'])126sage: W.base_ring()127Real Field with 53 bits of precision128sage: W.index_set()129('a', 'b', 'c', 'd')130131sage: CoxeterGroup(m, base_ring=ZZ)132Coxeter group over Integer Ring with Coxeter matrix:133[1 3 3 3]134[3 1 3 2]135[3 3 1 2]136[3 2 2 1]137sage: CoxeterGroup([[1,4],[4,1]], base_ring=QQ)138Traceback (most recent call last):139...140TypeError: unable to convert sqrt(2) to a rational141142Using the well-known conversion between Coxeter matrices and Coxeter143graphs, we can input a Coxeter graph. Following the standard convention,144edges with no label (i.e. labelled by ``None``) are treated as 3::145146sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)])147sage: W = CoxeterGroup(G); W148Coxeter group over Universal Cyclotomic Field with Coxeter matrix:149[ 1 3 2 3]150[ 3 1 2 15]151[ 2 2 1 7]152[ 3 15 7 1]153sage: G2 = W.coxeter_graph()154sage: CoxeterGroup(G2) is W155True156157Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels158of `\infty` are given by `-1` in the Coxeter matrix::159160sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)])161sage: W = CoxeterGroup(G)162sage: W.coxeter_matrix()163[ 1 3 -1]164[ 3 1 4]165[-1 4 1]166167We can also create Coxeter groups from Cartan types using the168``implementation`` keyword::169170sage: W = CoxeterGroup(['D',5], implementation="reflection")171sage: W172Coxeter group over Universal Cyclotomic Field with Coxeter matrix:173[1 3 2 2 2]174[3 1 3 2 2]175[2 3 1 3 3]176[2 2 3 1 2]177[2 2 3 2 1]178sage: W = CoxeterGroup(['H',3], implementation="reflection")179sage: W180Coxeter group over Universal Cyclotomic Field with Coxeter matrix:181[1 3 2]182[3 1 5]183[2 5 1]184"""185@staticmethod186def __classcall_private__(cls, data, base_ring=None, index_set=None):187"""188Normalize arguments to ensure a unique representation.189190EXAMPLES::191192sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=UniversalCyclotomicField())193sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2))194sage: W1 is W2195True196sage: G1 = Graph([(1,2)])197sage: W3 = CoxeterGroup(G1)198sage: W1 is W3199True200sage: G2 = Graph([(1,2,3)])201sage: W4 = CoxeterGroup(G2)202sage: W1 is W4203True204205Check with `\infty` because of the hack of using `-1` to represent206`\infty` in the Coxeter matrix::207208sage: G = Graph([(0, 1, 3), (1, 2, oo)])209sage: W1 = CoxeterGroup(matrix([[1, 3, 2], [3,1,-1], [2,-1,1]]))210sage: W2 = CoxeterGroup(G)211sage: W1 is W2212True213sage: CoxeterGroup(W1.coxeter_graph()) is W1214True215"""216if isinstance(data, CartanType_abstract):217if index_set is None:218index_set = data.index_set()219data = data.coxeter_matrix()220elif isinstance(data, Graph):221G = data222n = G.num_verts()223224# Setup the basis matrix as all 2 except 1 on the diagonal225data = matrix(ZZ, [[2]*n]*n)226for i in range(n):227data[i, i] = ZZ.one()228229verts = G.vertices()230for e in G.edges():231m = e[2]232if m is None:233m = 3234elif m == infinity or m == -1: # FIXME: Hack because there is no ZZ\cup\{\infty\}235m = -1236elif m <= 1:237raise ValueError("invalid Coxeter graph label")238i = verts.index(e[0])239j = verts.index(e[1])240data[j, i] = data[i, j] = m241242if index_set is None:243index_set = G.vertices()244else:245try:246data = matrix(data)247except (ValueError, TypeError):248data = CartanType(data).coxeter_matrix()249if not data.is_symmetric():250raise ValueError("the Coxeter matrix is not symmetric")251if any(d != 1 for d in data.diagonal()):252raise ValueError("the Coxeter matrix diagonal is not all 1")253if any(val <= 1 and val != -1 for i, row in enumerate(data.rows())254for val in row[i+1:]):255raise ValueError("invalid Coxeter label")256257if index_set is None:258index_set = range(data.nrows())259260if base_ring is None:261base_ring = UniversalCyclotomicField()262data.set_immutable()263return super(CoxeterMatrixGroup, cls).__classcall__(cls,264data, base_ring, tuple(index_set))265266def __init__(self, coxeter_matrix, base_ring, index_set):267"""268Initialize ``self``.269270EXAMPLES::271272sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])273sage: TestSuite(W).run() # long time274sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar)275sage: TestSuite(W).run() # long time276sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]])277sage: TestSuite(W).run(max_runs=30) # long time278sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]])279sage: TestSuite(W).run(max_runs=30) # long time280"""281self._matrix = coxeter_matrix282self._index_set = index_set283n = ZZ(coxeter_matrix.nrows())284MS = MatrixSpace(base_ring, n, sparse=True)285# FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty286if base_ring is UniversalCyclotomicField():287val = lambda x: base_ring.gen(2*x) + ~base_ring.gen(2*x) if x != -1 else base_ring(2)288else:289from sage.functions.trig import cos290from sage.symbolic.constants import pi291val = lambda x: base_ring(2*cos(pi / x)) if x != -1 else base_ring(2)292gens = [MS.one() + MS({(i, j): val(coxeter_matrix[i, j])293for j in range(n)})294for i in range(n)]295FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring,296gens,297category=CoxeterGroups())298299def _repr_(self):300"""301Return a string representation of ``self``.302303EXAMPLES::304305sage: CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])306Coxeter group over Universal Cyclotomic Field with Coxeter matrix:307[1 3 2]308[3 1 3]309[2 3 1]310"""311return "Coxeter group over {} with Coxeter matrix:\n{}".format(self.base_ring(), self._matrix)312313def index_set(self):314"""315Return the index set of ``self``.316317EXAMPLES::318319sage: W = CoxeterGroup([[1,3],[3,1]])320sage: W.index_set()321(0, 1)322sage: W = CoxeterGroup([[1,3],[3,1]], index_set=['x', 'y'])323sage: W.index_set()324('x', 'y')325sage: W = CoxeterGroup(['H',3])326sage: W.index_set()327(1, 2, 3)328"""329return self._index_set330331def coxeter_matrix(self):332"""333Return the Coxeter matrix of ``self``.334335EXAMPLES::336337sage: W = CoxeterGroup([[1,3],[3,1]])338sage: W.coxeter_matrix()339[1 3]340[3 1]341sage: W = CoxeterGroup(['H',3])342sage: W.coxeter_matrix()343[1 3 2]344[3 1 5]345[2 5 1]346"""347return self._matrix348349def coxeter_graph(self):350"""351Return the Coxeter graph of ``self``.352353EXAMPLES::354355sage: W = CoxeterGroup(['H',3], implementation="reflection")356sage: G = W.coxeter_graph(); G357Graph on 3 vertices358sage: G.edges()359[(1, 2, None), (2, 3, 5)]360sage: CoxeterGroup(G) is W361True362sage: G = Graph([(0, 1, 3), (1, 2, oo)])363sage: W = CoxeterGroup(G)364sage: W.coxeter_graph() == G365True366sage: CoxeterGroup(W.coxeter_graph()) is W367True368"""369G = Graph()370G.add_vertices(self.index_set())371for i, row in enumerate(self._matrix.rows()):372for j, val in enumerate(row[i+1:]):373if val == 3:374G.add_edge(self._index_set[i], self._index_set[i+1+j])375elif val > 3:376G.add_edge(self._index_set[i], self._index_set[i+1+j], val)377elif val == -1: # FIXME: Hack because there is no ZZ\cup\{\infty\}378G.add_edge(self._index_set[i], self._index_set[i+1+j], infinity)379return G380381def simple_reflection(self, i):382"""383Return the simple reflection `s_i`.384385INPUT:386387- ``i`` -- an element from the index set388389EXAMPLES::390391sage: W = CoxeterGroup(['A',3], implementation="reflection")392sage: W.simple_reflection(1)393[-1 1 0]394[ 0 1 0]395[ 0 0 1]396sage: W.simple_reflection(2)397[ 1 0 0]398[ 1 -1 1]399[ 0 0 1]400sage: W.simple_reflection(3)401[ 1 0 0]402[ 0 1 0]403[ 0 1 -1]404"""405if not i in self._index_set:406raise ValueError("%s is not in the index set %s" % (i, self.index_set()))407return self.gen(self._index_set.index(i))408409class Element(MatrixGroupElement_generic):410"""411A Coxeter group element.412"""413def has_right_descent(self, i):414r"""415Return whether ``i`` is a right descent of ``self``.416417A Coxeter system `(W, S)` has a root system defined as418`\{ w(\alpha_s) \}_{w \in W}` and we define the positive419(resp. negative) roots `\alpha = \sum_{s \in S} c_s \alpha_s`420by all `c_s \geq 0` (resp.`c_s \leq 0`). In particular, we note421that if `\ell(w s) > \ell(w)` then `w(\alpha_s) > 0` and if422`\ell(ws) < \ell(w)` then `w(\alpha_s) < 0`.423Thus `i \in I` is a right descent if `w(\alpha_{s_i}) < 0`424or equivalently if the matrix representing `w` has all entries425of the `i`-th column being non-positive.426427INPUT:428429- ``i`` -- an element in the index set430431EXAMPLES::432433sage: W = CoxeterGroup(['A',3], implementation="reflection")434sage: a,b,c = W.gens()435sage: elt = b*a*c436sage: map(lambda i: elt.has_right_descent(i), [1, 2, 3])437[True, False, True]438"""439i = self.parent()._index_set.index(i)440col = self.matrix().column(i)441return all(x <= 0 for x in col)442443444445