Skip to content

Commit 59b4f54

Browse files
committed
Implementation of simplicial set covers and simplicial stes of group presentations
1 parent 2114066 commit 59b4f54

File tree

4 files changed

+284
-21
lines changed

4 files changed

+284
-21
lines changed

src/sage/categories/simplicial_sets.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,219 @@ def fundamental_group(self, simplify=True):
366366
else:
367367
return FG.quotient(rels)
368368

369+
def universal_cover_map(self):
370+
r"""
371+
Return the universal covering map of the simplicial set.
372+
373+
It requires the fundamental group to be finite.
374+
375+
EXAMPLES::
376+
377+
sage: RP2 = simplicial_sets.RealProjectiveSpace(2)
378+
sage: phi = RP2.universal_cover_map()
379+
sage: phi
380+
Simplicial set morphism:
381+
From: Simplicial set with 6 non-degenerate simplices
382+
To: RP^2
383+
Defn: [(1, 1), (1, e), (f, 1), (f, e), (f * f, 1), (f * f, e)] --> [1, 1, f, f, f * f, f * f]
384+
sage: phi.domain().face_data()
385+
{(f, 1): ((1, e), (1, 1)),
386+
(f, e): ((1, 1), (1, e)),
387+
(f * f, 1): ((f, e), s_0 (1, 1), (f, 1)),
388+
(f * f, e): ((f, 1), s_0 (1, e), (f, e)),
389+
(1, e): None,
390+
(1, 1): None}
391+
392+
"""
393+
from sage.groups.free_group import FreeGroup
394+
skel = self.n_skeleton(2)
395+
graph = skel.graph()
396+
if not skel.is_connected():
397+
graph = graph.subgraph(skel.base_point())
398+
edges = [e[2] for e in graph.edges(sort=False)]
399+
spanning_tree = [e[2] for e in graph.min_spanning_tree()]
400+
gens = [e for e in edges if e not in spanning_tree]
401+
402+
if not gens:
403+
return self
404+
405+
gens_dict = dict(zip(gens, range(len(gens))))
406+
FG = FreeGroup(len(gens), 'e')
407+
rels = []
408+
409+
for f in skel.n_cells(2):
410+
z = dict()
411+
for i, sigma in enumerate(skel.faces(f)):
412+
if sigma in spanning_tree:
413+
z[i] = FG.one()
414+
elif sigma.is_degenerate():
415+
z[i] = FG.one()
416+
elif sigma in edges:
417+
z[i] = FG.gen(gens_dict[sigma])
418+
else:
419+
# sigma is not in the correct connected component.
420+
z[i] = FG.one()
421+
rels.append(z[0]*z[1].inverse()*z[2])
422+
G = FG.quotient(rels)
423+
char = {g : G.gen(i) for i,g in enumerate(gens)}
424+
for e in edges:
425+
if not e in gens:
426+
char[e] = G.one()
427+
return self.covering_map(char)
428+
429+
def covering_map(self, character):
430+
r"""
431+
Return the covering map associated to a character.
432+
433+
The character is represented by a dictionary, that assigns an
434+
element of a finite group to each nondegenerate 1-dimensional
435+
cell. It should correspond to an epimorphism from the fundamental
436+
group.
437+
438+
INPUT:
439+
440+
- ``character`` -- a dictionary
441+
442+
443+
EXAMPLES::
444+
445+
sage: S1 = simplicial_sets.Sphere(1)
446+
sage: W = S1.wedge(S1)
447+
sage: G = CyclicPermutationGroup(3)
448+
sage: C = W.covering_map({a : G.gen(0), b : G.one()})
449+
sage: C
450+
Simplicial set morphism:
451+
From: Simplicial set with 9 non-degenerate simplices
452+
To: Wedge: (S^1 v S^1)
453+
Defn: [(*, ()), (*, (1,2,3)), (*, (1,3,2)), (sigma_1, ()), (sigma_1, ()), (sigma_1, (1,2,3)), (sigma_1, (1,2,3)), (sigma_1, (1,3,2)), (sigma_1, (1,3,2))] --> [*, *, *, sigma_1, sigma_1, sigma_1, sigma_1, sigma_1, sigma_1]
454+
sage: C.domain()
455+
Simplicial set with 9 non-degenerate simplices
456+
sage: C.domain().face_data()
457+
{(sigma_1, ()): ((*, ()), (*, ())),
458+
(sigma_1, (1,2,3)): ((*, (1,2,3)), (*, (1,2,3))),
459+
(sigma_1, (1,3,2)): ((*, (1,3,2)), (*, (1,3,2))),
460+
(sigma_1, ()): ((*, ()), (*, ())),
461+
(sigma_1, (1,2,3)): ((*, (1,2,3)), (*, (1,2,3))),
462+
(sigma_1, (1,3,2)): ((*, (1,3,2)), (*, (1,3,2))),
463+
(*, ()): None,
464+
(*, (1,3,2)): None,
465+
(*, (1,2,3)): None}
466+
467+
"""
468+
from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
469+
from sage.topology.simplicial_set_morphism import SimplicialSetMorphism
470+
char = {a : b for (a,b) in character.items()}
471+
G = list(char.values())[0].parent()
472+
if not G.is_finite():
473+
raise NotImplementedError("can only compute universal covers of spaces with finite fundamental group")
474+
cells_dict = {}
475+
faces_dict = {}
476+
477+
for s in self.n_cells(0):
478+
for g in G:
479+
cell = AbstractSimplex(0,name="({}, {})".format(s, g))
480+
cells_dict[(s,g)] = cell
481+
char[s] = G.one()
482+
483+
for d in range(1, self.dimension() + 1):
484+
for s in self.n_cells(d):
485+
if not s in char.keys():
486+
if d==1 and s.is_degenerate():
487+
char[s] = G.one()
488+
elif s.is_degenerate():
489+
if 0 in s.degeneracies():
490+
char[s] = G.one()
491+
else:
492+
char[s] = char[s.nondegenerate()]
493+
else:
494+
char[s] = char[self.face(s, d)]
495+
if s.is_nondegenerate():
496+
for g in G:
497+
cell = AbstractSimplex(d,name="({}, {})".format(s, g))
498+
cells_dict[(s,g)] = cell
499+
fd = []
500+
faces = self.faces(s)
501+
f0 = faces[0]
502+
for h in G:
503+
if h == g*char[s]:
504+
lifted = h
505+
break
506+
grelems = [cells_dict[(f0.nondegenerate(), lifted)].apply_degeneracies(*f0.degeneracies())]
507+
for f in faces[1:]:
508+
grelems.append(cells_dict[(f.nondegenerate(), g)].apply_degeneracies(*f.degeneracies()))
509+
faces_dict[cell] = grelems
510+
cover = SimplicialSet(faces_dict, base_point=cells_dict[(self.base_point(), G.one())])
511+
cover_map_data = {c : s[0] for (s,c) in cells_dict.items()}
512+
return SimplicialSetMorphism(data = cover_map_data, domain = cover, codomain = self)
513+
514+
def cover(self, character):
515+
r"""
516+
Return the cover of the simplicial set associated to a character
517+
of the fundamental group.
518+
519+
The character is represented by a dictionary, that assigns an
520+
element of a finite group to each nondegenerate 1-dimensional
521+
cell. It should correspond to an epimorphism from the fundamental
522+
group.
523+
524+
INPUT:
525+
526+
- ``character`` -- a dictionary
527+
528+
EXAMPLES::
529+
530+
sage: S1 = simplicial_sets.Sphere(1)
531+
sage: W = S1.wedge(S1)
532+
sage: G = CyclicPermutationGroup(3)
533+
sage: (a, b) = W.n_cells(1)
534+
sage: C = W.cover({a : G.gen(0), b : G.gen(0)^2}).domain()
535+
sage: C.face_data()
536+
{(sigma_1, ()): ((*, (1,2,3)), (*, ())),
537+
(sigma_1, (1,2,3)): ((*, (1,3,2)), (*, (1,2,3))),
538+
(sigma_1, (1,3,2)): ((*, ()), (*, (1,3,2))),
539+
(sigma_1, ()): ((*, (1,3,2)), (*, ())),
540+
(sigma_1, (1,2,3)): ((*, ()), (*, (1,2,3))),
541+
(sigma_1, (1,3,2)): ((*, (1,2,3)), (*, (1,3,2))),
542+
(*, (1,2,3)): None,
543+
(*, ()): None,
544+
(*, (1,3,2)): None}
545+
sage: C.homology(1)
546+
Z x Z x Z x Z
547+
sage: C.fundamental_group()
548+
Finitely presented group < e0, e1, e2, e3 | >
549+
550+
"""
551+
return self.covering_map(character).domain()
552+
553+
def universal_cover(self):
554+
r"""
555+
Return the universal cover of the simplicial set.
556+
The fundamental group must be finite in order to ensure that the
557+
universal cover is a simplicial set of finite type.
558+
559+
EXAMPLES::
560+
561+
sage: RP3 = simplicial_sets.RealProjectiveSpace(3)
562+
sage: C = RP3.universal_cover()
563+
sage: C
564+
Simplicial set with 8 non-degenerate simplices
565+
sage: C.face_data()
566+
{(f, 1): ((1, e), (1, 1)),
567+
(f, e): ((1, 1), (1, e)),
568+
(f * f, 1): ((f, e), s_0 (1, 1), (f, 1)),
569+
(f * f, e): ((f, 1), s_0 (1, e), (f, e)),
570+
(f * f * f, 1): ((f * f, e), s_0 (f, 1), s_1 (f, 1), (f * f, 1)),
571+
(f * f * f, e): ((f * f, 1), s_0 (f, e), s_1 (f, e), (f * f, e)),
572+
(1, e): None,
573+
(1, 1): None}
574+
sage: C.fundamental_group()
575+
Finitely presented group < | >
576+
577+
578+
"""
579+
return self.universal_cover_map().domain()
580+
581+
369582
def is_simply_connected(self):
370583
"""
371584
Return ``True`` if this pointed simplicial set is simply connected.

src/sage/topology/simplicial_set.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,26 +1686,12 @@ def graph(self):
16861686
sage: Sigma3.nerve().is_connected()
16871687
True
16881688
"""
1689-
skel = self.n_skeleton(1)
1690-
edges = skel.n_cells(1)
1691-
vertices = skel.n_cells(0)
1692-
used_vertices = set() # vertices which are in an edge
1693-
d = {}
1694-
for e in edges:
1695-
v = skel.face(e, 0)
1696-
w = skel.face(e, 1)
1697-
if v in d:
1698-
if w in d[v]:
1699-
d[v][w] = d[v][w] + [e]
1700-
else:
1701-
d[v][w] = [e]
1702-
else:
1703-
d[v] = {w: [e]}
1704-
used_vertices.update([v, w])
1705-
for v in vertices:
1706-
if v not in used_vertices:
1707-
d[v] = {}
1708-
return Graph(d, format='dict_of_dicts')
1689+
G = Graph(loops=True, multiedges=True)
1690+
for e in self.n_cells(1):
1691+
G.add_edge(self.face(e,0), self.face(e,1), e)
1692+
for v in self.n_cells(0):
1693+
G.add_vertex(v)
1694+
return G
17091695

