passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,1976 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs
3
+ r"""
4
+ Finite cubical complexes
5
+
6
+ AUTHORS:
7
+
8
+ - John H. Palmieri (2009-08)
9
+
10
+ This module implements the basic structure of finite cubical
11
+ complexes. For full mathematical details, see Kaczynski, Mischaikow,
12
+ and Mrozek [KMM2004]_, for example.
13
+
14
+ Cubical complexes are topological spaces built from gluing together
15
+ cubes of various dimensions; the collection of cubes must be closed
16
+ under taking faces, just as with a simplicial complex. In this
17
+ context, a "cube" means a product of intervals of length 1 or length 0
18
+ (degenerate intervals), with integer endpoints, and its faces are
19
+ obtained by using the nondegenerate intervals: if `C` is a cube -- a
20
+ product of degenerate and nondegenerate intervals -- and if `[i,i+1]`
21
+ is the `k`-th nondegenerate factor, then `C` has two faces indexed by
22
+ `k`: the cubes obtained by replacing `[i, i+1]` with `[i, i]` or
23
+ `[i+1, i+1]`.
24
+
25
+ So to construct a space homeomorphic to a circle as a cubical complex,
26
+ we could take for example the four line segments in the plane from
27
+ `(0,2)` to `(0,3)` to `(1,3)` to `(1,2)` to `(0,2)`. In Sage, this is
28
+ done with the following command::
29
+
30
+ sage: S1 = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]),
31
+ ....: ([0,1], [2,2]), ([1,1], [2,3])]); S1
32
+ Cubical complex with 4 vertices and 8 cubes
33
+
34
+ The argument to ``CubicalComplex`` is a list of the maximal "cubes" in
35
+ the complex. Each "cube" can be an instance of the class ``Cube`` or
36
+ a list (or tuple) of "intervals", and an "interval" is a pair of
37
+ integers, of one of the two forms `[i, i]` or `[i, i+1]`. So the
38
+ cubical complex ``S1`` above has four maximal cubes::
39
+
40
+ sage: len(S1.maximal_cells())
41
+ 4
42
+ sage: sorted(S1.maximal_cells())
43
+ [[0,0] x [2,3], [0,1] x [2,2], [0,1] x [3,3], [1,1] x [2,3]]
44
+
45
+ The first of these, for instance, is the product of the degenerate
46
+ interval `[0,0]` with the unit interval `[2,3]`: this is the line
47
+ segment in the plane from `(0,2)` to `(0,3)`. We could form a
48
+ topologically equivalent space by inserting some degenerate simplices::
49
+
50
+ sage: S1.homology() # needs sage.modules
51
+ {0: 0, 1: Z}
52
+ sage: X = CubicalComplex([([0,0], [2,3], [2]), ([0,1], [3,3], [2]),
53
+ ....: ([0,1], [2,2], [2]), ([1,1], [2,3], [2])])
54
+ sage: X.homology() # needs sage.modules
55
+ {0: 0, 1: Z}
56
+
57
+ Topologically, the cubical complex ``X`` consists of four edges of a
58
+ square in `\RR^3`: the same unit square as ``S1``, but embedded in
59
+ `\RR^3` with `z`-coordinate equal to 2. Thus ``X`` is homeomorphic to
60
+ ``S1`` (in fact, they're "cubically equivalent"), and this is
61
+ reflected in the fact that they have isomorphic homology groups.
62
+
63
+ .. NOTE::
64
+
65
+ This class derives from
66
+ :class:`~sage.homology.cell_complex.GenericCellComplex`, and so
67
+ inherits its methods. Some of those methods are not listed here;
68
+ see the :mod:`Generic Cell Complex <sage.homology.cell_complex>`
69
+ page instead.
70
+ """
71
+ from copy import copy
72
+ from functools import total_ordering
73
+
74
+ from .cell_complex import GenericCellComplex
75
+ from sage.structure.sage_object import SageObject
76
+ from sage.rings.integer import Integer
77
+ from sage.sets.set import Set
78
+ from sage.rings.integer_ring import ZZ
79
+ from sage.rings.rational_field import QQ
80
+ from sage.misc.cachefunc import cached_method
81
+ from sage.misc.lazy_import import lazy_import
82
+
83
+ lazy_import('sage.matrix.constructor', 'matrix')
84
+
85
+
86
+ @total_ordering
87
+ class Cube(SageObject):
88
+ r"""
89
+ Define a cube for use in constructing a cubical complex.
90
+
91
+ "Elementary cubes" are products of intervals with integer
92
+ endpoints, each of which is either a unit interval or a degenerate
93
+ (length 0) interval; for example,
94
+
95
+ .. MATH::
96
+
97
+ [0,1] \times [3,4] \times [2,2] \times [1,2]
98
+
99
+ is a 3-dimensional cube (since one of the intervals is degenerate)
100
+ embedded in `\RR^4`.
101
+
102
+ INPUT:
103
+
104
+ - ``data`` -- list or tuple of terms of the form ``(i,i+1)`` or
105
+ ``(i,i)`` or ``(i,)``; the last two are degenerate intervals
106
+
107
+ OUTPUT: an elementary cube
108
+
109
+ Each cube is stored in a standard form: a tuple of tuples, with a
110
+ nondegenerate interval ``[j,j]`` represented by ``(j,j)``, not
111
+ ``(j,)``. (This is so that for any interval ``I``, ``I[1]`` will
112
+ produce a value, not an :exc:`IndexError`.)
113
+
114
+ EXAMPLES::
115
+
116
+ sage: from sage.topology.cubical_complex import Cube
117
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C
118
+ [1,2] x [5,5] x [6,7] x [-1,0]
119
+ sage: C.dimension() # number of nondegenerate intervals
120
+ 3
121
+ sage: C.nondegenerate_intervals() # indices of these intervals
122
+ [0, 2, 3]
123
+ sage: C.face(1, upper=False)
124
+ [1,2] x [5,5] x [6,6] x [-1,0]
125
+ sage: C.face(1, upper=True)
126
+ [1,2] x [5,5] x [7,7] x [-1,0]
127
+ sage: Cube(()).dimension() # empty cube has dimension -1
128
+ -1
129
+ """
130
+ def __init__(self, data):
131
+ """
132
+ Define a cube for use in constructing a cubical complex.
133
+
134
+ See ``Cube`` for more information.
135
+
136
+ EXAMPLES::
137
+
138
+ sage: from sage.topology.cubical_complex import Cube
139
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C # indirect doctest
140
+ [1,2] x [5,5] x [6,7] x [-1,0]
141
+ sage: C == loads(dumps(C))
142
+ True
143
+ """
144
+ if isinstance(data, Cube):
145
+ data = tuple(data)
146
+ new_data = []
147
+ nondegenerate = []
148
+ i = 0
149
+ for x in data:
150
+ if len(x) == 2:
151
+ try:
152
+ Integer(x[0])
153
+ except TypeError:
154
+ raise ValueError("the interval %s is not of the correct form" % x)
155
+ if x[0] + 1 == x[1]:
156
+ nondegenerate.append(i)
157
+ elif x[0] != x[1]:
158
+ raise ValueError("the interval %s is not of the correct form" % x)
159
+ new_data.append(tuple(x))
160
+ elif len(x) == 1:
161
+ y = tuple(x)
162
+ new_data.append(y+y)
163
+ elif len(x) != 1:
164
+ raise ValueError("the interval %s is not of the correct form" % x)
165
+ i += 1
166
+ self.__tuple = tuple(new_data)
167
+ self.__nondegenerate = nondegenerate
168
+
169
+ def tuple(self):
170
+ """
171
+ The tuple attached to this cube.
172
+
173
+ EXAMPLES::
174
+
175
+ sage: from sage.topology.cubical_complex import Cube
176
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
177
+ sage: C.tuple()
178
+ ((1, 2), (5, 5), (6, 7), (-1, 0))
179
+ """
180
+ return self.__tuple
181
+
182
+ def is_face(self, other):
183
+ """
184
+ Return ``True`` iff this cube is a face of other.
185
+
186
+ EXAMPLES::
187
+
188
+ sage: from sage.topology.cubical_complex import Cube
189
+ sage: C1 = Cube([[1,2], [5,], [6,7], [-1, 0]])
190
+ sage: C2 = Cube([[1,2], [5,], [6,], [-1, 0]])
191
+ sage: C1.is_face(C2)
192
+ False
193
+ sage: C1.is_face(C1)
194
+ True
195
+ sage: C2.is_face(C1)
196
+ True
197
+ """
198
+ def is_subinterval(i1, i2):
199
+ return ((i1[0] == i2[0] and i1[1] == i2[1]) or
200
+ (i1[0] == i2[0] and i1[1] == i2[0]) or
201
+ (i1[0] == i2[1] and i1[1] == i2[1]))
202
+
203
+ t = self.tuple()
204
+ u = other.tuple()
205
+ if len(t) == len(u):
206
+ # these must be equal for self to be a face of other
207
+ return all(is_subinterval(ti, ui) for ti, ui in zip(t, u))
208
+ else:
209
+ return False
210
+
211
+ def _translate(self, vec):
212
+ """
213
+ Translate ``self`` by ``vec``.
214
+
215
+ INPUT:
216
+
217
+ - ``vec`` -- anything which can be converted to a tuple of integers
218
+
219
+ OUTPUT: cube; the translation of ``self`` by ``vec``
220
+
221
+ If ``vec`` is shorter than the list of intervals forming the
222
+ cube, pad with zeroes, and similarly if the cube's defining
223
+ tuple is too short.
224
+
225
+ EXAMPLES::
226
+
227
+ sage: from sage.topology.cubical_complex import Cube
228
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
229
+ sage: C._translate((-12,))
230
+ [-11,-10] x [5,5] x [6,7] x [-1,0]
231
+ sage: C._translate((0, 0, 0, 0, 0, 5))
232
+ [1,2] x [5,5] x [6,7] x [-1,0] x [0,0] x [5,5]
233
+ """
234
+ t = self.__tuple
235
+ embed = max(len(t), len(vec))
236
+ t = t + ((0, 0),) * (embed-len(t))
237
+ vec = tuple(vec) + (0,) * (embed-len(vec))
238
+ new = []
239
+ for (a, b) in zip(t, vec):
240
+ new.append([a[0] + b, a[1] + b])
241
+ return Cube(new)
242
+
243
+ def __getitem__(self, n):
244
+ """
245
+ Return the `n`-th interval in this cube.
246
+
247
+ INPUT:
248
+
249
+ - ``n`` -- integer
250
+
251
+ OUTPUT: tuple representing the `n`-th interval in the cube
252
+
253
+ EXAMPLES::
254
+
255
+ sage: from sage.topology.cubical_complex import Cube
256
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
257
+ sage: C[2]
258
+ (6, 7)
259
+ sage: C[1]
260
+ (5, 5)
261
+ """
262
+ return self.__tuple[n]
263
+
264
+ def __iter__(self):
265
+ """
266
+ Iterator for the intervals of this cube.
267
+
268
+ EXAMPLES::
269
+
270
+ sage: from sage.topology.cubical_complex import Cube
271
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
272
+ sage: [x[0] for x in C]
273
+ [1, 5, 6, -1]
274
+ """
275
+ return iter(self.__tuple)
276
+
277
+ def __add__(self, other):
278
+ """
279
+ Cube obtained by concatenating the underlying tuples of the
280
+ two arguments.
281
+
282
+ INPUT:
283
+
284
+ - ``other`` -- another cube
285
+
286
+ OUTPUT: the product of ``self`` and ``other``, as a Cube
287
+
288
+ EXAMPLES::
289
+
290
+ sage: from sage.topology.cubical_complex import Cube
291
+ sage: C = Cube([[1,2], [3,]])
292
+ sage: D = Cube([[4], [0,1]])
293
+ sage: C.product(D)
294
+ [1,2] x [3,3] x [4,4] x [0,1]
295
+
296
+ You can also use ``__add__`` or ``+`` or ``__mul__`` or ``*``::
297
+
298
+ sage: D * C
299
+ [4,4] x [0,1] x [1,2] x [3,3]
300
+ sage: D + C * C
301
+ [4,4] x [0,1] x [1,2] x [3,3] x [1,2] x [3,3]
302
+ """
303
+ return Cube(self.__tuple + other.__tuple)
304
+
305
+ # the __add__ operation actually produces the product of the two cubes
306
+ __mul__ = __add__
307
+ product = __add__
308
+
309
+ def nondegenerate_intervals(self):
310
+ """
311
+ The list of indices of nondegenerate intervals of this cube.
312
+
313
+ EXAMPLES::
314
+
315
+ sage: from sage.topology.cubical_complex import Cube
316
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
317
+ sage: C.nondegenerate_intervals()
318
+ [0, 2, 3]
319
+ sage: C = Cube([[1,], [5,], [6,], [-1,]])
320
+ sage: C.nondegenerate_intervals()
321
+ []
322
+ """
323
+ return self.__nondegenerate
324
+
325
+ def dimension(self):
326
+ """
327
+ The dimension of this cube: the number of its nondegenerate intervals.
328
+
329
+ EXAMPLES::
330
+
331
+ sage: from sage.topology.cubical_complex import Cube
332
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])
333
+ sage: C.dimension()
334
+ 3
335
+ sage: C = Cube([[1,], [5,], [6,], [-1,]])
336
+ sage: C.dimension()
337
+ 0
338
+ sage: Cube([]).dimension() # empty cube has dimension -1
339
+ -1
340
+ """
341
+ if len(self.__tuple) == 0: # empty cube
342
+ return -1
343
+ return len(self.nondegenerate_intervals())
344
+
345
+ def face(self, n, upper=True):
346
+ """
347
+ The `n`-th primary face of this cube.
348
+
349
+ INPUT:
350
+
351
+ - ``n`` -- integer between 0 and one less than the dimension
352
+ of this cube
353
+ - ``upper`` -- boolean (default=True);if ``True``, return the "upper"
354
+ `n`-th primary face; otherwise, return the "lower" `n`-th primary
355
+ face
356
+
357
+ OUTPUT: the cube obtained by replacing the `n`-th non-degenerate
358
+ interval with either its upper or lower endpoint.
359
+
360
+ EXAMPLES::
361
+
362
+ sage: from sage.topology.cubical_complex import Cube
363
+ sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C
364
+ [1,2] x [5,5] x [6,7] x [-1,0]
365
+ sage: C.face(0)
366
+ [2,2] x [5,5] x [6,7] x [-1,0]
367
+ sage: C.face(0, upper=False)
368
+ [1,1] x [5,5] x [6,7] x [-1,0]
369
+ sage: C.face(1)
370
+ [1,2] x [5,5] x [7,7] x [-1,0]
371
+ sage: C.face(2, upper=False)
372
+ [1,2] x [5,5] x [6,7] x [-1,-1]
373
+ sage: C.face(3)
374
+ Traceback (most recent call last):
375
+ ...
376
+ ValueError: can only compute the n-th face if 0 <= n < dim
377
+ """
378
+ if n < 0 or n >= self.dimension():
379
+ raise ValueError("can only compute the n-th face if 0 <= n < dim")
380
+ idx = self.nondegenerate_intervals()[n]
381
+ t = self.__tuple
382
+ if upper:
383
+ new = t[idx][1]
384
+ else:
385
+ new = t[idx][0]
386
+ return Cube(t[0:idx] + ((new, new),) + t[idx+1:])
387
+
388
+ def faces(self):
389
+ """
390
+ The list of faces (of codimension 1) of this cube.
391
+
392
+ EXAMPLES::
393
+
394
+ sage: from sage.topology.cubical_complex import Cube
395
+ sage: C = Cube([[1,2], [3,4]])
396
+ sage: C.faces()
397
+ [[2,2] x [3,4], [1,2] x [4,4], [1,1] x [3,4], [1,2] x [3,3]]
398
+ """
399
+ upper = [self.face(i, True) for i in range(self.dimension())]
400
+ lower = [self.face(i, False) for i in range(self.dimension())]
401
+ return upper + lower
402
+
403
+ def faces_as_pairs(self):
404
+ """
405
+ The list of faces (of codimension 1) of this cube, as pairs
406
+ (upper, lower).
407
+
408
+ EXAMPLES::
409
+
410
+ sage: from sage.topology.cubical_complex import Cube
411
+ sage: C = Cube([[1,2], [3,4]])
412
+ sage: C.faces_as_pairs()
413
+ [([2,2] x [3,4], [1,1] x [3,4]), ([1,2] x [4,4], [1,2] x [3,3])]
414
+ """
415
+ upper = [self.face(i, True) for i in range(self.dimension())]
416
+ lower = [self.face(i, False) for i in range(self.dimension())]
417
+ return list(zip(upper, lower))
418
+
419
+ def _compare_for_gluing(self, other):
420
+ r"""
421
+ Given two cubes ``self`` and ``other``, describe how to
422
+ transform them so that they become equal.
423
+
424
+ INPUT:
425
+
426
+ - ``other`` -- a cube of the same dimension as ``self``
427
+
428
+ OUTPUT: a triple ``(insert_self, insert_other, translate)``.
429
+ ``insert_self`` is a tuple with entries ``(index, (list of
430
+ degenerate intervals))``. ``insert_other`` is similar.
431
+ ``translate`` is a tuple of integers, suitable as a second
432
+ argument for the ``_translate`` method.
433
+
434
+ To do this, ``self`` and ``other`` must have the same
435
+ dimension; degenerate intervals from ``other`` are added to
436
+ ``self``, and vice versa. Intervals in ``other`` are
437
+ translated so that they coincide with the intervals in
438
+ ``self``. The output is a triple, as noted above: in the
439
+ tuple ``insert_self``, an entry like ``(3, (3, 4, 0))`` means
440
+ that in position 3 in ``self``, insert the degenerate
441
+ intervals ``[3,3]``, ``[4,4]``, and ``[0,0]``. The same goes
442
+ for ``insert_other``. After applying the translations to the
443
+ cube ``other``, call ``_translate`` with argument the tuple
444
+ ``translate``.
445
+
446
+ This is used in forming connected sums of cubical complexes:
447
+ the two complexes are modified, via this method, so that they
448
+ have a cube which matches up, then those matching cubes are
449
+ removed.
450
+
451
+ In the example below, this method is called with arguments
452
+ ``C1`` and ``C2``, where
453
+
454
+ .. MATH::
455
+
456
+ C1 = [0,1] \times [3] \times [4] \times [6,7] \\
457
+ C2 = [2] \times [7,8] \times [9] \times [1,2] \times [0] \times [5]
458
+
459
+ To C1, we need to add [2] in position 0 and [0] and [5] in
460
+ position 5. To C2, we need to add [4] in position 3. Once
461
+ this has been done, we need to translate the new C2 by the
462
+ vector ``(0, -7, -6, 0, 5, 0, 0)``.
463
+
464
+ EXAMPLES::
465
+
466
+ sage: from sage.topology.cubical_complex import Cube
467
+ sage: C1 = Cube([[0,1], [3], [4], [6,7]])
468
+ sage: C2 = Cube([[2], [7,8], [9], [1,2], [0], [5]])
469
+ sage: C1._compare_for_gluing(C2)
470
+ ([(0, ((2, 2),)), (5, ((0, 0), (5, 5)))], [(3, ((4, 4),))], [0, -7, -6, 0, 5, 0, 0])
471
+
472
+ sage: C1 = Cube([[1,1], [0,1]])
473
+ sage: C2 = Cube([[2,3], [4,4], [5,5]])
474
+ sage: C1._compare_for_gluing(C2)
475
+ ([(2, ((4, 4), (5, 5)))], [(0, ((1, 1),))], [0, -2, 0, 0])
476
+ """
477
+ d = self.dimension()
478
+ if d != other.dimension():
479
+ raise ValueError("cubes must be of the same dimension")
480
+ insert_self = []
481
+ insert_other = []
482
+ translate = []
483
+ self_tuple = self.tuple()
484
+ other_tuple = other.tuple()
485
+ nondegen = (list(zip(self.nondegenerate_intervals(),
486
+ other.nondegenerate_intervals()))
487
+ + [(len(self_tuple), len(other_tuple))])
488
+ old = (-1, -1)
489
+ self_added = 0
490
+ other_added = 0
491
+
492
+ for current in nondegen:
493
+ # number of positions between nondegenerate intervals:
494
+ self_diff = current[0] - old[0]
495
+ other_diff = current[1] - old[1]
496
+ diff = self_diff - other_diff
497
+
498
+ if diff < 0:
499
+ insert_self.append((old[0] + self_diff + self_added,
500
+ other.tuple()[current[1]+diff:current[1]]))
501
+ common_terms = self_diff
502
+ diff = -diff
503
+ self_added += diff
504
+ elif diff > 0:
505
+ insert_other.append((old[1] + other_diff + other_added,
506
+ self.tuple()[current[0]-diff:current[0]]))
507
+ common_terms = other_diff
508
+ other_added += diff
509
+ else:
510
+ common_terms = other_diff
511
+
512
+ if old[0] > -1:
513
+ translate.extend([self_tuple[old[0]+idx][0] -
514
+ other_tuple[old[1]+idx][0] for idx in
515
+ range(common_terms)])
516
+ translate.extend(diff*[0])
517
+ old = current
518
+
519
+ return (insert_self, insert_other, translate)
520
+
521
+ def _triangulation_(self):
522
+ r"""
523
+ Triangulate this cube by "pulling vertices," as described by
524
+ Hetyei. Return a list of simplices which triangulate
525
+ ``self``.
526
+
527
+ ALGORITHM:
528
+
529
+ If the cube is given by
530
+
531
+ .. MATH::
532
+
533
+ C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]
534
+
535
+ let `v_1` be the "upper" corner of `C`: `v` is the point
536
+ `(j_1, ..., j_k)`. Choose a coordinate `n` where the interval
537
+ `[i_n, j_n]` is non-degenerate and form `v_2` by replacing
538
+ `j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex
539
+ so defined will be `(i_1, ..., i_k)`. These vertices define a
540
+ simplex, as do the vertices obtained by making different
541
+ choices at each stage. Return the list of such simplices;
542
+ thus if `C` is `n`-dimensional, then it is subdivided into
543
+ `n!` simplices.
544
+
545
+ REFERENCES:
546
+
547
+ - G. Hetyei, "On the Stanley ring of a cubical complex",
548
+ Discrete Comput. Geom. 14 (1995), 305-330.
549
+
550
+ EXAMPLES::
551
+
552
+ sage: from sage.topology.cubical_complex import Cube
553
+ sage: C = Cube([[1,2], [3,4]]); C
554
+ [1,2] x [3,4]
555
+ sage: C._triangulation_()
556
+ [((1, 3), (1, 4), (2, 4)), ((1, 3), (2, 3), (2, 4))]
557
+ sage: C = Cube([[1,2], [3,4], [8,9]])
558
+ sage: len(C._triangulation_())
559
+ 6
560
+ """
561
+ from .simplicial_complex import Simplex
562
+ if self.dimension() < 0: # the empty cube
563
+ return [Simplex(())] # the empty simplex
564
+ v = tuple([max(j) for j in self.tuple()])
565
+ Sv = Simplex((v,))
566
+ if self.dimension() == 0: # just v
567
+ return [Sv]
568
+ return [S.join(Sv, rename_vertices=False)
569
+ for i in range(self.dimension())
570
+ for S in self.face(i, upper=False)._triangulation_()]
571
+
572
+ def alexander_whitney(self, dim):
573
+ r"""
574
+ Subdivide this cube into pairs of cubes.
575
+
576
+ This provides a cubical approximation for the diagonal map
577
+ `K \to K \times K`.
578
+
579
+ INPUT:
580
+
581
+ - ``dim`` -- integer between 0 and one more than the
582
+ dimension of this cube
583
+
584
+ OUTPUT:
585
+
586
+ - a list containing triples ``(coeff, left, right)``
587
+
588
+ This uses the algorithm described by Pilarczyk and Réal [PR2015]_
589
+ on p. 267; the formula is originally due to Serre. Calling
590
+ this method ``alexander_whitney`` is an abuse of notation,
591
+ since the actual Alexander-Whitney map goes from `C(K \times
592
+ L) \to C(K) \otimes C(L)`, where `C(-)` denotes the associated
593
+ chain complex, but this subdivision of cubes is at the heart
594
+ of it.
595
+
596
+ EXAMPLES::
597
+
598
+ sage: from sage.topology.cubical_complex import Cube
599
+ sage: C1 = Cube([[0,1], [3,4]])
600
+ sage: C1.alexander_whitney(0)
601
+ [(1, [0,0] x [3,3], [0,1] x [3,4])]
602
+ sage: C1.alexander_whitney(1)
603
+ [(1, [0,1] x [3,3], [1,1] x [3,4]), (-1, [0,0] x [3,4], [0,1] x [4,4])]
604
+ sage: C1.alexander_whitney(2)
605
+ [(1, [0,1] x [3,4], [1,1] x [4,4])]
606
+ """
607
+ from sage.sets.set import Set
608
+ N = Set(self.nondegenerate_intervals())
609
+ result = []
610
+ for J in N.subsets(dim):
611
+ Jprime = N.difference(J)
612
+ nu = 0
613
+ for i in J:
614
+ for j in Jprime:
615
+ if j < i:
616
+ nu += 1
617
+ t = self.tuple()
618
+ left = []
619
+ right = []
620
+ for j in range(len(t)):
621
+ if j in Jprime:
622
+ left.append((t[j][0], t[j][0]))
623
+ right.append(t[j])
624
+ elif j in J:
625
+ left.append(t[j])
626
+ right.append((t[j][1], t[j][1]))
627
+ else:
628
+ left.append(t[j])
629
+ right.append(t[j])
630
+ result.append(((-1)**nu, Cube(left), Cube(right)))
631
+ return result
632
+
633
+ def __eq__(self, other):
634
+ """
635
+ Return ``True`` iff this cube is the same as ``other``: that is,
636
+ if they are the product of the same intervals in the same
637
+ order.
638
+
639
+ INPUT:
640
+
641
+ - ``other`` -- another cube
642
+
643
+ EXAMPLES::
644
+
645
+ sage: from sage.topology.cubical_complex import Cube
646
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
647
+ sage: C2 = Cube([[1], [2,3], [4,5]])
648
+ sage: C3 = Cube([[0], [2,3], [4,5]])
649
+ sage: C1 == C2 # indirect doctest
650
+ True
651
+ sage: C1 == C3 # indirect doctest
652
+ False
653
+ """
654
+ return tuple(self) == tuple(other)
655
+
656
+ def __ne__(self, other):
657
+ """
658
+ Return ``True`` iff this cube is not equal to ``other``.
659
+
660
+ INPUT:
661
+
662
+ - ``other`` -- another cube
663
+
664
+ EXAMPLES::
665
+
666
+ sage: from sage.topology.cubical_complex import Cube
667
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
668
+ sage: C2 = Cube([[1], [2,3], [4,5]])
669
+ sage: C3 = Cube([[0], [2,3], [4,5]])
670
+ sage: C1 != C2 # indirect doctest
671
+ False
672
+ sage: C1 != C3 # indirect doctest
673
+ True
674
+ """
675
+ return not self == other
676
+
677
+ def __lt__(self, other):
678
+ """
679
+ Return ``True`` iff the tuple for this cube is less than that for
680
+ ``other``.
681
+
682
+ INPUT:
683
+
684
+ - ``other`` -- another cube
685
+
686
+ EXAMPLES::
687
+
688
+ sage: from sage.topology.cubical_complex import Cube
689
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
690
+ sage: C2 = Cube([[1], [2,3], [4,5]])
691
+ sage: C3 = Cube([[0], [2,3], [4,5]])
692
+ sage: C1 < C1
693
+ False
694
+ sage: C1 < C3
695
+ False
696
+ sage: C3 < C1
697
+ True
698
+
699
+ Test ``@total_ordering`` by testing other comparisons::
700
+
701
+ sage: C1 <= C1
702
+ True
703
+ sage: C1 <= C2
704
+ True
705
+ sage: C1 >= C2
706
+ True
707
+ sage: C1 > C2
708
+ False
709
+ sage: C3 <= C1
710
+ True
711
+ sage: C1 > C3
712
+ True
713
+ """
714
+ return tuple(self) < tuple(other)
715
+
716
+ def __hash__(self):
717
+ """
718
+ Hash value for this cube. This computes the hash value of the
719
+ underlying tuple, since this is what's important when testing
720
+ equality.
721
+
722
+ EXAMPLES::
723
+
724
+ sage: from sage.topology.cubical_complex import Cube
725
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
726
+ sage: hash(C1) == hash(((1,1),(2,3),(4,5)))
727
+ True
728
+ """
729
+ return hash(self.__tuple)
730
+
731
+ def _repr_(self):
732
+ """
733
+ Print representation of a cube.
734
+
735
+ EXAMPLES::
736
+
737
+ sage: from sage.topology.cubical_complex import Cube
738
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
739
+ sage: C1
740
+ [1,1] x [2,3] x [4,5]
741
+ sage: C1._repr_()
742
+ '[1,1] x [2,3] x [4,5]'
743
+ """
744
+ s = ("[{},{}]".format(str(x), str(y)) for x, y in self.__tuple)
745
+ return " x ".join(s)
746
+
747
+ def _latex_(self):
748
+ r"""
749
+ LaTeX representation of a cube..
750
+
751
+ EXAMPLES::
752
+
753
+ sage: from sage.topology.cubical_complex import Cube
754
+ sage: C1 = Cube([[1,1], [2,3], [4,5]])
755
+ sage: latex(C1)
756
+ [1,1] \times [2,3] \times [4,5]
757
+ sage: C1._latex_()
758
+ '[1,1] \\times [2,3] \\times [4,5]'
759
+ """
760
+ return self._repr_().replace('x', r'\times')
761
+
762
+
763
+ class CubicalComplex(GenericCellComplex):
764
+ r"""
765
+ Define a cubical complex.
766
+
767
+ INPUT:
768
+
769
+ - ``maximal_faces`` -- set of maximal faces
770
+ - ``maximality_check`` -- boolean (default: ``True``); see below
771
+
772
+ OUTPUT: a cubical complex
773
+
774
+ ``maximal_faces`` should be a list or tuple or set (or anything
775
+ which may be converted to a set) of "cubes": instances of the
776
+ class :class:`Cube`, or lists or tuples suitable for conversion to
777
+ cubes. These cubes are the maximal cubes in the complex.
778
+
779
+ In addition, ``maximal_faces`` may be a cubical complex, in which
780
+ case that complex is returned. Also, ``maximal_faces`` may
781
+ instead be any object which has a ``_cubical_`` method (e.g., a
782
+ simplicial complex); then that method is used to convert the
783
+ object to a cubical complex.
784
+
785
+ If ``maximality_check`` is True, check that each maximal face is,
786
+ in fact, maximal. In this case, when producing the internal
787
+ representation of the cubical complex, omit those that are not.
788
+ It is highly recommended that this be True; various methods for
789
+ this class may fail if faces which are claimed to be maximal are
790
+ in fact not.
791
+
792
+ EXAMPLES:
793
+
794
+ The empty complex, consisting of one cube, the empty cube::
795
+
796
+ sage: CubicalComplex()
797
+ Cubical complex with 0 vertices and 1 cube
798
+
799
+ A "circle" (four edges connecting the vertices (0,2), (0,3),
800
+ (1,2), and (1,3))::
801
+
802
+ sage: S1 = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]),
803
+ ....: ([0,1], [2,2]), ([1,1], [2,3])]); S1
804
+ Cubical complex with 4 vertices and 8 cubes
805
+ sage: S1.homology() # needs sage.modules
806
+ {0: 0, 1: Z}
807
+
808
+ A set of five points and its product with ``S1``::
809
+
810
+ sage: pts = CubicalComplex([([0],), ([3],), ([6],), ([-12],), ([5],)])
811
+ sage: pts
812
+ Cubical complex with 5 vertices and 5 cubes
813
+ sage: pts.homology() # needs sage.modules
814
+ {0: Z x Z x Z x Z}
815
+ sage: X = S1.product(pts); X
816
+ Cubical complex with 20 vertices and 40 cubes
817
+ sage: X.homology() # needs sage.modules
818
+ {0: Z x Z x Z x Z, 1: Z^5}
819
+
820
+ Converting a simplicial complex to a cubical complex::
821
+
822
+ sage: S2 = simplicial_complexes.Sphere(2)
823
+ sage: C2 = CubicalComplex(S2)
824
+ sage: all(C2.homology(n) == S2.homology(n) for n in range(3)) # needs sage.modules
825
+ True
826
+
827
+ You can get the set of maximal cells or a dictionary of all cells::
828
+
829
+ sage: X.maximal_cells() # random: order may depend on the version of Python
830
+ {[0,0] x [2,3] x [-12,-12], [0,1] x [3,3] x [5,5], [0,1] x [2,2] x [3,3], [0,1] x [2,2] x [0,0], [0,1] x [3,3] x [6,6], [1,1] x [2,3] x [0,0], [0,1] x [2,2] x [-12,-12], [0,0] x [2,3] x [6,6], [1,1] x [2,3] x [-12,-12], [1,1] x [2,3] x [5,5], [0,1] x [2,2] x [5,5], [0,1] x [3,3] x [3,3], [1,1] x [2,3] x [3,3], [0,0] x [2,3] x [5,5], [0,1] x [3,3] x [0,0], [1,1] x [2,3] x [6,6], [0,1] x [2,2] x [6,6], [0,0] x [2,3] x [0,0], [0,0] x [2,3] x [3,3], [0,1] x [3,3] x [-12,-12]}
831
+ sage: sorted(X.maximal_cells())
832
+ [[0,0] x [2,3] x [-12,-12],
833
+ [0,0] x [2,3] x [0,0],
834
+ [0,0] x [2,3] x [3,3],
835
+ [0,0] x [2,3] x [5,5],
836
+ [0,0] x [2,3] x [6,6],
837
+ [0,1] x [2,2] x [-12,-12],
838
+ [0,1] x [2,2] x [0,0],
839
+ [0,1] x [2,2] x [3,3],
840
+ [0,1] x [2,2] x [5,5],
841
+ [0,1] x [2,2] x [6,6],
842
+ [0,1] x [3,3] x [-12,-12],
843
+ [0,1] x [3,3] x [0,0],
844
+ [0,1] x [3,3] x [3,3],
845
+ [0,1] x [3,3] x [5,5],
846
+ [0,1] x [3,3] x [6,6],
847
+ [1,1] x [2,3] x [-12,-12],
848
+ [1,1] x [2,3] x [0,0],
849
+ [1,1] x [2,3] x [3,3],
850
+ [1,1] x [2,3] x [5,5],
851
+ [1,1] x [2,3] x [6,6]]
852
+ sage: S1.cells()
853
+ {-1: set(),
854
+ 0: {[0,0] x [2,2], [0,0] x [3,3], [1,1] x [2,2], [1,1] x [3,3]},
855
+ 1: {[0,0] x [2,3], [0,1] x [2,2], [0,1] x [3,3], [1,1] x [2,3]}}
856
+
857
+ Chain complexes, homology, and cohomology::
858
+
859
+ sage: T = S1.product(S1); T
860
+ Cubical complex with 16 vertices and 64 cubes
861
+ sage: T.chain_complex() # needs sage.modules
862
+ Chain complex with at most 3 nonzero terms over Integer Ring
863
+ sage: T.homology(base_ring=QQ) # needs sage.modules
864
+ {0: Vector space of dimension 0 over Rational Field,
865
+ 1: Vector space of dimension 2 over Rational Field,
866
+ 2: Vector space of dimension 1 over Rational Field}
867
+ sage: RP2 = cubical_complexes.RealProjectivePlane()
868
+ sage: RP2.cohomology(dim=[1, 2], base_ring=GF(2)) # needs sage.modules
869
+ {1: Vector space of dimension 1 over Finite Field of size 2,
870
+ 2: Vector space of dimension 1 over Finite Field of size 2}
871
+
872
+ Joins are not implemented::
873
+
874
+ sage: S1.join(S1)
875
+ Traceback (most recent call last):
876
+ ...
877
+ NotImplementedError: joins are not implemented for cubical complexes
878
+
879
+ Therefore, neither are cones or suspensions.
880
+ """
881
+ def __init__(self, maximal_faces=None, maximality_check=True):
882
+ r"""
883
+ Define a cubical complex. See ``CubicalComplex`` for more
884
+ documentation.
885
+
886
+ EXAMPLES::
887
+
888
+ sage: X = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]), ([0,1], [2,2]), ([1,1], [2,3])]); X
889
+ Cubical complex with 4 vertices and 8 cubes
890
+ sage: X == loads(dumps(X))
891
+ True
892
+ """
893
+ if maximal_faces is None:
894
+ maximal_faces = []
895
+ C = None
896
+ if isinstance(maximal_faces, CubicalComplex):
897
+ C = maximal_faces
898
+ try:
899
+ C = maximal_faces._cubical_()
900
+ except AttributeError:
901
+ pass
902
+ if C is not None:
903
+ self._facets = copy(C._facets)
904
+ self._cells = copy(C._cells)
905
+ self._complex = copy(C._complex)
906
+ return
907
+
908
+ good_faces = []
909
+ maximal_cubes = [Cube(f) for f in maximal_faces]
910
+ for face in maximal_cubes:
911
+ # check whether each given face is actually maximal
912
+ face_is_maximal = True
913
+ if maximality_check:
914
+ faces_to_be_removed = []
915
+ for other in good_faces:
916
+ if other.is_face(face):
917
+ faces_to_be_removed.append(other)
918
+ elif face_is_maximal:
919
+ face_is_maximal = not face.is_face(other)
920
+ for x in faces_to_be_removed:
921
+ good_faces.remove(x)
922
+ if face_is_maximal:
923
+ good_faces += [face]
924
+ # if no maximal faces, add the empty face as a facet
925
+ if len(maximal_cubes) == 0:
926
+ good_faces.append(Cube(()))
927
+ # self._facets: tuple of facets
928
+ self._facets = tuple(good_faces)
929
+ # self._cells: dictionary of dictionaries of faces. The main
930
+ # dictionary is keyed by subcomplexes, and each value is a
931
+ # dictionary keyed by dimension. This should be empty until
932
+ # needed -- that is, until the faces method is called
933
+ self._cells = {}
934
+ # self._complex: dictionary indexed by dimension d, base_ring,
935
+ # etc.: differential from dim d to dim d-1 in the associated
936
+ # chain complex. thus to get the differential in the cochain
937
+ # complex from dim d-1 to dim d, take the transpose of this
938
+ # one.
939
+ self._complex = {}
940
+
941
+ def maximal_cells(self):
942
+ """
943
+ The set of maximal cells (with respect to inclusion) of this
944
+ cubical complex.
945
+
946
+ OUTPUT: set of maximal cells
947
+
948
+ This just returns the set of cubes used in defining the
949
+ cubical complex, so if the complex was defined with no
950
+ maximality checking, none is done here, either.
951
+
952
+ EXAMPLES::
953
+
954
+ sage: interval = cubical_complexes.Cube(1)
955
+ sage: interval
956
+ Cubical complex with 2 vertices and 3 cubes
957
+ sage: interval.maximal_cells()
958
+ {[0,1]}
959
+ sage: interval.product(interval).maximal_cells()
960
+ {[0,1] x [0,1]}
961
+ """
962
+ return Set(self._facets)
963
+
964
+ def __eq__(self, other):
965
+ r"""
966
+ Return ``True`` if the set of maximal cells is the same for
967
+ ``self`` and ``other``.
968
+
969
+ INPUT:
970
+
971
+ - ``other`` -- another cubical complex
972
+
973
+ EXAMPLES::
974
+
975
+ sage: I1 = cubical_complexes.Cube(1)
976
+ sage: I2 = cubical_complexes.Cube(1)
977
+ sage: I1.product(I2) == I2.product(I1)
978
+ True
979
+ sage: I1.product(I2.product(I2)) == I2.product(I1.product(I1))
980
+ True
981
+ sage: S1 = cubical_complexes.Sphere(1)
982
+ sage: I1.product(S1) == S1.product(I1)
983
+ False
984
+ """
985
+ return self.maximal_cells() == other.maximal_cells()
986
+
987
+ def __ne__(self, other):
988
+ r"""
989
+ Return ``True`` if ``self`` and ``other`` are not equal.
990
+
991
+ INPUT:
992
+
993
+ - ``other`` -- another cubical complex
994
+
995
+ EXAMPLES::
996
+
997
+ sage: I1 = cubical_complexes.Cube(1)
998
+ sage: I2 = cubical_complexes.Cube(1)
999
+ sage: I1.product(I2) != I2.product(I1)
1000
+ False
1001
+ sage: I1.product(I2.product(I2)) != I2.product(I1.product(I1))
1002
+ False
1003
+ sage: S1 = cubical_complexes.Sphere(1)
1004
+ sage: I1.product(S1) != S1.product(I1)
1005
+ True
1006
+ """
1007
+ return not self.__eq__(other)
1008
+
1009
+ def __hash__(self):
1010
+ r"""
1011
+ TESTS::
1012
+
1013
+ sage: I1 = cubical_complexes.Cube(1)
1014
+ sage: I2 = cubical_complexes.Cube(1)
1015
+ sage: hash(I1) == hash(I2)
1016
+ True
1017
+ sage: hash(I1.product(I1)) == hash(I2.product(I1))
1018
+ True
1019
+ """
1020
+ return hash(frozenset(self._facets))
1021
+
1022
+ def is_subcomplex(self, other):
1023
+ r"""
1024
+ Return ``True`` if ``self`` is a subcomplex of ``other``.
1025
+
1026
+ INPUT:
1027
+
1028
+ - ``other`` -- a cubical complex
1029
+
1030
+ Each maximal cube of ``self`` must be a face of a maximal cube
1031
+ of ``other`` for this to be True.
1032
+
1033
+ EXAMPLES::
1034
+
1035
+ sage: S1 = cubical_complexes.Sphere(1)
1036
+ sage: C0 = cubical_complexes.Cube(0)
1037
+ sage: C1 = cubical_complexes.Cube(1)
1038
+ sage: cyl = S1.product(C1)
1039
+ sage: end = S1.product(C0)
1040
+ sage: end.is_subcomplex(cyl)
1041
+ True
1042
+ sage: cyl.is_subcomplex(end)
1043
+ False
1044
+
1045
+ The embedding of the cubical complex is important here::
1046
+
1047
+ sage: C2 = cubical_complexes.Cube(2)
1048
+ sage: C1.is_subcomplex(C2)
1049
+ False
1050
+ sage: C1.product(C0).is_subcomplex(C2)
1051
+ True
1052
+
1053
+ ``C1`` is not a subcomplex of ``C2`` because it's not embedded
1054
+ in `\RR^2`. On the other hand, ``C1 x C0`` is a face of
1055
+ ``C2``. Look at their maximal cells::
1056
+
1057
+ sage: C1.maximal_cells()
1058
+ {[0,1]}
1059
+ sage: C2.maximal_cells()
1060
+ {[0,1] x [0,1]}
1061
+ sage: C1.product(C0).maximal_cells()
1062
+ {[0,1] x [0,0]}
1063
+ """
1064
+ other_facets = other.maximal_cells()
1065
+ return all(any(cube.is_face(other_cube)
1066
+ for other_cube in other_facets)
1067
+ for cube in self.maximal_cells())
1068
+
1069
+ def cells(self, subcomplex=None):
1070
+ """
1071
+ The cells of this cubical complex, in the form of a dictionary:
1072
+ the keys are integers, representing dimension, and the value
1073
+ associated to an integer d is the list of d-cells.
1074
+
1075
+ If the optional argument ``subcomplex`` is present, then
1076
+ return only the faces which are *not* in the subcomplex.
1077
+
1078
+ INPUT:
1079
+
1080
+ - ``subcomplex`` -- a subcomplex of this cubical complex (default:
1081
+ ``None``)
1082
+
1083
+ OUTPUT: dictionary; the cells of this complex not contained in
1084
+ ``subcomplex``
1085
+
1086
+ EXAMPLES::
1087
+
1088
+ sage: S2 = cubical_complexes.Sphere(2)
1089
+ sage: sorted(S2.cells()[2])
1090
+ [[0,0] x [0,1] x [0,1],
1091
+ [0,1] x [0,0] x [0,1],
1092
+ [0,1] x [0,1] x [0,0],
1093
+ [0,1] x [0,1] x [1,1],
1094
+ [0,1] x [1,1] x [0,1],
1095
+ [1,1] x [0,1] x [0,1]]
1096
+ """
1097
+ if subcomplex not in self._cells:
1098
+ if subcomplex is not None and subcomplex.dimension() > -1:
1099
+ if not subcomplex.is_subcomplex(self):
1100
+ raise ValueError("the 'subcomplex' is not actually a subcomplex")
1101
+ # Cells is the dictionary of cells in self but not in
1102
+ # subcomplex, indexed by dimension
1103
+ Cells = {}
1104
+ # sub_facets is the dictionary of facets in the subcomplex
1105
+ sub_facets = {}
1106
+ dimension = max([cube.dimension() for cube in self._facets])
1107
+ # initialize the lists: add each maximal cube to Cells and sub_facets
1108
+ for i in range(-1, dimension+1):
1109
+ Cells[i] = set()
1110
+ sub_facets[i] = set()
1111
+ for f in self._facets:
1112
+ Cells[f.dimension()].add(f)
1113
+ if subcomplex is not None:
1114
+ for g in subcomplex._facets:
1115
+ dim = g.dimension()
1116
+ Cells[dim].discard(g)
1117
+ sub_facets[dim].add(g)
1118
+ # bad_faces is the set of faces in the subcomplex in the
1119
+ # current dimension
1120
+ bad_faces = sub_facets[dimension]
1121
+ for dim in range(dimension, -1, -1):
1122
+ # bad_bdries = boundaries of bad_faces: things to be
1123
+ # discarded in dim-1
1124
+ bad_bdries = sub_facets[dim-1]
1125
+ for f in bad_faces:
1126
+ bad_bdries.update(f.faces())
1127
+ for f in Cells[dim]:
1128
+ Cells[dim-1].update(set(f.faces()).difference(bad_bdries))
1129
+ bad_faces = bad_bdries
1130
+ self._cells[subcomplex] = Cells
1131
+ return self._cells[subcomplex]
1132
+
1133
+ def n_cubes(self, n, subcomplex=None):
1134
+ """
1135
+ The set of cubes of dimension n of this cubical complex.
1136
+ If the optional argument ``subcomplex`` is present, then
1137
+ return the ``n``-dimensional cubes which are *not* in the
1138
+ subcomplex.
1139
+
1140
+ INPUT:
1141
+
1142
+ - ``n`` -- integer; dimension
1143
+ - ``subcomplex`` -- a subcomplex of this cubical complex (default:
1144
+ ``None``)
1145
+
1146
+ OUTPUT: set; cells in dimension ``n``
1147
+
1148
+ EXAMPLES::
1149
+
1150
+ sage: C = cubical_complexes.Cube(3)
1151
+ sage: C.n_cubes(3)
1152
+ {[0,1] x [0,1] x [0,1]}
1153
+ sage: sorted(C.n_cubes(2))
1154
+ [[0,0] x [0,1] x [0,1],
1155
+ [0,1] x [0,0] x [0,1],
1156
+ [0,1] x [0,1] x [0,0],
1157
+ [0,1] x [0,1] x [1,1],
1158
+ [0,1] x [1,1] x [0,1],
1159
+ [1,1] x [0,1] x [0,1]]
1160
+ """
1161
+ return set(self.n_cells(n, subcomplex))
1162
+
1163
+ def chain_complex(self, subcomplex=None, augmented=False,
1164
+ verbose=False, check=False, dimensions=None,
1165
+ base_ring=ZZ, cochain=False):
1166
+ r"""
1167
+ The chain complex associated to this cubical complex.
1168
+
1169
+ INPUT:
1170
+
1171
+ - ``dimensions`` -- if ``None``, compute the chain complex in all
1172
+ dimensions. If a list or tuple of integers, compute the
1173
+ chain complex in those dimensions, setting the chain groups
1174
+ in all other dimensions to zero. NOT IMPLEMENTED YET: this
1175
+ function always returns the entire chain complex
1176
+ - ``base_ring`` -- commutative ring (default: ZZ)
1177
+ - ``subcomplex`` -- a subcomplex of this cubical complex (default: empty).
1178
+ Compute the chain complex relative to this subcomplex.
1179
+ - ``augmented`` -- boolean (default: ``False``); if ``True``, return
1180
+ the augmented chain complex (that is, include a class in dimension
1181
+ `-1` corresponding to the empty cell). This is ignored if
1182
+ ``dimensions`` is specified.
1183
+ - ``cochain`` -- boolean (default: ``False``); if ``True``, return the
1184
+ cochain complex (that is, the dual of the chain complex).
1185
+ - ``verbose`` -- boolean (default: ``False``); if ``True``, print some
1186
+ messages as the chain complex is computed.
1187
+ - ``check`` -- boolean (default: ``False``); if ``True``, make sure
1188
+ that the chain complex is actually a chain complex: the differentials
1189
+ are composable and their product is zero.
1190
+
1191
+ .. NOTE::
1192
+
1193
+ If subcomplex is nonempty, then the argument ``augmented``
1194
+ has no effect: the chain complex relative to a nonempty
1195
+ subcomplex is zero in dimension `-1`.
1196
+
1197
+ EXAMPLES::
1198
+
1199
+ sage: # needs sage.modules
1200
+ sage: S2 = cubical_complexes.Sphere(2)
1201
+ sage: S2.chain_complex()
1202
+ Chain complex with at most 3 nonzero terms over Integer Ring
1203
+ sage: Prod = S2.product(S2); Prod
1204
+ Cubical complex with 64 vertices and 676 cubes
1205
+ sage: Prod.chain_complex()
1206
+ Chain complex with at most 5 nonzero terms over Integer Ring
1207
+ sage: Prod.chain_complex(base_ring=QQ)
1208
+ Chain complex with at most 5 nonzero terms over Rational Field
1209
+ sage: C1 = cubical_complexes.Cube(1)
1210
+ sage: S0 = cubical_complexes.Sphere(0)
1211
+ sage: C1.chain_complex(subcomplex=S0)
1212
+ Chain complex with at most 1 nonzero terms over Integer Ring
1213
+ sage: C1.homology(subcomplex=S0)
1214
+ {0: 0, 1: Z}
1215
+
1216
+ Check that :issue:`32203` has been fixed::
1217
+
1218
+ sage: # needs sage.modules
1219
+ sage: Square = CubicalComplex([([0,1],[0,1])])
1220
+ sage: EdgesLTR = CubicalComplex([([0,0],[0,1]),([0,1],[1,1]),([1,1],[0,1])])
1221
+ sage: EdgesLBR = CubicalComplex([([0,0],[0,1]),([0,1],[0,0]),([1,1],[0,1])])
1222
+ sage: Square.homology(subcomplex=EdgesLTR)[2] == Square.homology(subcomplex=EdgesLBR)[2]
1223
+ True
1224
+ """
1225
+ from sage.homology.chain_complex import ChainComplex
1226
+
1227
+ # initialize subcomplex
1228
+ if subcomplex is None:
1229
+ subcomplex = CubicalComplex()
1230
+ else:
1231
+ # subcomplex is not empty, so don't augment the chain complex
1232
+ augmented = False
1233
+ differentials = {}
1234
+ if augmented:
1235
+ empty_cell = 1 # number of (-1)-dimensional cubes
1236
+ else:
1237
+ empty_cell = 0
1238
+ vertices = self._n_cells_sorted(0, subcomplex=subcomplex)
1239
+ n = len(vertices)
1240
+ mat = matrix(base_ring, empty_cell, n, n*empty_cell*[1])
1241
+ if cochain:
1242
+ differentials[-1] = mat.transpose()
1243
+ else:
1244
+ differentials[0] = mat
1245
+ current = vertices
1246
+ # now loop from 1 to dimension of the complex
1247
+ for dim in range(1, self.dimension()+1):
1248
+ if verbose:
1249
+ print(" starting dimension %s" % dim)
1250
+ if (dim, subcomplex) in self._complex:
1251
+ if cochain:
1252
+ differentials[dim-1] = self._complex[(dim, subcomplex)].transpose().change_ring(base_ring)
1253
+ mat = differentials[dim-1]
1254
+ else:
1255
+ differentials[dim] = self._complex[(dim, subcomplex)].change_ring(base_ring)
1256
+ mat = differentials[dim]
1257
+ if verbose:
1258
+ print(" boundary matrix (cached): it's {} by {}.".format(mat.nrows(), mat.ncols()))
1259
+ else:
1260
+ # 'current' is the list of cells in dimension n
1261
+ #
1262
+ # 'old' is a dictionary, with keys the cells in the
1263
+ # previous dimension, values the integers 0, 1, 2,
1264
+ # ... (the index of the face). finding an entry in a
1265
+ # dictionary seems to be faster than finding the index
1266
+ # of an entry in a list.
1267
+ old = dict(zip(current, range(len(current))))
1268
+ current = self._n_cells_sorted(dim, subcomplex=subcomplex)
1269
+ # construct matrix. it is easiest to construct it as
1270
+ # a sparse matrix, specifying which entries are
1271
+ # nonzero via a dictionary.
1272
+ matrix_data = {}
1273
+ col = 0
1274
+ if old and current:
1275
+ for cube in current:
1276
+ faces = cube.faces_as_pairs()
1277
+ sign = 1
1278
+ for upper, lower in faces:
1279
+ # trac 32203: use two "try/except" loops
1280
+ # in case lower is in old but upper is not.
1281
+ try:
1282
+ matrix_data[(old[upper], col)] = sign
1283
+ except KeyError:
1284
+ pass
1285
+ try:
1286
+ matrix_data[(old[lower], col)] = -1*sign
1287
+ except KeyError:
1288
+ pass
1289
+ # The signs in the boundary alternate as
1290
+ # we iterate through the faces.
1291
+ sign *= -1
1292
+ col += 1
1293
+ mat = matrix(ZZ, len(old), len(current), matrix_data)
1294
+ self._complex[(dim, subcomplex)] = mat
1295
+ if cochain:
1296
+ differentials[dim-1] = mat.transpose().change_ring(base_ring)
1297
+ else:
1298
+ differentials[dim] = mat.change_ring(base_ring)
1299
+ if verbose:
1300
+ print(" boundary matrix computed: it's {} by {}.".format(mat.nrows(), mat.ncols()))
1301
+ # finally, return the chain complex
1302
+ if cochain:
1303
+ return ChainComplex(data=differentials, base_ring=base_ring,
1304
+ degree=1, check=check)
1305
+ else:
1306
+ return ChainComplex(data=differentials, base_ring=base_ring,
1307
+ degree=-1, check=check)
1308
+
1309
+ def alexander_whitney(self, cube, dim_left):
1310
+ r"""
1311
+ Subdivide ``cube`` in this cubical complex into pairs of cubes.
1312
+
1313
+ See :meth:`Cube.alexander_whitney` for more details. This
1314
+ method just calls that one.
1315
+
1316
+ INPUT:
1317
+
1318
+ - ``cube`` -- a cube in this cubical complex
1319
+ - ``dim`` -- integer between 0 and one more than the
1320
+ dimension of this cube
1321
+
1322
+ OUTPUT: list containing triples ``(coeff, left, right)``
1323
+
1324
+ EXAMPLES::
1325
+
1326
+ sage: C = cubical_complexes.Cube(3)
1327
+ sage: c = list(C.n_cubes(3))[0]; c
1328
+ [0,1] x [0,1] x [0,1]
1329
+ sage: C.alexander_whitney(c, 1)
1330
+ [(1, [0,1] x [0,0] x [0,0], [1,1] x [0,1] x [0,1]),
1331
+ (-1, [0,0] x [0,1] x [0,0], [0,1] x [1,1] x [0,1]),
1332
+ (1, [0,0] x [0,0] x [0,1], [0,1] x [0,1] x [1,1])]
1333
+ """
1334
+ return cube.alexander_whitney(dim_left)
1335
+
1336
+ def n_skeleton(self, n):
1337
+ r"""
1338
+ The n-skeleton of this cubical complex.
1339
+
1340
+ INPUT:
1341
+
1342
+ - ``n`` -- nonnegative integer; dimension
1343
+
1344
+ OUTPUT: cubical complex
1345
+
1346
+ EXAMPLES::
1347
+
1348
+ sage: S2 = cubical_complexes.Sphere(2)
1349
+ sage: C3 = cubical_complexes.Cube(3)
1350
+ sage: S2 == C3.n_skeleton(2)
1351
+ True
1352
+ """
1353
+ if n >= self.dimension():
1354
+ return self
1355
+ else:
1356
+ data = []
1357
+ for d in range(n+1):
1358
+ data.extend(list(self.cells()[d]))
1359
+ return CubicalComplex(data)
1360
+
1361
+ def graph(self):
1362
+ """
1363
+ The 1-skeleton of this cubical complex, as a graph.
1364
+
1365
+ EXAMPLES::
1366
+
1367
+ sage: cubical_complexes.Sphere(2).graph()
1368
+ Graph on 8 vertices
1369
+ """
1370
+ from sage.graphs.graph import Graph
1371
+
1372
+ data = {}
1373
+ vertex_dict = {}
1374
+ i = 0
1375
+ for vertex in self.n_cells(0):
1376
+ vertex_dict[vertex] = i
1377
+ data[i] = []
1378
+ i += 1
1379
+ for edge in self.n_cells(1):
1380
+ start = edge.face(0, False)
1381
+ end = edge.face(0, True)
1382
+ data[vertex_dict[start]].append(vertex_dict[end])
1383
+ return Graph(data)
1384
+
1385
+ def is_pure(self):
1386
+ """
1387
+ Return ``True`` iff this cubical complex is pure: that is,
1388
+ all of its maximal faces have the same dimension.
1389
+
1390
+ .. WARNING::
1391
+
1392
+ This may give the wrong answer if the cubical complex
1393
+ was constructed with ``maximality_check`` set to False.
1394
+
1395
+ EXAMPLES::
1396
+
1397
+ sage: S4 = cubical_complexes.Sphere(4)
1398
+ sage: S4.is_pure()
1399
+ True
1400
+ sage: C = CubicalComplex([([0,0], [3,3]), ([1,2], [4,5])])
1401
+ sage: C.is_pure()
1402
+ False
1403
+ """
1404
+ dims = [face.dimension() for face in self._facets]
1405
+ return max(dims) == min(dims)
1406
+
1407
+ def join(self, other):
1408
+ r"""
1409
+ The join of this cubical complex with another one.
1410
+
1411
+ NOT IMPLEMENTED.
1412
+
1413
+ INPUT:
1414
+
1415
+ - ``other`` -- another cubical complex
1416
+
1417
+ EXAMPLES::
1418
+
1419
+ sage: C1 = cubical_complexes.Cube(1)
1420
+ sage: C1.join(C1)
1421
+ Traceback (most recent call last):
1422
+ ...
1423
+ NotImplementedError: joins are not implemented for cubical complexes
1424
+ """
1425
+ raise NotImplementedError("joins are not implemented for cubical complexes")
1426
+
1427
+ # Use * to mean 'join':
1428
+ # __mul__ = join
1429
+
1430
+ def cone(self):
1431
+ r"""
1432
+ The cone on this cubical complex.
1433
+
1434
+ NOT IMPLEMENTED
1435
+
1436
+ The cone is the complex formed by taking the join of the
1437
+ original complex with a one-point complex (that is, a
1438
+ 0-dimensional cube). Since joins are not implemented for
1439
+ cubical complexes, neither are cones.
1440
+
1441
+ EXAMPLES::
1442
+
1443
+ sage: C1 = cubical_complexes.Cube(1)
1444
+ sage: C1.cone()
1445
+ Traceback (most recent call last):
1446
+ ...
1447
+ NotImplementedError: cones are not implemented for cubical complexes
1448
+ """
1449
+ # return self.join(cubical_complexes.Cube(0))
1450
+ raise NotImplementedError("cones are not implemented for cubical complexes")
1451
+
1452
+ def suspension(self, n=1):
1453
+ r"""
1454
+ The suspension of this cubical complex.
1455
+
1456
+ NOT IMPLEMENTED
1457
+
1458
+ INPUT:
1459
+
1460
+ - ``n`` -- positive integer (default: 1); suspend this many times
1461
+
1462
+ The suspension is the complex formed by taking the join of the
1463
+ original complex with a two-point complex (the 0-sphere).
1464
+ Since joins are not implemented for cubical complexes, neither
1465
+ are suspensions.
1466
+
1467
+ EXAMPLES::
1468
+
1469
+ sage: C1 = cubical_complexes.Cube(1)
1470
+ sage: C1.suspension()
1471
+ Traceback (most recent call last):
1472
+ ...
1473
+ NotImplementedError: suspensions are not implemented for cubical complexes
1474
+ """
1475
+ # if n < 0:
1476
+ # raise ValueError("n must be nonnegative")
1477
+ # if n == 0:
1478
+ # return self
1479
+ # if n == 1:
1480
+ # return self.join(cubical_complexes.Sphere(0))
1481
+ # return self.suspension().suspension(int(n-1))
1482
+ raise NotImplementedError("suspensions are not implemented for cubical complexes")
1483
+
1484
+ def product(self, other):
1485
+ r"""
1486
+ Return the product of this cubical complex with another one.
1487
+
1488
+ INPUT:
1489
+
1490
+ - ``other`` -- another cubical complex
1491
+
1492
+ EXAMPLES::
1493
+
1494
+ sage: RP2 = cubical_complexes.RealProjectivePlane()
1495
+ sage: S1 = cubical_complexes.Sphere(1)
1496
+ sage: RP2.product(S1).homology()[1] # long time: 5 seconds
1497
+ Z x C2
1498
+ """
1499
+ facets = [f.product(g) for f in self._facets for g in other._facets]
1500
+ return CubicalComplex(facets)
1501
+
1502
+ def disjoint_union(self, other):
1503
+ """
1504
+ The disjoint union of this cubical complex with another one.
1505
+
1506
+ INPUT:
1507
+
1508
+ - ``right`` -- the other cubical complex (the right-hand factor)
1509
+
1510
+ Algorithm: first embed both complexes in d-dimensional
1511
+ Euclidean space. Then embed in (1+d)-dimensional space,
1512
+ calling the new axis `x`, and putting the first complex at
1513
+ `x=0`, the second at `x=1`.
1514
+
1515
+ EXAMPLES::
1516
+
1517
+ sage: S1 = cubical_complexes.Sphere(1)
1518
+ sage: S2 = cubical_complexes.Sphere(2)
1519
+ sage: S1.disjoint_union(S2).homology() # needs sage.modules
1520
+ {0: Z, 1: Z, 2: Z}
1521
+ """
1522
+ embedded_left = len(tuple(self.maximal_cells()[0]))
1523
+ embedded_right = len(tuple(other.maximal_cells()[0]))
1524
+ zero = [0] * max(embedded_left, embedded_right)
1525
+ C00 = Cube([[0, 0]])
1526
+ facets = [C00.product(f._translate(zero))
1527
+ for f in self.maximal_cells()]
1528
+ C11 = Cube([[1, 1]])
1529
+ facets.extend(C11.product(f._translate(zero))
1530
+ for f in other.maximal_cells())
1531
+ return CubicalComplex(facets)
1532
+
1533
+ def wedge(self, other):
1534
+ """
1535
+ The wedge (one-point union) of this cubical complex with
1536
+ another one.
1537
+
1538
+ INPUT:
1539
+
1540
+ - ``right`` -- the other cubical complex (the right-hand factor)
1541
+
1542
+ Algorithm: if ``self`` is embedded in `d` dimensions and
1543
+ ``other`` in `n` dimensions, embed them in `d+n` dimensions:
1544
+ ``self`` using the first `d` coordinates, ``other`` using the
1545
+ last `n`, translating them so that they have the origin as a
1546
+ common vertex.
1547
+
1548
+ .. NOTE::
1549
+
1550
+ This operation is not well-defined if ``self`` or
1551
+ ``other`` is not path-connected.
1552
+
1553
+ EXAMPLES::
1554
+
1555
+ sage: S1 = cubical_complexes.Sphere(1)
1556
+ sage: S2 = cubical_complexes.Sphere(2)
1557
+ sage: S1.wedge(S2).homology() # needs sage.modules
1558
+ {0: 0, 1: Z, 2: Z}
1559
+ """
1560
+ embedded_left = len(tuple(self.maximal_cells()[0]))
1561
+ embedded_right = len(tuple(other.maximal_cells()[0]))
1562
+ translate_left = [-a[0] for a in self.maximal_cells()[0]] + [0] * embedded_right
1563
+ translate_right = [-a[0] for a in other.maximal_cells()[0]]
1564
+ point_right = Cube([[0, 0]] * embedded_left)
1565
+
1566
+ facets = [f._translate(translate_left) for f in self.maximal_cells()]
1567
+ facets.extend(point_right.product(f._translate(translate_right))
1568
+ for f in other.maximal_cells())
1569
+ return CubicalComplex(facets)
1570
+
1571
+ def connected_sum(self, other):
1572
+ """
1573
+ Return the connected sum of ``self`` with ``other``.
1574
+
1575
+ INPUT:
1576
+
1577
+ - ``other`` -- another cubical complex
1578
+
1579
+ OUTPUT: the connected sum ``self # other``
1580
+
1581
+ .. warning::
1582
+
1583
+ This does not check that ``self`` and ``other`` are manifolds, only
1584
+ that their facets all have the same dimension. Since a
1585
+ (more or less) random facet is chosen from each complex and
1586
+ then glued together, this method may return random
1587
+ results if applied to non-manifolds, depending on which
1588
+ facet is chosen.
1589
+
1590
+ EXAMPLES::
1591
+
1592
+ sage: T = cubical_complexes.Torus()
1593
+ sage: S2 = cubical_complexes.Sphere(2)
1594
+ sage: T.connected_sum(S2).cohomology() == T.cohomology() # needs sage.modules
1595
+ True
1596
+ sage: RP2 = cubical_complexes.RealProjectivePlane()
1597
+ sage: T.connected_sum(RP2).homology(1) # needs sage.modules
1598
+ Z x Z x C2
1599
+ sage: RP2.connected_sum(RP2).connected_sum(RP2).homology(1) # needs sage.modules
1600
+ Z x Z x C2
1601
+ """
1602
+ # connected_sum: first check whether the complexes are pure
1603
+ # and of the same dimension. Then insert degenerate intervals
1604
+ # and translate them so that they have a common cube C. Add one
1605
+ # more dimension, embedding the first complex as (..., 0) and
1606
+ # the second as (..., 1). Keep all of the other facets, but remove
1607
+ # C x 0 and C x 1, putting in its place (its boundary) x (0,1).
1608
+ if not (self.is_pure() and other.is_pure() and
1609
+ self.dimension() == other.dimension()):
1610
+ raise ValueError("complexes are not pure of the same dimension")
1611
+
1612
+ self_facets = list(self.maximal_cells())
1613
+ other_facets = list(other.maximal_cells())
1614
+
1615
+ C1 = self_facets.pop()
1616
+ C2 = other_facets.pop()
1617
+ (insert_self, insert_other, translate) = C1._compare_for_gluing(C2)
1618
+
1619
+ CL = list(C1.tuple())
1620
+ for (idx, L) in insert_self:
1621
+ CL[idx:idx] = L
1622
+ removed = Cube(CL)
1623
+
1624
+ # start assembling the facets in the connected sum: first, the
1625
+ # cylinder on the removed face.
1626
+ new_facets = []
1627
+ cylinder = removed.product(Cube([[0, 1]]))
1628
+ # don't want to include the ends of the cylinder, so don't
1629
+ # include the last pair of faces. therefore, choose faces up
1630
+ # to removed.dimension(), not cylinder.dimension().
1631
+ for n in range(removed.dimension()):
1632
+ new_facets.append(cylinder.face(n, upper=False))
1633
+ new_facets.append(cylinder.face(n, upper=True))
1634
+
1635
+ for cube in self_facets:
1636
+ CL = list(cube.tuple())
1637
+ for (idx, L) in insert_self:
1638
+ CL[idx:idx] = L
1639
+ CL.append((0, 0))
1640
+ new_facets.append(Cube(CL))
1641
+ for cube in other_facets:
1642
+ CL = list(cube.tuple())
1643
+ for (idx, L) in insert_other:
1644
+ CL[idx:idx] = L
1645
+ CL.append((1, 1))
1646
+ new_facets.append(Cube(CL)._translate(translate))
1647
+ return CubicalComplex(new_facets)
1648
+
1649
+ def _translate(self, vec):
1650
+ """
1651
+ Translate ``self`` by ``vec``.
1652
+
1653
+ INPUT:
1654
+
1655
+ - ``vec`` -- anything which can be converted to a tuple of integers
1656
+
1657
+ OUTPUT: cubical complex; the translation of ``self`` by ``vec``
1658
+
1659
+ If ``vec`` is shorter than the list of intervals forming the
1660
+ complex, pad with zeroes, and similarly if the complexes
1661
+ defining tuples are too short.
1662
+
1663
+ EXAMPLES::
1664
+
1665
+ sage: C1 = cubical_complexes.Cube(1)
1666
+ sage: C1.maximal_cells()
1667
+ {[0,1]}
1668
+ sage: C1._translate([2,6]).maximal_cells()
1669
+ {[2,3] x [6,6]}
1670
+ """
1671
+ return CubicalComplex([f._translate(vec) for f in self.maximal_cells()])
1672
+
1673
+ # This is cached for speed reasons: it can be very slow to run
1674
+ # this function.
1675
+ @cached_method
1676
+ def algebraic_topological_model(self, base_ring=None):
1677
+ r"""
1678
+ Algebraic topological model for this cubical complex with
1679
+ coefficients in ``base_ring``.
1680
+
1681
+ The term "algebraic topological model" is defined by Pilarczyk
1682
+ and Réal [PR2015]_.
1683
+
1684
+ INPUT:
1685
+
1686
+ - ``base_ring`` -- coefficient ring (default: ``QQ``); must be a field
1687
+
1688
+ Denote by `C` the chain complex associated to this cubical
1689
+ complex. The algebraic topological model is a chain complex
1690
+ `M` with zero differential, with the same homology as `C`,
1691
+ along with chain maps `\pi: C \to M` and `\iota: M \to C`
1692
+ satisfying `\iota \pi = 1_M` and `\pi \iota` chain homotopic
1693
+ to `1_C`. The chain homotopy `\phi` must satisfy
1694
+
1695
+ - `\phi \phi = 0`,
1696
+ - `\pi \phi = 0`,
1697
+ - `\phi \iota = 0`.
1698
+
1699
+ Such a chain homotopy is called a *chain contraction*.
1700
+
1701
+ OUTPUT: a pair consisting of
1702
+
1703
+ - chain contraction ``phi`` associated to `C`, `M`, `\pi`, and
1704
+ `\iota`
1705
+ - the chain complex `M`
1706
+
1707
+ Note that from the chain contraction ``phi``, one can recover the
1708
+ chain maps `\pi` and `\iota` via ``phi.pi()`` and
1709
+ ``phi.iota()``. Then one can recover `C` and `M` from, for
1710
+ example, ``phi.pi().domain()`` and ``phi.pi().codomain()``,
1711
+ respectively.
1712
+
1713
+ EXAMPLES::
1714
+
1715
+ sage: # needs sage.modules
1716
+ sage: RP2 = cubical_complexes.RealProjectivePlane()
1717
+ sage: phi, M = RP2.algebraic_topological_model(GF(2))
1718
+ sage: M.homology()
1719
+ {0: Vector space of dimension 1 over Finite Field of size 2,
1720
+ 1: Vector space of dimension 1 over Finite Field of size 2,
1721
+ 2: Vector space of dimension 1 over Finite Field of size 2}
1722
+ sage: T = cubical_complexes.Torus()
1723
+ sage: phi, M = T.algebraic_topological_model(QQ)
1724
+ sage: M.homology()
1725
+ {0: Vector space of dimension 1 over Rational Field,
1726
+ 1: Vector space of dimension 2 over Rational Field,
1727
+ 2: Vector space of dimension 1 over Rational Field}
1728
+ """
1729
+ from sage.homology.algebraic_topological_model import algebraic_topological_model
1730
+ if base_ring is None:
1731
+ base_ring = QQ
1732
+ return algebraic_topological_model(self, base_ring)
1733
+
1734
+ def _simplicial_(self):
1735
+ r"""
1736
+ Simplicial complex constructed from ``self``.
1737
+
1738
+ ALGORITHM:
1739
+
1740
+ This is constructed as described by Hetyei: choose a total
1741
+ ordering of the vertices of the cubical complex. Then for
1742
+ each maximal face
1743
+
1744
+ .. MATH::
1745
+
1746
+ C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]
1747
+
1748
+ let `v_1` be the "upper" corner of `C`: `v` is the point
1749
+ `(j_1, ..., j_k)`. Choose a coordinate `n` where the interval
1750
+ `[i_n, j_n]` is non-degenerate and form `v_2` by replacing
1751
+ `j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex
1752
+ so defined will be `(i_1, ..., i_k)`. These vertices define a
1753
+ simplex, and do the vertices obtained by making different
1754
+ choices at each stage. Thus each `n`-cube is subdivided into
1755
+ `n!` simplices.
1756
+
1757
+ REFERENCES:
1758
+
1759
+ - G. Hetyei, "On the Stanley ring of a cubical complex",
1760
+ Discrete Comput. Geom. 14 (1995), 305-330.
1761
+
1762
+ EXAMPLES::
1763
+
1764
+ sage: T = cubical_complexes.Torus(); T
1765
+ Cubical complex with 16 vertices and 64 cubes
1766
+ sage: len(T.maximal_cells())
1767
+ 16
1768
+
1769
+ When this is triangulated, each maximal 2-dimensional cube
1770
+ gets turned into a pair of triangles. Since there were 16
1771
+ maximal cubes, this results in 32 facets in the simplicial
1772
+ complex::
1773
+
1774
+ sage: Ts = T._simplicial_(); Ts
1775
+ Simplicial complex with 16 vertices and 32 facets
1776
+ sage: T.homology() == Ts.homology() # needs sage.modules
1777
+ True
1778
+
1779
+ Each `n`-dimensional cube produces `n!` `n`-simplices::
1780
+
1781
+ sage: S4 = cubical_complexes.Sphere(4)
1782
+ sage: len(S4.maximal_cells())
1783
+ 10
1784
+ sage: SimplicialComplex(S4) # calls S4._simplicial_()
1785
+ Simplicial complex with 32 vertices and 240 facets
1786
+ """
1787
+ from .simplicial_complex import SimplicialComplex
1788
+ simplices = []
1789
+ for C in self.maximal_cells():
1790
+ simplices.extend(C._triangulation_())
1791
+ return SimplicialComplex(simplices)
1792
+
1793
+ def _string_constants(self):
1794
+ """
1795
+ Tuple containing the name of the type of complex, and the
1796
+ singular and plural of the name of the cells from which it is
1797
+ built. This is used in constructing the string representation.
1798
+
1799
+ EXAMPLES::
1800
+
1801
+ sage: S3 = cubical_complexes.Sphere(3)
1802
+ sage: S3._string_constants()
1803
+ ('Cubical', 'cube', 'cubes')
1804
+ sage: S3._repr_() # indirect doctest
1805
+ 'Cubical complex with 16 vertices and 80 cubes'
1806
+ """
1807
+ return ('Cubical', 'cube', 'cubes')
1808
+
1809
+
1810
+ class CubicalComplexExamples:
1811
+ r"""
1812
+ Some examples of cubical complexes.
1813
+
1814
+ Here are the available examples; you can also type
1815
+ "cubical_complexes." and hit TAB to get a list::
1816
+
1817
+ Sphere
1818
+ Torus
1819
+ RealProjectivePlane
1820
+ KleinBottle
1821
+ SurfaceOfGenus
1822
+ Cube
1823
+
1824
+ EXAMPLES::
1825
+
1826
+ sage: cubical_complexes.Torus() # indirect doctest
1827
+ Cubical complex with 16 vertices and 64 cubes
1828
+ sage: cubical_complexes.Cube(7)
1829
+ Cubical complex with 128 vertices and 2187 cubes
1830
+ sage: cubical_complexes.Sphere(7)
1831
+ Cubical complex with 256 vertices and 6560 cubes
1832
+ """
1833
+
1834
+ def Sphere(self, n):
1835
+ r"""
1836
+ A cubical complex representation of the `n`-dimensional sphere,
1837
+ formed by taking the boundary of an `(n+1)`-dimensional cube.
1838
+
1839
+ INPUT:
1840
+
1841
+ - ``n`` -- nonnegative integer; the dimension of the sphere
1842
+
1843
+ EXAMPLES::
1844
+
1845
+ sage: cubical_complexes.Sphere(7)
1846
+ Cubical complex with 256 vertices and 6560 cubes
1847
+ """
1848
+ return CubicalComplex(Cube([[0, 1]]*(n+1)).faces())
1849
+
1850
+ def Torus(self):
1851
+ r"""
1852
+ A cubical complex representation of the torus, obtained by
1853
+ taking the product of the circle with itself.
1854
+
1855
+ EXAMPLES::
1856
+
1857
+ sage: cubical_complexes.Torus()
1858
+ Cubical complex with 16 vertices and 64 cubes
1859
+ """
1860
+ S1 = cubical_complexes.Sphere(1)
1861
+ return S1.product(S1)
1862
+
1863
+ def RealProjectivePlane(self):
1864
+ r"""
1865
+ A cubical complex representation of the real projective plane.
1866
+ This is taken from the examples from CHomP, the Computational
1867
+ Homology Project: http://chomp.rutgers.edu/.
1868
+
1869
+ EXAMPLES::
1870
+
1871
+ sage: cubical_complexes.RealProjectivePlane()
1872
+ Cubical complex with 21 vertices and 81 cubes
1873
+ """
1874
+ return CubicalComplex([
1875
+ ([0, 1], [0], [0], [0, 1], [0]),
1876
+ ([0, 1], [0], [0], [0], [0, 1]),
1877
+ ([0], [0, 1], [0, 1], [0], [0]),
1878
+ ([0], [0, 1], [0], [0, 1], [0]),
1879
+ ([0], [0], [0, 1], [0], [0, 1]),
1880
+ ([0, 1], [0, 1], [1], [0], [0]),
1881
+ ([0, 1], [1], [0, 1], [0], [0]),
1882
+ ([1], [0, 1], [0, 1], [0], [0]),
1883
+ ([0, 1], [0, 1], [0], [0], [1]),
1884
+ ([0, 1], [1], [0], [0], [0, 1]),
1885
+ ([1], [0, 1], [0], [0], [0, 1]),
1886
+ ([0, 1], [0], [0, 1], [1], [0]),
1887
+ ([0, 1], [0], [1], [0, 1], [0]),
1888
+ ([1], [0], [0, 1], [0, 1], [0]),
1889
+ ([0], [0, 1], [0], [0, 1], [1]),
1890
+ ([0], [0, 1], [0], [1], [0, 1]),
1891
+ ([0], [1], [0], [0, 1], [0, 1]),
1892
+ ([0], [0], [0, 1], [0, 1], [1]),
1893
+ ([0], [0], [0, 1], [1], [0, 1]),
1894
+ ([0], [0], [1], [0, 1], [0, 1])])
1895
+
1896
+ def KleinBottle(self):
1897
+ r"""
1898
+ A cubical complex representation of the Klein bottle, formed
1899
+ by taking the connected sum of the real projective plane with
1900
+ itself.
1901
+
1902
+ EXAMPLES::
1903
+
1904
+ sage: cubical_complexes.KleinBottle()
1905
+ Cubical complex with 42 vertices and 168 cubes
1906
+ """
1907
+ RP2 = cubical_complexes.RealProjectivePlane()
1908
+ return RP2.connected_sum(RP2)
1909
+
1910
+ def SurfaceOfGenus(self, g, orientable=True):
1911
+ """
1912
+ A surface of genus `g` as a cubical complex.
1913
+
1914
+ INPUT:
1915
+
1916
+ - ``g`` -- nonnegative integer; the genus
1917
+ - ``orientable`` -- boolean (default: ``True``); whether the surface
1918
+ should be orientable
1919
+
1920
+ In the orientable case, return a sphere if `g` is zero, and
1921
+ otherwise return a `g`-fold connected sum of a torus with
1922
+ itself.
1923
+
1924
+ In the non-orientable case, raise an error if `g` is zero. If
1925
+ `g` is positive, return a `g`-fold connected sum of a
1926
+ real projective plane with itself.
1927
+
1928
+ EXAMPLES::
1929
+
1930
+ sage: cubical_complexes.SurfaceOfGenus(2)
1931
+ Cubical complex with 32 vertices and 134 cubes
1932
+ sage: cubical_complexes.SurfaceOfGenus(1, orientable=False)
1933
+ Cubical complex with 21 vertices and 81 cubes
1934
+ """
1935
+ try:
1936
+ g = Integer(g)
1937
+ except TypeError:
1938
+ raise ValueError("genus must be a nonnegative integer")
1939
+ if g < 0:
1940
+ raise ValueError("genus must be a nonnegative integer")
1941
+ if g == 0:
1942
+ if not orientable:
1943
+ raise ValueError("no non-orientable surface of genus zero")
1944
+ else:
1945
+ return cubical_complexes.Sphere(2)
1946
+ if orientable:
1947
+ T = cubical_complexes.Torus()
1948
+ else:
1949
+ T = cubical_complexes.RealProjectivePlane()
1950
+ S = T
1951
+ for i in range(g-1):
1952
+ S = S.connected_sum(T)
1953
+ return S
1954
+
1955
+ def Cube(self, n):
1956
+ r"""
1957
+ A cubical complex representation of an `n`-dimensional cube.
1958
+
1959
+ INPUT:
1960
+
1961
+ - ``n`` -- nonnegative integer; the dimension
1962
+
1963
+ EXAMPLES::
1964
+
1965
+ sage: cubical_complexes.Cube(0)
1966
+ Cubical complex with 1 vertex and 1 cube
1967
+ sage: cubical_complexes.Cube(3)
1968
+ Cubical complex with 8 vertices and 27 cubes
1969
+ """
1970
+ if n == 0:
1971
+ return CubicalComplex([Cube([[0]])])
1972
+ else:
1973
+ return CubicalComplex([Cube([[0, 1]] * n)])
1974
+
1975
+
1976
+ cubical_complexes = CubicalComplexExamples()