passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_x86_64.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_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,2616 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: needs sage.graphs
|
|
3
|
+
r"""
|
|
4
|
+
Finite polyhedral complexes
|
|
5
|
+
|
|
6
|
+
This module implements the basic structure of finite polyhedral complexes.
|
|
7
|
+
For more information, see :class:`PolyhedralComplex`.
|
|
8
|
+
|
|
9
|
+
AUTHORS:
|
|
10
|
+
|
|
11
|
+
- Yuan Zhou (2021-05): initial implementation
|
|
12
|
+
|
|
13
|
+
List of PolyhedralComplex methods
|
|
14
|
+
---------------------------------
|
|
15
|
+
|
|
16
|
+
**Maximal cells and cells**
|
|
17
|
+
|
|
18
|
+
.. csv-table::
|
|
19
|
+
:class: contentstable
|
|
20
|
+
:widths: 30, 70
|
|
21
|
+
:delim: |
|
|
22
|
+
|
|
23
|
+
:meth:`~PolyhedralComplex.maximal_cells` | Return the dictionary of the maximal cells in this polyhedral complex.
|
|
24
|
+
:meth:`~PolyhedralComplex.maximal_cell_iterator` | Return an iterator over maximal cells in this polyhedral complex.
|
|
25
|
+
:meth:`~PolyhedralComplex.maximal_cells_sorted` | Return the sorted list of all maximal cells in this polyhedral complex.
|
|
26
|
+
:meth:`~PolyhedralComplex.n_maximal_cells` | List the maximal cells of dimension `n` in this polyhedral complex.
|
|
27
|
+
:meth:`~PolyhedralComplex._n_maximal_cells_sorted` | Return the sorted list of maximal cells of dim `n` in this complex.
|
|
28
|
+
:meth:`~PolyhedralComplex.is_maximal_cell` | Return ``True`` if the given cell is a maximal cell in this complex.
|
|
29
|
+
:meth:`~PolyhedralComplex.cells` | Return the dictionary of the cells in this polyhedral complex.
|
|
30
|
+
:meth:`~PolyhedralComplex.cell_iterator` | Return an iterator over cells in this polyhedral complex.
|
|
31
|
+
:meth:`~PolyhedralComplex.cells_sorted` | Return the sorted list of all cells in this polyhedral complex.
|
|
32
|
+
:meth:`~sage.topology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex.
|
|
33
|
+
:meth:`~PolyhedralComplex._n_cells_sorted` | Return the sorted list of `n`-cells in this polyhedral complex.
|
|
34
|
+
:meth:`~PolyhedralComplex.is_cell` | Return ``True`` if the given cell is in this polyhedral complex.
|
|
35
|
+
:meth:`~PolyhedralComplex.face_poset` | Return the poset of nonempty cells in the polyhedral complex.
|
|
36
|
+
:meth:`~PolyhedralComplex.relative_boundary_cells` | List the maximal cells on the boundary of the polyhedral complex.
|
|
37
|
+
|
|
38
|
+
**Properties of the polyhedral complex**
|
|
39
|
+
|
|
40
|
+
.. csv-table::
|
|
41
|
+
:class: contentstable
|
|
42
|
+
:widths: 30, 70
|
|
43
|
+
:delim: |
|
|
44
|
+
|
|
45
|
+
:meth:`~PolyhedralComplex.dimension` | Return the dimension of the polyhedral complex.
|
|
46
|
+
:meth:`~PolyhedralComplex.ambient_dimension` | Return the ambient dimension of the polyhedral complex.
|
|
47
|
+
:meth:`~PolyhedralComplex.is_pure` | Return ``True`` if the polyhedral complex is pure.
|
|
48
|
+
:meth:`~PolyhedralComplex.is_full_dimensional` | Return ``True`` if the polyhedral complex is full dimensional.
|
|
49
|
+
:meth:`~PolyhedralComplex.is_compact` | Return ``True`` if the polyhedral complex is bounded.
|
|
50
|
+
:meth:`~PolyhedralComplex.is_connected` | Return ``True`` if the polyhedral complex is connected.
|
|
51
|
+
:meth:`~PolyhedralComplex.is_subcomplex` | Return ``True`` if this complex is a subcomplex of the other.
|
|
52
|
+
:meth:`~PolyhedralComplex.is_convex` | Return ``True`` if the polyhedral complex is convex.
|
|
53
|
+
:meth:`~PolyhedralComplex.is_mutable` | Return ``True`` if the polyhedral complex is mutable.
|
|
54
|
+
:meth:`~PolyhedralComplex.is_immutable` | Return ``True`` if the polyhedral complex is not mutable.
|
|
55
|
+
:meth:`~PolyhedralComplex.is_simplicial_complex` | Return ``True`` if the polyhedral complex is a simplicial complex.
|
|
56
|
+
:meth:`~PolyhedralComplex.is_polyhedral_fan` | Return ``True`` if the polyhedral complex is a fan.
|
|
57
|
+
:meth:`~PolyhedralComplex.is_simplicial_fan` | Return ``True`` if the polyhedral complex is a simplicial fan.
|
|
58
|
+
|
|
59
|
+
**New polyhedral complexes from old ones**
|
|
60
|
+
|
|
61
|
+
.. csv-table::
|
|
62
|
+
:class: contentstable
|
|
63
|
+
:widths: 30, 70
|
|
64
|
+
:delim: |
|
|
65
|
+
|
|
66
|
+
:meth:`~PolyhedralComplex.connected_component` | Return the connected component containing a cell as a subcomplex.
|
|
67
|
+
:meth:`~PolyhedralComplex.connected_components` | Return the connected components of this polyhedral complex.
|
|
68
|
+
:meth:`~PolyhedralComplex.n_skeleton` | Return the `n`-skeleton of this polyhedral complex.
|
|
69
|
+
:meth:`~PolyhedralComplex.stratify` | Return the (pure) subcomplex formed by the maximal cells of dim `n` in this complex.
|
|
70
|
+
:meth:`~PolyhedralComplex.boundary_subcomplex` | Return the boundary subcomplex of this polyhedral complex.
|
|
71
|
+
:meth:`~PolyhedralComplex.product` | Return the (Cartesian) product of this polyhedral complex with another one.
|
|
72
|
+
:meth:`~PolyhedralComplex.disjoint_union` | Return the disjoint union of this polyhedral complex with another one.
|
|
73
|
+
:meth:`~PolyhedralComplex.union` | Return the union of this polyhedral complex with another one.
|
|
74
|
+
:meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one.
|
|
75
|
+
:meth:`~PolyhedralComplex.subdivide` | Return a new polyhedral complex (with option ``make_simplicial``) subdividing this one.
|
|
76
|
+
|
|
77
|
+
**Update polyhedral complex**
|
|
78
|
+
|
|
79
|
+
.. csv-table::
|
|
80
|
+
:class: contentstable
|
|
81
|
+
:widths: 30, 70
|
|
82
|
+
:delim: |
|
|
83
|
+
|
|
84
|
+
:meth:`~PolyhedralComplex.set_immutable` | Make this polyhedral complex immutable.
|
|
85
|
+
:meth:`~PolyhedralComplex.add_cell` | Add a cell to this polyhedral complex.
|
|
86
|
+
:meth:`~PolyhedralComplex.remove_cell` | Remove a cell from this polyhedral complex.
|
|
87
|
+
|
|
88
|
+
**Miscellaneous**
|
|
89
|
+
|
|
90
|
+
.. csv-table::
|
|
91
|
+
:class: contentstable
|
|
92
|
+
:widths: 30, 70
|
|
93
|
+
:delim: |
|
|
94
|
+
|
|
95
|
+
:meth:`~PolyhedralComplex.plot` | Return a Graphic object showing the plot of polyhedral complex.
|
|
96
|
+
:meth:`~PolyhedralComplex.graph` | Return a directed graph corresponding to the 1-skeleton of this polyhedral complex, given that it is bounded.
|
|
97
|
+
:meth:`~PolyhedralComplex.union_as_polyhedron` | Return a ``Polyhedron`` which is the union of cells in this polyhedral complex, given that it is convex.
|
|
98
|
+
|
|
99
|
+
Classes and functions
|
|
100
|
+
---------------------
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
# ****************************************************************************
|
|
104
|
+
# Copyright (C) 2021 Yuan Zhou <yuan.zhou@uky.edu>
|
|
105
|
+
#
|
|
106
|
+
# This program is free software: you can redistribute it and/or modify
|
|
107
|
+
# it under the terms of the GNU General Public License as published by
|
|
108
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
109
|
+
# (at your option) any later version.
|
|
110
|
+
# https://www.gnu.org/licenses/
|
|
111
|
+
# ****************************************************************************
|
|
112
|
+
|
|
113
|
+
from copy import copy
|
|
114
|
+
|
|
115
|
+
import sage.geometry.abc
|
|
116
|
+
|
|
117
|
+
from sage.topology.cell_complex import GenericCellComplex
|
|
118
|
+
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
119
|
+
from sage.modules.free_module_element import vector
|
|
120
|
+
from sage.rings.integer_ring import ZZ
|
|
121
|
+
from sage.graphs.graph import Graph
|
|
122
|
+
from sage.combinat.posets.posets import Poset
|
|
123
|
+
from sage.combinat.subset import powerset
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class PolyhedralComplex(GenericCellComplex):
|
|
127
|
+
r"""
|
|
128
|
+
A polyhedral complex.
|
|
129
|
+
|
|
130
|
+
A **polyhedral complex** `PC` is a collection of polyhedra in a certain
|
|
131
|
+
ambient space `\RR^n` such that the following hold.
|
|
132
|
+
|
|
133
|
+
- If a polyhedron `P` is in `PC`, then all the faces of `P` are in `PC`.
|
|
134
|
+
|
|
135
|
+
- If polyhedra `P` and `Q` are in `PC`, then `P \cap Q` is either empty
|
|
136
|
+
or a face of both `P` and `Q`.
|
|
137
|
+
|
|
138
|
+
In this context, a "polyhedron" means the geometric realization
|
|
139
|
+
of a polyhedron. This is in contrast to :mod:`simplicial complex
|
|
140
|
+
<sage.topology.simplicial_complex>`, whose cells are abstract simplices.
|
|
141
|
+
The concept of a polyhedral complex generalizes that of a **geometric**
|
|
142
|
+
simplicial complex.
|
|
143
|
+
|
|
144
|
+
.. NOTE::
|
|
145
|
+
|
|
146
|
+
This class derives from
|
|
147
|
+
:class:`~sage.topology.cell_complex.GenericCellComplex`, and so
|
|
148
|
+
inherits its methods. Some of those methods are not listed here;
|
|
149
|
+
see the :mod:`Generic Cell Complex <sage.topology.cell_complex>`
|
|
150
|
+
page instead.
|
|
151
|
+
|
|
152
|
+
INPUT:
|
|
153
|
+
|
|
154
|
+
- ``maximal_cells`` -- list, tuple, or dictionary (indexed by
|
|
155
|
+
dimension) of cells of the Complex. Each cell is of class
|
|
156
|
+
:class:`Polyhedron` of the same ambient dimension. To set up a
|
|
157
|
+
:class:PolyhedralComplex, it is sufficient to provide the maximal
|
|
158
|
+
faces. Use keyword argument ``partial=True`` to set up a partial
|
|
159
|
+
polyhedral complex, which is a subset of the faces (viewed as
|
|
160
|
+
relatively open) of a polyhedral complex that is not necessarily
|
|
161
|
+
closed under taking intersection.
|
|
162
|
+
|
|
163
|
+
- ``maximality_check`` -- boolean (default: ``True``);
|
|
164
|
+
if ``True``, then the constructor checks that each given
|
|
165
|
+
maximal cell is indeed maximal, and ignores those that are not
|
|
166
|
+
|
|
167
|
+
- ``face_to_face_check`` -- boolean (default: ``False``);
|
|
168
|
+
if ``True``, then the constructor checks whether the cells
|
|
169
|
+
are face-to-face, and it raises a :exc:`ValueError` if they are not
|
|
170
|
+
|
|
171
|
+
- ``is_mutable`` and ``is_immutable`` -- boolean (default: ``True`` and
|
|
172
|
+
``False`` respectively); set ``is_mutable=False`` or ``is_immutable=True``
|
|
173
|
+
to make this polyhedral complex immutable
|
|
174
|
+
|
|
175
|
+
- ``backend`` -- string (optional); the name of the backend used for
|
|
176
|
+
computations on Sage polyhedra; if it is not given, then each cell has
|
|
177
|
+
its own backend; otherwise it must be one of the following:
|
|
178
|
+
|
|
179
|
+
* ``'ppl'`` -- the Parma Polyhedra Library
|
|
180
|
+
|
|
181
|
+
* ``'cdd'`` -- CDD
|
|
182
|
+
|
|
183
|
+
* ``'normaliz'`` -- normaliz
|
|
184
|
+
|
|
185
|
+
* ``'polymake'`` -- polymake
|
|
186
|
+
|
|
187
|
+
* ``'field'`` -- a generic Sage implementation
|
|
188
|
+
|
|
189
|
+
- ``ambient_dim`` -- integer (optional); used to set up an empty
|
|
190
|
+
complex in the intended ambient space
|
|
191
|
+
|
|
192
|
+
EXAMPLES::
|
|
193
|
+
|
|
194
|
+
sage: pc = PolyhedralComplex([
|
|
195
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1/7, 2/7)]),
|
|
196
|
+
....: Polyhedron(vertices=[(1/7, 2/7), (0, 0), (0, 1/4)])])
|
|
197
|
+
sage: [p.Vrepresentation() for p in pc.cells_sorted()]
|
|
198
|
+
[(A vertex at (0, 0), A vertex at (0, 1/4), A vertex at (1/7, 2/7)),
|
|
199
|
+
(A vertex at (0, 0), A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)),
|
|
200
|
+
(A vertex at (0, 0), A vertex at (0, 1/4)),
|
|
201
|
+
(A vertex at (0, 0), A vertex at (1/7, 2/7)),
|
|
202
|
+
(A vertex at (0, 0), A vertex at (1/3, 1/3)),
|
|
203
|
+
(A vertex at (0, 1/4), A vertex at (1/7, 2/7)),
|
|
204
|
+
(A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)),
|
|
205
|
+
(A vertex at (0, 0),),
|
|
206
|
+
(A vertex at (0, 1/4),),
|
|
207
|
+
(A vertex at (1/7, 2/7),),
|
|
208
|
+
(A vertex at (1/3, 1/3),)]
|
|
209
|
+
sage: pc.plot() # needs sage.plot
|
|
210
|
+
Graphics object consisting of 10 graphics primitives
|
|
211
|
+
sage: pc.is_pure()
|
|
212
|
+
True
|
|
213
|
+
sage: pc.is_full_dimensional()
|
|
214
|
+
True
|
|
215
|
+
sage: pc.is_compact()
|
|
216
|
+
True
|
|
217
|
+
sage: pc.boundary_subcomplex()
|
|
218
|
+
Polyhedral complex with 4 maximal cells
|
|
219
|
+
sage: pc.is_convex()
|
|
220
|
+
True
|
|
221
|
+
sage: pc.union_as_polyhedron().Hrepresentation()
|
|
222
|
+
(An inequality (1, -4) x + 1 >= 0,
|
|
223
|
+
An inequality (-1, 1) x + 0 >= 0,
|
|
224
|
+
An inequality (1, 0) x + 0 >= 0)
|
|
225
|
+
sage: pc.face_poset()
|
|
226
|
+
Finite poset containing 11 elements
|
|
227
|
+
sage: pc.is_connected()
|
|
228
|
+
True
|
|
229
|
+
sage: pc.connected_component() == pc
|
|
230
|
+
True
|
|
231
|
+
|
|
232
|
+
TESTS:
|
|
233
|
+
|
|
234
|
+
Check that non-maximal cells are ignored if ``maximality_check=True``::
|
|
235
|
+
|
|
236
|
+
sage: pc = PolyhedralComplex([
|
|
237
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
238
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0)]) ])
|
|
239
|
+
sage: pc.maximal_cells()
|
|
240
|
+
{2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}}
|
|
241
|
+
|
|
242
|
+
Check that non face-to-face can be detected::
|
|
243
|
+
|
|
244
|
+
sage: PolyhedralComplex([
|
|
245
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
246
|
+
....: Polyhedron(vertices=[(2, 2), (0, 0)]) ],
|
|
247
|
+
....: face_to_face_check=True)
|
|
248
|
+
Traceback (most recent call last):
|
|
249
|
+
...
|
|
250
|
+
ValueError: the given cells are not face-to-face
|
|
251
|
+
|
|
252
|
+
Check that all the cells must have the same ambient dimension::
|
|
253
|
+
|
|
254
|
+
sage: PolyhedralComplex([
|
|
255
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
256
|
+
....: Polyhedron(vertices=[[2], [0]]) ])
|
|
257
|
+
Traceback (most recent call last):
|
|
258
|
+
...
|
|
259
|
+
ValueError: the given cells are not polyhedra in the same ambient space
|
|
260
|
+
|
|
261
|
+
Check that backend is passed to all the cells::
|
|
262
|
+
|
|
263
|
+
sage: P = Polyhedron(vertices=[(0, 0), (1, 1)])
|
|
264
|
+
sage: P.backend()
|
|
265
|
+
'ppl'
|
|
266
|
+
|
|
267
|
+
sage: # needs cddexec_gmp
|
|
268
|
+
sage: pc = PolyhedralComplex([P], backend='cdd')
|
|
269
|
+
sage: Q = pc.maximal_cells_sorted()[0]
|
|
270
|
+
sage: Q.backend()
|
|
271
|
+
'cdd'
|
|
272
|
+
"""
|
|
273
|
+
def __init__(self, maximal_cells=None, backend=None, maximality_check=True,
|
|
274
|
+
face_to_face_check=False, is_mutable=True, is_immutable=False,
|
|
275
|
+
ambient_dim=None) -> None:
|
|
276
|
+
r"""
|
|
277
|
+
Define a PolyhedralComplex.
|
|
278
|
+
|
|
279
|
+
See ``PolyhedralComplex`` for more information.
|
|
280
|
+
|
|
281
|
+
EXAMPLES::
|
|
282
|
+
|
|
283
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])])
|
|
284
|
+
sage: pc
|
|
285
|
+
Polyhedral complex with 1 maximal cell
|
|
286
|
+
sage: TestSuite(pc).run()
|
|
287
|
+
"""
|
|
288
|
+
self._backend = backend
|
|
289
|
+
if maximal_cells is None:
|
|
290
|
+
cells_dict = {}
|
|
291
|
+
elif isinstance(maximal_cells, (list, tuple)):
|
|
292
|
+
if backend:
|
|
293
|
+
maximal_cells = [p.base_extend(p.base_ring(), backend)
|
|
294
|
+
for p in maximal_cells]
|
|
295
|
+
cells_dict = cells_list_to_cells_dict(maximal_cells)
|
|
296
|
+
elif isinstance(maximal_cells, dict):
|
|
297
|
+
cells_dict = {}
|
|
298
|
+
for k, l in maximal_cells.items():
|
|
299
|
+
if backend:
|
|
300
|
+
cells_dict[k] = {p.base_extend(p.base_ring(), backend)
|
|
301
|
+
for p in l}
|
|
302
|
+
else:
|
|
303
|
+
cells_dict[k] = set(l)
|
|
304
|
+
else:
|
|
305
|
+
raise ValueError("the maximal cells are not given in correct form")
|
|
306
|
+
if not cells_dict:
|
|
307
|
+
self._dim = -1
|
|
308
|
+
if ambient_dim is None:
|
|
309
|
+
ambient_dim = -1
|
|
310
|
+
else:
|
|
311
|
+
self._dim = max(cells_dict.keys())
|
|
312
|
+
if ambient_dim is None:
|
|
313
|
+
ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim()
|
|
314
|
+
self._ambient_dim = ambient_dim
|
|
315
|
+
self._maximal_cells = cells_dict
|
|
316
|
+
if not all((isinstance(cell, sage.geometry.abc.Polyhedron) and
|
|
317
|
+
cell.ambient_dim() == self._ambient_dim)
|
|
318
|
+
for cell in self.maximal_cell_iterator()):
|
|
319
|
+
raise ValueError("the given cells are not polyhedra " +
|
|
320
|
+
"in the same ambient space")
|
|
321
|
+
# initialize the attributes
|
|
322
|
+
self._is_convex = None
|
|
323
|
+
self._polyhedron = None
|
|
324
|
+
self._maximal_cells_sorted = None # needed for hash
|
|
325
|
+
self._cells = None
|
|
326
|
+
self._face_poset = None
|
|
327
|
+
|
|
328
|
+
if maximality_check:
|
|
329
|
+
self.cells() # compute self._cells and self._face_poset
|
|
330
|
+
self._maximal_cells = cells_list_to_cells_dict(
|
|
331
|
+
self._face_poset.maximal_elements())
|
|
332
|
+
if face_to_face_check:
|
|
333
|
+
poset = self.face_poset()
|
|
334
|
+
maximal_cells = poset.maximal_elements() # a list
|
|
335
|
+
for i in range(len(maximal_cells)):
|
|
336
|
+
p = maximal_cells[i]
|
|
337
|
+
for j in range(i, len(maximal_cells)):
|
|
338
|
+
q = maximal_cells[j]
|
|
339
|
+
r = p.intersection(q)
|
|
340
|
+
if not (r.is_empty() or (r in poset) and
|
|
341
|
+
poset.is_gequal(p, r) and poset.is_gequal(q, r)):
|
|
342
|
+
raise ValueError("the given cells are not face-to-face")
|
|
343
|
+
self._is_immutable = False
|
|
344
|
+
if not is_mutable or is_immutable:
|
|
345
|
+
self.set_immutable()
|
|
346
|
+
|
|
347
|
+
def cells(self, subcomplex=None) -> dict:
|
|
348
|
+
"""
|
|
349
|
+
The cells of this polyhedral complex, in the form of a dictionary:
|
|
350
|
+
the keys are integers, representing dimension, and the value
|
|
351
|
+
associated to an integer `d` is the set of `d`-cells.
|
|
352
|
+
|
|
353
|
+
INPUT:
|
|
354
|
+
|
|
355
|
+
- ``subcomplex`` -- (optional) if a subcomplex is given then
|
|
356
|
+
return the cells which are **not** in this subcomplex
|
|
357
|
+
|
|
358
|
+
EXAMPLES::
|
|
359
|
+
|
|
360
|
+
sage: pc = PolyhedralComplex([
|
|
361
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
362
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
363
|
+
sage: list(pc.cells().keys())
|
|
364
|
+
[2, 1, 0]
|
|
365
|
+
"""
|
|
366
|
+
if subcomplex is not None:
|
|
367
|
+
raise NotImplementedError("providing subcomplex is not implemented")
|
|
368
|
+
if self._cells is not None:
|
|
369
|
+
return self._cells
|
|
370
|
+
maximal_cells = self.maximal_cells()
|
|
371
|
+
cells = {}
|
|
372
|
+
covers = {}
|
|
373
|
+
for k in range(self._dim, -1, -1):
|
|
374
|
+
if k in maximal_cells:
|
|
375
|
+
if k not in cells:
|
|
376
|
+
cells[k] = set()
|
|
377
|
+
cells[k].update(maximal_cells[k])
|
|
378
|
+
if k in cells:
|
|
379
|
+
for cell in cells[k]:
|
|
380
|
+
if cell not in covers:
|
|
381
|
+
covers[cell] = []
|
|
382
|
+
for facet in cell.facets():
|
|
383
|
+
p = facet.as_polyhedron()
|
|
384
|
+
if p not in covers:
|
|
385
|
+
covers[p] = []
|
|
386
|
+
covers[p].append(cell)
|
|
387
|
+
if (k-1) not in cells:
|
|
388
|
+
cells[k-1] = set()
|
|
389
|
+
cells[k-1].add(p)
|
|
390
|
+
self._face_poset = Poset(covers)
|
|
391
|
+
self._cells = cells
|
|
392
|
+
return self._cells
|
|
393
|
+
|
|
394
|
+
def cell_iterator(self, increasing=True):
|
|
395
|
+
"""
|
|
396
|
+
An iterator for the cells in this polyhedral complex.
|
|
397
|
+
|
|
398
|
+
INPUT:
|
|
399
|
+
|
|
400
|
+
- ``increasing`` -- boolean (default: ``True``); if ``True``, return
|
|
401
|
+
cells in increasing order of dimension, thus starting with the
|
|
402
|
+
zero-dimensional cells; otherwise it returns cells in decreasing
|
|
403
|
+
order of dimension
|
|
404
|
+
|
|
405
|
+
.. NOTE::
|
|
406
|
+
|
|
407
|
+
Among the cells of a fixed dimension, there is no sorting.
|
|
408
|
+
|
|
409
|
+
EXAMPLES::
|
|
410
|
+
|
|
411
|
+
sage: pc = PolyhedralComplex([
|
|
412
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
413
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
414
|
+
sage: len(list(pc.cell_iterator()))
|
|
415
|
+
11
|
|
416
|
+
"""
|
|
417
|
+
cells = self.cells()
|
|
418
|
+
dim_index = range(self.dimension() + 1)
|
|
419
|
+
if not increasing:
|
|
420
|
+
dim_index = reversed(dim_index)
|
|
421
|
+
for d in dim_index:
|
|
422
|
+
if d in cells:
|
|
423
|
+
yield from cells[d]
|
|
424
|
+
|
|
425
|
+
def _n_cells_sorted(self, n, subcomplex=None) -> list:
|
|
426
|
+
"""
|
|
427
|
+
Sorted list of cells of dimension ``n`` of this polyhedral complex.
|
|
428
|
+
|
|
429
|
+
INPUT:
|
|
430
|
+
|
|
431
|
+
- ``n`` -- nonnegative integer; the dimension
|
|
432
|
+
- ``subcomplex`` -- (optional) if a subcomplex is given then
|
|
433
|
+
return the cells which are **not** in this subcomplex
|
|
434
|
+
|
|
435
|
+
EXAMPLES::
|
|
436
|
+
|
|
437
|
+
sage: pc = PolyhedralComplex([
|
|
438
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
439
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
440
|
+
sage: [p.Vrepresentation() for p in pc._n_cells_sorted(1)]
|
|
441
|
+
[(A vertex at (0, 0), A vertex at (0, 2)),
|
|
442
|
+
(A vertex at (0, 0), A vertex at (1, 1)),
|
|
443
|
+
(A vertex at (0, 0), A vertex at (1, 2)),
|
|
444
|
+
(A vertex at (0, 2), A vertex at (1, 2)),
|
|
445
|
+
(A vertex at (1, 1), A vertex at (1, 2))]
|
|
446
|
+
sage: pc._n_cells_sorted(3)
|
|
447
|
+
[]
|
|
448
|
+
"""
|
|
449
|
+
n_cells = self.n_cells(n, subcomplex)
|
|
450
|
+
return sorted(n_cells,
|
|
451
|
+
key=lambda p: (p.vertices(), p.rays(), p.lines()))
|
|
452
|
+
|
|
453
|
+
def cells_sorted(self, subcomplex=None) -> list:
|
|
454
|
+
"""
|
|
455
|
+
The sorted list of the cells of this polyhedral complex
|
|
456
|
+
in non-increasing dimensions.
|
|
457
|
+
|
|
458
|
+
INPUT:
|
|
459
|
+
|
|
460
|
+
- ``subcomplex`` -- (optional) if a subcomplex is given then
|
|
461
|
+
return the cells which are **not** in this subcomplex
|
|
462
|
+
|
|
463
|
+
EXAMPLES::
|
|
464
|
+
|
|
465
|
+
sage: pc = PolyhedralComplex([
|
|
466
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
467
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
468
|
+
sage: len(pc.cells_sorted())
|
|
469
|
+
11
|
|
470
|
+
sage: pc.cells_sorted()[0].Vrepresentation()
|
|
471
|
+
(A vertex at (0, 0), A vertex at (0, 2), A vertex at (1, 2))
|
|
472
|
+
"""
|
|
473
|
+
cells = []
|
|
474
|
+
for n in range(self._dim, -1, -1):
|
|
475
|
+
cells.extend(self._n_cells_sorted(n, subcomplex))
|
|
476
|
+
return cells
|
|
477
|
+
|
|
478
|
+
def maximal_cells(self) -> dict:
|
|
479
|
+
"""
|
|
480
|
+
The maximal cells of this polyhedral complex, in the form of a
|
|
481
|
+
dictionary: the keys are integers, representing dimension, and the
|
|
482
|
+
value associated to an integer `d` is the set of `d`-maximal cells.
|
|
483
|
+
|
|
484
|
+
.. WARNING::
|
|
485
|
+
|
|
486
|
+
This may give the wrong answer if the polyhedral complex
|
|
487
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
488
|
+
|
|
489
|
+
EXAMPLES::
|
|
490
|
+
|
|
491
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
492
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
493
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
494
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
495
|
+
sage: len(pc.maximal_cells()[2])
|
|
496
|
+
2
|
|
497
|
+
sage: 1 in pc.maximal_cells()
|
|
498
|
+
False
|
|
499
|
+
|
|
500
|
+
Wrong answer due to ``maximality_check=False``::
|
|
501
|
+
|
|
502
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
503
|
+
....: maximality_check=False)
|
|
504
|
+
sage: len(pc_invalid.maximal_cells()[1])
|
|
505
|
+
1
|
|
506
|
+
"""
|
|
507
|
+
return self._maximal_cells
|
|
508
|
+
|
|
509
|
+
def maximal_cell_iterator(self, increasing=False):
|
|
510
|
+
r"""
|
|
511
|
+
An iterator for the maximal cells in this polyhedral complex.
|
|
512
|
+
|
|
513
|
+
INPUT:
|
|
514
|
+
|
|
515
|
+
- ``increasing`` -- boolean (default: ``False``); if ``True``, return
|
|
516
|
+
maximal cells in increasing order of dimension.
|
|
517
|
+
Otherwise it returns cells in decreasing order of dimension.
|
|
518
|
+
|
|
519
|
+
.. NOTE::
|
|
520
|
+
|
|
521
|
+
Among the cells of a fixed dimension, there is no sorting.
|
|
522
|
+
|
|
523
|
+
.. WARNING::
|
|
524
|
+
|
|
525
|
+
This may give the wrong answer if the polyhedral complex
|
|
526
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
527
|
+
|
|
528
|
+
EXAMPLES::
|
|
529
|
+
|
|
530
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
531
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
532
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
533
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
534
|
+
sage: len(list(pc.maximal_cell_iterator()))
|
|
535
|
+
2
|
|
536
|
+
|
|
537
|
+
Wrong answer due to ``maximality_check=False``::
|
|
538
|
+
|
|
539
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
540
|
+
....: maximality_check=False)
|
|
541
|
+
sage: len(list(pc_invalid.maximal_cell_iterator()))
|
|
542
|
+
3
|
|
543
|
+
"""
|
|
544
|
+
maximal_cells = self.maximal_cells()
|
|
545
|
+
dim_index = range(-1, self.dimension() + 1)
|
|
546
|
+
if not increasing:
|
|
547
|
+
dim_index = reversed(dim_index)
|
|
548
|
+
for d in dim_index:
|
|
549
|
+
if d in maximal_cells:
|
|
550
|
+
yield from maximal_cells[d]
|
|
551
|
+
|
|
552
|
+
def n_maximal_cells(self, n) -> list:
|
|
553
|
+
r"""
|
|
554
|
+
List of maximal cells of dimension ``n`` of this polyhedral complex.
|
|
555
|
+
|
|
556
|
+
INPUT:
|
|
557
|
+
|
|
558
|
+
- ``n`` -- nonnegative integer; the dimension
|
|
559
|
+
|
|
560
|
+
.. NOTE::
|
|
561
|
+
|
|
562
|
+
The resulting list need not be sorted. If you want a sorted
|
|
563
|
+
list of `n`-cells, use :meth:`_n_maximal_cells_sorted`.
|
|
564
|
+
|
|
565
|
+
.. WARNING::
|
|
566
|
+
|
|
567
|
+
This may give the wrong answer if the polyhedral complex
|
|
568
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
569
|
+
|
|
570
|
+
EXAMPLES::
|
|
571
|
+
|
|
572
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
573
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
574
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
575
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
576
|
+
sage: len(pc.n_maximal_cells(2))
|
|
577
|
+
2
|
|
578
|
+
sage: len(pc.n_maximal_cells(1))
|
|
579
|
+
0
|
|
580
|
+
|
|
581
|
+
Wrong answer due to ``maximality_check=False``::
|
|
582
|
+
|
|
583
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
584
|
+
....: maximality_check=False)
|
|
585
|
+
sage: len(pc_invalid.n_maximal_cells(1))
|
|
586
|
+
1
|
|
587
|
+
"""
|
|
588
|
+
if n in self.maximal_cells():
|
|
589
|
+
return list(self.maximal_cells()[n])
|
|
590
|
+
return []
|
|
591
|
+
|
|
592
|
+
def _n_maximal_cells_sorted(self, n) -> list:
|
|
593
|
+
"""
|
|
594
|
+
Sorted list of maximal cells of dimension ``n`` of this polyhedral
|
|
595
|
+
complex.
|
|
596
|
+
|
|
597
|
+
INPUT:
|
|
598
|
+
|
|
599
|
+
- ``n`` -- nonnegative integer; the dimension
|
|
600
|
+
|
|
601
|
+
.. WARNING::
|
|
602
|
+
|
|
603
|
+
This may give the wrong answer if the polyhedral complex
|
|
604
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
605
|
+
|
|
606
|
+
EXAMPLES::
|
|
607
|
+
|
|
608
|
+
sage: pc = PolyhedralComplex([
|
|
609
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
610
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
611
|
+
sage: pc._n_maximal_cells_sorted(2)[0].vertices_list()
|
|
612
|
+
[[0, 0], [0, 2], [1, 2]]
|
|
613
|
+
"""
|
|
614
|
+
n_maximal_cells = self.n_maximal_cells(n)
|
|
615
|
+
return sorted(n_maximal_cells,
|
|
616
|
+
key=lambda p: (p.vertices(), p.rays(), p.lines()))
|
|
617
|
+
|
|
618
|
+
def maximal_cells_sorted(self) -> list:
|
|
619
|
+
"""
|
|
620
|
+
Return the sorted list of the maximal cells of this polyhedral complex
|
|
621
|
+
by non-increasing dimensions.
|
|
622
|
+
|
|
623
|
+
EXAMPLES::
|
|
624
|
+
|
|
625
|
+
sage: pc = PolyhedralComplex([
|
|
626
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
627
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
628
|
+
sage: [p.vertices_list() for p in pc.maximal_cells_sorted()]
|
|
629
|
+
[[[0, 0], [0, 2], [1, 2]], [[0, 0], [1, 1], [1, 2]]]
|
|
630
|
+
"""
|
|
631
|
+
if self._maximal_cells_sorted is None:
|
|
632
|
+
maximal_cells = []
|
|
633
|
+
for n in range(self._dim, -1, -1):
|
|
634
|
+
maximal_cells.extend(self._n_maximal_cells_sorted(n))
|
|
635
|
+
self._maximal_cells_sorted = maximal_cells
|
|
636
|
+
return self._maximal_cells_sorted
|
|
637
|
+
|
|
638
|
+
def is_maximal_cell(self, c) -> bool:
|
|
639
|
+
"""
|
|
640
|
+
Return whether the given cell ``c`` is a maximal cell of ``self``.
|
|
641
|
+
|
|
642
|
+
.. WARNING::
|
|
643
|
+
|
|
644
|
+
This may give the wrong answer if the polyhedral complex
|
|
645
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
646
|
+
|
|
647
|
+
EXAMPLES::
|
|
648
|
+
|
|
649
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
650
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
651
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
652
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
653
|
+
sage: pc.is_maximal_cell(p1)
|
|
654
|
+
True
|
|
655
|
+
sage: pc.is_maximal_cell(p3)
|
|
656
|
+
False
|
|
657
|
+
|
|
658
|
+
Wrong answer due to ``maximality_check=False``::
|
|
659
|
+
|
|
660
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
661
|
+
....: maximality_check=False)
|
|
662
|
+
sage: pc_invalid.is_maximal_cell(p3)
|
|
663
|
+
True
|
|
664
|
+
"""
|
|
665
|
+
d = c.dimension()
|
|
666
|
+
# return (c in self.n_maximal_cells(d)) # use set instead of list
|
|
667
|
+
return (d in self.maximal_cells()) and (c in self.maximal_cells()[d])
|
|
668
|
+
|
|
669
|
+
def is_cell(self, c) -> bool:
|
|
670
|
+
"""
|
|
671
|
+
Return whether the given cell ``c`` is a cell of ``self``.
|
|
672
|
+
|
|
673
|
+
EXAMPLES::
|
|
674
|
+
|
|
675
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
676
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
677
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
678
|
+
sage: pc = PolyhedralComplex([p1, p2])
|
|
679
|
+
sage: pc.is_cell(p3)
|
|
680
|
+
True
|
|
681
|
+
sage: pc.is_cell(Polyhedron(vertices=[(0, 0)]))
|
|
682
|
+
True
|
|
683
|
+
"""
|
|
684
|
+
d = c.dimension()
|
|
685
|
+
return (d in self.cells()) and (c in self.cells()[d])
|
|
686
|
+
|
|
687
|
+
def dimension(self):
|
|
688
|
+
"""
|
|
689
|
+
The dimension of this cell complex: the maximum
|
|
690
|
+
dimension of its cells.
|
|
691
|
+
|
|
692
|
+
EXAMPLES::
|
|
693
|
+
|
|
694
|
+
sage: pc = PolyhedralComplex([
|
|
695
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
696
|
+
....: Polyhedron(vertices=[(1, 2), (0, 2)]) ])
|
|
697
|
+
sage: pc.dimension()
|
|
698
|
+
2
|
|
699
|
+
sage: empty_pc = PolyhedralComplex([])
|
|
700
|
+
sage: empty_pc.dimension()
|
|
701
|
+
-1
|
|
702
|
+
"""
|
|
703
|
+
return self._dim
|
|
704
|
+
|
|
705
|
+
def ambient_dimension(self):
|
|
706
|
+
"""
|
|
707
|
+
The ambient dimension of this cell complex: the ambient
|
|
708
|
+
dimension of each of its cells.
|
|
709
|
+
|
|
710
|
+
EXAMPLES::
|
|
711
|
+
|
|
712
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 2, 3)])])
|
|
713
|
+
sage: pc.ambient_dimension()
|
|
714
|
+
3
|
|
715
|
+
sage: empty_pc = PolyhedralComplex([])
|
|
716
|
+
sage: empty_pc.ambient_dimension()
|
|
717
|
+
-1
|
|
718
|
+
sage: pc0 = PolyhedralComplex(ambient_dim=2)
|
|
719
|
+
sage: pc0.ambient_dimension()
|
|
720
|
+
2
|
|
721
|
+
"""
|
|
722
|
+
return self._ambient_dim
|
|
723
|
+
|
|
724
|
+
def plot(self, **kwds):
|
|
725
|
+
"""
|
|
726
|
+
Return a plot of the polyhedral complex, if it is of dim at most 3.
|
|
727
|
+
|
|
728
|
+
INPUT:
|
|
729
|
+
|
|
730
|
+
- ``explosion_factor`` -- (default: 0) if positive, separate the cells of
|
|
731
|
+
the complex by extra space. In this case, the following keyword arguments
|
|
732
|
+
can be passed to :func:`exploded_plot`:
|
|
733
|
+
|
|
734
|
+
- ``center`` -- (default: ``None``, denoting the origin) the center of explosion
|
|
735
|
+
- ``sticky_vertices`` -- (default: ``False``) boolean or dict;
|
|
736
|
+
whether to draw line segments between shared vertices of the given polyhedra.
|
|
737
|
+
A dict gives options for :func:`sage.plot.line`.
|
|
738
|
+
- ``sticky_center`` -- (default: ``True``) boolean or dict. When ``center`` is
|
|
739
|
+
a vertex of some of the polyhedra, whether to draw line segments connecting the
|
|
740
|
+
``center`` to the shifted copies of these vertices.
|
|
741
|
+
A dict gives options for :func:`sage.plot.line`.
|
|
742
|
+
|
|
743
|
+
- ``color`` -- (default: ``None``) if ``'rainbow'``, assign a different color
|
|
744
|
+
to every maximal cell; otherwise, passed on to
|
|
745
|
+
:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`.
|
|
746
|
+
|
|
747
|
+
- other keyword arguments are passed on to
|
|
748
|
+
:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`.
|
|
749
|
+
|
|
750
|
+
EXAMPLES::
|
|
751
|
+
|
|
752
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
753
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
754
|
+
sage: p3 = Polyhedron(vertices=[(0, 0), (0, 2), (-1, 1)])
|
|
755
|
+
sage: pc1 = PolyhedralComplex([p1, p2, p3, -p1, -p2, -p3])
|
|
756
|
+
sage: bb = dict(xmin=-2, xmax=2, ymin=-3, ymax=3, axes=False)
|
|
757
|
+
sage: g0 = pc1.plot(color='rainbow', **bb) # needs sage.plot
|
|
758
|
+
sage: g1 = pc1.plot(explosion_factor=0.5, **bb) # needs cddexec sage.plot
|
|
759
|
+
sage: g2 = pc1.plot(explosion_factor=1, color='rainbow', alpha=0.5, **bb) # needs sage.plot
|
|
760
|
+
sage: graphics_array([g0, g1, g2]).show(axes=False) # not tested
|
|
761
|
+
|
|
762
|
+
sage: pc2 = PolyhedralComplex([polytopes.hypercube(3)])
|
|
763
|
+
sage: pc3 = pc2.subdivide(new_vertices=[(0, 0, 0)])
|
|
764
|
+
sage: g3 = pc3.plot(explosion_factor=1, color='rainbow', # needs sage.plot
|
|
765
|
+
....: alpha=0.5, axes=False, online=True)
|
|
766
|
+
sage: pc4 = pc2.subdivide(make_simplicial=True)
|
|
767
|
+
sage: g4 = pc4.plot(explosion_factor=1, center=(1, -1, 1), fill='blue', # needs sage.plot
|
|
768
|
+
....: wireframe='white', point={'color':'red', 'size':10},
|
|
769
|
+
....: alpha=0.6, online=True)
|
|
770
|
+
sage: pc5 = PolyhedralComplex([
|
|
771
|
+
....: Polyhedron(rays=[[1,0,0], [0,1,0], [0,0,-1]]),
|
|
772
|
+
....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,-1]]),
|
|
773
|
+
....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,1]]),
|
|
774
|
+
....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,-1]]),
|
|
775
|
+
....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,1]]),
|
|
776
|
+
....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,-1]]),
|
|
777
|
+
....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,1]])])
|
|
778
|
+
sage: g5 = pc5.plot(explosion_factor=0.3, color='rainbow', alpha=0.8, # needs cddexec sage.plot
|
|
779
|
+
....: point={'size': 20}, axes=False, online=True)
|
|
780
|
+
"""
|
|
781
|
+
if self.dimension() > 3:
|
|
782
|
+
raise ValueError("cannot plot in high dimension")
|
|
783
|
+
if kwds.get('explosion_factor', 0):
|
|
784
|
+
return exploded_plot(self.maximal_cell_iterator(), **kwds)
|
|
785
|
+
|
|
786
|
+
from sage.plot.colors import rainbow
|
|
787
|
+
from sage.plot.graphics import Graphics
|
|
788
|
+
|
|
789
|
+
color = kwds.get('color')
|
|
790
|
+
polyhedra = self.maximal_cell_iterator()
|
|
791
|
+
if color == 'rainbow':
|
|
792
|
+
polyhedra = list(polyhedra)
|
|
793
|
+
cell_colors_dict = dict(zip(polyhedra,
|
|
794
|
+
rainbow(len(polyhedra))))
|
|
795
|
+
g = Graphics()
|
|
796
|
+
for cell in polyhedra:
|
|
797
|
+
options = copy(kwds)
|
|
798
|
+
if color == 'rainbow':
|
|
799
|
+
options['color'] = cell_colors_dict[cell]
|
|
800
|
+
g += cell.plot(**options)
|
|
801
|
+
return g
|
|
802
|
+
|
|
803
|
+
def is_pure(self) -> bool:
|
|
804
|
+
"""
|
|
805
|
+
Test if this polyhedral complex is pure.
|
|
806
|
+
|
|
807
|
+
A polyhedral complex is pure if and only if all of its maximal cells
|
|
808
|
+
have the same dimension.
|
|
809
|
+
|
|
810
|
+
.. WARNING::
|
|
811
|
+
|
|
812
|
+
This may give the wrong answer if the polyhedral complex
|
|
813
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
814
|
+
|
|
815
|
+
EXAMPLES::
|
|
816
|
+
|
|
817
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
818
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
819
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
820
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
821
|
+
sage: pc.is_pure()
|
|
822
|
+
True
|
|
823
|
+
|
|
824
|
+
Wrong answer due to ``maximality_check=False``::
|
|
825
|
+
|
|
826
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
827
|
+
....: maximality_check=False)
|
|
828
|
+
sage: pc_invalid.is_pure()
|
|
829
|
+
False
|
|
830
|
+
"""
|
|
831
|
+
return len(self._maximal_cells) == 1
|
|
832
|
+
|
|
833
|
+
def is_full_dimensional(self) -> bool:
|
|
834
|
+
"""
|
|
835
|
+
Return whether this polyhedral complex is full-dimensional.
|
|
836
|
+
|
|
837
|
+
This means that its dimension is equal to its ambient dimension.
|
|
838
|
+
|
|
839
|
+
EXAMPLES::
|
|
840
|
+
|
|
841
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
842
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
843
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
844
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
845
|
+
sage: pc.is_full_dimensional()
|
|
846
|
+
True
|
|
847
|
+
sage: PolyhedralComplex([p3]).is_full_dimensional()
|
|
848
|
+
False
|
|
849
|
+
"""
|
|
850
|
+
return self._dim == self._ambient_dim
|
|
851
|
+
|
|
852
|
+
def __hash__(self) -> int:
|
|
853
|
+
"""
|
|
854
|
+
Compute the hash value of ``self`` using its ``maximal_cells_sorted``.
|
|
855
|
+
|
|
856
|
+
EXAMPLES::
|
|
857
|
+
|
|
858
|
+
sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])
|
|
859
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])
|
|
860
|
+
sage: pc1 = PolyhedralComplex([p1, p2], is_mutable=False)
|
|
861
|
+
sage: hash(pc1) == hash(pc1)
|
|
862
|
+
True
|
|
863
|
+
sage: pc2 = PolyhedralComplex([p2, p1], is_mutable=False)
|
|
864
|
+
sage: hash(pc1) == hash(pc2)
|
|
865
|
+
True
|
|
866
|
+
sage: pc3 = PolyhedralComplex([p1, p2])
|
|
867
|
+
sage: hash(pc3)
|
|
868
|
+
Traceback (most recent call last):
|
|
869
|
+
...
|
|
870
|
+
ValueError: this polyhedral complex must be immutable; call set_immutable()
|
|
871
|
+
"""
|
|
872
|
+
if not self._is_immutable:
|
|
873
|
+
raise ValueError("this polyhedral complex must be immutable; " +
|
|
874
|
+
"call set_immutable()")
|
|
875
|
+
return hash(tuple(self.maximal_cells_sorted()))
|
|
876
|
+
|
|
877
|
+
def __eq__(self, right) -> bool:
|
|
878
|
+
"""
|
|
879
|
+
Two polyhedral complexes are equal iff their maximal cells are equal.
|
|
880
|
+
|
|
881
|
+
EXAMPLES::
|
|
882
|
+
|
|
883
|
+
sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])
|
|
884
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])
|
|
885
|
+
sage: pc1 = PolyhedralComplex([p1, p2])
|
|
886
|
+
sage: pc1 == pc1
|
|
887
|
+
True
|
|
888
|
+
sage: pc2 = PolyhedralComplex([p2, p1])
|
|
889
|
+
sage: pc1 == pc2
|
|
890
|
+
True
|
|
891
|
+
"""
|
|
892
|
+
return isinstance(right, PolyhedralComplex) and (
|
|
893
|
+
self.maximal_cells_sorted() == right.maximal_cells_sorted())
|
|
894
|
+
|
|
895
|
+
def __ne__(self, right) -> bool:
|
|
896
|
+
"""
|
|
897
|
+
Return ``True`` if ``self`` and ``right`` are not equal.
|
|
898
|
+
|
|
899
|
+
EXAMPLES::
|
|
900
|
+
|
|
901
|
+
sage: pc1 = PolyhedralComplex([
|
|
902
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])])
|
|
903
|
+
sage: pc2 = PolyhedralComplex([
|
|
904
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])])
|
|
905
|
+
sage: pc1 != pc2
|
|
906
|
+
True
|
|
907
|
+
"""
|
|
908
|
+
return not self.__eq__(right)
|
|
909
|
+
|
|
910
|
+
def __copy__(self):
|
|
911
|
+
"""
|
|
912
|
+
Return a mutable copy of ``self``.
|
|
913
|
+
|
|
914
|
+
EXAMPLES::
|
|
915
|
+
|
|
916
|
+
sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[(0, 0)])])
|
|
917
|
+
sage: pc2 = copy(pc1)
|
|
918
|
+
sage: pc1 == pc2
|
|
919
|
+
True
|
|
920
|
+
"""
|
|
921
|
+
return PolyhedralComplex(self._maximal_cells, maximality_check=False,
|
|
922
|
+
backend=self._backend)
|
|
923
|
+
|
|
924
|
+
def _an_element_(self):
|
|
925
|
+
"""
|
|
926
|
+
Return a (maximal) cell of this complex.
|
|
927
|
+
|
|
928
|
+
EXAMPLES::
|
|
929
|
+
|
|
930
|
+
sage: PolyhedralComplex()._an_element_()
|
|
931
|
+
Traceback (most recent call last):
|
|
932
|
+
...
|
|
933
|
+
EmptySetError: the complex is empty
|
|
934
|
+
sage: pc = PolyhedralComplex([
|
|
935
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
936
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])])
|
|
937
|
+
sage: element = pc._an_element_().vertices_list()
|
|
938
|
+
sage: element # random output (one of the two maximal cells)
|
|
939
|
+
[[0, 0], [0, 1/2], [1, 2]]
|
|
940
|
+
sage: element in ([[0, 0], [0, 1/2], [1, 2]], [[0, 0], [1/3, 1/3], [1, 2]])
|
|
941
|
+
True
|
|
942
|
+
"""
|
|
943
|
+
try:
|
|
944
|
+
return next(self.maximal_cell_iterator(increasing=False))
|
|
945
|
+
except StopIteration:
|
|
946
|
+
from sage.categories.sets_cat import EmptySetError
|
|
947
|
+
raise EmptySetError("the complex is empty")
|
|
948
|
+
|
|
949
|
+
def __contains__(self, x) -> bool:
|
|
950
|
+
"""
|
|
951
|
+
Return ``True`` if ``x`` is a polyhedron which is contained in this complex.
|
|
952
|
+
|
|
953
|
+
EXAMPLES::
|
|
954
|
+
|
|
955
|
+
sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])
|
|
956
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])
|
|
957
|
+
sage: pc = PolyhedralComplex([p1, p2])
|
|
958
|
+
sage: (p1 in pc) and (p2 in pc)
|
|
959
|
+
True
|
|
960
|
+
sage: Polyhedron(vertices=[(1, 2), (0, 0)]) in pc
|
|
961
|
+
True
|
|
962
|
+
sage: Polyhedron(vertices=[(1, 1), (0, 0)]) in pc
|
|
963
|
+
False
|
|
964
|
+
sage: Polyhedron(vertices=[(0, 0)]) in pc
|
|
965
|
+
True
|
|
966
|
+
sage: (0, 0) in pc # not a polyhedron
|
|
967
|
+
False
|
|
968
|
+
"""
|
|
969
|
+
if not isinstance(x, sage.geometry.abc.Polyhedron):
|
|
970
|
+
return False
|
|
971
|
+
dim = x.dimension()
|
|
972
|
+
return dim in self.cells() and x in self.cells()[dim]
|
|
973
|
+
|
|
974
|
+
def __call__(self, x):
|
|
975
|
+
"""
|
|
976
|
+
If ``x`` is a polyhedron in this complex, return it.
|
|
977
|
+
Otherwise, raise a :exc:`ValueError`.
|
|
978
|
+
|
|
979
|
+
EXAMPLES::
|
|
980
|
+
|
|
981
|
+
sage: pc = PolyhedralComplex([
|
|
982
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
983
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])])
|
|
984
|
+
sage: pc(Polyhedron(vertices=[(1, 2), (0, 0)]))
|
|
985
|
+
A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices
|
|
986
|
+
sage: pc(Polyhedron(vertices=[(1, 1)]))
|
|
987
|
+
Traceback (most recent call last):
|
|
988
|
+
...
|
|
989
|
+
ValueError: the polyhedron is not in this complex
|
|
990
|
+
"""
|
|
991
|
+
if x not in self:
|
|
992
|
+
raise ValueError('the polyhedron is not in this complex')
|
|
993
|
+
return x
|
|
994
|
+
|
|
995
|
+
def face_poset(self):
|
|
996
|
+
r"""
|
|
997
|
+
The face poset of this polyhedral complex, the poset of
|
|
998
|
+
nonempty cells, ordered by inclusion.
|
|
999
|
+
|
|
1000
|
+
EXAMPLES::
|
|
1001
|
+
|
|
1002
|
+
sage: pc = PolyhedralComplex([
|
|
1003
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
1004
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])])
|
|
1005
|
+
sage: poset = pc.face_poset()
|
|
1006
|
+
sage: poset
|
|
1007
|
+
Finite poset containing 11 elements
|
|
1008
|
+
sage: d = {i: i.vertices_matrix() for i in poset}
|
|
1009
|
+
sage: poset.plot(element_labels=d) # needs sage.plot
|
|
1010
|
+
Graphics object consisting of 28 graphics primitives
|
|
1011
|
+
|
|
1012
|
+
For a nonbounded polyhedral complex::
|
|
1013
|
+
|
|
1014
|
+
sage: pc = PolyhedralComplex([
|
|
1015
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
1016
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]),
|
|
1017
|
+
....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]),
|
|
1018
|
+
....: Polyhedron(rays=[(1, 0)])])
|
|
1019
|
+
sage: poset = pc.face_poset()
|
|
1020
|
+
sage: poset
|
|
1021
|
+
Finite poset containing 13 elements
|
|
1022
|
+
sage: d = {i:''.join([str(v)+'\n'
|
|
1023
|
+
....: for v in i.Vrepresentation()]) for i in poset}
|
|
1024
|
+
sage: poset.show(element_labels=d, figsize=15) # not tested
|
|
1025
|
+
sage: pc = PolyhedralComplex([
|
|
1026
|
+
....: Polyhedron(rays=[(1,0),(0,1)]),
|
|
1027
|
+
....: Polyhedron(rays=[(-1,0),(0,1)]),
|
|
1028
|
+
....: Polyhedron(rays=[(-1,0),(0,-1)]),
|
|
1029
|
+
....: Polyhedron(rays=[(1,0),(0,-1)])])
|
|
1030
|
+
sage: pc.face_poset()
|
|
1031
|
+
Finite poset containing 9 elements
|
|
1032
|
+
"""
|
|
1033
|
+
if self._face_poset is None:
|
|
1034
|
+
self.cells() # poset is obtained and cached in cells()
|
|
1035
|
+
return self._face_poset
|
|
1036
|
+
|
|
1037
|
+
def is_subcomplex(self, other) -> bool:
|
|
1038
|
+
r"""
|
|
1039
|
+
Return whether ``self`` is a subcomplex of ``other``.
|
|
1040
|
+
|
|
1041
|
+
INPUT:
|
|
1042
|
+
|
|
1043
|
+
- ``other`` -- a polyhedral complex
|
|
1044
|
+
|
|
1045
|
+
Each maximal cell of ``self`` must be a cell of ``other``
|
|
1046
|
+
for this to be ``True``.
|
|
1047
|
+
|
|
1048
|
+
EXAMPLES::
|
|
1049
|
+
|
|
1050
|
+
sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])
|
|
1051
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])
|
|
1052
|
+
sage: p3 = Polyhedron(vertices=[(0, 0), (1, 0)])
|
|
1053
|
+
sage: pc = PolyhedralComplex([p1, Polyhedron(vertices=[(1, 0)])])
|
|
1054
|
+
sage: pc.is_subcomplex(PolyhedralComplex([p1, p2, p3]))
|
|
1055
|
+
True
|
|
1056
|
+
sage: pc.is_subcomplex(PolyhedralComplex([p1, p2]))
|
|
1057
|
+
False
|
|
1058
|
+
"""
|
|
1059
|
+
other_cells = other.cells()
|
|
1060
|
+
for (d, stratum) in self.maximal_cells().items():
|
|
1061
|
+
if not stratum.issubset(other_cells.get(d, set())):
|
|
1062
|
+
return False
|
|
1063
|
+
return True
|
|
1064
|
+
|
|
1065
|
+
def is_compact(self) -> bool:
|
|
1066
|
+
"""
|
|
1067
|
+
Test for boundedness of the polyhedral complex.
|
|
1068
|
+
|
|
1069
|
+
EXAMPLES::
|
|
1070
|
+
|
|
1071
|
+
sage: p1 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])
|
|
1072
|
+
sage: p2 = Polyhedron(rays=[(1, 0)])
|
|
1073
|
+
sage: PolyhedralComplex([p1]).is_compact()
|
|
1074
|
+
True
|
|
1075
|
+
sage: PolyhedralComplex([p1, p2]).is_compact()
|
|
1076
|
+
False
|
|
1077
|
+
"""
|
|
1078
|
+
return all(p.is_compact() for p in self.maximal_cell_iterator())
|
|
1079
|
+
|
|
1080
|
+
def graph(self):
|
|
1081
|
+
"""
|
|
1082
|
+
Return the 1-skeleton of this polyhedral complex, as a graph.
|
|
1083
|
+
|
|
1084
|
+
The vertices of the graph are of type ``vector``. This raises
|
|
1085
|
+
a :exc:`NotImplementedError` if the polyhedral complex is unbounded.
|
|
1086
|
+
|
|
1087
|
+
.. WARNING::
|
|
1088
|
+
|
|
1089
|
+
This may give the wrong answer if the polyhedral complex
|
|
1090
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
1091
|
+
|
|
1092
|
+
EXAMPLES::
|
|
1093
|
+
|
|
1094
|
+
sage: pc = PolyhedralComplex([
|
|
1095
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
1096
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
1097
|
+
sage: g = pc.graph(); g
|
|
1098
|
+
Graph on 4 vertices
|
|
1099
|
+
sage: g.vertices(sort=True)
|
|
1100
|
+
[(0, 0), (0, 2), (1, 1), (1, 2)]
|
|
1101
|
+
sage: g.edges(sort=True, labels=False)
|
|
1102
|
+
[((0, 0), (0, 2)), ((0, 0), (1, 1)), ((0, 0), (1, 2)), ((0, 2), (1, 2)), ((1, 1), (1, 2))]
|
|
1103
|
+
sage: PolyhedralComplex([Polyhedron(rays=[(1,1)])]).graph()
|
|
1104
|
+
Traceback (most recent call last):
|
|
1105
|
+
...
|
|
1106
|
+
NotImplementedError: the polyhedral complex is unbounded
|
|
1107
|
+
|
|
1108
|
+
Wrong answer due to ``maximality_check=False``::
|
|
1109
|
+
|
|
1110
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1111
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1112
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
1113
|
+
sage: PolyhedralComplex([p1, p2]).is_pure()
|
|
1114
|
+
True
|
|
1115
|
+
sage: PolyhedralComplex([p2, p3], maximality_check=True).is_pure()
|
|
1116
|
+
True
|
|
1117
|
+
sage: PolyhedralComplex([p2, p3], maximality_check=False).is_pure()
|
|
1118
|
+
False
|
|
1119
|
+
"""
|
|
1120
|
+
if not self.is_compact():
|
|
1121
|
+
raise NotImplementedError("the polyhedral complex is unbounded")
|
|
1122
|
+
edges = self.n_cells(1)
|
|
1123
|
+
d = {}
|
|
1124
|
+
for e in edges:
|
|
1125
|
+
v, max_e = sorted(e.vertices_matrix().columns())
|
|
1126
|
+
if v in d:
|
|
1127
|
+
d[v].append(max_e)
|
|
1128
|
+
else:
|
|
1129
|
+
d[v] = [max_e]
|
|
1130
|
+
for v in self.n_maximal_cells(0):
|
|
1131
|
+
d[v] = []
|
|
1132
|
+
return Graph(d)
|
|
1133
|
+
|
|
1134
|
+
def is_connected(self) -> bool:
|
|
1135
|
+
"""
|
|
1136
|
+
Return whether ``self`` is connected.
|
|
1137
|
+
|
|
1138
|
+
EXAMPLES::
|
|
1139
|
+
|
|
1140
|
+
sage: pc1 = PolyhedralComplex([
|
|
1141
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
1142
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
1143
|
+
sage: pc1.is_connected()
|
|
1144
|
+
True
|
|
1145
|
+
sage: pc2 = PolyhedralComplex([
|
|
1146
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
1147
|
+
....: Polyhedron(vertices=[(0, 2)])])
|
|
1148
|
+
sage: pc2.is_connected()
|
|
1149
|
+
False
|
|
1150
|
+
sage: pc3 = PolyhedralComplex([
|
|
1151
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
1152
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]),
|
|
1153
|
+
....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]),
|
|
1154
|
+
....: Polyhedron(rays=[(1, 0)])])
|
|
1155
|
+
sage: pc3.is_connected()
|
|
1156
|
+
False
|
|
1157
|
+
sage: pc4 = PolyhedralComplex([
|
|
1158
|
+
....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]),
|
|
1159
|
+
....: Polyhedron(rays=[(1, 0)])])
|
|
1160
|
+
sage: pc4.is_connected()
|
|
1161
|
+
True
|
|
1162
|
+
"""
|
|
1163
|
+
if self.is_compact():
|
|
1164
|
+
return self.graph().is_connected() # faster than using poset?
|
|
1165
|
+
else:
|
|
1166
|
+
return self.face_poset().is_connected()
|
|
1167
|
+
|
|
1168
|
+
def connected_component(self, cell=None):
|
|
1169
|
+
"""
|
|
1170
|
+
Return the connected component of this polyhedral complex
|
|
1171
|
+
containing a given cell.
|
|
1172
|
+
|
|
1173
|
+
INPUT:
|
|
1174
|
+
|
|
1175
|
+
- ``cell`` -- (default: ``self.an_element()``) a cell of ``self``
|
|
1176
|
+
|
|
1177
|
+
OUTPUT:
|
|
1178
|
+
|
|
1179
|
+
The connected component containing ``cell``. If the polyhedral complex
|
|
1180
|
+
is empty or if it does not contain the given cell, raise an error.
|
|
1181
|
+
|
|
1182
|
+
EXAMPLES::
|
|
1183
|
+
|
|
1184
|
+
sage: t1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1185
|
+
sage: t2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1186
|
+
sage: v1 = Polyhedron(vertices=[(1, 1)])
|
|
1187
|
+
sage: v2 = Polyhedron(vertices=[(0, 2)])
|
|
1188
|
+
sage: v3 = Polyhedron(vertices=[(-1, 0)])
|
|
1189
|
+
sage: o = Polyhedron(vertices=[(0, 0)])
|
|
1190
|
+
sage: r = Polyhedron(rays=[(1, 0)])
|
|
1191
|
+
sage: l = Polyhedron(vertices=[(-1, 0)], lines=[(1, -1)])
|
|
1192
|
+
sage: pc1 = PolyhedralComplex([t1, t2])
|
|
1193
|
+
sage: pc1.connected_component() == pc1
|
|
1194
|
+
True
|
|
1195
|
+
sage: pc1.connected_component(v1) == pc1
|
|
1196
|
+
True
|
|
1197
|
+
sage: pc2 = PolyhedralComplex([t1, v2])
|
|
1198
|
+
sage: pc2.connected_component(t1) == PolyhedralComplex([t1])
|
|
1199
|
+
True
|
|
1200
|
+
sage: pc2.connected_component(o) == PolyhedralComplex([t1])
|
|
1201
|
+
True
|
|
1202
|
+
sage: pc2.connected_component(v3)
|
|
1203
|
+
Traceback (most recent call last):
|
|
1204
|
+
...
|
|
1205
|
+
ValueError: the polyhedral complex does not contain the given cell
|
|
1206
|
+
sage: pc2.connected_component(r)
|
|
1207
|
+
Traceback (most recent call last):
|
|
1208
|
+
...
|
|
1209
|
+
ValueError: the polyhedral complex does not contain the given cell
|
|
1210
|
+
sage: pc3 = PolyhedralComplex([t1, t2, r])
|
|
1211
|
+
sage: pc3.connected_component(v2) == pc3
|
|
1212
|
+
True
|
|
1213
|
+
sage: pc4 = PolyhedralComplex([t1, t2, r, l])
|
|
1214
|
+
sage: pc4.connected_component(o) == pc3
|
|
1215
|
+
True
|
|
1216
|
+
sage: pc4.connected_component(v3)
|
|
1217
|
+
Traceback (most recent call last):
|
|
1218
|
+
...
|
|
1219
|
+
ValueError: the polyhedral complex does not contain the given cell
|
|
1220
|
+
sage: pc5 = PolyhedralComplex([t1, t2, r, l, v3])
|
|
1221
|
+
sage: pc5.connected_component(v3) == PolyhedralComplex([v3])
|
|
1222
|
+
True
|
|
1223
|
+
sage: PolyhedralComplex([]).connected_component()
|
|
1224
|
+
Traceback (most recent call last):
|
|
1225
|
+
...
|
|
1226
|
+
ValueError: the empty polyhedral complex has no connected components
|
|
1227
|
+
"""
|
|
1228
|
+
if self.dimension() == -1:
|
|
1229
|
+
raise ValueError(
|
|
1230
|
+
"the empty polyhedral complex has no connected components")
|
|
1231
|
+
if cell is None:
|
|
1232
|
+
cell = self._an_element_()
|
|
1233
|
+
if self.is_compact(): # use graph (faster than poset?)
|
|
1234
|
+
if not cell.is_compact():
|
|
1235
|
+
raise ValueError(
|
|
1236
|
+
"the polyhedral complex does not contain the given cell")
|
|
1237
|
+
v = cell.vertices_matrix().columns()[0]
|
|
1238
|
+
g = self.graph()
|
|
1239
|
+
if v not in g:
|
|
1240
|
+
raise ValueError(
|
|
1241
|
+
"the polyhedral complex does not contain the given cell")
|
|
1242
|
+
vertices = g.connected_component_containing_vertex(v, sort=False)
|
|
1243
|
+
facets = [f for f in self.maximal_cell_iterator()
|
|
1244
|
+
if any(vf in f.vertices_matrix().columns()
|
|
1245
|
+
for vf in vertices)]
|
|
1246
|
+
else: # use face_poset
|
|
1247
|
+
g = self.face_poset().hasse_diagram()
|
|
1248
|
+
if cell not in g:
|
|
1249
|
+
raise ValueError(
|
|
1250
|
+
"the polyhedral complex does not contain the given cell")
|
|
1251
|
+
faces = g.connected_component_containing_vertex(cell, sort=False)
|
|
1252
|
+
facets = [f for f in self.maximal_cell_iterator()
|
|
1253
|
+
if f in faces]
|
|
1254
|
+
return PolyhedralComplex(facets, maximality_check=False,
|
|
1255
|
+
is_immutable=self._is_immutable,
|
|
1256
|
+
backend=self._backend)
|
|
1257
|
+
|
|
1258
|
+
def connected_components(self) -> list:
|
|
1259
|
+
"""
|
|
1260
|
+
Return the connected components of this polyhedral complex,
|
|
1261
|
+
as list of (sub-)PolyhedralComplexes.
|
|
1262
|
+
|
|
1263
|
+
EXAMPLES::
|
|
1264
|
+
|
|
1265
|
+
sage: t1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1266
|
+
sage: t2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1267
|
+
sage: v1 = Polyhedron(vertices=[(1, 1)])
|
|
1268
|
+
sage: v2 = Polyhedron(vertices=[(0, 2)])
|
|
1269
|
+
sage: v3 = Polyhedron(vertices=[(-1, 0)])
|
|
1270
|
+
sage: o = Polyhedron(vertices=[(0, 0)])
|
|
1271
|
+
sage: r = Polyhedron(rays=[(1, 0)])
|
|
1272
|
+
sage: l = Polyhedron(vertices=[(-1, 0)], lines=[(1, -1)])
|
|
1273
|
+
sage: pc1 = PolyhedralComplex([t1, t2])
|
|
1274
|
+
sage: len(pc1.connected_components())
|
|
1275
|
+
1
|
|
1276
|
+
sage: pc2 = PolyhedralComplex([t1, v2])
|
|
1277
|
+
sage: len(pc2.connected_components())
|
|
1278
|
+
2
|
|
1279
|
+
sage: pc3 = PolyhedralComplex([t1, t2, r])
|
|
1280
|
+
sage: len(pc3.connected_components())
|
|
1281
|
+
1
|
|
1282
|
+
sage: pc4 = PolyhedralComplex([t1, t2, r, l])
|
|
1283
|
+
sage: len(pc4.connected_components())
|
|
1284
|
+
2
|
|
1285
|
+
sage: pc5 = PolyhedralComplex([t1, t2, r, l, v3])
|
|
1286
|
+
sage: len(pc5.connected_components())
|
|
1287
|
+
3
|
|
1288
|
+
sage: PolyhedralComplex([]).connected_components()
|
|
1289
|
+
Traceback (most recent call last):
|
|
1290
|
+
...
|
|
1291
|
+
ValueError: the empty polyhedral complex has no connected components
|
|
1292
|
+
"""
|
|
1293
|
+
if self.dimension() == -1:
|
|
1294
|
+
raise ValueError(
|
|
1295
|
+
"the empty polyhedral complex has no connected components")
|
|
1296
|
+
if self.is_compact(): # use graph (faster than poset)?
|
|
1297
|
+
g = self.graph()
|
|
1298
|
+
lists_of_vertices = g.connected_components(sort=False)
|
|
1299
|
+
lists_of_facets = [[f for f in self.maximal_cell_iterator()
|
|
1300
|
+
if any(vf in f.vertices_matrix().columns()
|
|
1301
|
+
for vf in vertices)]
|
|
1302
|
+
for vertices in lists_of_vertices]
|
|
1303
|
+
else: # use face_poset
|
|
1304
|
+
g = self.face_poset().hasse_diagram()
|
|
1305
|
+
lists_of_faces = g.connected_components(sort=False)
|
|
1306
|
+
lists_of_facets = [
|
|
1307
|
+
[f for f in self.maximal_cell_iterator() if f in faces]
|
|
1308
|
+
for faces in lists_of_faces]
|
|
1309
|
+
return [PolyhedralComplex(facets, maximality_check=False,
|
|
1310
|
+
is_immutable=self._is_immutable,
|
|
1311
|
+
backend=self._backend)
|
|
1312
|
+
for facets in lists_of_facets]
|
|
1313
|
+
|
|
1314
|
+
def n_skeleton(self, n):
|
|
1315
|
+
r"""
|
|
1316
|
+
The `n`-skeleton of this polyhedral complex.
|
|
1317
|
+
|
|
1318
|
+
The `n`-skeleton of a polyhedral complex is obtained by discarding
|
|
1319
|
+
all of the cells in dimensions larger than `n`.
|
|
1320
|
+
|
|
1321
|
+
INPUT:
|
|
1322
|
+
|
|
1323
|
+
- ``n`` -- nonnegative integer; the dimension
|
|
1324
|
+
|
|
1325
|
+
.. SEEALSO::
|
|
1326
|
+
|
|
1327
|
+
:meth:`stratify`
|
|
1328
|
+
|
|
1329
|
+
EXAMPLES::
|
|
1330
|
+
|
|
1331
|
+
sage: pc = PolyhedralComplex([
|
|
1332
|
+
....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]),
|
|
1333
|
+
....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])])
|
|
1334
|
+
sage: pc.n_skeleton(2)
|
|
1335
|
+
Polyhedral complex with 2 maximal cells
|
|
1336
|
+
sage: pc.n_skeleton(1)
|
|
1337
|
+
Polyhedral complex with 5 maximal cells
|
|
1338
|
+
sage: pc.n_skeleton(0)
|
|
1339
|
+
Polyhedral complex with 4 maximal cells
|
|
1340
|
+
"""
|
|
1341
|
+
if n >= self.dimension():
|
|
1342
|
+
return copy(self)
|
|
1343
|
+
facets = [f for f in self.maximal_cell_iterator() if f.dimension() < n]
|
|
1344
|
+
facets.extend(self.n_cells(n))
|
|
1345
|
+
return PolyhedralComplex(facets, maximality_check=False,
|
|
1346
|
+
is_immutable=self._is_immutable,
|
|
1347
|
+
backend=self._backend)
|
|
1348
|
+
|
|
1349
|
+
def stratify(self, n):
|
|
1350
|
+
r"""
|
|
1351
|
+
Return the pure sub-polyhedral complex which is constructed from the
|
|
1352
|
+
`n`-dimensional maximal cells of this polyhedral complex.
|
|
1353
|
+
|
|
1354
|
+
.. SEEALSO::
|
|
1355
|
+
|
|
1356
|
+
:meth:`n_skeleton`
|
|
1357
|
+
|
|
1358
|
+
.. WARNING::
|
|
1359
|
+
|
|
1360
|
+
This may give the wrong answer if the polyhedral complex
|
|
1361
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
1362
|
+
|
|
1363
|
+
EXAMPLES::
|
|
1364
|
+
|
|
1365
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1366
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1367
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
1368
|
+
sage: pc = PolyhedralComplex([p1, p2, p3])
|
|
1369
|
+
sage: pc.stratify(2) == pc
|
|
1370
|
+
True
|
|
1371
|
+
sage: pc.stratify(1)
|
|
1372
|
+
Polyhedral complex with 0 maximal cells
|
|
1373
|
+
|
|
1374
|
+
Wrong answer due to ``maximality_check=False``::
|
|
1375
|
+
|
|
1376
|
+
sage: pc_invalid = PolyhedralComplex([p1, p2, p3],
|
|
1377
|
+
....: maximality_check=False)
|
|
1378
|
+
sage: pc_invalid.stratify(1)
|
|
1379
|
+
Polyhedral complex with 1 maximal cell
|
|
1380
|
+
"""
|
|
1381
|
+
n_faces = self.n_maximal_cells(n)
|
|
1382
|
+
return PolyhedralComplex(n_faces, maximality_check=False,
|
|
1383
|
+
is_immutable=self._is_immutable,
|
|
1384
|
+
backend=self._backend)
|
|
1385
|
+
|
|
1386
|
+
def boundary_subcomplex(self):
|
|
1387
|
+
"""
|
|
1388
|
+
Return the sub-polyhedral complex that is the boundary of ``self``.
|
|
1389
|
+
|
|
1390
|
+
A point `P` is on the boundary of a set `S` if `P` is in the
|
|
1391
|
+
closure of `S` but not in the interior of `S`.
|
|
1392
|
+
|
|
1393
|
+
EXAMPLES::
|
|
1394
|
+
|
|
1395
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1396
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1397
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
1398
|
+
sage: bd = PolyhedralComplex([p1, p2]).boundary_subcomplex()
|
|
1399
|
+
sage: len(bd.n_maximal_cells(2))
|
|
1400
|
+
0
|
|
1401
|
+
sage: len(bd.n_maximal_cells(1))
|
|
1402
|
+
4
|
|
1403
|
+
sage: pt = PolyhedralComplex([p3])
|
|
1404
|
+
sage: pt.boundary_subcomplex() == pt
|
|
1405
|
+
True
|
|
1406
|
+
|
|
1407
|
+
Test on polyhedral complex which is not pure::
|
|
1408
|
+
|
|
1409
|
+
sage: pc_non_pure = PolyhedralComplex([p1, p3])
|
|
1410
|
+
sage: pc_non_pure.boundary_subcomplex() == pc_non_pure.n_skeleton(1)
|
|
1411
|
+
True
|
|
1412
|
+
|
|
1413
|
+
Test with ``maximality_check == False``::
|
|
1414
|
+
|
|
1415
|
+
sage: pc_invalid = PolyhedralComplex([p2, p3],
|
|
1416
|
+
....: maximality_check=False)
|
|
1417
|
+
sage: pc_invalid.boundary_subcomplex() == pc_invalid.n_skeleton(1)
|
|
1418
|
+
True
|
|
1419
|
+
|
|
1420
|
+
Test unbounded cases::
|
|
1421
|
+
|
|
1422
|
+
sage: pc1 = PolyhedralComplex([
|
|
1423
|
+
....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])])
|
|
1424
|
+
sage: pc1.boundary_subcomplex() == pc1.n_skeleton(1)
|
|
1425
|
+
True
|
|
1426
|
+
sage: pc1b = PolyhedralComplex([Polyhedron(
|
|
1427
|
+
....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])])
|
|
1428
|
+
sage: pc1b.boundary_subcomplex() == pc1b
|
|
1429
|
+
True
|
|
1430
|
+
sage: pc2 = PolyhedralComplex([
|
|
1431
|
+
....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])])
|
|
1432
|
+
sage: pc2.boundary_subcomplex() == pc2.n_skeleton(1)
|
|
1433
|
+
True
|
|
1434
|
+
sage: pc3 = PolyhedralComplex([
|
|
1435
|
+
....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]),
|
|
1436
|
+
....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])])
|
|
1437
|
+
sage: pc3.boundary_subcomplex() == pc3.n_skeleton(1)
|
|
1438
|
+
False
|
|
1439
|
+
"""
|
|
1440
|
+
if self.is_full_dimensional():
|
|
1441
|
+
return PolyhedralComplex(self.relative_boundary_cells(),
|
|
1442
|
+
is_immutable=self._is_immutable,
|
|
1443
|
+
backend=self._backend)
|
|
1444
|
+
else:
|
|
1445
|
+
ans = copy(self)
|
|
1446
|
+
if self._is_immutable:
|
|
1447
|
+
ans.set_immutable()
|
|
1448
|
+
return ans
|
|
1449
|
+
|
|
1450
|
+
def relative_boundary_cells(self) -> list:
|
|
1451
|
+
r"""
|
|
1452
|
+
Return the maximal cells of the relative-boundary sub-complex.
|
|
1453
|
+
|
|
1454
|
+
A point `P` is in the relative boundary of a set `S` if `P` is in the
|
|
1455
|
+
closure of `S` but not in the relative interior of `S`.
|
|
1456
|
+
|
|
1457
|
+
.. WARNING::
|
|
1458
|
+
|
|
1459
|
+
This may give the wrong answer if the polyhedral complex
|
|
1460
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
1461
|
+
|
|
1462
|
+
EXAMPLES::
|
|
1463
|
+
|
|
1464
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1465
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1466
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
1467
|
+
sage: p4 = Polyhedron(vertices=[(2, 2)])
|
|
1468
|
+
sage: pc = PolyhedralComplex([p1, p2])
|
|
1469
|
+
sage: rbd_cells = pc.relative_boundary_cells()
|
|
1470
|
+
sage: len(rbd_cells)
|
|
1471
|
+
4
|
|
1472
|
+
sage: all(p.dimension() == 1 for p in rbd_cells)
|
|
1473
|
+
True
|
|
1474
|
+
sage: pc_lower_dim = PolyhedralComplex([p3])
|
|
1475
|
+
sage: sorted([p.vertices() for p in pc_lower_dim.relative_boundary_cells()])
|
|
1476
|
+
[(A vertex at (0, 2),), (A vertex at (1, 2),)]
|
|
1477
|
+
|
|
1478
|
+
Test on polyhedral complex which is not pure::
|
|
1479
|
+
|
|
1480
|
+
sage: pc_non_pure = PolyhedralComplex([p1, p3, p4])
|
|
1481
|
+
sage: (set(pc_non_pure.relative_boundary_cells())
|
|
1482
|
+
....: == set([f.as_polyhedron() for f in p1.faces(1)] + [p3, p4]))
|
|
1483
|
+
True
|
|
1484
|
+
|
|
1485
|
+
Test with ``maximality_check == False``::
|
|
1486
|
+
|
|
1487
|
+
sage: pc_invalid = PolyhedralComplex([p2, p3],
|
|
1488
|
+
....: maximality_check=False)
|
|
1489
|
+
sage: (set(pc_invalid.relative_boundary_cells())
|
|
1490
|
+
....: == set([f.as_polyhedron() for f in p2.faces(1)]))
|
|
1491
|
+
True
|
|
1492
|
+
|
|
1493
|
+
Test unbounded case::
|
|
1494
|
+
|
|
1495
|
+
sage: pc3 = PolyhedralComplex([
|
|
1496
|
+
....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]),
|
|
1497
|
+
....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])])
|
|
1498
|
+
sage: len(pc3.relative_boundary_cells())
|
|
1499
|
+
4
|
|
1500
|
+
"""
|
|
1501
|
+
d = self.dimension()
|
|
1502
|
+
poset = self.face_poset()
|
|
1503
|
+
faces = self.n_cells(d - 1)
|
|
1504
|
+
ans = [face for face in faces if len(poset.upper_covers(face)) == 1]
|
|
1505
|
+
if not self.is_pure():
|
|
1506
|
+
ans.extend(p for p in poset.maximal_elements() if p.dimension() < d)
|
|
1507
|
+
return ans
|
|
1508
|
+
|
|
1509
|
+
def is_convex(self) -> bool:
|
|
1510
|
+
r"""
|
|
1511
|
+
Return whether the set of points in ``self`` is a convex set.
|
|
1512
|
+
|
|
1513
|
+
When ``self`` is convex, the union of its cells is a Polyhedron.
|
|
1514
|
+
|
|
1515
|
+
.. SEEALSO::
|
|
1516
|
+
|
|
1517
|
+
:meth:`union_as_polyhedron`
|
|
1518
|
+
|
|
1519
|
+
EXAMPLES::
|
|
1520
|
+
|
|
1521
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1522
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1523
|
+
sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)])
|
|
1524
|
+
sage: p4 = Polyhedron(vertices=[(2, 2)])
|
|
1525
|
+
sage: PolyhedralComplex([p1, p2]).is_convex()
|
|
1526
|
+
True
|
|
1527
|
+
sage: PolyhedralComplex([p1, p3]).is_convex()
|
|
1528
|
+
False
|
|
1529
|
+
sage: PolyhedralComplex([p1, p4]).is_convex()
|
|
1530
|
+
False
|
|
1531
|
+
|
|
1532
|
+
Test unbounded cases::
|
|
1533
|
+
|
|
1534
|
+
sage: pc1 = PolyhedralComplex([
|
|
1535
|
+
....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])])
|
|
1536
|
+
sage: pc1.is_convex()
|
|
1537
|
+
True
|
|
1538
|
+
sage: pc2 = PolyhedralComplex([
|
|
1539
|
+
....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])])
|
|
1540
|
+
sage: pc2.is_convex()
|
|
1541
|
+
True
|
|
1542
|
+
sage: pc3 = PolyhedralComplex([
|
|
1543
|
+
....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]),
|
|
1544
|
+
....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])])
|
|
1545
|
+
sage: pc3.is_convex()
|
|
1546
|
+
False
|
|
1547
|
+
sage: pc4 = PolyhedralComplex([Polyhedron(rays=[[1,0], [-1,1]]),
|
|
1548
|
+
....: Polyhedron(rays=[[1,0], [-1,-1]])])
|
|
1549
|
+
sage: pc4.is_convex()
|
|
1550
|
+
False
|
|
1551
|
+
|
|
1552
|
+
The whole 3d space minus the first orthant is not convex::
|
|
1553
|
+
|
|
1554
|
+
sage: pc5 = PolyhedralComplex([
|
|
1555
|
+
....: Polyhedron(rays=[[1,0,0], [0,1,0], [0,0,-1]]),
|
|
1556
|
+
....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,-1]]),
|
|
1557
|
+
....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,1]]),
|
|
1558
|
+
....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,-1]]),
|
|
1559
|
+
....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,1]]),
|
|
1560
|
+
....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,-1]]),
|
|
1561
|
+
....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,1]])])
|
|
1562
|
+
sage: pc5.is_convex()
|
|
1563
|
+
False
|
|
1564
|
+
|
|
1565
|
+
Test some non-full-dimensional examples::
|
|
1566
|
+
|
|
1567
|
+
sage: l = PolyhedralComplex([Polyhedron(vertices=[(1, 2), (0, 2)])])
|
|
1568
|
+
sage: l.is_convex()
|
|
1569
|
+
True
|
|
1570
|
+
sage: pc1b = PolyhedralComplex([Polyhedron(
|
|
1571
|
+
....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])])
|
|
1572
|
+
sage: pc1b.is_convex()
|
|
1573
|
+
True
|
|
1574
|
+
sage: pc4b = PolyhedralComplex([
|
|
1575
|
+
....: Polyhedron(rays=[[1,0,0], [-1,1,0]]),
|
|
1576
|
+
....: Polyhedron(rays=[[1,0,0], [-1,-1,0]])])
|
|
1577
|
+
sage: pc4b.is_convex()
|
|
1578
|
+
False
|
|
1579
|
+
"""
|
|
1580
|
+
if self._is_convex is not None:
|
|
1581
|
+
return self._is_convex
|
|
1582
|
+
if not self.is_pure():
|
|
1583
|
+
self._is_convex = False
|
|
1584
|
+
return False
|
|
1585
|
+
d = self.dimension()
|
|
1586
|
+
if not self.is_full_dimensional():
|
|
1587
|
+
# if max cells must lie in different subspaces, can't be convex.
|
|
1588
|
+
from sage.modules.free_module import span
|
|
1589
|
+
f = self.n_maximal_cells(d)[0]
|
|
1590
|
+
affine_space = span(f.equations_list(), f.base_ring())
|
|
1591
|
+
for f in self.n_maximal_cells(d)[1::]:
|
|
1592
|
+
if span(f.equations_list(), f.base_ring()) != affine_space:
|
|
1593
|
+
self._is_convex = False
|
|
1594
|
+
return False
|
|
1595
|
+
# orient the (relative) boundary halfspaces toward a strict convex
|
|
1596
|
+
# combination of the vertices. Then check if all vertices are contained
|
|
1597
|
+
# After making sure that the affine hulls of the cells are the same,
|
|
1598
|
+
# it does not matter that is not full dimensional.
|
|
1599
|
+
boundaries = self.relative_boundary_cells()
|
|
1600
|
+
vertices = set()
|
|
1601
|
+
rays = set()
|
|
1602
|
+
lines = set()
|
|
1603
|
+
for cell in boundaries:
|
|
1604
|
+
# it suffices to consider only vertices on the boundaries
|
|
1605
|
+
# Note that a line (as polyhedron) has vertex too
|
|
1606
|
+
for v in cell.vertices_list():
|
|
1607
|
+
vv = vector(v)
|
|
1608
|
+
vv.set_immutable()
|
|
1609
|
+
vertices.add(vv)
|
|
1610
|
+
for cell in self.n_maximal_cells(d):
|
|
1611
|
+
for r in cell.rays_list():
|
|
1612
|
+
rr = vector(r)
|
|
1613
|
+
rr.set_immutable()
|
|
1614
|
+
rays.add(rr)
|
|
1615
|
+
for li in cell.lines_list():
|
|
1616
|
+
ll = vector(li)
|
|
1617
|
+
ll.set_immutable()
|
|
1618
|
+
lines.add(ll)
|
|
1619
|
+
center = sum(vertices) / len(vertices)
|
|
1620
|
+
for cell in boundaries:
|
|
1621
|
+
for equation in cell.equations_list():
|
|
1622
|
+
coeff = vector(equation[1::])
|
|
1623
|
+
const = equation[0]
|
|
1624
|
+
if const + coeff * center == 0:
|
|
1625
|
+
sign = 0
|
|
1626
|
+
elif const + coeff * center > 0:
|
|
1627
|
+
sign = 1
|
|
1628
|
+
for v in vertices:
|
|
1629
|
+
if const + coeff * v < 0:
|
|
1630
|
+
self._is_convex = False
|
|
1631
|
+
return False
|
|
1632
|
+
elif const + coeff * center < 0:
|
|
1633
|
+
sign = -1
|
|
1634
|
+
for v in vertices:
|
|
1635
|
+
if const + coeff * v > 0:
|
|
1636
|
+
self._is_convex = False
|
|
1637
|
+
return False
|
|
1638
|
+
for r in rays:
|
|
1639
|
+
if sign == 0:
|
|
1640
|
+
sign = coeff * r
|
|
1641
|
+
else:
|
|
1642
|
+
if sign * (coeff * r) < 0:
|
|
1643
|
+
self._is_convex = False
|
|
1644
|
+
return False
|
|
1645
|
+
# lines are in the affine space of each boundary cell already
|
|
1646
|
+
self._is_convex = True
|
|
1647
|
+
self._polyhedron = Polyhedron(vertices=vertices, rays=rays, lines=lines,
|
|
1648
|
+
backend=self._backend)
|
|
1649
|
+
return True
|
|
1650
|
+
|
|
1651
|
+
def union_as_polyhedron(self):
|
|
1652
|
+
"""
|
|
1653
|
+
Return ``self`` as a :class:`Polyhedron` if ``self`` is convex.
|
|
1654
|
+
|
|
1655
|
+
EXAMPLES::
|
|
1656
|
+
|
|
1657
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1658
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1659
|
+
sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)])
|
|
1660
|
+
sage: P = PolyhedralComplex([p1, p2]).union_as_polyhedron()
|
|
1661
|
+
sage: P.vertices_list()
|
|
1662
|
+
[[0, 0], [0, 2], [1, 1], [1, 2]]
|
|
1663
|
+
sage: PolyhedralComplex([p1, p3]).union_as_polyhedron()
|
|
1664
|
+
Traceback (most recent call last):
|
|
1665
|
+
...
|
|
1666
|
+
ValueError: the polyhedral complex is not convex
|
|
1667
|
+
"""
|
|
1668
|
+
if not self.is_convex():
|
|
1669
|
+
raise ValueError("the polyhedral complex is not convex")
|
|
1670
|
+
return self._polyhedron
|
|
1671
|
+
|
|
1672
|
+
def product(self, right):
|
|
1673
|
+
"""
|
|
1674
|
+
The (Cartesian) product of this polyhedral complex with another one.
|
|
1675
|
+
|
|
1676
|
+
INPUT:
|
|
1677
|
+
|
|
1678
|
+
- ``right`` -- the other polyhedral complex (the right-hand factor)
|
|
1679
|
+
|
|
1680
|
+
OUTPUT: the product ``self x right``
|
|
1681
|
+
|
|
1682
|
+
EXAMPLES::
|
|
1683
|
+
|
|
1684
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1685
|
+
sage: pc_square = pc.product(pc)
|
|
1686
|
+
sage: pc_square
|
|
1687
|
+
Polyhedral complex with 1 maximal cell
|
|
1688
|
+
sage: next(pc_square.maximal_cell_iterator()).vertices()
|
|
1689
|
+
(A vertex at (0, 0),
|
|
1690
|
+
A vertex at (0, 1),
|
|
1691
|
+
A vertex at (1, 0),
|
|
1692
|
+
A vertex at (1, 1))
|
|
1693
|
+
"""
|
|
1694
|
+
maximal_cells = [f.product(g) for f in self.maximal_cell_iterator()
|
|
1695
|
+
for g in right.maximal_cell_iterator()]
|
|
1696
|
+
return PolyhedralComplex(maximal_cells, maximality_check=False,
|
|
1697
|
+
is_immutable=(self._is_immutable and
|
|
1698
|
+
right._is_immutable),
|
|
1699
|
+
backend=self._backend)
|
|
1700
|
+
|
|
1701
|
+
def disjoint_union(self, right):
|
|
1702
|
+
"""
|
|
1703
|
+
The disjoint union of this polyhedral complex with another one.
|
|
1704
|
+
|
|
1705
|
+
INPUT:
|
|
1706
|
+
|
|
1707
|
+
- ``right`` -- the other polyhedral complex (the right-hand factor)
|
|
1708
|
+
|
|
1709
|
+
EXAMPLES::
|
|
1710
|
+
|
|
1711
|
+
sage: p1 = Polyhedron(vertices=[(-1, 0), (0, 0), (0, 1)])
|
|
1712
|
+
sage: p2 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0)])
|
|
1713
|
+
sage: p3 = Polyhedron(vertices=[(0, -1), (1, -1), (1, 0)])
|
|
1714
|
+
sage: pc = PolyhedralComplex([p1]).disjoint_union(PolyhedralComplex([p3]))
|
|
1715
|
+
sage: set(pc.maximal_cell_iterator()) == set([p1, p3])
|
|
1716
|
+
True
|
|
1717
|
+
sage: pc.disjoint_union(PolyhedralComplex([p2]))
|
|
1718
|
+
Traceback (most recent call last):
|
|
1719
|
+
...
|
|
1720
|
+
ValueError: the two complexes are not disjoint
|
|
1721
|
+
"""
|
|
1722
|
+
maximal_cells_self = list(self.maximal_cell_iterator())
|
|
1723
|
+
maximal_cells_right = list(right.maximal_cell_iterator())
|
|
1724
|
+
for cell in maximal_cells_self:
|
|
1725
|
+
for cell_right in maximal_cells_right:
|
|
1726
|
+
if not cell.intersection(cell_right).is_empty():
|
|
1727
|
+
raise ValueError("the two complexes are not disjoint")
|
|
1728
|
+
return PolyhedralComplex(maximal_cells_self + maximal_cells_right,
|
|
1729
|
+
maximality_check=False,
|
|
1730
|
+
face_to_face_check=False,
|
|
1731
|
+
is_immutable=(self._is_immutable and
|
|
1732
|
+
right._is_immutable),
|
|
1733
|
+
backend=self._backend)
|
|
1734
|
+
|
|
1735
|
+
def union(self, right):
|
|
1736
|
+
"""
|
|
1737
|
+
The union of this polyhedral complex with another one.
|
|
1738
|
+
|
|
1739
|
+
INPUT:
|
|
1740
|
+
|
|
1741
|
+
- ``right`` -- the other polyhedral complex (the right-hand factor)
|
|
1742
|
+
|
|
1743
|
+
EXAMPLES::
|
|
1744
|
+
|
|
1745
|
+
sage: p1 = Polyhedron(vertices=[(-1, 0), (0, 0), (0, 1)])
|
|
1746
|
+
sage: p2 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0)])
|
|
1747
|
+
sage: p3 = Polyhedron(vertices=[(0, -1), (1, -1), (1, 0)])
|
|
1748
|
+
sage: pc = PolyhedralComplex([p1]).union(PolyhedralComplex([p3]))
|
|
1749
|
+
sage: set(pc.maximal_cell_iterator()) == set([p1, p3])
|
|
1750
|
+
True
|
|
1751
|
+
sage: pc.union(PolyhedralComplex([p2]))
|
|
1752
|
+
Polyhedral complex with 3 maximal cells
|
|
1753
|
+
sage: p4 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0), (1, -1)])
|
|
1754
|
+
sage: pc.union(PolyhedralComplex([p4]))
|
|
1755
|
+
Traceback (most recent call last):
|
|
1756
|
+
...
|
|
1757
|
+
ValueError: the given cells are not face-to-face
|
|
1758
|
+
"""
|
|
1759
|
+
maximal_cells = list(self.maximal_cell_iterator()) + list(
|
|
1760
|
+
right.maximal_cell_iterator())
|
|
1761
|
+
return PolyhedralComplex(maximal_cells, maximality_check=True,
|
|
1762
|
+
face_to_face_check=True,
|
|
1763
|
+
is_immutable=(self._is_immutable and
|
|
1764
|
+
right._is_immutable),
|
|
1765
|
+
backend=self._backend)
|
|
1766
|
+
|
|
1767
|
+
def join(self, right):
|
|
1768
|
+
"""
|
|
1769
|
+
The join of this polyhedral complex with another one.
|
|
1770
|
+
|
|
1771
|
+
INPUT:
|
|
1772
|
+
|
|
1773
|
+
- ``right`` -- the other polyhedral complex (the right-hand factor)
|
|
1774
|
+
|
|
1775
|
+
EXAMPLES::
|
|
1776
|
+
|
|
1777
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1778
|
+
sage: pc_join = pc.join(pc)
|
|
1779
|
+
sage: pc_join
|
|
1780
|
+
Polyhedral complex with 1 maximal cell
|
|
1781
|
+
sage: next(pc_join.maximal_cell_iterator()).vertices()
|
|
1782
|
+
(A vertex at (0, 0, 0),
|
|
1783
|
+
A vertex at (0, 0, 1),
|
|
1784
|
+
A vertex at (0, 1, 1),
|
|
1785
|
+
A vertex at (1, 0, 0))
|
|
1786
|
+
"""
|
|
1787
|
+
maximal_cells = [f.join(g) for f in self.maximal_cell_iterator()
|
|
1788
|
+
for g in right.maximal_cell_iterator()]
|
|
1789
|
+
return PolyhedralComplex(maximal_cells, maximality_check=False,
|
|
1790
|
+
is_immutable=(self._is_immutable and
|
|
1791
|
+
right._is_immutable),
|
|
1792
|
+
backend=self._backend)
|
|
1793
|
+
|
|
1794
|
+
############################################################
|
|
1795
|
+
# abstract methods not implemented in generic cell complex
|
|
1796
|
+
############################################################
|
|
1797
|
+
|
|
1798
|
+
def wedge(self, right):
|
|
1799
|
+
"""
|
|
1800
|
+
The wedge (one-point union) of ``self`` with ``right``.
|
|
1801
|
+
|
|
1802
|
+
.. TODO::
|
|
1803
|
+
|
|
1804
|
+
Implement the wedge product of two polyhedral complexes.
|
|
1805
|
+
|
|
1806
|
+
EXAMPLES::
|
|
1807
|
+
|
|
1808
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1809
|
+
sage: pc.wedge(pc)
|
|
1810
|
+
Traceback (most recent call last):
|
|
1811
|
+
...
|
|
1812
|
+
NotImplementedError: wedge is not implemented for polyhedral complex
|
|
1813
|
+
"""
|
|
1814
|
+
raise NotImplementedError("wedge is not implemented for "
|
|
1815
|
+
+ "polyhedral complex")
|
|
1816
|
+
|
|
1817
|
+
############################################################
|
|
1818
|
+
# chain complexes, homology
|
|
1819
|
+
############################################################
|
|
1820
|
+
def chain_complex(self, subcomplex=None, augmented=False,
|
|
1821
|
+
verbose=False, check=True, dimensions=None,
|
|
1822
|
+
base_ring=ZZ, cochain=False):
|
|
1823
|
+
"""
|
|
1824
|
+
The chain complex associated to this polyhedral complex.
|
|
1825
|
+
|
|
1826
|
+
.. TODO::
|
|
1827
|
+
|
|
1828
|
+
Implement chain complexes of a polyhedral complex.
|
|
1829
|
+
|
|
1830
|
+
EXAMPLES::
|
|
1831
|
+
|
|
1832
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1833
|
+
sage: pc.chain_complex()
|
|
1834
|
+
Traceback (most recent call last):
|
|
1835
|
+
...
|
|
1836
|
+
NotImplementedError: chain_complex is not implemented for polyhedral complex
|
|
1837
|
+
"""
|
|
1838
|
+
raise NotImplementedError("chain_complex is not implemented for "
|
|
1839
|
+
+ "polyhedral complex")
|
|
1840
|
+
|
|
1841
|
+
def alexander_whitney(self, cell, dim_left):
|
|
1842
|
+
"""
|
|
1843
|
+
The decomposition of ``cell`` in this complex into left and right
|
|
1844
|
+
factors, suitable for computing cup products.
|
|
1845
|
+
|
|
1846
|
+
.. TODO::
|
|
1847
|
+
|
|
1848
|
+
Implement :meth:`alexander_whitney` of a polyhedral complex.
|
|
1849
|
+
|
|
1850
|
+
EXAMPLES::
|
|
1851
|
+
|
|
1852
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1853
|
+
sage: pc.alexander_whitney(None, 1)
|
|
1854
|
+
Traceback (most recent call last):
|
|
1855
|
+
...
|
|
1856
|
+
NotImplementedError: alexander_whitney is not implemented for polyhedral complex
|
|
1857
|
+
"""
|
|
1858
|
+
raise NotImplementedError("alexander_whitney is not implemented for "
|
|
1859
|
+
+ "polyhedral complex")
|
|
1860
|
+
|
|
1861
|
+
############################################################
|
|
1862
|
+
# end of chain complexes, homology
|
|
1863
|
+
############################################################
|
|
1864
|
+
|
|
1865
|
+
# this function overrides the standard one for GenericCellComplex,
|
|
1866
|
+
# this one counts the number of maximal cells, not all cells, to
|
|
1867
|
+
# avoid calling and computing self.cells()
|
|
1868
|
+
def _repr_(self) -> str:
|
|
1869
|
+
"""
|
|
1870
|
+
Print representation.
|
|
1871
|
+
|
|
1872
|
+
.. WARNING::
|
|
1873
|
+
|
|
1874
|
+
This may give the wrong answer if the polyhedral complex
|
|
1875
|
+
was constructed with ``maximality_check`` set to ``False``.
|
|
1876
|
+
|
|
1877
|
+
EXAMPLES::
|
|
1878
|
+
|
|
1879
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
1880
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
1881
|
+
sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)])
|
|
1882
|
+
sage: PolyhedralComplex([p1, p2, p3])
|
|
1883
|
+
Polyhedral complex with 2 maximal cells
|
|
1884
|
+
|
|
1885
|
+
Wrong answer due to ``maximality_check=False``::
|
|
1886
|
+
|
|
1887
|
+
sage: PolyhedralComplex([p1, p2, p3], maximality_check=False)
|
|
1888
|
+
Polyhedral complex with 3 maximal cells
|
|
1889
|
+
"""
|
|
1890
|
+
num = len(list(self.maximal_cell_iterator()))
|
|
1891
|
+
if num == 1:
|
|
1892
|
+
return "Polyhedral complex with %s maximal cell" % num
|
|
1893
|
+
else:
|
|
1894
|
+
return "Polyhedral complex with %s maximal cells" % num
|
|
1895
|
+
|
|
1896
|
+
def set_immutable(self) -> None:
|
|
1897
|
+
"""
|
|
1898
|
+
Make this polyhedral complex immutable.
|
|
1899
|
+
|
|
1900
|
+
EXAMPLES::
|
|
1901
|
+
|
|
1902
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1903
|
+
sage: pc.is_mutable()
|
|
1904
|
+
True
|
|
1905
|
+
sage: pc.set_immutable()
|
|
1906
|
+
sage: pc.is_mutable()
|
|
1907
|
+
False
|
|
1908
|
+
"""
|
|
1909
|
+
self._is_immutable = True
|
|
1910
|
+
|
|
1911
|
+
def is_mutable(self) -> bool:
|
|
1912
|
+
"""
|
|
1913
|
+
Return whether ``self`` is mutable.
|
|
1914
|
+
|
|
1915
|
+
EXAMPLES::
|
|
1916
|
+
|
|
1917
|
+
sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1918
|
+
sage: pc1.is_mutable()
|
|
1919
|
+
True
|
|
1920
|
+
sage: pc2 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])],
|
|
1921
|
+
....: is_mutable=False)
|
|
1922
|
+
sage: pc2.is_mutable()
|
|
1923
|
+
False
|
|
1924
|
+
sage: pc1 == pc2
|
|
1925
|
+
True
|
|
1926
|
+
sage: pc3 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])],
|
|
1927
|
+
....: is_immutable=True)
|
|
1928
|
+
sage: pc3.is_mutable()
|
|
1929
|
+
False
|
|
1930
|
+
sage: pc2 == pc3
|
|
1931
|
+
True
|
|
1932
|
+
"""
|
|
1933
|
+
return not self._is_immutable
|
|
1934
|
+
|
|
1935
|
+
def is_immutable(self) -> bool:
|
|
1936
|
+
"""
|
|
1937
|
+
Return whether ``self`` is immutable.
|
|
1938
|
+
|
|
1939
|
+
EXAMPLES::
|
|
1940
|
+
|
|
1941
|
+
sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])])
|
|
1942
|
+
sage: pc1.is_immutable()
|
|
1943
|
+
False
|
|
1944
|
+
sage: pc2 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])],
|
|
1945
|
+
....: is_mutable=False)
|
|
1946
|
+
sage: pc2.is_immutable()
|
|
1947
|
+
True
|
|
1948
|
+
sage: pc3 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])],
|
|
1949
|
+
....: is_immutable=True)
|
|
1950
|
+
sage: pc3.is_immutable()
|
|
1951
|
+
True
|
|
1952
|
+
"""
|
|
1953
|
+
return self._is_immutable
|
|
1954
|
+
|
|
1955
|
+
def add_cell(self, cell):
|
|
1956
|
+
"""
|
|
1957
|
+
Add a cell to this polyhedral complex.
|
|
1958
|
+
|
|
1959
|
+
INPUT:
|
|
1960
|
+
|
|
1961
|
+
- ``cell`` -- a polyhedron
|
|
1962
|
+
|
|
1963
|
+
This **changes** the polyhedral complex, by adding a new cell and all
|
|
1964
|
+
of its subfaces.
|
|
1965
|
+
|
|
1966
|
+
EXAMPLES:
|
|
1967
|
+
|
|
1968
|
+
Set up an empty complex in the intended ambient space, then add a cell::
|
|
1969
|
+
|
|
1970
|
+
sage: pc = PolyhedralComplex(ambient_dim=2)
|
|
1971
|
+
sage: pc.add_cell(Polyhedron(vertices=[(1, 2), (0, 2)]))
|
|
1972
|
+
sage: pc
|
|
1973
|
+
Polyhedral complex with 1 maximal cell
|
|
1974
|
+
|
|
1975
|
+
If you add a cell which is already present, there is no effect::
|
|
1976
|
+
|
|
1977
|
+
sage: pc.add_cell(Polyhedron(vertices=[(1, 2)]))
|
|
1978
|
+
sage: pc
|
|
1979
|
+
Polyhedral complex with 1 maximal cell
|
|
1980
|
+
sage: pc.dimension()
|
|
1981
|
+
1
|
|
1982
|
+
|
|
1983
|
+
Add a cell and check that dimension is correctly updated::
|
|
1984
|
+
|
|
1985
|
+
sage: pc.add_cell(Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]))
|
|
1986
|
+
sage: pc.dimension()
|
|
1987
|
+
2
|
|
1988
|
+
sage: pc.maximal_cells()
|
|
1989
|
+
{2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}}
|
|
1990
|
+
sage: pc.is_convex()
|
|
1991
|
+
True
|
|
1992
|
+
|
|
1993
|
+
Add another cell and check that the properties are correctly updated::
|
|
1994
|
+
|
|
1995
|
+
sage: pc.add_cell(Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]))
|
|
1996
|
+
sage: pc
|
|
1997
|
+
Polyhedral complex with 2 maximal cells
|
|
1998
|
+
sage: len(pc._cells[1])
|
|
1999
|
+
5
|
|
2000
|
+
sage: pc._face_poset
|
|
2001
|
+
Finite poset containing 11 elements
|
|
2002
|
+
sage: pc._is_convex
|
|
2003
|
+
True
|
|
2004
|
+
sage: pc._polyhedron.vertices_list()
|
|
2005
|
+
[[0, 0], [0, 2], [1, 1], [1, 2]]
|
|
2006
|
+
|
|
2007
|
+
Add a ray which makes the complex non convex::
|
|
2008
|
+
|
|
2009
|
+
sage: pc.add_cell(Polyhedron(rays=[(1, 0)]))
|
|
2010
|
+
sage: pc
|
|
2011
|
+
Polyhedral complex with 3 maximal cells
|
|
2012
|
+
sage: len(pc._cells[1])
|
|
2013
|
+
6
|
|
2014
|
+
sage: (pc._is_convex is False) and (pc._polyhedron is None)
|
|
2015
|
+
True
|
|
2016
|
+
|
|
2017
|
+
TESTS::
|
|
2018
|
+
|
|
2019
|
+
sage: pc.add_cell(Polyhedron(vertices=[[0]]))
|
|
2020
|
+
Traceback (most recent call last):
|
|
2021
|
+
...
|
|
2022
|
+
ValueError: the given cell is not a polyhedron in the same ambient space
|
|
2023
|
+
sage: pc.add_cell(Polyhedron(vertices=[(1, 1), (0, 0), (2, 0)]))
|
|
2024
|
+
Traceback (most recent call last):
|
|
2025
|
+
...
|
|
2026
|
+
ValueError: the cell is not face-to-face with complex
|
|
2027
|
+
sage: pc.set_immutable()
|
|
2028
|
+
sage: pc.add_cell(Polyhedron(vertices=[(-1, -1)]))
|
|
2029
|
+
Traceback (most recent call last):
|
|
2030
|
+
...
|
|
2031
|
+
ValueError: this polyhedral complex is not mutable
|
|
2032
|
+
"""
|
|
2033
|
+
if self._is_immutable:
|
|
2034
|
+
raise ValueError("this polyhedral complex is not mutable")
|
|
2035
|
+
if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim:
|
|
2036
|
+
raise ValueError("the given cell is not a polyhedron " +
|
|
2037
|
+
"in the same ambient space")
|
|
2038
|
+
# if cell is already in self, do nothing.
|
|
2039
|
+
if self.is_cell(cell):
|
|
2040
|
+
return
|
|
2041
|
+
if self._backend:
|
|
2042
|
+
cell = cell.base_extend(cell.base_ring(), self._backend)
|
|
2043
|
+
# update cells and face poset
|
|
2044
|
+
cells = self.cells()
|
|
2045
|
+
covers = {p: self.face_poset().upper_covers(p)
|
|
2046
|
+
for p in self.cell_iterator()}
|
|
2047
|
+
d = cell.dimension()
|
|
2048
|
+
d_cells = [cell]
|
|
2049
|
+
if d not in cells:
|
|
2050
|
+
cells[d] = set(d_cells)
|
|
2051
|
+
else:
|
|
2052
|
+
cells[d].add(cell)
|
|
2053
|
+
covers[cell] = []
|
|
2054
|
+
while d > 0:
|
|
2055
|
+
d = d - 1
|
|
2056
|
+
new_facets = []
|
|
2057
|
+
for c in d_cells:
|
|
2058
|
+
for facet in c.facets():
|
|
2059
|
+
p = facet.as_polyhedron()
|
|
2060
|
+
if d not in cells:
|
|
2061
|
+
cells[d] = set()
|
|
2062
|
+
if p not in cells[d]:
|
|
2063
|
+
cells[d].add(p)
|
|
2064
|
+
covers[p] = [c]
|
|
2065
|
+
new_facets.append(p)
|
|
2066
|
+
else:
|
|
2067
|
+
covers[p].append(c)
|
|
2068
|
+
d_cells = new_facets
|
|
2069
|
+
self._face_poset = poset = Poset(covers)
|
|
2070
|
+
self._cells = cells
|
|
2071
|
+
# check face-to-face between cell and previous maximal cells
|
|
2072
|
+
for p in self.maximal_cell_iterator():
|
|
2073
|
+
r = p.intersection(cell)
|
|
2074
|
+
if not (r.is_empty() or (r in poset) and
|
|
2075
|
+
poset.is_gequal(p, r) and poset.is_gequal(cell, r)):
|
|
2076
|
+
raise ValueError("the cell is not face-to-face with complex")
|
|
2077
|
+
# update dim and maximal cells
|
|
2078
|
+
d = cell.dimension()
|
|
2079
|
+
self._dim = max(d, self._dim)
|
|
2080
|
+
maximal_cells = poset.maximal_elements() # a list
|
|
2081
|
+
self._maximal_cells = cells_list_to_cells_dict(maximal_cells)
|
|
2082
|
+
# update convexity if self was known to be convex, reset otherwise.
|
|
2083
|
+
if self._is_convex:
|
|
2084
|
+
try:
|
|
2085
|
+
new_complex = PolyhedralComplex([self._polyhedron, cell],
|
|
2086
|
+
face_to_face_check=True)
|
|
2087
|
+
except ValueError:
|
|
2088
|
+
self._is_convex = False
|
|
2089
|
+
self._polyhedron = None
|
|
2090
|
+
else:
|
|
2091
|
+
self._is_convex = new_complex.is_convex()
|
|
2092
|
+
self._polyhedron = new_complex._polyhedron
|
|
2093
|
+
else:
|
|
2094
|
+
self._is_convex = None
|
|
2095
|
+
self._polyhedron = None
|
|
2096
|
+
# reset cached attribute
|
|
2097
|
+
self._maximal_cells_sorted = None # needed for hash
|
|
2098
|
+
|
|
2099
|
+
def remove_cell(self, cell, check=False):
|
|
2100
|
+
r"""
|
|
2101
|
+
Remove ``cell`` from ``self`` and all the cells that contain ``cell``
|
|
2102
|
+
as a subface.
|
|
2103
|
+
|
|
2104
|
+
INPUT:
|
|
2105
|
+
|
|
2106
|
+
- ``cell`` -- a cell of the polyhedral complex
|
|
2107
|
+
|
|
2108
|
+
- ``check`` -- boolean (default: ``False``); if ``True``,
|
|
2109
|
+
raise an error if ``cell`` is not a cell of this complex
|
|
2110
|
+
|
|
2111
|
+
This does not return anything; instead, it **changes** the
|
|
2112
|
+
polyhedral complex.
|
|
2113
|
+
|
|
2114
|
+
EXAMPLES:
|
|
2115
|
+
|
|
2116
|
+
If you add a cell which is already present, there is no effect::
|
|
2117
|
+
|
|
2118
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
2119
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
2120
|
+
sage: r = Polyhedron(rays=[(1, 0)])
|
|
2121
|
+
sage: pc = PolyhedralComplex([p1, p2, r])
|
|
2122
|
+
sage: pc.dimension()
|
|
2123
|
+
2
|
|
2124
|
+
sage: pc.remove_cell(Polyhedron(vertices=[(0, 0), (1, 2)]))
|
|
2125
|
+
sage: pc.dimension()
|
|
2126
|
+
1
|
|
2127
|
+
sage: pc
|
|
2128
|
+
Polyhedral complex with 5 maximal cells
|
|
2129
|
+
sage: pc.remove_cell(Polyhedron(vertices=[(1, 2)]))
|
|
2130
|
+
sage: pc.dimension()
|
|
2131
|
+
1
|
|
2132
|
+
sage: pc
|
|
2133
|
+
Polyhedral complex with 3 maximal cells
|
|
2134
|
+
sage: pc.remove_cell(Polyhedron(vertices=[(0, 0)]))
|
|
2135
|
+
sage: pc.dimension()
|
|
2136
|
+
0
|
|
2137
|
+
|
|
2138
|
+
TESTS:
|
|
2139
|
+
|
|
2140
|
+
Check that :exc:`ValueError` and empty complex are treated properly::
|
|
2141
|
+
|
|
2142
|
+
sage: p = Polyhedron(vertices=[[1]])
|
|
2143
|
+
sage: pc = PolyhedralComplex([p])
|
|
2144
|
+
sage: pc.remove_cell(Polyhedron(vertices=[[0]]), check=True)
|
|
2145
|
+
Traceback (most recent call last):
|
|
2146
|
+
...
|
|
2147
|
+
ValueError: trying to remove a cell which is not in the polyhedral complex
|
|
2148
|
+
sage: pc.remove_cell(Polyhedron(vertices=[(1, 1)]))
|
|
2149
|
+
Traceback (most recent call last):
|
|
2150
|
+
...
|
|
2151
|
+
ValueError: the given cell is not a polyhedron in the same ambient space
|
|
2152
|
+
sage: pc.remove_cell(p)
|
|
2153
|
+
sage: pc.dimension()
|
|
2154
|
+
-1
|
|
2155
|
+
sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0]])], is_mutable=False)
|
|
2156
|
+
sage: pc.remove_cell(Polyhedron(vertices=[[0]]))
|
|
2157
|
+
Traceback (most recent call last):
|
|
2158
|
+
...
|
|
2159
|
+
ValueError: this polyhedral complex is not mutable
|
|
2160
|
+
|
|
2161
|
+
Check that this function is coherent with
|
|
2162
|
+
:meth:`~sage.topology.simplicial_complex.SimplicialComplex.remove_face`::
|
|
2163
|
+
|
|
2164
|
+
sage: v1 = (1, 0, 0, 0); v2 = (0, 1, 0, 0); v3 = (0, 0, 1, 0); v4 = (0, 0, 0, 1)
|
|
2165
|
+
sage: Z = PolyhedralComplex([Polyhedron(vertices=[v1, v2, v3, v4])]); Z
|
|
2166
|
+
Polyhedral complex with 1 maximal cell
|
|
2167
|
+
sage: Z.remove_cell(Polyhedron(vertices=[v1, v2]))
|
|
2168
|
+
sage: Z
|
|
2169
|
+
Polyhedral complex with 2 maximal cells
|
|
2170
|
+
sage: [c.vertices_list() for c in Z.maximal_cells_sorted()]
|
|
2171
|
+
[[[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]],
|
|
2172
|
+
[[0, 0, 0, 1], [0, 0, 1, 0], [1, 0, 0, 0]]]
|
|
2173
|
+
|
|
2174
|
+
sage: v0 = (0, 0, 0, 0)
|
|
2175
|
+
sage: S = PolyhedralComplex([Polyhedron(vertices=[v0, v1, v2]), Polyhedron(vertices=[v2, v3])])
|
|
2176
|
+
sage: S.maximal_cells()
|
|
2177
|
+
{1: {A 1-dimensional polyhedron in ZZ^4 defined as the convex hull of 2 vertices},
|
|
2178
|
+
2: {A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices}}
|
|
2179
|
+
sage: S.remove_cell(Polyhedron(vertices=[v0, v1, v2]))
|
|
2180
|
+
sage: S
|
|
2181
|
+
Polyhedral complex with 4 maximal cells
|
|
2182
|
+
sage: [c.vertices_list() for c in S.maximal_cells_sorted()]
|
|
2183
|
+
[[[0, 0, 0, 0], [0, 1, 0, 0]],
|
|
2184
|
+
[[0, 0, 0, 0], [1, 0, 0, 0]],
|
|
2185
|
+
[[0, 0, 1, 0], [0, 1, 0, 0]],
|
|
2186
|
+
[[0, 1, 0, 0], [1, 0, 0, 0]]]
|
|
2187
|
+
|
|
2188
|
+
sage: T = PolyhedralComplex([Polyhedron(vertices=[[1], [2]]), Polyhedron(vertices=[[1], [-3]])])
|
|
2189
|
+
sage: T.remove_cell(Polyhedron(vertices=[[-3], [1]]))
|
|
2190
|
+
sage: [c.vertices_list() for c in T.maximal_cells_sorted()]
|
|
2191
|
+
[[[1], [2]], [[-3]]]
|
|
2192
|
+
sage: [c.vertices_list() for c in T.cells_sorted()]
|
|
2193
|
+
[[[1], [2]], [[-3]], [[1]], [[2]]]
|
|
2194
|
+
"""
|
|
2195
|
+
if self._is_immutable:
|
|
2196
|
+
raise ValueError("this polyhedral complex is not mutable")
|
|
2197
|
+
if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim:
|
|
2198
|
+
raise ValueError("the given cell is not a polyhedron " +
|
|
2199
|
+
"in the same ambient space")
|
|
2200
|
+
# if cell is not in self, delete nothing.
|
|
2201
|
+
if not self.is_cell(cell): # self.cells() is called
|
|
2202
|
+
if check:
|
|
2203
|
+
raise ValueError("trying to remove a cell which is not " +
|
|
2204
|
+
"in the polyhedral complex")
|
|
2205
|
+
return
|
|
2206
|
+
# update cells and face poset
|
|
2207
|
+
poset = self._face_poset
|
|
2208
|
+
deleting = poset.order_filter([cell])
|
|
2209
|
+
for c in deleting:
|
|
2210
|
+
d = c.dimension()
|
|
2211
|
+
self._cells[d].remove(c)
|
|
2212
|
+
if not self._cells[d]:
|
|
2213
|
+
del self._cells[d]
|
|
2214
|
+
covers = {p: [q for q in poset.upper_covers(p) if q not in deleting]
|
|
2215
|
+
for p in self.cell_iterator()}
|
|
2216
|
+
self._face_poset = Poset(covers)
|
|
2217
|
+
# update dim and maximal cells
|
|
2218
|
+
maximal_cells = self._face_poset.maximal_elements() # a list
|
|
2219
|
+
self._maximal_cells = cells_list_to_cells_dict(maximal_cells)
|
|
2220
|
+
if not maximal_cells:
|
|
2221
|
+
self._dim = -1
|
|
2222
|
+
else:
|
|
2223
|
+
self._dim = max(self._maximal_cells.keys())
|
|
2224
|
+
# reset cached attributes
|
|
2225
|
+
self._maximal_cells_sorted = None # needed for hash
|
|
2226
|
+
self._is_convex = None
|
|
2227
|
+
self._polyhedron = None
|
|
2228
|
+
|
|
2229
|
+
def is_simplicial_complex(self) -> bool:
|
|
2230
|
+
"""
|
|
2231
|
+
Test if this polyhedral complex is a simplicial complex.
|
|
2232
|
+
|
|
2233
|
+
A polyhedral complex is **simplicial** if all of its (maximal) cells
|
|
2234
|
+
are simplices, i.e., every cell is a bounded polytope with `d+1`
|
|
2235
|
+
vertices, where `d` is the dimension of the polytope.
|
|
2236
|
+
|
|
2237
|
+
EXAMPLES::
|
|
2238
|
+
|
|
2239
|
+
sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)])
|
|
2240
|
+
sage: p2 = Polyhedron(rays=[(1, 0)])
|
|
2241
|
+
sage: PolyhedralComplex([p1]).is_simplicial_complex()
|
|
2242
|
+
True
|
|
2243
|
+
sage: PolyhedralComplex([p2]).is_simplicial_complex()
|
|
2244
|
+
False
|
|
2245
|
+
"""
|
|
2246
|
+
return all(p.is_simplex() for p in self.maximal_cell_iterator())
|
|
2247
|
+
|
|
2248
|
+
def is_polyhedral_fan(self) -> bool:
|
|
2249
|
+
"""
|
|
2250
|
+
Test if this polyhedral complex is a polyhedral fan.
|
|
2251
|
+
|
|
2252
|
+
A polyhedral complex is a **fan** if all of its (maximal) cells
|
|
2253
|
+
are cones.
|
|
2254
|
+
|
|
2255
|
+
EXAMPLES::
|
|
2256
|
+
|
|
2257
|
+
sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)])
|
|
2258
|
+
sage: p2 = Polyhedron(rays=[(1, 0)])
|
|
2259
|
+
sage: PolyhedralComplex([p1]).is_polyhedral_fan()
|
|
2260
|
+
False
|
|
2261
|
+
sage: PolyhedralComplex([p2]).is_polyhedral_fan()
|
|
2262
|
+
True
|
|
2263
|
+
sage: halfplane = Polyhedron(rays=[(1, 0), (-1, 0), (0, 1)])
|
|
2264
|
+
sage: PolyhedralComplex([halfplane]).is_polyhedral_fan()
|
|
2265
|
+
True
|
|
2266
|
+
"""
|
|
2267
|
+
return all((p.n_vertices() == 1) and (
|
|
2268
|
+
vector(p.vertices_list()[0]) == p.ambient_space().zero())
|
|
2269
|
+
for p in self.maximal_cell_iterator())
|
|
2270
|
+
|
|
2271
|
+
def is_simplicial_fan(self) -> bool:
|
|
2272
|
+
"""
|
|
2273
|
+
Test if this polyhedral complex is a simplicial fan.
|
|
2274
|
+
|
|
2275
|
+
A polyhedral complex is a **simplicial fan** if all of its (maximal)
|
|
2276
|
+
cells are simplicial cones, i.e., every cell is a pointed cone (with
|
|
2277
|
+
vertex being the origin) generated by `d` linearly independent rays,
|
|
2278
|
+
where `d` is the dimension of the cone.
|
|
2279
|
+
|
|
2280
|
+
EXAMPLES::
|
|
2281
|
+
|
|
2282
|
+
sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)])
|
|
2283
|
+
sage: p2 = Polyhedron(rays=[(1, 0)])
|
|
2284
|
+
sage: PolyhedralComplex([p1]).is_simplicial_fan()
|
|
2285
|
+
False
|
|
2286
|
+
sage: PolyhedralComplex([p2]).is_simplicial_fan()
|
|
2287
|
+
True
|
|
2288
|
+
sage: halfplane = Polyhedron(rays=[(1, 0), (-1, 0), (0, 1)])
|
|
2289
|
+
sage: PolyhedralComplex([halfplane]).is_simplicial_fan()
|
|
2290
|
+
False
|
|
2291
|
+
"""
|
|
2292
|
+
return self.is_polyhedral_fan() and all(
|
|
2293
|
+
(p.n_lines() == 0 and p.n_rays() == p.dimension())
|
|
2294
|
+
for p in self.maximal_cell_iterator())
|
|
2295
|
+
|
|
2296
|
+
def subdivide(self, make_simplicial=False,
|
|
2297
|
+
new_vertices=None, new_rays=None):
|
|
2298
|
+
"""
|
|
2299
|
+
Construct a new polyhedral complex by iterative stellar subdivision of
|
|
2300
|
+
``self`` for each new vertex/ray given.
|
|
2301
|
+
|
|
2302
|
+
Currently, subdivision is only supported for bounded polyhedral complex
|
|
2303
|
+
or polyhedral fan.
|
|
2304
|
+
|
|
2305
|
+
INPUT:
|
|
2306
|
+
|
|
2307
|
+
- ``make_simplicial`` -- boolean (default: ``False``); if ``True``,
|
|
2308
|
+
the returned polyhedral complex is simplicial
|
|
2309
|
+
|
|
2310
|
+
- ``new_vertices``, ``new_rays`` -- list (optional); new generators
|
|
2311
|
+
to be added during subdivision
|
|
2312
|
+
|
|
2313
|
+
EXAMPLES::
|
|
2314
|
+
|
|
2315
|
+
sage: square_vertices = [(1, 1, 1), (-1, 1, 1), (-1, -1, 1), (1, -1, 1)]
|
|
2316
|
+
sage: pc = PolyhedralComplex([
|
|
2317
|
+
....: Polyhedron(vertices=[(0, 0, 0)] + square_vertices),
|
|
2318
|
+
....: Polyhedron(vertices=[(0, 0, 2)] + square_vertices)])
|
|
2319
|
+
sage: pc.is_compact() and not pc.is_simplicial_complex()
|
|
2320
|
+
True
|
|
2321
|
+
sage: subdivided_pc = pc.subdivide(new_vertices=[(0, 0, 1)])
|
|
2322
|
+
sage: subdivided_pc
|
|
2323
|
+
Polyhedral complex with 8 maximal cells
|
|
2324
|
+
sage: subdivided_pc.is_simplicial_complex()
|
|
2325
|
+
True
|
|
2326
|
+
sage: simplicial_pc = pc.subdivide(make_simplicial=True)
|
|
2327
|
+
sage: simplicial_pc
|
|
2328
|
+
Polyhedral complex with 4 maximal cells
|
|
2329
|
+
sage: simplicial_pc.is_simplicial_complex()
|
|
2330
|
+
True
|
|
2331
|
+
|
|
2332
|
+
sage: # needs sage.symbolic
|
|
2333
|
+
sage: fan = PolyhedralComplex([Polyhedron(rays=square_vertices)])
|
|
2334
|
+
sage: fan.is_polyhedral_fan() and not fan.is_simplicial_fan()
|
|
2335
|
+
True
|
|
2336
|
+
sage: fan.subdivide(new_vertices=[(0, 0, 1)])
|
|
2337
|
+
Traceback (most recent call last):
|
|
2338
|
+
...
|
|
2339
|
+
ValueError: new vertices cannot be used for subdivision
|
|
2340
|
+
sage: subdivided_fan = fan.subdivide(new_rays=[(0, 0, 1)])
|
|
2341
|
+
sage: subdivided_fan
|
|
2342
|
+
Polyhedral complex with 4 maximal cells
|
|
2343
|
+
sage: subdivided_fan.is_simplicial_fan()
|
|
2344
|
+
True
|
|
2345
|
+
sage: simplicial_fan = fan.subdivide(make_simplicial=True)
|
|
2346
|
+
sage: simplicial_fan
|
|
2347
|
+
Polyhedral complex with 2 maximal cells
|
|
2348
|
+
sage: simplicial_fan.is_simplicial_fan()
|
|
2349
|
+
True
|
|
2350
|
+
|
|
2351
|
+
sage: # needs sage.symbolic
|
|
2352
|
+
sage: halfspace = PolyhedralComplex([Polyhedron(rays=[(0, 0, 1)],
|
|
2353
|
+
....: lines=[(1, 0, 0), (0, 1, 0)])])
|
|
2354
|
+
sage: halfspace.is_simplicial_fan()
|
|
2355
|
+
False
|
|
2356
|
+
sage: subdiv_halfspace = halfspace.subdivide(make_simplicial=True)
|
|
2357
|
+
sage: subdiv_halfspace
|
|
2358
|
+
Polyhedral complex with 4 maximal cells
|
|
2359
|
+
sage: subdiv_halfspace.is_simplicial_fan()
|
|
2360
|
+
True
|
|
2361
|
+
"""
|
|
2362
|
+
if self.is_compact():
|
|
2363
|
+
if new_rays:
|
|
2364
|
+
raise ValueError("rays/lines cannot be used for subdivision")
|
|
2365
|
+
# bounded version of `fan.subdivide`; not require rational.
|
|
2366
|
+
vertices = set()
|
|
2367
|
+
if make_simplicial and not self.is_simplicial_complex():
|
|
2368
|
+
for p in self.maximal_cell_iterator():
|
|
2369
|
+
for v in p.vertices_list():
|
|
2370
|
+
vertices.add(tuple(v))
|
|
2371
|
+
if new_vertices:
|
|
2372
|
+
for v in new_vertices:
|
|
2373
|
+
vertices.add(tuple(v))
|
|
2374
|
+
if not vertices:
|
|
2375
|
+
return self # Nothing has to be done
|
|
2376
|
+
# bounded version of `fan._subdivide_stellar`; not require rational.
|
|
2377
|
+
cells = list(self.maximal_cell_iterator())
|
|
2378
|
+
for v in vertices:
|
|
2379
|
+
new = []
|
|
2380
|
+
for cell in cells:
|
|
2381
|
+
if v in cell:
|
|
2382
|
+
for cell_facet in cell.facets():
|
|
2383
|
+
facet = cell_facet.as_polyhedron()
|
|
2384
|
+
if v in facet:
|
|
2385
|
+
continue
|
|
2386
|
+
p = facet.convex_hull(Polyhedron(vertices=[v]))
|
|
2387
|
+
new.append(p)
|
|
2388
|
+
else:
|
|
2389
|
+
new.append(cell)
|
|
2390
|
+
cells = new
|
|
2391
|
+
return PolyhedralComplex(cells, maximality_check=False,
|
|
2392
|
+
backend=self._backend)
|
|
2393
|
+
elif self.is_polyhedral_fan():
|
|
2394
|
+
if new_vertices and any(vi != 0 for v in new_vertices for vi in v):
|
|
2395
|
+
raise ValueError("new vertices cannot be used for subdivision")
|
|
2396
|
+
# mimic :meth:`~sage.geometry.fan <RationalPolyhedralFan>.subdivide`
|
|
2397
|
+
# but here we allow for non-pointed cones, and we subdivide them.
|
|
2398
|
+
rays_normalized = set()
|
|
2399
|
+
self_rays = []
|
|
2400
|
+
cones = []
|
|
2401
|
+
for p in self.maximal_cell_iterator():
|
|
2402
|
+
prays = p.rays_list()
|
|
2403
|
+
for r in prays:
|
|
2404
|
+
r_n = vector(r).normalized()
|
|
2405
|
+
r_n.set_immutable()
|
|
2406
|
+
if r_n not in rays_normalized:
|
|
2407
|
+
rays_normalized.add(r_n)
|
|
2408
|
+
self_rays.append(vector(r))
|
|
2409
|
+
plines = p.lines_list()
|
|
2410
|
+
if not plines:
|
|
2411
|
+
cones.append(p)
|
|
2412
|
+
continue
|
|
2413
|
+
# consider a line as two rays
|
|
2414
|
+
for pl in plines:
|
|
2415
|
+
l_plus = vector(pl).normalized()
|
|
2416
|
+
l_plus.set_immutable()
|
|
2417
|
+
if l_plus not in rays_normalized:
|
|
2418
|
+
rays_normalized.add(l_plus)
|
|
2419
|
+
self_rays.append(vector(pl))
|
|
2420
|
+
l_minus = (-vector(pl)).normalized()
|
|
2421
|
+
l_minus.set_immutable()
|
|
2422
|
+
if l_minus not in rays_normalized:
|
|
2423
|
+
rays_normalized.add(l_minus)
|
|
2424
|
+
self_rays.append(-vector(pl))
|
|
2425
|
+
# subdivide the non-pointed p into pointed cones
|
|
2426
|
+
# we rely on the canonical V-repr of Sage polyhedra.
|
|
2427
|
+
num_lines = len(plines)
|
|
2428
|
+
for neg_rays in powerset(range(num_lines)):
|
|
2429
|
+
lines = [vector(plines[i]) if i not in neg_rays
|
|
2430
|
+
else -vector(plines[i]) for i in range(num_lines)]
|
|
2431
|
+
cones.append(Polyhedron(rays=(prays + lines),
|
|
2432
|
+
backend=self._backend))
|
|
2433
|
+
rays = []
|
|
2434
|
+
if new_rays:
|
|
2435
|
+
for r in new_rays:
|
|
2436
|
+
if vector(r).is_zero():
|
|
2437
|
+
raise ValueError("zero cannot be used for subdivision")
|
|
2438
|
+
r_n = vector(r).normalized()
|
|
2439
|
+
r_n.set_immutable()
|
|
2440
|
+
if r_n not in rays_normalized:
|
|
2441
|
+
rays_normalized.add(r_n)
|
|
2442
|
+
rays.append(vector(r))
|
|
2443
|
+
if make_simplicial and not self.is_simplicial_fan():
|
|
2444
|
+
rays = self_rays + rays
|
|
2445
|
+
if not rays:
|
|
2446
|
+
return self # Nothing has to be done
|
|
2447
|
+
# mimic :class:`RationalPolyhedralFan`._subdivide_stellar(rays)
|
|
2448
|
+
# start with self maximal cells (subdivided into pointed cones)
|
|
2449
|
+
for ray in rays:
|
|
2450
|
+
new = []
|
|
2451
|
+
for cone in cones:
|
|
2452
|
+
if ray in cone:
|
|
2453
|
+
for cone_facet in cone.facets():
|
|
2454
|
+
facet = cone_facet.as_polyhedron()
|
|
2455
|
+
if ray in facet:
|
|
2456
|
+
continue
|
|
2457
|
+
new_cone = facet.convex_hull(Polyhedron(rays=[ray]))
|
|
2458
|
+
new.append(new_cone)
|
|
2459
|
+
else:
|
|
2460
|
+
new.append(cone)
|
|
2461
|
+
cones = new
|
|
2462
|
+
return PolyhedralComplex(cones, maximality_check=False,
|
|
2463
|
+
backend=self._backend)
|
|
2464
|
+
else:
|
|
2465
|
+
# TODO: ``self`` is unbounded, make it projectively simplicial.
|
|
2466
|
+
# (1) homogenize self of dim d to fan in space of dim d+1;
|
|
2467
|
+
# (2) call fan.subdivide(make_simplicial=True);
|
|
2468
|
+
# (3) take section back to the space of dim d.
|
|
2469
|
+
raise NotImplementedError('subdivision of a non-compact polyhedral ' +
|
|
2470
|
+
'complex that is not a fan is not supported')
|
|
2471
|
+
|
|
2472
|
+
############################################################
|
|
2473
|
+
# Helper functions
|
|
2474
|
+
############################################################
|
|
2475
|
+
|
|
2476
|
+
|
|
2477
|
+
def cells_list_to_cells_dict(cells_list) -> dict:
|
|
2478
|
+
r"""
|
|
2479
|
+
Helper function that returns the dictionary whose keys are the dimensions,
|
|
2480
|
+
and the value associated to an integer `d` is the set of `d`-dimensional
|
|
2481
|
+
polyhedra in the given list.
|
|
2482
|
+
|
|
2483
|
+
EXAMPLES::
|
|
2484
|
+
|
|
2485
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
2486
|
+
sage: p2 = Polyhedron(vertices=[(1, 1), (0, 0)])
|
|
2487
|
+
sage: p3 = Polyhedron(vertices=[(0, 0)])
|
|
2488
|
+
sage: p4 = Polyhedron(vertices=[(1, 1)])
|
|
2489
|
+
sage: sage.geometry.polyhedral_complex.cells_list_to_cells_dict([p1, p2, p3, p4])
|
|
2490
|
+
{0: {A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
|
|
2491
|
+
A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex},
|
|
2492
|
+
1: {A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices},
|
|
2493
|
+
2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}}
|
|
2494
|
+
"""
|
|
2495
|
+
cells_dict = {}
|
|
2496
|
+
for cell in cells_list:
|
|
2497
|
+
d = cell.dimension()
|
|
2498
|
+
if d in cells_dict:
|
|
2499
|
+
cells_dict[d].add(cell)
|
|
2500
|
+
else:
|
|
2501
|
+
cells_dict[d] = set([cell])
|
|
2502
|
+
return cells_dict
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
def exploded_plot(polyhedra, *,
|
|
2506
|
+
center=None, explosion_factor=1, sticky_vertices=False,
|
|
2507
|
+
sticky_center=True, point=None, **kwds):
|
|
2508
|
+
r"""
|
|
2509
|
+
Return a plot of several ``polyhedra`` in one figure with extra space
|
|
2510
|
+
between them.
|
|
2511
|
+
|
|
2512
|
+
INPUT:
|
|
2513
|
+
|
|
2514
|
+
- ``polyhedra`` -- an iterable of
|
|
2515
|
+
:class:`~sage.geometry.polyhedron.base.Polyhedron_base` objects
|
|
2516
|
+
|
|
2517
|
+
- ``center`` -- (default: ``None``, denoting the origin) the center of
|
|
2518
|
+
explosion
|
|
2519
|
+
|
|
2520
|
+
- ``explosion_factor`` -- (default: 1) a nonnegative number; translate
|
|
2521
|
+
polyhedra by this factor of the distance from ``center`` to their center
|
|
2522
|
+
|
|
2523
|
+
- ``sticky_vertices`` -- (default: ``False``) boolean or dict; whether to
|
|
2524
|
+
draw line segments between shared vertices of the given polyhedra. A dict
|
|
2525
|
+
gives options for :func:`sage.plot.line`.
|
|
2526
|
+
|
|
2527
|
+
- ``sticky_center`` -- (default: ``True``) boolean or dict. When ``center``
|
|
2528
|
+
is a vertex of some of the polyhedra, whether to draw line segments
|
|
2529
|
+
connecting the ``center`` to the shifted copies of these vertices. A dict
|
|
2530
|
+
gives options for :func:`sage.plot.line`.
|
|
2531
|
+
|
|
2532
|
+
- ``color`` -- (default: ``None``) if ``'rainbow'``, assign a different
|
|
2533
|
+
color to every maximal cell and every vertex; otherwise, passed on to
|
|
2534
|
+
:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`
|
|
2535
|
+
|
|
2536
|
+
- other keyword arguments are passed on to
|
|
2537
|
+
:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`
|
|
2538
|
+
|
|
2539
|
+
EXAMPLES::
|
|
2540
|
+
|
|
2541
|
+
sage: from sage.geometry.polyhedral_complex import exploded_plot
|
|
2542
|
+
sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])
|
|
2543
|
+
sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])
|
|
2544
|
+
sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)])
|
|
2545
|
+
sage: exploded_plot([p1, p2, p3]) # needs sage.plot
|
|
2546
|
+
Graphics object consisting of 20 graphics primitives
|
|
2547
|
+
sage: exploded_plot([p1, p2, p3], center=(1, 1)) # needs sage.plot
|
|
2548
|
+
Graphics object consisting of 19 graphics primitives
|
|
2549
|
+
sage: exploded_plot([p1, p2, p3], center=(1, 1), sticky_vertices=True) # needs sage.plot
|
|
2550
|
+
Graphics object consisting of 23 graphics primitives
|
|
2551
|
+
"""
|
|
2552
|
+
from sage.plot.colors import rainbow
|
|
2553
|
+
from sage.plot.graphics import Graphics
|
|
2554
|
+
from sage.plot.line import line
|
|
2555
|
+
from sage.plot.point import point as plot_point
|
|
2556
|
+
import itertools
|
|
2557
|
+
|
|
2558
|
+
polyhedra = list(polyhedra)
|
|
2559
|
+
g = Graphics()
|
|
2560
|
+
if not polyhedra:
|
|
2561
|
+
return g
|
|
2562
|
+
dim = polyhedra[0].ambient_dimension()
|
|
2563
|
+
if center is None:
|
|
2564
|
+
from sage.rings.rational_field import QQ
|
|
2565
|
+
center = vector(QQ, dim)
|
|
2566
|
+
else:
|
|
2567
|
+
center = vector(center)
|
|
2568
|
+
translations = [explosion_factor * ((p.center()
|
|
2569
|
+
+ sum(r.vector() for r in p.rays()))
|
|
2570
|
+
- center)
|
|
2571
|
+
for p in polyhedra]
|
|
2572
|
+
vertex_translations_dict = {}
|
|
2573
|
+
for P, t in zip(polyhedra, translations):
|
|
2574
|
+
for v in P.vertices():
|
|
2575
|
+
v = v.vector()
|
|
2576
|
+
v.set_immutable()
|
|
2577
|
+
vertex_translations_dict[v] = vertex_translations_dict.get(v, [])
|
|
2578
|
+
vertex_translations_dict[v].append(v + t)
|
|
2579
|
+
|
|
2580
|
+
color = kwds.get('color')
|
|
2581
|
+
if color == 'rainbow':
|
|
2582
|
+
cell_colors_dict = dict(zip(polyhedra,
|
|
2583
|
+
rainbow(len(polyhedra))))
|
|
2584
|
+
for p, t in zip(polyhedra, translations):
|
|
2585
|
+
options = copy(kwds)
|
|
2586
|
+
if color == 'rainbow':
|
|
2587
|
+
options['color'] = cell_colors_dict[p]
|
|
2588
|
+
g += (p + t).plot(point=False, **options)
|
|
2589
|
+
|
|
2590
|
+
if sticky_vertices or sticky_center:
|
|
2591
|
+
if sticky_vertices is True:
|
|
2592
|
+
sticky_vertices = dict(color='gray')
|
|
2593
|
+
if sticky_center is True:
|
|
2594
|
+
sticky_center = dict(color='gray')
|
|
2595
|
+
for vertex, vertex_translations in vertex_translations_dict.items():
|
|
2596
|
+
if vertex == center:
|
|
2597
|
+
if sticky_center:
|
|
2598
|
+
for vt in vertex_translations:
|
|
2599
|
+
g += line((center, vt), **sticky_center)
|
|
2600
|
+
else:
|
|
2601
|
+
if sticky_vertices:
|
|
2602
|
+
for vt1, vt2 in itertools.combinations(vertex_translations, 2):
|
|
2603
|
+
g += line((vt1, vt2), **sticky_vertices)
|
|
2604
|
+
if point is None:
|
|
2605
|
+
# default from sage.geometry.polyhedron.plot
|
|
2606
|
+
point = dict(size=10)
|
|
2607
|
+
if point is not False:
|
|
2608
|
+
if color == 'rainbow':
|
|
2609
|
+
vertex_colors_dict = dict(zip(vertex_translations_dict.keys(),
|
|
2610
|
+
rainbow(len(vertex_translations_dict.keys()))))
|
|
2611
|
+
for vertex, vertex_translations in vertex_translations_dict.items():
|
|
2612
|
+
options = copy(point)
|
|
2613
|
+
if color == 'rainbow':
|
|
2614
|
+
options['color'] = vertex_colors_dict[vertex]
|
|
2615
|
+
g += plot_point(vertex_translations, **options)
|
|
2616
|
+
return g
|