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