From d38831ef816033b518592f3bfe4286dd5b6cdf16 Mon Sep 17 00:00:00 2001 From: Christian Stump Date: Wed, 18 Jan 2023 18:25:53 +0100 Subject: [PATCH 1/7] faster iteration for complex non-real reflection group and new hash --- src/sage/combinat/root_system/reflection_group_complex.py | 2 ++ src/sage/combinat/root_system/reflection_group_element.pxd | 1 + src/sage/combinat/root_system/reflection_group_element.pyx | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index f0cd54d009a..3e1306c7a47 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -301,6 +301,8 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio canonicalize=False, category=category) + self._length_of_permutation_representation = len(Permutation(self.gens()[0])) + l_set = list(range(1, len(self.gens()) + 1)) if self._index_set is None: self._index_set = tuple(l_set) diff --git a/src/sage/combinat/root_system/reflection_group_element.pxd b/src/sage/combinat/root_system/reflection_group_element.pxd index 04e98fc3fb2..290e58e3a2d 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pxd +++ b/src/sage/combinat/root_system/reflection_group_element.pxd @@ -1,6 +1,7 @@ from sage.groups.perm_gps.permgroup_element cimport PermutationGroupElement cdef class ComplexReflectionGroupElement(PermutationGroupElement): + cpdef test_hash(self) cpdef action(self, vec, on_space=*) cpdef action_on_root_indices(self, i) diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index 8f5a61ff0a2..4694c7bfb23 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -84,6 +84,12 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): """ return hash(self._parent) | super().__hash__() + cpdef test_hash(self): + cdef int i + cdef int l = self.parent()._length_of_permutation_representation + cdef tuple perm = tuple([self.perm[i] for i in range(l)]) + return hash(self._parent) | hash(perm) + def reduced_word(self): r""" Return a word in the simple reflections to obtain ``self``. From 8226d70375dd0895c88106749879869897188daa Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 25 Jan 2023 10:43:07 +0900 Subject: [PATCH 2/7] Updating __hash__ and updating gap3 doctests to pass. --- src/sage/combinat/root_system/reflection_group_element.pxd | 1 - src/sage/combinat/root_system/reflection_group_element.pyx | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/sage/combinat/root_system/reflection_group_element.pxd b/src/sage/combinat/root_system/reflection_group_element.pxd index 290e58e3a2d..04e98fc3fb2 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pxd +++ b/src/sage/combinat/root_system/reflection_group_element.pxd @@ -1,7 +1,6 @@ from sage.groups.perm_gps.permgroup_element cimport PermutationGroupElement cdef class ComplexReflectionGroupElement(PermutationGroupElement): - cpdef test_hash(self) cpdef action(self, vec, on_space=*) cpdef action_on_root_indices(self, i) diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index 4694c7bfb23..8f5a61ff0a2 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -84,12 +84,6 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): """ return hash(self._parent) | super().__hash__() - cpdef test_hash(self): - cdef int i - cdef int l = self.parent()._length_of_permutation_representation - cdef tuple perm = tuple([self.perm[i] for i in range(l)]) - return hash(self._parent) | hash(perm) - def reduced_word(self): r""" Return a word in the simple reflections to obtain ``self``. From ad219268cce22e1ef0102e1c9afaac9c1dadef53 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jan 2023 16:39:54 +0900 Subject: [PATCH 3/7] Adding some systematic doctests and one detail. --- src/sage/combinat/root_system/reflection_group_complex.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 3e1306c7a47..f0cd54d009a 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -301,8 +301,6 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio canonicalize=False, category=category) - self._length_of_permutation_representation = len(Permutation(self.gens()[0])) - l_set = list(range(1, len(self.gens()) + 1)) if self._index_set is None: self._index_set = tuple(l_set) From 5cba15090589b999eed82b89c801cf8b2bc695f5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jan 2023 17:40:31 +0900 Subject: [PATCH 4/7] Implementation of Milnor fiber/Coxeter complex/poset. --- .../finite_complex_reflection_groups.py | 157 ++++++++++++++++++ src/sage/categories/finite_coxeter_groups.py | 132 +++++++++++++++ 2 files changed, 289 insertions(+) diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index 889d81de3ad..b4bd63d4115 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 34 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 + Finite poset containing 47 elements + sage: sum(x**P.rank(elt) for elt in P) + 16*x^3 + 24*x^2 + 6*x + 1 + sage: P.is_meet_semilattice() + 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,62 @@ 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 = list(I) + Ip.remove(i) + Ip = tuple(Ip) + 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..11d718fc516 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -758,6 +758,138 @@ 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], implementation="permutation") + sage: P = W.coxeter_poset() + sage: P + Finite meet-semilattice containing 363 elements + sage: P.rank() + 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], implementation="permutation") + sage: C = W.coxeter_complex() + sage: C + Simplicial complex with 62 vertices and 120 facets + sage: C.homology() + {0: 0, 1: 0, 2: Z} + """ + I = self.index_set() + facets = {} + 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) + Ip = list(I) + Ip.remove(i) + V.append((gp, tuple(Ip))) + 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 From 0770552b06dc63c7af3009eb71ca43f2696b530e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 15 Feb 2023 12:25:04 +0900 Subject: [PATCH 5/7] Fixing doctests for GAP3 and non-GAP3 systems. --- .../finite_complex_reflection_groups.py | 8 ++++---- src/sage/categories/finite_coxeter_groups.py | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index b4bd63d4115..1708688d49e 100644 --- a/src/sage/categories/finite_complex_reflection_groups.py +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -558,7 +558,7 @@ def milnor_fiber_poset(self): sage: W = ReflectionGroup(4) # optional - gap3 sage: P = W.milnor_fiber_poset() # optional - gap3 sage: P # optional - gap3 - Finite meet-semilattice containing 34 elements + Finite meet-semilattice containing 41 elements sage: sum(x**P.rank(elt) for elt in P) # optional - gap3 24*x^2 + 16*x + 1 @@ -566,11 +566,11 @@ def milnor_fiber_poset(self): sage: W.is_well_generated() # optional - gap3 False sage: P = W.milnor_fiber_poset() # optional - gap3 - sage: P + sage: P # optional - gap3 Finite poset containing 47 elements - sage: sum(x**P.rank(elt) for elt in P) + 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() + sage: P.is_meet_semilattice() # optional - gap3 False """ I = self.index_set() diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 11d718fc516..12bca8ef093 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -792,12 +792,19 @@ def coxeter_poset(self): sage: P.rank() 2 - sage: W = CoxeterGroup(['H', 3], implementation="permutation") + 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 = {} @@ -857,12 +864,19 @@ def coxeter_complex(self): sage: C.homology() {0: 0, 1: Z} - sage: W = CoxeterGroup(['H', 3], implementation="permutation") + 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 = {} From 0b126dea41a0113dc0b77bd10af037747fa521c5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Sep 2023 11:03:34 +0900 Subject: [PATCH 6/7] Avoid repeatedly building I \ {i}. --- src/sage/categories/finite_complex_reflection_groups.py | 4 +--- src/sage/categories/finite_coxeter_groups.py | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index 1708688d49e..0ef63cfd42b 100644 --- a/src/sage/categories/finite_complex_reflection_groups.py +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -1210,9 +1210,7 @@ def milnor_fiber_complex(self): I = self.index_set() cosets = {} for i in I: - Ip = list(I) - Ip.remove(i) - Ip = tuple(Ip) + 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]): diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 12bca8ef093..e678aa31031 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -880,6 +880,7 @@ def coxeter_complex(self): """ 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: @@ -892,9 +893,7 @@ def coxeter_complex(self): D = gp.descents(side='right') if D and D[0] == i: D.pop(0) - Ip = list(I) - Ip.remove(i) - V.append((gp, tuple(Ip))) + V.append((gp, Ip[i])) facets[g] = V verts = set() for F in facets.values(): From 6d9ee050ab1fea31b0d929a3201d552928b6bb0f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Sep 2023 17:41:21 +0900 Subject: [PATCH 7/7] Making the linter happy. --- src/sage/categories/finite_complex_reflection_groups.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index 0ef63cfd42b..3ee374b739c 100644 --- a/src/sage/categories/finite_complex_reflection_groups.py +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -1233,7 +1233,6 @@ def milnor_fiber_complex(self): from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(facets) - class Irreducible(CategoryWithAxiom): r""" The category of finite irreducible well-generated