passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,2954 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs
3
+ r"""
4
+ Methods of constructing simplicial sets
5
+
6
+ This implements various constructions on simplicial sets:
7
+ subsimplicial sets, pullbacks, products, pushouts, quotients, wedges,
8
+ disjoint unions, smash products, cones, and suspensions. The best way
9
+ to access these is with methods attached to simplicial sets
10
+ themselves, as in the following.
11
+
12
+ EXAMPLES::
13
+
14
+ sage: K = simplicial_sets.Simplex(1)
15
+ sage: square = K.product(K)
16
+
17
+ sage: K = simplicial_sets.Simplex(1)
18
+ sage: endpoints = K.n_skeleton(0)
19
+ sage: circle = K.quotient(endpoints)
20
+
21
+ The mapping cone of a morphism of simplicial sets is constructed as a
22
+ pushout::
23
+
24
+ sage: eta = simplicial_sets.HopfMap()
25
+ sage: CP2 = eta.mapping_cone()
26
+ sage: type(CP2)
27
+ <class 'sage.topology.simplicial_set_constructions.PushoutOfSimplicialSets_finite_with_category'>
28
+
29
+ See the main documentation for simplicial sets, as well as for the
30
+ classes for pushouts, pullbacks, etc., for more details.
31
+
32
+ Many of the classes defined here inherit from
33
+ :class:`sage.structure.unique_representation.UniqueRepresentation`. This
34
+ means that they produce identical output if given the same input, so
35
+ for example, if ``K`` is a simplicial set, calling ``K.suspension()``
36
+ twice returns the same result both times::
37
+
38
+ sage: CP2.suspension() is CP2.suspension()
39
+ True
40
+
41
+ So on one hand, a command like ``simplicial_sets.Sphere(2)``
42
+ constructs a distinct copy of a 2-sphere each time it is called; on
43
+ the other, once you have constructed a 2-sphere, then constructing its
44
+ cone, its suspension, its product with another simplicial set, etc.,
45
+ will give you the same result each time::
46
+
47
+ sage: simplicial_sets.Sphere(2) == simplicial_sets.Sphere(2)
48
+ False
49
+ sage: S2 = simplicial_sets.Sphere(2)
50
+ sage: S2.product(S2) == S2.product(S2)
51
+ True
52
+ sage: S2.disjoint_union(CP2, S2) == S2.disjoint_union(CP2, S2)
53
+ True
54
+
55
+ AUTHORS:
56
+
57
+ - John H. Palmieri (2016-07)
58
+ """
59
+ # ****************************************************************************
60
+ # Copyright (C) 2016 John H. Palmieri <palmieri at math.washington.edu>
61
+ #
62
+ # Distributed under the terms of the GNU General Public License (GPL)
63
+ # https://www.gnu.org/licenses/
64
+ #
65
+ # This code is distributed in the hope that it will be useful,
66
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
67
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
68
+ #
69
+ # See the GNU General Public License for more details; the full text
70
+ # is available at:
71
+ #
72
+ # https://www.gnu.org/licenses/
73
+ #
74
+ # ****************************************************************************
75
+
76
+ import itertools
77
+
78
+ from sage.misc.latex import latex
79
+ from sage.sets.set import Set
80
+ from sage.structure.parent import Parent
81
+ from sage.structure.unique_representation import UniqueRepresentation
82
+
83
+ from .simplicial_set import AbstractSimplex, \
84
+ SimplicialSet_arbitrary, SimplicialSet_finite, \
85
+ standardize_degeneracies, face_degeneracies
86
+ from .simplicial_set_examples import Empty, Point
87
+
88
+ from sage.misc.lazy_import import lazy_import
89
+ lazy_import('sage.categories.simplicial_sets', 'SimplicialSets')
90
+
91
+
92
+ ########################################################################
93
+ # classes which inherit from SimplicialSet_arbitrary
94
+
95
+ # Note: many of the classes below for infinite simplicial sets have an
96
+ # attribute '_n_skeleton'. This is used to cache the highest
97
+ # dimensional skeleton calculated so far for this simplicial set,
98
+ # along with its dimension, so for example, the starting value is
99
+ # often (-1, Empty()): the (-1)-skeleton is the empty simplicial
100
+ # set. It gets used and updated in the n_skeleton method.
101
+
102
+ class SubSimplicialSet(SimplicialSet_finite, UniqueRepresentation):
103
+ @staticmethod
104
+ def __classcall__(self, data, ambient=None):
105
+ """
106
+ Convert ``data`` from a dict to a tuple.
107
+
108
+ TESTS::
109
+
110
+ sage: from sage.topology.simplicial_set_constructions import SubSimplicialSet
111
+ sage: K = simplicial_sets.Simplex(2)
112
+ sage: e = K.n_cells(1)[0]
113
+ sage: A = SubSimplicialSet({e: K.faces(e)}, ambient=K)
114
+ sage: B = SubSimplicialSet({e: list(K.faces(e))}, ambient=K)
115
+ sage: A == B
116
+ True
117
+ """
118
+ L = []
119
+ for x in data:
120
+ if data[x] is None:
121
+ L.append((x, None))
122
+ else:
123
+ L.append((x, tuple(data[x])))
124
+ return super().__classcall__(self, tuple(L), ambient)
125
+
126
+ def __init__(self, data, ambient=None):
127
+ r"""
128
+ Return a finite simplicial set as a subsimplicial set of another
129
+ simplicial set.
130
+
131
+ This keeps track of the ambient simplicial set and the
132
+ inclusion map from the subcomplex into it.
133
+
134
+ INPUT:
135
+
136
+ - ``data`` -- the data defining the subset: a dictionary where
137
+ the keys are simplices from the ambient simplicial set and
138
+ the values are their faces.
139
+
140
+ - ``ambient`` -- the ambient simplicial set. If omitted, use
141
+ the same simplicial set as the subset and the ambient
142
+ complex.
143
+
144
+ EXAMPLES::
145
+
146
+ sage: S3 = simplicial_sets.Sphere(3)
147
+ sage: K = simplicial_sets.KleinBottle()
148
+ sage: X = S3.disjoint_union(K)
149
+ sage: Y = X.structure_map(0).image() # the S3 summand
150
+ sage: Y.inclusion_map()
151
+ Simplicial set morphism:
152
+ From: Simplicial set with 2 non-degenerate simplices
153
+ To: Disjoint union: (S^3 u Klein bottle)
154
+ Defn: [v_0, sigma_3] --> [v_0, sigma_3]
155
+ sage: Y.ambient_space()
156
+ Disjoint union: (S^3 u Klein bottle)
157
+
158
+ TESTS::
159
+
160
+ sage: T = simplicial_sets.Torus()
161
+ sage: latex(T.n_skeleton(2))
162
+ S^{1} \times S^{1}
163
+
164
+ sage: T.n_skeleton(1).n_skeleton(1) == T.n_skeleton(1)
165
+ True
166
+
167
+ sage: T.n_skeleton(1) is T.n_skeleton(1)
168
+ True
169
+ """
170
+ data = dict(data)
171
+ if ambient is None:
172
+ ambient = self
173
+ if (ambient.is_pointed()
174
+ and hasattr(ambient, '_basepoint')
175
+ and ambient.base_point() in data):
176
+ SimplicialSet_finite.__init__(self, data, base_point=ambient.base_point())
177
+ else:
178
+ SimplicialSet_finite.__init__(self, data)
179
+ if self == ambient:
180
+ self.rename(ambient.get_custom_name())
181
+ self._latex_name = latex(ambient)
182
+ # When constructing the inclusion map, we do not need to check
183
+ # the validity of the morphism, and more importantly, we
184
+ # cannot check it in the infinite case: the appropriate data
185
+ # may not have yet been constructed. So use "check=False".
186
+ self._inclusion = self.Hom(ambient)({x: x for x in data}, check=False)
187
+
188
+ def inclusion_map(self):
189
+ r"""
190
+ Return the inclusion map from this subsimplicial set into its
191
+ ambient space.
192
+
193
+ EXAMPLES::
194
+
195
+ sage: RP6 = simplicial_sets.RealProjectiveSpace(6) # needs sage.groups
196
+ sage: K = RP6.n_skeleton(2) # needs sage.groups
197
+ sage: K.inclusion_map() # needs sage.groups
198
+ Simplicial set morphism:
199
+ From: Simplicial set with 3 non-degenerate simplices
200
+ To: RP^6
201
+ Defn: [1, f, f * f] --> [1, f, f * f]
202
+
203
+ `RP^6` itself is constructed as a subsimplicial set of
204
+ `RP^\infty`::
205
+
206
+ sage: latex(RP6.inclusion_map()) # needs sage.groups
207
+ RP^{6} \to RP^{\infty}
208
+ """
209
+ return self._inclusion
210
+
211
+ def ambient_space(self):
212
+ """
213
+ Return the simplicial set of which this is a subsimplicial set.
214
+
215
+ EXAMPLES::
216
+
217
+ sage: T = simplicial_sets.Torus()
218
+ sage: eight = T.wedge_as_subset()
219
+ sage: eight
220
+ Simplicial set with 3 non-degenerate simplices
221
+ sage: eight.fundamental_group() # needs sage.groups
222
+ Finitely presented group < e0, e1 | >
223
+ sage: eight.ambient_space()
224
+ Torus
225
+ """
226
+ return self._inclusion.codomain()
227
+
228
+
229
+ class PullbackOfSimplicialSets(SimplicialSet_arbitrary, UniqueRepresentation):
230
+ @staticmethod
231
+ def __classcall_private__(self, maps=None):
232
+ """
233
+ TESTS::
234
+
235
+ sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets
236
+ sage: S2 = simplicial_sets.Sphere(2)
237
+ sage: one = S2.Hom(S2).identity()
238
+ sage: PullbackOfSimplicialSets([one, one]) == PullbackOfSimplicialSets((one, one))
239
+ True
240
+ """
241
+ if maps:
242
+ return super().__classcall__(self, tuple(maps))
243
+ return super().__classcall__(self)
244
+
245
+ def __init__(self, maps=None):
246
+ r"""
247
+ Return the pullback obtained from the morphisms ``maps``.
248
+
249
+ INPUT:
250
+
251
+ - ``maps`` -- list or tuple of morphisms of simplicial sets
252
+
253
+ If only a single map `f: X \to Y` is given, then return
254
+ `X`. If no maps are given, return the one-point simplicial
255
+ set. Otherwise, given a simplicial set `Y` and maps `f_i: X_i
256
+ \to Y` for `0 \leq i \leq m`, construct the pullback `P`: see
257
+ :wikipedia:`Pullback_(category_theory)`. This is constructed
258
+ as pullbacks of sets for each set of `n`-simplices, so `P_n`
259
+ is the subset of the product `\prod (X_i)_n` consisting of
260
+ those elements `(x_i)` for which `f_i(x_i) = f_j(x_j)` for all
261
+ `i`, `j`.
262
+
263
+ This is pointed if the maps `f_i` are.
264
+
265
+ EXAMPLES:
266
+
267
+ The pullback of a quotient map by a subsimplicial set and the
268
+ base point map gives a simplicial set isomorphic to the
269
+ original subcomplex::
270
+
271
+ sage: # needs sage.groups
272
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
273
+ sage: K = RP5.quotient(RP5.n_skeleton(2))
274
+ sage: X = K.pullback(K.quotient_map(), K.base_point_map())
275
+ sage: X.homology() == RP5.n_skeleton(2).homology() # needs sage.modules
276
+ True
277
+
278
+ Pullbacks of identity maps::
279
+
280
+ sage: S2 = simplicial_sets.Sphere(2)
281
+ sage: one = S2.Hom(S2).identity()
282
+ sage: P = S2.pullback(one, one)
283
+ sage: P.homology() # needs sage.modules
284
+ {0: 0, 1: 0, 2: Z}
285
+
286
+ The pullback is constructed in terms of the product -- of
287
+ course, the product is a special case of the pullback -- and
288
+ the simplices are named appropriately::
289
+
290
+ sage: P.nondegenerate_simplices()
291
+ [(v_0, v_0), (sigma_2, sigma_2)]
292
+ """
293
+ # Import this here to prevent circular imports.
294
+ from sage.topology.simplicial_set_morphism import SimplicialSetMorphism
295
+ if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps):
296
+ raise ValueError('the maps must be morphisms of simplicial sets')
297
+
298
+ Cat = SimplicialSets()
299
+ if maps:
300
+ if all(f.domain().is_finite() for f in maps):
301
+ Cat = Cat.Finite()
302
+ if all(f.is_pointed() for f in maps):
303
+ Cat = Cat.Pointed()
304
+ Parent.__init__(self, category=Cat)
305
+ self._maps = maps
306
+ self._n_skeleton = (-1, Empty())
307
+
308
+ def n_skeleton(self, n):
309
+ """
310
+ Return the `n`-skeleton of this simplicial set.
311
+
312
+ That is, the simplicial set generated by all nondegenerate
313
+ simplices of dimension at most `n`.
314
+
315
+ INPUT:
316
+
317
+ - ``n`` -- the dimension
318
+
319
+ The `n`-skeleton of the pullback is computed as the pullback
320
+ of the `n`-skeleta of the component simplicial sets.
321
+
322
+ EXAMPLES::
323
+
324
+ sage: # needs sage.groups
325
+ sage: G = groups.misc.MultiplicativeAbelian([2])
326
+ sage: B = simplicial_sets.ClassifyingSpace(G)
327
+ sage: one = Hom(B,B).identity()
328
+ sage: c = Hom(B,B).constant_map()
329
+ sage: P = B.pullback(one, c)
330
+ sage: P.n_skeleton(2)
331
+ Pullback of maps:
332
+ Simplicial set endomorphism of Simplicial set with 3 non-degenerate simplices
333
+ Defn: Identity map
334
+ Simplicial set endomorphism of Simplicial set with 3 non-degenerate simplices
335
+ Defn: Constant map at 1
336
+ sage: P.n_skeleton(3).homology() # needs sage.modules
337
+ {0: 0, 1: C2, 2: 0, 3: Z}
338
+ """
339
+ if self.is_finite():
340
+ maps = self._maps
341
+ if maps:
342
+ codomain = SimplicialSet_finite.n_skeleton(maps[0].codomain(), n)
343
+ domains = [SimplicialSet_finite.n_skeleton(f.domain(), n) for f in maps]
344
+ new_maps = [f.n_skeleton(n, d, codomain) for (f, d) in zip(maps, domains)]
345
+ return PullbackOfSimplicialSets_finite(new_maps)
346
+ return PullbackOfSimplicialSets_finite(maps)
347
+ start, skel = self._n_skeleton
348
+ if start == n:
349
+ return skel
350
+ elif start > n:
351
+ return skel.n_skeleton(n)
352
+ ans = PullbackOfSimplicialSets_finite([f.n_skeleton(n) for f in self._maps])
353
+ self._n_skeleton = (n, ans)
354
+ return ans
355
+
356
+ def defining_map(self, i):
357
+ r"""
358
+ Return the `i`-th map defining the pullback.
359
+
360
+ INPUT:
361
+
362
+ - ``i`` -- integer
363
+
364
+ If this pullback was constructed as ``Y.pullback(f_0, f_1, ...)``,
365
+ this returns `f_i`.
366
+
367
+ EXAMPLES::
368
+
369
+ sage: # needs sage.groups
370
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
371
+ sage: K = RP5.quotient(RP5.n_skeleton(2))
372
+ sage: Y = K.pullback(K.quotient_map(), K.base_point_map())
373
+ sage: Y.defining_map(1)
374
+ Simplicial set morphism:
375
+ From: Point
376
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
377
+ Defn: Constant map at *
378
+ sage: Y.defining_map(0).domain()
379
+ RP^5
380
+ """
381
+ return self._maps[i]
382
+
383
+ def _repr_(self):
384
+ """
385
+ Print representation.
386
+
387
+ EXAMPLES::
388
+
389
+ sage: S3 = simplicial_sets.Sphere(3)
390
+ sage: c = Hom(S3,S3).constant_map()
391
+ sage: one = Hom(S3, S3).identity()
392
+ sage: S3.pullback(c, one)
393
+ Pullback of maps:
394
+ Simplicial set endomorphism of S^3
395
+ Defn: Constant map at v_0
396
+ Simplicial set endomorphism of S^3
397
+ Defn: Identity map
398
+ """
399
+ if not self._maps:
400
+ return 'Point'
401
+ s = 'Pullback of maps:'
402
+ for f in self._maps:
403
+ t = '\n' + str(f)
404
+ s += t.replace('\n', '\n ')
405
+ return s
406
+
407
+
408
+ class PullbackOfSimplicialSets_finite(PullbackOfSimplicialSets, SimplicialSet_finite):
409
+ """
410
+ The pullback of finite simplicial sets obtained from ``maps``.
411
+
412
+ When the simplicial sets involved are all finite, there are more
413
+ methods available to the resulting pullback, as compared to case
414
+ when some of the components are infinite: the structure maps from
415
+ the pullback and the pullback's universal property: see
416
+ :meth:`structure_map` and :meth:`universal_property`.
417
+ """
418
+ @staticmethod
419
+ def __classcall_private__(self, maps=None):
420
+ """
421
+ TESTS::
422
+
423
+ sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets_finite
424
+ sage: S2 = simplicial_sets.Sphere(2)
425
+ sage: one = S2.Hom(S2).identity()
426
+ sage: PullbackOfSimplicialSets_finite([one, one]) == PullbackOfSimplicialSets_finite((one, one))
427
+ True
428
+ """
429
+ if maps:
430
+ return super().__classcall__(self, tuple(maps))
431
+ return super().__classcall__(self)
432
+
433
+ def __init__(self, maps=None):
434
+ r"""
435
+ Return the pullback obtained from the morphisms ``maps``.
436
+
437
+ See :class:`PullbackOfSimplicialSets` for more information.
438
+
439
+ INPUT:
440
+
441
+ - ``maps`` -- list or tuple of morphisms of simplicial sets
442
+
443
+ EXAMPLES::
444
+
445
+ sage: eta = simplicial_sets.HopfMap()
446
+ sage: S3 = eta.domain()
447
+ sage: S2 = eta.codomain()
448
+ sage: c = Hom(S2,S2).constant_map()
449
+ sage: S2.pullback(eta, c).is_finite()
450
+ True
451
+
452
+ sage: # needs sage.groups
453
+ sage: G = groups.misc.MultiplicativeAbelian([2])
454
+ sage: B = simplicial_sets.ClassifyingSpace(G)
455
+ sage: one = Hom(B,B).identity()
456
+ sage: c = Hom(B,B).constant_map()
457
+ sage: B.pullback(one, c).is_finite()
458
+ False
459
+
460
+ TESTS::
461
+
462
+ sage: P = simplicial_sets.Point()
463
+ sage: P.pullback(P.constant_map(), P.constant_map())
464
+ Pullback of maps:
465
+ Simplicial set endomorphism of Point
466
+ Defn: Identity map
467
+ Simplicial set endomorphism of Point
468
+ Defn: Identity map
469
+ """
470
+ # Import this here to prevent circular imports.
471
+ from sage.topology.simplicial_set_morphism import SimplicialSetMorphism
472
+ if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps):
473
+ raise ValueError('the maps must be morphisms of simplicial sets')
474
+ if not maps:
475
+ star = AbstractSimplex(0, name='*')
476
+ SimplicialSet_finite.__init__(self, {star: None}, base_point=star, name='Point')
477
+ self._maps = ()
478
+ self._translation = {}
479
+ return
480
+ if len(maps) == 1:
481
+ f = maps[0]
482
+ if f.is_pointed():
483
+ SimplicialSet_finite.__init__(self, f.domain().face_data(),
484
+ base_point=f.domain().base_point())
485
+ else:
486
+ SimplicialSet_finite.__init__(self, f.domain().face_data())
487
+ self._maps = (f,)
488
+ return
489
+ codomain = maps[0].codomain()
490
+ if any(codomain != f.codomain() for f in maps[1:]):
491
+ raise ValueError('the codomains of the maps must be equal')
492
+ # Now construct the pullback by constructing the product and only
493
+ # keeping the appropriate simplices.
494
+ domains = [f.domain() for f in maps]
495
+ nondegen = [X.nondegenerate_simplices() for X in domains]
496
+ data_factors = [X.face_data() for X in domains]
497
+ # data: dictionary to construct the new simplicial set.
498
+ data = {}
499
+ # translate: keep track of the nondegenerate simplices in the
500
+ # new simplicial set for computing faces: keys are tuples of
501
+ # pairs (sigma, degens), with sigma a nondegenerate simplex in
502
+ # one of the factors, degens the tuple of applied
503
+ # degeneracies. The associated value is the actual simplex in
504
+ # the product.
505
+ translate = {}
506
+ for simplices in itertools.product(*nondegen):
507
+ dims = [s.dimension() for s in simplices]
508
+ dim_max = max(dims)
509
+ sum_dims = sum(dims)
510
+ for d in range(dim_max, sum_dims + 1):
511
+ S = Set(range(d))
512
+ # Is there a way to speed up the following? Given the
513
+ # tuple dims=(n_1, n_2, ..., n_k) and given d between
514
+ # max(dims) and sum(dims), we are trying to construct
515
+ # k-tuples of subsets (D_1, D_2, ..., D_k) of range(d)
516
+ # such that the intersection of all of the D_i's is
517
+ # empty.
518
+ for I in itertools.product(*[S.subsets(d - _) for _ in dims]):
519
+ if set.intersection(*[set(_) for _ in I]):
520
+ # To get a nondegenerate face, can't have a
521
+ # degeneracy in common for all the factors.
522
+ continue
523
+ degens = [tuple(sorted(_, reverse=True)) for _ in I]
524
+
525
+ sigma = simplices[0].apply_degeneracies(*degens[0])
526
+ target = maps[0](sigma)
527
+ if any(target != f(tau.apply_degeneracies(*degen))
528
+ for (f, tau, degen) in zip(maps[1:], simplices[1:], degens[1:])):
529
+ continue
530
+
531
+ simplex_factors = tuple(zip(simplices, tuple(degens)))
532
+ s = '(' + ', '.join('{}'.format(_[0].apply_degeneracies(*_[1]))
533
+ for _ in simplex_factors) + ')'
534
+ ls = '(' + ', '.join('{}'.format(latex(_[0].apply_degeneracies(*_[1])))
535
+ for _ in simplex_factors) + ')'
536
+ simplex = AbstractSimplex(d, name=s, latex_name=ls)
537
+ translate[simplex_factors] = simplex
538
+ # Now compute the faces of simplex.
539
+ if d == 0:
540
+ # It's a vertex, so it has no faces.
541
+ faces = None
542
+ else:
543
+ faces = []
544
+ for i in range(d+1):
545
+ # Compute d_i on simplex.
546
+ #
547
+ # face_degens: tuple of pairs (J, t): J is the
548
+ # list of degeneracies to apply to the
549
+ # corresponding entry in simplex_factors, t is
550
+ # the face map to apply.
551
+ face_degens = [face_degeneracies(i, _) for _ in degens]
552
+ face_factors = []
553
+ new_degens = []
554
+ for x, Face, face_dict in zip(simplices, face_degens, data_factors):
555
+ J = Face[0]
556
+ t = Face[1]
557
+ if t is None:
558
+ face_factors.append(x.nondegenerate())
559
+ else:
560
+ underlying = face_dict[x][t]
561
+ temp_degens = underlying.degeneracies()
562
+ underlying = underlying.nondegenerate()
563
+ J = standardize_degeneracies(*(J + list(temp_degens)))
564
+ face_factors.append(underlying)
565
+ new_degens.append(J)
566
+
567
+ # By the simplicial identities, s_{i_1}
568
+ # s_{i_2} ... s_{i_n} z (if decreasing) is in
569
+ # the image of s_{i_k} for each k.
570
+ #
571
+ # So find the intersection K of each J, the
572
+ # degeneracies applied to left_face and
573
+ # right_face. Then the face will be s_{K}
574
+ # (s_{J'_L} left_face, s_{J'_R} right_face),
575
+ # where you get J'_L from J_L by pulling out K
576
+ # from J_L.
577
+ #
578
+ # J'_L is obtained as follows: for each j in
579
+ # J_L, decrease j by q if q = #{x in K: x < j}
580
+ K = set.intersection(*[set(J) for J in new_degens])
581
+
582
+ face_degens = []
583
+ for J in new_degens:
584
+ new_J = []
585
+ for j in J:
586
+ if j not in K:
587
+ q = len([x for x in K if x < j])
588
+ new_J.append(j - q)
589
+ face_degens.append(tuple(new_J))
590
+ K = sorted(K, reverse=True)
591
+ underlying_face = translate[tuple(zip(tuple(face_factors), tuple(face_degens)))]
592
+ faces.append(underlying_face.apply_degeneracies(*K))
593
+ data[simplex] = faces
594
+
595
+ if all(f.is_pointed() for f in maps):
596
+ basept = translate[tuple([(sset.base_point(), ()) for sset in domains])]
597
+ if not data:
598
+ data = {basept: None}
599
+ SimplicialSet_finite.__init__(self, data, base_point=basept)
600
+ else:
601
+ SimplicialSet_finite.__init__(self, data)
602
+ self._maps = maps
603
+ # self._translation: tuple converted from dict. keys: tuples
604
+ # of pairs (sigma, degens), with sigma a nondegenerate simplex
605
+ # in one of the factors, degens the tuple of applied
606
+ # degeneracies. The associated value is the actual simplex in
607
+ # the product.
608
+ self._translation = tuple(translate.items())
609
+
610
+ def structure_map(self, i):
611
+ r"""
612
+ Return the `i`-th projection map of the pullback.
613
+
614
+ INPUT:
615
+
616
+ - ``i`` -- integer
617
+
618
+ If this pullback `P` was constructed as ``Y.pullback(f_0, f_1,
619
+ ...)``, where `f_i: X_i \to Y`, then there are structure maps
620
+ `\bar{f}_i: P \to X_i`. This method constructs `\bar{f}_i`.
621
+
622
+ EXAMPLES::
623
+
624
+ sage: # needs sage.groups
625
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
626
+ sage: K = RP5.quotient(RP5.n_skeleton(2))
627
+ sage: Y = K.pullback(K.quotient_map(), K.base_point_map())
628
+ sage: Y.structure_map(0)
629
+ Simplicial set morphism:
630
+ From: Pullback of maps:
631
+ Simplicial set morphism:
632
+ From: RP^5
633
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
634
+ Defn: [1, f, f * f, f * f * f, f * f * f * f, f * f * f * f * f]
635
+ --> [*, s_0 *, s_1 s_0 *, f * f * f, f * f * f * f, f * f * f * f * f]
636
+ Simplicial set morphism:
637
+ From: Point
638
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
639
+ Defn: Constant map at *
640
+ To: RP^5
641
+ Defn: [(1, *), (f, s_0 *), (f * f, s_1 s_0 *)] --> [1, f, f * f]
642
+ sage: Y.structure_map(1).codomain()
643
+ Point
644
+
645
+ These maps are also accessible via :meth:`projection_map`::
646
+
647
+ sage: Y.projection_map(1).codomain() # needs sage.groups
648
+ Point
649
+ """
650
+ if len(self._maps) == 1:
651
+ return self.Hom(self).identity()
652
+ f = {}
653
+ for x in self._translation:
654
+ f[x[1]] = x[0][i][0].apply_degeneracies(*x[0][i][1])
655
+ codomain = self.defining_map(i).domain()
656
+ return self.Hom(codomain)(f)
657
+
658
+ projection_map = structure_map
659
+
660
+ def universal_property(self, *maps):
661
+ r"""
662
+ Return the map induced by ``maps``.
663
+
664
+ INPUT:
665
+
666
+ - ``maps`` -- maps from a simplicial set `Z` to the "factors"
667
+ `X_i` forming the pullback
668
+
669
+ If the pullback `P` is formed by maps `f_i: X_i \to Y`, then
670
+ given maps `g_i: Z \to X_i` such that `f_i g_i = f_j g_j` for
671
+ all `i`, `j`, then there is a unique map `g: Z \to P` making
672
+ the appropriate diagram commute. This constructs that map.
673
+
674
+ EXAMPLES::
675
+
676
+ sage: S1 = simplicial_sets.Sphere(1)
677
+ sage: T = S1.product(S1)
678
+ sage: K = T.factor(0, as_subset=True)
679
+ sage: f = S1.Hom(T)({S1.n_cells(0)[0]: K.n_cells(0)[0],
680
+ ....: S1.n_cells(1)[0]: K.n_cells(1)[0]})
681
+ sage: P = S1.product(T)
682
+ sage: P.universal_property(S1.Hom(S1).identity(), f)
683
+ Simplicial set morphism:
684
+ From: S^1
685
+ To: S^1 x S^1 x S^1
686
+ Defn: [v_0, sigma_1] --> [(v_0, (v_0, v_0)), (sigma_1, (sigma_1, s_0 v_0))]
687
+ """
688
+ if len(self._maps) != len(maps):
689
+ raise ValueError('wrong number of maps specified')
690
+ if len(self._maps) == 1:
691
+ return maps[0]
692
+ domain = maps[0].domain()
693
+ if any(g.domain() != domain for g in maps[1:]):
694
+ raise ValueError('the maps do not all have the same codomain')
695
+ composite = self._maps[0] * maps[0]
696
+ if any(f*g != composite for f, g in zip(self._maps[1:], maps[1:])):
697
+ raise ValueError('the maps are not compatible')
698
+ data = {}
699
+ translate = dict(self._translation)
700
+ for sigma in domain.nondegenerate_simplices():
701
+ target = tuple([(f(sigma).nondegenerate(), tuple(f(sigma).degeneracies()))
702
+ for f in maps])
703
+ # If there any degeneracies in common, remove them: the
704
+ # dictionary "translate" has nondegenerate simplices as
705
+ # its keys.
706
+ in_common = set.intersection(*[set(_[1]) for _ in target])
707
+ if in_common:
708
+ target = tuple((tau, tuple(sorted(set(degens).difference(in_common),
709
+ reverse=True)))
710
+ for tau, degens in target)
711
+ in_common = sorted(in_common, reverse=True)
712
+ data[sigma] = translate[target].apply_degeneracies(*in_common)
713
+ return domain.Hom(self)(data)
714
+
715
+
716
+ class Factors:
717
+ """
718
+ Classes which inherit from this should define a ``_factors``
719
+ attribute for their instances, and this class accesses that
720
+ attribute. This is used by :class:`ProductOfSimplicialSets`,
721
+ :class:`WedgeOfSimplicialSets`, and
722
+ :class:`DisjointUnionOfSimplicialSets`.
723
+ """
724
+ def factors(self):
725
+ """
726
+ Return the factors involved in this construction of simplicial sets.
727
+
728
+ EXAMPLES::
729
+
730
+ sage: S2 = simplicial_sets.Sphere(2)
731
+ sage: S3 = simplicial_sets.Sphere(3)
732
+ sage: S2.wedge(S3).factors() == (S2, S3)
733
+ True
734
+ sage: S2.product(S3).factors()[0]
735
+ S^2
736
+ """
737
+ return self._factors
738
+
739
+ def factor(self, i):
740
+ r"""
741
+ Return the `i`-th factor of this construction of simplicial sets.
742
+
743
+ INPUT:
744
+
745
+ - ``i`` -- integer; the index of the factor
746
+
747
+ EXAMPLES::
748
+
749
+ sage: S2 = simplicial_sets.Sphere(2)
750
+ sage: S3 = simplicial_sets.Sphere(3)
751
+ sage: K = S2.disjoint_union(S3)
752
+ sage: K.factor(0)
753
+ S^2
754
+
755
+ sage: # needs sage.groups
756
+ sage: G = groups.misc.MultiplicativeAbelian([2])
757
+ sage: B = simplicial_sets.ClassifyingSpace(G)
758
+ sage: X = B.wedge(S3, B)
759
+ sage: X.factor(1)
760
+ S^3
761
+ sage: X.factor(2)
762
+ Classifying space of Multiplicative Abelian group isomorphic to C2
763
+ """
764
+ return self.factors()[i]
765
+
766
+
767
+ class ProductOfSimplicialSets(PullbackOfSimplicialSets, Factors):
768
+ @staticmethod
769
+ def __classcall__(cls, factors=None):
770
+ """
771
+ TESTS::
772
+
773
+ sage: from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets
774
+ sage: S2 = simplicial_sets.Sphere(2)
775
+ sage: ProductOfSimplicialSets([S2, S2]) == ProductOfSimplicialSets((S2, S2))
776
+ True
777
+ """
778
+ if factors:
779
+ return super().__classcall__(cls, factors=tuple(factors))
780
+ return super().__classcall__(cls)
781
+
782
+ def __init__(self, factors=None):
783
+ r"""
784
+ Return the product of simplicial sets.
785
+
786
+ INPUT:
787
+
788
+ - ``factors`` -- list or tuple of simplicial sets
789
+
790
+ Return the product of the simplicial sets in ``factors``.
791
+
792
+ If `X` and `Y` are simplicial sets, then their product `X
793
+ \times Y` is defined to be the simplicial set with
794
+ `n`-simplices `X_n \times Y_n`. Therefore the simplices in
795
+ the product have the form `(s_I \sigma, s_J \tau)`, where `s_I
796
+ = s_{i_1} ... s_{i_p}` and `s_J = s_{j_1} ... s_{j_q}` are
797
+ composites of degeneracy maps, written in decreasing order.
798
+ Such a simplex is nondegenerate if the indices `I` and `J` are
799
+ disjoint. Therefore if `\sigma` and `\tau` are nondegenerate
800
+ simplices of dimensions `m` and `n`, in the product they will
801
+ lead to nondegenerate simplices up to dimension `m+n`, and no
802
+ further.
803
+
804
+ This extends in the more or less obvious way to products with
805
+ more than two factors: with three factors, a simplex `(s_I
806
+ \sigma, s_J \tau, s_K \rho)` is nondegenerate if `I \cap J
807
+ \cap K` is empty, etc.
808
+
809
+ If a simplicial set is constructed as a product, the factors
810
+ are recorded and are accessible via the method
811
+ :meth:`Factors.factors`. If it is constructed as a product and then
812
+ copied, this information is lost.
813
+
814
+ EXAMPLES::
815
+
816
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
817
+ sage: v = AbstractSimplex(0, name='v')
818
+ sage: w = AbstractSimplex(0, name='w')
819
+ sage: e = AbstractSimplex(1, name='e')
820
+ sage: X = SimplicialSet({e: (v, w)})
821
+ sage: square = X.product(X)
822
+
823
+ ``square`` is now the standard triangulation of the square: 4
824
+ vertices, 5 edges (the four on the border plus the diagonal),
825
+ 2 triangles::
826
+
827
+ sage: square.f_vector()
828
+ [4, 5, 2]
829
+
830
+ sage: S1 = simplicial_sets.Sphere(1)
831
+ sage: T = S1.product(S1)
832
+ sage: T.homology(reduced=False) # needs sage.modules
833
+ {0: Z, 1: Z x Z, 2: Z}
834
+
835
+ Since ``S1`` is pointed, so is ``T``::
836
+
837
+ sage: S1.is_pointed()
838
+ True
839
+ sage: S1.base_point()
840
+ v_0
841
+ sage: T.is_pointed()
842
+ True
843
+ sage: T.base_point()
844
+ (v_0, v_0)
845
+
846
+ sage: S2 = simplicial_sets.Sphere(2)
847
+ sage: S3 = simplicial_sets.Sphere(3)
848
+ sage: Z = S2.product(S3)
849
+ sage: Z.homology() # needs sage.modules
850
+ {0: 0, 1: 0, 2: Z, 3: Z, 4: 0, 5: Z}
851
+
852
+ Products involving infinite simplicial sets::
853
+
854
+ sage: # needs sage.groups
855
+ sage: G = groups.misc.MultiplicativeAbelian([2])
856
+ sage: B = simplicial_sets.ClassifyingSpace(G)
857
+ sage: B.rename('RP^oo')
858
+ sage: X = B.product(B); X
859
+ RP^oo x RP^oo
860
+ sage: X.n_cells(1)
861
+ [(f, f), (f, s_0 1), (s_0 1, f)]
862
+ sage: X.homology(range(3), base_ring=GF(2)) # needs sage.modules
863
+ {0: Vector space of dimension 0 over Finite Field of size 2,
864
+ 1: Vector space of dimension 2 over Finite Field of size 2,
865
+ 2: Vector space of dimension 3 over Finite Field of size 2}
866
+ sage: Y = B.product(S2)
867
+ sage: Y.homology(range(5), base_ring=GF(2)) # needs sage.modules
868
+ {0: Vector space of dimension 0 over Finite Field of size 2,
869
+ 1: Vector space of dimension 1 over Finite Field of size 2,
870
+ 2: Vector space of dimension 2 over Finite Field of size 2,
871
+ 3: Vector space of dimension 2 over Finite Field of size 2,
872
+ 4: Vector space of dimension 2 over Finite Field of size 2}
873
+ """
874
+ PullbackOfSimplicialSets.__init__(self, [space.constant_map()
875
+ for space in factors])
876
+ self._factors = factors
877
+
878
+ def n_skeleton(self, n):
879
+ """
880
+ Return the `n`-skeleton of this simplicial set.
881
+
882
+ That is, the simplicial set generated by all nondegenerate
883
+ simplices of dimension at most `n`.
884
+
885
+ INPUT:
886
+
887
+ - ``n`` -- the dimension
888
+
889
+ In the finite case, this returns the ordinary `n`-skeleton. In
890
+ the infinite case, it computes the `n`-skeleton of the product
891
+ of the `n`-skeleta of the factors.
892
+
893
+ EXAMPLES::
894
+
895
+ sage: S2 = simplicial_sets.Sphere(2)
896
+ sage: S3 = simplicial_sets.Sphere(3)
897
+ sage: S2.product(S3).n_skeleton(2)
898
+ Simplicial set with 2 non-degenerate simplices
899
+
900
+ sage: # needs sage.groups
901
+ sage: G = groups.misc.MultiplicativeAbelian([2])
902
+ sage: B = simplicial_sets.ClassifyingSpace(G)
903
+ sage: X = B.product(B)
904
+ sage: X.n_skeleton(2)
905
+ Simplicial set with 13 non-degenerate simplices
906
+ """
907
+ n_skel = SimplicialSet_finite.n_skeleton
908
+ if self.is_finite():
909
+ n_skel = SimplicialSet_finite.n_skeleton
910
+ return n_skel(self, n)
911
+ start, skel = self._n_skeleton
912
+ if start == n:
913
+ return skel
914
+ elif start > n:
915
+ return skel.n_skeleton(n)
916
+ ans = n_skel(ProductOfSimplicialSets_finite([X.n_skeleton(n) for X in self._factors]), n)
917
+ self._n_skeleton = (n, ans)
918
+ return ans
919
+
920
+ def factor(self, i, as_subset=False):
921
+ r"""
922
+ Return the `i`-th factor of the product.
923
+
924
+ INPUT:
925
+
926
+ - ``i`` -- integer; the index of the factor
927
+
928
+ - ``as_subset`` -- boolean (default: ``False``)
929
+
930
+ If ``as_subset`` is ``True``, return the `i`-th factor as a
931
+ subsimplicial set of the product, identifying it with its
932
+ product with the base point in each other factor. As a
933
+ subsimplicial set, it comes equipped with an inclusion
934
+ map. This option will raise an error if any factor does not
935
+ have a base point.
936
+
937
+ If ``as_subset`` is ``False``, return the `i`-th factor in
938
+ its original form as a simplicial set.
939
+
940
+ EXAMPLES::
941
+
942
+ sage: S2 = simplicial_sets.Sphere(2)
943
+ sage: S3 = simplicial_sets.Sphere(3)
944
+ sage: K = S2.product(S3)
945
+ sage: K.factor(0)
946
+ S^2
947
+
948
+ sage: K.factor(0, as_subset=True)
949
+ Simplicial set with 2 non-degenerate simplices
950
+ sage: K.factor(0, as_subset=True).homology() # needs sage.modules
951
+ {0: 0, 1: 0, 2: Z}
952
+
953
+ sage: K.factor(0) is S2
954
+ True
955
+ sage: K.factor(0, as_subset=True) is S2
956
+ False
957
+ """
958
+ if as_subset:
959
+ if any(not _.is_pointed() for _ in self.factors()):
960
+ raise ValueError('"as_subset=True" is only valid '
961
+ 'if each factor is pointed')
962
+
963
+ basept_factors = [sset.base_point() for sset in self.factors()]
964
+ basept_factors = basept_factors[:i] + basept_factors[i+1:]
965
+ to_factors = {v: k for k, v in self._translation}
966
+ simps = []
967
+ for x in self.nondegenerate_simplices():
968
+ simplices = [sigma[0] for sigma in to_factors[x]]
969
+ if simplices[:i] + simplices[i+1:] == basept_factors:
970
+ simps.append(x)
971
+ return self.subsimplicial_set(simps)
972
+ return self.factors()[i]
973
+
974
+ def _repr_(self):
975
+ """
976
+ Print representation.
977
+
978
+ EXAMPLES::
979
+
980
+ sage: S2 = simplicial_sets.Sphere(2)
981
+ sage: K = simplicial_sets.KleinBottle()
982
+ sage: G = groups.misc.MultiplicativeAbelian([2]) # needs sage.groups
983
+ sage: B = simplicial_sets.ClassifyingSpace(G) # needs sage.groups
984
+ sage: S2.product(S2)
985
+ S^2 x S^2
986
+ sage: S2.product(K, B) # needs sage.groups
987
+ S^2 x Klein bottle x Classifying space of Multiplicative Abelian group isomorphic to C2
988
+ """
989
+ return ' x '.join(str(X) for X in self._factors)
990
+
991
+ def _latex_(self):
992
+ r"""
993
+ LaTeX representation.
994
+
995
+ EXAMPLES::
996
+
997
+ sage: S2 = simplicial_sets.Sphere(2)
998
+ sage: latex(S2.product(S2))
999
+ S^{2} \times S^{2}
1000
+ sage: RPoo = simplicial_sets.RealProjectiveSpace(Infinity) # needs sage.groups
1001
+ sage: latex(S2.product(RPoo, S2)) # needs sage.groups
1002
+ S^{2} \times RP^{\infty} \times S^{2}
1003
+ """
1004
+ return ' \\times '.join(latex(X) for X in self._factors)
1005
+
1006
+
1007
+ class ProductOfSimplicialSets_finite(ProductOfSimplicialSets, PullbackOfSimplicialSets_finite):
1008
+ r"""
1009
+ The product of finite simplicial sets.
1010
+
1011
+ When the factors are all finite, there are more methods available
1012
+ for the resulting product, as compared to products with infinite
1013
+ factors: projection maps, the wedge as a subcomplex, and the fat
1014
+ wedge as a subcomplex. See :meth:`projection_map`,
1015
+ :meth:`wedge_as_subset`, and :meth:`fat_wedge_as_subset`
1016
+ """
1017
+ def __init__(self, factors=None):
1018
+ r"""
1019
+ Return the product of finite simplicial sets.
1020
+
1021
+ See :class:`ProductOfSimplicialSets` for more information.
1022
+
1023
+ EXAMPLES::
1024
+
1025
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1026
+ sage: v = AbstractSimplex(0, name='v')
1027
+ sage: e = AbstractSimplex(1)
1028
+ sage: X = SimplicialSet({e: (v, v)})
1029
+ sage: W = X.product(X, X)
1030
+ sage: W.homology() # needs sage.groups
1031
+ {0: 0, 1: Z x Z x Z, 2: Z x Z x Z, 3: Z}
1032
+ sage: W.is_pointed()
1033
+ False
1034
+
1035
+ sage: X = X.set_base_point(v)
1036
+ sage: w = AbstractSimplex(0, name='w')
1037
+ sage: f = AbstractSimplex(1)
1038
+ sage: Y = SimplicialSet({f: (v,w)}, base_point=w)
1039
+ sage: Z = Y.product(X)
1040
+ sage: Z.is_pointed()
1041
+ True
1042
+ sage: Z.base_point()
1043
+ (w, v)
1044
+ """
1045
+ PullbackOfSimplicialSets_finite.__init__(self, [space.constant_map()
1046
+ for space in factors])
1047
+ self._factors = tuple([f.domain() for f in self._maps])
1048
+
1049
+ def projection_map(self, i):
1050
+ """
1051
+ Return the map projecting onto the `i`-th factor.
1052
+
1053
+ INPUT:
1054
+
1055
+ - ``i`` -- integer; the index of the projection map
1056
+
1057
+ EXAMPLES::
1058
+
1059
+ sage: T = simplicial_sets.Torus()
1060
+ sage: f_0 = T.projection_map(0)
1061
+ sage: f_1 = T.projection_map(1)
1062
+
1063
+ sage: # needs sage.modules
1064
+ sage: m_0 = f_0.induced_homology_morphism().to_matrix(1) # matrix in dim 1
1065
+ sage: m_1 = f_1.induced_homology_morphism().to_matrix(1)
1066
+ sage: m_0.rank()
1067
+ 1
1068
+ sage: m_0 == m_1
1069
+ False
1070
+ """
1071
+ return self.structure_map(i)
1072
+
1073
+ def wedge_as_subset(self):
1074
+ """
1075
+ Return the wedge as a subsimplicial set of this product of pointed
1076
+ simplicial sets.
1077
+
1078
+ This will raise an error if any factor is not pointed.
1079
+
1080
+ EXAMPLES::
1081
+
1082
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1083
+ sage: v = AbstractSimplex(0, name='v')
1084
+ sage: e = AbstractSimplex(1, name='e')
1085
+ sage: w = AbstractSimplex(0, name='w')
1086
+ sage: f = AbstractSimplex(1, name='f')
1087
+ sage: X = SimplicialSet({e: (v, v)}, base_point=v)
1088
+ sage: Y = SimplicialSet({f: (w, w)}, base_point=w)
1089
+ sage: P = X.product(Y)
1090
+ sage: W = P.wedge_as_subset()
1091
+ sage: W.nondegenerate_simplices()
1092
+ [(v, w), (e, s_0 w), (s_0 v, f)]
1093
+ sage: W.homology() # needs sage.modules
1094
+ {0: 0, 1: Z x Z}
1095
+ """
1096
+ basept_factors = [sset.base_point() for sset in self.factors()]
1097
+ to_factors = {v: k for k, v in self._translation}
1098
+ simps = []
1099
+ for x in self.nondegenerate_simplices():
1100
+ simplices = to_factors[x]
1101
+ not_base_pt = 0
1102
+ for sigma, star in zip(simplices, basept_factors):
1103
+ if not_base_pt > 1:
1104
+ continue
1105
+ if sigma[0].nondegenerate() != star:
1106
+ not_base_pt += 1
1107
+ if not_base_pt <= 1:
1108
+ simps.append(x)
1109
+ return self.subsimplicial_set(simps)
1110
+
1111
+ def fat_wedge_as_subset(self):
1112
+ """
1113
+ Return the fat wedge as a subsimplicial set of this product of
1114
+ pointed simplicial sets.
1115
+
1116
+ The fat wedge consists of those terms where at least one
1117
+ factor is the base point. Thus with two factors this is the
1118
+ ordinary wedge, but with more factors, it is larger.
1119
+
1120
+ EXAMPLES::
1121
+
1122
+ sage: S1 = simplicial_sets.Sphere(1)
1123
+ sage: X = S1.product(S1, S1)
1124
+ sage: W = X.fat_wedge_as_subset()
1125
+ sage: W.homology() # needs sage.modules
1126
+ {0: 0, 1: Z x Z x Z, 2: Z x Z x Z}
1127
+ """
1128
+ basept_factors = [sset.base_point() for sset in self.factors()]
1129
+ to_factors = {v: k for k, v in self._translation}
1130
+ simps = []
1131
+ for x in self.nondegenerate_simplices():
1132
+ simplices = to_factors[x]
1133
+ combined = zip(simplices, basept_factors)
1134
+ if any(sigma[0] == pt for (sigma, pt) in combined):
1135
+ simps.append(x)
1136
+ return self.subsimplicial_set(simps)
1137
+
1138
+
1139
+ class PushoutOfSimplicialSets(SimplicialSet_arbitrary, UniqueRepresentation):
1140
+ @staticmethod
1141
+ def __classcall_private__(cls, maps=None, vertex_name=None):
1142
+ """
1143
+ TESTS::
1144
+
1145
+ sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets
1146
+ sage: S2 = simplicial_sets.Sphere(2)
1147
+ sage: one = S2.Hom(S2).identity()
1148
+ sage: PushoutOfSimplicialSets([one, one]) == PushoutOfSimplicialSets((one, one))
1149
+ True
1150
+ """
1151
+ if maps:
1152
+ return super().__classcall__(cls, maps=tuple(maps),
1153
+ vertex_name=vertex_name)
1154
+ return super().__classcall__(cls, vertex_name=vertex_name)
1155
+
1156
+ def __init__(self, maps=None, vertex_name=None):
1157
+ r"""
1158
+ Return the pushout obtained from the morphisms ``maps``.
1159
+
1160
+ INPUT:
1161
+
1162
+ - ``maps`` -- list or tuple of morphisms of simplicial sets
1163
+ - ``vertex_name`` -- (default: ``None``)
1164
+
1165
+ If only a single map `f: X \to Y` is given, then return
1166
+ `Y`. If no maps are given, return the empty simplicial
1167
+ set. Otherwise, given a simplicial set `X` and maps `f_i: X
1168
+ \to Y_i` for `0 \leq i \leq m`, construct the pushout `P`: see
1169
+ :wikipedia:`Pushout_(category_theory)`. This is constructed as
1170
+ pushouts of sets for each set of `n`-simplices, so `P_n` is
1171
+ the disjoint union of the sets `(Y_i)_n`, with elements
1172
+ `f_i(x)` identified for `n`-simplex `x` in `X`.
1173
+
1174
+ Simplices in the pushout are given names as follows: if a
1175
+ simplex comes from a single `Y_i`, it inherits its
1176
+ name. Otherwise it must come from a simplex (or several) in
1177
+ `X`, and then it inherits one of those names, and it should be
1178
+ the first alphabetically. For example, if vertices `v`, `w`,
1179
+ and `z` in `X` are glued together, then the resulting vertex
1180
+ in the pushout will be called `v`.
1181
+
1182
+ Base points are taken care of automatically: if each of the
1183
+ maps `f_i` is pointed, so is the pushout. If `X` is a point or
1184
+ if `X` is nonempty and any of the spaces `Y_i` is a point, use
1185
+ those for the base point. In all of these cases, if
1186
+ ``vertex_name`` is ``None``, generate the name of the base
1187
+ point automatically; otherwise, use ``vertex_name`` for its
1188
+ name.
1189
+
1190
+ In all other cases, the pushout is not pointed.
1191
+
1192
+ EXAMPLES::
1193
+
1194
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1195
+ sage: v = AbstractSimplex(0, name='v')
1196
+ sage: a = AbstractSimplex(0, name='a')
1197
+ sage: b = AbstractSimplex(0, name='b')
1198
+ sage: c = AbstractSimplex(0, name='c')
1199
+ sage: e0 = AbstractSimplex(1, name='e_0')
1200
+ sage: e1 = AbstractSimplex(1, name='e_1')
1201
+ sage: e2 = AbstractSimplex(1, name='e_2')
1202
+ sage: X = SimplicialSet({e2: (b, a)})
1203
+ sage: Y0 = SimplicialSet({e2: (b,a), e0: (c,b), e1: (c,a)})
1204
+ sage: Y1 = simplicial_sets.Simplex(0)
1205
+ sage: f0_data = {a:a, b:b, e2: e2}
1206
+ sage: v = Y1.n_cells(0)[0]
1207
+ sage: f1_data = {a:v, b:v, e2:v.apply_degeneracies(0)}
1208
+ sage: f0 = X.Hom(Y0)(f0_data)
1209
+ sage: f1 = X.Hom(Y1)(f1_data)
1210
+ sage: P = X.pushout(f0, f1)
1211
+ sage: P.nondegenerate_simplices()
1212
+ [a, c, e_0, e_1]
1213
+
1214
+ There are defining maps `f_i: X \to Y_i` and structure maps
1215
+ `\bar{f}_i: Y_i \to P`; the latter are only implemented in
1216
+ Sage when each `Y_i` is finite. ::
1217
+
1218
+ sage: P.defining_map(0) == f0
1219
+ True
1220
+ sage: P.structure_map(1)
1221
+ Simplicial set morphism:
1222
+ From: 0-simplex
1223
+ To: Pushout of maps:
1224
+ Simplicial set morphism:
1225
+ From: Simplicial set with 3 non-degenerate simplices
1226
+ To: Simplicial set with 6 non-degenerate simplices
1227
+ Defn: [a, b, e_2] --> [a, b, e_2]
1228
+ Simplicial set morphism:
1229
+ From: Simplicial set with 3 non-degenerate simplices
1230
+ To: 0-simplex
1231
+ Defn: Constant map at (0,)
1232
+ Defn: Constant map at a
1233
+ sage: P.structure_map(0).domain() == Y0
1234
+ True
1235
+ sage: P.structure_map(0).codomain() == P
1236
+ True
1237
+
1238
+ An inefficient way of constructing a suspension for an
1239
+ unpointed set: take the pushout of two copies of the inclusion
1240
+ map `X \to CX`::
1241
+
1242
+ sage: T = simplicial_sets.Torus()
1243
+ sage: T = T.unset_base_point()
1244
+ sage: CT = T.cone()
1245
+ sage: inc = CT.base_as_subset().inclusion_map()
1246
+ sage: P = T.pushout(inc, inc)
1247
+ sage: P.homology() # needs sage.modules
1248
+ {0: 0, 1: 0, 2: Z x Z, 3: Z}
1249
+ sage: len(P.nondegenerate_simplices())
1250
+ 20
1251
+
1252
+ It is more efficient to construct the suspension as the
1253
+ quotient `CX/X`::
1254
+
1255
+ sage: len(CT.quotient(CT.base_as_subset()).nondegenerate_simplices())
1256
+ 8
1257
+
1258
+ It is more efficient still if the original simplicial set has
1259
+ a base point::
1260
+
1261
+ sage: T = simplicial_sets.Torus()
1262
+ sage: len(T.suspension().nondegenerate_simplices())
1263
+ 6
1264
+
1265
+ sage: S1 = simplicial_sets.Sphere(1)
1266
+ sage: pt = simplicial_sets.Point()
1267
+ sage: bouquet = pt.pushout(S1.base_point_map(),
1268
+ ....: S1.base_point_map(),
1269
+ ....: S1.base_point_map())
1270
+ sage: bouquet.homology(1) # needs sage.modules
1271
+ Z x Z x Z
1272
+ """
1273
+ # Import this here to prevent circular imports.
1274
+ from sage.topology.simplicial_set_morphism import SimplicialSetMorphism
1275
+ if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps):
1276
+ raise ValueError('the maps must be morphisms of simplicial sets')
1277
+ Cat = SimplicialSets()
1278
+ if maps:
1279
+ if all(f.codomain().is_finite() for f in maps):
1280
+ Cat = Cat.Finite()
1281
+ if all(f.is_pointed() for f in maps):
1282
+ Cat = Cat.Pointed()
1283
+ Parent.__init__(self, category=Cat)
1284
+ self._maps = maps
1285
+ self._n_skeleton = (-1, Empty())
1286
+ self._vertex_name = vertex_name
1287
+
1288
+ def n_skeleton(self, n):
1289
+ """
1290
+ Return the `n`-skeleton of this simplicial set.
1291
+
1292
+ That is, the simplicial set generated by all nondegenerate
1293
+ simplices of dimension at most `n`.
1294
+
1295
+ INPUT:
1296
+
1297
+ - ``n`` -- the dimension
1298
+
1299
+ The `n`-skeleton of the pushout is computed as the pushout
1300
+ of the `n`-skeleta of the component simplicial sets.
1301
+
1302
+ EXAMPLES::
1303
+
1304
+ sage: # needs sage.groups
1305
+ sage: G = groups.misc.MultiplicativeAbelian([2])
1306
+ sage: B = simplicial_sets.ClassifyingSpace(G)
1307
+ sage: K = B.n_skeleton(3)
1308
+ sage: Q = K.pushout(K.inclusion_map(), K.constant_map())
1309
+ sage: Q.n_skeleton(5).homology() # needs sage.modules
1310
+ {0: 0, 1: 0, 2: 0, 3: 0, 4: Z, 5: Z}
1311
+
1312
+ Of course, computing the `n`-skeleton and then taking homology
1313
+ need not yield the same answer as asking for homology through
1314
+ dimension `n`, since the latter computation will use the
1315
+ `(n+1)`-skeleton::
1316
+
1317
+ sage: Q.homology(range(6)) # needs sage.groups sage.modules
1318
+ {0: 0, 1: 0, 2: 0, 3: 0, 4: Z, 5: C2}
1319
+ """
1320
+ if self.is_finite():
1321
+ maps = self._maps
1322
+ if maps:
1323
+ domain = SimplicialSet_finite.n_skeleton(maps[0].domain(), n)
1324
+ codomains = [SimplicialSet_finite.n_skeleton(f.codomain(), n) for f in maps]
1325
+ new_maps = [f.n_skeleton(n, domain, c) for (f, c) in zip(maps, codomains)]
1326
+ return PushoutOfSimplicialSets_finite(new_maps,
1327
+ vertex_name=self._vertex_name)
1328
+ return PushoutOfSimplicialSets_finite(maps,
1329
+ vertex_name=self._vertex_name)
1330
+ start, skel = self._n_skeleton
1331
+ if start == n:
1332
+ return skel
1333
+ elif start > n:
1334
+ return skel.n_skeleton(n)
1335
+ ans = PushoutOfSimplicialSets_finite([f.n_skeleton(n) for f in self._maps],
1336
+ vertex_name=self._vertex_name)
1337
+ self._n_skeleton = (n, ans)
1338
+ return ans
1339
+
1340
+ def defining_map(self, i):
1341
+ r"""
1342
+ Return the `i`-th map defining the pushout.
1343
+
1344
+ INPUT:
1345
+
1346
+ - ``i`` -- integer
1347
+
1348
+ If this pushout was constructed as ``X.pushout(f_0, f_1, ...)``,
1349
+ this returns `f_i`.
1350
+
1351
+ EXAMPLES::
1352
+
1353
+ sage: S1 = simplicial_sets.Sphere(1)
1354
+ sage: T = simplicial_sets.Torus()
1355
+ sage: X = S1.wedge(T) # a pushout
1356
+ sage: X.defining_map(0)
1357
+ Simplicial set morphism:
1358
+ From: Point
1359
+ To: S^1
1360
+ Defn: Constant map at v_0
1361
+ sage: X.defining_map(1).domain()
1362
+ Point
1363
+ sage: X.defining_map(1).codomain()
1364
+ Torus
1365
+ """
1366
+ return self._maps[i]
1367
+
1368
+ def _repr_(self):
1369
+ """
1370
+ Print representation.
1371
+
1372
+ EXAMPLES::
1373
+
1374
+ sage: S2 = simplicial_sets.Sphere(2)
1375
+ sage: S3 = simplicial_sets.Sphere(3)
1376
+ sage: pt = simplicial_sets.Point()
1377
+ sage: pt.pushout(S2.base_point_map(), S3.base_point_map())
1378
+ Pushout of maps:
1379
+ Simplicial set morphism:
1380
+ From: Point
1381
+ To: S^2
1382
+ Defn: Constant map at v_0
1383
+ Simplicial set morphism:
1384
+ From: Point
1385
+ To: S^3
1386
+ Defn: Constant map at v_0
1387
+ """
1388
+ if not self._maps:
1389
+ return 'Empty simplicial set'
1390
+ s = 'Pushout of maps:'
1391
+ for f in self._maps:
1392
+ t = '\n' + str(f)
1393
+ s += t.replace('\n', '\n ')
1394
+ return s
1395
+
1396
+
1397
+ class PushoutOfSimplicialSets_finite(PushoutOfSimplicialSets, SimplicialSet_finite):
1398
+ """
1399
+ The pushout of finite simplicial sets obtained from ``maps``.
1400
+
1401
+ When the simplicial sets involved are all finite, there are more
1402
+ methods available to the resulting pushout, as compared to case
1403
+ when some of the components are infinite: the structure maps to the
1404
+ pushout and the pushout's universal property: see
1405
+ :meth:`structure_map` and :meth:`universal_property`.
1406
+ """
1407
+ @staticmethod
1408
+ def __classcall_private__(cls, maps=None, vertex_name=None):
1409
+ """
1410
+ TESTS::
1411
+
1412
+ sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite
1413
+ sage: S2 = simplicial_sets.Sphere(2)
1414
+ sage: one = S2.Hom(S2).identity()
1415
+ sage: PushoutOfSimplicialSets_finite([one, one]) == PushoutOfSimplicialSets_finite((one, one))
1416
+ True
1417
+ """
1418
+ if maps:
1419
+ return super().__classcall__(cls, maps=tuple(maps),
1420
+ vertex_name=vertex_name)
1421
+ return super().__classcall__(cls, vertex_name=vertex_name)
1422
+
1423
+ def __init__(self, maps=None, vertex_name=None):
1424
+ r"""
1425
+ Return the pushout obtained from the morphisms ``maps``.
1426
+
1427
+ See :class:`PushoutOfSimplicialSets` for more information.
1428
+
1429
+ INPUT:
1430
+
1431
+ - ``maps`` -- list or tuple of morphisms of simplicial sets
1432
+ - ``vertex_name`` -- (default: ``None``)
1433
+
1434
+ EXAMPLES::
1435
+
1436
+ sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite
1437
+ sage: T = simplicial_sets.Torus()
1438
+ sage: S2 = simplicial_sets.Sphere(2)
1439
+ sage: PushoutOfSimplicialSets_finite([T.base_point_map(), S2.base_point_map()]).n_cells(0)[0]
1440
+ *
1441
+ sage: PushoutOfSimplicialSets_finite([T.base_point_map(), S2.base_point_map()], vertex_name='v').n_cells(0)[0]
1442
+ v
1443
+ """
1444
+ from sage.graphs.graph import Graph
1445
+
1446
+ # Import this here to prevent circular imports.
1447
+ from sage.topology.simplicial_set_morphism import SimplicialSetMorphism
1448
+ if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps):
1449
+ raise ValueError('the maps must be morphisms of simplicial sets')
1450
+ if not maps:
1451
+ SimplicialSet_finite.__init__(self, {})
1452
+ self._maps = ()
1453
+ self._structure = ()
1454
+ return
1455
+ domain = maps[0].domain()
1456
+ if len(maps) == 1:
1457
+ # f: X --> Y
1458
+ f = maps[0]
1459
+ codomain = f.codomain()
1460
+ if f.is_pointed():
1461
+ base_point = codomain.base_point()
1462
+ if vertex_name is not None:
1463
+ base_point.rename(vertex_name)
1464
+ SimplicialSet_finite.__init__(self, codomain.face_data(),
1465
+ base_point=base_point)
1466
+ elif len(domain.nondegenerate_simplices()) == 1:
1467
+ # X is a point.
1468
+ base_point = f(domain().n_cells(0)[0])
1469
+ if vertex_name is not None:
1470
+ base_point.rename(vertex_name)
1471
+ SimplicialSet_finite.__init__(self, codomain.face_data(),
1472
+ base_point=base_point)
1473
+ elif len(codomain.nondegenerate_simplices()) == 1:
1474
+ # Y is a point.
1475
+ base_point = codomain.n_cells(0)[0]
1476
+ if vertex_name is not None:
1477
+ base_point.rename(vertex_name)
1478
+ SimplicialSet_finite.__init__(self, codomain.face_data(),
1479
+ base_point=base_point)
1480
+ else:
1481
+ SimplicialSet_finite.__init__(self, codomain.face_data())
1482
+ self._maps = (f,)
1483
+ self._structure = (f,)
1484
+ return
1485
+ if any(domain != f.domain() for f in maps[1:]):
1486
+ raise ValueError('the domains of the maps must be equal')
1487
+ # Data to define the pushout:
1488
+ data = {}
1489
+ codomains = [f.codomain() for f in maps]
1490
+ # spaces: indexed list of spaces. Entries are of the form
1491
+ # (space, int) where int=-1 for the domain, and for the
1492
+ # codomains, int is the corresponding index.
1493
+ spaces = [(Y, i-1) for i, Y in enumerate([domain] + codomains)]
1494
+ # Dictionaries to translate from simplices in domain,
1495
+ # codomains to simplices in the pushout. The keys are of the
1496
+ # form (space, int). int=-1 for the domain, and for the
1497
+ # codomains, int is the corresponding index.
1498
+ _to_P = {Y: {} for Y in spaces}
1499
+ max_dim = max(Y.dimension() for Y in codomains)
1500
+ for n in range(1 + max_dim):
1501
+ # Now we impose an equivalence relation on the simplices,
1502
+ # setting x equivalent to f_i(x) for each simplex x in X
1503
+ # and each defining map f_i. We do this by constructing a
1504
+ # graph and finding its connected components: the vertices
1505
+ # of the graph are the n-cells of X and the Y_i, and
1506
+ # there are edges from x to f_i(x).
1507
+ vertices = []
1508
+ for Y, i in spaces:
1509
+ vertices.extend([(cell, i) for cell in Y.n_cells(n)])
1510
+ edges = []
1511
+ for x in domain.n_cells(n):
1512
+ edges.extend([[(x, -1), (f(x), i)] for i, f in enumerate(maps)])
1513
+ G = Graph([vertices, edges], format='vertices_and_edges')
1514
+ data[n] = [set(_) for _ in G.connected_components(sort=False)]
1515
+ # data is now a dictionary indexed by dimension, and data[n]
1516
+ # consists of sets of n-simplices of the domain and the
1517
+ # codomains, each set an equivalence class of n-simplices
1518
+ # under the gluing. So if any element of one of those sets is
1519
+ # degenerate, we can throw the whole thing away. Otherwise, we
1520
+ # can choose a representative to compute the faces.
1521
+ simplices = {}
1522
+ for dim in sorted(data):
1523
+ for s in data[dim]:
1524
+ degenerate = any(sigma[0].is_degenerate() for sigma in s)
1525
+ if degenerate:
1526
+ # Identify the degeneracies involved.
1527
+ degens = []
1528
+ for (sigma, j) in s:
1529
+ if len(sigma.degeneracies()) > len(degens):
1530
+ degens = sigma.degeneracies()
1531
+ space = spaces[j+1]
1532
+ old = _to_P[space][sigma.nondegenerate()]
1533
+ for sigma, j in s:
1534
+ # Now update the _to_P[space] dictionaries.
1535
+ space = spaces[j+1]
1536
+ _to_P[space][sigma] = old.apply_degeneracies(*degens)
1537
+ else: # nondegenerate
1538
+ if len(s) == 1:
1539
+ name = str(list(s)[0][0])
1540
+ latex_name = latex(list(s)[0][0])
1541
+ else:
1542
+ # Choose a name from a simplex in domain.
1543
+ for sigma, j in sorted(s):
1544
+ if j == -1:
1545
+ name = str(sigma)
1546
+ latex_name = latex(sigma)
1547
+ break
1548
+ new = AbstractSimplex(dim, name=name,
1549
+ latex_name=latex_name)
1550
+ if dim == 0:
1551
+ faces = None
1552
+ for sigma, j in s:
1553
+ space = spaces[j+1]
1554
+ _to_P[space][sigma] = new
1555
+ if dim > 0:
1556
+ faces = [_to_P[space][tau.nondegenerate()].apply_degeneracies(*tau.degeneracies())
1557
+ for tau in space[0].faces(sigma)]
1558
+ simplices[new] = faces
1559
+
1560
+ some_Y_is_pt = False
1561
+ if len(domain.nondegenerate_simplices()) > 1:
1562
+ # Only investigate this if X is not empty and not a point.
1563
+ for Y, i in spaces:
1564
+ if len(Y.nondegenerate_simplices()) == 1:
1565
+ some_Y_is_pt = True
1566
+ break
1567
+ if len(domain.nondegenerate_simplices()) == 1:
1568
+ # X is a point.
1569
+ base_point = _to_P[(domain, -1)][domain.n_cells(0)[0]]
1570
+ if vertex_name is not None:
1571
+ base_point.rename(vertex_name)
1572
+ SimplicialSet_finite.__init__(self, simplices, base_point=base_point)
1573
+ elif some_Y_is_pt:
1574
+ # We found (Y,i) above.
1575
+ base_point = _to_P[(Y, i)][Y.n_cells(0)[0]]
1576
+ if vertex_name is not None:
1577
+ base_point.rename(vertex_name)
1578
+ SimplicialSet_finite.__init__(self, simplices, base_point=base_point)
1579
+ elif all(f.is_pointed() for f in maps):
1580
+ pt = _to_P[(codomains[0], 0)][codomains[0].base_point()]
1581
+ if any(_to_P[(Y, i)][Y.base_point()] != pt for Y, i in spaces[2:]):
1582
+ raise ValueError('something unexpected went wrong '
1583
+ 'with base points')
1584
+ base_point = _to_P[(domain, -1)][domain.base_point()]
1585
+ if vertex_name is not None:
1586
+ base_point.rename(vertex_name)
1587
+ SimplicialSet_finite.__init__(self, simplices, base_point=base_point)
1588
+ else:
1589
+ SimplicialSet_finite.__init__(self, simplices)
1590
+ # The relevant maps:
1591
+ self._maps = maps
1592
+ self._structure = tuple([Y.Hom(self)(_to_P[(Y, i)])
1593
+ for Y, i in spaces[1:]])
1594
+ self._vertex_name = vertex_name
1595
+
1596
+ def structure_map(self, i):
1597
+ r"""
1598
+ Return the `i`-th structure map of the pushout.
1599
+
1600
+ INPUT:
1601
+
1602
+ - ``i`` -- integer
1603
+
1604
+ If this pushout `Z` was constructed as ``X.pushout(f_0, f_1, ...)``,
1605
+ where `f_i: X \to Y_i`, then there are structure maps
1606
+ `\bar{f}_i: Y_i \to Z`. This method constructs `\bar{f}_i`.
1607
+
1608
+ EXAMPLES::
1609
+
1610
+ sage: S1 = simplicial_sets.Sphere(1)
1611
+ sage: T = simplicial_sets.Torus()
1612
+ sage: X = S1.disjoint_union(T) # a pushout
1613
+ sage: X.structure_map(0)
1614
+ Simplicial set morphism:
1615
+ From: S^1
1616
+ To: Disjoint union: (S^1 u Torus)
1617
+ Defn: [v_0, sigma_1] --> [v_0, sigma_1]
1618
+ sage: X.structure_map(1).domain()
1619
+ Torus
1620
+ sage: X.structure_map(1).codomain()
1621
+ Disjoint union: (S^1 u Torus)
1622
+ """
1623
+ return self._structure[i]
1624
+
1625
+ def universal_property(self, *maps):
1626
+ r"""
1627
+ Return the map induced by ``maps``.
1628
+
1629
+ INPUT:
1630
+
1631
+ - ``maps`` -- maps "factors" `Y_i` forming the pushout to a
1632
+ fixed simplicial set `Z`
1633
+
1634
+ If the pushout `P` is formed by maps `f_i: X \to Y_i`, then
1635
+ given maps `g_i: Y_i \to Z` such that `g_i f_i = g_j f_j` for
1636
+ all `i`, `j`, then there is a unique map `g: P \to Z` making
1637
+ the appropriate diagram commute. This constructs that map.
1638
+
1639
+ EXAMPLES::
1640
+
1641
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1642
+ sage: v = AbstractSimplex(0, name='v')
1643
+ sage: w = AbstractSimplex(0, name='w')
1644
+ sage: x = AbstractSimplex(0, name='x')
1645
+ sage: evw = AbstractSimplex(1, name='vw')
1646
+ sage: evx = AbstractSimplex(1, name='vx')
1647
+ sage: ewx = AbstractSimplex(1, name='wx')
1648
+ sage: X = SimplicialSet({evw: (w, v), evx: (x, v)})
1649
+ sage: Y_0 = SimplicialSet({evw: (w, v), evx: (x, v), ewx: (x, w)})
1650
+ sage: Y_1 = SimplicialSet({evx: (x, v)})
1651
+
1652
+ sage: f_0 = Hom(X, Y_0)({v:v, w:w, x:x, evw:evw, evx:evx})
1653
+ sage: f_1 = Hom(X, Y_1)({v:v, w:v, x:x,
1654
+ ....: evw:v.apply_degeneracies(0), evx:evx})
1655
+ sage: P = X.pushout(f_0, f_1)
1656
+
1657
+ sage: one = Hom(Y_1, Y_1).identity()
1658
+ sage: g = Hom(Y_0, Y_1)({v:v, w:v, x:x,
1659
+ ....: evw:v.apply_degeneracies(0), evx:evx, ewx:evx})
1660
+ sage: P.universal_property(g, one)
1661
+ Simplicial set morphism:
1662
+ From: Pushout of maps:
1663
+ Simplicial set morphism:
1664
+ From: Simplicial set with 5 non-degenerate simplices
1665
+ To: Simplicial set with 6 non-degenerate simplices
1666
+ Defn: [v, w, x, vw, vx] --> [v, w, x, vw, vx]
1667
+ Simplicial set morphism:
1668
+ From: Simplicial set with 5 non-degenerate simplices
1669
+ To: Simplicial set with 3 non-degenerate simplices
1670
+ Defn: [v, w, x, vw, vx] --> [v, v, x, s_0 v, vx]
1671
+ To: Simplicial set with 3 non-degenerate simplices
1672
+ Defn: [v, x, vx, wx] --> [v, x, vx, vx]
1673
+ """
1674
+ codomain = maps[0].codomain()
1675
+ if any(g.codomain() != codomain for g in maps[1:]):
1676
+ raise ValueError('the maps do not all have the same codomain')
1677
+ composite = maps[0] * self._maps[0]
1678
+ if any(g*f != composite for g, f in zip(maps[1:], self._maps[1:])):
1679
+ raise ValueError('the maps are not compatible')
1680
+ data = {}
1681
+ for i, g in enumerate(maps):
1682
+ f_i_dict = self.structure_map(i)._dictionary
1683
+ for sigma in f_i_dict:
1684
+ tau = f_i_dict[sigma]
1685
+ # For sigma_i in Y_i, define the map G by
1686
+ # G(\bar{f}_i)(sigma_i) = g_i(sigma_i).
1687
+ if tau not in data:
1688
+ data[tau] = g(sigma)
1689
+ return self.Hom(codomain)(data)
1690
+
1691
+
1692
+ class QuotientOfSimplicialSet(PushoutOfSimplicialSets):
1693
+ def __init__(self, inclusion, vertex_name='*'):
1694
+ r"""
1695
+ Return the quotient of a simplicial set by a subsimplicial set.
1696
+
1697
+ INPUT:
1698
+
1699
+ - ``inclusion`` -- inclusion map of a subcomplex (=
1700
+ subsimplicial set) of a simplicial set
1701
+ - ``vertex_name`` -- string (default: ``'*'``)
1702
+
1703
+ A subcomplex `A` comes equipped with the inclusion map `A \to
1704
+ X` to its ambient complex `X`, and this constructs the
1705
+ quotient `X/A`, collapsing `A` to a point. The resulting point
1706
+ is called ``vertex_name``, which is ``'*'`` by default.
1707
+
1708
+ When the simplicial sets involved are finite, there is a
1709
+ :meth:`QuotientOfSimplicialSet_finite.quotient_map` method available.
1710
+
1711
+ EXAMPLES::
1712
+
1713
+ sage: # needs sage.groups
1714
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
1715
+ sage: RP2 = RP5.n_skeleton(2)
1716
+ sage: RP5_2 = RP5.quotient(RP2); RP5_2
1717
+ Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
1718
+ sage: RP5_2.quotient_map()
1719
+ Simplicial set morphism:
1720
+ From: RP^5
1721
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
1722
+ Defn: [1, f, f * f, f * f * f, f * f * f * f, f * f * f * f * f]
1723
+ --> [*, s_0 *, s_1 s_0 *, f * f * f, f * f * f * f, f * f * f * f * f]
1724
+ """
1725
+ subcomplex = inclusion.domain()
1726
+ PushoutOfSimplicialSets.__init__(self, [inclusion,
1727
+ subcomplex.constant_map()],
1728
+ vertex_name=vertex_name)
1729
+
1730
+ ambient = inclusion.codomain()
1731
+ if ambient.is_pointed() and ambient.is_finite():
1732
+ if ambient.base_point() not in subcomplex:
1733
+ self._basepoint = self.structure_map(0)(ambient.base_point())
1734
+
1735
+ def ambient(self):
1736
+ """
1737
+ Return the ambient space.
1738
+
1739
+ That is, if this quotient is `K/L`, return `K`.
1740
+
1741
+ EXAMPLES::
1742
+
1743
+ sage: # needs sage.groups
1744
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
1745
+ sage: RP2 = RP5.n_skeleton(2)
1746
+ sage: RP5_2 = RP5.quotient(RP2)
1747
+ sage: RP5_2.ambient()
1748
+ RP^5
1749
+
1750
+ sage: # needs sage.groups
1751
+ sage: G = groups.misc.MultiplicativeAbelian([2])
1752
+ sage: B = simplicial_sets.ClassifyingSpace(G)
1753
+ sage: K = B.n_skeleton(3)
1754
+ sage: Q = B.quotient(K)
1755
+ sage: Q.ambient()
1756
+ Classifying space of Multiplicative Abelian group isomorphic to C2
1757
+ """
1758
+ return self._maps[0].codomain()
1759
+
1760
+ def subcomplex(self):
1761
+ """
1762
+ Return the subcomplex space associated to this quotient.
1763
+
1764
+ That is, if this quotient is `K/L`, return `L`.
1765
+
1766
+ EXAMPLES::
1767
+
1768
+ sage: # needs sage.groups
1769
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
1770
+ sage: RP2 = RP5.n_skeleton(2)
1771
+ sage: RP5_2 = RP5.quotient(RP2)
1772
+ sage: RP5_2.subcomplex()
1773
+ Simplicial set with 3 non-degenerate simplices
1774
+
1775
+ sage: # needs sage.groups
1776
+ sage: G = groups.misc.MultiplicativeAbelian([2])
1777
+ sage: B = simplicial_sets.ClassifyingSpace(G)
1778
+ sage: K = B.n_skeleton(3)
1779
+ sage: Q = B.quotient(K)
1780
+ sage: Q.subcomplex()
1781
+ Simplicial set with 4 non-degenerate simplices
1782
+ """
1783
+ return self._maps[0].domain()
1784
+
1785
+ def n_skeleton(self, n):
1786
+ """
1787
+ Return the `n`-skeleton of this simplicial set.
1788
+
1789
+ That is, the simplicial set generated by all nondegenerate
1790
+ simplices of dimension at most `n`.
1791
+
1792
+ INPUT:
1793
+
1794
+ - ``n`` -- the dimension
1795
+
1796
+ The `n`-skeleton of the quotient is computed as the quotient
1797
+ of the `n`-skeleta.
1798
+
1799
+ EXAMPLES::
1800
+
1801
+ sage: # needs sage.groups
1802
+ sage: G = groups.misc.MultiplicativeAbelian([2])
1803
+ sage: B = simplicial_sets.ClassifyingSpace(G)
1804
+ sage: K = B.n_skeleton(3)
1805
+ sage: Q = B.quotient(K)
1806
+ sage: Q.n_skeleton(6)
1807
+ Quotient: (Simplicial set with 7
1808
+ non-degenerate simplices/Simplicial set with 4
1809
+ non-degenerate simplices)
1810
+ sage: Q.n_skeleton(6).homology() # needs sage.modules
1811
+ {0: 0, 1: 0, 2: 0, 3: 0, 4: Z, 5: C2, 6: 0}
1812
+ """
1813
+ if self.is_finite():
1814
+ ambient = SimplicialSet_finite.n_skeleton(self.ambient(), n)
1815
+ subcomplex = SimplicialSet_finite.n_skeleton(self.subcomplex(), n)
1816
+ subcomplex = ambient.subsimplicial_set(subcomplex.nondegenerate_simplices())
1817
+ return QuotientOfSimplicialSet_finite(subcomplex.inclusion_map(),
1818
+ vertex_name=self._vertex_name)
1819
+ start, skel = self._n_skeleton
1820
+ if start == n:
1821
+ return skel
1822
+ elif start > n:
1823
+ return skel.n_skeleton(n)
1824
+ ambient = self.ambient().n_skeleton(n)
1825
+ subcomplex = ambient.subsimplicial_set(self.subcomplex().nondegenerate_simplices(n))
1826
+ ans = QuotientOfSimplicialSet_finite(subcomplex.inclusion_map(),
1827
+ vertex_name=self._vertex_name)
1828
+ self._n_skeleton = (n, ans)
1829
+ return ans
1830
+
1831
+ def _repr_(self):
1832
+ """
1833
+ Print representation.
1834
+
1835
+ EXAMPLES::
1836
+
1837
+ sage: T = simplicial_sets.Torus()
1838
+ sage: T.quotient(T.n_skeleton(1))
1839
+ Quotient: (Torus/Simplicial set with 4 non-degenerate simplices)
1840
+ """
1841
+ return 'Quotient: ({}/{})'.format(self.ambient(), self.subcomplex())
1842
+
1843
+ def _latex_(self):
1844
+ r"""
1845
+ LaTeX representation.
1846
+
1847
+ EXAMPLES::
1848
+
1849
+ sage: # needs sage.groups
1850
+ sage: RPoo = simplicial_sets.RealProjectiveSpace(Infinity)
1851
+ sage: RP3 = RPoo.n_skeleton(3)
1852
+ sage: RP3.rename_latex('RP^{3}')
1853
+ sage: latex(RPoo.quotient(RP3))
1854
+ RP^{\infty} / RP^{3}
1855
+ """
1856
+ return '{} / {}'.format(latex(self.ambient()), latex(self.subcomplex()))
1857
+
1858
+
1859
+ class QuotientOfSimplicialSet_finite(QuotientOfSimplicialSet,
1860
+ PushoutOfSimplicialSets_finite):
1861
+ """
1862
+ The quotient of finite simplicial sets.
1863
+
1864
+ When the simplicial sets involved are finite, there is a
1865
+ :meth:`quotient_map` method available.
1866
+ """
1867
+ def __init__(self, inclusion, vertex_name='*'):
1868
+ r"""
1869
+ Return the quotient of a simplicial set by a subsimplicial set.
1870
+
1871
+ See :class:`QuotientOfSimplicialSet` for more information.
1872
+
1873
+ EXAMPLES::
1874
+
1875
+ sage: # needs sage.groups
1876
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
1877
+ sage: RP2 = RP5.n_skeleton(2)
1878
+ sage: RP5_2 = RP5.quotient(RP2); RP5_2
1879
+ Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
1880
+ sage: RP5_2.quotient_map()
1881
+ Simplicial set morphism:
1882
+ From: RP^5
1883
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
1884
+ Defn: [1, f, f * f, f * f * f, f * f * f * f, f * f * f * f * f]
1885
+ --> [*, s_0 *, s_1 s_0 *, f * f * f, f * f * f * f, f * f * f * f * f]
1886
+ """
1887
+ subcomplex = inclusion.domain()
1888
+ PushoutOfSimplicialSets_finite.__init__(self, [inclusion,
1889
+ subcomplex.constant_map()],
1890
+ vertex_name=vertex_name)
1891
+ ambient = inclusion.codomain()
1892
+ if ambient.is_pointed():
1893
+ if ambient.base_point() not in subcomplex:
1894
+ self._basepoint = self.structure_map(0)(ambient.base_point())
1895
+
1896
+ def quotient_map(self):
1897
+ """
1898
+ Return the quotient map from the original simplicial set to the
1899
+ quotient.
1900
+
1901
+ EXAMPLES::
1902
+
1903
+ sage: K = simplicial_sets.Simplex(1)
1904
+ sage: S1 = K.quotient(K.n_skeleton(0))
1905
+ sage: q = S1.quotient_map()
1906
+ sage: q
1907
+ Simplicial set morphism:
1908
+ From: 1-simplex
1909
+ To: Quotient: (1-simplex/Simplicial set with 2 non-degenerate simplices)
1910
+ Defn: [(0,), (1,), (0, 1)] --> [*, *, (0, 1)]
1911
+ sage: q.domain() == K
1912
+ True
1913
+ sage: q.codomain() == S1
1914
+ True
1915
+ """
1916
+ return self.structure_map(0)
1917
+
1918
+
1919
+ class SmashProductOfSimplicialSets_finite(QuotientOfSimplicialSet_finite,
1920
+ Factors):
1921
+ @staticmethod
1922
+ def __classcall__(cls, factors=None):
1923
+ """
1924
+ TESTS::
1925
+
1926
+ sage: from sage.topology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite as Smash
1927
+ sage: S2 = simplicial_sets.Sphere(2)
1928
+ sage: Smash([S2, S2]) == Smash((S2, S2))
1929
+ True
1930
+ """
1931
+ if factors:
1932
+ return super().__classcall__(cls, factors=tuple(factors))
1933
+ return super().__classcall__(cls)
1934
+
1935
+ def __init__(self, factors=None):
1936
+ r"""
1937
+ Return the smash product of finite pointed simplicial sets.
1938
+
1939
+ INPUT:
1940
+
1941
+ - ``factors`` -- list or tuple of simplicial sets
1942
+
1943
+ Return the smash product of the simplicial sets in
1944
+ ``factors``: the smash product `X \wedge Y` is defined to be
1945
+ the quotient `(X \times Y) / (X \vee Y)`, where `X \vee Y` is
1946
+ the wedge sum.
1947
+
1948
+ Each element of ``factors`` must be finite and pointed. (As of
1949
+ July 2016, constructing the wedge as a subcomplex of the
1950
+ product is only possible in Sage for finite simplicial sets.)
1951
+
1952
+ EXAMPLES::
1953
+
1954
+ sage: T = simplicial_sets.Torus()
1955
+ sage: S2 = simplicial_sets.Sphere(2)
1956
+ sage: T.smash_product(S2).homology() == T.suspension(2).homology() # needs sage.modules
1957
+ True
1958
+ """
1959
+ if any(not space.is_pointed() for space in factors):
1960
+ raise ValueError('the simplicial sets must be pointed')
1961
+ prod = ProductOfSimplicialSets_finite(factors)
1962
+ wedge = prod.wedge_as_subset()
1963
+ QuotientOfSimplicialSet_finite.__init__(self, wedge.inclusion_map())
1964
+ self._factors = factors
1965
+
1966
+ def _repr_(self):
1967
+ """
1968
+ Print representation.
1969
+
1970
+ EXAMPLES::
1971
+
1972
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4) # needs sage.groups
1973
+ sage: S1 = simplicial_sets.Sphere(1)
1974
+ sage: S1.smash_product(RP4, S1) # needs sage.groups
1975
+ Smash product: (S^1 ^ RP^4 ^ S^1)
1976
+ """
1977
+ s = 'Smash product: ('
1978
+ s += ' ^ '.join(str(X) for X in self._factors)
1979
+ s += ')'
1980
+ return s
1981
+
1982
+ def _latex_(self):
1983
+ r"""
1984
+ LaTeX representation.
1985
+
1986
+ EXAMPLES::
1987
+
1988
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4) # needs sage.groups
1989
+ sage: S1 = simplicial_sets.Sphere(1)
1990
+ sage: latex(S1.smash_product(RP4, S1)) # needs sage.groups
1991
+ S^{1} \wedge RP^{4} \wedge S^{1}
1992
+ """
1993
+ return ' \\wedge '.join(latex(X) for X in self._factors)
1994
+
1995
+
1996
+ class WedgeOfSimplicialSets(PushoutOfSimplicialSets, Factors):
1997
+ @staticmethod
1998
+ def __classcall__(cls, factors=None):
1999
+ """
2000
+ TESTS::
2001
+
2002
+ sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets
2003
+ sage: S2 = simplicial_sets.Sphere(2)
2004
+ sage: WedgeOfSimplicialSets([S2, S2]) == WedgeOfSimplicialSets((S2, S2))
2005
+ True
2006
+ """
2007
+ if factors:
2008
+ return super().__classcall__(cls, factors=tuple(factors))
2009
+ return super().__classcall__(cls)
2010
+
2011
+ def __init__(self, factors=None):
2012
+ r"""
2013
+ Return the wedge sum of pointed simplicial sets.
2014
+
2015
+ INPUT:
2016
+
2017
+ - ``factors`` -- list or tuple of simplicial sets
2018
+
2019
+ Return the wedge of the simplicial sets in ``factors``: the
2020
+ wedge sum `X \vee Y` is formed by taking the disjoint
2021
+ union of `X` and `Y` and identifying their base points. In
2022
+ this construction, the new base point is renamed '*'.
2023
+
2024
+ The wedge comes equipped with maps to and from each factor, or
2025
+ actually, maps from each factor, and maps to simplicial sets
2026
+ isomorphic to each factor. The codomains of the latter maps
2027
+ are quotients of the wedge, not identical to the original
2028
+ factors.
2029
+
2030
+ EXAMPLES::
2031
+
2032
+ sage: CP2 = simplicial_sets.ComplexProjectiveSpace(2)
2033
+ sage: K = simplicial_sets.KleinBottle()
2034
+ sage: W = CP2.wedge(K)
2035
+ sage: W.homology() # needs sage.modules
2036
+ {0: 0, 1: Z x C2, 2: Z, 3: 0, 4: Z}
2037
+
2038
+ sage: W.inclusion_map(1)
2039
+ Simplicial set morphism:
2040
+ From: Klein bottle
2041
+ To: Wedge: (CP^2 v Klein bottle)
2042
+ Defn: [Delta_{0,0}, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2043
+ --> [*, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2044
+
2045
+ sage: W.projection_map(0).domain()
2046
+ Wedge: (CP^2 v Klein bottle)
2047
+ sage: W.projection_map(0).codomain() # copy of CP^2
2048
+ Quotient: (Wedge: (CP^2 v Klein bottle)/Simplicial set with 6 non-degenerate simplices)
2049
+ sage: W.projection_map(0).codomain().homology() # needs sage.modules
2050
+ {0: 0, 1: 0, 2: Z, 3: 0, 4: Z}
2051
+
2052
+ An error occurs if any of the factors is not pointed::
2053
+
2054
+ sage: CP2.wedge(simplicial_sets.Simplex(1))
2055
+ Traceback (most recent call last):
2056
+ ...
2057
+ ValueError: the simplicial sets must be pointed
2058
+ """
2059
+ if any(not space.is_pointed() for space in factors):
2060
+ raise ValueError('the simplicial sets must be pointed')
2061
+ PushoutOfSimplicialSets.__init__(self, [space.base_point_map()
2062
+ for space in factors])
2063
+ if factors:
2064
+ vertices = PushoutOfSimplicialSets_finite([space.n_skeleton(0).base_point_map()
2065
+ for space in factors])
2066
+ self._basepoint = vertices.base_point()
2067
+ self.base_point().rename('*')
2068
+ self._factors = factors
2069
+
2070
+ summands = Factors.factors
2071
+ summand = Factors.factor
2072
+
2073
+ def _repr_(self):
2074
+ """
2075
+ Print representation.
2076
+
2077
+ EXAMPLES::
2078
+
2079
+ sage: K = simplicial_sets.KleinBottle()
2080
+ sage: K.wedge(K, K)
2081
+ Wedge: (Klein bottle v Klein bottle v Klein bottle)
2082
+ """
2083
+ s = 'Wedge: ('
2084
+ s += ' v '.join(str(X) for X in self._factors)
2085
+ s += ')'
2086
+ return s
2087
+
2088
+ def _latex_(self):
2089
+ r"""
2090
+ LaTeX representation.
2091
+
2092
+ EXAMPLES::
2093
+
2094
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4) # needs sage.groups
2095
+ sage: S1 = simplicial_sets.Sphere(1)
2096
+ sage: latex(S1.wedge(RP4, S1)) # needs sage.groups
2097
+ S^{1} \vee RP^{4} \vee S^{1}
2098
+ """
2099
+ return ' \\vee '.join(latex(X) for X in self._factors)
2100
+
2101
+
2102
+ class WedgeOfSimplicialSets_finite(WedgeOfSimplicialSets, PushoutOfSimplicialSets_finite):
2103
+ """
2104
+ The wedge sum of finite pointed simplicial sets.
2105
+ """
2106
+ def __init__(self, factors=None):
2107
+ r"""
2108
+ Return the wedge sum of finite pointed simplicial sets.
2109
+
2110
+ INPUT:
2111
+
2112
+ - ``factors`` -- tuple of simplicial sets
2113
+
2114
+ If there are no factors, a point is returned.
2115
+
2116
+ See :class:`WedgeOfSimplicialSets` for more information.
2117
+
2118
+ EXAMPLES::
2119
+
2120
+ sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets_finite
2121
+ sage: K = simplicial_sets.Simplex(3)
2122
+ sage: WedgeOfSimplicialSets_finite((K,K))
2123
+ Traceback (most recent call last):
2124
+ ...
2125
+ ValueError: the simplicial sets must be pointed
2126
+ """
2127
+ if not factors:
2128
+ # An empty wedge is a point, constructed as a pushout.
2129
+ PushoutOfSimplicialSets_finite.__init__(self, [Point().identity()])
2130
+ else:
2131
+ if any(not space.is_pointed() for space in factors):
2132
+ raise ValueError('the simplicial sets must be pointed')
2133
+ PushoutOfSimplicialSets_finite.__init__(self, [space.base_point_map()
2134
+ for space in factors])
2135
+ self.base_point().rename('*')
2136
+ self._factors = factors
2137
+
2138
+ def inclusion_map(self, i):
2139
+ """
2140
+ Return the inclusion map of the `i`-th factor.
2141
+
2142
+ EXAMPLES::
2143
+
2144
+ sage: S1 = simplicial_sets.Sphere(1)
2145
+ sage: S2 = simplicial_sets.Sphere(2)
2146
+ sage: W = S1.wedge(S2, S1)
2147
+ sage: W.inclusion_map(1)
2148
+ Simplicial set morphism:
2149
+ From: S^2
2150
+ To: Wedge: (S^1 v S^2 v S^1)
2151
+ Defn: [v_0, sigma_2] --> [*, sigma_2]
2152
+ sage: W.inclusion_map(0).domain()
2153
+ S^1
2154
+ sage: W.inclusion_map(2).domain()
2155
+ S^1
2156
+ """
2157
+ return self.structure_map(i)
2158
+
2159
+ def projection_map(self, i):
2160
+ """
2161
+ Return the projection map onto the `i`-th factor.
2162
+
2163
+ EXAMPLES::
2164
+
2165
+ sage: S1 = simplicial_sets.Sphere(1)
2166
+ sage: S2 = simplicial_sets.Sphere(2)
2167
+ sage: W = S1.wedge(S2, S1)
2168
+ sage: W.projection_map(1)
2169
+ Simplicial set morphism:
2170
+ From: Wedge: (S^1 v S^2 v S^1)
2171
+ To: Quotient: (Wedge: (S^1 v S^2 v S^1)/Simplicial set with
2172
+ 3 non-degenerate simplices)
2173
+ Defn: [*, sigma_1, sigma_1, sigma_2] --> [*, s_0 *, s_0 *, sigma_2]
2174
+ sage: W.projection_map(1).image().homology(1) # needs sage.modules
2175
+ 0
2176
+ sage: W.projection_map(1).image().homology(2) # needs sage.modules
2177
+ Z
2178
+ """
2179
+ m = len(self._factors)
2180
+ simplices = ([self.inclusion_map(j).image().nondegenerate_simplices()
2181
+ for j in range(i)]
2182
+ + [self.inclusion_map(j).image().nondegenerate_simplices()
2183
+ for j in range(i+1, m)])
2184
+ return self.quotient(list(itertools.chain(*simplices))).quotient_map()
2185
+
2186
+
2187
+ class DisjointUnionOfSimplicialSets(PushoutOfSimplicialSets, Factors):
2188
+ @staticmethod
2189
+ def __classcall__(cls, factors=None):
2190
+ """
2191
+ TESTS::
2192
+
2193
+ sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets
2194
+ sage: from sage.topology.simplicial_set_examples import Empty
2195
+ sage: S2 = simplicial_sets.Sphere(2)
2196
+ sage: DisjointUnionOfSimplicialSets([S2, S2]) == DisjointUnionOfSimplicialSets((S2, S2))
2197
+ True
2198
+ sage: DisjointUnionOfSimplicialSets([S2, Empty(), S2, Empty()]) == DisjointUnionOfSimplicialSets((S2, S2))
2199
+ True
2200
+ """
2201
+ if factors:
2202
+ # Discard any empty factors.
2203
+ factors = [F for F in factors if F != Empty()]
2204
+ if factors:
2205
+ return super().__classcall__(cls, factors=tuple(factors))
2206
+ return super().__classcall__(cls)
2207
+
2208
+ def __init__(self, factors=None):
2209
+ r"""
2210
+ Return the disjoint union of simplicial sets.
2211
+
2212
+ INPUT:
2213
+
2214
+ - ``factors`` -- list or tuple of simplicial sets
2215
+
2216
+ Discard any factors which are empty and return the disjoint
2217
+ union of the remaining simplicial sets in ``factors``. The
2218
+ disjoint union comes equipped with a map from each factor, as
2219
+ long as all of the factors are finite.
2220
+
2221
+ EXAMPLES::
2222
+
2223
+ sage: CP2 = simplicial_sets.ComplexProjectiveSpace(2)
2224
+ sage: K = simplicial_sets.KleinBottle()
2225
+ sage: W = CP2.disjoint_union(K)
2226
+ sage: W.homology() # needs sage.modules
2227
+ {0: Z, 1: Z x C2, 2: Z, 3: 0, 4: Z}
2228
+
2229
+ sage: W.inclusion_map(1)
2230
+ Simplicial set morphism:
2231
+ From: Klein bottle
2232
+ To: Disjoint union: (CP^2 u Klein bottle)
2233
+ Defn: [Delta_{0,0}, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2234
+ --> [Delta_{0,0}, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2235
+ """
2236
+ PushoutOfSimplicialSets.__init__(self, [space._map_from_empty_set()
2237
+ for space in factors])
2238
+ self._factors = factors
2239
+ self._n_skeleton = (-1, Empty())
2240
+
2241
+ def n_skeleton(self, n):
2242
+ """
2243
+ Return the `n`-skeleton of this simplicial set.
2244
+
2245
+ That is, the simplicial set generated by all nondegenerate
2246
+ simplices of dimension at most `n`.
2247
+
2248
+ INPUT:
2249
+
2250
+ - ``n`` -- the dimension
2251
+
2252
+ The `n`-skeleton of the disjoint union is computed as the
2253
+ disjoint union of the `n`-skeleta of the component simplicial
2254
+ sets.
2255
+
2256
+ EXAMPLES::
2257
+
2258
+ sage: # needs sage.groups
2259
+ sage: G = groups.misc.MultiplicativeAbelian([2])
2260
+ sage: B = simplicial_sets.ClassifyingSpace(G)
2261
+ sage: T = simplicial_sets.Torus()
2262
+ sage: X = B.disjoint_union(T)
2263
+ sage: X.n_skeleton(3).homology() # needs sage.modules
2264
+ {0: Z, 1: Z x Z x C2, 2: Z, 3: Z}
2265
+ """
2266
+ if self.is_finite():
2267
+ return DisjointUnionOfSimplicialSets_finite(tuple([X.n_skeleton(n)
2268
+ for X in self._factors]))
2269
+ start, skel = self._n_skeleton
2270
+ if start == n:
2271
+ return skel
2272
+ elif start > n:
2273
+ return skel.n_skeleton(n)
2274
+ ans = DisjointUnionOfSimplicialSets_finite(tuple([X.n_skeleton(n)
2275
+ for X in self._factors]))
2276
+ self._n_skeleton = (n, ans)
2277
+ return ans
2278
+
2279
+ summands = Factors.factors
2280
+ summand = Factors.factor
2281
+
2282
+ def _repr_(self):
2283
+ """
2284
+ Print representation.
2285
+
2286
+ EXAMPLES::
2287
+
2288
+ sage: T = simplicial_sets.Torus()
2289
+ sage: RP3 = simplicial_sets.RealProjectiveSpace(3) # needs sage.groups
2290
+ sage: T.disjoint_union(T, RP3) # needs sage.groups
2291
+ Disjoint union: (Torus u Torus u RP^3)
2292
+ """
2293
+ s = 'Disjoint union: ('
2294
+ s += ' u '.join(str(X) for X in self._factors)
2295
+ s += ')'
2296
+ return s
2297
+
2298
+ def _latex_(self):
2299
+ r"""
2300
+ LaTeX representation.
2301
+
2302
+ EXAMPLES::
2303
+
2304
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4) # needs sage.groups
2305
+ sage: S1 = simplicial_sets.Sphere(1)
2306
+ sage: latex(S1.disjoint_union(RP4, S1)) # needs sage.groups
2307
+ S^{1} \amalg RP^{4} \amalg S^{1}
2308
+ """
2309
+ return ' \\amalg '.join(latex(X) for X in self._factors)
2310
+
2311
+
2312
+ class DisjointUnionOfSimplicialSets_finite(DisjointUnionOfSimplicialSets,
2313
+ PushoutOfSimplicialSets_finite):
2314
+ """
2315
+ The disjoint union of finite simplicial sets.
2316
+ """
2317
+ def __init__(self, factors=None):
2318
+ r"""
2319
+ Return the disjoint union of finite simplicial sets.
2320
+
2321
+ INPUT:
2322
+
2323
+ - ``factors`` -- tuple of simplicial sets
2324
+
2325
+ Return the disjoint union of the simplicial sets in
2326
+ ``factors``. The disjoint union comes equipped with a map
2327
+ from each factor. If there are no factors, this returns the
2328
+ empty simplicial set.
2329
+
2330
+ EXAMPLES::
2331
+
2332
+ sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets_finite
2333
+ sage: from sage.topology.simplicial_set_examples import Empty
2334
+ sage: S = simplicial_sets.Sphere(4)
2335
+ sage: DisjointUnionOfSimplicialSets_finite((S,S,S))
2336
+ Disjoint union: (S^4 u S^4 u S^4)
2337
+ sage: DisjointUnionOfSimplicialSets_finite([Empty(), Empty()]) == Empty()
2338
+ True
2339
+ """
2340
+ if not factors:
2341
+ PushoutOfSimplicialSets_finite.__init__(self)
2342
+ else:
2343
+ PushoutOfSimplicialSets_finite.__init__(self, [space._map_from_empty_set()
2344
+ for space in factors])
2345
+ self._factors = factors
2346
+
2347
+ def inclusion_map(self, i):
2348
+ """
2349
+ Return the inclusion map of the `i`-th factor.
2350
+
2351
+ EXAMPLES::
2352
+
2353
+ sage: S1 = simplicial_sets.Sphere(1)
2354
+ sage: S2 = simplicial_sets.Sphere(2)
2355
+ sage: W = S1.disjoint_union(S2, S1)
2356
+ sage: W.inclusion_map(1)
2357
+ Simplicial set morphism:
2358
+ From: S^2
2359
+ To: Disjoint union: (S^1 u S^2 u S^1)
2360
+ Defn: [v_0, sigma_2] --> [v_0, sigma_2]
2361
+ sage: W.inclusion_map(0).domain()
2362
+ S^1
2363
+ sage: W.inclusion_map(2).domain()
2364
+ S^1
2365
+ """
2366
+ return self.structure_map(i)
2367
+
2368
+
2369
+ class ConeOfSimplicialSet(SimplicialSet_arbitrary, UniqueRepresentation):
2370
+ def __init__(self, base):
2371
+ r"""
2372
+ Return the unreduced cone on a finite simplicial set.
2373
+
2374
+ INPUT:
2375
+
2376
+ - ``base`` -- return the cone on this simplicial set
2377
+
2378
+ Add a point `*` (which will become the base point) and for
2379
+ each simplex `\sigma` in ``base``, add both `\sigma` and a
2380
+ simplex made up of `*` and `\sigma` (topologically, form the
2381
+ join of `*` and `\sigma`).
2382
+
2383
+ EXAMPLES::
2384
+
2385
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2386
+ sage: v = AbstractSimplex(0, name='v')
2387
+ sage: e = AbstractSimplex(1, name='e')
2388
+ sage: X = SimplicialSet({e: (v, v)})
2389
+ sage: CX = X.cone() # indirect doctest
2390
+ sage: CX.nondegenerate_simplices()
2391
+ [*, v, (v,*), e, (e,*)]
2392
+ sage: CX.base_point()
2393
+ *
2394
+ """
2395
+ Cat = SimplicialSets().Pointed()
2396
+ if base.is_finite():
2397
+ Cat = Cat.Finite()
2398
+ Parent.__init__(self, category=Cat)
2399
+ star = AbstractSimplex(0, name='*')
2400
+ self._base = base
2401
+ self._basepoint = star
2402
+ self._n_skeleton = (-1, Empty())
2403
+
2404
+ def n_skeleton(self, n):
2405
+ """
2406
+ Return the `n`-skeleton of this simplicial set.
2407
+
2408
+ That is, the simplicial set generated by all nondegenerate
2409
+ simplices of dimension at most `n`.
2410
+
2411
+ INPUT:
2412
+
2413
+ - ``n`` -- the dimension
2414
+
2415
+ In the case when the cone is infinite, the `n`-skeleton of the
2416
+ cone is computed as the `n`-skeleton of the cone of the
2417
+ `n`-skeleton.
2418
+
2419
+ EXAMPLES::
2420
+
2421
+ sage: # needs sage.groups
2422
+ sage: G = groups.misc.MultiplicativeAbelian([2])
2423
+ sage: B = simplicial_sets.ClassifyingSpace(G)
2424
+ sage: X = B.disjoint_union(B)
2425
+ sage: CX = B.cone()
2426
+ sage: CX.n_skeleton(3).homology() # needs sage.modules
2427
+ {0: 0, 1: 0, 2: 0, 3: Z}
2428
+ """
2429
+ if self.is_finite():
2430
+ return SimplicialSet_finite.n_skeleton(self, n)
2431
+ start, skel = self._n_skeleton
2432
+ if start == n:
2433
+ return skel
2434
+ elif start > n:
2435
+ return skel.n_skeleton(n)
2436
+ ans = ConeOfSimplicialSet_finite(self._base.n_skeleton(n)).n_skeleton(n)
2437
+ self._n_skeleton = (n, ans)
2438
+ self._basepoint = ans.base_point()
2439
+ return ans
2440
+
2441
+ def _repr_(self):
2442
+ """
2443
+ Print representation.
2444
+
2445
+ EXAMPLES::
2446
+
2447
+ sage: simplicial_sets.Simplex(3).cone()
2448
+ Cone of 3-simplex
2449
+ """
2450
+ return 'Cone of {}'.format(self._base)
2451
+
2452
+ def _latex_(self):
2453
+ r"""
2454
+ LaTeX representation.
2455
+
2456
+ EXAMPLES::
2457
+
2458
+ sage: latex(simplicial_sets.Simplex(3).cone())
2459
+ C \Delta^{3}
2460
+ """
2461
+ return 'C {}'.format(latex(self._base))
2462
+
2463
+
2464
+ class ConeOfSimplicialSet_finite(ConeOfSimplicialSet, SimplicialSet_finite):
2465
+ def __init__(self, base):
2466
+ r"""
2467
+ Return the unreduced cone on a finite simplicial set.
2468
+
2469
+ INPUT:
2470
+
2471
+ - ``base`` -- return the cone on this simplicial set
2472
+
2473
+ Add a point `*` (which will become the base point) and for
2474
+ each simplex `\sigma` in ``base``, add both `\sigma` and a
2475
+ simplex made up of `*` and `\sigma` (topologically, form the
2476
+ join of `*` and `\sigma`).
2477
+
2478
+ EXAMPLES::
2479
+
2480
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2481
+ sage: v = AbstractSimplex(0, name='v')
2482
+ sage: e = AbstractSimplex(1, name='e')
2483
+ sage: X = SimplicialSet({e: (v, v)})
2484
+ sage: CX = X.cone() # indirect doctest
2485
+ sage: CX.nondegenerate_simplices()
2486
+ [*, v, (v,*), e, (e,*)]
2487
+ sage: CX.base_point()
2488
+ *
2489
+ """
2490
+ star = AbstractSimplex(0, name='*')
2491
+ data = {}
2492
+ data[star] = None
2493
+ # Dictionary for translating old simplices to new: keys are
2494
+ # old simplices, corresponding value is the new simplex
2495
+ # (sigma, *).
2496
+ new_simplices = {'cone': star}
2497
+ for sigma in base.nondegenerate_simplices():
2498
+ new = AbstractSimplex(sigma.dimension()+1,
2499
+ name='({},*)'.format(sigma),
2500
+ latex_name='({},*)'.format(latex(sigma)))
2501
+ if sigma.dimension() == 0:
2502
+ data[sigma] = None
2503
+ data[new] = (star, sigma)
2504
+ else:
2505
+ sigma_faces = base.face_data()[sigma]
2506
+ data[sigma] = sigma_faces
2507
+ new_faces = [new_simplices[face.nondegenerate()].apply_degeneracies(*face.degeneracies())
2508
+ for face in sigma_faces]
2509
+ data[new] = (new_faces + [sigma])
2510
+ new_simplices[sigma] = new
2511
+ SimplicialSet_finite.__init__(self, data, base_point=star)
2512
+ # self._base: original simplicial set.
2513
+ self._base = base
2514
+ # self._joins: dictionary, each key is a simplex sigma in
2515
+ # base, the corresponding value is the new simplex (sigma, *)
2516
+ # in the cone. Also, one other key is 'cone', and the value is
2517
+ # the cone vertex. This is used in the suspension class to
2518
+ # construct the suspension of a morphism. It could be used to
2519
+ # construct the cone of a morphism, also, although cones of
2520
+ # morphisms are not yet implemented.
2521
+ self._joins = new_simplices
2522
+
2523
+ def base_as_subset(self):
2524
+ """
2525
+ If this is the cone `CX` on `X`, return `X` as a subsimplicial set.
2526
+
2527
+ EXAMPLES::
2528
+
2529
+ sage: # needs sage.groups
2530
+ sage: X = simplicial_sets.RealProjectiveSpace(4).unset_base_point()
2531
+ sage: Y = X.cone()
2532
+ sage: Y.base_as_subset()
2533
+ Simplicial set with 5 non-degenerate simplices
2534
+ sage: Y.base_as_subset() == X
2535
+ True
2536
+ """
2537
+ X = self._base
2538
+ return self.subsimplicial_set(X.nondegenerate_simplices())
2539
+
2540
+ def map_from_base(self):
2541
+ r"""
2542
+ If this is the cone `CX` on `X`, return the inclusion map from `X`.
2543
+
2544
+ EXAMPLES::
2545
+
2546
+ sage: X = simplicial_sets.Simplex(2).n_skeleton(1)
2547
+ sage: Y = X.cone()
2548
+ sage: Y.map_from_base()
2549
+ Simplicial set morphism:
2550
+ From: Simplicial set with 6 non-degenerate simplices
2551
+ To: Cone of Simplicial set with 6 non-degenerate simplices
2552
+ Defn: [(0,), (1,), (2,), (0, 1), (0, 2), (1, 2)]
2553
+ --> [(0,), (1,), (2,), (0, 1), (0, 2), (1, 2)]
2554
+ """
2555
+ return self.base_as_subset().inclusion_map()
2556
+
2557
+
2558
+ class ReducedConeOfSimplicialSet(QuotientOfSimplicialSet):
2559
+ def __init__(self, base):
2560
+ r"""
2561
+ Return the reduced cone on a simplicial set.
2562
+
2563
+ INPUT:
2564
+
2565
+ - ``base`` -- return the cone on this simplicial set
2566
+
2567
+ Start with the unreduced cone: take ``base`` and add a point
2568
+ `*` (which will become the base point) and for each simplex
2569
+ `\sigma` in ``base``, add both `\sigma` and a simplex made up
2570
+ of `*` and `\sigma` (topologically, form the join of `*` and
2571
+ `\sigma`).
2572
+
2573
+ Now reduce: take the quotient by the 1-simplex connecting the
2574
+ old base point to the new one.
2575
+
2576
+ EXAMPLES::
2577
+
2578
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2579
+ sage: v = AbstractSimplex(0, name='v')
2580
+ sage: e = AbstractSimplex(1, name='e')
2581
+ sage: X = SimplicialSet({e: (v, v)})
2582
+ sage: X = X.set_base_point(v)
2583
+ sage: CX = X.cone() # indirect doctest
2584
+ sage: CX.nondegenerate_simplices()
2585
+ [*, e, (e,*)]
2586
+ """
2587
+ C = ConeOfSimplicialSet(base)
2588
+ for t in C.n_cells(1):
2589
+ edge_faces = sorted([C.base_point(), base.base_point()])
2590
+ if sorted(C.faces(t)) == edge_faces:
2591
+ edge = t
2592
+ break
2593
+ inc = C.subsimplicial_set([edge]).inclusion_map()
2594
+ QuotientOfSimplicialSet.__init__(self, inc)
2595
+ self._base = base
2596
+ self._n_skeleton = (-1, Empty())
2597
+
2598
+ def n_skeleton(self, n):
2599
+ """
2600
+ Return the `n`-skeleton of this simplicial set.
2601
+
2602
+ That is, the simplicial set generated by all nondegenerate
2603
+ simplices of dimension at most `n`.
2604
+
2605
+ INPUT:
2606
+
2607
+ - ``n`` -- the dimension
2608
+
2609
+ In the case when the cone is infinite, the `n`-skeleton of the
2610
+ cone is computed as the `n`-skeleton of the cone of the
2611
+ `n`-skeleton.
2612
+
2613
+ EXAMPLES::
2614
+
2615
+ sage: G = groups.misc.MultiplicativeAbelian([2]) # needs sage.groups
2616
+ sage: B = simplicial_sets.ClassifyingSpace(G) # needs sage.groups
2617
+ sage: B.cone().n_skeleton(3).homology() # needs sage.groups sage.modules
2618
+ {0: 0, 1: 0, 2: 0, 3: Z}
2619
+ """
2620
+ if self.is_finite():
2621
+ return SimplicialSet_finite.n_skeleton(self, n)
2622
+ start, skel = self._n_skeleton
2623
+ if start == n:
2624
+ return skel
2625
+ elif start > n:
2626
+ return skel.n_skeleton(n)
2627
+ ans = ReducedConeOfSimplicialSet_finite(self._base.n_skeleton(n)).n_skeleton(n)
2628
+ self._n_skeleton = (n, ans)
2629
+ return ans
2630
+
2631
+ def _repr_(self):
2632
+ """
2633
+ Print representation.
2634
+
2635
+ EXAMPLES::
2636
+
2637
+ sage: X = simplicial_sets.Sphere(4)
2638
+ sage: X.cone()
2639
+ Reduced cone of S^4
2640
+ """
2641
+ return 'Reduced cone of {}'.format(self._base)
2642
+
2643
+ def _latex_(self):
2644
+ r"""
2645
+ LaTeX representation.
2646
+
2647
+ EXAMPLES::
2648
+
2649
+ sage: latex(simplicial_sets.Sphere(4).cone())
2650
+ C S^{4}
2651
+ """
2652
+ return 'C {}'.format(latex(self._base))
2653
+
2654
+
2655
+ class ReducedConeOfSimplicialSet_finite(ReducedConeOfSimplicialSet,
2656
+ QuotientOfSimplicialSet_finite):
2657
+ def __init__(self, base):
2658
+ r"""
2659
+ Return the reduced cone on a simplicial set.
2660
+
2661
+ INPUT:
2662
+
2663
+ - ``base`` -- return the cone on this simplicial set
2664
+
2665
+ Start with the unreduced cone: take ``base`` and add a point
2666
+ `*` (which will become the base point) and for each simplex
2667
+ `\sigma` in ``base``, add both `\sigma` and a simplex made up
2668
+ of `*` and `\sigma` (topologically, form the join of `*` and
2669
+ `\sigma`).
2670
+
2671
+ Now reduce: take the quotient by the 1-simplex connecting the
2672
+ old base point to the new one.
2673
+
2674
+ EXAMPLES::
2675
+
2676
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2677
+ sage: v = AbstractSimplex(0, name='v')
2678
+ sage: e = AbstractSimplex(1, name='e')
2679
+ sage: X = SimplicialSet({e: (v, v)})
2680
+ sage: X = X.set_base_point(v)
2681
+ sage: CX = X.cone() # indirect doctest
2682
+ sage: CX.nondegenerate_simplices()
2683
+ [*, e, (e,*)]
2684
+ """
2685
+ C = ConeOfSimplicialSet_finite(base)
2686
+ edge_faces = sorted([C.base_point(), base.base_point()])
2687
+ for t in C.n_cells(1):
2688
+ if sorted(C.faces(t)) == edge_faces:
2689
+ edge = t
2690
+ break
2691
+ inc = C.subsimplicial_set([edge]).inclusion_map()
2692
+ QuotientOfSimplicialSet_finite.__init__(self, inc)
2693
+ self._base = base
2694
+ q = self.quotient_map()
2695
+ self._joins = {sigma: q(C._joins[sigma]) for sigma in C._joins}
2696
+
2697
+ def map_from_base(self):
2698
+ r"""
2699
+ If this is the cone `\tilde{C}X` on `X`, return the map from `X`.
2700
+
2701
+ The map is defined to be the composite `X \to CX \to
2702
+ \tilde{C}X`. This is used by the
2703
+ :class:`SuspensionOfSimplicialSet_finite` class to construct
2704
+ the reduced suspension: take the quotient of the reduced cone
2705
+ by the image of `X` therein.
2706
+
2707
+ EXAMPLES::
2708
+
2709
+ sage: S3 = simplicial_sets.Sphere(3)
2710
+ sage: CS3 = S3.cone()
2711
+ sage: CS3.map_from_base()
2712
+ Simplicial set morphism:
2713
+ From: S^3
2714
+ To: Reduced cone of S^3
2715
+ Defn: [v_0, sigma_3] --> [*, sigma_3]
2716
+ """
2717
+ quotient_map = self.quotient_map()
2718
+ unreduced = quotient_map.domain()
2719
+ temp_map = unreduced.map_from_base()
2720
+ X = self._base
2721
+ incl = X.Hom(unreduced)(temp_map._dictionary)
2722
+ return quotient_map * incl
2723
+
2724
+
2725
+ class SuspensionOfSimplicialSet(SimplicialSet_arbitrary, UniqueRepresentation):
2726
+ def __init__(self, base):
2727
+ r"""
2728
+ Return the (reduced) suspension of a simplicial set.
2729
+
2730
+ INPUT:
2731
+
2732
+ - ``base`` -- return the suspension of this simplicial set
2733
+
2734
+ If this simplicial set ``X=base`` is not pointed, or if it is
2735
+ itself an unreduced suspension, return the unreduced
2736
+ suspension: the quotient `CX/X`, where `CX` is the (ordinary,
2737
+ unreduced) cone on `X`. If `X` is pointed, then use the
2738
+ reduced cone instead, and so return the reduced suspension.
2739
+
2740
+ We use `S` to denote unreduced suspension, `\Sigma` for
2741
+ reduced suspension.
2742
+
2743
+ EXAMPLES::
2744
+
2745
+ sage: # needs sage.groups
2746
+ sage: G = groups.misc.MultiplicativeAbelian([2])
2747
+ sage: B = simplicial_sets.ClassifyingSpace(G)
2748
+ sage: B.suspension()
2749
+ Sigma(Classifying space of Multiplicative Abelian group isomorphic to C2)
2750
+ sage: B.suspension().n_skeleton(3).homology() # needs sage.modules
2751
+ {0: 0, 1: 0, 2: C2, 3: 0}
2752
+
2753
+ If ``X`` is finite, the suspension comes with a quotient map
2754
+ from the cone::
2755
+
2756
+ sage: S3 = simplicial_sets.Sphere(3)
2757
+ sage: S4 = S3.suspension()
2758
+ sage: S4.quotient_map()
2759
+ Simplicial set morphism:
2760
+ From: Reduced cone of S^3
2761
+ To: Sigma(S^3)
2762
+ Defn: [*, sigma_3, (sigma_3,*)] --> [*, s_2 s_1 s_0 *, (sigma_3,*)]
2763
+
2764
+ TESTS::
2765
+
2766
+ sage: S3.suspension() == S3.suspension()
2767
+ True
2768
+ sage: S3.suspension() == simplicial_sets.Sphere(3).suspension()
2769
+ False
2770
+ sage: B.suspension() == B.suspension() # needs sage.groups
2771
+ True
2772
+ """
2773
+ Cat = SimplicialSets()
2774
+ if base.is_finite():
2775
+ Cat = Cat.Finite()
2776
+ reduced = (base.is_pointed()
2777
+ and (not hasattr(base, '_reduced')
2778
+ or (hasattr(base, '_reduced') and base._reduced)))
2779
+ if reduced:
2780
+ Cat = Cat.Pointed()
2781
+ Parent.__init__(self, category=Cat)
2782
+ self._reduced = reduced
2783
+ self._base = base
2784
+ self._n_skeleton = (-1, Empty())
2785
+
2786
+ def n_skeleton(self, n):
2787
+ """
2788
+ Return the `n`-skeleton of this simplicial set.
2789
+
2790
+ That is, the simplicial set generated by all nondegenerate
2791
+ simplices of dimension at most `n`.
2792
+
2793
+ INPUT:
2794
+
2795
+ - ``n`` -- the dimension
2796
+
2797
+ In the case when the suspension is infinite, the `n`-skeleton
2798
+ of the suspension is computed as the `n`-skeleton of the
2799
+ suspension of the `n`-skeleton.
2800
+
2801
+ EXAMPLES::
2802
+
2803
+ sage: # needs sage.groups
2804
+ sage: G = groups.misc.MultiplicativeAbelian([2])
2805
+ sage: B = simplicial_sets.ClassifyingSpace(G)
2806
+ sage: SigmaB = B.suspension()
2807
+ sage: SigmaB.n_skeleton(4).homology(base_ring=GF(2)) # needs sage.modules
2808
+ {0: Vector space of dimension 0 over Finite Field of size 2,
2809
+ 1: Vector space of dimension 0 over Finite Field of size 2,
2810
+ 2: Vector space of dimension 1 over Finite Field of size 2,
2811
+ 3: Vector space of dimension 1 over Finite Field of size 2,
2812
+ 4: Vector space of dimension 1 over Finite Field of size 2}
2813
+ """
2814
+ if self.is_finite():
2815
+ return SimplicialSet_finite.n_skeleton(self, n)
2816
+ start, skel = self._n_skeleton
2817
+ if start == n:
2818
+ return skel
2819
+ elif start > n:
2820
+ return skel.n_skeleton(n)
2821
+ ans = SuspensionOfSimplicialSet_finite(self._base.n_skeleton(n)).n_skeleton(n)
2822
+ self._n_skeleton = (n, ans)
2823
+ return ans
2824
+
2825
+ def __repr_or_latex__(self, output_type=None):
2826
+ r"""
2827
+ Print representation, for either :meth:`_repr_` or :meth:`_latex_`.
2828
+
2829
+ INPUT:
2830
+
2831
+ - ``output_type`` -- either ``'latex'`` for LaTeX output or
2832
+ anything else for ``str`` output
2833
+
2834
+ We use `S` to denote unreduced suspension, `\Sigma` for
2835
+ reduced suspension.
2836
+
2837
+ EXAMPLES::
2838
+
2839
+ sage: T = simplicial_sets.Torus()
2840
+ sage: K = T.suspension(10)
2841
+ sage: K.__repr_or_latex__()
2842
+ 'Sigma^10(Torus)'
2843
+ sage: K.__repr_or_latex__('latex')
2844
+ '\\Sigma^{10}(S^{1} \\times S^{1})'
2845
+ """
2846
+ latex_output = (output_type == 'latex')
2847
+ base = self._base
2848
+ if self._reduced:
2849
+ # Reduced suspension.
2850
+ if latex_output:
2851
+ symbol = '\\Sigma'
2852
+ else:
2853
+ symbol = 'Sigma'
2854
+ else:
2855
+ # Unreduced suspension.
2856
+ symbol = 'S'
2857
+ idx = 1
2858
+ while isinstance(base, SuspensionOfSimplicialSet):
2859
+ idx += 1
2860
+ base = base._base
2861
+ if latex_output:
2862
+ base = latex(base)
2863
+ exp = '^{{{}}}'
2864
+ else:
2865
+ exp = '^{}'
2866
+ if idx > 1:
2867
+ return ('{}' + exp + '({})').format(symbol, idx, base)
2868
+ else:
2869
+ return ('{}({})').format(symbol, base)
2870
+
2871
+ def _repr_(self):
2872
+ r"""
2873
+ Print representation.
2874
+
2875
+ We use `S` to denote unreduced suspension, `\Sigma` for
2876
+ reduced suspension.
2877
+
2878
+ EXAMPLES::
2879
+
2880
+ sage: S2 = simplicial_sets.Sphere(2)
2881
+ sage: S2.suspension(3)
2882
+ Sigma^3(S^2)
2883
+ sage: K = simplicial_sets.Simplex(2)
2884
+ sage: K.suspension(3)
2885
+ S^3(2-simplex)
2886
+ sage: K.suspension()
2887
+ S(2-simplex)
2888
+ """
2889
+ return self.__repr_or_latex__()
2890
+
2891
+ def _latex_(self):
2892
+ r"""
2893
+ LaTeX representation.
2894
+
2895
+ We use `S` to denote unreduced suspension, `\Sigma` for
2896
+ reduced suspension.
2897
+
2898
+ EXAMPLES::
2899
+
2900
+ sage: S2 = simplicial_sets.Sphere(2)
2901
+ sage: latex(S2.suspension(3))
2902
+ \Sigma^{3}(S^{2})
2903
+ sage: K = simplicial_sets.Simplex(2)
2904
+ sage: latex(K.suspension(3))
2905
+ S^{3}(\Delta^{2})
2906
+ sage: latex(K.suspension())
2907
+ S(\Delta^{2})
2908
+ """
2909
+ return self.__repr_or_latex__('latex')
2910
+
2911
+
2912
+ class SuspensionOfSimplicialSet_finite(SuspensionOfSimplicialSet,
2913
+ QuotientOfSimplicialSet_finite):
2914
+ """
2915
+ The (reduced) suspension of a finite simplicial set.
2916
+
2917
+ See :class:`SuspensionOfSimplicialSet` for more information.
2918
+ """
2919
+ def __init__(self, base):
2920
+ r"""
2921
+ INPUT:
2922
+
2923
+ - ``base`` -- return the suspension of this finite simplicial set
2924
+
2925
+ See :class:`SuspensionOfSimplicialSet` for more information.
2926
+
2927
+ EXAMPLES::
2928
+
2929
+ sage: X = simplicial_sets.Sphere(3)
2930
+ sage: X.suspension(2)
2931
+ Sigma^2(S^3)
2932
+ sage: Y = X.unset_base_point()
2933
+ sage: Y.suspension(2)
2934
+ S^2(Simplicial set with 2 non-degenerate simplices)
2935
+ """
2936
+ self._base = base
2937
+ reduced = (base.is_pointed()
2938
+ and (not hasattr(base, '_reduced')
2939
+ or (hasattr(base, '_reduced') and base._reduced)))
2940
+ if reduced:
2941
+ C = ReducedConeOfSimplicialSet_finite(base)
2942
+ subcomplex = C.map_from_base().image()
2943
+ else:
2944
+ C = ConeOfSimplicialSet_finite(base)
2945
+ subcomplex = C.base_as_subset()
2946
+ QuotientOfSimplicialSet_finite.__init__(self, subcomplex.inclusion_map())
2947
+ self._reduced = reduced
2948
+ # self._suspensions: dictionary, each key is a simplex sigma
2949
+ # in base, the corresponding value is the new simplex (sigma, *)
2950
+ # in S(base). Another key is 'cone', and its value is the cone
2951
+ # vertex in C(base). This is used to construct the suspension of a
2952
+ # morphism.
2953
+ q = self.quotient_map()
2954
+ self._suspensions = {sigma: q(C._joins[sigma]) for sigma in C._joins}