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,4102 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs
3
+ r"""
4
+ Simplicial sets
5
+
6
+ AUTHORS:
7
+
8
+ - John H. Palmieri (2016-07)
9
+
10
+ This module implements simplicial sets.
11
+
12
+ A *simplicial set* `X` is a collection of sets `X_n` indexed by the
13
+ nonnegative integers; the set `X_n` is called the set of
14
+ `n`-simplices. These sets are connected by maps
15
+
16
+ .. MATH::
17
+
18
+ d_i: X_n \to X_{n-1}, \ \ 0 \leq i \leq n \ \ \text{(face maps)} \\
19
+ s_j: X_n \to X_{n+1}, \ \ 0 \leq j \leq n \ \ \text{(degeneracy maps)}
20
+
21
+ satisfying the *simplicial identities*:
22
+
23
+ .. MATH::
24
+
25
+ d_i d_j &= d_{j-1} d_i \ \ \text{if } i<j \\
26
+ d_i s_j &= s_{j-1} d_i \ \ \text{if } i<j \\
27
+ d_j s_j &= 1 = d_{j+1} s_j \\
28
+ d_i s_j &= s_{j} d_{i-1} \ \ \text{if } i>j+1 \\
29
+ s_i s_j &= s_{j+1} s_{i} \ \ \text{if } i<j+1
30
+
31
+ See :wikipedia:`Simplicial_set`, Peter May's seminal book [May1967]_, or
32
+ Greg Friedman's "Illustrated introduction" :arxiv:`0809.4221` for more
33
+ information.
34
+
35
+ Several simplicial sets are predefined, and users can construct others
36
+ either by hand (using :class:`SimplicialSet_finite`) or from existing
37
+ ones using pushouts, pullbacks, etc.
38
+
39
+ EXAMPLES:
40
+
41
+ Some of the predefined simplicial sets::
42
+
43
+ sage: simplicial_sets.Torus()
44
+ Torus
45
+ sage: simplicial_sets.RealProjectiveSpace(7) # needs sage.groups
46
+ RP^7
47
+ sage: S5 = simplicial_sets.Sphere(5); S5
48
+ S^5
49
+ sage: S5.nondegenerate_simplices()
50
+ [v_0, sigma_5]
51
+
52
+ One class of infinite simplicial sets is available: classifying spaces
53
+ of groups, or more generally, nerves of finite monoids::
54
+
55
+ sage: Sigma4 = groups.permutation.Symmetric(4) # needs sage.groups
56
+ sage: Sigma4.nerve() # needs sage.groups
57
+ Nerve of Symmetric group of order 4! as a permutation group
58
+
59
+ The same simplicial set (albeit with a different name) can also be
60
+ constructed as ::
61
+
62
+ sage: simplicial_sets.ClassifyingSpace(Sigma4) # needs sage.groups
63
+ Classifying space of Symmetric group of order 4! as a permutation group
64
+
65
+ Type ``simplicial_sets.`` and hit the :kbd:`Tab` key to get a full list
66
+ of the predefined simplicial sets.
67
+
68
+ You can construct new simplicial sets from old by taking quotients,
69
+ subsimplicial sets, disjoint unions, wedges (if they are pointed),
70
+ smash products (if they are pointed and finite), products, pushouts,
71
+ pullbacks, cones, and suspensions, most of which also have maps
72
+ associated with them. Wedges, for example::
73
+
74
+ sage: T = simplicial_sets.Torus()
75
+ sage: S3 = simplicial_sets.Sphere(3)
76
+ sage: T.is_pointed() and S3.is_pointed()
77
+ True
78
+ sage: T.wedge(S3)
79
+ Wedge: (Torus v S^3)
80
+ sage: T.disjoint_union(S3) == T.coproduct(S3)
81
+ False
82
+
83
+ sage: W = T.wedge(S3)
84
+ sage: W.inclusion_map(0).domain()
85
+ Torus
86
+ sage: W.projection_map(1).codomain()
87
+ Quotient: (Wedge: (Torus v S^3)/Simplicial set with 6 non-degenerate simplices)
88
+
89
+ If the `1`-sphere were not already available via
90
+ ``simplicial_sets.Sphere(1)``, you could construct it as follows::
91
+
92
+ sage: pt = simplicial_sets.Simplex(0)
93
+ sage: edge = pt.cone()
94
+ sage: S1 = edge.quotient(edge.n_skeleton(0))
95
+ sage: S1
96
+ Quotient: (Cone of 0-simplex/Simplicial set with 2 non-degenerate simplices)
97
+
98
+ At this point, ``S1`` is pointed: every quotient is automatically
99
+ given a base point, namely the image of the subcomplex. So its
100
+ suspension is the reduced suspension, and therefore is small::
101
+
102
+ sage: S5 = S1.suspension(4)
103
+ sage: S5
104
+ Sigma^4(Quotient: (Cone of 0-simplex/Simplicial set with 2 non-degenerate simplices))
105
+ sage: S5.f_vector()
106
+ [1, 0, 0, 0, 0, 1]
107
+
108
+ If we forget about the base point in ``S1``, we would get the
109
+ unreduced suspension instead::
110
+
111
+ sage: Z1 = S1.unset_base_point()
112
+ sage: Z1.suspension(4).f_vector()
113
+ [2, 2, 2, 2, 1, 1]
114
+
115
+ The cone on a pointed simplicial set is the reduced cone. The
116
+ `n`-simplex in Sage is not pointed, but the simplicial set ``Point``
117
+ is. ::
118
+
119
+ sage: simplicial_sets.Simplex(0).cone().f_vector()
120
+ [2, 1]
121
+ sage: simplicial_sets.Point().cone().f_vector()
122
+ [1]
123
+
124
+ For most simplicial sets (the ``Point`` is the main exception), each
125
+ time it is constructed, it gives a distinct copy, and two distinct
126
+ simplicial sets are never equal::
127
+
128
+ sage: T = simplicial_sets.Torus()
129
+ sage: T == simplicial_sets.Torus()
130
+ False
131
+ sage: T == T
132
+ True
133
+ sage: simplicial_sets.Torus() == simplicial_sets.Torus()
134
+ False
135
+ sage: simplicial_sets.Point() == simplicial_sets.Point()
136
+ True
137
+
138
+ You can construct subsimplicial sets by specifying a list of simplices,
139
+ and then you can define the quotient simplicial set::
140
+
141
+ sage: X = simplicial_sets.Simplex(2)
142
+ sage: e,f,g = X.n_cells(1)
143
+ sage: Y = X.subsimplicial_set([e,f,g])
144
+ sage: Z = X.quotient(Y)
145
+
146
+ Or equivalently::
147
+
148
+ sage: Y = X.n_skeleton(1)
149
+ sage: Z = X.quotient(Y)
150
+ sage: Z
151
+ Quotient: (2-simplex/Simplicial set with 6 non-degenerate simplices)
152
+
153
+ Note that subsimplicial sets and quotients come equipped with
154
+ inclusion and quotient morphisms::
155
+
156
+ sage: inc = Y.inclusion_map()
157
+ sage: inc.domain() == Y and inc.codomain() == X
158
+ True
159
+ sage: quo = Z.quotient_map()
160
+ sage: quo.domain()
161
+ 2-simplex
162
+ sage: quo.codomain() == Z
163
+ True
164
+
165
+ You can compute homology groups and the fundamental group of
166
+ any simplicial set::
167
+
168
+ sage: S1 = simplicial_sets.Sphere(1)
169
+ sage: eight = S1.wedge(S1)
170
+ sage: eight.fundamental_group() # needs sage.groups
171
+ Finitely presented group < e0, e1 | >
172
+
173
+ sage: # needs sage.groups
174
+ sage: Sigma3 = groups.permutation.Symmetric(3)
175
+ sage: BSigma3 = Sigma3.nerve()
176
+ sage: pi = BSigma3.fundamental_group(); pi
177
+ Finitely presented group < e1, e2 | e2^2, e1^3, (e2*e1)^2 >
178
+ sage: pi.order()
179
+ 6
180
+ sage: pi.is_abelian()
181
+ False
182
+
183
+ sage: RP6 = simplicial_sets.RealProjectiveSpace(6) # needs sage.groups
184
+ sage: RP6.homology(reduced=False, base_ring=GF(2)) # needs sage.groups sage.modules
185
+ {0: Vector space of dimension 1 over Finite Field of size 2,
186
+ 1: Vector space of dimension 1 over Finite Field of size 2,
187
+ 2: Vector space of dimension 1 over Finite Field of size 2,
188
+ 3: Vector space of dimension 1 over Finite Field of size 2,
189
+ 4: Vector space of dimension 1 over Finite Field of size 2,
190
+ 5: Vector space of dimension 1 over Finite Field of size 2,
191
+ 6: Vector space of dimension 1 over Finite Field of size 2}
192
+ sage: RP6.homology(reduced=False, base_ring=QQ) # needs sage.groups sage.modules
193
+ {0: Vector space of dimension 1 over Rational Field,
194
+ 1: Vector space of dimension 0 over Rational Field,
195
+ 2: Vector space of dimension 0 over Rational Field,
196
+ 3: Vector space of dimension 0 over Rational Field,
197
+ 4: Vector space of dimension 0 over Rational Field,
198
+ 5: Vector space of dimension 0 over Rational Field,
199
+ 6: Vector space of dimension 0 over Rational Field}
200
+
201
+ When infinite simplicial sets are involved, most computations are done
202
+ by taking an `n`-skeleton for an appropriate `n`, either implicitly or
203
+ explicitly::
204
+
205
+ sage: # needs sage.groups
206
+ sage: G = groups.misc.MultiplicativeAbelian([3])
207
+ sage: B3 = simplicial_sets.ClassifyingSpace(G)
208
+ sage: B3.disjoint_union(B3).n_skeleton(3)
209
+ Disjoint union: (Simplicial set with 15 non-degenerate simplices
210
+ u Simplicial set with 15 non-degenerate simplices)
211
+ sage: S1 = simplicial_sets.Sphere(1)
212
+ sage: B3.product(S1).homology(range(4)) # needs sage.modules
213
+ {0: 0, 1: Z x C3, 2: C3, 3: C3}
214
+
215
+ Without the ``range`` argument, this would raise an error, since
216
+ ``B3`` is infinite::
217
+
218
+ sage: B3.product(S1).homology() # needs sage.groups sage.modules
219
+ Traceback (most recent call last):
220
+ ...
221
+ NotImplementedError: this simplicial set may be infinite,
222
+ so specify dimensions when computing homology
223
+
224
+ It should be easy to construct many simplicial sets from the
225
+ predefined ones using pushouts, pullbacks, etc., but they can also be
226
+ constructed "by hand": first define some simplices, then define a
227
+ simplicial set by specifying their faces::
228
+
229
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
230
+ sage: v = AbstractSimplex(0, name='v')
231
+ sage: w = AbstractSimplex(0, name='w')
232
+ sage: e = AbstractSimplex(1, name='e')
233
+ sage: f = AbstractSimplex(1, name='f')
234
+ sage: X = SimplicialSet({e: (v,w), f: (w,w)})
235
+
236
+ Now `e` is an edge from `v` to `w` and `f` is an edge starting and
237
+ ending at `w`. Therefore the first homology group of `X` should be a
238
+ copy of the integers::
239
+
240
+ sage: X.homology(1) # needs sage.modules
241
+ Z
242
+ """
243
+ # ****************************************************************************
244
+ # Copyright (C) 2016 John H. Palmieri <palmieri at math.washington.edu>
245
+ #
246
+ # Distributed under the terms of the GNU General Public License (GPL)
247
+ # https://www.gnu.org/licenses/
248
+ #
249
+ # This code is distributed in the hope that it will be useful,
250
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
251
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
252
+ #
253
+ # See the GNU General Public License for more details; the full text
254
+ # is available at:
255
+ #
256
+ # https://www.gnu.org/licenses/
257
+ #
258
+ # ****************************************************************************
259
+
260
+ import copy
261
+
262
+ from sage.misc.cachefunc import cached_method
263
+ from sage.misc.fast_methods import WithEqualityById
264
+ from sage.misc.lazy_import import lazy_import
265
+ from sage.rings.integer import Integer
266
+ from sage.rings.integer_ring import ZZ
267
+ from sage.rings.rational_field import QQ
268
+ from sage.structure.parent import Parent
269
+ from sage.structure.sage_object import SageObject
270
+
271
+ from .cell_complex import GenericCellComplex
272
+ from .delta_complex import DeltaComplex
273
+ from .simplicial_complex import SimplicialComplex
274
+
275
+ lazy_import('sage.categories.simplicial_sets', 'SimplicialSets')
276
+ lazy_import('sage.matrix.constructor', 'matrix')
277
+
278
+
279
+ ########################################################################
280
+ # The classes for simplices.
281
+
282
+ class AbstractSimplex_class(SageObject):
283
+ """
284
+ A simplex of dimension ``dim``.
285
+
286
+ INPUT:
287
+
288
+ - ``dim`` -- integer; the dimension
289
+ - ``degeneracies`` -- (optional) iterable, the indices of the
290
+ degeneracy maps
291
+ - ``underlying`` -- (optional) a non-degenerate simplex
292
+ - ``name`` -- (optional) string
293
+ - ``latex_name`` -- (optional) string
294
+
295
+ Users should not call this directly, but instead use
296
+ :func:`AbstractSimplex`. See that function for more documentation.
297
+ """
298
+
299
+ def __init__(self, dim, degeneracies=(), underlying=None, name=None,
300
+ latex_name=None):
301
+ """
302
+ A simplex of dimension ``dim``.
303
+
304
+ INPUT:
305
+
306
+ - ``dim`` -- integer; the dimension
307
+ - ``degeneracies`` -- (optional) iterable, the indices of the degeneracy maps
308
+ - ``underlying`` -- (optional) a non-degenerate simplex
309
+ - ``name`` -- (optional) string
310
+ - ``latex_name`` -- (optional) string
311
+
312
+ Users should not call this directly, but instead use
313
+ :func:`AbstractSimplex`. See that function for more
314
+ documentation.
315
+
316
+ TESTS::
317
+
318
+ sage: from sage.topology.simplicial_set import AbstractSimplex
319
+ sage: AbstractSimplex(3, (1,2))
320
+ s_3 s_1 Delta^3
321
+ sage: AbstractSimplex(3, None)
322
+ Delta^3
323
+
324
+ sage: v = AbstractSimplex(0, name='v')
325
+ sage: e = AbstractSimplex(0, (0,), underlying=v)
326
+ sage: e
327
+ s_0 v
328
+ sage: e.nondegenerate() is v
329
+ True
330
+
331
+ sage: AbstractSimplex(3.2, None)
332
+ Traceback (most recent call last):
333
+ ...
334
+ ValueError: the dimension must be an integer
335
+ sage: AbstractSimplex(-3, None)
336
+ Traceback (most recent call last):
337
+ ...
338
+ ValueError: the dimension must be nonnegative
339
+
340
+ sage: AbstractSimplex(0, (1,))
341
+ Traceback (most recent call last):
342
+ ...
343
+ ValueError: invalid list of degeneracy maps on 0-simplex
344
+
345
+ Distinct non-degenerate simplices should never be equal, even
346
+ if they have the same starting data::
347
+
348
+ sage: from sage.topology.simplicial_set import AbstractSimplex_class
349
+ sage: AbstractSimplex_class(3) == AbstractSimplex_class(3)
350
+ False
351
+ sage: AbstractSimplex(3) == AbstractSimplex(3)
352
+ False
353
+
354
+ Hashing::
355
+
356
+ sage: v = AbstractSimplex(0)
357
+ sage: w = AbstractSimplex(0)
358
+ sage: hash(v) == hash(w)
359
+ False
360
+ sage: x = v.apply_degeneracies(2,1,0)
361
+ sage: hash(x) == hash(v.apply_degeneracies(2,1,0))
362
+ True
363
+
364
+ Equality::
365
+
366
+ sage: v = AbstractSimplex(0)
367
+ sage: w = AbstractSimplex(0)
368
+ sage: v == w
369
+ False
370
+ sage: v.apply_degeneracies(2,1,0) is v.apply_degeneracies(2,1,0)
371
+ False
372
+ sage: v.apply_degeneracies(2,1,0) == v.apply_degeneracies(2,1,0)
373
+ True
374
+ sage: v == None
375
+ False
376
+ """
377
+ try:
378
+ Integer(dim)
379
+ except TypeError:
380
+ raise ValueError('the dimension must be an integer')
381
+ if dim < 0:
382
+ raise ValueError('the dimension must be nonnegative')
383
+ self._dim = dim
384
+ if degeneracies:
385
+ self._degens = standardize_degeneracies(*degeneracies)
386
+ for (d, s) in enumerate(reversed(self._degens)):
387
+ if d + dim < s:
388
+ raise ValueError('invalid list of degeneracy maps '
389
+ 'on {}-simplex'.format(dim))
390
+ if underlying is None:
391
+ self._underlying = NonDegenerateSimplex(dim)
392
+ else:
393
+ self._underlying = underlying
394
+ else:
395
+ self._degens = ()
396
+ if underlying is None:
397
+ self._underlying = self
398
+ else:
399
+ self._underlying = underlying
400
+ if name is not None:
401
+ self.rename(name)
402
+ self._latex_name = latex_name
403
+
404
+ def __hash__(self):
405
+ """
406
+ If nondegenerate: return the id of this simplex.
407
+
408
+ Otherwise, combine the id of its underlying nondegenerate
409
+ simplex with the tuple of indeterminacies.
410
+
411
+ EXAMPLES::
412
+
413
+ sage: from sage.topology.simplicial_set import AbstractSimplex
414
+ sage: v = AbstractSimplex(0)
415
+ sage: w = AbstractSimplex(0)
416
+ sage: hash(v) == hash(w)
417
+ False
418
+ sage: x = v.apply_degeneracies(2,1,0)
419
+ sage: id(x) == id(v.apply_degeneracies(2,1,0))
420
+ False
421
+ sage: hash(x) == hash(v.apply_degeneracies(2,1,0))
422
+ True
423
+ """
424
+ if self.is_nondegenerate():
425
+ return id(self)
426
+ return hash(self.nondegenerate()) ^ hash(self._degens)
427
+
428
+ def __eq__(self, other):
429
+ """
430
+ Two nondegenerate simplices are equal if they are identical.
431
+ Two degenerate simplices are equal if their underlying
432
+ nondegenerate simplices are identical and their tuples of
433
+ degeneracies are equal.
434
+
435
+ EXAMPLES::
436
+
437
+ sage: from sage.topology.simplicial_set import AbstractSimplex
438
+ sage: v = AbstractSimplex(0)
439
+ sage: w = AbstractSimplex(0)
440
+ sage: v == w
441
+ False
442
+ sage: v.apply_degeneracies(2,1,0) is v.apply_degeneracies(2,1,0)
443
+ False
444
+ sage: v.apply_degeneracies(2,1,0) == v.apply_degeneracies(2,1,0)
445
+ True
446
+
447
+ TESTS::
448
+
449
+ sage: v == None
450
+ False
451
+ """
452
+ if not isinstance(other, AbstractSimplex_class):
453
+ return False
454
+ return (self._degens == other._degens
455
+ and self.nondegenerate() is other.nondegenerate())
456
+
457
+ def __ne__(self, other):
458
+ """
459
+ This returns the negation of ``__eq__``.
460
+
461
+ EXAMPLES::
462
+
463
+ sage: from sage.topology.simplicial_set import AbstractSimplex
464
+ sage: v = AbstractSimplex(0)
465
+ sage: w = AbstractSimplex(0)
466
+ sage: v != w
467
+ True
468
+ sage: x = v.apply_degeneracies(1, 0)
469
+ sage: y = v.apply_degeneracies(1, 0)
470
+ sage: x != y
471
+ False
472
+ """
473
+ return not self == other
474
+
475
+ def __lt__(self, other):
476
+ """
477
+ We implement sorting in the hopes that sorted lists of simplices,
478
+ for example as defining data for a simplicial set, will be
479
+ well-defined invariants.
480
+
481
+ Sort by dimension first. If dimensions are equal, if only one
482
+ has a custom name (as set by specifying ``name=NAME`` upon
483
+ creation or by calling ``object.rename('NAME')``, put it
484
+ first. If both have custom names, sort by the names. As a last
485
+ resort, sort by their id.
486
+
487
+ TESTS::
488
+
489
+ sage: from sage.topology.simplicial_set import AbstractSimplex
490
+ sage: v = AbstractSimplex(0)
491
+ sage: w = AbstractSimplex(0)
492
+
493
+ At this point, comparison between v and w is random, based on
494
+ their location in memory. ::
495
+
496
+ sage: v < w and w < v
497
+ False
498
+ sage: v < w or w < v
499
+ True
500
+ sage: (v < w and w > v) or (w < v and v > w)
501
+ True
502
+
503
+ Now we add names to force an ordering::
504
+
505
+ sage: w.rename('w')
506
+ sage: v < w
507
+ False
508
+ sage: v > w
509
+ True
510
+ sage: v >= w
511
+ True
512
+ sage: v.rename('v')
513
+ sage: v < w
514
+ True
515
+ sage: v <= w
516
+ True
517
+ sage: v > w
518
+ False
519
+
520
+ Test other sorting. Dimensions::
521
+
522
+ sage: AbstractSimplex(0) < AbstractSimplex(3)
523
+ True
524
+ sage: AbstractSimplex(0, ((0,0,0))) <= AbstractSimplex(2)
525
+ False
526
+
527
+ Degenerate comes after non-degenerate, and if both are
528
+ degenerate, sort on the degeneracies::
529
+
530
+ sage: AbstractSimplex(0, ((0,0))) <= AbstractSimplex(2)
531
+ False
532
+ sage: AbstractSimplex(1, ((0,))) < AbstractSimplex(1, ((1,)))
533
+ True
534
+ sage: v.apply_degeneracies(1,0) > w.apply_degeneracies(1,0)
535
+ False
536
+ sage: w.rename('a')
537
+ sage: v.apply_degeneracies(1,0) > w.apply_degeneracies(1,0)
538
+ True
539
+
540
+ Testing `<=`, `>`, `>=`::
541
+
542
+ sage: v = AbstractSimplex(0, name='v')
543
+ sage: w = AbstractSimplex(0, name='w')
544
+ sage: v <= v
545
+ True
546
+ sage: w <= v
547
+ False
548
+ sage: v.apply_degeneracies(1,0) <= w.apply_degeneracies(1,0)
549
+ True
550
+
551
+ sage: v > v
552
+ False
553
+ sage: w > v
554
+ True
555
+ sage: v.apply_degeneracies(1,0) > w.apply_degeneracies(1,0)
556
+ False
557
+
558
+ sage: v >= v
559
+ True
560
+ sage: w >= v
561
+ True
562
+ sage: v.apply_degeneracies(1,0) >= w.apply_degeneracies(1,0)
563
+ False
564
+ """
565
+ if self.dimension() < other.dimension():
566
+ return True
567
+ if self.dimension() > other.dimension():
568
+ return False
569
+ if self.degeneracies() and not other.degeneracies():
570
+ return False
571
+ if other.degeneracies() and not self.degeneracies():
572
+ return True
573
+ if self.degeneracies() and other.degeneracies() and self.degeneracies() != other.degeneracies():
574
+ return self.degeneracies() < other.degeneracies()
575
+ if self.nondegenerate().get_custom_name() is not None:
576
+ if other.nondegenerate().get_custom_name() is not None:
577
+ return self.nondegenerate().get_custom_name() < other.nondegenerate().get_custom_name()
578
+ return True
579
+
580
+ if other.nondegenerate().get_custom_name() is not None:
581
+ return False
582
+ return id(self) < id(other)
583
+
584
+ def __gt__(self, other):
585
+ """
586
+ See :meth:`__lt__` for more doctests.
587
+
588
+ EXAMPLES::
589
+
590
+ sage: from sage.topology.simplicial_set import AbstractSimplex
591
+ sage: e = AbstractSimplex(1, (1,0), name='e')
592
+ sage: f = AbstractSimplex(1, (2,1), name='f')
593
+ sage: e > f
594
+ False
595
+ """
596
+ return not (self < other or self == other)
597
+
598
+ def __le__(self, other):
599
+ """
600
+ See :meth:`__lt__` for more doctests.
601
+
602
+ EXAMPLES::
603
+
604
+ sage: from sage.topology.simplicial_set import AbstractSimplex
605
+ sage: e = AbstractSimplex(1, (1,0), name='e')
606
+ sage: f = AbstractSimplex(1, (2,1), name='f')
607
+ sage: e <= f
608
+ True
609
+ """
610
+ return self < other or self == other
611
+
612
+ def __ge__(self, other):
613
+ """
614
+ See :meth:`__lt__` for more doctests.
615
+
616
+ EXAMPLES::
617
+
618
+ sage: from sage.topology.simplicial_set import AbstractSimplex
619
+ sage: e = AbstractSimplex(1, (1,0), name='e')
620
+ sage: f = AbstractSimplex(1, (2,1), name='f')
621
+ sage: e >= f
622
+ False
623
+ """
624
+ return not self < other
625
+
626
+ def nondegenerate(self):
627
+ """
628
+ The non-degenerate simplex underlying this one.
629
+
630
+ Therefore return itself if this simplex is non-degenerate.
631
+
632
+ EXAMPLES::
633
+
634
+ sage: from sage.topology.simplicial_set import AbstractSimplex
635
+ sage: v = AbstractSimplex(0, name='v')
636
+ sage: sigma = v.apply_degeneracies(1, 0)
637
+ sage: sigma.nondegenerate()
638
+ v
639
+ sage: tau = AbstractSimplex(1, (3,2,1))
640
+ sage: x = tau.nondegenerate(); x
641
+ Delta^1
642
+ sage: x == tau.nondegenerate()
643
+ True
644
+
645
+ sage: AbstractSimplex(1, None)
646
+ Delta^1
647
+ sage: AbstractSimplex(1, None) == x
648
+ False
649
+ sage: AbstractSimplex(1, None) == tau.nondegenerate()
650
+ False
651
+ """
652
+ return self._underlying
653
+
654
+ def degeneracies(self):
655
+ """
656
+ Return the list of indices for the degeneracy maps for this
657
+ simplex.
658
+
659
+ EXAMPLES::
660
+
661
+ sage: from sage.topology.simplicial_set import AbstractSimplex
662
+ sage: AbstractSimplex(4, (0,0,0)).degeneracies()
663
+ [2, 1, 0]
664
+ sage: AbstractSimplex(4, None).degeneracies()
665
+ []
666
+ """
667
+ return list(self._degens)
668
+
669
+ def is_degenerate(self):
670
+ """
671
+ Return ``True`` if this simplex is degenerate.
672
+
673
+ EXAMPLES::
674
+
675
+ sage: from sage.topology.simplicial_set import AbstractSimplex
676
+ sage: AbstractSimplex(3, (2,1)).is_degenerate()
677
+ True
678
+ sage: AbstractSimplex(3, None).is_degenerate()
679
+ False
680
+ """
681
+ return bool(self.degeneracies())
682
+
683
+ def is_nondegenerate(self):
684
+ """
685
+ Return ``True`` if this simplex is non-degenerate.
686
+
687
+ EXAMPLES::
688
+
689
+ sage: from sage.topology.simplicial_set import AbstractSimplex
690
+ sage: AbstractSimplex(3, (2,1)).is_nondegenerate()
691
+ False
692
+ sage: AbstractSimplex(3, None).is_nondegenerate()
693
+ True
694
+ sage: AbstractSimplex(5).is_nondegenerate()
695
+ True
696
+ """
697
+ return not self.is_degenerate()
698
+
699
+ def dimension(self):
700
+ """
701
+ The dimension of this simplex.
702
+
703
+ EXAMPLES::
704
+
705
+ sage: from sage.topology.simplicial_set import AbstractSimplex
706
+ sage: AbstractSimplex(3, (2,1)).dimension()
707
+ 5
708
+ sage: AbstractSimplex(3, None).dimension()
709
+ 3
710
+ sage: AbstractSimplex(7).dimension()
711
+ 7
712
+ """
713
+ return self._dim + len(self.degeneracies())
714
+
715
+ def apply_degeneracies(self, *args):
716
+ """
717
+ Apply the degeneracies given by the arguments ``args`` to this simplex.
718
+
719
+ INPUT:
720
+
721
+ - ``args`` -- integer
722
+
723
+ EXAMPLES::
724
+
725
+ sage: from sage.topology.simplicial_set import AbstractSimplex
726
+ sage: v = AbstractSimplex(0)
727
+ sage: e = v.apply_degeneracies(0)
728
+ sage: e.nondegenerate() == v
729
+ True
730
+ sage: f = e.apply_degeneracies(0)
731
+ sage: f
732
+ s_1 s_0 Delta^0
733
+ sage: f.degeneracies()
734
+ [1, 0]
735
+ sage: f.nondegenerate() == v
736
+ True
737
+ sage: v.apply_degeneracies(1, 0)
738
+ s_1 s_0 Delta^0
739
+
740
+ TESTS::
741
+
742
+ sage: e.apply_degeneracies() == e
743
+ True
744
+
745
+ Do not pass an explicit list or tuple as the argument: call
746
+ this with the syntax ``x.apply_degeneracies(1,0)``, not
747
+ ``x.apply_degeneracies([1,0])``::
748
+
749
+ sage: e.apply_degeneracies([1,0])
750
+ Traceback (most recent call last):
751
+ ...
752
+ TypeError: degeneracies are indexed by nonnegative integers; do not use an explicit list or tuple
753
+ """
754
+ if not args:
755
+ return self
756
+ underlying = self.nondegenerate()
757
+ return AbstractSimplex(underlying.dimension(),
758
+ degeneracies=list(args) + self.degeneracies(),
759
+ underlying=underlying)
760
+
761
+ def __copy__(self):
762
+ """
763
+ Return a copy of this simplex.
764
+
765
+ Forget the "underlying" non-degenerate simplex. If this
766
+ simplex has a name, then its copy's name is obtained by adding
767
+ a prime ``'`` at the end.
768
+
769
+ TESTS::
770
+
771
+ sage: from sage.topology.simplicial_set import AbstractSimplex
772
+ sage: v = AbstractSimplex(0)
773
+ sage: copy(v) == v
774
+ False
775
+ sage: copy(v).nondegenerate() == v
776
+ False
777
+ sage: x = v.apply_degeneracies(1, 0)
778
+ sage: y = copy(v).apply_degeneracies(1, 0)
779
+ sage: z = copy(x)
780
+ sage: x == y or x == z or y == z
781
+ False
782
+ sage: x.nondegenerate() == copy(v)
783
+ False
784
+ sage: y.nondegenerate() == v
785
+ False
786
+
787
+ sage: v.rename('v')
788
+ sage: copy(v)
789
+ v'
790
+ sage: copy(copy(v))
791
+ v''
792
+ """
793
+ # Don't preserve the underlying simplex when copying, just the
794
+ # dimension, the degeneracies, and the name (with a prime
795
+ # added).
796
+ sigma = AbstractSimplex(self._dim, degeneracies=self.degeneracies())
797
+ if self.get_custom_name() is not None:
798
+ sigma.rename(self.get_custom_name() + "'")
799
+ return sigma
800
+
801
+ def __deepcopy__(self, memo):
802
+ """
803
+ Return a "deep" copy of this simplex.
804
+
805
+ INPUT:
806
+
807
+ - ``memo`` -- "memo" dictionary required by the ``copy.deepcopy`` method
808
+
809
+ This returns the same object as the :meth:`__copy__` method
810
+ and also updates ``memo``.
811
+
812
+ EXAMPLES::
813
+
814
+ sage: from sage.topology.simplicial_set import AbstractSimplex
815
+ sage: import copy
816
+ sage: v = AbstractSimplex(0)
817
+ sage: copy.deepcopy(v) == v
818
+ False
819
+
820
+ TESTS:
821
+
822
+ The purpose for this method is to be able to make distinct
823
+ copies of simplicial sets::
824
+
825
+ sage: # needs sage.groups
826
+ sage: from sage.topology.simplicial_set import SimplicialSet
827
+ sage: RP3 = simplicial_sets.RealProjectiveSpace(3)
828
+ sage: dict(copy.copy(RP3._data)) == dict(RP3._data)
829
+ True
830
+ sage: dict(copy.deepcopy(RP3._data)) == dict(RP3._data)
831
+ False
832
+ sage: SimplicialSet(RP3) == RP3
833
+ False
834
+ sage: copy.copy(RP3) == RP3
835
+ False
836
+ """
837
+ underlying = self.nondegenerate()
838
+ degens = self.degeneracies()
839
+ try:
840
+ return memo[underlying].apply_degeneracies(*degens)
841
+ except KeyError:
842
+ sigma = AbstractSimplex(underlying._dim)
843
+ if underlying.get_custom_name() is not None:
844
+ sigma.rename(underlying.get_custom_name() + "'")
845
+ memo[underlying] = sigma
846
+ return sigma.apply_degeneracies(*degens)
847
+
848
+ def _repr_(self):
849
+ """
850
+ Print representation.
851
+
852
+ TESTS::
853
+
854
+ sage: from sage.topology.simplicial_set import AbstractSimplex
855
+ sage: AbstractSimplex(3, None)
856
+ Delta^3
857
+ sage: AbstractSimplex(3, (0,))
858
+ s_0 Delta^3
859
+ sage: AbstractSimplex(3, (0, 0))
860
+ s_1 s_0 Delta^3
861
+
862
+ Test renaming::
863
+
864
+ sage: v = AbstractSimplex(0)
865
+ sage: v
866
+ Delta^0
867
+ sage: v.rename('v')
868
+ sage: v
869
+ v
870
+ sage: v.apply_degeneracies(1, 0)
871
+ s_1 s_0 v
872
+ """
873
+ if self.degeneracies():
874
+ degens = ' '.join(f's_{i}' for i in self.degeneracies())
875
+ return degens + ' {}'.format(self.nondegenerate())
876
+ return 'Delta^{}'.format(self._dim)
877
+
878
+ def _latex_(self):
879
+ r"""
880
+ LaTeX representation.
881
+
882
+ TESTS::
883
+
884
+ sage: from sage.topology.simplicial_set import AbstractSimplex
885
+ sage: latex(AbstractSimplex(18, None))
886
+ \Delta^{18}
887
+ sage: latex(AbstractSimplex(3, (0, 0,)))
888
+ s_{1} s_{0} \Delta^{3}
889
+ sage: latex(AbstractSimplex(3, (0, 0,), name='x'))
890
+ x
891
+ sage: latex(AbstractSimplex(3, name='x').apply_degeneracies(0, 0))
892
+ s_{1} s_{0} x
893
+ sage: latex(AbstractSimplex(3, (0, 0,), name='x', latex_name='y'))
894
+ y
895
+ sage: latex(AbstractSimplex(3, name='x', latex_name='y').apply_degeneracies(0, 0))
896
+ s_{1} s_{0} y
897
+ """
898
+ if self._latex_name is not None:
899
+ return self._latex_name
900
+ if self.get_custom_name() is not None:
901
+ return self.get_custom_name()
902
+ if self.nondegenerate()._latex_name is not None:
903
+ simplex = self.nondegenerate()._latex_name
904
+ elif self.nondegenerate().get_custom_name() is not None:
905
+ simplex = self.nondegenerate().get_custom_name()
906
+ else:
907
+ simplex = "\\Delta^{{{}}}".format(self._dim)
908
+ if self.degeneracies():
909
+ degens = ' '.join(f's_{{{i}}}' for i in self.degeneracies())
910
+ return degens + ' ' + simplex
911
+ return simplex
912
+
913
+
914
+ # If we inherit from AbstractSimplex_class first in the following,
915
+ # then we have to override __eq__ and __hash__. If we inherit from
916
+ # WithEqualityById first, then we have to override __lt__, __gt__,
917
+ # __ge__, __le__. Inheriting from AbstractSimplex_class first seems to
918
+ # be slightly faster.
919
+ class NonDegenerateSimplex(AbstractSimplex_class, WithEqualityById):
920
+ def __init__(self, dim, name=None, latex_name=None):
921
+ """
922
+ A nondegenerate simplex.
923
+
924
+ INPUT:
925
+
926
+ - ``dim`` -- nonnegative integer; the dimension
927
+
928
+ - ``name`` -- (optional) string; a name for this simplex
929
+
930
+ - ``latex_name`` -- (optional) string; a name for this simplex to
931
+ use in the LaTeX representation
932
+
933
+ EXAMPLES::
934
+
935
+ sage: from sage.topology.simplicial_set import AbstractSimplex
936
+ sage: v = AbstractSimplex(0, name='v')
937
+ sage: v
938
+ v
939
+ sage: type(v)
940
+ <class 'sage.topology.simplicial_set.NonDegenerateSimplex'>
941
+
942
+ Distinct non-degenerate simplices should never be equal, even
943
+ if they have the same starting data. ::
944
+
945
+ sage: v == AbstractSimplex(0, name='v')
946
+ False
947
+ sage: AbstractSimplex(3) == AbstractSimplex(3)
948
+ False
949
+
950
+ sage: from sage.topology.simplicial_set import NonDegenerateSimplex
951
+ sage: x = NonDegenerateSimplex(0, name='x')
952
+ sage: x == NonDegenerateSimplex(0, name='x')
953
+ False
954
+ """
955
+ AbstractSimplex_class.__init__(self, dim, name=name, latex_name=latex_name)
956
+
957
+ __eq__ = WithEqualityById.__eq__
958
+ __hash__ = WithEqualityById.__hash__
959
+
960
+
961
+ # The following function returns an instance of either
962
+ # AbstractSimplex_class or NonDegenerateSimplex.
963
+
964
+ def AbstractSimplex(dim, degeneracies=(), underlying=None,
965
+ name=None, latex_name=None):
966
+ r"""
967
+ An abstract simplex, a building block of a simplicial set.
968
+
969
+ In a simplicial set, a simplex either is non-degenerate or is
970
+ obtained by applying degeneracy maps to a non-degenerate simplex.
971
+
972
+ INPUT:
973
+
974
+ - ``dim`` -- nonnegative integer; the dimension of the
975
+ underlying non-degenerate simplex
976
+
977
+ - ``degeneracies`` -- (default: ``None``) list or tuple of
978
+ nonnegative integers, the degeneracies to be applied
979
+
980
+ - ``underlying`` -- (optional) a non-degenerate simplex to which
981
+ the degeneracies are being applied
982
+
983
+ - ``name`` -- (optional) string; a name for this simplex
984
+
985
+ - ``latex_name`` -- (optional) string; a name for this simplex to
986
+ use in the LaTeX representation
987
+
988
+ So to define a simplex formed by applying the degeneracy maps `s_2
989
+ s_1` to a 1-simplex, call ``AbstractSimplex(1, (2, 1))``.
990
+
991
+ Specify ``underlying`` if you need to keep explicit track of the
992
+ underlying non-degenerate simplex, for example when computing
993
+ faces of another simplex. This is mainly for use by the method
994
+ :meth:`AbstractSimplex_class.apply_degeneracies`.
995
+
996
+ EXAMPLES::
997
+
998
+ sage: from sage.topology.simplicial_set import AbstractSimplex
999
+ sage: AbstractSimplex(3, (3, 1))
1000
+ s_3 s_1 Delta^3
1001
+ sage: AbstractSimplex(3, None)
1002
+ Delta^3
1003
+ sage: AbstractSimplex(3)
1004
+ Delta^3
1005
+
1006
+ Simplices may be named (or renamed), affecting how they are printed::
1007
+
1008
+ sage: AbstractSimplex(0)
1009
+ Delta^0
1010
+ sage: v = AbstractSimplex(0, name='v')
1011
+ sage: v
1012
+ v
1013
+ sage: v.rename('w_0')
1014
+ sage: v
1015
+ w_0
1016
+ sage: latex(v)
1017
+ w_0
1018
+ sage: latex(AbstractSimplex(0, latex_name='\\sigma'))
1019
+ \sigma
1020
+
1021
+ The simplicial identities are used to put the degeneracies in
1022
+ standard decreasing form::
1023
+
1024
+ sage: x = AbstractSimplex(0, (0, 0, 0))
1025
+ sage: x
1026
+ s_2 s_1 s_0 Delta^0
1027
+ sage: x.degeneracies()
1028
+ [2, 1, 0]
1029
+
1030
+ Use of the ``underlying`` argument::
1031
+
1032
+ sage: v = AbstractSimplex(0, name='v')
1033
+ sage: e = AbstractSimplex(0, (0,), underlying=v)
1034
+ sage: e
1035
+ s_0 v
1036
+ sage: e.nondegenerate() is v
1037
+ True
1038
+
1039
+ sage: e.dimension()
1040
+ 1
1041
+ sage: e.is_degenerate()
1042
+ True
1043
+
1044
+ Distinct non-degenerate simplices are never equal::
1045
+
1046
+ sage: AbstractSimplex(0, None) == AbstractSimplex(0, None)
1047
+ False
1048
+ sage: AbstractSimplex(0, (2,1,0)) == AbstractSimplex(0, (2,1,0))
1049
+ False
1050
+
1051
+ sage: e = AbstractSimplex(0, ((0,)))
1052
+ sage: f = AbstractSimplex(0, ((0,)))
1053
+ sage: e == f
1054
+ False
1055
+ sage: e.nondegenerate() == f.nondegenerate()
1056
+ False
1057
+
1058
+ This means that if, when defining a simplicial set, you specify
1059
+ the faces of a 2-simplex as::
1060
+
1061
+ (e, e, e)
1062
+
1063
+ then the faces are the same degenerate vertex, but if you specify
1064
+ the faces as::
1065
+
1066
+ (AbstractSimplex(0, ((0,))), AbstractSimplex(0, ((0,))), AbstractSimplex(0, ((0,))))
1067
+
1068
+ then the faces are three different degenerate vertices.
1069
+
1070
+ View a command like ``AbstractSimplex(0, (2,1,0))`` as first
1071
+ constructing ``AbstractSimplex(0)`` and then applying degeneracies
1072
+ to it, and you always get distinct simplices from different calls
1073
+ to ``AbstractSimplex(0)``. On the other hand, if you apply
1074
+ degeneracies to the same non-degenerate simplex, the resulting
1075
+ simplices are equal::
1076
+
1077
+ sage: v = AbstractSimplex(0)
1078
+ sage: v.apply_degeneracies(1, 0) == v.apply_degeneracies(1, 0)
1079
+ True
1080
+ sage: AbstractSimplex(1, (0,), underlying=v) == AbstractSimplex(1, (0,), underlying=v)
1081
+ True
1082
+ """
1083
+ if degeneracies:
1084
+ if underlying is None:
1085
+ underlying = NonDegenerateSimplex(dim)
1086
+ return AbstractSimplex_class(dim, degeneracies=degeneracies,
1087
+ underlying=underlying,
1088
+ name=name,
1089
+ latex_name=latex_name)
1090
+ else:
1091
+ return NonDegenerateSimplex(dim, name=name,
1092
+ latex_name=latex_name)
1093
+
1094
+
1095
+ ########################################################################
1096
+ # The main classes for simplicial sets.
1097
+
1098
+ class SimplicialSet_arbitrary(Parent):
1099
+ r"""
1100
+ A simplicial set.
1101
+
1102
+ A simplicial set `X` is a collection of sets `X_n`, the
1103
+ *n-simplices*, indexed by the nonnegative integers, together with
1104
+ maps
1105
+
1106
+ .. MATH::
1107
+
1108
+ d_i: X_n \to X_{n-1}, \ \ 0 \leq i \leq n \ \ \text{(face maps)} \\
1109
+ s_j: X_n \to X_{n+1}, \ \ 0 \leq j \leq n \ \ \text{(degeneracy maps)}
1110
+
1111
+ satisfying the *simplicial identities*:
1112
+
1113
+ .. MATH::
1114
+
1115
+ d_i d_j &= d_{j-1} d_i \ \ \text{if } i<j \\
1116
+ d_i s_j &= s_{j-1} d_i \ \ \text{if } i<j \\
1117
+ d_j s_j &= 1 = d_{j+1} s_j \\
1118
+ d_i s_j &= s_{j} d_{i-1} \ \ \text{if } i>j+1 \\
1119
+ s_i s_j &= s_{j+1} s_{i} \ \ \text{if } i<j+1
1120
+
1121
+ This class is not fully implemented and is not intended to be
1122
+ called directly by users. It is intended instead to be used by
1123
+ other classes which inherit from this one. See
1124
+ :class:`SimplicialSet_finite` and :class:`Nerve` for two
1125
+ examples. In particular, any such class must implement a method
1126
+ ``n_skeleton`` -- without this, most computations will be
1127
+ impossible. It must also implement an ``__init__`` method which
1128
+ should also set the category, so that methods defined at the
1129
+ category level, like ``is_pointed`` and ``is_finite``, work
1130
+ correctly.
1131
+
1132
+ Note that the method :meth:`subsimplicial_set` calls
1133
+ :meth:`n_skeleton`, so to avoid circularity, the
1134
+ :meth:`n_skeleton` method should call
1135
+ :class:`.simplicial_set_constructions.SubSimplicialSet` directly,
1136
+ not :meth:`subsimplicial_set`.
1137
+ """
1138
+
1139
+ # This is cached because it is used frequently in morphism
1140
+ # construction when verifying that the morphism commutes with the
1141
+ # face maps.
1142
+ @cached_method
1143
+ def faces(self, simplex):
1144
+ """
1145
+ Return the list of faces of ``simplex`` in this simplicial set.
1146
+
1147
+ INPUT:
1148
+
1149
+ - ``simplex`` -- a simplex in this simplicial set, either
1150
+ degenerate or not
1151
+
1152
+ EXAMPLES::
1153
+
1154
+ sage: S2 = simplicial_sets.Sphere(2)
1155
+ sage: sigma = S2.n_cells(2)[0]
1156
+ sage: S2.faces(sigma)
1157
+ (s_0 v_0, s_0 v_0, s_0 v_0)
1158
+ sage: S2.faces(sigma.apply_degeneracies(0))
1159
+ [sigma_2, sigma_2, s_1 s_0 v_0, s_1 s_0 v_0]
1160
+
1161
+ sage: # needs sage.groups
1162
+ sage: C3 = groups.misc.MultiplicativeAbelian([3])
1163
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3)
1164
+ sage: f2 = BC3.n_cells(1)[1]; f2
1165
+ f^2
1166
+ sage: BC3.faces(f2)
1167
+ (1, 1)
1168
+
1169
+ TESTS::
1170
+
1171
+ sage: v_0 = S2.n_cells(0)[0]
1172
+ sage: S2.faces(v_0) is None
1173
+ True
1174
+
1175
+ sage: from sage.topology.simplicial_set import AbstractSimplex
1176
+ sage: w = AbstractSimplex(0)
1177
+ sage: S2.faces(w)
1178
+ Traceback (most recent call last):
1179
+ ...
1180
+ ValueError: this simplex is not in this simplicial set
1181
+ """
1182
+ dim = simplex.dimension()
1183
+ if simplex not in self:
1184
+ raise ValueError('this simplex is not in this simplicial set')
1185
+ if simplex.is_nondegenerate():
1186
+ if self.is_finite():
1187
+ return self.face_data()[simplex]
1188
+ else:
1189
+ return self.n_skeleton(dim).face_data()[simplex]
1190
+ underlying = simplex.nondegenerate()
1191
+ faces = []
1192
+ for J, t in [face_degeneracies(m, simplex.degeneracies())
1193
+ for m in range(dim+1)]:
1194
+ if t is None:
1195
+ faces.append(underlying.apply_degeneracies(*J))
1196
+ else:
1197
+ faces.append(self.face(underlying, t).apply_degeneracies(*J))
1198
+ return faces
1199
+
1200
+ def face(self, simplex, i):
1201
+ """
1202
+ Return the `i`-th face of ``simplex`` in this simplicial set.
1203
+
1204
+ INPUT:
1205
+
1206
+ - ``simplex`` -- a simplex in this simplicial set
1207
+ - ``i`` -- integer
1208
+
1209
+ EXAMPLES::
1210
+
1211
+ sage: S2 = simplicial_sets.Sphere(2)
1212
+ sage: sigma = S2.n_cells(2)[0]
1213
+ sage: v_0 = S2.n_cells(0)[0]
1214
+ sage: S2.face(sigma, 0)
1215
+ s_0 v_0
1216
+ sage: S2.face(sigma, 0) == v_0.apply_degeneracies(0)
1217
+ True
1218
+ sage: S2.face(S2.face(sigma, 0), 0) == v_0
1219
+ True
1220
+ """
1221
+ if i < 0 or i > simplex.dimension():
1222
+ raise ValueError('cannot compute face {} of {}-dimensional '
1223
+ 'simplex'.format(i, simplex.dimension()))
1224
+ faces = self.faces(simplex)
1225
+ if faces is not None:
1226
+ return self.faces(simplex)[i]
1227
+ return None
1228
+
1229
+ def __contains__(self, x):
1230
+ """
1231
+ Return ``True`` if ``x`` is a simplex which is contained in this complex.
1232
+
1233
+ EXAMPLES::
1234
+
1235
+ sage: S0 = simplicial_sets.Sphere(0)
1236
+ sage: S1 = simplicial_sets.Sphere(1)
1237
+ sage: v0 = S0.n_cells(0)[0]
1238
+ sage: v0 in S0
1239
+ True
1240
+ sage: v0 in S1
1241
+ False
1242
+
1243
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1244
+ sage: v = AbstractSimplex(0)
1245
+ sage: e = AbstractSimplex(1)
1246
+ sage: K = SimplicialSet({e: (v, v)}) # the circle
1247
+ sage: v in K
1248
+ True
1249
+ sage: v0 in K
1250
+ False
1251
+ sage: S1.n_cells(1)[0] in K
1252
+ False
1253
+
1254
+ TESTS:
1255
+
1256
+ Make sure we answer gracefully for unexpected input::
1257
+
1258
+ sage: 248 in K
1259
+ False
1260
+ """
1261
+ try:
1262
+ underlying = x.nondegenerate()
1263
+ return underlying in self.n_cells(underlying.dimension())
1264
+ except AttributeError:
1265
+ return False
1266
+
1267
+ def alexander_whitney(self, simplex, dim_left):
1268
+ r"""
1269
+ Return the 'subdivision' of ``simplex`` in this simplicial set
1270
+ into a pair of simplices.
1271
+
1272
+ The left factor should have dimension ``dim_left``, so the
1273
+ right factor should have dimension ``dim - dim_left``, if
1274
+ ``dim`` is the dimension of the starting simplex. The results
1275
+ are obtained by applying iterated face maps to
1276
+ ``simplex``. Writing `d` for ``dim`` and `j` for ``dim_left``:
1277
+ apply `d_{j+1} d_{j+2} ... d_{d}` to get the left factor,
1278
+ `d_0 ... d_0` to get the right factor.
1279
+
1280
+ INPUT:
1281
+
1282
+ - ``dim_left`` -- integer; the dimension of the left-hand factor
1283
+
1284
+ OUTPUT: list containing the triple ``(c, left, right)``,
1285
+ where ``left`` and ``right`` are the two simplices described
1286
+ above. If either ``left`` or ``right`` is degenerate, ``c`` is
1287
+ 0; otherwise, ``c`` is 1. This is so that, when used to
1288
+ compute cup products, it is easy to ignore terms which have
1289
+ degenerate factors.
1290
+
1291
+ EXAMPLES::
1292
+
1293
+ sage: S2 = simplicial_sets.Sphere(2)
1294
+ sage: sigma = S2.n_cells(2)[0]
1295
+ sage: S2.alexander_whitney(sigma, 0)
1296
+ [(1, v_0, sigma_2)]
1297
+ sage: S2.alexander_whitney(sigma, 1)
1298
+ [(0, s_0 v_0, s_0 v_0)]
1299
+ """
1300
+ dim = simplex.dimension()
1301
+ if dim_left < 0 or dim_left > dim:
1302
+ raise ValueError('alexander_whitney is only valid if dim_left '
1303
+ 'is between 0 and the dimension of the simplex')
1304
+ left = simplex
1305
+ for i in range(dim, dim_left, -1):
1306
+ left = self.face(left, i)
1307
+ right = simplex
1308
+ for i in range(dim_left):
1309
+ right = self.face(right, 0)
1310
+ if left.is_degenerate() or right.is_degenerate():
1311
+ c = ZZ.zero()
1312
+ else:
1313
+ c = ZZ.one()
1314
+ return [(c, left, right)]
1315
+
1316
+ def nondegenerate_simplices(self, max_dim=None):
1317
+ """
1318
+ Return the sorted list of non-degenerate simplices in this simplicial set.
1319
+
1320
+ INPUT:
1321
+
1322
+ - ``max_dim`` -- (default: ``None``) if specified,
1323
+ return the non-degenerate simplices of this dimension or
1324
+ smaller. This argument is required if this simplicial set is
1325
+ infinite.
1326
+
1327
+ The sorting is in increasing order of dimension, and within
1328
+ each dimension, by the name (if present) of each simplex.
1329
+
1330
+ .. NOTE::
1331
+
1332
+ The sorting is done when the simplicial set is
1333
+ constructed, so changing the name of a simplex after
1334
+ construction will not affect the ordering.
1335
+
1336
+ EXAMPLES::
1337
+
1338
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1339
+ sage: v = AbstractSimplex(0)
1340
+ sage: w = AbstractSimplex(0)
1341
+ sage: S0 = SimplicialSet({v: None, w: None})
1342
+ sage: S0.nondegenerate_simplices()
1343
+ [Delta^0, Delta^0]
1344
+
1345
+ Name the vertices and reconstruct the simplicial set: they
1346
+ should be ordered alphabetically::
1347
+
1348
+ sage: v.rename('v')
1349
+ sage: w.rename('w')
1350
+ sage: S0 = SimplicialSet({v: None, w: None})
1351
+ sage: S0.nondegenerate_simplices()
1352
+ [v, w]
1353
+
1354
+ Rename but do not reconstruct the set; the ordering does not
1355
+ take the new names into account::
1356
+
1357
+ sage: v.rename('z')
1358
+ sage: S0.nondegenerate_simplices() # old ordering is used
1359
+ [z, w]
1360
+
1361
+ sage: X0 = SimplicialSet({v: None, w: None})
1362
+ sage: X0.nondegenerate_simplices() # new ordering is used
1363
+ [w, z]
1364
+
1365
+ Test an infinite example::
1366
+
1367
+ sage: # needs sage.groups
1368
+ sage: C3 = groups.misc.MultiplicativeAbelian([3])
1369
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3)
1370
+ sage: BC3.nondegenerate_simplices(2)
1371
+ [1, f, f^2, f * f, f * f^2, f^2 * f, f^2 * f^2]
1372
+ sage: BC3.nondegenerate_simplices()
1373
+ Traceback (most recent call last):
1374
+ ...
1375
+ NotImplementedError: this simplicial set may be infinite, so specify max_dim
1376
+ """
1377
+ if self.is_finite():
1378
+ if max_dim is None:
1379
+ return list(self._simplices)
1380
+ return [sigma for sigma in self._simplices if sigma.dimension() <= max_dim]
1381
+ if max_dim is None:
1382
+ raise NotImplementedError('this simplicial set may be '
1383
+ 'infinite, so specify max_dim')
1384
+ return list(self.n_skeleton(max_dim)._simplices)
1385
+
1386
+ def cells(self, subcomplex=None, max_dim=None):
1387
+ """
1388
+ Return a dictionary of all non-degenerate simplices.
1389
+
1390
+ INPUT:
1391
+
1392
+ - ``subcomplex`` -- (optional) a subsimplicial set of this
1393
+ simplicial set. If ``subcomplex`` is specified, then return the
1394
+ simplices in the quotient by the subcomplex.
1395
+
1396
+ - ``max_dim`` -- (default: ``None``) if specified,
1397
+ return the non-degenerate simplices of this dimension or
1398
+ smaller. This argument is required if this simplicial set is
1399
+ infinite.
1400
+
1401
+ Each key is a dimension, and the corresponding value is the
1402
+ list of simplices in that dimension.
1403
+
1404
+ EXAMPLES::
1405
+
1406
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1407
+ sage: v = AbstractSimplex(0)
1408
+ sage: w = AbstractSimplex(0)
1409
+ sage: S0 = SimplicialSet({v: None, w: None})
1410
+ sage: S0.cells()
1411
+ {0: [Delta^0, Delta^0]}
1412
+
1413
+ sage: v.rename('v')
1414
+ sage: w.rename('w')
1415
+ sage: S0.cells()
1416
+ {0: [v, w]}
1417
+
1418
+ sage: e = AbstractSimplex(1, name='e')
1419
+ sage: S1 = SimplicialSet({e: (v, v)})
1420
+ sage: S1.cells()
1421
+ {0: [v], 1: [e]}
1422
+
1423
+ sage: S0.cells(S0.subsimplicial_set([v, w]))
1424
+ {0: [*]}
1425
+
1426
+ sage: X = SimplicialSet({e: (v,w)})
1427
+ sage: X.cells(X.subsimplicial_set([v, w]))
1428
+ {0: [*], 1: [e]}
1429
+
1430
+ Test an infinite example::
1431
+
1432
+ sage: # needs sage.groups
1433
+ sage: C3 = groups.misc.MultiplicativeAbelian([3])
1434
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3)
1435
+ sage: BC3.cells(max_dim=2)
1436
+ {0: [1], 1: [f, f^2], 2: [f * f, f * f^2, f^2 * f, f^2 * f^2]}
1437
+ sage: BC3.cells()
1438
+ Traceback (most recent call last):
1439
+ ...
1440
+ NotImplementedError: this simplicial set may be infinite, so specify max_dim
1441
+ """
1442
+ if subcomplex is None:
1443
+ if self.is_finite():
1444
+ simplices = {}
1445
+ for sigma in self.nondegenerate_simplices():
1446
+ if sigma.dimension() in simplices:
1447
+ simplices[sigma.dimension()].append(sigma)
1448
+ else:
1449
+ simplices[sigma.dimension()] = [sigma]
1450
+ if max_dim is not None:
1451
+ return {d: sorted(simplices[d]) for d in simplices
1452
+ if d <= max_dim}
1453
+ return {d: sorted(simplices[d]) for d in simplices}
1454
+ # Infinite case:
1455
+ if max_dim is None:
1456
+ raise NotImplementedError('this simplicial set may be '
1457
+ 'infinite, so specify max_dim')
1458
+ return self.n_skeleton(max_dim).cells()
1459
+ # subcomplex is not None:
1460
+ return self.quotient(subcomplex).cells(max_dim=max_dim)
1461
+
1462
+ def n_cells(self, n, subcomplex=None):
1463
+ """
1464
+ Return the list of cells of dimension ``n`` of this cell complex.
1465
+ If the optional argument ``subcomplex`` is present, then
1466
+ return the ``n``-dimensional faces in the quotient by this
1467
+ subcomplex.
1468
+
1469
+ INPUT:
1470
+
1471
+ - ``n`` -- the dimension
1472
+
1473
+ - ``subcomplex`` -- (default: ``None``) a subcomplex
1474
+ of this cell complex. Return the cells which are in the
1475
+ quotient by this subcomplex.
1476
+
1477
+ EXAMPLES::
1478
+
1479
+ sage: simplicial_sets.Sphere(3).n_cells(3)
1480
+ [sigma_3]
1481
+ sage: simplicial_sets.Sphere(3).n_cells(2)
1482
+ []
1483
+ sage: C2 = groups.misc.MultiplicativeAbelian([2]) # needs sage.groups
1484
+ sage: BC2 = C2.nerve() # needs sage.groups
1485
+ sage: BC2.n_cells(3) # needs sage.groups
1486
+ [f * f * f]
1487
+ """
1488
+ cells = self.cells(subcomplex=subcomplex, max_dim=n)
1489
+ try:
1490
+ return list(cells[n])
1491
+ except KeyError:
1492
+ # Don't barf if someone asks for n_cells in a dimension
1493
+ # where there are none.
1494
+ return []
1495
+
1496
+ def _an_element_(self):
1497
+ """
1498
+ Return an element: a vertex of this simplicial set.
1499
+
1500
+ Return ``None`` if the simplicial set is empty.
1501
+
1502
+ EXAMPLES::
1503
+
1504
+ sage: S4 = simplicial_sets.Sphere(4)
1505
+ sage: S4._an_element_()
1506
+ v_0
1507
+ sage: S4._an_element_() in S4
1508
+ True
1509
+ sage: from sage.topology.simplicial_set_examples import Empty
1510
+ sage: Empty()._an_element_() is None
1511
+ True
1512
+ """
1513
+ vertices = self.n_cells(0)
1514
+ if vertices:
1515
+ return vertices[0]
1516
+ return None
1517
+
1518
+ def all_n_simplices(self, n):
1519
+ """
1520
+ Return a list of all simplices, non-degenerate and degenerate, in dimension ``n``.
1521
+
1522
+ EXAMPLES::
1523
+
1524
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1525
+ sage: v = AbstractSimplex(0, name='v')
1526
+ sage: w = AbstractSimplex(0, name='w')
1527
+ sage: degen = v.apply_degeneracies(0)
1528
+ sage: tau = AbstractSimplex(2, name='tau')
1529
+ sage: Y = SimplicialSet({tau: (degen, degen, degen), w: None})
1530
+
1531
+ ``Y`` is the disjoint union of a 2-sphere, with vertex ``v``
1532
+ and non-degenerate 2-simplex ``tau``, and a point ``w``. ::
1533
+
1534
+ sage: Y.all_n_simplices(0)
1535
+ [v, w]
1536
+ sage: Y.all_n_simplices(1)
1537
+ [s_0 v, s_0 w]
1538
+ sage: Y.all_n_simplices(2)
1539
+ [tau, s_1 s_0 v, s_1 s_0 w]
1540
+
1541
+ An example involving an infinite simplicial set::
1542
+
1543
+ sage: C3 = groups.misc.MultiplicativeAbelian([3]) # needs sage.groups
1544
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3) # needs sage.groups
1545
+ sage: BC3.all_n_simplices(2) # needs sage.groups
1546
+ [f * f,
1547
+ f * f^2,
1548
+ f^2 * f,
1549
+ f^2 * f^2, s_0 f, s_0 f^2, s_1 f, s_1 f^2, s_1 s_0 1]
1550
+ """
1551
+ non_degen = list(self.nondegenerate_simplices(max_dim=n))
1552
+ ans = {_ for _ in non_degen if _.dimension() == n}
1553
+ for sigma in non_degen:
1554
+ d = sigma.dimension()
1555
+ ans.update([sigma.apply_degeneracies(*_)
1556
+ for _ in all_degeneracies(d, n-d)])
1557
+ return sorted(ans)
1558
+
1559
+ def _map_from_empty_set(self):
1560
+ """
1561
+ Return the unique map from the empty set to this simplicial set.
1562
+
1563
+ This is used to in the method :meth:`disjoint_union` to
1564
+ construct disjoint unions as pushouts.
1565
+
1566
+ EXAMPLES::
1567
+
1568
+ sage: T = simplicial_sets.Torus()
1569
+ sage: T._map_from_empty_set()
1570
+ Simplicial set morphism:
1571
+ From: Empty simplicial set
1572
+ To: Torus
1573
+ Defn: [] --> []
1574
+ """
1575
+ from sage.topology.simplicial_set_examples import Empty
1576
+ return Empty().Hom(self)({})
1577
+
1578
+ def identity(self):
1579
+ """
1580
+ Return the identity map on this simplicial set.
1581
+
1582
+ EXAMPLES::
1583
+
1584
+ sage: S3 = simplicial_sets.Sphere(3)
1585
+ sage: S3.identity()
1586
+ Simplicial set endomorphism of S^3
1587
+ Defn: Identity map
1588
+
1589
+ sage: # needs sage.groups
1590
+ sage: C3 = groups.misc.MultiplicativeAbelian([3])
1591
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3)
1592
+ sage: one = BC3.identity()
1593
+ sage: [(sigma, one(sigma)) for sigma in BC3.n_cells(2)]
1594
+ [(f * f, f * f),
1595
+ (f * f^2, f * f^2),
1596
+ (f^2 * f, f^2 * f),
1597
+ (f^2 * f^2, f^2 * f^2)]
1598
+ """
1599
+ return self.Hom(self).identity()
1600
+
1601
+ def constant_map(self, codomain=None, point=None):
1602
+ """
1603
+ Return a constant map with this simplicial set as its domain.
1604
+
1605
+ INPUT:
1606
+
1607
+ - ``codomain`` -- (default: ``None``) if ``None``, the
1608
+ codomain is the standard one-point space constructed by
1609
+ :func:`Point`. Otherwise, either the codomain must be a
1610
+ pointed simplicial set, in which case the map is constant at
1611
+ the base point, or ``point`` must be specified.
1612
+ - ``point`` -- (default: ``None``) if specified, it
1613
+ must be a 0-simplex in the codomain, and it will be the
1614
+ target of the constant map
1615
+
1616
+ EXAMPLES::
1617
+
1618
+ sage: S4 = simplicial_sets.Sphere(4)
1619
+ sage: S4.constant_map()
1620
+ Simplicial set morphism:
1621
+ From: S^4
1622
+ To: Point
1623
+ Defn: Constant map at *
1624
+ sage: S0 = simplicial_sets.Sphere(0)
1625
+ sage: S4.constant_map(codomain=S0)
1626
+ Simplicial set morphism:
1627
+ From: S^4
1628
+ To: S^0
1629
+ Defn: Constant map at v_0
1630
+
1631
+ sage: Sigma3 = groups.permutation.Symmetric(3) # needs sage.groups
1632
+ sage: Sigma3.nerve().constant_map() # needs sage.groups
1633
+ Simplicial set morphism:
1634
+ From: Nerve of Symmetric group of order 3! as a permutation group
1635
+ To: Point
1636
+ Defn: Constant map at *
1637
+
1638
+ TESTS::
1639
+
1640
+ sage: S0 = S0.unset_base_point()
1641
+ sage: S4.constant_map(codomain=S0)
1642
+ Traceback (most recent call last):
1643
+ ...
1644
+ ValueError: codomain is not pointed, so specify a target for the constant map
1645
+ """
1646
+ from sage.topology.simplicial_set_examples import Point
1647
+ if codomain is None:
1648
+ codomain = Point()
1649
+ return self.Hom(codomain).constant_map(point)
1650
+
1651
+ def is_reduced(self):
1652
+ """
1653
+ Return ``True`` if this simplicial set has only one vertex.
1654
+
1655
+ EXAMPLES::
1656
+
1657
+ sage: simplicial_sets.Sphere(0).is_reduced()
1658
+ False
1659
+ sage: simplicial_sets.Sphere(3).is_reduced()
1660
+ True
1661
+ """
1662
+ return len(self.n_cells(0)) == 1
1663
+
1664
+ def graph(self):
1665
+ """
1666
+ Return the 1-skeleton of this simplicial set, as a graph.
1667
+
1668
+ EXAMPLES::
1669
+
1670
+ sage: Delta3 = simplicial_sets.Simplex(3)
1671
+ sage: G = Delta3.graph()
1672
+ sage: G.edges(sort=True)
1673
+ [((0,), (1,), (0, 1)),
1674
+ ((0,), (2,), (0, 2)),
1675
+ ((0,), (3,), (0, 3)),
1676
+ ((1,), (2,), (1, 2)),
1677
+ ((1,), (3,), (1, 3)),
1678
+ ((2,), (3,), (2, 3))]
1679
+
1680
+ sage: T = simplicial_sets.Torus()
1681
+ sage: T.graph()
1682
+ Looped multi-graph on 1 vertex
1683
+ sage: len(T.graph().edges(sort=False))
1684
+ 3
1685
+
1686
+ sage: # needs pyparsing
1687
+ sage: CP3 = simplicial_sets.ComplexProjectiveSpace(3)
1688
+ sage: G = CP3.graph()
1689
+ sage: len(G.vertices(sort=False))
1690
+ 1
1691
+ sage: len(G.edges(sort=False))
1692
+ 0
1693
+
1694
+ sage: Sigma3 = groups.permutation.Symmetric(3) # needs sage.groups
1695
+ sage: Sigma3.nerve().is_connected() # needs sage.groups
1696
+ True
1697
+ """
1698
+ from sage.graphs.graph import Graph
1699
+
1700
+ G = Graph(loops=True, multiedges=True)
1701
+ for e in self.n_cells(1):
1702
+ G.add_edge(self.face(e, 0), self.face(e, 1), e)
1703
+ for v in self.n_cells(0):
1704
+ G.add_vertex(v)
1705
+ return G
1706
+
1707
+ def is_connected(self):
1708
+ """
1709
+ Return ``True`` if this simplicial set is connected.
1710
+
1711
+ EXAMPLES::
1712
+
1713
+ sage: T = simplicial_sets.Torus()
1714
+ sage: K = simplicial_sets.KleinBottle()
1715
+ sage: X = T.disjoint_union(K)
1716
+ sage: T.is_connected()
1717
+ True
1718
+ sage: K.is_connected()
1719
+ True
1720
+ sage: X.is_connected()
1721
+ False
1722
+ sage: simplicial_sets.Sphere(0).is_connected()
1723
+ False
1724
+ """
1725
+ return self.graph().is_connected()
1726
+
1727
+ def subsimplicial_set(self, simplices):
1728
+ """
1729
+ Return the sub-simplicial set of this simplicial set
1730
+ determined by ``simplices``, a set of nondegenerate simplices.
1731
+
1732
+ INPUT:
1733
+
1734
+ - ``simplices`` -- set, list, or tuple of nondegenerate
1735
+ simplices in this simplicial set, or a simplicial
1736
+ complex -- see below.
1737
+
1738
+ Each sub-simplicial set comes equipped with an inclusion map
1739
+ to its ambient space, and you can easily recover its ambient
1740
+ space.
1741
+
1742
+ If ``simplices`` is a simplicial complex, then the original
1743
+ simplicial set should itself have been converted from a
1744
+ simplicial complex, and ``simplices`` should be a subcomplex
1745
+ of that.
1746
+
1747
+ EXAMPLES::
1748
+
1749
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
1750
+ sage: v = AbstractSimplex(0, name='v')
1751
+ sage: w = AbstractSimplex(0, name='w')
1752
+ sage: e = AbstractSimplex(1, name='e')
1753
+ sage: f = AbstractSimplex(1, name='f')
1754
+
1755
+ sage: X = SimplicialSet({e: (v, w), f: (w, v)})
1756
+ sage: Y = X.subsimplicial_set([e]); Y
1757
+ Simplicial set with 3 non-degenerate simplices
1758
+ sage: Y.nondegenerate_simplices()
1759
+ [v, w, e]
1760
+
1761
+ sage: S3 = simplicial_complexes.Sphere(3)
1762
+ sage: K = SimplicialSet(S3)
1763
+ sage: tau = K.n_cells(3)[0]
1764
+ sage: tau.dimension()
1765
+ 3
1766
+ sage: K.subsimplicial_set([tau])
1767
+ Simplicial set with 15 non-degenerate simplices
1768
+
1769
+ A subsimplicial set knows about its ambient space and the
1770
+ inclusion map into it::
1771
+
1772
+ sage: # needs sage.groups
1773
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4)
1774
+ sage: M = RP4.n_skeleton(2); M
1775
+ Simplicial set with 3 non-degenerate simplices
1776
+ sage: M.ambient_space()
1777
+ RP^4
1778
+ sage: M.inclusion_map()
1779
+ Simplicial set morphism:
1780
+ From: Simplicial set with 3 non-degenerate simplices
1781
+ To: RP^4
1782
+ Defn: [1, f, f * f] --> [1, f, f * f]
1783
+
1784
+ An infinite ambient simplicial set::
1785
+
1786
+ sage: # needs sage.groups
1787
+ sage: G = groups.misc.MultiplicativeAbelian([2])
1788
+ sage: B = simplicial_sets.ClassifyingSpace(G)
1789
+ sage: BxB = B.product(B)
1790
+ sage: BxB.n_cells(2)[5:]
1791
+ [(s_0 f, s_1 f), (s_1 f, f * f), (s_1 f, s_0 f), (s_1 s_0 1, f * f)]
1792
+ sage: BxB.subsimplicial_set(BxB.n_cells(2)[5:])
1793
+ Simplicial set with 8 non-degenerate simplices
1794
+
1795
+ TESTS:
1796
+
1797
+ Make sure vertices are treated properly::
1798
+
1799
+ sage: X.subsimplicial_set([v]).nondegenerate_simplices()
1800
+ [v]
1801
+ sage: X.subsimplicial_set([v, w]).nondegenerate_simplices()
1802
+ [v, w]
1803
+ sage: S0 = SimplicialSet({v: None, w: None})
1804
+ sage: S0.subsimplicial_set([w]).nondegenerate_simplices()
1805
+ [w]
1806
+
1807
+ Raise an error if an element of ``simplices`` is not actually
1808
+ in the original simplicial set::
1809
+
1810
+ sage: sigma = AbstractSimplex(2, name='sigma_2')
1811
+ sage: Z = X.subsimplicial_set([e, sigma])
1812
+ Traceback (most recent call last):
1813
+ ...
1814
+ ValueError: not all simplices are in the original simplicial set
1815
+
1816
+ Simplicial complexes::
1817
+
1818
+ sage: X = simplicial_complexes.ComplexProjectivePlane()
1819
+ sage: Y = X._contractible_subcomplex()
1820
+ sage: CP2 = SimplicialSet(X)
1821
+ sage: sub = CP2.subsimplicial_set(Y)
1822
+ sage: CP2.f_vector()
1823
+ [9, 36, 84, 90, 36]
1824
+ sage: K = CP2.quotient(sub)
1825
+ sage: K.f_vector()
1826
+ [1, 0, 16, 30, 16]
1827
+ sage: K.homology() # needs sage.modules
1828
+ {0: 0, 1: 0, 2: Z, 3: 0, 4: Z}
1829
+
1830
+ Try to construct a subcomplex from a simplicial complex which
1831
+ is not actually contained in ``self``::
1832
+
1833
+ sage: Z = SimplicialComplex([[0,1,2,3,4]])
1834
+ sage: CP2.subsimplicial_set(Z)
1835
+ Traceback (most recent call last):
1836
+ ...
1837
+ ValueError: not all simplices are in the original simplicial set
1838
+ """
1839
+ # If simplices is a simplicial complex, turn it into a list of
1840
+ # nondegenerate simplices.
1841
+ from .simplicial_set_constructions import SubSimplicialSet
1842
+ if isinstance(simplices, SimplicialComplex):
1843
+ new = []
1844
+ for f in simplices.facets():
1845
+ d = f.dimension()
1846
+ found = False
1847
+ for x in self.n_cells(d):
1848
+ if str(x) == str(tuple(sorted(f, key=str))):
1849
+ new.append(x)
1850
+ found = True
1851
+ break
1852
+ if not found:
1853
+ raise ValueError('not all simplices are in the original simplicial set')
1854
+ simplices = new
1855
+
1856
+ if not self.is_finite():
1857
+ max_dim = max(sigma.dimension() for sigma in simplices)
1858
+ data = self.n_skeleton(max_dim).face_data()
1859
+ nondegenerate_simplices = self.nondegenerate_simplices(max_dim)
1860
+ else:
1861
+ data = self.face_data()
1862
+ nondegenerate_simplices = self.nondegenerate_simplices()
1863
+ vertices = set()
1864
+ keep = set(simplices)
1865
+ old_keep = set()
1866
+ while keep != old_keep:
1867
+ old_keep = copy.copy(keep)
1868
+ for x in old_keep:
1869
+ underlying = x.nondegenerate()
1870
+ if underlying not in data.keys():
1871
+ raise ValueError('not all simplices are in the original simplicial set')
1872
+ keep.add(underlying)
1873
+ if underlying in data and data[underlying]:
1874
+ keep.update([f.nondegenerate() for f in data[underlying]])
1875
+ else:
1876
+ # x is a vertex
1877
+ assert underlying.dimension() == 0
1878
+ vertices.add(underlying)
1879
+ missing = set(nondegenerate_simplices).difference(keep)
1880
+ for x in missing:
1881
+ if x in data:
1882
+ del data[x]
1883
+ for x in vertices:
1884
+ data[x] = None
1885
+ return SubSimplicialSet(data, self)
1886
+
1887
+ def chain_complex(self, dimensions=None, base_ring=ZZ, augmented=False,
1888
+ cochain=False, verbose=False, subcomplex=None,
1889
+ check=False):
1890
+ r"""
1891
+ Return the normalized chain complex.
1892
+
1893
+ INPUT:
1894
+
1895
+ - ``dimensions`` -- if ``None``, compute the chain complex in all
1896
+ dimensions. If a list or tuple of integers, compute the
1897
+ chain complex in those dimensions, setting the chain groups
1898
+ in all other dimensions to zero.
1899
+
1900
+ - ``base_ring`` -- (default: `\ZZ`) commutative ring
1901
+
1902
+ - ``augmented`` -- boolean (default: ``False``); if ``True``,
1903
+ return the augmented chain complex (that is, include a class
1904
+ in dimension `-1` corresponding to the empty cell)
1905
+
1906
+ - ``cochain`` -- boolean (default: ``False``); if ``True``,
1907
+ return the cochain complex (that is, the dual of the chain
1908
+ complex)
1909
+
1910
+ - ``verbose`` -- boolean (default: ``False``); ignored
1911
+
1912
+ - ``subcomplex`` -- (default: ``None``) if present,
1913
+ compute the chain complex relative to this subcomplex
1914
+
1915
+ - ``check`` -- boolean (default: ``False``); if ``True``, make
1916
+ sure that the chain complex is actually a chain complex:
1917
+ the differentials are composable and their product is zero
1918
+
1919
+ .. NOTE::
1920
+
1921
+ If this simplicial set is not finite, you must specify
1922
+ dimensions in which to compute its chain complex via the
1923
+ argument ``dimensions``.
1924
+
1925
+ EXAMPLES::
1926
+
1927
+ sage: simplicial_sets.Sphere(5).chain_complex() # needs sage.modules
1928
+ Chain complex with at most 3 nonzero terms over Integer Ring
1929
+
1930
+ sage: C3 = groups.misc.MultiplicativeAbelian([3]) # needs sage.groups
1931
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3) # needs sage.groups
1932
+ sage: BC3.chain_complex(range(4), base_ring=GF(3)) # needs sage.groups sage.modules
1933
+ Chain complex with at most 4 nonzero terms over Finite Field of size 3
1934
+
1935
+ TESTS::
1936
+
1937
+ sage: BC3.chain_complex() # needs sage.groups
1938
+ Traceback (most recent call last):
1939
+ ...
1940
+ NotImplementedError: this simplicial set may be infinite, so specify dimensions when computing its chain complex
1941
+ """
1942
+ kwds = {'base_ring': base_ring, 'augmented': augmented, 'cochain': cochain,
1943
+ 'verbose': verbose, 'subcomplex': subcomplex, 'check': check}
1944
+ if not self.is_finite():
1945
+ if dimensions is None:
1946
+ raise NotImplementedError('this simplicial set may be infinite, '
1947
+ 'so specify dimensions when computing '
1948
+ 'its chain complex')
1949
+ else:
1950
+ max_dim = max(dimensions)
1951
+ return SimplicialSet_finite.chain_complex(self.n_skeleton(max_dim+1),
1952
+ dimensions=dimensions,
1953
+ **kwds)
1954
+ return SimplicialSet_finite.chain_complex(self, dimensions=dimensions,
1955
+ **kwds)
1956
+
1957
+ def homology(self, dim=None, **kwds):
1958
+ r"""
1959
+ Return the (reduced) homology of this simplicial set.
1960
+
1961
+ INPUT:
1962
+
1963
+ - ``dim`` -- (default: ``None``) if ``None``, then
1964
+ return the homology in every dimension. If ``dim`` is an
1965
+ integer or list, return the homology in the given
1966
+ dimensions. (Actually, if ``dim`` is a list, return the
1967
+ homology in the range from ``min(dim)`` to ``max(dim)``.)
1968
+
1969
+ - ``base_ring`` -- (default: ``ZZ``) commutative
1970
+ ring; must be ``ZZ`` or a field
1971
+
1972
+ Other arguments are also allowed: see the documentation for
1973
+ :meth:`.cell_complex.GenericCellComplex.homology`.
1974
+
1975
+ .. NOTE::
1976
+
1977
+ If this simplicial set is not finite, you must specify
1978
+ dimensions in which to compute homology via the argument
1979
+ ``dim``.
1980
+
1981
+ EXAMPLES::
1982
+
1983
+ sage: simplicial_sets.Sphere(5).homology() # needs sage.modules
1984
+ {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: Z}
1985
+
1986
+ sage: C3 = groups.misc.MultiplicativeAbelian([3]) # needs sage.groups
1987
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3) # needs sage.groups
1988
+ sage: BC3.homology(range(4), base_ring=GF(3)) # needs sage.groups sage.modules
1989
+ {0: Vector space of dimension 0 over Finite Field of size 3,
1990
+ 1: Vector space of dimension 1 over Finite Field of size 3,
1991
+ 2: Vector space of dimension 1 over Finite Field of size 3,
1992
+ 3: Vector space of dimension 1 over Finite Field of size 3}
1993
+
1994
+ sage: # needs sage.groups
1995
+ sage: C2 = groups.misc.MultiplicativeAbelian([2])
1996
+ sage: BC2 = simplicial_sets.ClassifyingSpace(C2)
1997
+ sage: BK = BC2.product(BC2)
1998
+ sage: BK.homology(range(4)) # needs sage.modules
1999
+ {0: 0, 1: C2 x C2, 2: C2, 3: C2 x C2 x C2}
2000
+
2001
+ TESTS::
2002
+
2003
+ sage: S3 = simplicial_sets.Sphere(3)
2004
+ sage: S3.homology(0) # needs sage.modules
2005
+ 0
2006
+ sage: S3.homology((0,)) # needs sage.modules
2007
+ {0: 0}
2008
+ sage: S3.homology(0, reduced=False) # needs sage.modules
2009
+ Z
2010
+
2011
+ sage: BC3.homology() # needs sage.groups sage.modules
2012
+ Traceback (most recent call last):
2013
+ ...
2014
+ NotImplementedError: this simplicial set may be infinite, so specify dimensions when computing homology
2015
+ """
2016
+ if not self.is_finite():
2017
+ if dim is None:
2018
+ raise NotImplementedError('this simplicial set may be infinite, so '
2019
+ 'specify dimensions when computing homology')
2020
+ else:
2021
+ if isinstance(dim, (list, tuple, range)):
2022
+ dim = list(dim)
2023
+ max_dim = max(dim)
2024
+ space = self.n_skeleton(max_dim+1)
2025
+ min_dim = min(dim)
2026
+ H = GenericCellComplex.homology(space, **kwds)
2027
+ return {n: H[n] for n in H if min_dim <= n <= max_dim}
2028
+ else:
2029
+ max_dim = dim
2030
+ space = self.n_skeleton(max_dim+1)
2031
+ else:
2032
+ space = self
2033
+ return GenericCellComplex.homology(space, dim=dim, **kwds)
2034
+
2035
+ def cohomology(self, dim=None, **kwds):
2036
+ r"""
2037
+ Return the cohomology of this simplicial set.
2038
+
2039
+ INPUT:
2040
+
2041
+ - ``dim`` -- (default: ``None``) if ``None``, then
2042
+ return the homology in every dimension. If ``dim`` is an
2043
+ integer or list, return the homology in the given
2044
+ dimensions. (Actually, if ``dim`` is a list, return the
2045
+ homology in the range from ``min(dim)`` to ``max(dim)``.)
2046
+
2047
+ - ``base_ring`` -- (default: ``ZZ``) commutative
2048
+ ring; must be ``ZZ`` or a field
2049
+
2050
+ Other arguments are also allowed, the same as for the
2051
+ :meth:`homology` method -- see
2052
+ :meth:`.cell_complex.GenericCellComplex.homology` for complete
2053
+ documentation -- except that :meth:`homology` accepts a
2054
+ ``cohomology`` key word, while this function does not:
2055
+ ``cohomology`` is automatically true here. Indeed, this
2056
+ function just calls :meth:`homology` with argument
2057
+ ``cohomology=True``.
2058
+
2059
+ .. NOTE::
2060
+
2061
+ If this simplicial set is not finite, you must specify
2062
+ dimensions in which to compute homology via the argument
2063
+ ``dim``.
2064
+
2065
+ EXAMPLES::
2066
+
2067
+ sage: simplicial_sets.KleinBottle().homology(1) # needs sage.modules
2068
+ Z x C2
2069
+ sage: simplicial_sets.KleinBottle().cohomology(1) # needs sage.modules
2070
+ Z
2071
+ sage: simplicial_sets.KleinBottle().cohomology(2) # needs sage.modules
2072
+ C2
2073
+
2074
+ TESTS::
2075
+
2076
+ sage: C3 = groups.misc.MultiplicativeAbelian([3]) # needs sage.groups
2077
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3) # needs sage.groups
2078
+ sage: BC3.cohomology() # needs sage.groups
2079
+ Traceback (most recent call last):
2080
+ ...
2081
+ NotImplementedError: this simplicial set may be infinite,
2082
+ so specify dimensions when computing homology
2083
+ """
2084
+ return self.homology(dim=dim, cohomology=True, **kwds)
2085
+
2086
+ def betti(self, dim=None, subcomplex=None):
2087
+ r"""
2088
+ The Betti numbers of this simplicial complex as a dictionary
2089
+ (or a single Betti number, if only one dimension is given):
2090
+ the `i`-th Betti number is the rank of the `i`-th homology group.
2091
+
2092
+ INPUT:
2093
+
2094
+ - ``dim`` -- (default: ``None``) if ``None``, then
2095
+ return the homology in every dimension. If ``dim`` is an
2096
+ integer or list, return the homology in the given
2097
+ dimensions. (Actually, if ``dim`` is a list, return the
2098
+ homology in the range from ``min(dim)`` to ``max(dim)``.)
2099
+
2100
+ - ``subcomplex`` -- (default: ``None``) a subcomplex
2101
+ of this cell complex. Compute the Betti numbers of the
2102
+ homology relative to this subcomplex.
2103
+
2104
+ .. NOTE::
2105
+
2106
+ If this simplicial set is not finite, you must specify
2107
+ dimensions in which to compute Betti numbers via the
2108
+ argument ``dim``.
2109
+
2110
+ EXAMPLES:
2111
+
2112
+ Build the two-sphere as a three-fold join of a
2113
+ two-point space with itself::
2114
+
2115
+ sage: simplicial_sets.Sphere(5).betti() # needs sage.modules
2116
+ {0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1}
2117
+
2118
+ sage: C3 = groups.misc.MultiplicativeAbelian([3]) # needs sage.groups
2119
+ sage: BC3 = simplicial_sets.ClassifyingSpace(C3) # needs sage.groups
2120
+ sage: BC3.betti(range(4)) # needs sage.groups sage.modules
2121
+ {0: 1, 1: 0, 2: 0, 3: 0}
2122
+ """
2123
+ dic = {}
2124
+ H = self.homology(dim, base_ring=QQ, subcomplex=subcomplex)
2125
+ try:
2126
+ for n in H.keys():
2127
+ dic[n] = H[n].dimension()
2128
+ if n == 0:
2129
+ dic[n] += 1
2130
+ except AttributeError:
2131
+ return H.dimension()
2132
+ else:
2133
+ return dic
2134
+
2135
+ def n_chains(self, n, base_ring=ZZ, cochains=False):
2136
+ r"""
2137
+ Return the free module of (normalized) chains in degree ``n``
2138
+ over ``base_ring``.
2139
+
2140
+ This is the free module on the nondegenerate simplices in the
2141
+ given dimension.
2142
+
2143
+ INPUT:
2144
+
2145
+ - ``n`` -- integer
2146
+ - ``base_ring`` -- ring (default: `\ZZ`)
2147
+ - ``cochains`` -- boolean (default: ``False``); if
2148
+ ``True``, return cochains instead
2149
+
2150
+ The only difference between chains and cochains is notation:
2151
+ the generator corresponding to the dual of a simplex
2152
+ ``sigma`` is written as ``'\chi_sigma'`` in the group of
2153
+ cochains.
2154
+
2155
+ EXAMPLES::
2156
+
2157
+ sage: S3 = simplicial_sets.Sphere(3)
2158
+ sage: C = S3.n_chains(3, cochains=True) # needs sage.modules
2159
+ sage: list(C.basis()) # needs sage.modules
2160
+ [\chi_sigma_3]
2161
+
2162
+ sage: # needs sage.groups
2163
+ sage: Sigma3 = groups.permutation.Symmetric(3)
2164
+ sage: BSigma3 = simplicial_sets.ClassifyingSpace(Sigma3)
2165
+ sage: list(BSigma3.n_chains(1).basis()) # needs sage.modules
2166
+ [(1,2), (1,2,3), (1,3), (1,3,2), (2,3)]
2167
+ sage: list(BSigma3.n_chains(1, cochains=True).basis()) # needs sage.modules
2168
+ [\chi_(1,2), \chi_(1,2,3), \chi_(1,3), \chi_(1,3,2), \chi_(2,3)]
2169
+ """
2170
+ if self.is_finite():
2171
+ return GenericCellComplex.n_chains(self, n=n,
2172
+ base_ring=base_ring,
2173
+ cochains=cochains)
2174
+
2175
+ from sage.homology.chains import Chains, Cochains
2176
+
2177
+ n_cells = tuple(self.n_cells(n))
2178
+ if cochains:
2179
+ return Cochains(self, n, n_cells, base_ring)
2180
+ else:
2181
+ return Chains(self, n, n_cells, base_ring)
2182
+
2183
+ def quotient(self, subcomplex, vertex_name='*'):
2184
+ """
2185
+ Return the quotient of this simplicial set by ``subcomplex``.
2186
+
2187
+ That is, ``subcomplex`` is replaced by a vertex.
2188
+
2189
+ INPUT:
2190
+
2191
+ - ``subcomplex`` -- subsimplicial set of this simplicial set,
2192
+ or a list, tuple, or set of simplices defining a
2193
+ subsimplicial set
2194
+
2195
+ - ``vertex_name`` -- string (default: ``'*'``); name to be given to the
2196
+ new vertex
2197
+
2198
+ In Sage, from a quotient simplicial set, you can recover the
2199
+ ambient space, the subcomplex, and (if the ambient space is
2200
+ finite) the quotient map.
2201
+
2202
+ Base points: if the original simplicial set has a base point
2203
+ not contained in ``subcomplex`` and if the original simplicial
2204
+ set is finite, then use its image as the base point for the
2205
+ quotient. In all other cases, ``*`` is the base point.
2206
+
2207
+ EXAMPLES::
2208
+
2209
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2210
+ sage: v = AbstractSimplex(0, name='v')
2211
+ sage: w = AbstractSimplex(0, name='w')
2212
+ sage: e = AbstractSimplex(1, name='e')
2213
+ sage: f = AbstractSimplex(1, name='f')
2214
+ sage: X = SimplicialSet({e: (v, w), f: (v, w)})
2215
+ sage: Y = X.quotient([f])
2216
+ sage: Y.nondegenerate_simplices()
2217
+ [*, e]
2218
+ sage: Y.homology(1) # needs sage.modules
2219
+ Z
2220
+
2221
+ sage: E = SimplicialSet({e: (v, w)})
2222
+ sage: Z = E.quotient([v, w])
2223
+ sage: Z.nondegenerate_simplices()
2224
+ [*, e]
2225
+ sage: Z.homology(1) # needs sage.modules
2226
+ Z
2227
+
2228
+ sage: F = E.quotient([v])
2229
+ sage: F.nondegenerate_simplices()
2230
+ [*, w, e]
2231
+ sage: F.base_point()
2232
+ *
2233
+
2234
+ sage: # needs sage.groups
2235
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5)
2236
+ sage: RP2 = RP5.n_skeleton(2)
2237
+ sage: RP5_2 = RP5.quotient(RP2)
2238
+ sage: RP5_2.homology(base_ring=GF(2)) # needs sage.modules
2239
+ {0: Vector space of dimension 0 over Finite Field of size 2,
2240
+ 1: Vector space of dimension 0 over Finite Field of size 2,
2241
+ 2: Vector space of dimension 0 over Finite Field of size 2,
2242
+ 3: Vector space of dimension 1 over Finite Field of size 2,
2243
+ 4: Vector space of dimension 1 over Finite Field of size 2,
2244
+ 5: Vector space of dimension 1 over Finite Field of size 2}
2245
+ sage: RP5_2.ambient()
2246
+ RP^5
2247
+ sage: RP5_2.subcomplex()
2248
+ Simplicial set with 3 non-degenerate simplices
2249
+ sage: RP5_2.quotient_map()
2250
+ Simplicial set morphism:
2251
+ From: RP^5
2252
+ To: Quotient: (RP^5/Simplicial set with 3 non-degenerate simplices)
2253
+ Defn: [1, f, f * f, f * f * f, f * f * f * f, f * f * f * f * f]
2254
+ --> [*, s_0 *, s_1 s_0 *, f * f * f, f * f * f * f, f * f * f * f * f]
2255
+
2256
+ Behavior of base points::
2257
+
2258
+ sage: K = simplicial_sets.Simplex(3)
2259
+ sage: K.is_pointed()
2260
+ False
2261
+ sage: L = K.subsimplicial_set([K.n_cells(1)[-1]])
2262
+ sage: L.nondegenerate_simplices()
2263
+ [(2,), (3,), (2, 3)]
2264
+ sage: K.quotient([K.n_cells(1)[-1]]).base_point()
2265
+ *
2266
+
2267
+ sage: K = K.set_base_point(K.n_cells(0)[0])
2268
+ sage: K.base_point()
2269
+ (0,)
2270
+ sage: L = K.subsimplicial_set([K.n_cells(1)[-1]])
2271
+ sage: L.nondegenerate_simplices()
2272
+ [(2,), (3,), (2, 3)]
2273
+ sage: K.quotient(L).base_point()
2274
+ (0,)
2275
+
2276
+ TESTS::
2277
+
2278
+ sage: pt = RP5.quotient(RP5.n_skeleton(5)); pt # needs sage.groups
2279
+ Quotient: (RP^5/RP^5)
2280
+ sage: len(pt.nondegenerate_simplices()) # needs sage.groups
2281
+ 1
2282
+ """
2283
+ from .simplicial_set_constructions import SubSimplicialSet
2284
+ from .simplicial_set_constructions import QuotientOfSimplicialSet, \
2285
+ QuotientOfSimplicialSet_finite
2286
+ if not isinstance(subcomplex, SimplicialSet_finite):
2287
+ # If it's not a simplicial set, subcomplex should be a
2288
+ # list, tuple, or set of simplices, so form the actual
2289
+ # subcomplex:
2290
+ subcomplex = self.subsimplicial_set(subcomplex)
2291
+ else:
2292
+ # Test whether subcomplex is actually a subcomplex of
2293
+ # self.
2294
+ if (not isinstance(subcomplex, SubSimplicialSet)
2295
+ and subcomplex.ambient_space() == self):
2296
+ raise ValueError('the "subcomplex" is not actually a subcomplex')
2297
+ if self.is_finite():
2298
+ return QuotientOfSimplicialSet_finite(subcomplex.inclusion_map(),
2299
+ vertex_name=vertex_name)
2300
+ else:
2301
+ return QuotientOfSimplicialSet(subcomplex.inclusion_map(),
2302
+ vertex_name=vertex_name)
2303
+
2304
+ def disjoint_union(self, *others):
2305
+ """
2306
+ Return the disjoint union of this simplicial set with ``others``.
2307
+
2308
+ INPUT:
2309
+
2310
+ - ``others`` -- one or several simplicial sets
2311
+
2312
+ As long as the factors are all finite, the inclusion map from
2313
+ each factor is available. Any factors which are empty are
2314
+ ignored completely: they do not appear in the list of factors,
2315
+ etc.
2316
+
2317
+ EXAMPLES::
2318
+
2319
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2320
+ sage: v = AbstractSimplex(0, name='v')
2321
+ sage: w = AbstractSimplex(0, name='w')
2322
+ sage: e = AbstractSimplex(1, name='e')
2323
+ sage: f = AbstractSimplex(1, name='f')
2324
+ sage: X = SimplicialSet({e: (v, v)})
2325
+ sage: Y = SimplicialSet({f: (v, w)})
2326
+ sage: Z = X.disjoint_union(Y)
2327
+
2328
+ Since ``X`` and ``Y`` have simplices in common, Sage uses a
2329
+ copy of ``Y`` when constructing the disjoint union. Note the
2330
+ name conflict in the list of simplices: ``v`` appears twice::
2331
+
2332
+ sage: Z = X.disjoint_union(Y)
2333
+ sage: Z.nondegenerate_simplices()
2334
+ [v, v, w, e, f]
2335
+
2336
+ Factors and inclusion maps::
2337
+
2338
+ sage: T = simplicial_sets.Torus()
2339
+ sage: S2 = simplicial_sets.Sphere(2)
2340
+ sage: A = T.disjoint_union(S2)
2341
+ sage: A.factors()
2342
+ (Torus, S^2)
2343
+ sage: i = A.inclusion_map(0)
2344
+ sage: i.domain()
2345
+ Torus
2346
+ sage: i.codomain()
2347
+ Disjoint union: (Torus u S^2)
2348
+
2349
+ Empty factors are ignored::
2350
+
2351
+ sage: from sage.topology.simplicial_set_examples import Empty
2352
+ sage: E = Empty()
2353
+ sage: K = S2.disjoint_union(S2, E, E, S2)
2354
+ sage: K == S2.disjoint_union(S2, S2)
2355
+ True
2356
+ sage: K.factors()
2357
+ (S^2, S^2, S^2)
2358
+ """
2359
+ from .simplicial_set_constructions import DisjointUnionOfSimplicialSets, \
2360
+ DisjointUnionOfSimplicialSets_finite
2361
+ if all(space.is_finite() for space in [self] + list(others)):
2362
+ return DisjointUnionOfSimplicialSets_finite((self,) + others)
2363
+ else:
2364
+ return DisjointUnionOfSimplicialSets((self,) + others)
2365
+
2366
+ def coproduct(self, *others):
2367
+ """
2368
+ Return the coproduct of this simplicial set with ``others``.
2369
+
2370
+ INPUT:
2371
+
2372
+ - ``others`` -- one or several simplicial sets
2373
+
2374
+ If these simplicial sets are pointed, return their wedge sum;
2375
+ if they are not, return their disjoint union. If some are
2376
+ pointed and some are not, raise an error: it is not clear in
2377
+ which category to work.
2378
+
2379
+ EXAMPLES::
2380
+
2381
+ sage: S2 = simplicial_sets.Sphere(2)
2382
+ sage: K = simplicial_sets.KleinBottle()
2383
+ sage: D3 = simplicial_sets.Simplex(3)
2384
+ sage: Y = S2.unset_base_point()
2385
+ sage: Z = K.unset_base_point()
2386
+
2387
+ sage: S2.coproduct(K).is_pointed()
2388
+ True
2389
+ sage: S2.coproduct(K)
2390
+ Wedge: (S^2 v Klein bottle)
2391
+ sage: D3.coproduct(Y, Z).is_pointed()
2392
+ False
2393
+ sage: D3.coproduct(Y, Z)
2394
+ Disjoint union: (3-simplex u Simplicial set with 2 non-degenerate simplices
2395
+ u Simplicial set with 6 non-degenerate simplices)
2396
+
2397
+ The coproduct comes equipped with an inclusion map from each
2398
+ summand, as long as the summands are all finite::
2399
+
2400
+ sage: S2.coproduct(K).inclusion_map(0)
2401
+ Simplicial set morphism:
2402
+ From: S^2
2403
+ To: Wedge: (S^2 v Klein bottle)
2404
+ Defn: [v_0, sigma_2] --> [*, sigma_2]
2405
+ sage: D3.coproduct(Y, Z).inclusion_map(2)
2406
+ Simplicial set morphism:
2407
+ From: Simplicial set with 6 non-degenerate simplices
2408
+ To: Disjoint union: (3-simplex
2409
+ u Simplicial set with 2 non-degenerate simplices
2410
+ u Simplicial set with 6 non-degenerate simplices)
2411
+ Defn: [Delta_{0,0}, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2412
+ --> [Delta_{0,0}, Delta_{1,0}, Delta_{1,1}, Delta_{1,2}, Delta_{2,0}, Delta_{2,1}]
2413
+
2414
+ TESTS::
2415
+
2416
+ sage: D3.coproduct(S2, Z)
2417
+ Traceback (most recent call last):
2418
+ ...
2419
+ ValueError: some, but not all, of the simplicial sets are pointed,
2420
+ so the categorical coproduct is not defined: the category is ambiguous
2421
+ """
2422
+ if self.is_pointed() and all(X.is_pointed() for X in others):
2423
+ return self.wedge(*others)
2424
+ if self.is_pointed() or any(X.is_pointed() for X in others):
2425
+ raise ValueError('some, but not all, of the simplicial sets are pointed, '
2426
+ 'so the categorical coproduct is not defined: the '
2427
+ 'category is ambiguous')
2428
+ return self.disjoint_union(*others)
2429
+
2430
+ def product(self, *others):
2431
+ r"""
2432
+ Return the product of this simplicial set with ``others``.
2433
+
2434
+ INPUT:
2435
+
2436
+ - ``others`` -- one or several simplicial sets
2437
+
2438
+ If `X` and `Y` are simplicial sets, then their product `X
2439
+ \times Y` is defined to be the simplicial set with
2440
+ `n`-simplices `X_n \times Y_n`. See
2441
+ :class:`.simplicial_set_constructions.ProductOfSimplicialSets`
2442
+ for more information.
2443
+
2444
+ If a simplicial set is constructed as a product, the factors
2445
+ are recorded and are accessible via the method
2446
+ :meth:`.simplicial_set_constructions.Factors.factors`.
2447
+ If each factor is finite, then you can also construct the
2448
+ projection maps onto each factor, the wedge as a subcomplex,
2449
+ and the fat wedge as a subcomplex.
2450
+
2451
+ EXAMPLES::
2452
+
2453
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2454
+ sage: v = AbstractSimplex(0, name='v')
2455
+ sage: w = AbstractSimplex(0, name='w')
2456
+ sage: e = AbstractSimplex(1, name='e')
2457
+ sage: X = SimplicialSet({e: (v, w)})
2458
+ sage: square = X.product(X)
2459
+
2460
+ ``square`` is now the standard triangulation of the square: 4
2461
+ vertices, 5 edges (the four on the border and the diagonal), 2
2462
+ triangles::
2463
+
2464
+ sage: square.f_vector()
2465
+ [4, 5, 2]
2466
+
2467
+ sage: S1 = simplicial_sets.Sphere(1)
2468
+ sage: T = S1.product(S1)
2469
+ sage: T.homology(reduced=False) # needs sage.modules
2470
+ {0: Z, 1: Z x Z, 2: Z}
2471
+
2472
+ Since ``S1`` is pointed, so is ``T``::
2473
+
2474
+ sage: S1.is_pointed()
2475
+ True
2476
+ sage: S1.base_point()
2477
+ v_0
2478
+ sage: T.is_pointed()
2479
+ True
2480
+ sage: T.base_point()
2481
+ (v_0, v_0)
2482
+
2483
+ sage: S2 = simplicial_sets.Sphere(2)
2484
+ sage: S3 = simplicial_sets.Sphere(3)
2485
+ sage: S2xS3 = S2.product(S3)
2486
+ sage: S2xS3.homology(reduced=False) # needs sage.modules
2487
+ {0: Z, 1: 0, 2: Z, 3: Z, 4: 0, 5: Z}
2488
+
2489
+ sage: S2xS3.factors() == (S2, S3)
2490
+ True
2491
+ sage: S2xS3.factors() == (S3, S2)
2492
+ False
2493
+
2494
+ sage: # needs sage.groups
2495
+ sage: G = groups.misc.MultiplicativeAbelian([2])
2496
+ sage: B = simplicial_sets.ClassifyingSpace(G)
2497
+ sage: B.rename('RP^oo')
2498
+ sage: X = B.product(B, S2); X
2499
+ RP^oo x RP^oo x S^2
2500
+ sage: X.factor(1)
2501
+ RP^oo
2502
+ sage: X.factors()
2503
+ (RP^oo, RP^oo, S^2)
2504
+
2505
+ Projection maps and wedges::
2506
+
2507
+ sage: S2xS3.projection_map(0)
2508
+ Simplicial set morphism:
2509
+ From: S^2 x S^3
2510
+ To: S^2
2511
+ Defn: ...
2512
+ sage: S2xS3.wedge_as_subset().homology() # needs sage.modules
2513
+ {0: 0, 1: 0, 2: Z, 3: Z}
2514
+
2515
+ In the case of pointed simplicial sets, there is an inclusion
2516
+ of each factor into the product. These are not automatically
2517
+ defined in Sage, but they are easy to construct using identity
2518
+ maps and constant maps and the universal property of the
2519
+ product::
2520
+
2521
+ sage: one = S2.identity()
2522
+ sage: const = S2.constant_map(codomain=S3)
2523
+ sage: S2xS3.universal_property(one, const)
2524
+ Simplicial set morphism:
2525
+ From: S^2
2526
+ To: S^2 x S^3
2527
+ Defn: [v_0, sigma_2] --> [(v_0, v_0), (sigma_2, s_1 s_0 v_0)]
2528
+ """
2529
+ from .simplicial_set_constructions import ProductOfSimplicialSets, \
2530
+ ProductOfSimplicialSets_finite
2531
+ if self.is_finite() and all(X.is_finite() for X in others):
2532
+ return ProductOfSimplicialSets_finite((self,) + others)
2533
+ else:
2534
+ return ProductOfSimplicialSets((self,) + others)
2535
+
2536
+ cartesian_product = product
2537
+
2538
+ def pushout(self, *maps):
2539
+ r"""
2540
+ Return the pushout obtained from given ``maps``.
2541
+
2542
+ INPUT:
2543
+
2544
+ - ``maps`` -- several maps of simplicial sets, each of which
2545
+ has this simplicial set as its domain
2546
+
2547
+ If only a single map `f: X \to Y` is given, then return
2548
+ `Y`. If more than one map is given, say `f_i: X \to Y_i` for
2549
+ `0 \leq i \leq m`, then return the pushout defined by those
2550
+ maps. If no maps are given, return the empty simplicial set.
2551
+
2552
+ In addition to the defining maps `f_i` used to construct the
2553
+ pushout `P`, there are also maps `\bar{f}_i: Y_i \to P`, which
2554
+ we refer to as *structure maps*. The pushout also has a
2555
+ universal property: given maps `g_i: Y_i \to Z` such that `g_i
2556
+ f_i = g_j f_j` for all `i`, `j`, then there is a unique map
2557
+ `g: P \to Z` making the appropriate diagram commute: that is,
2558
+ `g \bar{f}_i = g_i` for all `i`.
2559
+
2560
+ In Sage, a pushout is equipped with its defining maps, and as
2561
+ long as the simplicial sets involved are finite, you can also
2562
+ access the structure maps and the universal property.
2563
+
2564
+ EXAMPLES:
2565
+
2566
+ Construct the 4-sphere as a quotient of a 4-simplex::
2567
+
2568
+ sage: K = simplicial_sets.Simplex(4)
2569
+ sage: L = K.n_skeleton(3)
2570
+ sage: S4 = L.pushout(L.constant_map(), L.inclusion_map()); S4
2571
+ Pushout of maps:
2572
+ Simplicial set morphism:
2573
+ From: Simplicial set with 30 non-degenerate simplices
2574
+ To: Point
2575
+ Defn: Constant map at *
2576
+ Simplicial set morphism:
2577
+ From: Simplicial set with 30 non-degenerate simplices
2578
+ To: 4-simplex
2579
+ Defn: [(0,), (1,), (2,), (3,), (4,),
2580
+ (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4),
2581
+ (2, 3), (2, 4), (3, 4),
2582
+ (0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 2, 3), (0, 2, 4),
2583
+ (0, 3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4),
2584
+ (0, 1, 2, 3), (0, 1, 2, 4), (0, 1, 3, 4), (0, 2, 3, 4),
2585
+ (1, 2, 3, 4)]
2586
+ --> [(0,), (1,), (2,), (3,), (4,),
2587
+ (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4),
2588
+ (2, 3), (2, 4), (3, 4),
2589
+ (0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 2, 3), (0, 2, 4), (0, 3, 4),
2590
+ (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4),
2591
+ (0, 1, 2, 3), (0, 1, 2, 4), (0, 1, 3, 4), (0, 2, 3, 4), (1, 2, 3, 4)]
2592
+ sage: len(S4.nondegenerate_simplices())
2593
+ 2
2594
+ sage: S4.homology(4) # needs sage.modules
2595
+ Z
2596
+
2597
+ The associated maps::
2598
+
2599
+ sage: S1 = simplicial_sets.Sphere(1)
2600
+ sage: T = S1.product(S1)
2601
+ sage: K = T.factor(0, as_subset=True)
2602
+ sage: W = S1.wedge(T) # wedge, constructed as a pushout
2603
+ sage: W.defining_map(1)
2604
+ Simplicial set morphism:
2605
+ From: Point
2606
+ To: S^1 x S^1
2607
+ Defn: Constant map at (v_0, v_0)
2608
+ sage: W.structure_map(0)
2609
+ Simplicial set morphism:
2610
+ From: S^1
2611
+ To: Wedge: (S^1 v S^1 x S^1)
2612
+ Defn: [v_0, sigma_1] --> [*, sigma_1]
2613
+
2614
+ sage: f = S1.Hom(T)({S1.n_cells(0)[0]: K.n_cells(0)[0],
2615
+ ....: S1.n_cells(1)[0]: K.n_cells(1)[0]})
2616
+
2617
+ The maps `f: S^1 \to T` and `1: T \to T` induce a map `S^1 \vee T \to T`::
2618
+
2619
+ sage: g = W.universal_property(f, Hom(T,T).identity())
2620
+ sage: g.domain() == W
2621
+ True
2622
+ sage: g.codomain() == T
2623
+ True
2624
+
2625
+ TESTS::
2626
+
2627
+ sage: K = simplicial_sets.Simplex(5)
2628
+ sage: K.pushout()
2629
+ Empty simplicial set
2630
+
2631
+ sage: S0 = simplicial_sets.Sphere(0)
2632
+ sage: pt_map = S0.base_point_map()
2633
+ sage: pt_map.domain().pushout(pt_map) == S0
2634
+ True
2635
+
2636
+ sage: K.pushout(K.constant_map(), pt_map)
2637
+ Traceback (most recent call last):
2638
+ ...
2639
+ ValueError: the domains of the maps must be equal
2640
+ """
2641
+ from .simplicial_set_constructions import PushoutOfSimplicialSets, \
2642
+ PushoutOfSimplicialSets_finite
2643
+ if any(self != f.domain() for f in maps):
2644
+ raise ValueError('the domains of the maps must be equal')
2645
+ if not maps:
2646
+ return PushoutOfSimplicialSets_finite()
2647
+ if all(f.codomain().is_finite() for f in maps):
2648
+ return PushoutOfSimplicialSets_finite(maps)
2649
+ else:
2650
+ return PushoutOfSimplicialSets(maps)
2651
+
2652
+ def pullback(self, *maps):
2653
+ r"""
2654
+ Return the pullback obtained from given ``maps``.
2655
+
2656
+ INPUT:
2657
+
2658
+ - ``maps`` -- several maps of simplicial sets, each of which
2659
+ has this simplicial set as its codomain
2660
+
2661
+ If only a single map `f: X \to Y` is given, then return
2662
+ `X`. If more than one map is given, say `f_i: X_i \to Y` for
2663
+ `0 \leq i \leq m`, then return the pullback defined by those
2664
+ maps. If no maps are given, return the one-point simplicial
2665
+ set.
2666
+
2667
+ In addition to the defining maps `f_i` used to construct the
2668
+ pullback `P`, there are also maps `\bar{f}_i: P \to X_i`,
2669
+ which we refer to as *structure maps* or *projection
2670
+ maps*. The pullback also has a universal property: given maps
2671
+ `g_i: Z \to X_i` such that `f_i g_i = f_j g_j` for all `i`,
2672
+ `j`, then there is a unique map `g: Z \to P` making the
2673
+ appropriate diagram commute: that is, `\bar{f}_i g = g_i` for
2674
+ all `i`. For example, given maps `f: X \to Y` and `g: X \to
2675
+ Z`, there is an induced map `g: X \to Y \times Z`.
2676
+
2677
+ In Sage, a pullback is equipped with its defining maps, and as
2678
+ long as the simplicial sets involved are finite, you can also
2679
+ access the structure maps and the universal property.
2680
+
2681
+ EXAMPLES:
2682
+
2683
+ Construct a product as a pullback::
2684
+
2685
+ sage: S2 = simplicial_sets.Sphere(2)
2686
+ sage: pt = simplicial_sets.Point()
2687
+ sage: P = pt.pullback(S2.constant_map(), S2.constant_map())
2688
+ sage: P.homology(2) # needs sage.modules
2689
+ Z x Z
2690
+
2691
+ If the pullback is defined via maps `f_i: X_i \to Y`, then
2692
+ there are structure maps `\bar{f}_i: Y_i \to P`. The structure
2693
+ maps are only available in Sage when all of the maps involved
2694
+ have finite domains. ::
2695
+
2696
+ sage: S2 = simplicial_sets.Sphere(2)
2697
+ sage: one = S2.Hom(S2).identity()
2698
+ sage: P = S2.pullback(one, one)
2699
+ sage: P.homology() # needs sage.modules
2700
+ {0: 0, 1: 0, 2: Z}
2701
+
2702
+ sage: P.defining_map(0) == one
2703
+ True
2704
+ sage: P.structure_map(1)
2705
+ Simplicial set morphism:
2706
+ From: Pullback of maps:
2707
+ Simplicial set endomorphism of S^2
2708
+ Defn: Identity map
2709
+ Simplicial set endomorphism of S^2
2710
+ Defn: Identity map
2711
+ To: S^2
2712
+ Defn: [(v_0, v_0), (sigma_2, sigma_2)] --> [v_0, sigma_2]
2713
+ sage: P.structure_map(0).domain() == P
2714
+ True
2715
+ sage: P.structure_map(0).codomain() == S2
2716
+ True
2717
+
2718
+ The universal property::
2719
+
2720
+ sage: S1 = simplicial_sets.Sphere(1)
2721
+ sage: T = S1.product(S1)
2722
+ sage: K = T.factor(0, as_subset=True)
2723
+ sage: f = S1.Hom(T)({S1.n_cells(0)[0]: K.n_cells(0)[0],
2724
+ ....: S1.n_cells(1)[0]: K.n_cells(1)[0]})
2725
+ sage: D = S1.cone() # the cone C(S^1)
2726
+ sage: g = D.map_from_base() # map from S^1 to C(S^1)
2727
+ sage: P = T.product(D)
2728
+ sage: h = P.universal_property(f, g)
2729
+ sage: h.domain() == S1
2730
+ True
2731
+ sage: h.codomain() == P
2732
+ True
2733
+
2734
+ TESTS::
2735
+
2736
+ sage: pt.pullback(S2.constant_map(), S2.base_point_map())
2737
+ Traceback (most recent call last):
2738
+ ...
2739
+ ValueError: the codomains of the maps must be equal
2740
+ """
2741
+ from .simplicial_set_constructions import PullbackOfSimplicialSets, \
2742
+ PullbackOfSimplicialSets_finite
2743
+ if any(self != f.codomain() for f in maps):
2744
+ raise ValueError('the codomains of the maps must be equal')
2745
+ if not maps:
2746
+ return PullbackOfSimplicialSets_finite()
2747
+ if self.is_finite() and all(f.domain().is_finite() for f in maps):
2748
+ return PullbackOfSimplicialSets_finite(maps)
2749
+ else:
2750
+ return PullbackOfSimplicialSets(maps)
2751
+
2752
+ # Ideally, this would be defined at the category level and only
2753
+ # for pointed simplicial sets, but the abstract_method "wedge" in
2754
+ # cell_complex.py would shadow that.
2755
+ def wedge(self, *others):
2756
+ r"""
2757
+ Return the wedge sum of this pointed simplicial set with ``others``.
2758
+
2759
+ - ``others`` -- one or several simplicial sets
2760
+
2761
+ This constructs the quotient of the disjoint union in which
2762
+ the base points of all of the simplicial sets have been
2763
+ identified. This is the coproduct in the category of pointed
2764
+ simplicial sets.
2765
+
2766
+ This raises an error if any of the factors is not pointed.
2767
+
2768
+ From the wedge, you can access the factors, and if the
2769
+ simplicial sets involved are all finite, you can also access
2770
+ the inclusion map of each factor into the wedge, as well as
2771
+ the projection map onto each factor.
2772
+
2773
+ EXAMPLES::
2774
+
2775
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2776
+ sage: v = AbstractSimplex(0, name='v')
2777
+ sage: e = AbstractSimplex(1, name='e')
2778
+ sage: w = AbstractSimplex(0, name='w')
2779
+ sage: f = AbstractSimplex(1, name='f')
2780
+ sage: X = SimplicialSet({e: (v, v)}, base_point=v)
2781
+ sage: Y = SimplicialSet({f: (w, w)}, base_point=w)
2782
+ sage: W = X.wedge(Y)
2783
+ sage: W.nondegenerate_simplices()
2784
+ [*, e, f]
2785
+ sage: W.homology() # needs sage.modules
2786
+ {0: 0, 1: Z x Z}
2787
+ sage: S2 = simplicial_sets.Sphere(2)
2788
+ sage: X.wedge(S2).homology(reduced=False) # needs sage.modules
2789
+ {0: Z, 1: Z, 2: Z}
2790
+ sage: X.wedge(X).nondegenerate_simplices()
2791
+ [*, e, e]
2792
+
2793
+ sage: S3 = simplicial_sets.Sphere(3)
2794
+ sage: W = S2.wedge(S3, S2)
2795
+ sage: W.inclusion_map(2)
2796
+ Simplicial set morphism:
2797
+ From: S^2
2798
+ To: Wedge: (S^2 v S^3 v S^2)
2799
+ Defn: [v_0, sigma_2] --> [*, sigma_2]
2800
+ sage: W.projection_map(1)
2801
+ Simplicial set morphism:
2802
+ From: Wedge: (S^2 v S^3 v S^2)
2803
+ To: Quotient: (Wedge: (S^2 v S^3 v S^2)/Simplicial set with 3 non-degenerate simplices)
2804
+ Defn: [*, sigma_2, sigma_2, sigma_3] --> [*, s_1 s_0 *, s_1 s_0 *, sigma_3]
2805
+
2806
+ Note that the codomain of the projection map is not identical
2807
+ to the original ``S2``, but is instead a quotient of the wedge
2808
+ which is isomorphic to ``S2``::
2809
+
2810
+ sage: S2.f_vector()
2811
+ [1, 0, 1]
2812
+ sage: W.projection_map(2).codomain().f_vector()
2813
+ [1, 0, 1]
2814
+ sage: (W.projection_map(2) * W.inclusion_map(2)).is_bijective()
2815
+ True
2816
+
2817
+ TESTS::
2818
+
2819
+ sage: Z = SimplicialSet({e: (v,w)})
2820
+ sage: X.wedge(Z)
2821
+ Traceback (most recent call last):
2822
+ ...
2823
+ ValueError: the simplicial sets must be pointed
2824
+ """
2825
+ from .simplicial_set_constructions import WedgeOfSimplicialSets, \
2826
+ WedgeOfSimplicialSets_finite
2827
+ if all(space.is_finite() for space in [self] + list(others)):
2828
+ return WedgeOfSimplicialSets_finite((self,) + others)
2829
+ else:
2830
+ return WedgeOfSimplicialSets((self,) + others)
2831
+
2832
+ def cone(self):
2833
+ r"""
2834
+ Return the (reduced) cone on this simplicial set.
2835
+
2836
+ If this simplicial set `X` is not pointed, construct the
2837
+ ordinary cone: add a point `v` (which will become the base
2838
+ point) and for each simplex `\sigma` in `X`, add both `\sigma`
2839
+ and a simplex made up of `v` and `\sigma` (topologically, form
2840
+ the join of `v` and `\sigma`).
2841
+
2842
+ If this simplicial set is pointed, then construct the reduced
2843
+ cone: take the quotient of the unreduced cone by the 1-simplex
2844
+ connecting the old base point to the new one.
2845
+
2846
+ In either case, as long as the simplicial set is finite, it
2847
+ comes equipped in Sage with a map from it into the cone.
2848
+
2849
+ EXAMPLES::
2850
+
2851
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
2852
+ sage: v = AbstractSimplex(0, name='v')
2853
+ sage: e = AbstractSimplex(1, name='e')
2854
+ sage: X = SimplicialSet({e: (v, v)})
2855
+ sage: CX = X.cone() # unreduced cone, since X not pointed
2856
+ sage: CX.nondegenerate_simplices()
2857
+ [*, v, (v,*), e, (e,*)]
2858
+ sage: CX.base_point()
2859
+ *
2860
+
2861
+ `X` as a subset of the cone, and also the map from `X`, in the
2862
+ unreduced case::
2863
+
2864
+ sage: CX.base_as_subset()
2865
+ Simplicial set with 2 non-degenerate simplices
2866
+ sage: CX.map_from_base()
2867
+ Simplicial set morphism:
2868
+ From: Simplicial set with 2 non-degenerate simplices
2869
+ To: Cone of Simplicial set with 2 non-degenerate simplices
2870
+ Defn: [v, e] --> [v, e]
2871
+
2872
+ In the reduced case, only the map from `X` is available::
2873
+
2874
+ sage: X = X.set_base_point(v)
2875
+ sage: CX = X.cone() # reduced cone
2876
+ sage: CX.nondegenerate_simplices()
2877
+ [*, e, (e,*)]
2878
+ sage: CX.map_from_base()
2879
+ Simplicial set morphism:
2880
+ From: Simplicial set with 2 non-degenerate simplices
2881
+ To: Reduced cone of Simplicial set with 2 non-degenerate simplices
2882
+ Defn: [v, e] --> [*, e]
2883
+ """
2884
+ from .simplicial_set_constructions import \
2885
+ ConeOfSimplicialSet, ConeOfSimplicialSet_finite, \
2886
+ ReducedConeOfSimplicialSet, ReducedConeOfSimplicialSet_finite
2887
+ if self.is_pointed():
2888
+ if self.is_finite():
2889
+ return ReducedConeOfSimplicialSet_finite(self)
2890
+ else:
2891
+ return ReducedConeOfSimplicialSet(self)
2892
+ if self.is_finite():
2893
+ return ConeOfSimplicialSet_finite(self)
2894
+ else:
2895
+ return ConeOfSimplicialSet(self)
2896
+
2897
+ def suspension(self, n=1):
2898
+ """
2899
+ Return the (reduced) `n`-th suspension of this simplicial set.
2900
+
2901
+ INPUT:
2902
+
2903
+ - ``n`` -- integer (default: 1); suspend this many times
2904
+
2905
+ If this simplicial set `X` is not pointed, return the
2906
+ suspension: the quotient `CX/X`, where `CX` is the (ordinary,
2907
+ unreduced) cone on `X`. If `X` is pointed, then use the
2908
+ reduced cone instead, and so return the reduced suspension.
2909
+
2910
+ EXAMPLES::
2911
+
2912
+ sage: # needs sage.groups
2913
+ sage: RP4 = simplicial_sets.RealProjectiveSpace(4)
2914
+ sage: S1 = simplicial_sets.Sphere(1)
2915
+ sage: SigmaRP4 = RP4.suspension()
2916
+ sage: S1_smash_RP4 = S1.smash_product(RP4)
2917
+ sage: SigmaRP4.homology() == S1_smash_RP4.homology()
2918
+ True
2919
+
2920
+ The version of the suspension obtained by the smash product is
2921
+ typically less efficient than the reduced suspension produced
2922
+ here::
2923
+
2924
+ sage: SigmaRP4.f_vector() # needs sage.groups
2925
+ [1, 0, 1, 1, 1, 1]
2926
+ sage: S1_smash_RP4.f_vector() # needs sage.groups
2927
+ [1, 1, 4, 6, 8, 5]
2928
+
2929
+ TESTS::
2930
+
2931
+ sage: RP4.suspension(-3) # needs sage.groups
2932
+ Traceback (most recent call last):
2933
+ ...
2934
+ ValueError: n must be nonnegative
2935
+ """
2936
+ from .simplicial_set_constructions import \
2937
+ SuspensionOfSimplicialSet, SuspensionOfSimplicialSet_finite
2938
+ if n < 0:
2939
+ raise ValueError('n must be nonnegative')
2940
+ if n == 0:
2941
+ return self
2942
+ if self.is_finite():
2943
+ Sigma = SuspensionOfSimplicialSet_finite(self)
2944
+ else:
2945
+ Sigma = SuspensionOfSimplicialSet(self)
2946
+ if n == 1:
2947
+ return Sigma
2948
+ return Sigma.suspension(n-1)
2949
+
2950
+ def join(self, *others):
2951
+ """
2952
+ The join of this simplicial set with ``others``.
2953
+
2954
+ Not implemented. See
2955
+ https://ncatlab.org/nlab/show/join+of+simplicial+sets for a
2956
+ few descriptions, for anyone interested in implementing
2957
+ this. See also P. J. Ehlers and Tim Porter, Joins for
2958
+ (Augmented) Simplicial Sets, Jour. Pure Applied Algebra, 145
2959
+ (2000) 37-44 :arxiv:`9904039`.
2960
+
2961
+ - ``others`` -- one or several simplicial sets
2962
+
2963
+ EXAMPLES::
2964
+
2965
+ sage: K = simplicial_sets.Simplex(2)
2966
+ sage: K.join(K)
2967
+ Traceback (most recent call last):
2968
+ ...
2969
+ NotImplementedError: joins are not implemented for simplicial sets
2970
+ """
2971
+ raise NotImplementedError('joins are not implemented for simplicial sets')
2972
+
2973
+ def reduce(self):
2974
+ """
2975
+ Reduce this simplicial set.
2976
+
2977
+ That is, take the quotient by a spanning tree of the
2978
+ 1-skeleton, so that the resulting simplicial set has only one
2979
+ vertex. This only makes sense if the simplicial set is
2980
+ connected, so raise an error if not. If already reduced,
2981
+ return itself.
2982
+
2983
+ EXAMPLES::
2984
+
2985
+ sage: K = simplicial_sets.Simplex(2)
2986
+ sage: K.is_reduced()
2987
+ False
2988
+ sage: X = K.reduce()
2989
+ sage: X.is_reduced()
2990
+ True
2991
+
2992
+ ``X`` is reduced, so calling ``reduce`` on it again
2993
+ returns ``X`` itself::
2994
+
2995
+ sage: X is X.reduce()
2996
+ True
2997
+ sage: K is K.reduce()
2998
+ False
2999
+
3000
+ Raise an error for disconnected simplicial sets::
3001
+
3002
+ sage: S0 = simplicial_sets.Sphere(0)
3003
+ sage: S0.reduce()
3004
+ Traceback (most recent call last):
3005
+ ...
3006
+ ValueError: this simplicial set is not connected
3007
+ """
3008
+ if self.is_reduced():
3009
+ return self
3010
+ if not self.is_connected():
3011
+ raise ValueError("this simplicial set is not connected")
3012
+ graph = self.graph()
3013
+ spanning_tree = [e[2] for e in graph.min_spanning_tree()]
3014
+ return self.quotient(spanning_tree)
3015
+
3016
+ def _Hom_(self, other, category=None):
3017
+ """
3018
+ Return the set of simplicial maps between simplicial sets
3019
+ ``self`` and ``other``.
3020
+
3021
+ INPUT:
3022
+
3023
+ - ``other`` -- another simplicial set
3024
+ - ``category`` -- (optional) the category in which to compute
3025
+ the maps. By default this is ``SimplicialSets``, and it must
3026
+ be a subcategory of this or else an error is raised.
3027
+
3028
+ EXAMPLES::
3029
+
3030
+ sage: S3 = simplicial_sets.Sphere(3)
3031
+ sage: S2 = simplicial_sets.Sphere(2)
3032
+ sage: S3._Hom_(S2)
3033
+ Set of Morphisms from S^3 to S^2 in Category of finite pointed simplicial sets
3034
+ sage: Hom(S3, S2)
3035
+ Set of Morphisms from S^3 to S^2 in Category of finite pointed simplicial sets
3036
+ sage: K4 = simplicial_sets.Simplex(4)
3037
+ sage: S3._Hom_(K4)
3038
+ Set of Morphisms from S^3 to 4-simplex in Category of finite simplicial sets
3039
+ """
3040
+ # Import this here to prevent circular imports.
3041
+ from sage.topology.simplicial_set_morphism import SimplicialSetHomset
3042
+ # Error-checking on the ``category`` argument is done when
3043
+ # calling Hom(X,Y), so no need to do it again here.
3044
+ if category is None:
3045
+ if self.is_finite() and other.is_finite():
3046
+ if self.is_pointed() and other.is_pointed():
3047
+ category = SimplicialSets().Finite().Pointed()
3048
+ else:
3049
+ category = SimplicialSets().Finite()
3050
+ else:
3051
+ if self.is_pointed() and other.is_pointed():
3052
+ category = SimplicialSets().Pointed()
3053
+ else:
3054
+ category = SimplicialSets()
3055
+ return SimplicialSetHomset(self, other, category=category)
3056
+
3057
+ def rename_latex(self, s):
3058
+ """
3059
+ Rename or set the LaTeX name for this simplicial set.
3060
+
3061
+ INPUT:
3062
+
3063
+ - ``s`` -- string; the LaTeX representation. Or ``s`` can be
3064
+ ``None``, in which case the LaTeX name is unset.
3065
+
3066
+ EXAMPLES::
3067
+
3068
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3069
+ sage: v = AbstractSimplex(0)
3070
+ sage: X = SimplicialSet({v: None}, latex_name='*')
3071
+ sage: latex(X)
3072
+ *
3073
+ sage: X.rename_latex('x_0')
3074
+ sage: latex(X)
3075
+ x_0
3076
+ """
3077
+ self._latex_name = s
3078
+
3079
+ def _latex_(self):
3080
+ r"""
3081
+ LaTeX representation.
3082
+
3083
+ If ``latex_name`` is set when the simplicial set is defined,
3084
+ or if :meth:`rename_latex` is used to set the LaTeX name, use
3085
+ that. Otherwise, use its string representation.
3086
+
3087
+ EXAMPLES::
3088
+
3089
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3090
+ sage: v = AbstractSimplex(0)
3091
+ sage: X = SimplicialSet({v: None}, latex_name='*')
3092
+ sage: latex(X)
3093
+ *
3094
+ sage: X.rename_latex('y_0')
3095
+ sage: latex(X)
3096
+ y_0
3097
+ sage: X.rename_latex(None)
3098
+ sage: latex(X)
3099
+ Simplicial set with 1 non-degenerate simplex
3100
+ sage: X.rename('v')
3101
+ sage: latex(X)
3102
+ v
3103
+ """
3104
+ if hasattr(self, '_latex_name') and self._latex_name is not None:
3105
+ return self._latex_name
3106
+ return str(self)
3107
+
3108
+ def _repr_(self):
3109
+ """
3110
+ Print representation.
3111
+
3112
+ EXAMPLES::
3113
+
3114
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3115
+ sage: v = AbstractSimplex(0)
3116
+ sage: w = AbstractSimplex(0)
3117
+ sage: degen = v.apply_degeneracies(0)
3118
+ sage: tau = AbstractSimplex(2)
3119
+ sage: SimplicialSet({tau: (degen, degen, degen), w: None})
3120
+ Simplicial set with 3 non-degenerate simplices
3121
+ sage: SimplicialSet({w: None})
3122
+ Simplicial set with 1 non-degenerate simplex
3123
+
3124
+ Test names and renaming::
3125
+
3126
+ sage: SimplicialSet({w: None}, name='pt')
3127
+ pt
3128
+ sage: K = SimplicialSet({w: None}, name='pt')
3129
+ sage: K.rename('point')
3130
+ sage: K
3131
+ point
3132
+ """
3133
+ num = len(self.nondegenerate_simplices())
3134
+ if num == 1:
3135
+ return "Simplicial set with 1 non-degenerate simplex"
3136
+ return "Simplicial set with {} non-degenerate simplices".format(num)
3137
+
3138
+
3139
+ class SimplicialSet_finite(SimplicialSet_arbitrary, GenericCellComplex):
3140
+ r"""
3141
+ A finite simplicial set.
3142
+
3143
+ A simplicial set `X` is a collection of sets `X_n`, the
3144
+ *n-simplices*, indexed by the nonnegative integers, together with
3145
+ face maps `d_i` and degeneracy maps `s_j`. A simplex is
3146
+ *degenerate* if it is in the image of some `s_j`, and a simplicial
3147
+ set is *finite* if there are only finitely many non-degenerate
3148
+ simplices.
3149
+
3150
+ INPUT:
3151
+
3152
+ - ``data`` -- the data defining the simplicial set; see below for
3153
+ details
3154
+
3155
+ - ``base_point`` -- (default: ``None``) 0-simplex in this
3156
+ simplicial set, its base point
3157
+
3158
+ - ``name`` -- string (default: ``None``); the name of the
3159
+ simplicial set
3160
+
3161
+ - ``check`` -- boolean (default: ``True``); if ``True``,
3162
+ check the simplicial identity on the face maps when defining the
3163
+ simplicial set
3164
+
3165
+ - ``category`` -- (default: ``None``) the category in
3166
+ which to define this simplicial set. The default is either
3167
+ finite simplicial sets or finite pointed simplicial sets,
3168
+ depending on whether a base point is defined.
3169
+
3170
+ - ``latex_name`` -- string (default: ``None``); the LaTeX
3171
+ representation of the simplicial set
3172
+
3173
+ ``data`` should have one of the following forms: it could be a
3174
+ simplicial complex or `\Delta`-complex, in case it is converted to
3175
+ a simplicial set. Alternatively, it could be a dictionary. The
3176
+ keys are the nondegenerate simplices of the simplicial set, and
3177
+ the value corresponding to a simplex `\sigma` is a tuple listing
3178
+ the faces of `\sigma`. The 0-dimensional simplices may be omitted
3179
+ from ``data`` if they (or their degeneracies) are faces of other
3180
+ simplices; otherwise they must be included with value ``None``.
3181
+
3182
+ See :mod:`.simplicial_set` and the methods for simplicial sets for
3183
+ more information and examples.
3184
+
3185
+ EXAMPLES::
3186
+
3187
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3188
+ sage: u = AbstractSimplex(0, name='u')
3189
+ sage: v = AbstractSimplex(0, name='v')
3190
+ sage: w = AbstractSimplex(0, name='w')
3191
+ sage: e = AbstractSimplex(1, name='e')
3192
+ sage: f = AbstractSimplex(1, name='f')
3193
+
3194
+ In the following simplicial set, ``u`` is an isolated vertex::
3195
+
3196
+ sage: X = SimplicialSet({e: (v,w), f: (w,w), u: None})
3197
+ sage: X
3198
+ Simplicial set with 5 non-degenerate simplices
3199
+ sage: X.rename('X')
3200
+ sage: X
3201
+ X
3202
+ sage: X = SimplicialSet({e: (v,w), f: (w,w), u: None}, name='Y')
3203
+ sage: X
3204
+ Y
3205
+ """
3206
+
3207
+ def __init__(self, data, base_point=None, name=None, check=True,
3208
+ category=None, latex_name=None):
3209
+ r"""
3210
+ TESTS::
3211
+
3212
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3213
+ sage: v = AbstractSimplex(0)
3214
+ sage: e = AbstractSimplex(1)
3215
+ sage: SimplicialSet({e: (v, v, v)})
3216
+ Traceback (most recent call last):
3217
+ ...
3218
+ ValueError: wrong number of faces for simplex in dimension 1
3219
+ sage: SimplicialSet({e: (v,)})
3220
+ Traceback (most recent call last):
3221
+ ...
3222
+ ValueError: wrong number of faces for simplex in dimension 1
3223
+
3224
+ Base points::
3225
+
3226
+ sage: SimplicialSet({e: (v,v)}, base_point=AbstractSimplex(0))
3227
+ Traceback (most recent call last):
3228
+ ...
3229
+ ValueError: the base point is not a simplex in this simplicial set
3230
+ sage: SimplicialSet({e: (v,v)}, base_point=e)
3231
+ Traceback (most recent call last):
3232
+ ...
3233
+ ValueError: the base "point" is not a zero-simplex
3234
+
3235
+ Simplicial identity::
3236
+
3237
+ sage: sigma = AbstractSimplex(2)
3238
+ sage: w = AbstractSimplex(0)
3239
+ sage: K = SimplicialSet({sigma: (v.apply_degeneracies(0),
3240
+ ....: v.apply_degeneracies(0),
3241
+ ....: v.apply_degeneracies(0))})
3242
+ sage: SimplicialSet({sigma: (v.apply_degeneracies(0),
3243
+ ....: v.apply_degeneracies(0),
3244
+ ....: w.apply_degeneracies(0))})
3245
+ Traceback (most recent call last):
3246
+ ...
3247
+ ValueError: simplicial identity d_i d_j = d_{j-1} d_i fails in dimension 2
3248
+
3249
+ Returning a copy of the original::
3250
+
3251
+ sage: v = AbstractSimplex(0)
3252
+ sage: e = AbstractSimplex(1)
3253
+ sage: S1 = SimplicialSet({e: (v, v)})
3254
+ sage: SimplicialSet(S1) == S1
3255
+ False
3256
+
3257
+ Test suites::
3258
+
3259
+ sage: skip = ["_test_pickling", "_test_elements"]
3260
+ sage: TestSuite(S1).run(skip=skip)
3261
+ sage: TestSuite(simplicial_sets.Sphere(5)).run(skip=skip)
3262
+ sage: TestSuite(simplicial_sets.RealProjectiveSpace(6)).run(skip=skip) # needs sage.groups
3263
+ """
3264
+ def face(sigma, i):
3265
+ """
3266
+ Return the i-th face of sigma, a simplex in this simplicial set.
3267
+
3268
+ Once the simplicial set has been fully initialized, use
3269
+ the :meth:`face` method instead.
3270
+ """
3271
+ if sigma.is_nondegenerate():
3272
+ return data[sigma][i]
3273
+ else:
3274
+ underlying = sigma.nondegenerate()
3275
+ J, t = face_degeneracies(i, sigma.degeneracies())
3276
+ if t is None:
3277
+ return underlying.apply_degeneracies(*J)
3278
+ else:
3279
+ return data[underlying][t].apply_degeneracies(*J)
3280
+
3281
+ if isinstance(data, GenericCellComplex):
3282
+ # Construct new data appropriately.
3283
+ if isinstance(data, SimplicialComplex):
3284
+ simplices = {}
3285
+ faces = {}
3286
+ for d in range(data.dimension()+1):
3287
+ old_faces = faces
3288
+ faces = {}
3289
+ for idx, sigma in enumerate(data.n_cells(d)):
3290
+ new_sigma = AbstractSimplex(d)
3291
+ new_sigma.rename(str(tuple(sorted(sigma, key=str))))
3292
+ if d > 0:
3293
+ simplices[new_sigma] = [old_faces[_] for _ in sigma.faces()]
3294
+ else:
3295
+ simplices[new_sigma] = None
3296
+ faces[sigma] = new_sigma
3297
+ data = simplices
3298
+
3299
+ elif isinstance(data, DeltaComplex):
3300
+ simplices = {}
3301
+ current = []
3302
+ for d in range(data.dimension()+1):
3303
+ faces = tuple(current)
3304
+ current = []
3305
+ for idx, sigma in enumerate(data.n_cells(d)):
3306
+ new_sigma = AbstractSimplex(d)
3307
+ # Name: Delta_{d,idx} where d is dimension,
3308
+ # idx is its index in the list of d-simplices.
3309
+ new_sigma.rename('Delta_{{{},{}}}'.format(d, idx))
3310
+ if d > 0:
3311
+ simplices[new_sigma] = [faces[_] for _ in sigma]
3312
+ else:
3313
+ simplices[new_sigma] = None
3314
+ current.append(new_sigma)
3315
+ data = simplices
3316
+ elif isinstance(data, SimplicialSet_finite):
3317
+ data = dict(copy.deepcopy(data._data))
3318
+ else:
3319
+ raise NotImplementedError('I do not know how to convert this '
3320
+ 'to a simplicial set')
3321
+ # Convert each value in data to a tuple, and then convert all
3322
+ # of data to a tuple, so that it is hashable.
3323
+ for x in data:
3324
+ if data[x]:
3325
+ if x.dimension() != len(data[x]) - 1:
3326
+ raise ValueError('wrong number of faces for simplex '
3327
+ 'in dimension {}'.format(x.dimension()))
3328
+ if not all(y.dimension() == x.dimension() - 1 for y in data[x]):
3329
+ raise ValueError('faces of a {}-simplex have the wrong '
3330
+ 'dimension'.format(x.dimension()))
3331
+ data[x] = tuple(data[x])
3332
+
3333
+ # To obtain the non-degenerate simplices, look at both the
3334
+ # keys for data and also the underlying non-degenerate
3335
+ # simplices in its values.
3336
+ simplices = set(data.keys())
3337
+ for t in data.values():
3338
+ if t:
3339
+ simplices.update([_.nondegenerate() for _ in t])
3340
+
3341
+ for x in simplices:
3342
+ if x not in data:
3343
+ # x had better be a vertex.
3344
+ assert x.dimension() == 0
3345
+ data[x] = None
3346
+
3347
+ # Check the simplicial identity d_i d_j = d_{j-1} d_i.
3348
+ if check:
3349
+ for sigma in simplices:
3350
+ d = sigma.dimension()
3351
+ if d >= 2:
3352
+ for j in range(d+1):
3353
+ for i in range(j):
3354
+ if face(face(sigma, j), i) != face(face(sigma, i), j-1):
3355
+ raise ValueError('simplicial identity d_i d_j '
3356
+ '= d_{{j-1}} d_i fails '
3357
+ 'in dimension {}'.format(d))
3358
+
3359
+ # Now define the attributes for an instance of this class.
3360
+ # self._data: a tuple representing the defining data of the
3361
+ # simplicial set.
3362
+ self._data = tuple(data.items())
3363
+ # self._simplices: a sorted tuple of non-degenerate simplices.
3364
+ self._simplices = sorted(simplices)
3365
+ # self._basepoint: the base point, or None.
3366
+ if base_point is not None:
3367
+ if base_point not in simplices:
3368
+ raise ValueError('the base point is not a simplex in '
3369
+ 'this simplicial set')
3370
+ if base_point.dimension() != 0:
3371
+ raise ValueError('the base "point" is not a zero-simplex')
3372
+ self._basepoint = base_point
3373
+ if category is None:
3374
+ if base_point is None:
3375
+ category = SimplicialSets().Finite()
3376
+ else:
3377
+ category = SimplicialSets().Finite().Pointed()
3378
+ Parent.__init__(self, category=category)
3379
+ if name:
3380
+ self.rename(name)
3381
+ self._latex_name = latex_name
3382
+
3383
+ def __eq__(self, other):
3384
+ """
3385
+ Return ``True`` if ``self`` and ``other`` are equal as simplicial sets.
3386
+
3387
+ Two simplicial sets are equal if they have the same defining
3388
+ data. This means that they have *the same* simplices in each
3389
+ dimension, not just that they have the same numbers of
3390
+ `n`-simplices for each `n` with corresponding face maps.
3391
+
3392
+ EXAMPLES::
3393
+
3394
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3395
+ sage: v = AbstractSimplex(0)
3396
+ sage: w = AbstractSimplex(0)
3397
+ sage: e = AbstractSimplex(1)
3398
+ sage: X = SimplicialSet({e: (v, v)})
3399
+ sage: Y = SimplicialSet({e: (w, w)})
3400
+ sage: X == X
3401
+ True
3402
+ sage: X == SimplicialSet({e: (v, v)})
3403
+ True
3404
+ sage: X == Y
3405
+ False
3406
+ """
3407
+ if self.is_pointed():
3408
+ return (isinstance(other, SimplicialSet_finite)
3409
+ and other.is_pointed()
3410
+ and sorted(self._data) == sorted(other._data)
3411
+ and self.base_point() == other.base_point())
3412
+ else:
3413
+ return (isinstance(other, SimplicialSet_finite)
3414
+ and not other.is_pointed()
3415
+ and sorted(self._data) == sorted(other._data))
3416
+
3417
+ def __ne__(self, other):
3418
+ """
3419
+ Return ``True`` if ``self`` and ``other`` are not equal as simplicial sets.
3420
+
3421
+ EXAMPLES::
3422
+
3423
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3424
+ sage: v = AbstractSimplex(0)
3425
+ sage: w = AbstractSimplex(0)
3426
+ sage: X = SimplicialSet({v: None})
3427
+ sage: Y = SimplicialSet({w: None})
3428
+ sage: X != X
3429
+ False
3430
+ sage: X != SimplicialSet({v: None})
3431
+ False
3432
+ sage: X != Y
3433
+ True
3434
+ """
3435
+ return not (self == other)
3436
+
3437
+ # This is cached because it is used frequently in simplicial set
3438
+ # construction: the last two lines in the __init__ method access
3439
+ # dictionaries which use instances of SimplicialSet_finite as keys and so
3440
+ # computes their hash. If the tuple self._data is long, this can
3441
+ # take a long time.
3442
+ @cached_method
3443
+ def __hash__(self):
3444
+ """
3445
+ The hash is formed from that of the tuple ``self._data``.
3446
+
3447
+ EXAMPLES::
3448
+
3449
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3450
+ sage: v = AbstractSimplex(0)
3451
+ sage: X = SimplicialSet({v: None})
3452
+ sage: degen = v.apply_degeneracies(0)
3453
+ sage: tau = AbstractSimplex(2)
3454
+ sage: Y = SimplicialSet({tau: (degen, degen, degen)})
3455
+
3456
+ sage: hash(X) # random
3457
+ 17
3458
+ sage: hash(X) != hash(Y)
3459
+ True
3460
+ """
3461
+ if self.is_pointed():
3462
+ return hash(self._data) ^ hash(self.base_point())
3463
+ else:
3464
+ return hash(self._data)
3465
+
3466
+ def __copy__(self):
3467
+ """
3468
+ Return a distinct copy of this simplicial set.
3469
+
3470
+ The copy will not be equal to the original simplicial set.
3471
+
3472
+ EXAMPLES::
3473
+
3474
+ sage: T = simplicial_sets.Torus()
3475
+ sage: copy(T) == T
3476
+ False
3477
+ sage: T.n_cells(0)[0] == copy(T).n_cells(0)[0]
3478
+ False
3479
+ sage: T.homology() == copy(T).homology() # needs sage.modules
3480
+ True
3481
+ """
3482
+ return SimplicialSet(dict(copy.deepcopy(self._data)))
3483
+
3484
+ def face_data(self):
3485
+ """
3486
+ Return the face-map data -- a dictionary -- defining this simplicial set.
3487
+
3488
+ EXAMPLES::
3489
+
3490
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3491
+ sage: v = AbstractSimplex(0, name='v')
3492
+ sage: w = AbstractSimplex(0, name='w')
3493
+ sage: e = AbstractSimplex(1, name='e')
3494
+ sage: X = SimplicialSet({e: (v, w)})
3495
+ sage: X.face_data()[e]
3496
+ (v, w)
3497
+
3498
+ sage: Y = SimplicialSet({v: None, w: None})
3499
+ sage: v in Y.face_data()
3500
+ True
3501
+ sage: Y.face_data()[v] is None
3502
+ True
3503
+ """
3504
+ return dict(self._data)
3505
+
3506
+ def n_skeleton(self, n):
3507
+ """
3508
+ Return the `n`-skeleton of this simplicial set.
3509
+
3510
+ That is, the subsimplicial set generated by all nondegenerate
3511
+ simplices of dimension at most `n`.
3512
+
3513
+ INPUT:
3514
+
3515
+ - ``n`` -- the dimension
3516
+
3517
+ EXAMPLES::
3518
+
3519
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3520
+ sage: v = AbstractSimplex(0, name='v')
3521
+ sage: w = AbstractSimplex(0, name='w')
3522
+ sage: degen = v.apply_degeneracies(0)
3523
+ sage: tau = AbstractSimplex(2, name='tau')
3524
+ sage: Y = SimplicialSet({tau: (degen, degen, degen), w: None})
3525
+
3526
+ ``Y`` is the disjoint union of a 2-sphere, with vertex ``v``
3527
+ and non-degenerate 2-simplex ``tau``, and a point ``w``. ::
3528
+
3529
+ sage: Y.nondegenerate_simplices()
3530
+ [v, w, tau]
3531
+ sage: Y.n_skeleton(1).nondegenerate_simplices()
3532
+ [v, w]
3533
+ sage: Y.n_skeleton(2).nondegenerate_simplices()
3534
+ [v, w, tau]
3535
+ """
3536
+ data = [x for x in self.nondegenerate_simplices()
3537
+ if x.dimension() <= n]
3538
+ return self.subsimplicial_set(data)
3539
+
3540
+ def _facets_(self):
3541
+ r"""
3542
+ Return the list of facets of this simplicial set, where by
3543
+ "facet" we mean a non-degenerate simplex which is not a face
3544
+ of another non-degenerate simplex.
3545
+
3546
+ EXAMPLES::
3547
+
3548
+ sage: T = simplicial_sets.Torus()
3549
+ sage: T._facets_()
3550
+ [(s_0 sigma_1, s_1 sigma_1),
3551
+ (s_1 sigma_1, s_0 sigma_1)]
3552
+ sage: S5 = simplicial_sets.Sphere(5)
3553
+ sage: S5._facets_()
3554
+ [sigma_5]
3555
+ sage: simplicial_sets.Sphere(0)._facets_()
3556
+ [v_0, w_0]
3557
+ """
3558
+ faces = set()
3559
+ for dim in range(self.dimension(), 0, -1):
3560
+ for sigma in self.n_cells(dim):
3561
+ faces.update([tau.nondegenerate() for tau in self.faces(sigma)])
3562
+ return sorted(set(self.nondegenerate_simplices()).difference(faces))
3563
+
3564
+ def f_vector(self):
3565
+ """
3566
+ Return the list of the number of non-degenerate simplices in each
3567
+ dimension.
3568
+
3569
+ Unlike for some other cell complexes in Sage, this does not
3570
+ include the empty simplex in dimension `-1`; thus its `i`-th
3571
+ entry is the number of `i`-dimensional simplices.
3572
+
3573
+ EXAMPLES::
3574
+
3575
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3576
+ sage: v = AbstractSimplex(0)
3577
+ sage: w = AbstractSimplex(0)
3578
+ sage: S0 = SimplicialSet({v: None, w: None})
3579
+ sage: S0.f_vector()
3580
+ [2]
3581
+
3582
+ sage: e = AbstractSimplex(1)
3583
+ sage: S1 = SimplicialSet({e: (v, v)})
3584
+ sage: S1.f_vector()
3585
+ [1, 1]
3586
+ sage: simplicial_sets.Sphere(3).f_vector()
3587
+ [1, 0, 0, 1]
3588
+ """
3589
+ return [len(self.n_cells(_)) for _ in range(self.dimension()+1)]
3590
+
3591
+ def euler_characteristic(self):
3592
+ r"""
3593
+ Return the Euler characteristic of this simplicial set: the
3594
+ alternating sum over `n \geq 0` of the number of
3595
+ nondegenerate `n`-simplices.
3596
+
3597
+ EXAMPLES::
3598
+
3599
+ sage: simplicial_sets.RealProjectiveSpace(4).euler_characteristic() # needs sage.groups
3600
+ 1
3601
+ sage: simplicial_sets.Sphere(6).euler_characteristic()
3602
+ 2
3603
+ sage: simplicial_sets.KleinBottle().euler_characteristic()
3604
+ 0
3605
+ """
3606
+ return sum([(-1)**n * num for (n, num) in enumerate(self.f_vector())])
3607
+
3608
+ def chain_complex(self, dimensions=None, base_ring=ZZ, augmented=False,
3609
+ cochain=False, verbose=False, subcomplex=None,
3610
+ check=False):
3611
+ r"""
3612
+ Return the normalized chain complex.
3613
+
3614
+ INPUT:
3615
+
3616
+ - ``dimensions`` -- if ``None``, compute the chain complex in all
3617
+ dimensions. If a list or tuple of integers, compute the
3618
+ chain complex in those dimensions, setting the chain groups
3619
+ in all other dimensions to zero.
3620
+
3621
+ - ``base_ring`` -- commutative ring (default: `\ZZ`)
3622
+
3623
+ - ``augmented`` -- boolean (default: ``False``); if ``True``,
3624
+ return the augmented chain complex (that is, include a class
3625
+ in dimension `-1` corresponding to the empty cell).
3626
+
3627
+ - ``cochain`` -- boolean (default: ``False``); if ``True``,
3628
+ return the cochain complex (that is, the dual of the chain
3629
+ complex).
3630
+
3631
+ - ``verbose`` -- boolean (default: ``False``); ignored
3632
+
3633
+ - ``subcomplex`` -- (default: ``None``) if present,
3634
+ compute the chain complex relative to this subcomplex
3635
+
3636
+ - ``check`` -- boolean (default: ``False``); if ``True``, make
3637
+ sure that the chain complex is actually a chain complex:
3638
+ the differentials are composable and their product is zero.
3639
+
3640
+ The normalized chain complex of a simplicial set is isomorphic
3641
+ to the chain complex obtained by modding out by degenerate
3642
+ simplices, and the latter is what is actually constructed
3643
+ here.
3644
+
3645
+ EXAMPLES::
3646
+
3647
+ sage: # needs sage.modules
3648
+ sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
3649
+ sage: v = AbstractSimplex(0)
3650
+ sage: degen = v.apply_degeneracies(1, 0) # s_1 s_0 applied to v
3651
+ sage: sigma = AbstractSimplex(3)
3652
+ sage: S3 = SimplicialSet({sigma: (degen, degen, degen, degen)}) # the 3-sphere
3653
+ sage: S3.chain_complex().homology()
3654
+ {0: Z, 3: Z}
3655
+ sage: S3.chain_complex(augmented=True).homology()
3656
+ {-1: 0, 0: 0, 3: Z}
3657
+ sage: S3.chain_complex(dimensions=range(3), base_ring=QQ).homology()
3658
+ {0: Vector space of dimension 1 over Rational Field}
3659
+
3660
+ sage: RP5 = simplicial_sets.RealProjectiveSpace(5) # needs sage.groups
3661
+ sage: RP2 = RP5.n_skeleton(2) # needs sage.groups
3662
+ sage: RP5.chain_complex(subcomplex=RP2).homology() # needs sage.groups sage.modules
3663
+ {0: Z, 3: C2, 4: 0, 5: Z}
3664
+
3665
+ TESTS:
3666
+
3667
+ Convert some simplicial complexes and `\Delta`-complexes to
3668
+ simplicial sets, and compare homology calculations::
3669
+
3670
+ sage: # needs sage.modules
3671
+ sage: T = simplicial_complexes.Torus()
3672
+ sage: T.homology() == SimplicialSet(T).homology()
3673
+ True
3674
+ sage: RP2 = delta_complexes.RealProjectivePlane()
3675
+ sage: RP2.homology() == SimplicialSet(RP2).homology()
3676
+ True
3677
+ sage: cohoRP2 = RP2.cohomology(base_ring=GF(2))
3678
+ sage: cohoRP2 == SimplicialSet(RP2).cohomology(base_ring=GF(2))
3679
+ True
3680
+ """
3681
+ from sage.homology.chain_complex import ChainComplex
3682
+
3683
+ if dimensions is None:
3684
+ if not self.cells(): # Empty
3685
+ if cochain:
3686
+ return ChainComplex({-1: matrix(base_ring, 0, 0)},
3687
+ degree_of_differential=1)
3688
+ return ChainComplex({0: matrix(base_ring, 0, 0)},
3689
+ degree_of_differential=-1)
3690
+ dimensions = list(range(self.dimension() + 1))
3691
+ else:
3692
+ if not isinstance(dimensions, (list, tuple, range)):
3693
+ dimensions = list(range(dimensions - 1, dimensions + 2))
3694
+ else:
3695
+ dimensions = [n for n in dimensions if n >= 0]
3696
+ if not dimensions:
3697
+ # Return the empty chain complex.
3698
+ if cochain:
3699
+ return ChainComplex(base_ring=base_ring, degree=1)
3700
+ else:
3701
+ return ChainComplex(base_ring=base_ring, degree=-1)
3702
+
3703
+ differentials = {}
3704
+ # Convert the tuple self._data to a dictionary indexed by the
3705
+ # non-degenerate simplices.
3706
+ if subcomplex:
3707
+ X = self.quotient(subcomplex)
3708
+ face_data = X.face_data()
3709
+ nondegens = X.nondegenerate_simplices()
3710
+ else:
3711
+ face_data = self.face_data()
3712
+ nondegens = self.nondegenerate_simplices()
3713
+ # simplices: dictionary indexed by dimension, values the list
3714
+ # of non-degenerate simplices in that dimension.
3715
+ simplices = {}
3716
+ for sigma in nondegens:
3717
+ if sigma.dimension() in simplices:
3718
+ simplices[sigma.dimension()].append(sigma)
3719
+ else:
3720
+ simplices[sigma.dimension()] = [sigma]
3721
+ first = dimensions.pop(0)
3722
+ if first in simplices:
3723
+ rank = len(simplices[first])
3724
+ current = sorted(simplices[first])
3725
+ else:
3726
+ rank = 0
3727
+ current = []
3728
+ if augmented and first == 0:
3729
+ differentials[first-1] = matrix(base_ring, 0, 1)
3730
+ differentials[first] = matrix(base_ring, 1, rank,
3731
+ [1] * rank)
3732
+ else:
3733
+ differentials[first] = matrix(base_ring, 0, rank)
3734
+
3735
+ for d in dimensions:
3736
+ old_rank = rank
3737
+ faces = {_[1]: _[0] for _ in enumerate(current)}
3738
+ if d in simplices:
3739
+ current = sorted(simplices[d])
3740
+ rank = len(current)
3741
+ # old_rank: number of simplices in dimension d-1.
3742
+ # faces: list of simplices in dimension d-1.
3743
+ # rank: number of simplices in dimension d.
3744
+ # current: list of simplices in dimension d.
3745
+ if not faces:
3746
+ differentials[d] = matrix(base_ring, old_rank, rank)
3747
+ else:
3748
+ matrix_data = {}
3749
+ for col, sigma in enumerate(current):
3750
+ sign = 1
3751
+ for tau in face_data[sigma]:
3752
+ if tau.is_nondegenerate():
3753
+ row = faces[tau]
3754
+ if (row, col) in matrix_data:
3755
+ matrix_data[(row, col)] += sign
3756
+ else:
3757
+ matrix_data[(row, col)] = sign
3758
+ sign *= -1
3759
+
3760
+ differentials[d] = matrix(base_ring, old_rank,
3761
+ rank, matrix_data)
3762
+
3763
+ else:
3764
+ rank = 0
3765
+ current = []
3766
+ differentials[d] = matrix(base_ring, old_rank, rank)
3767
+
3768
+ if cochain:
3769
+ new_diffs = {}
3770
+ for d in differentials:
3771
+ new_diffs[d-1] = differentials[d].transpose()
3772
+ return ChainComplex(new_diffs, degree_of_differential=1,
3773
+ check=check)
3774
+ return ChainComplex(differentials, degree_of_differential=-1,
3775
+ check=check)
3776
+
3777
+ @cached_method
3778
+ def algebraic_topological_model(self, base_ring=None):
3779
+ r"""
3780
+ Return the algebraic topological model for this simplicial set
3781
+ with coefficients in ``base_ring``.
3782
+
3783
+ The term "algebraic topological model" is defined by Pilarczyk
3784
+ and Réal [PR2015]_.
3785
+
3786
+ INPUT:
3787
+
3788
+ - ``base_ring`` -- coefficient ring (default: ``QQ``); must be a field
3789
+
3790
+ Denote by `C` the chain complex associated to this simplicial
3791
+ set. The algebraic topological model is a chain complex `M`
3792
+ with zero differential, with the same homology as `C`, along
3793
+ with chain maps `\pi: C \to M` and `\iota: M \to C` satisfying
3794
+ `\iota \pi = 1_M` and `\pi \iota` chain homotopic to
3795
+ `1_C`. The chain homotopy `\phi` must satisfy
3796
+
3797
+ - `\phi \phi = 0`,
3798
+ - `\pi \phi = 0`,
3799
+ - `\phi \iota = 0`.
3800
+
3801
+ Such a chain homotopy is called a *chain contraction*.
3802
+
3803
+ OUTPUT: a pair consisting of
3804
+
3805
+ - chain contraction ``phi`` associated to `C`, `M`, `\pi`, and
3806
+ `\iota`
3807
+ - the chain complex `M`
3808
+
3809
+ Note that from the chain contraction ``phi``, one can recover the
3810
+ chain maps `\pi` and `\iota` via ``phi.pi()`` and
3811
+ ``phi.iota()``. Then one can recover `C` and `M` from, for
3812
+ example, ``phi.pi().domain()`` and ``phi.pi().codomain()``,
3813
+ respectively.
3814
+
3815
+ EXAMPLES::
3816
+
3817
+ sage: RP2 = simplicial_sets.RealProjectiveSpace(2) # needs sage.groups
3818
+ sage: phi, M = RP2.algebraic_topological_model(GF(2)) # needs sage.groups
3819
+ sage: M.homology() # needs sage.groups sage.modules
3820
+ {0: Vector space of dimension 1 over Finite Field of size 2,
3821
+ 1: Vector space of dimension 1 over Finite Field of size 2,
3822
+ 2: Vector space of dimension 1 over Finite Field of size 2}
3823
+
3824
+ sage: T = simplicial_sets.Torus()
3825
+ sage: phi, M = T.algebraic_topological_model(QQ) # needs sage.modules
3826
+ sage: M.homology() # needs sage.modules
3827
+ {0: Vector space of dimension 1 over Rational Field,
3828
+ 1: Vector space of dimension 2 over Rational Field,
3829
+ 2: Vector space of dimension 1 over Rational Field}
3830
+ """
3831
+ from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex
3832
+
3833
+ if base_ring is None:
3834
+ base_ring = QQ
3835
+ return algebraic_topological_model_delta_complex(self, base_ring)
3836
+
3837
+
3838
+ # TODO: possibly turn SimplicialSet into a function, for example
3839
+ # allowing for the construction of infinite simplicial sets.
3840
+ SimplicialSet = SimplicialSet_finite
3841
+
3842
+
3843
+ ########################################################################
3844
+ # Functions for manipulating face and degeneracy maps.
3845
+
3846
+ def standardize_degeneracies(*L):
3847
+ r"""
3848
+ Return list of indices of degeneracy maps in standard (decreasing)
3849
+ order.
3850
+
3851
+ INPUT:
3852
+
3853
+ - ``L`` -- list of integers representing a composition of
3854
+ degeneracies in a simplicial set
3855
+
3856
+ OUTPUT:
3857
+
3858
+ an equivalent list of degeneracies, standardized to be
3859
+ written in decreasing order, using the simplicial identity
3860
+
3861
+ .. MATH::
3862
+
3863
+ s_i s_j = s_{j+1} s_i \ \ \text{if } i \leq j.
3864
+
3865
+ For example, `s_0 s_2 = s_3 s_0` and `s_0 s_0 = s_1 s_0`.
3866
+
3867
+ EXAMPLES::
3868
+
3869
+ sage: from sage.topology.simplicial_set import standardize_degeneracies
3870
+ sage: standardize_degeneracies(0, 0)
3871
+ (1, 0)
3872
+ sage: standardize_degeneracies(0, 0, 0, 0)
3873
+ (3, 2, 1, 0)
3874
+ sage: standardize_degeneracies(1, 2)
3875
+ (3, 1)
3876
+
3877
+ TESTS::
3878
+
3879
+ sage: standardize_degeneracies()
3880
+ ()
3881
+ sage: standardize_degeneracies(2, -1)
3882
+ Traceback (most recent call last):
3883
+ ...
3884
+ ValueError: degeneracies are indexed by nonnegative integers
3885
+ sage: standardize_degeneracies([2, 1])
3886
+ Traceback (most recent call last):
3887
+ ...
3888
+ TypeError: degeneracies are indexed by nonnegative integers; do not use an explicit list or tuple
3889
+ """
3890
+ J = list(L)
3891
+ for m in J:
3892
+ try:
3893
+ if Integer(m) < 0:
3894
+ raise ValueError('degeneracies are indexed by nonnegative integers')
3895
+ except TypeError:
3896
+ # Likely if called via standard_degeneracies([1,2,3])
3897
+ # rather than standard_degeneracies(1,2,3).
3898
+ raise TypeError('degeneracies are indexed by nonnegative integers; do not use an explicit list or tuple')
3899
+ inadmissible = True
3900
+ while inadmissible:
3901
+ inadmissible = False
3902
+ for idx in range(len(J)-1):
3903
+ if J[idx] <= J[idx + 1]:
3904
+ inadmissible = True
3905
+ tmp = J[idx]
3906
+ J[idx] = J[idx + 1] + 1
3907
+ J[idx + 1] = tmp
3908
+ return tuple(J)
3909
+
3910
+
3911
+ def all_degeneracies(n, l=1):
3912
+ r"""
3913
+ Return list of all composites of degeneracies (written in
3914
+ "admissible" form, i.e., as a strictly decreasing sequence) of
3915
+ length `l` on an `n`-simplex.
3916
+
3917
+ INPUT:
3918
+
3919
+ - ``n``, ``l`` -- integers
3920
+
3921
+ On an `n`-simplex, one may apply the degeneracies `s_i` for `0
3922
+ \leq i \leq n`. Then on the resulting `n+1`-simplex, one may apply
3923
+ `s_i` for `0 \leq i \leq n+1`, and so on. But one also has to take
3924
+ into account the simplicial identity
3925
+
3926
+ .. MATH::
3927
+
3928
+ s_i s_j = s_{j+1} s_i \ \ \text{if } i \leq j.
3929
+
3930
+ There are `\binom{l+n}{n}` such composites: each non-degenerate
3931
+ `n`-simplex leads to `\binom{l+n}{n}` degenerate `l+n` simplices.
3932
+
3933
+ EXAMPLES::
3934
+
3935
+ sage: from sage.topology.simplicial_set import all_degeneracies
3936
+ sage: all_degeneracies(0, 3)
3937
+ {(2, 1, 0)}
3938
+ sage: all_degeneracies(1, 1)
3939
+ {(0,), (1,)}
3940
+ sage: all_degeneracies(1, 3)
3941
+ {(2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1)}
3942
+ """
3943
+ if l == 0:
3944
+ return set()
3945
+ if l == 1:
3946
+ return {(_,) for _ in range(n+1)}
3947
+ ans = set()
3948
+ for i in range(n+l):
3949
+ ans.update({tuple(standardize_degeneracies(*([i] + list(_))))
3950
+ for _ in all_degeneracies(n, l-1)})
3951
+ return ans
3952
+
3953
+
3954
+ def standardize_face_maps(*L):
3955
+ r"""
3956
+ Return list of indices of face maps in standard (non-increasing)
3957
+ order.
3958
+
3959
+ INPUT:
3960
+
3961
+ - ``L`` -- list of integers representing a composition of
3962
+ face maps in a simplicial set
3963
+
3964
+ OUTPUT:
3965
+
3966
+ an equivalent list of face maps, standardized to be
3967
+ written in non-increasing order, using the simplicial identity
3968
+
3969
+ .. MATH::
3970
+
3971
+ d_i d_j = d_{j-1} d_i \ \ \text{if } i<j.
3972
+
3973
+ For example, `d_0 d_2 = d_1 d_0` and `d_0 d_1 = d_0 d_0`.
3974
+
3975
+ EXAMPLES::
3976
+
3977
+ sage: from sage.topology.simplicial_set import standardize_face_maps
3978
+ sage: standardize_face_maps(0, 1)
3979
+ (0, 0)
3980
+ sage: standardize_face_maps(0, 2)
3981
+ (1, 0)
3982
+ sage: standardize_face_maps(1, 3, 5)
3983
+ (3, 2, 1)
3984
+ """
3985
+ J = list(L)
3986
+ for m in J:
3987
+ if Integer(m) < 0:
3988
+ raise ValueError('faces are indexed by nonnegative integers')
3989
+ inadmissible = True
3990
+ while inadmissible:
3991
+ inadmissible = False
3992
+ for idx in range(len(J)-1):
3993
+ if J[idx] < J[idx + 1]:
3994
+ inadmissible = True
3995
+ tmp = J[idx]
3996
+ J[idx] = J[idx + 1] - 1
3997
+ J[idx + 1] = tmp
3998
+ return tuple(J)
3999
+
4000
+
4001
+ def face_degeneracies(m, I):
4002
+ r"""
4003
+ Return the result of applying the face map `d_m` to the iterated
4004
+ degeneracy `s_I = s_{i_1} s_{i_2} ... s_{i_n}`.
4005
+
4006
+ INPUT:
4007
+
4008
+ - ``m`` -- integer
4009
+ - ``I`` -- tuple ``(i_1, i_2, ..., i_n)`` of integers; we assume
4010
+ that this sequence is strictly decreasing
4011
+
4012
+ Using the simplicial identities (see :mod:`.simplicial_set`), we
4013
+ can rewrite
4014
+
4015
+ .. MATH::
4016
+
4017
+ d_m s_{i_1} s_{i_2} ... s_{i_n}
4018
+
4019
+ in one of the forms
4020
+
4021
+ .. MATH::
4022
+
4023
+ s_{j_1} s_{j_2} ... s_{j_n} d_t, \quad
4024
+ s_{j_1} s_{j_2} ... s_{j_{n-1}}.
4025
+
4026
+ OUTPUT: the pair ``(J, t)`` or ``(J, None)``; ``J`` is returned as
4027
+ a list
4028
+
4029
+ EXAMPLES::
4030
+
4031
+ sage: from sage.topology.simplicial_set import face_degeneracies
4032
+ sage: face_degeneracies(0, (1, 0))
4033
+ ([0], None)
4034
+ sage: face_degeneracies(1, (1, 0))
4035
+ ([0], None)
4036
+ sage: face_degeneracies(2, (1, 0))
4037
+ ([0], None)
4038
+ sage: face_degeneracies(3, (1, 0))
4039
+ ([1, 0], 1)
4040
+ sage: face_degeneracies(3, ())
4041
+ ([], 3)
4042
+ """
4043
+ if not I:
4044
+ return ([], m)
4045
+ J = []
4046
+ t = m
4047
+ for i in I:
4048
+ if t is None:
4049
+ J.append(i)
4050
+ elif t < i:
4051
+ J.append(i-1)
4052
+ elif t == i or t == i+1:
4053
+ t = None
4054
+ else:
4055
+ J.append(i)
4056
+ t -= 1
4057
+ return (J, t)
4058
+
4059
+
4060
+ ########################################################################
4061
+
4062
+ def shrink_simplicial_complex(K):
4063
+ """
4064
+ Convert the simplicial complex ``K`` to a "small" simplicial set.
4065
+
4066
+ First convert ``K`` naively, then mod out by a large contractible
4067
+ subcomplex, as found by
4068
+ :meth:`.simplicial_complex.SimplicialComplex._contractible_subcomplex`.
4069
+ This will produce a simplicial set no larger than, and sometimes
4070
+ much smaller than, the initial simplicial complex.
4071
+
4072
+ EXAMPLES::
4073
+
4074
+ sage: from sage.topology.simplicial_set import shrink_simplicial_complex
4075
+ sage: K = simplicial_complexes.Simplex(3)
4076
+ sage: X = shrink_simplicial_complex(K)
4077
+ sage: X.f_vector()
4078
+ [1]
4079
+
4080
+ sage: Y = simplicial_complexes.Sphere(2)
4081
+ sage: S2 = shrink_simplicial_complex(Y); S2
4082
+ Quotient: (Simplicial set with
4083
+ 14 non-degenerate simplices/Simplicial set with
4084
+ 13 non-degenerate simplices)
4085
+ sage: S2.f_vector()
4086
+ [1, 0, 1]
4087
+ sage: S2.homology() # needs sage.modules
4088
+ {0: 0, 1: 0, 2: Z}
4089
+
4090
+ sage: Z = simplicial_complexes.SurfaceOfGenus(3)
4091
+ sage: Z.f_vector()
4092
+ [1, 15, 57, 38]
4093
+ sage: Z.homology() # needs sage.modules
4094
+ {0: 0, 1: Z^6, 2: Z}
4095
+ sage: M = shrink_simplicial_complex(Z)
4096
+ sage: M.f_vector() # random
4097
+ [1, 32, 27]
4098
+ sage: M.homology() # needs sage.modules
4099
+ {0: 0, 1: Z^6, 2: Z}
4100
+ """
4101
+ L = K._contractible_subcomplex()
4102
+ return SimplicialSet_finite(K).quotient(L)