def __init__(self, parent, data, coxeter_type, index_set): """ Initialize ``self``. TESTS:: sage: C = CoxeterMatrix(['A', 2, 1]) sage: TestSuite(C).run(skip=["_test_category", "_test_change_ring"]) """ self._matrix = Matrix_generic_dense(parent, data, False, True) self._matrix.set_immutable() if self._matrix.base_ring() not in [ZZ, QQ]: self._is_cyclotomic = False else: self._is_cyclotomic = True self._coxeter_type = coxeter_type if self._coxeter_type is not None: if self._coxeter_type.is_finite(): self._is_finite = True self._is_affine = False elif self._coxeter_type.is_affine(): self._is_finite = False self._is_affine = True else: self._is_finite = False self._is_affine = False else: self._is_finite = False self._is_affine = False self._index_set = index_set self._rank = self._matrix.nrows() self._dict = {(self._index_set[i], self._index_set[j]): self._matrix[i, j] for i in range(self._rank) for j in range(self._rank)} for i, key in enumerate(self._index_set): self._dict[key] = { key2: self._matrix[i, j] for j, key2 in enumerate(self._index_set) }
def __init__(self, parent, data, coxeter_type, index_set): """ Initialize ``self``. TESTS:: sage: C = CoxeterMatrix(['A', 2, 1]) sage: TestSuite(C).run(skip=["_test_category", "_test_change_ring"]) """ self._matrix = Matrix_generic_dense(parent, data, False, True) self._matrix.set_immutable() if self._matrix.base_ring() not in [ZZ, QQ]: self._is_cyclotomic = False else: self._is_cyclotomic = True self._coxeter_type = coxeter_type if self._coxeter_type is not None: if self._coxeter_type.is_finite(): self._is_finite = True self._is_affine = False elif self._coxeter_type.is_affine(): self._is_finite = False self._is_affine = True else: self._is_finite = False self._is_affine = False else: self._is_finite = False self._is_affine = False self._index_set = index_set self._rank = self._matrix.nrows() self._dict = { (self._index_set[i], self._index_set[j]): self._matrix[i, j] for i in range(self._rank) for j in range(self._rank) } for i, key in enumerate(self._index_set): self._dict[key] = {key2: self._matrix[i, j] for j, key2 in enumerate(self._index_set)}
class CoxeterMatrix(CoxeterType): r""" A Coxeter matrix. A Coxeter matrix `M = (m_{ij})_{i,j \in I}` is a matrix encoding a Coxeter system `(W, S)`, where the relations are given by `(s_i s_j)^{m_{ij}}`. Thus `M` is symmetric and has entries in `\{1, 2, 3, \ldots, \infty\}` with `m_{ij} = 1` if and only if `i = j`. We represent `m_{ij} = \infty` by any number `m_{ij} \leq -1`. In particular, we can construct a bilinear form `B = (b_{ij})_{i,j \in I}` from `M` by .. MATH:: b_{ij} = \begin{cases} m_{ij} & m_{ij} < 0\ (\text{i.e., } m_{ij} = \infty), \\ -\cos\left( \frac{\pi}{m_{ij}} \right) & \text{otherwise}. \end{cases} EXAMPLES:: sage: CoxeterMatrix(['A', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 3] [2 2 3 1] sage: CoxeterMatrix(['B', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: CoxeterMatrix(['C', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: CoxeterMatrix(['D', 4]) [1 3 2 2] [3 1 3 3] [2 3 1 2] [2 3 2 1] sage: CoxeterMatrix(['E', 6]) [1 2 3 2 2 2] [2 1 2 3 2 2] [3 2 1 3 2 2] [2 3 3 1 3 2] [2 2 2 3 1 3] [2 2 2 2 3 1] sage: CoxeterMatrix(['F', 4]) [1 3 2 2] [3 1 4 2] [2 4 1 3] [2 2 3 1] sage: CoxeterMatrix(['G', 2]) [1 6] [6 1] By default, entries representing `\infty` are given by `-1` in the Coxeter matrix:: sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)]) sage: CoxeterMatrix(G) [ 1 3 -1] [ 3 1 4] [-1 4 1] It is possible to give a number `\leq -1` to represent an infinite label:: sage: CoxeterMatrix([[1,-1],[-1,1]]) [ 1 -1] [-1 1] sage: CoxeterMatrix([[1,-3/2],[-3/2,1]]) [ 1 -3/2] [-3/2 1] """ __metaclass__ = ClasscallMetaclass @staticmethod def __classcall_private__(cls, data=None, index_set=None, coxeter_type=None, cartan_type=None, coxeter_type_check=True): r""" A Coxeter matrix can we created via a graph, a Coxeter type, or a matrix. .. NOTE:: To disable the Coxeter type check, use the optional argument ``coxeter_type_check = False``. EXAMPLES:: sage: C = CoxeterMatrix(['A',1,1],['a','b']) sage: C2 = CoxeterMatrix([[1, -1], [-1, 1]]) sage: C3 = CoxeterMatrix(matrix([[1, -1], [-1, 1]]), [0, 1]) sage: C == C2 and C == C3 True Check with `\infty` because of the hack of using `-1` to represent `\infty` in the Coxeter matrix:: sage: G = Graph([(0, 1, 3), (1, 2, oo)]) sage: W1 = CoxeterMatrix([[1, 3, 2], [3, 1, -1], [2, -1, 1]]) sage: W2 = CoxeterMatrix(G) sage: W1 == W2 True sage: CoxeterMatrix(W1.coxeter_graph()) == W1 True The base ring of the matrix depends on the entries given:: sage: CoxeterMatrix([[1,-1],[-1,1]])._matrix.base_ring() Integer Ring sage: CoxeterMatrix([[1,-3/2],[-3/2,1]])._matrix.base_ring() Rational Field sage: CoxeterMatrix([[1,-1.5],[-1.5,1]])._matrix.base_ring() Real Field with 53 bits of precision """ if not data: if coxeter_type: data = CoxeterType(coxeter_type) elif cartan_type: data = CoxeterType(CartanType(cartan_type)) # Special cases with no arguments passed if not data: data = [] n = 0 index_set = tuple() coxeter_type = None base_ring = ZZ mat = typecall(cls, MatrixSpace(base_ring, n, sparse=False), data, coxeter_type, index_set) mat._subdivisions = None return mat if isinstance(data, CoxeterMatrix): # Initiate from itself return data # Initiate from a graph: # TODO: Check if a CoxeterDiagram once implemented if isinstance(data, Graph): return cls._from_graph(data, coxeter_type_check) # Get the Coxeter type coxeter_type = None from sage.combinat.root_system.cartan_type import CartanType_abstract if isinstance(data, CartanType_abstract): coxeter_type = data.coxeter_type() else: try: coxeter_type = CoxeterType(data) except (TypeError, ValueError, NotImplementedError): pass # Initiate from a Coxeter type if coxeter_type: return cls._from_coxetertype(coxeter_type) # TODO:: remove when oo is possible in matrices. n = len(data[0]) data = [x if x != infinity else -1 for r in data for x in r] data = matrix(n, n, data) # until here # Get the index set if index_set: index_set = tuple(index_set) else: index_set = tuple(range(1, n + 1)) if len(set(index_set)) != n: raise ValueError("the given index set is not valid") return cls._from_matrix(data, coxeter_type, index_set, coxeter_type_check) def __init__(self, parent, data, coxeter_type, index_set): """ Initialize ``self``. TESTS:: sage: C = CoxeterMatrix(['A', 2, 1]) sage: TestSuite(C).run(skip=["_test_category", "_test_change_ring"]) """ self._matrix = Matrix_generic_dense(parent, data, False, True) self._matrix.set_immutable() if self._matrix.base_ring() not in [ZZ, QQ]: self._is_cyclotomic = False else: self._is_cyclotomic = True self._coxeter_type = coxeter_type if self._coxeter_type is not None: if self._coxeter_type.is_finite(): self._is_finite = True self._is_affine = False elif self._coxeter_type.is_affine(): self._is_finite = False self._is_affine = True else: self._is_finite = False self._is_affine = False else: self._is_finite = False self._is_affine = False self._index_set = index_set self._rank = self._matrix.nrows() self._dict = {(self._index_set[i], self._index_set[j]): self._matrix[i, j] for i in range(self._rank) for j in range(self._rank)} for i, key in enumerate(self._index_set): self._dict[key] = { key2: self._matrix[i, j] for j, key2 in enumerate(self._index_set) } @classmethod def _from_matrix(cls, data, coxeter_type, index_set, coxeter_type_check): """ Initiate the Coxeter matrix from a matrix. TESTS:: sage: CM = CoxeterMatrix([[1,2],[2,1]]); CM [1 2] [2 1] sage: CM = CoxeterMatrix([[1,-1],[-1,1]]); CM [ 1 -1] [-1 1] sage: CM = CoxeterMatrix([[1,-1.5],[-1.5,1]]); CM [ 1.00000000000000 -1.50000000000000] [-1.50000000000000 1.00000000000000] sage: CM = CoxeterMatrix([[1,-3/2],[-3/2,1]]); CM [ 1 -3/2] [-3/2 1] sage: CM = CoxeterMatrix([[1,-3/2,5],[-3/2,1,-1],[5,-1,1]]); CM [ 1 -3/2 5] [-3/2 1 -1] [ 5 -1 1] sage: CM = CoxeterMatrix([[1,-3/2,5],[-3/2,1,oo],[5,oo,1]]); CM [ 1 -3/2 5] [-3/2 1 -1] [ 5 -1 1] """ # Check that the data is valid check_coxeter_matrix(data) M = matrix(data) n = M.ncols() base_ring = M.base_ring() if not coxeter_type: if n == 1: coxeter_type = CoxeterType(['A', 1]) elif coxeter_type_check: coxeter_type = recognize_coxeter_type_from_matrix(M, index_set) else: coxeter_type = None raw_data = M.list() mat = typecall(cls, MatrixSpace(base_ring, n, sparse=False), raw_data, coxeter_type, index_set) mat._subdivisions = M._subdivisions return mat @classmethod def _from_graph(cls, graph, coxeter_type_check): """ Initiate the Coxeter matrix from a graph. TESTS:: sage: CoxeterMatrix(CoxeterMatrix(['A',4,1]).coxeter_graph()) [1 3 2 2 3] [3 1 3 2 2] [2 3 1 3 2] [2 2 3 1 3] [3 2 2 3 1] sage: CoxeterMatrix(CoxeterMatrix(['B',4,1]).coxeter_graph()) [1 2 3 2 2] [2 1 3 2 2] [3 3 1 3 2] [2 2 3 1 4] [2 2 2 4 1] sage: CoxeterMatrix(CoxeterMatrix(['F',4]).coxeter_graph()) [1 3 2 2] [3 1 4 2] [2 4 1 3] [2 2 3 1] sage: G=Graph() sage: G.add_edge([0,1,oo]) sage: CoxeterMatrix(G) [ 1 -1] [-1 1] sage: H = Graph() sage: H.add_edge([0,1,-1.5]) sage: CoxeterMatrix(H) [ 1.00000000000000 -1.50000000000000] [-1.50000000000000 1.00000000000000] """ verts = sorted(graph.vertices()) index_set = tuple(verts) n = len(index_set) # Setup the basis matrix as all 2 except 1 on the diagonal data = [] for i in range(n): data += [[]] for j in range(n): if i == j: data[-1] += [ZZ.one()] else: data[-1] += [2] for e in graph.edges(): label = e[2] if label is None: label = 3 elif label == infinity: label = -1 elif label not in ZZ and label > -1: raise ValueError("invalid Coxeter graph label") elif label == 0 or label == 1: raise ValueError("invalid Coxeter graph label") i = verts.index(e[0]) j = verts.index(e[1]) data[j][i] = data[i][j] = label return cls._from_matrix(data, None, index_set, coxeter_type_check) @classmethod def _from_coxetertype(cls, coxeter_type): """ Initiate the Coxeter matrix from a Coxeter type. TESTS:: sage: CoxeterMatrix(['A',4]).coxeter_type() Coxeter type of ['A', 4] sage: CoxeterMatrix(['A',4,1]).coxeter_type() Coxeter type of ['A', 4, 1] sage: CoxeterMatrix(['D',4,1]).coxeter_type() Coxeter type of ['D', 4, 1] """ index_set = coxeter_type.index_set() n = len(index_set) reverse = {index_set[i]: i for i in range(n)} data = [[1 if i == j else 2 for j in range(n)] for i in range(n)] for (i, j, l) in coxeter_type.coxeter_graph().edge_iterator(): if l == infinity: l = -1 data[reverse[i]][reverse[j]] = l data[reverse[j]][reverse[i]] = l return cls._from_matrix(data, coxeter_type, index_set, False) @classmethod def samples(self, finite=None, affine=None, crystallographic=None, higher_rank=None): """ Return a sample of the available Coxeter types. INPUT: - ``finite`` -- (default: ``None``) a boolean or ``None`` - ``affine`` -- (default: ``None``) a boolean or ``None`` - ``crystallographic`` -- (default: ``None``) a boolean or ``None`` - ``higher_rank`` -- (default: ``None``) a boolean or ``None`` The sample contains all the exceptional finite and affine Coxeter types, as well as typical representatives of the infinite families. Here the ``higher_rank`` term denotes non-finite, non-affine, Coxeter groups (including hyperbolic types). .. TODO:: Implement the hyperbolic and compact hyperbolic in the samples. EXAMPLES:: sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples()] [ Coxeter type of ['A', 1], Coxeter type of ['A', 5], <BLANKLINE> Coxeter type of ['B', 5], Coxeter type of ['D', 4], <BLANKLINE> Coxeter type of ['D', 5], Coxeter type of ['E', 6], <BLANKLINE> Coxeter type of ['E', 7], Coxeter type of ['E', 8], <BLANKLINE> Coxeter type of ['F', 4], Coxeter type of ['H', 3], <BLANKLINE> Coxeter type of ['H', 4], Coxeter type of ['I', 10], <BLANKLINE> Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], <BLANKLINE> Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], <BLANKLINE> Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], <BLANKLINE> Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], <BLANKLINE> [ 1 -1 -1] [-1 1 -1] Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1], [-1 -1 1], <BLANKLINE> [ 1 -2 3 2] [1 2 3] [-2 1 2 3] [2 1 7] [ 3 2 1 -8] [3 7 1], [ 2 3 -8 1] ] The finite, affine and crystallographic options allow respectively for restricting to (non) finite, (non) affine, and (non) crystallographic Cartan types:: sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(finite=True)] [Coxeter type of ['A', 1], Coxeter type of ['A', 5], Coxeter type of ['B', 5], Coxeter type of ['D', 4], Coxeter type of ['D', 5], Coxeter type of ['E', 6], Coxeter type of ['E', 7], Coxeter type of ['E', 8], Coxeter type of ['F', 4], Coxeter type of ['H', 3], Coxeter type of ['H', 4], Coxeter type of ['I', 10]] sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(affine=True)] [Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1]] sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(crystallographic=True)] [Coxeter type of ['A', 1], Coxeter type of ['A', 5], Coxeter type of ['B', 5], Coxeter type of ['D', 4], Coxeter type of ['D', 5], Coxeter type of ['E', 6], Coxeter type of ['E', 7], Coxeter type of ['E', 8], Coxeter type of ['F', 4], Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1]] sage: CoxeterMatrix.samples(crystallographic=False) [ [1 3 2 2] [1 3 2] [3 1 3 2] [ 1 -1 -1] [1 2 3] [3 1 5] [2 3 1 5] [ 1 10] [ 1 -1] [-1 1 -1] [2 1 7] [2 5 1], [2 2 5 1], [10 1], [-1 1], [-1 -1 1], [3 7 1], <BLANKLINE> [ 1 -2 3 2] [-2 1 2 3] [ 3 2 1 -8] [ 2 3 -8 1] ] .. TODO:: add some reducible Coxeter types (suggestions?) TESTS:: sage: for ct in CoxeterMatrix.samples(): TestSuite(ct).run() """ result = self._samples() if crystallographic is not None: result = [ t for t in result if t.is_crystallographic() == crystallographic ] if finite is not None: result = [t for t in result if t.is_finite() == finite] if affine is not None: result = [t for t in result if t.is_affine() == affine] if higher_rank is not None: result = [ t for t in result if not t.is_affine() and not t.is_finite() ] return result @cached_method def _samples(self): """ Return a sample of all implemented Coxeter types. .. NOTE:: This is intended to be used through :meth:`samples`. EXAMPLES:: sage: [CM.coxeter_type() for CM in CoxeterMatrix._samples()] [ Coxeter type of ['A', 1], Coxeter type of ['A', 5], <BLANKLINE> Coxeter type of ['B', 5], Coxeter type of ['D', 4], <BLANKLINE> Coxeter type of ['D', 5], Coxeter type of ['E', 6], <BLANKLINE> Coxeter type of ['E', 7], Coxeter type of ['E', 8], <BLANKLINE> Coxeter type of ['F', 4], Coxeter type of ['H', 3], <BLANKLINE> Coxeter type of ['H', 4], Coxeter type of ['I', 10], <BLANKLINE> Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], <BLANKLINE> Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], <BLANKLINE> Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], <BLANKLINE> Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], <BLANKLINE> [ 1 -1 -1] [-1 1 -1] Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1], [-1 -1 1], <BLANKLINE> [ 1 -2 3 2] [1 2 3] [-2 1 2 3] [2 1 7] [ 3 2 1 -8] [3 7 1], [ 2 3 -8 1] ] """ finite = [ CoxeterMatrix(t) for t in [['A', 1], ['A', 5], ['B', 5], ['D', 4], ['D', 5], ['E', 6], ['E', 7], ['E', 8], ['F', 4], ['H', 3], ['H', 4], ['I', 10]] ] affine = [ CoxeterMatrix(t) for t in [['A', 2, 1], ['B', 5, 1], ['C', 5, 1], ['D', 5, 1], ['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['A', 1, 1]] ] higher_matrices = [[[1, -1, -1], [-1, 1, -1], [-1, -1, 1]], [[1, 2, 3], [2, 1, 7], [3, 7, 1]], [[1, -2, 3, 2], [-2, 1, 2, 3], [3, 2, 1, -8], [2, 3, -8, 1]]] higher = [CoxeterMatrix(m) for m in higher_matrices] return finite + affine + higher def relabel(self, relabelling): """ Return a relabelled copy of this Coxeter matrix. INPUT: - ``relabelling`` -- a function (or dictionary) OUTPUT: an isomorphic Coxeter type obtained by relabelling the nodes of the Coxeter graph. Namely, the node with label ``i`` is relabelled ``f(i)`` (or, by ``f[i]`` if ``f`` is a dictionary). EXAMPLES:: sage: CoxeterMatrix(['F',4]).relabel({ 1:2, 2:3, 3:4, 4:1}) [1 4 2 3] [4 1 3 2] [2 3 1 2] [3 2 2 1] sage: CoxeterMatrix(['F',4]).relabel(lambda x: x+1 if x<4 else 1) [1 4 2 3] [4 1 3 2] [2 3 1 2] [3 2 2 1] """ if isinstance(relabelling, dict): data = [[ self[relabelling[i]][relabelling[j]] for j in self.index_set() ] for i in self.index_set()] else: data = [[ self[relabelling(i)][relabelling(j)] for j in self.index_set() ] for i in self.index_set()] return CoxeterMatrix(data) def __reduce__(self): """ Used for pickling. TESTS:: sage: C = CoxeterMatrix(['A',4]) sage: M = loads(dumps(C)) sage: M._index_set (1, 2, 3, 4) """ if self._coxeter_type: return (CoxeterMatrix, (self._coxeter_type, )) return (CoxeterMatrix, (self._matrix, self._index_set)) def _repr_(self): """ String representation of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]); CM [1 3 2] [3 1 3] [2 3 1] sage: CM = CoxeterMatrix([[1,-3/2],[-3/2,1]]); CM [ 1 -3/2] [-3/2 1] """ return repr(self._matrix) def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]) sage: CM._repr_option('ascii_art') True """ if key == 'ascii_art' or key == 'element_ascii_art': return self._matrix.nrows() > 1 return super(CoxeterMatrix, self)._repr_option(key) def _latex_(self): r""" Latex representation of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]) sage: latex(CM) \left(\begin{array}{rrr} 1 & 3 & 2 \\ 3 & 1 & 3 \\ 2 & 3 & 1 \end{array}\right) """ return self._matrix._latex_() def __iter__(self): """ Return an iterator for the rows of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix([[1,8],[8,1]]) sage: next(CM.__iter__()) (1, 8) """ return iter(self._matrix) def __getitem__(self, key): """ Return a dictionary of labels adjacent to a node or the label of an edge in the Coxeter graph. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]]) sage: CM = CoxeterMatrix([[1,-2],[-2,1]], ['a','b']) sage: CM['a'] {'a': 1, 'b': -2} sage: CM['b'] {'a': -2, 'b': 1} sage: CM['a','b'] -2 sage: CM['a','a'] 1 """ return self._dict[key] def __hash__(self): r""" Return hash of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM.__hash__() 1 sage: CM = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM.__hash__() 4 """ return hash(self._matrix) def __eq__(self, other): r""" Return if ``self`` and ``other`` are equal. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM2 = CoxeterMatrix([[1,-2],[-2,1]],['1','2']) sage: CM == CM2 True sage: CM == matrix(CM) False sage: CM3 = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM == CM3 False """ return isinstance(other, CoxeterMatrix) and self._matrix == other._matrix def __ne__(self, other): """ Return if ``self`` and ``other`` are not equal. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM2 = CoxeterMatrix([[1,-2],[-2,1]],['1','2']) sage: CM != CM2 False sage: matrix(CM) != CM True sage: CM3 = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM != CM3 True """ return not (self == other) def _matrix_(self, R=None): """ Return ``self`` as a matrix over the ring ``R``. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-3],[-3,1]]) sage: matrix(CM) [ 1 -3] [-3 1] sage: matrix(RR, CM) [ 1.00000000000000 -3.00000000000000] [-3.00000000000000 1.00000000000000] """ if R is not None: return self._matrix.change_ring(R) else: return self._matrix ########################################################################## # Coxeter type methods def index_set(self): """ Return the index set of ``self``. EXAMPLES:: sage: C = CoxeterMatrix(['A',1,1]) sage: C.index_set() (0, 1) sage: C = CoxeterMatrix(['E',6]) sage: C.index_set() (1, 2, 3, 4, 5, 6) """ return self._index_set def coxeter_type(self): """ Return the Coxeter type of ``self`` or ``self`` if unknown. EXAMPLES:: sage: C = CoxeterMatrix(['A',4,1]) sage: C.coxeter_type() Coxeter type of ['A', 4, 1] If the Coxeter type is unknown:: sage: C = CoxeterMatrix([[1,3,4], [3,1,-1], [4,-1,1]]) sage: C.coxeter_type() [ 1 3 4] [ 3 1 -1] [ 4 -1 1] """ if self._coxeter_type is None: return self return self._coxeter_type def rank(self): r""" Return the rank of ``self``. EXAMPLES:: sage: CoxeterMatrix(['C',3]).rank() 3 sage: CoxeterMatrix(["A2","B2","F4"]).rank() 8 """ return self._rank def coxeter_matrix(self): r""" Return the Coxeter matrix of ``self``. EXAMPLES:: sage: CoxeterMatrix(['C',3]).coxeter_matrix() [1 3 2] [3 1 4] [2 4 1] """ return self def bilinear_form(self, R=None): r""" Return the bilinear form of ``self``. EXAMPLES:: sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] [-1/2 -1/2 1] sage: CoxeterType(['H', 3]).bilinear_form() [ 1 -1/2 0] [ -1/2 1 1/2*E(5)^2 + 1/2*E(5)^3] [ 0 1/2*E(5)^2 + 1/2*E(5)^3 1] sage: C = CoxeterMatrix([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) sage: C.bilinear_form() [ 1 -1 -1] [-1 1 -1] [-1 -1 1] """ return CoxeterType.bilinear_form(self, R=R) @cached_method def coxeter_graph(self): """ Return the Coxeter graph of ``self``. EXAMPLES:: sage: C = CoxeterMatrix(['A',3]) sage: C.coxeter_graph() Graph on 3 vertices sage: C = CoxeterMatrix([['A',3],['A',1]]) sage: C.coxeter_graph() Graph on 4 vertices """ n = self.rank() I = self.index_set() val = lambda x: infinity if x == -1 else x G = Graph([(I[i], I[j], val((self._matrix)[i, j])) for i in range(n) for j in range(i) if self._matrix[i, j] not in [1, 2]]) G.add_vertices(I) return G.copy(immutable=True) def is_simply_laced(self): """ Return if ``self`` is simply-laced. A Coxeter matrix is simply-laced if all non-diagonal entries are either 2 or 3. EXAMPLES:: sage: cm = CoxeterMatrix([[1,3,3,3], [3,1,3,3], [3,3,1,3], [3,3,3,1]]) sage: cm.is_simply_laced() True """ # We include 1 in this list to account for the diagonal L = [1, 2, 3] return all(x in L for row in self for x in row) def is_crystallographic(self): """ Return if ``self`` is crystallographic. A Coxeter matrix is crystallographic if all non-diagonal entries are either 2, 4, or 6. EXAMPLES:: sage: CoxeterMatrix(['F',4]).is_crystallographic() True sage: CoxeterMatrix(['H',3]).is_crystallographic() False """ # We include 1 in this list to account for the diagonal L = [1, 2, 3, 4, 6] return all(x in L for row in self for x in row) def is_finite(self): """ Return if ``self`` is a finite type or ``False`` if unknown. EXAMPLES:: sage: M = CoxeterMatrix(['C',4]) sage: M.is_finite() True sage: M = CoxeterMatrix(['D',4,1]) sage: M.is_finite() False sage: M = CoxeterMatrix([[1, -1], [-1, 1]]) sage: M.is_finite() False """ return self._is_finite def is_affine(self): """ Return if ``self`` is an affine type or ``False`` if unknown. EXAMPLES:: sage: M = CoxeterMatrix(['C',4]) sage: M.is_affine() False sage: M = CoxeterMatrix(['D',4,1]) sage: M.is_affine() True sage: M = CoxeterMatrix([[1, 3],[3,1]]) sage: M.is_affine() False sage: M = CoxeterMatrix([[1, -1, 7], [-1, 1, 3], [7, 3, 1]]) sage: M.is_affine() False """ return self._is_affine
class CoxeterMatrix(CoxeterType): r""" A Coxeter matrix. A Coxeter matrix `M = (m_{ij})_{i,j \in I}` is a matrix encoding a Coxeter system `(W, S)`, where the relations are given by `(s_i s_j)^{m_{ij}}`. Thus `M` is symmetric and has entries in `\{1, 2, 3, \ldots, \infty\}` with `m_{ij} = 1` if and only if `i = j`. We represent `m_{ij} = \infty` by any number `m_{ij} \leq -1`. In particular, we can construct a bilinear form `B = (b_{ij})_{i,j \in I}` from `M` by .. MATH:: b_{ij} = \begin{cases} m_{ij} & m_{ij} < 0\ (\text{i.e., } m_{ij} = \infty), \\ -\cos\left( \frac{\pi}{m_{ij}} \right) & \text{otherwise}. \end{cases} EXAMPLES:: sage: CoxeterMatrix(['A', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 3] [2 2 3 1] sage: CoxeterMatrix(['B', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: CoxeterMatrix(['C', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: CoxeterMatrix(['D', 4]) [1 3 2 2] [3 1 3 3] [2 3 1 2] [2 3 2 1] sage: CoxeterMatrix(['E', 6]) [1 2 3 2 2 2] [2 1 2 3 2 2] [3 2 1 3 2 2] [2 3 3 1 3 2] [2 2 2 3 1 3] [2 2 2 2 3 1] sage: CoxeterMatrix(['F', 4]) [1 3 2 2] [3 1 4 2] [2 4 1 3] [2 2 3 1] sage: CoxeterMatrix(['G', 2]) [1 6] [6 1] By default, entries representing `\infty` are given by `-1` in the Coxeter matrix:: sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)]) sage: CoxeterMatrix(G) [ 1 3 -1] [ 3 1 4] [-1 4 1] It is possible to give a number `\leq -1` to represent an infinite label:: sage: CoxeterMatrix([[1,-1],[-1,1]]) [ 1 -1] [-1 1] sage: CoxeterMatrix([[1,-3/2],[-3/2,1]]) [ 1 -3/2] [-3/2 1] """ __metaclass__ = ClasscallMetaclass @staticmethod def __classcall_private__( cls, data=None, index_set=None, coxeter_type=None, cartan_type=None, coxeter_type_check=True ): r""" A Coxeter matrix can we created via a graph, a Coxeter type, or a matrix. .. NOTE:: To disable the Coxeter type check, use the optional argument ``coxeter_type_check = False``. EXAMPLES:: sage: C = CoxeterMatrix(['A',1,1],['a','b']) sage: C2 = CoxeterMatrix([[1, -1], [-1, 1]]) sage: C3 = CoxeterMatrix(matrix([[1, -1], [-1, 1]]), [0, 1]) sage: C == C2 and C == C3 True Check with `\infty` because of the hack of using `-1` to represent `\infty` in the Coxeter matrix:: sage: G = Graph([(0, 1, 3), (1, 2, oo)]) sage: W1 = CoxeterMatrix([[1, 3, 2], [3, 1, -1], [2, -1, 1]]) sage: W2 = CoxeterMatrix(G) sage: W1 == W2 True sage: CoxeterMatrix(W1.coxeter_graph()) == W1 True The base ring of the matrix depends on the entries given:: sage: CoxeterMatrix([[1,-1],[-1,1]])._matrix.base_ring() Integer Ring sage: CoxeterMatrix([[1,-3/2],[-3/2,1]])._matrix.base_ring() Rational Field sage: CoxeterMatrix([[1,-1.5],[-1.5,1]])._matrix.base_ring() Real Field with 53 bits of precision """ if not data: if coxeter_type: data = CoxeterType(coxeter_type) elif cartan_type: data = CoxeterType(CartanType(cartan_type)) # Special cases with no arguments passed if not data: data = [] n = 0 index_set = tuple() coxeter_type = None base_ring = ZZ mat = typecall(cls, MatrixSpace(base_ring, n, sparse=False), data, coxeter_type, index_set) mat._subdivisions = None return mat if isinstance(data, CoxeterMatrix): # Initiate from itself return data # Initiate from a graph: # TODO: Check if a CoxeterDiagram once implemented if isinstance(data, Graph): return cls._from_graph(data, coxeter_type_check) # Get the Coxeter type coxeter_type = None from sage.combinat.root_system.cartan_type import CartanType_abstract if isinstance(data, CartanType_abstract): coxeter_type = data.coxeter_type() else: try: coxeter_type = CoxeterType(data) except (TypeError, ValueError, NotImplementedError): pass # Initiate from a Coxeter type if coxeter_type: return cls._from_coxetertype(coxeter_type) # TODO:: remove when oo is possible in matrices. n = len(data[0]) data = [x if x != infinity else -1 for r in data for x in r] data = matrix(n, n, data) # until here # Get the index set if index_set: index_set = tuple(index_set) else: index_set = tuple(range(1, n + 1)) if len(set(index_set)) != n: raise ValueError("the given index set is not valid") return cls._from_matrix(data, coxeter_type, index_set, coxeter_type_check) def __init__(self, parent, data, coxeter_type, index_set): """ Initialize ``self``. TESTS:: sage: C = CoxeterMatrix(['A', 2, 1]) sage: TestSuite(C).run(skip=["_test_category", "_test_change_ring"]) """ self._matrix = Matrix_generic_dense(parent, data, False, True) self._matrix.set_immutable() if self._matrix.base_ring() not in [ZZ, QQ]: self._is_cyclotomic = False else: self._is_cyclotomic = True self._coxeter_type = coxeter_type if self._coxeter_type is not None: if self._coxeter_type.is_finite(): self._is_finite = True self._is_affine = False elif self._coxeter_type.is_affine(): self._is_finite = False self._is_affine = True else: self._is_finite = False self._is_affine = False else: self._is_finite = False self._is_affine = False self._index_set = index_set self._rank = self._matrix.nrows() self._dict = { (self._index_set[i], self._index_set[j]): self._matrix[i, j] for i in range(self._rank) for j in range(self._rank) } for i, key in enumerate(self._index_set): self._dict[key] = {key2: self._matrix[i, j] for j, key2 in enumerate(self._index_set)} @classmethod def _from_matrix(cls, data, coxeter_type, index_set, coxeter_type_check): """ Initiate the Coxeter matrix from a matrix. TESTS:: sage: CM = CoxeterMatrix([[1,2],[2,1]]); CM [1 2] [2 1] sage: CM = CoxeterMatrix([[1,-1],[-1,1]]); CM [ 1 -1] [-1 1] sage: CM = CoxeterMatrix([[1,-1.5],[-1.5,1]]); CM [ 1.00000000000000 -1.50000000000000] [-1.50000000000000 1.00000000000000] sage: CM = CoxeterMatrix([[1,-3/2],[-3/2,1]]); CM [ 1 -3/2] [-3/2 1] sage: CM = CoxeterMatrix([[1,-3/2,5],[-3/2,1,-1],[5,-1,1]]); CM [ 1 -3/2 5] [-3/2 1 -1] [ 5 -1 1] sage: CM = CoxeterMatrix([[1,-3/2,5],[-3/2,1,oo],[5,oo,1]]); CM [ 1 -3/2 5] [-3/2 1 -1] [ 5 -1 1] """ # Check that the data is valid check_coxeter_matrix(data) M = matrix(data) n = M.ncols() base_ring = M.base_ring() if not coxeter_type: if n == 1: coxeter_type = CoxeterType(["A", 1]) elif coxeter_type_check: coxeter_type = recognize_coxeter_type_from_matrix(M, index_set) else: coxeter_type = None raw_data = M.list() mat = typecall(cls, MatrixSpace(base_ring, n, sparse=False), raw_data, coxeter_type, index_set) mat._subdivisions = M._subdivisions return mat @classmethod def _from_graph(cls, graph, coxeter_type_check): """ Initiate the Coxeter matrix from a graph. TESTS:: sage: CoxeterMatrix(CoxeterMatrix(['A',4,1]).coxeter_graph()) [1 3 2 2 3] [3 1 3 2 2] [2 3 1 3 2] [2 2 3 1 3] [3 2 2 3 1] sage: CoxeterMatrix(CoxeterMatrix(['B',4,1]).coxeter_graph()) [1 2 3 2 2] [2 1 3 2 2] [3 3 1 3 2] [2 2 3 1 4] [2 2 2 4 1] sage: CoxeterMatrix(CoxeterMatrix(['F',4]).coxeter_graph()) [1 3 2 2] [3 1 4 2] [2 4 1 3] [2 2 3 1] sage: G=Graph() sage: G.add_edge([0,1,oo]) sage: CoxeterMatrix(G) [ 1 -1] [-1 1] sage: H = Graph() sage: H.add_edge([0,1,-1.5]) sage: CoxeterMatrix(H) [ 1.00000000000000 -1.50000000000000] [-1.50000000000000 1.00000000000000] """ verts = sorted(graph.vertices()) index_set = tuple(verts) n = len(index_set) # Setup the basis matrix as all 2 except 1 on the diagonal data = [] for i in range(n): data += [[]] for j in range(n): if i == j: data[-1] += [ZZ.one()] else: data[-1] += [2] for e in graph.edges(): label = e[2] if label is None: label = 3 elif label == infinity: label = -1 elif label not in ZZ and label > -1: raise ValueError("invalid Coxeter graph label") elif label == 0 or label == 1: raise ValueError("invalid Coxeter graph label") i = verts.index(e[0]) j = verts.index(e[1]) data[j][i] = data[i][j] = label return cls._from_matrix(data, None, index_set, coxeter_type_check) @classmethod def _from_coxetertype(cls, coxeter_type): """ Initiate the Coxeter matrix from a Coxeter type. TESTS:: sage: CoxeterMatrix(['A',4]).coxeter_type() Coxeter type of ['A', 4] sage: CoxeterMatrix(['A',4,1]).coxeter_type() Coxeter type of ['A', 4, 1] sage: CoxeterMatrix(['D',4,1]).coxeter_type() Coxeter type of ['D', 4, 1] """ index_set = coxeter_type.index_set() n = len(index_set) reverse = {index_set[i]: i for i in range(n)} data = [[1 if i == j else 2 for j in range(n)] for i in range(n)] for (i, j, l) in coxeter_type.coxeter_graph().edge_iterator(): if l == infinity: l = -1 data[reverse[i]][reverse[j]] = l data[reverse[j]][reverse[i]] = l return cls._from_matrix(data, coxeter_type, index_set, False) @classmethod def samples(self, finite=None, affine=None, crystallographic=None, higher_rank=None): """ Return a sample of the available Coxeter types. INPUT: - ``finite`` -- (default: ``None``) a boolean or ``None`` - ``affine`` -- (default: ``None``) a boolean or ``None`` - ``crystallographic`` -- (default: ``None``) a boolean or ``None`` - ``higher_rank`` -- (default: ``None``) a boolean or ``None`` The sample contains all the exceptional finite and affine Coxeter types, as well as typical representatives of the infinite families. Here the ``higher_rank`` term denotes non-finite, non-affine, Coxeter groups (including hyperbolic types). .. TODO:: Implement the hyperbolic and compact hyperbolic in the samples. EXAMPLES:: sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples()] [ Coxeter type of ['A', 1], Coxeter type of ['A', 5], <BLANKLINE> Coxeter type of ['B', 5], Coxeter type of ['D', 4], <BLANKLINE> Coxeter type of ['D', 5], Coxeter type of ['E', 6], <BLANKLINE> Coxeter type of ['E', 7], Coxeter type of ['E', 8], <BLANKLINE> Coxeter type of ['F', 4], Coxeter type of ['H', 3], <BLANKLINE> Coxeter type of ['H', 4], Coxeter type of ['I', 10], <BLANKLINE> Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], <BLANKLINE> Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], <BLANKLINE> Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], <BLANKLINE> Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], <BLANKLINE> [ 1 -1 -1] [-1 1 -1] Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1], [-1 -1 1], <BLANKLINE> [ 1 -2 3 2] [1 2 3] [-2 1 2 3] [2 1 7] [ 3 2 1 -8] [3 7 1], [ 2 3 -8 1] ] The finite, affine and crystallographic options allow respectively for restricting to (non) finite, (non) affine, and (non) crystallographic Cartan types:: sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(finite=True)] [Coxeter type of ['A', 1], Coxeter type of ['A', 5], Coxeter type of ['B', 5], Coxeter type of ['D', 4], Coxeter type of ['D', 5], Coxeter type of ['E', 6], Coxeter type of ['E', 7], Coxeter type of ['E', 8], Coxeter type of ['F', 4], Coxeter type of ['H', 3], Coxeter type of ['H', 4], Coxeter type of ['I', 10]] sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(affine=True)] [Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1]] sage: [CM.coxeter_type() for CM in CoxeterMatrix.samples(crystallographic=True)] [Coxeter type of ['A', 1], Coxeter type of ['A', 5], Coxeter type of ['B', 5], Coxeter type of ['D', 4], Coxeter type of ['D', 5], Coxeter type of ['E', 6], Coxeter type of ['E', 7], Coxeter type of ['E', 8], Coxeter type of ['F', 4], Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1]] sage: CoxeterMatrix.samples(crystallographic=False) [ [1 3 2 2] [1 3 2] [3 1 3 2] [ 1 -1 -1] [1 2 3] [3 1 5] [2 3 1 5] [ 1 10] [ 1 -1] [-1 1 -1] [2 1 7] [2 5 1], [2 2 5 1], [10 1], [-1 1], [-1 -1 1], [3 7 1], <BLANKLINE> [ 1 -2 3 2] [-2 1 2 3] [ 3 2 1 -8] [ 2 3 -8 1] ] .. TODO:: add some reducible Coxeter types (suggestions?) TESTS:: sage: for ct in CoxeterMatrix.samples(): TestSuite(ct).run() """ result = self._samples() if crystallographic is not None: result = [t for t in result if t.is_crystallographic() == crystallographic] if finite is not None: result = [t for t in result if t.is_finite() == finite] if affine is not None: result = [t for t in result if t.is_affine() == affine] if higher_rank is not None: result = [t for t in result if not t.is_affine() and not t.is_finite()] return result @cached_method def _samples(self): """ Return a sample of all implemented Coxeter types. .. NOTE:: This is intended to be used through :meth:`samples`. EXAMPLES:: sage: [CM.coxeter_type() for CM in CoxeterMatrix._samples()] [ Coxeter type of ['A', 1], Coxeter type of ['A', 5], <BLANKLINE> Coxeter type of ['B', 5], Coxeter type of ['D', 4], <BLANKLINE> Coxeter type of ['D', 5], Coxeter type of ['E', 6], <BLANKLINE> Coxeter type of ['E', 7], Coxeter type of ['E', 8], <BLANKLINE> Coxeter type of ['F', 4], Coxeter type of ['H', 3], <BLANKLINE> Coxeter type of ['H', 4], Coxeter type of ['I', 10], <BLANKLINE> Coxeter type of ['A', 2, 1], Coxeter type of ['B', 5, 1], <BLANKLINE> Coxeter type of ['C', 5, 1], Coxeter type of ['D', 5, 1], <BLANKLINE> Coxeter type of ['E', 6, 1], Coxeter type of ['E', 7, 1], <BLANKLINE> Coxeter type of ['E', 8, 1], Coxeter type of ['F', 4, 1], <BLANKLINE> [ 1 -1 -1] [-1 1 -1] Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1], [-1 -1 1], <BLANKLINE> [ 1 -2 3 2] [1 2 3] [-2 1 2 3] [2 1 7] [ 3 2 1 -8] [3 7 1], [ 2 3 -8 1] ] """ finite = [ CoxeterMatrix(t) for t in [ ["A", 1], ["A", 5], ["B", 5], ["D", 4], ["D", 5], ["E", 6], ["E", 7], ["E", 8], ["F", 4], ["H", 3], ["H", 4], ["I", 10], ] ] affine = [ CoxeterMatrix(t) for t in [ ["A", 2, 1], ["B", 5, 1], ["C", 5, 1], ["D", 5, 1], ["E", 6, 1], ["E", 7, 1], ["E", 8, 1], ["F", 4, 1], ["G", 2, 1], ["A", 1, 1], ] ] higher_matrices = [ [[1, -1, -1], [-1, 1, -1], [-1, -1, 1]], [[1, 2, 3], [2, 1, 7], [3, 7, 1]], [[1, -2, 3, 2], [-2, 1, 2, 3], [3, 2, 1, -8], [2, 3, -8, 1]], ] higher = [CoxeterMatrix(m) for m in higher_matrices] return finite + affine + higher def relabel(self, relabelling): """ Return a relabelled copy of this Coxeter matrix. INPUT: - ``relabelling`` -- a function (or dictionary) OUTPUT: an isomorphic Coxeter type obtained by relabelling the nodes of the Coxeter graph. Namely, the node with label ``i`` is relabelled ``f(i)`` (or, by ``f[i]`` if ``f`` is a dictionary). EXAMPLES:: sage: CoxeterMatrix(['F',4]).relabel({ 1:2, 2:3, 3:4, 4:1}) [1 4 2 3] [4 1 3 2] [2 3 1 2] [3 2 2 1] sage: CoxeterMatrix(['F',4]).relabel(lambda x: x+1 if x<4 else 1) [1 4 2 3] [4 1 3 2] [2 3 1 2] [3 2 2 1] """ if isinstance(relabelling, dict): data = [[self[relabelling[i]][relabelling[j]] for j in self.index_set()] for i in self.index_set()] else: data = [[self[relabelling(i)][relabelling(j)] for j in self.index_set()] for i in self.index_set()] return CoxeterMatrix(data) def __reduce__(self): """ Used for pickling. TESTS:: sage: C = CoxeterMatrix(['A',4]) sage: M = loads(dumps(C)) sage: M._index_set (1, 2, 3, 4) """ if self._coxeter_type: return (CoxeterMatrix, (self._coxeter_type,)) return (CoxeterMatrix, (self._matrix, self._index_set)) def _repr_(self): """ String representation of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]); CM [1 3 2] [3 1 3] [2 3 1] sage: CM = CoxeterMatrix([[1,-3/2],[-3/2,1]]); CM [ 1 -3/2] [-3/2 1] """ return repr(self._matrix) def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]) sage: CM._repr_option('ascii_art') True """ if key == "ascii_art" or key == "element_ascii_art": return self._matrix.nrows() > 1 return super(CoxeterMatrix, self)._repr_option(key) def _latex_(self): r""" Latex representation of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix(['A',3]) sage: latex(CM) \left(\begin{array}{rrr} 1 & 3 & 2 \\ 3 & 1 & 3 \\ 2 & 3 & 1 \end{array}\right) """ return self._matrix._latex_() def __iter__(self): """ Return an iterator for the rows of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix([[1,8],[8,1]]) sage: next(CM.__iter__()) (1, 8) """ return iter(self._matrix) def __getitem__(self, key): """ Return a dictionary of labels adjacent to a node or the label of an edge in the Coxeter graph. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]]) sage: CM = CoxeterMatrix([[1,-2],[-2,1]], ['a','b']) sage: CM['a'] {'a': 1, 'b': -2} sage: CM['b'] {'a': -2, 'b': 1} sage: CM['a','b'] -2 sage: CM['a','a'] 1 """ return self._dict[key] def __hash__(self): r""" Return hash of the Coxeter matrix. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM.__hash__() 1 sage: CM = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM.__hash__() 4 """ return hash(self._matrix) def __eq__(self, other): r""" Return if ``self`` and ``other`` are equal. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM2 = CoxeterMatrix([[1,-2],[-2,1]],['1','2']) sage: CM == CM2 True sage: CM == matrix(CM) False sage: CM3 = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM == CM3 False """ return isinstance(other, CoxeterMatrix) and self._matrix == other._matrix def __ne__(self, other): """ Return if ``self`` and ``other`` are not equal. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-2],[-2,1]],['a','b']) sage: CM2 = CoxeterMatrix([[1,-2],[-2,1]],['1','2']) sage: CM != CM2 False sage: matrix(CM) != CM True sage: CM3 = CoxeterMatrix([[1,-3],[-3,1]],['1','2']) sage: CM != CM3 True """ return not (self == other) def _matrix_(self, R=None): """ Return ``self`` as a matrix over the ring ``R``. EXAMPLES:: sage: CM = CoxeterMatrix([[1,-3],[-3,1]]) sage: matrix(CM) [ 1 -3] [-3 1] sage: matrix(RR, CM) [ 1.00000000000000 -3.00000000000000] [-3.00000000000000 1.00000000000000] """ if R is not None: return self._matrix.change_ring(R) else: return self._matrix ########################################################################## # Coxeter type methods def index_set(self): """ Return the index set of ``self``. EXAMPLES:: sage: C = CoxeterMatrix(['A',1,1]) sage: C.index_set() (0, 1) sage: C = CoxeterMatrix(['E',6]) sage: C.index_set() (1, 2, 3, 4, 5, 6) """ return self._index_set def coxeter_type(self): """ Return the Coxeter type of ``self`` or ``self`` if unknown. EXAMPLES:: sage: C = CoxeterMatrix(['A',4,1]) sage: C.coxeter_type() Coxeter type of ['A', 4, 1] If the Coxeter type is unknown:: sage: C = CoxeterMatrix([[1,3,4], [3,1,-1], [4,-1,1]]) sage: C.coxeter_type() [ 1 3 4] [ 3 1 -1] [ 4 -1 1] """ if self._coxeter_type is None: return self return self._coxeter_type def rank(self): r""" Return the rank of ``self``. EXAMPLES:: sage: CoxeterMatrix(['C',3]).rank() 3 sage: CoxeterMatrix(["A2","B2","F4"]).rank() 8 """ return self._rank def coxeter_matrix(self): r""" Return the Coxeter matrix of ``self``. EXAMPLES:: sage: CoxeterMatrix(['C',3]).coxeter_matrix() [1 3 2] [3 1 4] [2 4 1] """ return self def bilinear_form(self, R=None): r""" Return the bilinear form of ``self``. EXAMPLES:: sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] [-1/2 -1/2 1] sage: CoxeterType(['H', 3]).bilinear_form() [ 1 -1/2 0] [ -1/2 1 1/2*E(5)^2 + 1/2*E(5)^3] [ 0 1/2*E(5)^2 + 1/2*E(5)^3 1] sage: C = CoxeterMatrix([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) sage: C.bilinear_form() [ 1 -1 -1] [-1 1 -1] [-1 -1 1] """ return CoxeterType.bilinear_form(self, R=R) @cached_method def coxeter_graph(self): """ Return the Coxeter graph of ``self``. EXAMPLES:: sage: C = CoxeterMatrix(['A',3]) sage: C.coxeter_graph() Graph on 3 vertices sage: C = CoxeterMatrix([['A',3],['A',1]]) sage: C.coxeter_graph() Graph on 4 vertices """ n = self.rank() I = self.index_set() val = lambda x: infinity if x == -1 else x G = Graph( [ (I[i], I[j], val((self._matrix)[i, j])) for i in range(n) for j in range(i) if self._matrix[i, j] not in [1, 2] ] ) G.add_vertices(I) return G.copy(immutable=True) def is_simply_laced(self): """ Return if ``self`` is simply-laced. A Coxeter matrix is simply-laced if all non-diagonal entries are either 2 or 3. EXAMPLES:: sage: cm = CoxeterMatrix([[1,3,3,3], [3,1,3,3], [3,3,1,3], [3,3,3,1]]) sage: cm.is_simply_laced() True """ # We include 1 in this list to account for the diagonal L = [1, 2, 3] return all(x in L for row in self for x in row) def is_crystallographic(self): """ Return if ``self`` is crystallographic. A Coxeter matrix is crystallographic if all non-diagonal entries are either 2, 4, or 6. EXAMPLES:: sage: CoxeterMatrix(['F',4]).is_crystallographic() True sage: CoxeterMatrix(['H',3]).is_crystallographic() False """ # We include 1 in this list to account for the diagonal L = [1, 2, 3, 4, 6] return all(x in L for row in self for x in row) def is_finite(self): """ Return if ``self`` is a finite type or ``False`` if unknown. EXAMPLES:: sage: M = CoxeterMatrix(['C',4]) sage: M.is_finite() True sage: M = CoxeterMatrix(['D',4,1]) sage: M.is_finite() False sage: M = CoxeterMatrix([[1, -1], [-1, 1]]) sage: M.is_finite() False """ return self._is_finite def is_affine(self): """ Return if ``self`` is an affine type or ``False`` if unknown. EXAMPLES:: sage: M = CoxeterMatrix(['C',4]) sage: M.is_affine() False sage: M = CoxeterMatrix(['D',4,1]) sage: M.is_affine() True sage: M = CoxeterMatrix([[1, 3],[3,1]]) sage: M.is_affine() False sage: M = CoxeterMatrix([[1, -1, 7], [-1, 1, 3], [7, 3, 1]]) sage: M.is_affine() False """ return self._is_affine
def _mul_(self,other): res = Matrix_generic_dense._mul_(self,other) Approximation.__init__(res,res.parent()) res.set_immutable() return res
def __init__(self, parent, entries, copy, coerce): Matrix_generic_dense.__init__(self, parent, entries, copy, coerce) Approximation.__init__(self,parent) self._length = parent.dimension() self.set_immutable()