17101696
def is_connected(self):
17111697
"""

src/sage/topology/simplicial_set_catalog.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
- the Hopf map: this is a pre-built morphism, from which one can
2727
extract its domain, codomain, mapping cone, etc.
2828
29+
- the complex of a group presentation.
30+
2931
All of these examples are accessible by typing
3032
``simplicial_sets.NAME``, where ``NAME`` is the name of the
3133
example. Type ``simplicial_sets.[TAB]`` for a complete list.
@@ -48,4 +50,4 @@
4850
KleinBottle, Torus,
4951
Simplex, Horn, Point,
5052
ComplexProjectiveSpace,
51-
HopfMap)
53+
HopfMap, PresentationComplex)

src/sage/topology/simplicial_set_examples.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
AUTHORS:
1010
1111
- John H. Palmieri (2016-07)
12+
13+
- Miguel Marco (2022-12)
1214
"""
1315
# ****************************************************************************
1416
# Copyright (C) 2016 John H. Palmieri <palmieri at math.washington.edu>
@@ -48,6 +50,8 @@
4850
from sage.misc.lazy_import import lazy_import
4951
lazy_import('sage.categories.simplicial_sets', 'SimplicialSets')
5052

53+
54+
5155
########################################################################
5256
# The nerve of a finite monoid, used in sage.categories.finite_monoid.
5357

