def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form 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] """ n = self.rank() mat = self.coxeter_matrix()._matrix base_ring = mat.base_ring() from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if UCF.has_coerce_map_from(base_ring): R = UCF else: R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. if R is UCF: val = lambda x: (R.gen(2 * x) + ~R.gen(2 * x)) / R(-2) if x > -1 else R.one() * x else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: -R(cos(pi / SR(x))) if x > -1 else x MS = MatrixSpace(R, n, sparse=True) MC = MS._get_matrix_class() bilinear = MC( MS, entries={(i, j): val(mat[i, j]) for i in range(n) for j in range(n) if mat[i, j] != 2}, coerce=True, copy=True, ) bilinear.set_immutable() return bilinear
def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form 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] """ n = self.rank() mat = self.coxeter_matrix()._matrix base_ring = mat.base_ring() from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if R is None: R = UCF # if UCF.has_coerce_map_from(base_ring): # R = UCF # else: # R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. if R is UCF: val = lambda x: (R.gen(2 * x) + ~R.gen(2 * x)) / R( -2) if x > -1 else R.one() * x else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: -R(cos(pi / SR(x))) if x > -1 else x MS = MatrixSpace(R, n, sparse=True) MC = MS._get_matrix_class() bilinear = MC(MS, entries={(i, j): val(mat[i, j]) for i in range(n) for j in range(n) if mat[i, j] != 2}, coerce=True, copy=True) bilinear.set_immutable() return bilinear
def __classcall_private__(cls, data, base_ring=None, index_set=None): """ Normalize arguments to ensure a unique representation. EXAMPLES:: sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=UniversalCyclotomicField()) sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2)) sage: W1 is W2 True sage: G1 = Graph([(1,2)]) sage: W3 = CoxeterGroup(G1) sage: W1 is W3 True sage: G2 = Graph([(1,2,3)]) sage: W4 = CoxeterGroup(G2) sage: W1 is W4 True """ data = CoxeterMatrix(data, index_set=index_set) if base_ring is None: base_ring = UniversalCyclotomicField() return super(CoxeterMatrixGroup, cls).__classcall__(cls, data, base_ring, data.index_set())
def __classcall_private__(cls, data, base_ring=None, index_set=None): """ Normalize arguments to ensure a unique representation. EXAMPLES:: sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=ZZ) sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2)) sage: W1 is W2 True sage: G1 = Graph([(1,2)]) sage: W3 = CoxeterGroup(G1) sage: W1 is W3 True sage: G2 = Graph([(1,2,3)]) sage: W4 = CoxeterGroup(G2) sage: W1 is W4 True """ data = CoxeterMatrix(data, index_set=index_set) if base_ring is None: if data.is_simply_laced(): base_ring = ZZ elif data.is_finite(): letter = data.coxeter_type().cartan_type().type() if letter in ['B', 'C', 'F']: base_ring = QuadraticField(2) elif letter == 'G': base_ring = QuadraticField(3) elif letter == 'H': base_ring = QuadraticField(5) else: base_ring = UniversalCyclotomicField() else: base_ring = UniversalCyclotomicField() return super(CoxeterMatrixGroup, cls).__classcall__(cls, data, base_ring, data.index_set())
def H_value(self, p, f, t, ring=None): """ Return the trace of the Frobenius, computed in terms of Gauss sums using the hypergeometric trace formula. INPUT: - `p` -- a prime number - `f` -- an integer such that `q = p^f` - `t` -- a rational parameter - ``ring`` -- optional (default ``UniversalCyclotomicfield``) The ring could be also ``ComplexField(n)`` or ``QQbar``. OUTPUT: an integer .. WARNING:: This is apparently working correctly as can be tested using ComplexField(70) as value ring. Using instead UniversalCyclotomicfield, this is much slower than the `p`-adic version :meth:`padic_H_value`. EXAMPLES: With values in the UniversalCyclotomicField (slow):: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) sage: [H.H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.H_value(5,i,-1) for i in range(1,3)] [-4, 276] sage: [H.H_value(7,i,-1) for i in range(1,3)] # not tested [0, -476] sage: [H.H_value(11,i,-1) for i in range(1,3)] # not tested [0, -4972] sage: [H.H_value(13,i,-1) for i in range(1,3)] # not tested [-84, -1420] With values in ComplexField:: sage: [H.H_value(5,i,-1, ComplexField(60)) for i in range(1,3)] [-4, 276] Check issue from :trac:`28404`:: sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2])) sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) sage: [H1.H_value(5,1,i) for i in range(2,5)] [1, -4, -4] sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)] [-4, 1, -4] REFERENCES: - [BeCoMe]_ (Theorem 1.3) - [Benasque2009]_ """ alpha = self._alpha beta = self._beta t = QQ(t) if 0 in alpha: return self._swap.H_value(p, f, ~t, ring) if ring is None: ring = UniversalCyclotomicField() gamma = self.gamma_array() q = p ** f m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)} D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 M = self.M_value() Fq = GF(q) gen = Fq.multiplicative_generator() zeta_q = ring.zeta(q - 1) tM = Fq(M / t) for k in range(q - 1): if gen ** k == tM: teich = zeta_q ** k break gauss_table = [gauss_sum(zeta_q ** r, Fq) for r in range(q - 1)] sigma = sum(q**(D + m[0] - m[r]) * prod(gauss_table[(-v * r) % (q - 1)]**gv for v, gv in gamma.items()) * teich ** r for r in range(q - 1)) resu = ZZ(-1) ** m[0] / (1 - q) * sigma if not ring.is_exact(): resu = resu.real_part().round() return resu
def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form 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] """ n = self.rank() mat = self.coxeter_matrix()._matrix from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if R is None: R = UCF # if UCF.has_coerce_map_from(base_ring): # R = UCF # else: # R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. E = UCF.gen if R is UCF: def val(x): if x > -1: return (E(2 * x) + ~E(2 * x)) / R(-2) else: return R(x) elif is_QuadraticField(R): def val(x): if x > -1: return R( (E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) / R(-2) else: return R(x) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x > -1: return -R(cos(pi / SR(x))) else: return R(x) entries = [ SparseEntry(i, j, val(mat[i, j])) for i in range(n) for j in range(n) if mat[i, j] != 2 ] bilinear = Matrix(R, n, entries) bilinear.set_immutable() return bilinear
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty E = UniversalCyclotomicField().gen if base_ring is UniversalCyclotomicField(): def val(x): if x == -1: return 2 else: return E(2 * x) + ~E(2 * x) elif is_QuadraticField(base_ring): def val(x): if x == -1: return 2 else: return base_ring( (E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x == -1: return 2 else: return base_ring(2 * cos(pi / x)) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[index_set[i], index_set[j]]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Make the generators dense matrices for consistency and speed gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() self._index_set_inverse = { i: ii for ii, i in enumerate(self._matrix.index_set()) } FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def real_characters(G): r""" Return a pair ``(table of characters, character degrees)`` for the group ``G``. OUTPUT: - table of characters - a list of characters. Each character is represented as the list of its values on conjugacy classes. The order of conjugacy classes is the same as the one returned by GAP. - degrees - the list of degrees of the characters EXAMPLES:: sage: from surface_dynamics.misc.group_representation import real_characters sage: T, deg = real_characters(AlternatingGroup(5)) sage: T [(1, 1, 1, 1, 1), (3, -1, 0, -E(5) - E(5)^4, -E(5)^2 - E(5)^3), (3, -1, 0, -E(5)^2 - E(5)^3, -E(5) - E(5)^4), (4, 0, 1, -1, -1), (5, 1, -1, 0, 0)] sage: set(parent(x) for chi in T for x in chi) {Universal Cyclotomic Field} sage: deg [1, 3, 3, 4, 5] sage: T, deg = real_characters(CyclicPermutationGroup(6)) sage: T [(1, 1, 1, 1, 1, 1), (1, -1, 1, -1, 1, -1), (2, -1, -1, 2, -1, -1), (2, 1, -1, -2, -1, 1)] sage: deg [1, 1, 1, 1] """ from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField G = libgap(G) UCF = UniversalCyclotomicField() n = G.ConjugacyClasses().Length() Tgap = G.CharacterTable().Irr() degrees = [chi.Degree().sage() for chi in Tgap] Tgap = [tuple(UCF(chi[j]) for j in range(n)) for chi in Tgap] real_T = [] real_degrees = [] seen = set() for i, chi in enumerate(Tgap): if chi in seen: continue real_degrees.append(degrees[i]) if all(x.is_real() for x in chi): real_T.append(chi) seen.add(chi) else: seen.add(chi) chi_bar = tuple(z.conjugate() for z in chi) seen.add(chi_bar) real_T.append(tuple(chi[j] + chi[j].conjugate() for j in range(n))) return (real_T, real_degrees)
def H_value(self, p, f, t, ring=None): """ Return the trace of the Frobenius, computed in terms of Gauss sums using the hypergeometric trace formula. INPUT: - `p` -- a prime number - `f` -- an integer such that `q = p^f` - `t` -- a rational parameter - ``ring`` -- optional (default ``UniversalCyclotomicfield``) The ring could be also ``ComplexField(n)`` or ``QQbar``. OUTPUT: an integer .. WARNING:: This is apparently working correctly as can be tested using ComplexField(70) as value ring. Using instead UniversalCyclotomicfield, this is much slower than the `p`-adic version :meth:`padic_H_value`. EXAMPLES: With values in the UniversalCyclotomicField (slow):: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) sage: [H.H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.H_value(5,i,-1) for i in range(1,3)] [-4, 276] sage: [H.H_value(7,i,-1) for i in range(1,3)] # not tested [0, -476] sage: [H.H_value(11,i,-1) for i in range(1,3)] # not tested [0, -4972] sage: [H.H_value(13,i,-1) for i in range(1,3)] # not tested [-84, -1420] With values in ComplexField:: sage: [H.H_value(5,i,-1, ComplexField(60)) for i in range(1,3)] [-4, 276] REFERENCES: - [BeCoMe]_ (Theorem 1.3) - [Benasque2009]_ """ alpha = self._alpha beta = self._beta if 0 in alpha: H = self.swap_alpha_beta() return(H.H_value(p, f, ~t, ring)) if ring is None: ring = UniversalCyclotomicField() t = QQ(t) gamma = self.gamma_array() q = p ** f m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)} D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 M = self.M_value() Fq = GF(q) gen = Fq.multiplicative_generator() zeta_q = ring.zeta(q - 1) tM = Fq(M / t) for k in range(q - 1): if gen ** k == tM: teich = zeta_q ** k break gauss_table = [gauss_sum(zeta_q ** r, Fq) for r in range(q - 1)] sigma = sum(q**(D + m[0] - m[r]) * prod(gauss_table[(-v * r) % (q - 1)] ** gv for v, gv in gamma.items()) * teich ** r for r in range(q - 1)) resu = ZZ(-1) ** m[0] / (1 - q) * sigma if not ring.is_exact(): resu = resu.real_part().round() return resu
def permutahedron(self, point=None, base_ring=None): r""" Return the permutahedron of ``self``, This is the convex hull of the point ``point`` in the weight basis under the action of ``self`` on the underlying vector space `V`. .. SEEALSO:: :meth:`~sage.combinat.root_system.reflection_group_real.permutahedron` INPUT: - ``point`` -- optional, a point given by its coordinates in the weight basis (default is `(1, 1, 1, \ldots)`) - ``base_ring`` -- optional, the base ring of the polytope .. NOTE:: The result is expressed in the root basis coordinates. .. NOTE:: If function is too slow, switching the base ring to :class:`RDF` will almost certainly speed things up. EXAMPLES:: sage: W = CoxeterGroup(['H',3], base_ring=RDF) sage: W.permutahedron() doctest:warning ... UserWarning: This polyhedron data is numerically complicated; cdd could not convert between the inexact V and H representation without loss of data. The resulting object might show inconsistencies. A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 120 vertices sage: W = CoxeterGroup(['I',7]) sage: W.permutahedron() A 2-dimensional polyhedron in AA^2 defined as the convex hull of 14 vertices sage: W.permutahedron(base_ring=RDF) A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 14 vertices sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.permutahedron() # optional - gap3 A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices sage: W = ReflectionGroup(['A',3],['B',2]) # optional - gap3 sage: W.permutahedron() # optional - gap3 A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 192 vertices TESTS:: sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.permutahedron([3,5,8]) # optional - gap3 A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices .. PLOT:: :width: 300 px W = CoxeterGroup(['I',7]) p = W.permutahedron() sphinx_plot(p) """ n = self.one().canonical_matrix().rank() weights = self.fundamental_weights() if point is None: from sage.rings.integer_ring import ZZ point = [ZZ.one()] * n v = sum(point[i - 1] * weights[i] for i in weights.keys()) from sage.geometry.polyhedron.constructor import Polyhedron from sage.rings.qqbar import AA, QQbar from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField vertices = [v * w for w in self] if base_ring is None and v.base_ring() in [ UniversalCyclotomicField(), QQbar ]: vertices = [v.change_ring(AA) for v in vertices] base_ring = AA return Polyhedron(vertices=vertices, base_ring=base_ring)
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2 * x) + ~base_ring.gen( 2 * x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2 * cos(pi / x) ) if x != -1 else base_ring(2) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[i, j]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. # This describes the bilinear form corresponding to this # Coxeter system, and might lead us out of our base ring. base_field = base_ring.fraction_field() MS2 = MatrixSpace(base_field, n, sparse=True) MC2 = MS2._get_matrix_class() self._bilinear = MC2(MS2, entries={ (i, j): val(coxeter_matrix[i, j]) / base_field(-2) for i in range(n) for j in range(n) if coxeter_matrix[i, j] != 2 }, coerce=True, copy=True) self._bilinear.set_immutable() category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. is_finite = self._finite_recognition() if is_finite: category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=category)
def __classcall_private__(cls, data, base_ring=None, index_set=None): """ Normalize arguments to ensure a unique representation. EXAMPLES:: sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=UniversalCyclotomicField()) sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2)) sage: W1 is W2 True sage: G1 = Graph([(1,2)]) sage: W3 = CoxeterGroup(G1) sage: W1 is W3 True sage: G2 = Graph([(1,2,3)]) sage: W4 = CoxeterGroup(G2) sage: W1 is W4 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 = CoxeterGroup(matrix([[1, 3, 2], [3,1,-1], [2,-1,1]])) sage: W2 = CoxeterGroup(G) sage: W1 is W2 True sage: CoxeterGroup(W1.coxeter_graph()) is W1 True """ if isinstance(data, CartanType_abstract): if index_set is None: index_set = data.index_set() data = data.coxeter_matrix() elif isinstance(data, Graph): G = data n = G.num_verts() # Setup the basis matrix as all 2 except 1 on the diagonal data = matrix(ZZ, [[2] * n] * n) for i in range(n): data[i, i] = ZZ.one() verts = G.vertices() for e in G.edges(): m = e[2] if m is None: m = 3 elif m == infinity or m == -1: # FIXME: Hack because there is no ZZ\cup\{\infty\} m = -1 elif m <= 1: raise ValueError("invalid Coxeter graph label") i = verts.index(e[0]) j = verts.index(e[1]) data[j, i] = data[i, j] = m if index_set is None: index_set = G.vertices() else: try: data = matrix(data) except (ValueError, TypeError): data = CartanType(data).coxeter_matrix() if not data.is_symmetric(): raise ValueError("the Coxeter matrix is not symmetric") if any(d != 1 for d in data.diagonal()): raise ValueError("the Coxeter matrix diagonal is not all 1") if any(val <= 1 and val != -1 for i, row in enumerate(data.rows()) for val in row[i + 1:]): raise ValueError("invalid Coxeter label") if index_set is None: index_set = range(data.nrows()) if base_ring is None: base_ring = UniversalCyclotomicField() data.set_immutable() return super(CoxeterMatrixGroup, cls).__classcall__(cls, data, base_ring, tuple(index_set))