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.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- 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)
|