@@ -790,3 +794,61 @@ def HopfMap():
790794
return S3.Hom(S2)({alpha_1:s0_sigma, alpha_2:s1_sigma,
791795
alpha_3:s2_sigma, alpha_4:s0_sigma,
792796
alpha_5:s2_sigma, alpha_6:s1_sigma})
797+
798+
799+
def PresentationComplex(G):
800+
r"""
801+
Return a simplicial set constructed from a group presentation.
802+
The result is a subdivision of the presentation complex.
803+
804+
INPUT:
805+
806+
- "G" -- a finitely presented group
807+
808+
EXAMPLES::
809+
810+
sage: G = SymmetricGroup(2).as_finitely_presented_group()
811+
sage: G
812+
Finitely presented group < a | a^2 >
813+
sage: S = simplicial_sets.PresentationComplex(G)
814+
sage: S
815+
Simplicial set with 5 non-degenerate simplices
816+
sage: S.face_data()
817+
{Delta^0: None,
818+
a: (Delta^0, Delta^0),
819+
a^-1: (Delta^0, Delta^0),
820+
Ta: (a, s_0 Delta^0, a^-1),
821+
a^2: (a, s_0 Delta^0, a)}
822+
sage: S.fundamental_group()
823+
Finitely presented group < e0 | e0^2 >
824+
"""
825+
O = AbstractSimplex(0)
826+
SO = O.apply_degeneracies(0)
827+
edges = {g: AbstractSimplex(1, name=str(g)) for g in G.gens()}
828+
inverseedges = {g.inverse(): AbstractSimplex(1, name=str(g.inverse())) for g in G.gens()}
829+
all_edges = {}
830+
all_edges.update(edges)
831+
all_edges.update(inverseedges)
832+
triangles = {g: AbstractSimplex(2, name='T' + str(g)) for g in G.gens()}
833+
face_maps = {g: [O, O] for g in all_edges.values()}
834+
face_maps.update({triangles[t]: [all_edges[t], SO, all_edges[t.inverse()]] for t in triangles})
835+
for r in G.relations():
836+
if len(r.Tietze()) == 1:
837+
pass
838+
elif len(r.Tietze()) == 2:
839+
a = all_edges[G([r.Tietze()[0]])]
840+
b = all_edges[G([r.Tietze()[1]])]
841+
T = AbstractSimplex(2, name=str(r))
842+
face_maps[T] = [a, SO, b]
843+
else:
844+
words = [all_edges[G([a])] for a in r.Tietze()]
845+
words[-1] = all_edges[G([-r.Tietze()[-1]])]
846+
while len(words) > 3:
847+
auxedge = AbstractSimplex(1)
848+
face_maps[auxedge] = [O, O]
849+
auxtring = AbstractSimplex(2)
850+
face_maps[auxtring] = [words[1], auxedge, words[0]]
851+
words = [auxedge] + words[2:]
852+
auxtring = AbstractSimplex(2)
853+
face_maps[auxtring] = [words[1], words[2], words[0]]
854+
return SimplicialSet_finite(face_maps, base_point=O)

0 commit comments

Comments
 (0)