diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index 889d81de3ad..3ee374b739c 100644 --- a/src/sage/categories/finite_complex_reflection_groups.py +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -535,6 +535,107 @@ def base_change_matrix(self): from sage.matrix.constructor import Matrix return Matrix(list(self.independent_roots())).inverse() + def milnor_fiber_poset(self): + r""" + Return the Milnor fiber poset of ``self``. + + The *Milnor fiber poset* of a finite complex reflection group `W` + is defined as the poset of (right) standard cosets `gW_J`, + where `J` is a subset of the index set `I` of `W`, ordered + by reverse inclusion. This is conjecturally a meet semilattice + if and only if `W` is well-generated. + + EXAMPLES:: + + sage: W = ColoredPermutations(3, 2) + sage: P = W.milnor_fiber_poset() + sage: P + Finite meet-semilattice containing 34 elements + sage: R. = ZZ[] + sage: sum(x**P.rank(elt) for elt in P) + 18*x^2 + 15*x + 1 + + sage: W = ReflectionGroup(4) # optional - gap3 + sage: P = W.milnor_fiber_poset() # optional - gap3 + sage: P # optional - gap3 + Finite meet-semilattice containing 41 elements + sage: sum(x**P.rank(elt) for elt in P) # optional - gap3 + 24*x^2 + 16*x + 1 + + sage: W = ReflectionGroup([4,2,2]) # optional - gap3 + sage: W.is_well_generated() # optional - gap3 + False + sage: P = W.milnor_fiber_poset() # optional - gap3 + sage: P # optional - gap3 + Finite poset containing 47 elements + sage: sum(x**P.rank(elt) for elt in P) # optional - gap3 + 16*x^3 + 24*x^2 + 6*x + 1 + sage: P.is_meet_semilattice() # optional - gap3 + False + """ + I = self.index_set() + data = {} + next_reprs = {(): [g for g in self]} + next_cosets = {(): [frozenset([g]) for g in next_reprs[()]]} + next_level = set((i, ()) for i in range(len(next_cosets[()]))) + while next_level: + cur = next_level + cosets = next_cosets + reprs = next_reprs + next_level = set() + next_cosets = {} + next_reprs = {} + for Y in cur: + index, J = Y + for i in I: + if i in J: + continue + Jp = tuple(sorted(J + (i,))) + # See if the coset is already there + found_coset = False + if Jp in next_cosets: + rep = reprs[J][index] + for ii, C in enumerate(next_cosets[Jp]): + if rep in C: + found_coset = True + Yp = (reprs[J][index], J) + Xp = (next_reprs[Jp][ii], Jp) + if Xp in data: + data[Xp].append(Yp) + else: + data[Xp] = [Yp] + else: + next_cosets[Jp] = [] + next_reprs[Jp] = [] + if found_coset: + continue + + # Otherwise build the coset + next_level.add((len(next_cosets[Jp]), Jp)) + H = set(cosets[J][index]) + to_test = [(g, i) for g in H] + while to_test: + g, j = to_test.pop() + gp = g.apply_simple_reflection(j, side='right') + if gp in H: + continue + H.add(gp) + to_test.extend((gp, j) for j in Jp) + rep = min(H, key=lambda g: g.length()) + next_cosets[Jp].append(frozenset(H)) + next_reprs[Jp].append(rep) + Yp = (reprs[J][index], J) + Xp = (rep, Jp) + if Xp in data: + data[Xp].append(Yp) + else: + data[Xp] = [Yp] + if self.is_well_generated(): + from sage.combinat.posets.lattices import MeetSemilattice + return MeetSemilattice(data) + from sage.combinat.posets.posets import Poset + return Poset(data) + class ElementMethods: @abstract_method(optional=True) @@ -1079,6 +1180,59 @@ def coxeter_elements(self): """ return self.coxeter_element().conjugacy_class() + def milnor_fiber_complex(self): + r""" + Return the Milnor fiber complex of ``self``. + + The *Milnor fiber complex* of a finite well-generated + complex reflection group `W` is the simplicial complex whose + face poset is given by :meth:`milnor_fiber_poset`. When `W` + is an irreducible Shephard group, it is also an equivariant + strong deformation retract of the Milnor fiber `f_1^{-1}(1)`, + where `f_1: V \to \CC` is the polynomial invariant of smallest + degree acting on the reflection representation `V`. + + When `W` is a Coxeter group, this is isomorphic to the + :wikipedia:`Coxeter complex ` of `W`. + + EXAMPLES:: + + sage: W = ColoredPermutations(3, 2) + sage: C = W.milnor_fiber_complex() + sage: C.homology() + {0: 0, 1: Z x Z x Z x Z} + + sage: W = ReflectionGroup(5) # optional - gap3 + sage: C = W.milnor_fiber_complex() # optional - gap3 + sage: C.homology() # optional - gap3 + {0: 0, 1: Z^25} + """ + I = self.index_set() + cosets = {} + for i in I: + Ip = tuple([j for j in I if j != i]) + cosets[Ip] = [] + for g in self: + if any(g in C for C in cosets[Ip]): + continue + H = set([g]) + to_test = [(g, j) for j in Ip] + while to_test: + h, j = to_test.pop() + hp = h.apply_simple_reflection(j, side='right') + if hp in H: + continue + H.add(hp) + to_test.extend((hp, j) for j in Ip) + cosets[Ip].append(frozenset(H)) + verts = {} + for Ip in cosets: + for C in cosets[Ip]: + verts[C, Ip] = len(verts) + facets = [[verts[k] for k in verts if g in k[0]] for g in self] + from sage.topology.simplicial_complex import SimplicialComplex + return SimplicialComplex(facets) + class Irreducible(CategoryWithAxiom): r""" The category of finite irreducible well-generated diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index b08e857edaf..e678aa31031 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -758,6 +758,151 @@ def permutahedron(self, point=None, base_ring=None): from sage.geometry.polyhedron.constructor import Polyhedron return Polyhedron(vertices=vertices, base_ring=base_ring) + def coxeter_poset(self): + r""" + Return the Coxeter poset of ``self``. + + Let `W` be a Coxeter group. The *Coxeter poset* is defined as + the set of (right) standard cosets `gW_J`, where `J` is a + subset of the index set `I` of `W`, ordered by reverse inclusion. + + This is equal to the face poset of the :meth:`Coxeter complex + `. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A', 3]) + sage: P = W.coxeter_poset() + sage: P + Finite meet-semilattice containing 75 elements + sage: P.rank() + 3 + + sage: W = WeylGroup(['B', 3]) + sage: P = W.coxeter_poset() + sage: P + Finite meet-semilattice containing 147 elements + sage: P.rank() + 3 + + sage: W = CoxeterGroup(['I', 7]) + sage: P = W.coxeter_poset() + sage: P + Finite meet-semilattice containing 29 elements + sage: P.rank() + 2 + + sage: W = CoxeterGroup(['H', 3]) + sage: P = W.coxeter_poset() + sage: P + Finite meet-semilattice containing 363 elements + sage: P.rank() + 3 + + sage: W = CoxeterGroup(['H', 3], implementation="permutation") # optional - gap3 + sage: P = W.coxeter_poset() # optional - gap3 + sage: P # optional - gap3 + Finite meet-semilattice containing 363 elements + sage: P.rank() # optional - gap3 + 3 + """ + I = self.index_set() + data = {} + next_level = set((g, ()) for g in self) + while next_level: + cur = next_level + next_level = set() + for Y in cur: + g, J = Y + for i in I: + if i in J: + continue + Jp = tuple(sorted(J + (i,))) + gp = g.coset_representative(Jp, side='right') + X = (gp, Jp) + if X in data: + data[X].append(Y) + else: + data[X] = [Y] + next_level.add(X) + from sage.combinat.posets.lattices import MeetSemilattice + return MeetSemilattice(data) + + def coxeter_complex(self): + r""" + Return the Coxeter complex of ``self``. + + Let `W` be a Coxeter group, and let `X` be the corresponding Tits + cone, which is constructed as the `W` orbit of the fundamental + chamber in the reflection representation. The *Coxeter complex* + of `W` is the simplicial complex `(X \setminus \{0\}) / \RR_{>0}`. + The face poset of this simplicial complex is given by the + :meth:`coxeter_poset()`. When `W` is a finite group, then the + Coxeter complex is homeomorphic to an `(n-1)`-dimensional sphere, + where `n` is the rank of `W`. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A', 3]) + sage: C = W.coxeter_complex() + sage: C + Simplicial complex with 14 vertices and 24 facets + sage: C.homology() + {0: 0, 1: 0, 2: Z} + + sage: W = WeylGroup(['B', 3]) + sage: C = W.coxeter_complex() + sage: C + Simplicial complex with 26 vertices and 48 facets + sage: C.homology() + {0: 0, 1: 0, 2: Z} + + sage: W = CoxeterGroup(['I', 7]) + sage: C = W.coxeter_complex() + sage: C + Simplicial complex with 14 vertices and 14 facets + sage: C.homology() + {0: 0, 1: Z} + + sage: W = CoxeterGroup(['H', 3]) + sage: C = W.coxeter_complex() + sage: C + Simplicial complex with 62 vertices and 120 facets + sage: C.homology() + {0: 0, 1: 0, 2: Z} + + sage: W = CoxeterGroup(['H', 3], implementation="permutation") # optional - gap3 + sage: C = W.coxeter_complex() # optional - gap3 + sage: C # optional - gap3 + Simplicial complex with 62 vertices and 120 facets + sage: C.homology() # optional - gap3 + {0: 0, 1: 0, 2: Z} + """ + I = self.index_set() + facets = {} + Ip = {i: tuple([j for j in I if j != i]) for i in I} + for g in self: + V = [] + for i in I: + gp = g + D = gp.descents(side='right') + if D and D[0] == i: + D.pop(0) + while D: + gp = gp.apply_simple_reflection(D[0]) + D = gp.descents(side='right') + if D and D[0] == i: + D.pop(0) + V.append((gp, Ip[i])) + facets[g] = V + verts = set() + for F in facets.values(): + verts.update(F) + labels = {x: i for i,x in enumerate(verts)} + result = [[labels[v] for v in F] for F in facets.values()] + from sage.topology.simplicial_complex import SimplicialComplex + return SimplicialComplex(result) + class ElementMethods: @cached_in_parent_method