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,3502 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Library of commonly used, famous, or interesting polytopes
|
|
4
|
+
|
|
5
|
+
This module gathers several constructors of polytopes that can be reached
|
|
6
|
+
through ``polytopes.<tab>``. For example, here is the hypercube in dimension 5::
|
|
7
|
+
|
|
8
|
+
sage: polytopes.hypercube(5)
|
|
9
|
+
A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 32 vertices
|
|
10
|
+
|
|
11
|
+
The following constructions are available
|
|
12
|
+
|
|
13
|
+
.. csv-table::
|
|
14
|
+
:class: contentstable
|
|
15
|
+
:widths: 30
|
|
16
|
+
:delim: |
|
|
17
|
+
|
|
18
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.Birkhoff_polytope`
|
|
19
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.associahedron`
|
|
20
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.bitruncated_six_hundred_cell`
|
|
21
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.buckyball`
|
|
22
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_one_hundred_twenty_cell`
|
|
23
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_six_hundred_cell`
|
|
24
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_one_hundred_twenty_cell`
|
|
25
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_six_hundred_cell`
|
|
26
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cross_polytope`
|
|
27
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cube`
|
|
28
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cuboctahedron`
|
|
29
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.cyclic_polytope`
|
|
30
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.dodecahedron`
|
|
31
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.flow_polytope`
|
|
32
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.Gosset_3_21`
|
|
33
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.grand_antiprism`
|
|
34
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.great_rhombicuboctahedron`
|
|
35
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.hypercube`
|
|
36
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.hypersimplex`
|
|
37
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.icosahedron`
|
|
38
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.icosidodecahedron`
|
|
39
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.Kirkman_icosahedron`
|
|
40
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.octahedron`
|
|
41
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_one_hundred_twenty_cell`
|
|
42
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_six_hundred_cell`
|
|
43
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.one_hundred_twenty_cell`
|
|
44
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.parallelotope`
|
|
45
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.pentakis_dodecahedron`
|
|
46
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.permutahedron`
|
|
47
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.generalized_permutahedron`
|
|
48
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_one_hundred_twenty_cell`
|
|
49
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_six_hundred_cell`
|
|
50
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.regular_polygon`
|
|
51
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.rhombic_dodecahedron`
|
|
52
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.rhombicosidodecahedron`
|
|
53
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.runcinated_one_hundred_twenty_cell`
|
|
54
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_one_hundred_twenty_cell`
|
|
55
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_six_hundred_cell`
|
|
56
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.simplex`
|
|
57
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.six_hundred_cell`
|
|
58
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.small_rhombicuboctahedron`
|
|
59
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.snub_cube`
|
|
60
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.snub_dodecahedron`
|
|
61
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.tetrahedron`
|
|
62
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_cube`
|
|
63
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_dodecahedron`
|
|
64
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_icosidodecahedron`
|
|
65
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_tetrahedron`
|
|
66
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_octahedron`
|
|
67
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_one_hundred_twenty_cell`
|
|
68
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_six_hundred_cell`
|
|
69
|
+
:meth:`~sage.geometry.polyhedron.library.Polytopes.twenty_four_cell`
|
|
70
|
+
"""
|
|
71
|
+
########################################################################
|
|
72
|
+
# Copyright (C) 2008 Marshall Hampton <hamptonio@gmail.com>
|
|
73
|
+
# 2011 Volker Braun <vbraun.name@gmail.com>
|
|
74
|
+
# 2015 Vincent Delecroix <20100.delecroix@gmail.com>
|
|
75
|
+
# 2019 Jean-Philippe Labbé <labbe@math.fu-berlin.de>
|
|
76
|
+
#
|
|
77
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
78
|
+
#
|
|
79
|
+
# https://www.gnu.org/licenses/
|
|
80
|
+
########################################################################
|
|
81
|
+
|
|
82
|
+
import itertools
|
|
83
|
+
|
|
84
|
+
from sage.rings.integer_ring import ZZ
|
|
85
|
+
from sage.misc.lazy_import import lazy_import
|
|
86
|
+
from sage.rings.rational_field import QQ
|
|
87
|
+
lazy_import('sage.combinat.permutation', 'Permutations')
|
|
88
|
+
lazy_import('sage.groups.perm_gps.permgroup_named', 'AlternatingGroup')
|
|
89
|
+
from .constructor import Polyhedron
|
|
90
|
+
from .parent import Polyhedra
|
|
91
|
+
lazy_import('sage.graphs.digraph', 'DiGraph')
|
|
92
|
+
lazy_import('sage.graphs.graph', 'Graph')
|
|
93
|
+
lazy_import('sage.combinat.root_system.associahedron', 'Associahedron')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def zero_sum_projection(d, base_ring=None):
|
|
97
|
+
r"""
|
|
98
|
+
Return a matrix corresponding to the projection on the orthogonal of
|
|
99
|
+
`(1,1,\ldots,1)` in dimension `d`.
|
|
100
|
+
|
|
101
|
+
The projection maps the orthonormal basis `(1,-1,0,\ldots,0) / \sqrt(2)`,
|
|
102
|
+
`(1,1,-1,0,\ldots,0) / \sqrt(3)`, \ldots, `(1,1,\ldots,1,-1) / \sqrt(d)` to
|
|
103
|
+
the canonical basis in `\RR^{d-1}`.
|
|
104
|
+
|
|
105
|
+
OUTPUT:
|
|
106
|
+
|
|
107
|
+
A matrix of dimensions `(d-1)\times d` defined over ``base_ring`` (default:
|
|
108
|
+
:class:`RDF <sage.rings.real_double.RealDoubleField_class>`).
|
|
109
|
+
|
|
110
|
+
EXAMPLES::
|
|
111
|
+
|
|
112
|
+
sage: from sage.geometry.polyhedron.library import zero_sum_projection
|
|
113
|
+
sage: zero_sum_projection(2)
|
|
114
|
+
[ 0.7071067811865475 -0.7071067811865475]
|
|
115
|
+
sage: zero_sum_projection(3)
|
|
116
|
+
[ 0.7071067811865475 -0.7071067811865475 0.0]
|
|
117
|
+
[ 0.4082482904638631 0.4082482904638631 -0.8164965809277261]
|
|
118
|
+
|
|
119
|
+
Exact computation in :class:`AA <sage.rings.qqbar.AlgebraicRealField>`::
|
|
120
|
+
|
|
121
|
+
sage: zero_sum_projection(3, base_ring=AA) # needs sage.rings.number_field
|
|
122
|
+
[ 0.7071067811865475? -0.7071067811865475? 0]
|
|
123
|
+
[ 0.4082482904638630? 0.4082482904638630? -0.8164965809277260?]
|
|
124
|
+
"""
|
|
125
|
+
from sage.matrix.constructor import matrix
|
|
126
|
+
from sage.modules.free_module_element import vector
|
|
127
|
+
if base_ring is None:
|
|
128
|
+
from sage.rings.real_double import RDF as base_ring
|
|
129
|
+
basis = [vector(base_ring, [1]*i + [-i] + [0]*(d-i-1)) for i in range(1, d)]
|
|
130
|
+
return matrix(base_ring, [v / v.norm() for v in basis])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def project_points(*points, **kwds):
|
|
134
|
+
"""
|
|
135
|
+
Projects a set of points into a vector space of dimension one less.
|
|
136
|
+
|
|
137
|
+
INPUT:
|
|
138
|
+
|
|
139
|
+
- ``points``... -- the points to project
|
|
140
|
+
|
|
141
|
+
- ``base_ring`` -- (defaults to ``RDF`` if keyword is ``None`` or not
|
|
142
|
+
provided in ``kwds``) the base ring to use
|
|
143
|
+
|
|
144
|
+
The projection is isometric to the orthogonal projection on the hyperplane
|
|
145
|
+
made of zero sum vector. Hence, if the set of points have all equal sums,
|
|
146
|
+
then their projection is isometric (as a set of points).
|
|
147
|
+
|
|
148
|
+
The projection used is the matrix given by :func:`zero_sum_projection`.
|
|
149
|
+
|
|
150
|
+
EXAMPLES::
|
|
151
|
+
|
|
152
|
+
sage: from sage.geometry.polyhedron.library import project_points
|
|
153
|
+
sage: project_points([2,-1,3,2]) # abs tol 1e-15
|
|
154
|
+
[(2.1213203435596424, -2.041241452319315, -0.577350269189626)]
|
|
155
|
+
sage: project_points([1,2,3],[3,3,5]) # abs tol 1e-15
|
|
156
|
+
[(-0.7071067811865475, -1.2247448713915892), (0.0, -1.6329931618554523)]
|
|
157
|
+
|
|
158
|
+
These projections are compatible with the restriction. More precisely,
|
|
159
|
+
given a vector `v`, the projection of `v` restricted to the first `i`
|
|
160
|
+
coordinates will be equal to the projection of the first `i+1` coordinates
|
|
161
|
+
of `v`::
|
|
162
|
+
|
|
163
|
+
sage: project_points([1,2]) # abs tol 1e-15
|
|
164
|
+
[(-0.7071067811865475)]
|
|
165
|
+
sage: project_points([1,2,3]) # abs tol 1e-15
|
|
166
|
+
[(-0.7071067811865475, -1.2247448713915892)]
|
|
167
|
+
sage: project_points([1,2,3,4]) # abs tol 1e-15
|
|
168
|
+
[(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776)]
|
|
169
|
+
sage: project_points([1,2,3,4,0]) # abs tol 1e-15
|
|
170
|
+
[(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776, 2.23606797749979)]
|
|
171
|
+
|
|
172
|
+
Check that it is (almost) an isometry::
|
|
173
|
+
|
|
174
|
+
sage: V = list(map(vector, IntegerVectors(n=5, length=3)))
|
|
175
|
+
sage: P = project_points(*V)
|
|
176
|
+
sage: for i in range(21): # needs sage.combinat sage.symbolic
|
|
177
|
+
....: for j in range(21):
|
|
178
|
+
....: assert abs((V[i]-V[j]).norm() - (P[i]-P[j]).norm()) < 0.00001
|
|
179
|
+
|
|
180
|
+
Example with exact computation::
|
|
181
|
+
|
|
182
|
+
sage: V = [ vector(v) for v in IntegerVectors(n=4, length=2) ]
|
|
183
|
+
sage: P = project_points(*V, base_ring=AA) # needs sage.combinat sage.rings.number_field
|
|
184
|
+
sage: for i in range(len(V)): # needs sage.combinat sage.rings.number_field sage.symbolic
|
|
185
|
+
....: for j in range(len(V)):
|
|
186
|
+
....: assert (V[i]-V[j]).norm() == (P[i]-P[j]).norm()
|
|
187
|
+
"""
|
|
188
|
+
if not points:
|
|
189
|
+
return []
|
|
190
|
+
base_ring = kwds.pop('base_ring', None)
|
|
191
|
+
if base_ring is None:
|
|
192
|
+
from sage.rings.real_double import RDF as base_ring
|
|
193
|
+
from sage.modules.free_module_element import vector
|
|
194
|
+
vecs = [vector(base_ring, p) for p in points]
|
|
195
|
+
m = zero_sum_projection(len(vecs[0]), base_ring=base_ring)
|
|
196
|
+
return [m * v for v in vecs]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def gale_transform_to_polytope(vectors, base_ring=None, backend=None):
|
|
200
|
+
r"""
|
|
201
|
+
Return the polytope associated to the list of vectors forming a Gale transform.
|
|
202
|
+
|
|
203
|
+
This function is the inverse of
|
|
204
|
+
:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform`
|
|
205
|
+
up to projective transformation.
|
|
206
|
+
|
|
207
|
+
INPUT:
|
|
208
|
+
|
|
209
|
+
- ``vectors`` -- the vectors of the Gale transform
|
|
210
|
+
|
|
211
|
+
- ``base_ring`` -- string (default: ``None``);
|
|
212
|
+
the base ring to be used for the construction
|
|
213
|
+
|
|
214
|
+
- ``backend`` -- string (default: ``None``);
|
|
215
|
+
the backend to use to create the polytope
|
|
216
|
+
|
|
217
|
+
.. NOTE::
|
|
218
|
+
|
|
219
|
+
The order of the input vectors will not be preserved.
|
|
220
|
+
|
|
221
|
+
If the center of the (input) vectors is the origin,
|
|
222
|
+
the function is much faster and might give a nicer representation
|
|
223
|
+
of the polytope.
|
|
224
|
+
|
|
225
|
+
If this is not the case, the vectors will be scaled
|
|
226
|
+
(each by a positive scalar) accordingly to obtain the polytope.
|
|
227
|
+
|
|
228
|
+
.. SEEALSO::
|
|
229
|
+
|
|
230
|
+
:func`~sage.geometry.polyhedron.library.gale_transform_to_primal`.
|
|
231
|
+
|
|
232
|
+
EXAMPLES::
|
|
233
|
+
|
|
234
|
+
sage: from sage.geometry.polyhedron.library import gale_transform_to_polytope
|
|
235
|
+
sage: points = polytopes.octahedron().gale_transform()
|
|
236
|
+
sage: points
|
|
237
|
+
((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1))
|
|
238
|
+
sage: P = gale_transform_to_polytope(points); P
|
|
239
|
+
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
|
|
240
|
+
sage: P.vertices()
|
|
241
|
+
(A vertex at (-1, 0, 0),
|
|
242
|
+
A vertex at (0, -1, 0),
|
|
243
|
+
A vertex at (0, 0, -1),
|
|
244
|
+
A vertex at (0, 0, 1),
|
|
245
|
+
A vertex at (0, 1, 0),
|
|
246
|
+
A vertex at (1, 0, 0))
|
|
247
|
+
|
|
248
|
+
One can specify the base ring::
|
|
249
|
+
|
|
250
|
+
sage: gale_transform_to_polytope( # needs sage.libs.pari
|
|
251
|
+
....: [(1,1), (-1,-1), (1,0),
|
|
252
|
+
....: (-1,0), (1,-1), (-2,1)]).vertices()
|
|
253
|
+
(A vertex at (-25, 0, 0),
|
|
254
|
+
A vertex at (-15, 50, -60),
|
|
255
|
+
A vertex at (0, -25, 0),
|
|
256
|
+
A vertex at (0, 0, -25),
|
|
257
|
+
A vertex at (16, -35, 54),
|
|
258
|
+
A vertex at (24, 10, 31))
|
|
259
|
+
sage: gale_transform_to_polytope( # needs cddexec
|
|
260
|
+
....: [(1,1), (-1,-1), (1,0),
|
|
261
|
+
....: (-1,0), (1,-1), (-2,1)],
|
|
262
|
+
....: base_ring=RDF).vertices()
|
|
263
|
+
(A vertex at (-0.64, 1.4, -2.16),
|
|
264
|
+
A vertex at (-0.96, -0.4, -1.24),
|
|
265
|
+
A vertex at (0.6, -2.0, 2.4),
|
|
266
|
+
A vertex at (1.0, 0.0, 0.0),
|
|
267
|
+
A vertex at (0.0, 1.0, 0.0),
|
|
268
|
+
A vertex at (0.0, 0.0, 1.0))
|
|
269
|
+
|
|
270
|
+
One can also specify the backend::
|
|
271
|
+
|
|
272
|
+
sage: gale_transform_to_polytope(
|
|
273
|
+
....: [(1,1), (-1,-1), (1,0),
|
|
274
|
+
....: (-1,0), (1,-1), (-2,1)],
|
|
275
|
+
....: backend='field').backend()
|
|
276
|
+
'field'
|
|
277
|
+
sage: gale_transform_to_polytope( # needs cddexec
|
|
278
|
+
....: [(1,1), (-1,-1), (1,0),
|
|
279
|
+
....: (-1,0), (1,-1), (-2,1)],
|
|
280
|
+
....: backend='cdd', base_ring=RDF).backend()
|
|
281
|
+
'cdd'
|
|
282
|
+
|
|
283
|
+
A gale transform corresponds to a polytope if and only if
|
|
284
|
+
every oriented (linear) hyperplane
|
|
285
|
+
has at least two vectors on each side.
|
|
286
|
+
See Theorem 6.19 of [Zie2007]_.
|
|
287
|
+
If this is not the case, one of two errors is raised.
|
|
288
|
+
|
|
289
|
+
If there is such a hyperplane with no vector on one side,
|
|
290
|
+
the vectors are not totally cyclic::
|
|
291
|
+
|
|
292
|
+
sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,0)])
|
|
293
|
+
Traceback (most recent call last):
|
|
294
|
+
...
|
|
295
|
+
ValueError: input vectors not totally cyclic
|
|
296
|
+
|
|
297
|
+
If every hyperplane has at least one vector on each side, then the gale
|
|
298
|
+
transform corresponds to a point configuration.
|
|
299
|
+
It corresponds to a polytope if and only if this point configuration is
|
|
300
|
+
convex and if and only if every hyperplane contains at least two vectors of
|
|
301
|
+
the gale transform on each side.
|
|
302
|
+
|
|
303
|
+
If this is not the case, an error is raised::
|
|
304
|
+
|
|
305
|
+
sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,-1)])
|
|
306
|
+
Traceback (most recent call last):
|
|
307
|
+
...
|
|
308
|
+
ValueError: the gale transform does not correspond to a polytope
|
|
309
|
+
"""
|
|
310
|
+
vertices = gale_transform_to_primal(vectors, base_ring, backend)
|
|
311
|
+
P = Polyhedron(vertices=vertices, base_ring=base_ring, backend=backend)
|
|
312
|
+
|
|
313
|
+
if not P.n_vertices() == len(vertices):
|
|
314
|
+
# If the input vectors are not totally cyclic, ``gale_transform_to_primal``
|
|
315
|
+
# raises an error.
|
|
316
|
+
# As no error was raised so far, the gale transform corresponds to
|
|
317
|
+
# to a point configuration.
|
|
318
|
+
# It corresponds to a polytope if and only if
|
|
319
|
+
# ``vertices`` are in convex position.
|
|
320
|
+
raise ValueError("the gale transform does not correspond to a polytope")
|
|
321
|
+
|
|
322
|
+
return P
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def gale_transform_to_primal(vectors, base_ring=None, backend=None):
|
|
326
|
+
r"""
|
|
327
|
+
Return a point configuration dual to a totally cyclic vector configuration.
|
|
328
|
+
|
|
329
|
+
This is the dehomogenized vector configuration dual to the input.
|
|
330
|
+
The dual vector configuration is acyclic and can therefore
|
|
331
|
+
be dehomogenized as the input is totally cyclic.
|
|
332
|
+
|
|
333
|
+
INPUT:
|
|
334
|
+
|
|
335
|
+
- ``vectors`` -- the ordered vectors of the Gale transform
|
|
336
|
+
|
|
337
|
+
- ``base_ring`` -- string (default: ``None``);
|
|
338
|
+
the base ring to be used for the construction
|
|
339
|
+
|
|
340
|
+
- ``backend`` -- string (default: ``None``);
|
|
341
|
+
the backend to be use to construct a polyhedral,
|
|
342
|
+
used internally in case the center is not the origin,
|
|
343
|
+
see :func:`~sage.geometry.polyhedron.constructor.Polyhedron`
|
|
344
|
+
|
|
345
|
+
OUTPUT: an ordered point configuration as list of vectors
|
|
346
|
+
|
|
347
|
+
.. NOTE::
|
|
348
|
+
|
|
349
|
+
If the center of the (input) vectors is the origin,
|
|
350
|
+
the function is much faster and might give a nicer representation
|
|
351
|
+
of the point configuration.
|
|
352
|
+
|
|
353
|
+
If this is not the case, the vectors will be scaled
|
|
354
|
+
(each by a positive scalar) accordingly.
|
|
355
|
+
|
|
356
|
+
ALGORITHM:
|
|
357
|
+
|
|
358
|
+
Step 1: If the center of the (input) vectors is not the origin,
|
|
359
|
+
we do an appropriate transformation to make it so.
|
|
360
|
+
|
|
361
|
+
Step 2: We add a row of ones on top of ``Matrix(vectors)``.
|
|
362
|
+
The right kernel of this larger matrix is the dual configuration space,
|
|
363
|
+
and a basis of this space provides the dual point configuration.
|
|
364
|
+
|
|
365
|
+
More concretely, the dual vector configuration (inhomogeneous)
|
|
366
|
+
is obtained by taking a basis of the right kernel of ``Matrix(vectors)``.
|
|
367
|
+
If the center of the (input) vectors is the origin,
|
|
368
|
+
there exists a basis of the right kernel of the form
|
|
369
|
+
``[[1], [V]]``, where ``[1]`` represents a row of ones.
|
|
370
|
+
Then, ``V`` is a dehomogenization and thus the dual point configuration.
|
|
371
|
+
|
|
372
|
+
To extend ``[1]`` to a basis of ``Matrix(vectors)``, we add
|
|
373
|
+
a row of ones to ``Matrix(vectors)`` and calculate a basis of the
|
|
374
|
+
right kernel of the obtained matrix.
|
|
375
|
+
|
|
376
|
+
REFERENCES:
|
|
377
|
+
|
|
378
|
+
For more information, see Section 6.4 of [Zie2007]_
|
|
379
|
+
or Definition 2.5.1 and Definition 4.1.35 of [DLRS2010]_.
|
|
380
|
+
|
|
381
|
+
.. SEEALSO::
|
|
382
|
+
|
|
383
|
+
:func`~sage.geometry.polyhedron.library.gale_transform_to_polytope`.
|
|
384
|
+
|
|
385
|
+
EXAMPLES::
|
|
386
|
+
|
|
387
|
+
sage: from sage.geometry.polyhedron.library import gale_transform_to_primal
|
|
388
|
+
sage: points = ((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1))
|
|
389
|
+
sage: gale_transform_to_primal(points)
|
|
390
|
+
[(0, 0, 1), (0, 1, 0), (1, 0, 0), (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
|
|
391
|
+
|
|
392
|
+
One can specify the base ring::
|
|
393
|
+
|
|
394
|
+
sage: p = [(1,1), (-1,-1), (1,0), (-1,0), (1,-1), (-2,1)]
|
|
395
|
+
sage: gtpp = gale_transform_to_primal(p); gtpp # needs sage.libs.pari
|
|
396
|
+
[(16, -35, 54),
|
|
397
|
+
(24, 10, 31),
|
|
398
|
+
(-15, 50, -60),
|
|
399
|
+
(-25, 0, 0),
|
|
400
|
+
(0, -25, 0),
|
|
401
|
+
(0, 0, -25)]
|
|
402
|
+
sage: (matrix(RDF, gtpp)/25 + # needs sage.libs.pari
|
|
403
|
+
....: matrix(gale_transform_to_primal(p, base_ring=RDF))).norm() < 1e-15
|
|
404
|
+
True
|
|
405
|
+
|
|
406
|
+
One can also specify the backend to be used internally::
|
|
407
|
+
|
|
408
|
+
sage: gale_transform_to_primal(p, backend='field') # needs sage.libs.pari
|
|
409
|
+
[(48, -71, 88),
|
|
410
|
+
(84, -28, 99),
|
|
411
|
+
(-77, 154, -132),
|
|
412
|
+
(-55, 0, 0),
|
|
413
|
+
(0, -55, 0),
|
|
414
|
+
(0, 0, -55)]
|
|
415
|
+
sage: gale_transform_to_primal(p, backend='normaliz') # optional - pynormaliz, needs sage.libs.pari
|
|
416
|
+
[(16, -35, 54),
|
|
417
|
+
(24, 10, 31),
|
|
418
|
+
(-15, 50, -60),
|
|
419
|
+
(-25, 0, 0),
|
|
420
|
+
(0, -25, 0),
|
|
421
|
+
(0, 0, -25)]
|
|
422
|
+
|
|
423
|
+
The input vectors should be totally cyclic::
|
|
424
|
+
|
|
425
|
+
sage: gale_transform_to_primal([(0,1), (1,0), (1,1), (-1,0)])
|
|
426
|
+
Traceback (most recent call last):
|
|
427
|
+
...
|
|
428
|
+
ValueError: input vectors not totally cyclic
|
|
429
|
+
|
|
430
|
+
sage: gale_transform_to_primal(
|
|
431
|
+
....: [(1,1,0), (-1,-1,0), (1,0,0),
|
|
432
|
+
....: (-1,0,0), (1,-1,0), (-2,1,0)], backend='field')
|
|
433
|
+
Traceback (most recent call last):
|
|
434
|
+
...
|
|
435
|
+
ValueError: input vectors not totally cyclic
|
|
436
|
+
"""
|
|
437
|
+
from sage.modules.free_module_element import vector
|
|
438
|
+
from sage.matrix.constructor import matrix
|
|
439
|
+
if base_ring:
|
|
440
|
+
vectors = tuple(vector(base_ring, x) for x in vectors)
|
|
441
|
+
else:
|
|
442
|
+
vectors = tuple(vector(x) for x in vectors)
|
|
443
|
+
|
|
444
|
+
if not sum(vectors).is_zero():
|
|
445
|
+
# The center of the input vectors shall be the origin.
|
|
446
|
+
# If this is not the case, we scale them accordingly.
|
|
447
|
+
# This has the advantage that right kernel of ``vectors`` can be
|
|
448
|
+
# presented in the form ``[[1], [V]]``, where ``V`` are the points
|
|
449
|
+
# in the dual point configuration.
|
|
450
|
+
# (Dehomogenization is straightforward.)
|
|
451
|
+
|
|
452
|
+
# Scaling of the vectors is equivalent to finding a hyperplane that intersects
|
|
453
|
+
# all vectors of the dual point configuration. But if the input is already provided
|
|
454
|
+
# such that the vectors add up to zero, the coordinates might be nicer.
|
|
455
|
+
# (And this is faster.)
|
|
456
|
+
|
|
457
|
+
if base_ring:
|
|
458
|
+
ker = matrix(base_ring, vectors).left_kernel()
|
|
459
|
+
else:
|
|
460
|
+
ker = matrix(vectors).left_kernel()
|
|
461
|
+
solutions = Polyhedron(lines=tuple(ker.basis_matrix()), base_ring=base_ring, backend=backend)
|
|
462
|
+
|
|
463
|
+
from sage.matrix.special import identity_matrix
|
|
464
|
+
pos_orthant = Polyhedron(rays=identity_matrix(len(vectors)), base_ring=base_ring, backend=backend)
|
|
465
|
+
pos_solutions = solutions.intersection(pos_orthant)
|
|
466
|
+
if base_ring is ZZ:
|
|
467
|
+
pos_solutions = pos_solutions.change_ring(ZZ)
|
|
468
|
+
|
|
469
|
+
# Any integral point in ``pos_solutions`` will correspond to scaling-factors
|
|
470
|
+
# that make ``sum(vectors)`` zero.
|
|
471
|
+
x = pos_solutions.representative_point()
|
|
472
|
+
if not all(y > 0 for y in x):
|
|
473
|
+
raise ValueError("input vectors not totally cyclic")
|
|
474
|
+
vectors = tuple(vec*x[i] for i, vec in enumerate(vectors))
|
|
475
|
+
|
|
476
|
+
# The right kernel of ``vectors`` has a basis of the form ``[[1], [V]]``,
|
|
477
|
+
# where ``V`` is the dehomogenized dual point configuration.
|
|
478
|
+
# If we append a row of ones to ``vectors``, ``V`` is just the right kernel.
|
|
479
|
+
if base_ring:
|
|
480
|
+
m = matrix(base_ring, vectors).transpose().stack(matrix(base_ring, [[1]*len(vectors)]))
|
|
481
|
+
else:
|
|
482
|
+
m = matrix(vectors).transpose().stack(matrix([[1]*len(vectors)]))
|
|
483
|
+
|
|
484
|
+
if m.rank() != len(vectors[0]) + 1:
|
|
485
|
+
# The given vectors do not span the ambient space,
|
|
486
|
+
# then there exists a nonnegative value vector.
|
|
487
|
+
raise ValueError("input vectors not totally cyclic")
|
|
488
|
+
|
|
489
|
+
return m.right_kernel_matrix(basis='computed').columns()
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class Polytopes:
|
|
493
|
+
"""
|
|
494
|
+
A class of constructors for commonly used, famous, or interesting
|
|
495
|
+
polytopes.
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
def regular_polygon(self, n, exact=True, base_ring=None, backend=None):
|
|
499
|
+
"""
|
|
500
|
+
Return a regular polygon with `n` vertices.
|
|
501
|
+
|
|
502
|
+
INPUT:
|
|
503
|
+
|
|
504
|
+
- ``n`` -- positive integer; the number of vertices
|
|
505
|
+
|
|
506
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` floating point
|
|
507
|
+
numbers are used for coordinates
|
|
508
|
+
|
|
509
|
+
- ``base_ring`` -- a ring in which the coordinates will lie. It is
|
|
510
|
+
``None`` by default. If it is not provided and ``exact`` is ``True``
|
|
511
|
+
then it will be the field of real algebraic number, if ``exact`` is
|
|
512
|
+
``False`` it will be the real double field.
|
|
513
|
+
|
|
514
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
515
|
+
|
|
516
|
+
EXAMPLES::
|
|
517
|
+
|
|
518
|
+
sage: # needs sage.rings.number_field
|
|
519
|
+
sage: octagon = polytopes.regular_polygon(8)
|
|
520
|
+
sage: octagon
|
|
521
|
+
A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
|
|
522
|
+
sage: octagon.n_vertices()
|
|
523
|
+
8
|
|
524
|
+
sage: v = octagon.volume()
|
|
525
|
+
sage: v
|
|
526
|
+
2.828427124746190?
|
|
527
|
+
sage: v == 2*QQbar(2).sqrt()
|
|
528
|
+
True
|
|
529
|
+
|
|
530
|
+
Its non exact version::
|
|
531
|
+
|
|
532
|
+
sage: polytopes.regular_polygon(3, exact=False).vertices() # needs cddexec
|
|
533
|
+
(A vertex at (0.0, 1.0),
|
|
534
|
+
A vertex at (0.8660254038, -0.5),
|
|
535
|
+
A vertex at (-0.8660254038, -0.5))
|
|
536
|
+
sage: polytopes.regular_polygon(25, exact=False).n_vertices() # needs cddexec
|
|
537
|
+
25
|
|
538
|
+
|
|
539
|
+
TESTS::
|
|
540
|
+
|
|
541
|
+
sage: # optional - pynormaliz, needs sage.rings.number_field
|
|
542
|
+
sage: octagon = polytopes.regular_polygon(8, backend='normaliz')
|
|
543
|
+
sage: octagon
|
|
544
|
+
A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
|
|
545
|
+
sage: octagon.n_vertices()
|
|
546
|
+
8
|
|
547
|
+
sage: octagon.volume()
|
|
548
|
+
2*a
|
|
549
|
+
sage: TestSuite(octagon).run() # long time
|
|
550
|
+
|
|
551
|
+
sage: TestSuite(polytopes.regular_polygon(5, exact=False)).run() # needs scipy
|
|
552
|
+
"""
|
|
553
|
+
n = ZZ(n)
|
|
554
|
+
if n <= 2:
|
|
555
|
+
raise ValueError("n (={}) must be an integer greater than 2".format(n))
|
|
556
|
+
|
|
557
|
+
if base_ring is None:
|
|
558
|
+
if exact:
|
|
559
|
+
from sage.rings.qqbar import AA as base_ring
|
|
560
|
+
else:
|
|
561
|
+
from sage.rings.real_double import RDF as base_ring
|
|
562
|
+
|
|
563
|
+
try:
|
|
564
|
+
omega = 2*base_ring.pi() / n
|
|
565
|
+
verts = [((i*omega).sin(), (i*omega).cos()) for i in range(n)]
|
|
566
|
+
except AttributeError:
|
|
567
|
+
from sage.rings.qqbar import QQbar
|
|
568
|
+
z = QQbar.zeta(n)
|
|
569
|
+
verts = [(base_ring((z**k).imag()), base_ring((z**k).real())) for k in range(n)]
|
|
570
|
+
|
|
571
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
572
|
+
|
|
573
|
+
def Birkhoff_polytope(self, n, backend=None):
|
|
574
|
+
"""
|
|
575
|
+
Return the Birkhoff polytope with `n!` vertices.
|
|
576
|
+
|
|
577
|
+
The vertices of this polyhedron are the (flattened) `n` by `n`
|
|
578
|
+
permutation matrices. So the ambient vector space has dimension `n^2`
|
|
579
|
+
but the dimension of the polyhedron is `(n-1)^2`.
|
|
580
|
+
|
|
581
|
+
INPUT:
|
|
582
|
+
|
|
583
|
+
- ``n`` -- positive integer giving the size of the permutation matrices
|
|
584
|
+
|
|
585
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
586
|
+
|
|
587
|
+
.. SEEALSO::
|
|
588
|
+
|
|
589
|
+
:meth:`sage.matrix.matrix2.Matrix.as_sum_of_permutations` -- return
|
|
590
|
+
the current matrix as a sum of permutation matrices
|
|
591
|
+
|
|
592
|
+
EXAMPLES::
|
|
593
|
+
|
|
594
|
+
sage: b3 = polytopes.Birkhoff_polytope(3)
|
|
595
|
+
sage: b3.f_vector()
|
|
596
|
+
(1, 6, 15, 18, 9, 1)
|
|
597
|
+
sage: b3.ambient_dim(), b3.dim()
|
|
598
|
+
(9, 4)
|
|
599
|
+
sage: b3.is_lattice_polytope()
|
|
600
|
+
True
|
|
601
|
+
sage: p3 = b3.ehrhart_polynomial() # optional - latte_int
|
|
602
|
+
sage: p3 # optional - latte_int
|
|
603
|
+
1/8*t^4 + 3/4*t^3 + 15/8*t^2 + 9/4*t + 1
|
|
604
|
+
sage: [p3(i) for i in [1,2,3,4]] # optional - latte_int
|
|
605
|
+
[6, 21, 55, 120]
|
|
606
|
+
sage: [len((i*b3).integral_points()) for i in [1,2,3,4]] # needs sage.libs.pari
|
|
607
|
+
[6, 21, 55, 120]
|
|
608
|
+
|
|
609
|
+
sage: b4 = polytopes.Birkhoff_polytope(4)
|
|
610
|
+
sage: b4.n_vertices(), b4.ambient_dim(), b4.dim()
|
|
611
|
+
(24, 16, 9)
|
|
612
|
+
|
|
613
|
+
TESTS::
|
|
614
|
+
|
|
615
|
+
sage: b4norm = polytopes.Birkhoff_polytope(4,backend='normaliz') # optional - pynormaliz
|
|
616
|
+
sage: TestSuite(b4norm).run() # optional - pynormaliz
|
|
617
|
+
sage: TestSuite(polytopes.Birkhoff_polytope(3)).run()
|
|
618
|
+
"""
|
|
619
|
+
from itertools import permutations
|
|
620
|
+
verts = []
|
|
621
|
+
for p in permutations(range(n)):
|
|
622
|
+
verts.append([ZZ.one() if p[i] == j else ZZ.zero()
|
|
623
|
+
for j in range(n) for i in range(n)])
|
|
624
|
+
return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)
|
|
625
|
+
|
|
626
|
+
def simplex(self, dim=3, project=False, base_ring=None, backend=None):
|
|
627
|
+
r"""
|
|
628
|
+
Return the ``dim`` dimensional simplex.
|
|
629
|
+
|
|
630
|
+
The `d`-simplex is the convex hull in `\RR^{d+1}` of the standard basis
|
|
631
|
+
`(1,0,\ldots,0)`, `(0,1,\ldots,0)`, \ldots, `(0,0,\ldots,1)`. For more
|
|
632
|
+
information, see the :wikipedia:`Simplex`.
|
|
633
|
+
|
|
634
|
+
INPUT:
|
|
635
|
+
|
|
636
|
+
- ``dim`` -- the dimension of the simplex, a positive
|
|
637
|
+
integer
|
|
638
|
+
|
|
639
|
+
- ``project`` -- boolean (default: ``False``); if ``True``, the polytope
|
|
640
|
+
is (isometrically) projected to a vector space of dimension
|
|
641
|
+
``dim-1``. This corresponds to the projection given by the matrix
|
|
642
|
+
from :func:`zero_sum_projection`. By default, this operation turns
|
|
643
|
+
the coordinates into floating point approximations (see
|
|
644
|
+
``base_ring``).
|
|
645
|
+
|
|
646
|
+
- ``base_ring`` -- the base ring to use to create the polytope;
|
|
647
|
+
if ``project`` is ``False``, this defaults to `\ZZ`.
|
|
648
|
+
Otherwise, it defaults to ``RDF``.
|
|
649
|
+
|
|
650
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
651
|
+
|
|
652
|
+
.. SEEALSO::
|
|
653
|
+
|
|
654
|
+
:meth:`tetrahedron`
|
|
655
|
+
|
|
656
|
+
EXAMPLES::
|
|
657
|
+
|
|
658
|
+
sage: s5 = polytopes.simplex(5); s5
|
|
659
|
+
A 5-dimensional polyhedron in ZZ^6 defined as the convex hull of 6 vertices
|
|
660
|
+
sage: s5.f_vector()
|
|
661
|
+
(1, 6, 15, 20, 15, 6, 1)
|
|
662
|
+
|
|
663
|
+
sage: s5 = polytopes.simplex(5, project=True); s5 # needs cddexec
|
|
664
|
+
A 5-dimensional polyhedron in RDF^5 defined as the convex hull of 6 vertices
|
|
665
|
+
|
|
666
|
+
Its volume is `\sqrt{d+1} / d!`::
|
|
667
|
+
|
|
668
|
+
sage: s5 = polytopes.simplex(5, project=True) # needs cddexec
|
|
669
|
+
sage: s5.volume() # abs tol 1e-10 # needs cddexec scipy
|
|
670
|
+
0.0204124145231931
|
|
671
|
+
sage: sqrt(6.) / factorial(5)
|
|
672
|
+
0.0204124145231931
|
|
673
|
+
|
|
674
|
+
sage: s6 = polytopes.simplex(6, project=True) # needs cddexec
|
|
675
|
+
sage: s6.volume() # abs tol 1e-10 # needs cddexec scipy
|
|
676
|
+
0.00367465459870082
|
|
677
|
+
sage: sqrt(7.) / factorial(6)
|
|
678
|
+
0.00367465459870082
|
|
679
|
+
|
|
680
|
+
Computation in algebraic reals::
|
|
681
|
+
|
|
682
|
+
sage: s3 = polytopes.simplex(3, project=True, base_ring=AA) # needs sage.rings.number_field
|
|
683
|
+
sage: s3.volume() == sqrt(3+1) / factorial(3) # needs sage.rings.number_field
|
|
684
|
+
True
|
|
685
|
+
|
|
686
|
+
TESTS::
|
|
687
|
+
|
|
688
|
+
sage: s6norm = polytopes.simplex(6,backend='normaliz') # optional - pynormaliz
|
|
689
|
+
sage: TestSuite(s6norm).run() # optional - pynormaliz
|
|
690
|
+
sage: TestSuite(polytopes.simplex(5)).run()
|
|
691
|
+
"""
|
|
692
|
+
verts = list((ZZ**(dim + 1)).basis())
|
|
693
|
+
if project:
|
|
694
|
+
# Handling of default in base_ring is delegated to project_points
|
|
695
|
+
verts = project_points(*verts, base_ring=base_ring)
|
|
696
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
697
|
+
|
|
698
|
+
def icosahedron(self, exact=True, base_ring=None, backend=None):
|
|
699
|
+
r"""
|
|
700
|
+
Return an icosahedron with edge length 1.
|
|
701
|
+
|
|
702
|
+
The icosahedron is one of the Platonic solids. It has 20 faces
|
|
703
|
+
and is dual to the :meth:`dodecahedron`.
|
|
704
|
+
|
|
705
|
+
INPUT:
|
|
706
|
+
|
|
707
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
708
|
+
approximate ring for the coordinates
|
|
709
|
+
|
|
710
|
+
- ``base_ring`` -- (optional) the ring in which the coordinates will
|
|
711
|
+
belong to. Note that this ring must contain `\sqrt(5)`. If it is not
|
|
712
|
+
provided and ``exact=True`` it will be the number field
|
|
713
|
+
`\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
|
|
714
|
+
field.
|
|
715
|
+
|
|
716
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
717
|
+
|
|
718
|
+
EXAMPLES::
|
|
719
|
+
|
|
720
|
+
sage: ico = polytopes.icosahedron() # needs sage.groups sage.rings.number_field
|
|
721
|
+
sage: ico.f_vector() # needs sage.groups sage.rings.number_field
|
|
722
|
+
(1, 12, 30, 20, 1)
|
|
723
|
+
sage: ico.volume() # needs sage.groups sage.rings.number_field
|
|
724
|
+
5/12*sqrt5 + 5/4
|
|
725
|
+
|
|
726
|
+
Its non exact version::
|
|
727
|
+
|
|
728
|
+
sage: ico = polytopes.icosahedron(exact=False) # needs sage.groups
|
|
729
|
+
sage: ico.base_ring() # needs sage.groups
|
|
730
|
+
Real Double Field
|
|
731
|
+
sage: ico.volume() # known bug # needs sage.groups
|
|
732
|
+
2.181694990...
|
|
733
|
+
|
|
734
|
+
A version using `AA <sage.rings.qqbar.AlgebraicRealField>`::
|
|
735
|
+
|
|
736
|
+
sage: ico = polytopes.icosahedron(base_ring=AA) # long time # needs sage.groups sage.rings.number_field
|
|
737
|
+
sage: ico.base_ring() # long time # needs sage.groups sage.rings.number_field
|
|
738
|
+
Algebraic Real Field
|
|
739
|
+
sage: ico.volume() # long time # needs sage.groups sage.rings.number_field
|
|
740
|
+
2.181694990624913?
|
|
741
|
+
|
|
742
|
+
Note that if base ring is provided it must contain the square root of
|
|
743
|
+
`5`. Otherwise you will get an error::
|
|
744
|
+
|
|
745
|
+
sage: polytopes.icosahedron(base_ring=QQ) # needs sage.symbolic
|
|
746
|
+
Traceback (most recent call last):
|
|
747
|
+
...
|
|
748
|
+
TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
|
|
749
|
+
|
|
750
|
+
TESTS::
|
|
751
|
+
|
|
752
|
+
sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
753
|
+
sage: ico = polytopes.icosahedron(backend='normaliz')
|
|
754
|
+
sage: ico.f_vector()
|
|
755
|
+
(1, 12, 30, 20, 1)
|
|
756
|
+
sage: ico.volume()
|
|
757
|
+
5/12*sqrt5 + 5/4
|
|
758
|
+
sage: TestSuite(ico).run()
|
|
759
|
+
|
|
760
|
+
sage: ico = polytopes.icosahedron(exact=False) # needs sage.groups
|
|
761
|
+
sage: TestSuite(ico).run(skip='_test_lawrence') # needs sage.groups
|
|
762
|
+
"""
|
|
763
|
+
if base_ring is None and exact:
|
|
764
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
765
|
+
K = QuadraticField(5, 'sqrt5')
|
|
766
|
+
sqrt5 = K.gen()
|
|
767
|
+
g = (1 + sqrt5) / 2
|
|
768
|
+
base_ring = K
|
|
769
|
+
else:
|
|
770
|
+
if base_ring is None:
|
|
771
|
+
from sage.rings.real_double import RDF as base_ring
|
|
772
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
773
|
+
|
|
774
|
+
r12 = base_ring.one() / 2
|
|
775
|
+
z = base_ring.zero()
|
|
776
|
+
pts = [[z, s1 * r12, s2 * g / 2]
|
|
777
|
+
for s1, s2 in itertools.product([1, -1], repeat=2)]
|
|
778
|
+
verts = [p(v) for p in AlternatingGroup(3) for v in pts]
|
|
779
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
780
|
+
|
|
781
|
+
def dodecahedron(self, exact=True, base_ring=None, backend=None):
|
|
782
|
+
r"""
|
|
783
|
+
Return a dodecahedron.
|
|
784
|
+
|
|
785
|
+
The dodecahedron is the Platonic solid dual to the :meth:`icosahedron`.
|
|
786
|
+
|
|
787
|
+
INPUT:
|
|
788
|
+
|
|
789
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
790
|
+
approximate ring for the coordinates
|
|
791
|
+
|
|
792
|
+
- ``base_ring`` -- (optional) the ring in which the coordinates will
|
|
793
|
+
belong to. Note that this ring must contain `\sqrt(5)`. If it is not
|
|
794
|
+
provided and ``exact=True`` it will be the number field
|
|
795
|
+
`\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
|
|
796
|
+
field.
|
|
797
|
+
|
|
798
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
799
|
+
|
|
800
|
+
EXAMPLES::
|
|
801
|
+
|
|
802
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
803
|
+
sage: d12 = polytopes.dodecahedron()
|
|
804
|
+
sage: d12.f_vector()
|
|
805
|
+
(1, 20, 30, 12, 1)
|
|
806
|
+
sage: d12.volume()
|
|
807
|
+
-176*sqrt5 + 400
|
|
808
|
+
sage: numerical_approx(_)
|
|
809
|
+
6.45203596003699
|
|
810
|
+
|
|
811
|
+
sage: d12 = polytopes.dodecahedron(exact=False) # needs sage.groups
|
|
812
|
+
sage: d12.base_ring() # needs sage.groups
|
|
813
|
+
Real Double Field
|
|
814
|
+
|
|
815
|
+
Here is an error with a field that does not contain `\sqrt(5)`::
|
|
816
|
+
|
|
817
|
+
sage: polytopes.dodecahedron(base_ring=QQ) # needs sage.groups sage.symbolic
|
|
818
|
+
Traceback (most recent call last):
|
|
819
|
+
...
|
|
820
|
+
TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
|
|
821
|
+
|
|
822
|
+
TESTS::
|
|
823
|
+
|
|
824
|
+
sage: d12 = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
825
|
+
sage: d12.f_vector() # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
826
|
+
(1, 20, 30, 12, 1)
|
|
827
|
+
sage: TestSuite(d12).run() # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
828
|
+
"""
|
|
829
|
+
return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).polar()
|
|
830
|
+
|
|
831
|
+
def small_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
|
|
832
|
+
r"""
|
|
833
|
+
Return the (small) rhombicuboctahedron.
|
|
834
|
+
|
|
835
|
+
The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26
|
|
836
|
+
faces. See the :wikipedia:`Rhombicuboctahedron` for more information.
|
|
837
|
+
|
|
838
|
+
INPUT:
|
|
839
|
+
|
|
840
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
841
|
+
approximate ring for the coordinates
|
|
842
|
+
|
|
843
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
844
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
845
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
846
|
+
it will be the real double field.
|
|
847
|
+
|
|
848
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
849
|
+
|
|
850
|
+
EXAMPLES::
|
|
851
|
+
|
|
852
|
+
sage: sr = polytopes.small_rhombicuboctahedron() # needs sage.rings.number_field
|
|
853
|
+
sage: sr.f_vector() # needs sage.rings.number_field
|
|
854
|
+
(1, 24, 48, 26, 1)
|
|
855
|
+
sage: sr.volume() # needs sage.rings.number_field
|
|
856
|
+
80/3*sqrt2 + 32
|
|
857
|
+
|
|
858
|
+
The faces are `8` equilateral triangles and `18` squares::
|
|
859
|
+
|
|
860
|
+
sage: sum(1 for f in sr.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
|
|
861
|
+
8
|
|
862
|
+
sage: sum(1 for f in sr.facets() if len(f.vertices()) == 4) # needs sage.rings.number_field
|
|
863
|
+
18
|
|
864
|
+
|
|
865
|
+
Its non exact version::
|
|
866
|
+
|
|
867
|
+
sage: sr = polytopes.small_rhombicuboctahedron(False); sr # needs cddexec
|
|
868
|
+
A 3-dimensional polyhedron in RDF^3 defined as the convex hull of
|
|
869
|
+
24 vertices
|
|
870
|
+
sage: sr.f_vector() # needs cddexec
|
|
871
|
+
(1, 24, 48, 26, 1)
|
|
872
|
+
|
|
873
|
+
TESTS::
|
|
874
|
+
|
|
875
|
+
sage: # optional - pynormaliz, needs sage.rings.number_field
|
|
876
|
+
sage: sr = polytopes.small_rhombicuboctahedron(backend='normaliz')
|
|
877
|
+
sage: sr.f_vector()
|
|
878
|
+
(1, 24, 48, 26, 1)
|
|
879
|
+
sage: sr.volume()
|
|
880
|
+
80/3*sqrt2 + 32
|
|
881
|
+
sage: TestSuite(sr).run() # long time
|
|
882
|
+
"""
|
|
883
|
+
if base_ring is None and exact:
|
|
884
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
885
|
+
K = QuadraticField(2, 'sqrt2')
|
|
886
|
+
sqrt2 = K.gen()
|
|
887
|
+
base_ring = K
|
|
888
|
+
else:
|
|
889
|
+
if base_ring is None:
|
|
890
|
+
from sage.rings.real_double import RDF as base_ring
|
|
891
|
+
sqrt2 = base_ring(2).sqrt()
|
|
892
|
+
|
|
893
|
+
one = base_ring.one()
|
|
894
|
+
a = sqrt2 + one
|
|
895
|
+
verts = []
|
|
896
|
+
verts.extend([s1*one, s2*one, s3*a] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
|
|
897
|
+
verts.extend([s1*one, s3*a, s2*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
|
|
898
|
+
verts.extend([s1*a, s2*one, s3*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
|
|
899
|
+
return Polyhedron(vertices=verts, backend=backend)
|
|
900
|
+
|
|
901
|
+
def great_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
|
|
902
|
+
r"""
|
|
903
|
+
Return the great rhombicuboctahedron.
|
|
904
|
+
|
|
905
|
+
The great rhombicuboctahedron (or truncated cuboctahedron) is an
|
|
906
|
+
Archimedean solid with 48 vertices and 26 faces. For more information
|
|
907
|
+
see the :wikipedia:`Truncated_cuboctahedron`.
|
|
908
|
+
|
|
909
|
+
INPUT:
|
|
910
|
+
|
|
911
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
912
|
+
approximate ring for the coordinates
|
|
913
|
+
|
|
914
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
915
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
916
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
917
|
+
it will be the real double field.
|
|
918
|
+
|
|
919
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
920
|
+
|
|
921
|
+
EXAMPLES::
|
|
922
|
+
|
|
923
|
+
sage: gr = polytopes.great_rhombicuboctahedron() # long time # needs sage.rings.number_field
|
|
924
|
+
sage: gr.f_vector() # long time # needs sage.rings.number_field
|
|
925
|
+
(1, 48, 72, 26, 1)
|
|
926
|
+
|
|
927
|
+
A faster implementation is obtained by setting ``exact=False``::
|
|
928
|
+
|
|
929
|
+
sage: # needs cddexec
|
|
930
|
+
sage: gr = polytopes.great_rhombicuboctahedron(exact=False)
|
|
931
|
+
sage: gr.f_vector()
|
|
932
|
+
(1, 48, 72, 26, 1)
|
|
933
|
+
|
|
934
|
+
Its facets are 4 squares, 8 regular hexagons and 6 regular octagons::
|
|
935
|
+
|
|
936
|
+
sage: # needs cddexec
|
|
937
|
+
sage: sum(1 for f in gr.facets() if len(f.vertices()) == 4)
|
|
938
|
+
12
|
|
939
|
+
sage: sum(1 for f in gr.facets() if len(f.vertices()) == 6)
|
|
940
|
+
8
|
|
941
|
+
sage: sum(1 for f in gr.facets() if len(f.vertices()) == 8)
|
|
942
|
+
6
|
|
943
|
+
"""
|
|
944
|
+
if base_ring is None and exact:
|
|
945
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
946
|
+
base_ring = QuadraticField(2, 'sqrt2')
|
|
947
|
+
sqrt2 = base_ring.gen()
|
|
948
|
+
else:
|
|
949
|
+
if base_ring is None:
|
|
950
|
+
from sage.rings.real_double import RDF as base_ring
|
|
951
|
+
sqrt2 = base_ring(2).sqrt()
|
|
952
|
+
|
|
953
|
+
one = base_ring.one()
|
|
954
|
+
v1 = sqrt2 + 1
|
|
955
|
+
v2 = 2 * sqrt2 + 1
|
|
956
|
+
verts = [[s1 * z1, s2 * z2, s3 * z3]
|
|
957
|
+
for z1, z2, z3 in itertools.permutations([one, v1, v2])
|
|
958
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
959
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
960
|
+
|
|
961
|
+
def rhombic_dodecahedron(self, backend=None):
|
|
962
|
+
"""
|
|
963
|
+
Return the rhombic dodecahedron.
|
|
964
|
+
|
|
965
|
+
The rhombic dodecahedron is a polytope dual to the cuboctahedron. It
|
|
966
|
+
has 14 vertices and 12 faces. For more information see
|
|
967
|
+
the :wikipedia:`Rhombic_dodecahedron`.
|
|
968
|
+
|
|
969
|
+
INPUT:
|
|
970
|
+
|
|
971
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
972
|
+
|
|
973
|
+
.. SEEALSO::
|
|
974
|
+
|
|
975
|
+
:meth:`cuboctahedron`
|
|
976
|
+
|
|
977
|
+
EXAMPLES::
|
|
978
|
+
|
|
979
|
+
sage: rd = polytopes.rhombic_dodecahedron()
|
|
980
|
+
sage: rd.f_vector()
|
|
981
|
+
(1, 14, 24, 12, 1)
|
|
982
|
+
|
|
983
|
+
Its facets are 12 quadrilaterals (not all identical)::
|
|
984
|
+
|
|
985
|
+
sage: sum(1 for f in rd.facets() if len(f.vertices()) == 4)
|
|
986
|
+
12
|
|
987
|
+
|
|
988
|
+
Some more computations::
|
|
989
|
+
|
|
990
|
+
sage: p = rd.ehrhart_polynomial() # optional - latte_int
|
|
991
|
+
sage: p # optional - latte_int
|
|
992
|
+
16*t^3 + 12*t^2 + 4*t + 1
|
|
993
|
+
sage: [p(i) for i in [1,2,3,4]] # optional - latte_int
|
|
994
|
+
[33, 185, 553, 1233]
|
|
995
|
+
sage: [len((i*rd).integral_points()) for i in [1,2,3,4]]
|
|
996
|
+
[33, 185, 553, 1233]
|
|
997
|
+
|
|
998
|
+
TESTS::
|
|
999
|
+
|
|
1000
|
+
sage: rd_norm = polytopes.rhombic_dodecahedron(backend='normaliz') # optional - pynormaliz
|
|
1001
|
+
sage: TestSuite(rd_norm).run() # optional - pynormaliz
|
|
1002
|
+
"""
|
|
1003
|
+
v = [[2,0,0],[-2,0,0],[0,2,0],[0,-2,0],[0,0,2],[0,0,-2]]
|
|
1004
|
+
v.extend(itertools.product([1, -1], repeat=3))
|
|
1005
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1006
|
+
|
|
1007
|
+
def cuboctahedron(self, backend=None):
|
|
1008
|
+
r"""
|
|
1009
|
+
Return the cuboctahedron.
|
|
1010
|
+
|
|
1011
|
+
The cuboctahedron is an Archimedean solid with 12 vertices and 14 faces
|
|
1012
|
+
dual to the rhombic dodecahedron. It can be defined as the convex hull
|
|
1013
|
+
of the twelve vertices `(0, \pm 1, \pm 1)`, `(\pm 1, 0, \pm 1)` and
|
|
1014
|
+
`(\pm 1, \pm 1, 0)`. For more information, see the
|
|
1015
|
+
:wikipedia:`Cuboctahedron`.
|
|
1016
|
+
|
|
1017
|
+
INPUT:
|
|
1018
|
+
|
|
1019
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1020
|
+
|
|
1021
|
+
.. SEEALSO::
|
|
1022
|
+
|
|
1023
|
+
:meth:`rhombic_dodecahedron`
|
|
1024
|
+
|
|
1025
|
+
EXAMPLES::
|
|
1026
|
+
|
|
1027
|
+
sage: co = polytopes.cuboctahedron()
|
|
1028
|
+
sage: co.f_vector()
|
|
1029
|
+
(1, 12, 24, 14, 1)
|
|
1030
|
+
|
|
1031
|
+
Its facets are 8 triangles and 6 squares::
|
|
1032
|
+
|
|
1033
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
|
|
1034
|
+
8
|
|
1035
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
|
|
1036
|
+
6
|
|
1037
|
+
|
|
1038
|
+
Some more computation::
|
|
1039
|
+
|
|
1040
|
+
sage: co.volume()
|
|
1041
|
+
20/3
|
|
1042
|
+
sage: co.ehrhart_polynomial() # optional - latte_int
|
|
1043
|
+
20/3*t^3 + 8*t^2 + 10/3*t + 1
|
|
1044
|
+
|
|
1045
|
+
TESTS::
|
|
1046
|
+
|
|
1047
|
+
sage: co_norm = polytopes.cuboctahedron(backend='normaliz') # optional - pynormaliz
|
|
1048
|
+
sage: TestSuite(co_norm).run() # optional - pynormaliz
|
|
1049
|
+
"""
|
|
1050
|
+
v = [[0, -1, -1], [0, 1, -1], [0, -1, 1], [0, 1, 1],
|
|
1051
|
+
[-1, -1, 0], [1, -1, 0], [-1, 1, 0], [1, 1, 0],
|
|
1052
|
+
[-1, 0, -1], [1, 0, -1], [-1, 0, 1], [1, 0, 1]]
|
|
1053
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1054
|
+
|
|
1055
|
+
def truncated_cube(self, exact=True, base_ring=None, backend=None):
|
|
1056
|
+
r"""
|
|
1057
|
+
Return the truncated cube.
|
|
1058
|
+
|
|
1059
|
+
The truncated cube is an Archimedean solid with 24 vertices
|
|
1060
|
+
and 14 faces. It can be defined as the convex hull of the 24 vertices
|
|
1061
|
+
`(\pm x, \pm 1, \pm 1), (\pm 1, \pm x, \pm 1), (\pm 1, \pm 1, \pm x)`
|
|
1062
|
+
where `x = \sqrt(2) - 1`. For more information, see the
|
|
1063
|
+
:wikipedia:`Truncated_cube`.
|
|
1064
|
+
|
|
1065
|
+
INPUT:
|
|
1066
|
+
|
|
1067
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1068
|
+
approximate ring for the coordinates
|
|
1069
|
+
|
|
1070
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1071
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1072
|
+
`\QQ[\sqrt{2}]` and if ``exact=False`` it
|
|
1073
|
+
will be the real double field.
|
|
1074
|
+
|
|
1075
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1076
|
+
|
|
1077
|
+
EXAMPLES::
|
|
1078
|
+
|
|
1079
|
+
sage: co = polytopes.truncated_cube() # needs sage.rings.number_field
|
|
1080
|
+
sage: co.f_vector() # needs sage.rings.number_field
|
|
1081
|
+
(1, 24, 36, 14, 1)
|
|
1082
|
+
|
|
1083
|
+
Its facets are 8 triangles and 6 octogons::
|
|
1084
|
+
|
|
1085
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
|
|
1086
|
+
8
|
|
1087
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 8) # needs sage.rings.number_field
|
|
1088
|
+
6
|
|
1089
|
+
|
|
1090
|
+
Some more computation::
|
|
1091
|
+
|
|
1092
|
+
sage: co.volume() # needs sage.rings.number_field
|
|
1093
|
+
56/3*sqrt2 - 56/3
|
|
1094
|
+
|
|
1095
|
+
TESTS::
|
|
1096
|
+
|
|
1097
|
+
sage: co = polytopes.truncated_cube(backend='normaliz') # optional - pynormaliz, needs sage.rings.number_field
|
|
1098
|
+
sage: co.f_vector() # optional - pynormaliz # needs sage.rings.number_field
|
|
1099
|
+
(1, 24, 36, 14, 1)
|
|
1100
|
+
sage: TestSuite(co).run() # optional - pynormaliz # needs sage.rings.number_field
|
|
1101
|
+
"""
|
|
1102
|
+
if base_ring is None and exact:
|
|
1103
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1104
|
+
K = QuadraticField(2, 'sqrt2')
|
|
1105
|
+
sqrt2 = K.gen()
|
|
1106
|
+
g = sqrt2 - 1
|
|
1107
|
+
base_ring = K
|
|
1108
|
+
else:
|
|
1109
|
+
if base_ring is None:
|
|
1110
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1111
|
+
g = base_ring(2).sqrt() - 1
|
|
1112
|
+
|
|
1113
|
+
v = [[a * g, b, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
|
|
1114
|
+
v += [[a, b * g, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
|
|
1115
|
+
v += [[a, b, c * g] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
|
|
1116
|
+
return Polyhedron(vertices=v, base_ring=base_ring, backend=backend)
|
|
1117
|
+
|
|
1118
|
+
def tetrahedron(self, backend=None):
|
|
1119
|
+
"""
|
|
1120
|
+
Return the tetrahedron.
|
|
1121
|
+
|
|
1122
|
+
The tetrahedron is a Platonic solid with 4 vertices and 4 faces
|
|
1123
|
+
dual to itself. It can be defined as the convex hull
|
|
1124
|
+
of the 4 vertices `(0, 0, 0)`, `(1, 1, 0)`, `(1, 0, 1)` and
|
|
1125
|
+
`(0, 1, 1)`. For more information, see the
|
|
1126
|
+
:wikipedia:`Tetrahedron`.
|
|
1127
|
+
|
|
1128
|
+
INPUT:
|
|
1129
|
+
|
|
1130
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1131
|
+
|
|
1132
|
+
.. SEEALSO::
|
|
1133
|
+
|
|
1134
|
+
:meth:`simplex`
|
|
1135
|
+
|
|
1136
|
+
EXAMPLES::
|
|
1137
|
+
|
|
1138
|
+
sage: co = polytopes.tetrahedron()
|
|
1139
|
+
sage: co.f_vector()
|
|
1140
|
+
(1, 4, 6, 4, 1)
|
|
1141
|
+
|
|
1142
|
+
Its facets are 4 triangles::
|
|
1143
|
+
|
|
1144
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
|
|
1145
|
+
4
|
|
1146
|
+
|
|
1147
|
+
Some more computation::
|
|
1148
|
+
|
|
1149
|
+
sage: co.volume()
|
|
1150
|
+
1/3
|
|
1151
|
+
sage: co.ehrhart_polynomial() # optional - latte_int
|
|
1152
|
+
1/3*t^3 + t^2 + 5/3*t + 1
|
|
1153
|
+
|
|
1154
|
+
TESTS::
|
|
1155
|
+
|
|
1156
|
+
sage: t_norm = polytopes.tetrahedron(backend='normaliz') # optional - pynormaliz
|
|
1157
|
+
sage: TestSuite(t_norm).run() # optional - pynormaliz
|
|
1158
|
+
"""
|
|
1159
|
+
v = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
|
|
1160
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1161
|
+
|
|
1162
|
+
def truncated_tetrahedron(self, backend=None):
|
|
1163
|
+
r"""
|
|
1164
|
+
Return the truncated tetrahedron.
|
|
1165
|
+
|
|
1166
|
+
The truncated tetrahedron is an Archimedean solid with 12
|
|
1167
|
+
vertices and 8 faces. It can be defined as the convex hull off
|
|
1168
|
+
all the permutations of `(\pm 1, \pm 1, \pm 3)` with an even
|
|
1169
|
+
number of minus signs. For more information, see the
|
|
1170
|
+
:wikipedia:`Truncated_tetrahedron`.
|
|
1171
|
+
|
|
1172
|
+
INPUT:
|
|
1173
|
+
|
|
1174
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1175
|
+
|
|
1176
|
+
EXAMPLES::
|
|
1177
|
+
|
|
1178
|
+
sage: co = polytopes.truncated_tetrahedron()
|
|
1179
|
+
sage: co.f_vector()
|
|
1180
|
+
(1, 12, 18, 8, 1)
|
|
1181
|
+
|
|
1182
|
+
Its facets are 4 triangles and 4 hexagons::
|
|
1183
|
+
|
|
1184
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
|
|
1185
|
+
4
|
|
1186
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
|
|
1187
|
+
4
|
|
1188
|
+
|
|
1189
|
+
Some more computation::
|
|
1190
|
+
|
|
1191
|
+
sage: co.volume()
|
|
1192
|
+
184/3
|
|
1193
|
+
sage: co.ehrhart_polynomial() # optional - latte_int
|
|
1194
|
+
184/3*t^3 + 28*t^2 + 26/3*t + 1
|
|
1195
|
+
|
|
1196
|
+
TESTS::
|
|
1197
|
+
|
|
1198
|
+
sage: tt_norm = polytopes.truncated_tetrahedron(backend='normaliz') # optional - pynormaliz
|
|
1199
|
+
sage: TestSuite(tt_norm).run() # optional - pynormaliz
|
|
1200
|
+
"""
|
|
1201
|
+
v = [(3,1,1), (1,3,1), (1,1,3),
|
|
1202
|
+
(-3,-1,1), (-1,-3,1), (-1,-1,3),
|
|
1203
|
+
(-3,1,-1), (-1,3,-1), (-1,1,-3),
|
|
1204
|
+
(3,-1,-1), (1,-3,-1), (1,-1,-3)]
|
|
1205
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1206
|
+
|
|
1207
|
+
def truncated_octahedron(self, backend=None):
|
|
1208
|
+
r"""
|
|
1209
|
+
Return the truncated octahedron.
|
|
1210
|
+
|
|
1211
|
+
The truncated octahedron is an Archimedean solid with 24
|
|
1212
|
+
vertices and 14 faces. It can be defined as the convex hull
|
|
1213
|
+
off all the permutations of `(0, \pm 1, \pm 2)`. For more
|
|
1214
|
+
information, see the :wikipedia:`Truncated_octahedron`.
|
|
1215
|
+
|
|
1216
|
+
This is also known as the permutohedron of dimension 3.
|
|
1217
|
+
|
|
1218
|
+
INPUT:
|
|
1219
|
+
|
|
1220
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1221
|
+
|
|
1222
|
+
EXAMPLES::
|
|
1223
|
+
|
|
1224
|
+
sage: co = polytopes.truncated_octahedron()
|
|
1225
|
+
sage: co.f_vector()
|
|
1226
|
+
(1, 24, 36, 14, 1)
|
|
1227
|
+
|
|
1228
|
+
Its facets are 6 squares and 8 hexagons::
|
|
1229
|
+
|
|
1230
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
|
|
1231
|
+
6
|
|
1232
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
|
|
1233
|
+
8
|
|
1234
|
+
|
|
1235
|
+
Some more computation::
|
|
1236
|
+
|
|
1237
|
+
sage: co.volume()
|
|
1238
|
+
32
|
|
1239
|
+
sage: co.ehrhart_polynomial() # optional - latte_int # needs sage.combinat
|
|
1240
|
+
32*t^3 + 18*t^2 + 6*t + 1
|
|
1241
|
+
|
|
1242
|
+
TESTS::
|
|
1243
|
+
|
|
1244
|
+
sage: to_norm = polytopes.truncated_octahedron(backend='normaliz') # optional - pynormaliz, needs sage.combinat
|
|
1245
|
+
sage: TestSuite(to_norm).run() # optional - pynormaliz, needs sage.combinat
|
|
1246
|
+
"""
|
|
1247
|
+
v = [(0, e, f) for e in [-1, 1] for f in [-2, 2]]
|
|
1248
|
+
v = [(xyz[sigma(1) - 1], xyz[sigma(2) - 1], xyz[sigma(3) - 1])
|
|
1249
|
+
for sigma in Permutations(3) for xyz in v]
|
|
1250
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1251
|
+
|
|
1252
|
+
def octahedron(self, backend=None):
|
|
1253
|
+
r"""
|
|
1254
|
+
Return the octahedron.
|
|
1255
|
+
|
|
1256
|
+
The octahedron is a Platonic solid with 6 vertices and 8 faces
|
|
1257
|
+
dual to the cube. It can be defined as the convex hull
|
|
1258
|
+
of the six vertices `(0, 0, \pm 1)`, `(\pm 1, 0, 0)` and
|
|
1259
|
+
`(0, \pm 1, 0)`. For more information, see the
|
|
1260
|
+
:wikipedia:`Octahedron`.
|
|
1261
|
+
|
|
1262
|
+
INPUT:
|
|
1263
|
+
|
|
1264
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1265
|
+
|
|
1266
|
+
EXAMPLES::
|
|
1267
|
+
|
|
1268
|
+
sage: co = polytopes.octahedron()
|
|
1269
|
+
sage: co.f_vector()
|
|
1270
|
+
(1, 6, 12, 8, 1)
|
|
1271
|
+
|
|
1272
|
+
Its facets are 8 triangles::
|
|
1273
|
+
|
|
1274
|
+
sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
|
|
1275
|
+
8
|
|
1276
|
+
|
|
1277
|
+
Some more computation::
|
|
1278
|
+
|
|
1279
|
+
sage: co.volume()
|
|
1280
|
+
4/3
|
|
1281
|
+
sage: co.ehrhart_polynomial() # optional - latte_int
|
|
1282
|
+
4/3*t^3 + 2*t^2 + 8/3*t + 1
|
|
1283
|
+
|
|
1284
|
+
TESTS::
|
|
1285
|
+
|
|
1286
|
+
sage: o_norm = polytopes.octahedron(backend='normaliz') # optional - pynormaliz
|
|
1287
|
+
sage: TestSuite(o_norm).run() # optional - pynormaliz
|
|
1288
|
+
"""
|
|
1289
|
+
v = [[0, 0, -1], [0, 0, 1], [1, 0, 0],
|
|
1290
|
+
[-1, 0, 0], [0, 1, 0], [0, -1, 0]]
|
|
1291
|
+
return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
|
|
1292
|
+
|
|
1293
|
+
def snub_cube(self, exact=False, base_ring=None, backend=None, verbose=False):
|
|
1294
|
+
"""
|
|
1295
|
+
Return a snub cube.
|
|
1296
|
+
|
|
1297
|
+
The snub cube is an Archimedean solid. It has 24 vertices and 38 faces.
|
|
1298
|
+
For more information see the :wikipedia:`Snub_cube`.
|
|
1299
|
+
|
|
1300
|
+
The constant `z` used in constructing this polytope is the reciprocal
|
|
1301
|
+
of the tribonacci constant, that is, the solution of the equation
|
|
1302
|
+
`x^3 + x^2 + x - 1 = 0`.
|
|
1303
|
+
See :wikipedia:`Generalizations_of_Fibonacci_numbers#Tribonacci_numbers`.
|
|
1304
|
+
|
|
1305
|
+
INPUT:
|
|
1306
|
+
|
|
1307
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
1308
|
+
coordinates instead of floating point approximations
|
|
1309
|
+
|
|
1310
|
+
- ``base_ring`` -- the field to use; if ``None`` (the default),
|
|
1311
|
+
construct the exact number field needed (if ``exact`` is ``True``) or
|
|
1312
|
+
default to ``RDF`` (if ``exact`` is ``True``)
|
|
1313
|
+
|
|
1314
|
+
- ``backend`` -- the backend to use to create the polytope; if
|
|
1315
|
+
``None`` (the default), the backend will be selected automatically
|
|
1316
|
+
|
|
1317
|
+
EXAMPLES::
|
|
1318
|
+
|
|
1319
|
+
sage: # needs sage.groups
|
|
1320
|
+
sage: sc_inexact = polytopes.snub_cube(exact=False); sc_inexact
|
|
1321
|
+
A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
|
|
1322
|
+
sage: sc_inexact.f_vector()
|
|
1323
|
+
(1, 24, 60, 38, 1)
|
|
1324
|
+
|
|
1325
|
+
sage: # long time, needs sage.groups sage.rings.number_field
|
|
1326
|
+
sage: sc_exact = polytopes.snub_cube(exact=True)
|
|
1327
|
+
sage: sc_exact.f_vector()
|
|
1328
|
+
(1, 24, 60, 38, 1)
|
|
1329
|
+
sage: sorted(sc_exact.vertices())
|
|
1330
|
+
[A vertex at (-1, -z, -z^2),
|
|
1331
|
+
A vertex at (-1, -z^2, z),
|
|
1332
|
+
A vertex at (-1, z^2, -z),
|
|
1333
|
+
A vertex at (-1, z, z^2),
|
|
1334
|
+
A vertex at (-z, -1, z^2),
|
|
1335
|
+
A vertex at (-z, -z^2, -1),
|
|
1336
|
+
A vertex at (-z, z^2, 1),
|
|
1337
|
+
A vertex at (-z, 1, -z^2),
|
|
1338
|
+
A vertex at (-z^2, -1, -z),
|
|
1339
|
+
A vertex at (-z^2, -z, 1),
|
|
1340
|
+
A vertex at (-z^2, z, -1),
|
|
1341
|
+
A vertex at (-z^2, 1, z),
|
|
1342
|
+
A vertex at (z^2, -1, z),
|
|
1343
|
+
A vertex at (z^2, -z, -1),
|
|
1344
|
+
A vertex at (z^2, z, 1),
|
|
1345
|
+
A vertex at (z^2, 1, -z),
|
|
1346
|
+
A vertex at (z, -1, -z^2),
|
|
1347
|
+
A vertex at (z, -z^2, 1),
|
|
1348
|
+
A vertex at (z, z^2, -1),
|
|
1349
|
+
A vertex at (z, 1, z^2),
|
|
1350
|
+
A vertex at (1, -z, z^2),
|
|
1351
|
+
A vertex at (1, -z^2, -z),
|
|
1352
|
+
A vertex at (1, z^2, z),
|
|
1353
|
+
A vertex at (1, z, -z^2)]
|
|
1354
|
+
sage: sc_exact.is_combinatorially_isomorphic(sc_inexact)
|
|
1355
|
+
True
|
|
1356
|
+
|
|
1357
|
+
TESTS::
|
|
1358
|
+
|
|
1359
|
+
sage: sc = polytopes.snub_cube(exact=True, backend='normaliz') # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
1360
|
+
sage: sc.f_vector() # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
1361
|
+
(1, 24, 60, 38, 1)
|
|
1362
|
+
"""
|
|
1363
|
+
def construct_z(field):
|
|
1364
|
+
# z here is the reciprocal of the tribonacci constant, that is, the
|
|
1365
|
+
# solution of the equation x^3 + x^2 + x - 1 = 0.
|
|
1366
|
+
tsqr33 = 3 * field(33).sqrt()
|
|
1367
|
+
return ((17 + tsqr33)**QQ((1, 3)) - (-17 + tsqr33)**QQ((1, 3)) - 1) / 3
|
|
1368
|
+
|
|
1369
|
+
if exact and base_ring is None:
|
|
1370
|
+
# construct the exact number field
|
|
1371
|
+
from sage.rings.qqbar import AA
|
|
1372
|
+
from sage.rings.number_field.number_field import NumberField
|
|
1373
|
+
R = QQ['x']
|
|
1374
|
+
f = R([-1, 1, 1, 1])
|
|
1375
|
+
embedding = construct_z(AA)
|
|
1376
|
+
base_ring = NumberField(f, name='z', embedding=embedding)
|
|
1377
|
+
z = base_ring.gen()
|
|
1378
|
+
else:
|
|
1379
|
+
if base_ring is None:
|
|
1380
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1381
|
+
z = construct_z(base_ring)
|
|
1382
|
+
|
|
1383
|
+
verts = []
|
|
1384
|
+
z2 = z ** 2
|
|
1385
|
+
A3 = AlternatingGroup(3)
|
|
1386
|
+
for e in [-1, 1]:
|
|
1387
|
+
for f in [-1, 1]:
|
|
1388
|
+
for g in [-1, 1]:
|
|
1389
|
+
if e * f * g == -1:
|
|
1390
|
+
v = [e, f * z, g * z2]
|
|
1391
|
+
for p in A3:
|
|
1392
|
+
verts += [p(v)]
|
|
1393
|
+
else:
|
|
1394
|
+
v = [f * z, e, g * z2]
|
|
1395
|
+
for p in A3:
|
|
1396
|
+
verts += [p(v)]
|
|
1397
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
1398
|
+
|
|
1399
|
+
def buckyball(self, exact=True, base_ring=None, backend=None):
|
|
1400
|
+
r"""
|
|
1401
|
+
Return the bucky ball.
|
|
1402
|
+
|
|
1403
|
+
The bucky ball, also known as the truncated icosahedron is an
|
|
1404
|
+
Archimedean solid. It has 32 faces and 60 vertices.
|
|
1405
|
+
|
|
1406
|
+
.. SEEALSO::
|
|
1407
|
+
|
|
1408
|
+
:meth:`icosahedron`
|
|
1409
|
+
|
|
1410
|
+
INPUT:
|
|
1411
|
+
|
|
1412
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1413
|
+
approximate ring for the coordinates
|
|
1414
|
+
|
|
1415
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1416
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1417
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
1418
|
+
it will be the real double field.
|
|
1419
|
+
|
|
1420
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1421
|
+
|
|
1422
|
+
EXAMPLES::
|
|
1423
|
+
|
|
1424
|
+
sage: bb = polytopes.buckyball() # long time # needs sage.groups sage.rings.number_field
|
|
1425
|
+
sage: bb.f_vector() # long time # needs sage.groups sage.rings.number_field
|
|
1426
|
+
(1, 60, 90, 32, 1)
|
|
1427
|
+
sage: bb.base_ring() # long time # needs sage.groups sage.rings.number_field
|
|
1428
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1429
|
+
with sqrt5 = 2.236067977499790?
|
|
1430
|
+
|
|
1431
|
+
A much faster implementation using floating point approximations::
|
|
1432
|
+
|
|
1433
|
+
sage: bb = polytopes.buckyball(exact=False) # needs sage.groups
|
|
1434
|
+
sage: bb.f_vector() # needs sage.groups
|
|
1435
|
+
(1, 60, 90, 32, 1)
|
|
1436
|
+
sage: bb.base_ring() # needs sage.groups
|
|
1437
|
+
Real Double Field
|
|
1438
|
+
|
|
1439
|
+
Its facets are 5 regular pentagons and 6 regular hexagons::
|
|
1440
|
+
|
|
1441
|
+
sage: sum(1 for f in bb.facets() if len(f.vertices()) == 5) # needs sage.groups
|
|
1442
|
+
12
|
|
1443
|
+
sage: sum(1 for f in bb.facets() if len(f.vertices()) == 6) # needs sage.groups
|
|
1444
|
+
20
|
|
1445
|
+
|
|
1446
|
+
TESTS::
|
|
1447
|
+
|
|
1448
|
+
sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
1449
|
+
sage: bb = polytopes.buckyball(backend='normaliz')
|
|
1450
|
+
sage: bb.f_vector()
|
|
1451
|
+
(1, 60, 90, 32, 1)
|
|
1452
|
+
sage: bb.base_ring()
|
|
1453
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1454
|
+
with sqrt5 = 2.236067977499790?
|
|
1455
|
+
"""
|
|
1456
|
+
return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).truncation()
|
|
1457
|
+
|
|
1458
|
+
def icosidodecahedron(self, exact=True, backend=None):
|
|
1459
|
+
"""
|
|
1460
|
+
Return the icosidodecahedron.
|
|
1461
|
+
|
|
1462
|
+
The Icosidodecahedron is a polyhedron with twenty triangular faces and
|
|
1463
|
+
twelve pentagonal faces. For more information see the
|
|
1464
|
+
:wikipedia:`Icosidodecahedron`.
|
|
1465
|
+
|
|
1466
|
+
INPUT:
|
|
1467
|
+
|
|
1468
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1469
|
+
approximate ring for the coordinates
|
|
1470
|
+
|
|
1471
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1472
|
+
|
|
1473
|
+
EXAMPLES::
|
|
1474
|
+
|
|
1475
|
+
sage: id = polytopes.icosidodecahedron() # needs sage.groups sage.rings.number_field
|
|
1476
|
+
sage: id.f_vector() # needs sage.groups sage.rings.number_field
|
|
1477
|
+
(1, 30, 60, 32, 1)
|
|
1478
|
+
|
|
1479
|
+
TESTS::
|
|
1480
|
+
|
|
1481
|
+
sage: id = polytopes.icosidodecahedron(exact=False); id # needs sage.groups sage.rings.number_field
|
|
1482
|
+
A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices
|
|
1483
|
+
sage: TestSuite(id).run(skip=["_test_is_combinatorially_isomorphic", # needs sage.groups sage.rings.number_field
|
|
1484
|
+
....: "_test_product",
|
|
1485
|
+
....: "_test_pyramid",
|
|
1486
|
+
....: "_test_lawrence"])
|
|
1487
|
+
|
|
1488
|
+
sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
1489
|
+
sage: id = polytopes.icosidodecahedron(backend='normaliz')
|
|
1490
|
+
sage: id.f_vector()
|
|
1491
|
+
(1, 30, 60, 32, 1)
|
|
1492
|
+
sage: id.base_ring()
|
|
1493
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1494
|
+
with sqrt5 = 2.236067977499790?
|
|
1495
|
+
sage: TestSuite(id).run() # long time
|
|
1496
|
+
"""
|
|
1497
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1498
|
+
from itertools import product
|
|
1499
|
+
|
|
1500
|
+
K = QuadraticField(5, 'sqrt5')
|
|
1501
|
+
one = K.one()
|
|
1502
|
+
phi = (one+K.gen())/2
|
|
1503
|
+
|
|
1504
|
+
gens = [((-1)**a*one/2, (-1)**b*phi/2, (-1)**c*(one+phi)/2)
|
|
1505
|
+
for a, b, c in product([0, 1], repeat=3)]
|
|
1506
|
+
gens.extend([(0, 0, phi), (0, 0, -phi)])
|
|
1507
|
+
|
|
1508
|
+
verts = []
|
|
1509
|
+
for p in AlternatingGroup(3):
|
|
1510
|
+
verts.extend(p(x) for x in gens)
|
|
1511
|
+
|
|
1512
|
+
if exact:
|
|
1513
|
+
return Polyhedron(vertices=verts, base_ring=K, backend=backend)
|
|
1514
|
+
else:
|
|
1515
|
+
from sage.rings.real_mpfr import RR
|
|
1516
|
+
verts = [(RR(x), RR(y), RR(z)) for x, y, z in verts]
|
|
1517
|
+
return Polyhedron(vertices=verts, backend=backend)
|
|
1518
|
+
|
|
1519
|
+
def icosidodecahedron_V2(self, exact=True, base_ring=None, backend=None):
|
|
1520
|
+
r"""
|
|
1521
|
+
Return the icosidodecahedron.
|
|
1522
|
+
|
|
1523
|
+
The icosidodecahedron is an Archimedean solid.
|
|
1524
|
+
It has 32 faces and 30 vertices. For more information, see the
|
|
1525
|
+
:wikipedia:`Icosidodecahedron`.
|
|
1526
|
+
|
|
1527
|
+
INPUT:
|
|
1528
|
+
|
|
1529
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1530
|
+
approximate ring for the coordinates
|
|
1531
|
+
|
|
1532
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to
|
|
1533
|
+
If it is not provided and ``exact=True`` it will be a the number
|
|
1534
|
+
field `\QQ[\phi]` where `\phi` is the golden ratio and if
|
|
1535
|
+
``exact=False`` it will be the real double field.
|
|
1536
|
+
|
|
1537
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1538
|
+
|
|
1539
|
+
EXAMPLES::
|
|
1540
|
+
|
|
1541
|
+
sage: id = polytopes.icosidodecahedron_V2() # long time (6s)
|
|
1542
|
+
sage: id.f_vector() # long time
|
|
1543
|
+
(1, 30, 60, 32, 1)
|
|
1544
|
+
sage: id.base_ring() # long time
|
|
1545
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1546
|
+
with sqrt5 = 2.236067977499790?
|
|
1547
|
+
|
|
1548
|
+
A much faster implementation using floating point approximations::
|
|
1549
|
+
|
|
1550
|
+
sage: # needs cddexec
|
|
1551
|
+
sage: id = polytopes.icosidodecahedron_V2(exact=False)
|
|
1552
|
+
sage: id.f_vector()
|
|
1553
|
+
(1, 30, 60, 32, 1)
|
|
1554
|
+
sage: id.base_ring()
|
|
1555
|
+
Real Double Field
|
|
1556
|
+
|
|
1557
|
+
Its facets are 20 triangles and 12 regular pentagons::
|
|
1558
|
+
|
|
1559
|
+
sage: # needs cddexec
|
|
1560
|
+
sage: sum(1 for f in id.facets() if len(f.vertices()) == 3)
|
|
1561
|
+
20
|
|
1562
|
+
sage: sum(1 for f in id.facets() if len(f.vertices()) == 5)
|
|
1563
|
+
12
|
|
1564
|
+
|
|
1565
|
+
TESTS::
|
|
1566
|
+
|
|
1567
|
+
sage: # optional - pynormaliz, needs sage.rings.number_field
|
|
1568
|
+
sage: id = polytopes.icosidodecahedron_V2(backend='normaliz')
|
|
1569
|
+
sage: id.f_vector()
|
|
1570
|
+
(1, 30, 60, 32, 1)
|
|
1571
|
+
sage: id.base_ring()
|
|
1572
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1573
|
+
with sqrt5 = 2.236067977499790?
|
|
1574
|
+
sage: TestSuite(id).run() # long time
|
|
1575
|
+
"""
|
|
1576
|
+
if base_ring is None and exact:
|
|
1577
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1578
|
+
K = QuadraticField(5, 'sqrt5')
|
|
1579
|
+
sqrt5 = K.gen()
|
|
1580
|
+
g = (1 + sqrt5) / 2
|
|
1581
|
+
base_ring = K
|
|
1582
|
+
else:
|
|
1583
|
+
if base_ring is None:
|
|
1584
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1585
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
1586
|
+
|
|
1587
|
+
pts = [[g, 0, 0], [-g, 0, 0]]
|
|
1588
|
+
pts += [[s1 * base_ring.one() / 2, s2 * g / 2, s3 * (1 + g)/2]
|
|
1589
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1590
|
+
verts = pts
|
|
1591
|
+
verts += [[v[1], v[2], v[0]] for v in pts]
|
|
1592
|
+
verts += [[v[2], v[0], v[1]] for v in pts]
|
|
1593
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
1594
|
+
|
|
1595
|
+
def truncated_dodecahedron(self, exact=True, base_ring=None, backend=None):
|
|
1596
|
+
r"""
|
|
1597
|
+
Return the truncated dodecahedron.
|
|
1598
|
+
|
|
1599
|
+
The truncated dodecahedron is an Archimedean solid.
|
|
1600
|
+
It has 32 faces and 60 vertices. For more information, see the
|
|
1601
|
+
:wikipedia:`Truncated dodecahedron`.
|
|
1602
|
+
|
|
1603
|
+
INPUT:
|
|
1604
|
+
|
|
1605
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1606
|
+
approximate ring for the coordinates
|
|
1607
|
+
|
|
1608
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1609
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1610
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
1611
|
+
it will be the real double field.
|
|
1612
|
+
|
|
1613
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1614
|
+
|
|
1615
|
+
EXAMPLES::
|
|
1616
|
+
|
|
1617
|
+
sage: td = polytopes.truncated_dodecahedron() # needs sage.rings.number_field
|
|
1618
|
+
sage: td.f_vector() # needs sage.rings.number_field
|
|
1619
|
+
(1, 60, 90, 32, 1)
|
|
1620
|
+
sage: td.base_ring() # needs sage.rings.number_field
|
|
1621
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1622
|
+
with sqrt5 = 2.236067977499790?
|
|
1623
|
+
|
|
1624
|
+
Its facets are 20 triangles and 12 regular decagons::
|
|
1625
|
+
|
|
1626
|
+
sage: sum(1 for f in td.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
|
|
1627
|
+
20
|
|
1628
|
+
sage: sum(1 for f in td.facets() if len(f.vertices()) == 10) # needs sage.rings.number_field
|
|
1629
|
+
12
|
|
1630
|
+
|
|
1631
|
+
The faster implementation using floating point approximations does not
|
|
1632
|
+
fully work unfortunately, see https://github.com/cddlib/cddlib/pull/7
|
|
1633
|
+
for a detailed discussion of this case::
|
|
1634
|
+
|
|
1635
|
+
sage: # needs cddexec
|
|
1636
|
+
sage: td = polytopes.truncated_dodecahedron(exact=False) # random
|
|
1637
|
+
doctest:warning
|
|
1638
|
+
...
|
|
1639
|
+
UserWarning: This polyhedron data is numerically complicated; cdd
|
|
1640
|
+
could not convert between the inexact V and H representation
|
|
1641
|
+
without loss of data. The resulting object might show
|
|
1642
|
+
inconsistencies.
|
|
1643
|
+
sage: td.f_vector()
|
|
1644
|
+
Traceback (most recent call last):
|
|
1645
|
+
...
|
|
1646
|
+
ValueError: not all vertices are intersections of facets
|
|
1647
|
+
sage: td.base_ring()
|
|
1648
|
+
Real Double Field
|
|
1649
|
+
|
|
1650
|
+
TESTS::
|
|
1651
|
+
|
|
1652
|
+
sage: # optional - pynormaliz, needs sage.rings.number_field
|
|
1653
|
+
sage: td = polytopes.truncated_dodecahedron(backend='normaliz')
|
|
1654
|
+
sage: td.f_vector()
|
|
1655
|
+
(1, 60, 90, 32, 1)
|
|
1656
|
+
sage: td.base_ring()
|
|
1657
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1658
|
+
with sqrt5 = 2.236067977499790?
|
|
1659
|
+
"""
|
|
1660
|
+
if base_ring is None and exact:
|
|
1661
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1662
|
+
K = QuadraticField(5, 'sqrt5')
|
|
1663
|
+
sqrt5 = K.gen()
|
|
1664
|
+
g = (1 + sqrt5) / 2
|
|
1665
|
+
base_ring = K
|
|
1666
|
+
else:
|
|
1667
|
+
if base_ring is None:
|
|
1668
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1669
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
1670
|
+
|
|
1671
|
+
z = base_ring.zero()
|
|
1672
|
+
pts = [[z, s1 * base_ring.one() / g, s2 * (2 + g)]
|
|
1673
|
+
for s1, s2 in itertools.product([1, -1], repeat=2)]
|
|
1674
|
+
pts += [[s1 * base_ring.one() / g, s2 * g, s3 * (2 * g)]
|
|
1675
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1676
|
+
pts += [[s1 * g, s2 * base_ring(2), s3 * (g ** 2)]
|
|
1677
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1678
|
+
verts = pts
|
|
1679
|
+
verts += [[v[1], v[2], v[0]] for v in pts]
|
|
1680
|
+
verts += [[v[2], v[0], v[1]] for v in pts]
|
|
1681
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
1682
|
+
|
|
1683
|
+
def pentakis_dodecahedron(self, exact=True, base_ring=None, backend=None):
|
|
1684
|
+
r"""
|
|
1685
|
+
Return the pentakis dodecahedron.
|
|
1686
|
+
|
|
1687
|
+
The pentakis dodecahedron (orkisdodecahedron) is a face-regular,
|
|
1688
|
+
vertex-uniform polytope dual to the truncated icosahedron. It has 60
|
|
1689
|
+
facets and 32 vertices. See the :wikipedia:`Pentakis_dodecahedron` for more
|
|
1690
|
+
information.
|
|
1691
|
+
|
|
1692
|
+
INPUT:
|
|
1693
|
+
|
|
1694
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1695
|
+
approximate ring for the coordinates
|
|
1696
|
+
|
|
1697
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1698
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1699
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
1700
|
+
it will be the real double field.
|
|
1701
|
+
|
|
1702
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1703
|
+
|
|
1704
|
+
EXAMPLES::
|
|
1705
|
+
|
|
1706
|
+
sage: pd = polytopes.pentakis_dodecahedron() # long time (10s)
|
|
1707
|
+
sage: pd.n_vertices() # long time
|
|
1708
|
+
32
|
|
1709
|
+
sage: pd.n_inequalities() # long time
|
|
1710
|
+
60
|
|
1711
|
+
|
|
1712
|
+
A much faster implementation is obtained when setting ``exact=False``::
|
|
1713
|
+
|
|
1714
|
+
sage: pd = polytopes.pentakis_dodecahedron(exact=False) # needs sage.groups
|
|
1715
|
+
sage: pd.n_vertices() # needs sage.groups
|
|
1716
|
+
32
|
|
1717
|
+
sage: pd.n_inequalities() # needs sage.groups
|
|
1718
|
+
60
|
|
1719
|
+
|
|
1720
|
+
The 60 are triangles::
|
|
1721
|
+
|
|
1722
|
+
sage: all(len(f.vertices()) == 3 for f in pd.facets()) # needs sage.groups
|
|
1723
|
+
True
|
|
1724
|
+
"""
|
|
1725
|
+
return self.buckyball(exact=exact, base_ring=base_ring, backend=backend).polar()
|
|
1726
|
+
|
|
1727
|
+
def Kirkman_icosahedron(self, backend=None):
|
|
1728
|
+
r"""
|
|
1729
|
+
Return the Kirkman icosahedron.
|
|
1730
|
+
|
|
1731
|
+
The Kirkman icosahedron is a 3-polytope with integer coordinates: `(\pm
|
|
1732
|
+
9, \pm 6, \pm 6)`, `(\pm 12, \pm 4, 0)`, `(0, \pm 12, \pm 8)`, `(\pm 6,
|
|
1733
|
+
0, \pm 12)`. See [Fe2012]_ for more information.
|
|
1734
|
+
|
|
1735
|
+
INPUT:
|
|
1736
|
+
|
|
1737
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1738
|
+
|
|
1739
|
+
EXAMPLES::
|
|
1740
|
+
|
|
1741
|
+
sage: ki = polytopes.Kirkman_icosahedron()
|
|
1742
|
+
sage: ki.f_vector()
|
|
1743
|
+
(1, 20, 38, 20, 1)
|
|
1744
|
+
|
|
1745
|
+
sage: ki.volume()
|
|
1746
|
+
6528
|
|
1747
|
+
|
|
1748
|
+
sage: vertices = ki.vertices()
|
|
1749
|
+
sage: edges = [[vector(edge[0]),vector(edge[1])] for edge in ki.bounded_edges()]
|
|
1750
|
+
sage: edge_lengths = [norm(edge[0]-edge[1]) for edge in edges]
|
|
1751
|
+
sage: sorted(set(edge_lengths))
|
|
1752
|
+
[7, 8, 9, 11, 12, 14, 16]
|
|
1753
|
+
|
|
1754
|
+
TESTS::
|
|
1755
|
+
|
|
1756
|
+
sage: ki_norm = polytopes.Kirkman_icosahedron(backend='normaliz') # optional - pynormaliz
|
|
1757
|
+
sage: TestSuite(ki_norm).run() # optional - pynormaliz
|
|
1758
|
+
"""
|
|
1759
|
+
vertices = [[9, 6, 6], [-9, 6, 6], [9, -6, 6], [9, 6, -6],
|
|
1760
|
+
[-9, -6, 6], [-9, 6, -6], [9, -6, -6], [-9, -6, -6],
|
|
1761
|
+
[12, 4, 0], [-12, 4, 0], [12, -4, 0], [-12, -4, 0],
|
|
1762
|
+
[0, 12, 8], [0, -12, 8], [0, 12, -8], [0, -12, -8],
|
|
1763
|
+
[6, 0, 12], [-6, 0, 12], [6, 0, -12], [-6, 0, -12]]
|
|
1764
|
+
return Polyhedron(vertices=vertices, base_ring=ZZ, backend=backend)
|
|
1765
|
+
|
|
1766
|
+
def rhombicosidodecahedron(self, exact=True, base_ring=None, backend=None):
|
|
1767
|
+
r"""
|
|
1768
|
+
Return the rhombicosidodecahedron.
|
|
1769
|
+
|
|
1770
|
+
The rhombicosidodecahedron is an Archimedean solid.
|
|
1771
|
+
It has 62 faces and 60 vertices. For more information, see the
|
|
1772
|
+
:wikipedia:`Rhombicosidodecahedron`.
|
|
1773
|
+
|
|
1774
|
+
INPUT:
|
|
1775
|
+
|
|
1776
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1777
|
+
approximate ring for the coordinates
|
|
1778
|
+
|
|
1779
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1780
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1781
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
1782
|
+
it will be the real double field.
|
|
1783
|
+
|
|
1784
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1785
|
+
|
|
1786
|
+
EXAMPLES::
|
|
1787
|
+
|
|
1788
|
+
sage: rid = polytopes.rhombicosidodecahedron() # long time (6secs)
|
|
1789
|
+
sage: rid.f_vector() # long time
|
|
1790
|
+
(1, 60, 120, 62, 1)
|
|
1791
|
+
sage: rid.base_ring() # long time
|
|
1792
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1793
|
+
with sqrt5 = 2.236067977499790?
|
|
1794
|
+
|
|
1795
|
+
A much faster implementation using floating point approximations::
|
|
1796
|
+
|
|
1797
|
+
sage: # needs cddexec
|
|
1798
|
+
sage: rid = polytopes.rhombicosidodecahedron(exact=False)
|
|
1799
|
+
sage: rid.f_vector()
|
|
1800
|
+
(1, 60, 120, 62, 1)
|
|
1801
|
+
sage: rid.base_ring()
|
|
1802
|
+
Real Double Field
|
|
1803
|
+
|
|
1804
|
+
Its facets are 20 triangles, 30 squares and 12 pentagons::
|
|
1805
|
+
|
|
1806
|
+
sage: # needs cddexec
|
|
1807
|
+
sage: sum(1 for f in rid.facets() if len(f.vertices()) == 3)
|
|
1808
|
+
20
|
|
1809
|
+
sage: sum(1 for f in rid.facets() if len(f.vertices()) == 4)
|
|
1810
|
+
30
|
|
1811
|
+
sage: sum(1 for f in rid.facets() if len(f.vertices()) == 5)
|
|
1812
|
+
12
|
|
1813
|
+
|
|
1814
|
+
TESTS::
|
|
1815
|
+
|
|
1816
|
+
sage: # optional - pynormaliz
|
|
1817
|
+
sage: rid = polytopes.rhombicosidodecahedron(backend='normaliz')
|
|
1818
|
+
sage: rid.f_vector()
|
|
1819
|
+
(1, 60, 120, 62, 1)
|
|
1820
|
+
sage: rid.base_ring()
|
|
1821
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1822
|
+
with sqrt5 = 2.236067977499790?
|
|
1823
|
+
"""
|
|
1824
|
+
if base_ring is None and exact:
|
|
1825
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1826
|
+
K = QuadraticField(5, 'sqrt5')
|
|
1827
|
+
sqrt5 = K.gen()
|
|
1828
|
+
g = (1 + sqrt5) / 2
|
|
1829
|
+
base_ring = K
|
|
1830
|
+
else:
|
|
1831
|
+
if base_ring is None:
|
|
1832
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1833
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
1834
|
+
|
|
1835
|
+
pts = [[s1 * base_ring.one(), s2 * base_ring.one(), s3 * (g**3)]
|
|
1836
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1837
|
+
pts += [[s1 * (g**2), s2 * g, s3 * 2 * g]
|
|
1838
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1839
|
+
pts += [[s1 * (2 + g), 0, s2 * (g**2)]
|
|
1840
|
+
for s1, s2 in itertools.product([1, -1], repeat=2)]
|
|
1841
|
+
# the vertices are all even permutations of the lists in pts
|
|
1842
|
+
verts = pts
|
|
1843
|
+
verts += [[v[1], v[2], v[0]] for v in pts]
|
|
1844
|
+
verts += [[v[2], v[0], v[1]] for v in pts]
|
|
1845
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
1846
|
+
|
|
1847
|
+
def truncated_icosidodecahedron(self, exact=True, base_ring=None, backend=None):
|
|
1848
|
+
r"""
|
|
1849
|
+
Return the truncated icosidodecahedron.
|
|
1850
|
+
|
|
1851
|
+
The truncated icosidodecahedron is an Archimedean solid.
|
|
1852
|
+
It has 62 faces and 120 vertices. For more information, see the
|
|
1853
|
+
:wikipedia:`Truncated_icosidodecahedron`.
|
|
1854
|
+
|
|
1855
|
+
INPUT:
|
|
1856
|
+
|
|
1857
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use an
|
|
1858
|
+
approximate ring for the coordinates
|
|
1859
|
+
|
|
1860
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to. If
|
|
1861
|
+
it is not provided and ``exact=True`` it will be a the number field
|
|
1862
|
+
`\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
|
|
1863
|
+
it will be the real double field.
|
|
1864
|
+
|
|
1865
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1866
|
+
|
|
1867
|
+
EXAMPLES::
|
|
1868
|
+
|
|
1869
|
+
sage: ti = polytopes.truncated_icosidodecahedron() # long time
|
|
1870
|
+
sage: ti.f_vector() # long time
|
|
1871
|
+
(1, 120, 180, 62, 1)
|
|
1872
|
+
sage: ti.base_ring() # long time
|
|
1873
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1874
|
+
with sqrt5 = 2.236067977499790?
|
|
1875
|
+
|
|
1876
|
+
The implementation using floating point approximations is much faster::
|
|
1877
|
+
|
|
1878
|
+
sage: # needs cddexec
|
|
1879
|
+
sage: ti = polytopes.truncated_icosidodecahedron(exact=False) # random
|
|
1880
|
+
sage: ti.f_vector()
|
|
1881
|
+
(1, 120, 180, 62, 1)
|
|
1882
|
+
sage: ti.base_ring()
|
|
1883
|
+
Real Double Field
|
|
1884
|
+
|
|
1885
|
+
Its facets are 30 squares, 20 hexagons and 12 decagons::
|
|
1886
|
+
|
|
1887
|
+
sage: # needs cddexec
|
|
1888
|
+
sage: sum(1 for f in ti.facets() if len(f.vertices()) == 4)
|
|
1889
|
+
30
|
|
1890
|
+
sage: sum(1 for f in ti.facets() if len(f.vertices()) == 6)
|
|
1891
|
+
20
|
|
1892
|
+
sage: sum(1 for f in ti.facets() if len(f.vertices()) == 10)
|
|
1893
|
+
12
|
|
1894
|
+
|
|
1895
|
+
TESTS::
|
|
1896
|
+
|
|
1897
|
+
sage: # optional - pynormaliz
|
|
1898
|
+
sage: ti = polytopes.truncated_icosidodecahedron(backend='normaliz')
|
|
1899
|
+
sage: ti.f_vector()
|
|
1900
|
+
(1, 120, 180, 62, 1)
|
|
1901
|
+
sage: ti.base_ring()
|
|
1902
|
+
Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
|
|
1903
|
+
"""
|
|
1904
|
+
if base_ring is None and exact:
|
|
1905
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
1906
|
+
K = QuadraticField(5, 'sqrt5')
|
|
1907
|
+
sqrt5 = K.gen()
|
|
1908
|
+
g = (1 + sqrt5) / 2
|
|
1909
|
+
base_ring = K
|
|
1910
|
+
else:
|
|
1911
|
+
if base_ring is None:
|
|
1912
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1913
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
1914
|
+
|
|
1915
|
+
pts = [[s1 * 1 / g, s2 * 1 / g, s3 * (3 + g)]
|
|
1916
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1917
|
+
pts += [[s1 * 2 / g, s2 * g, s3 * (1 + 2 * g)]
|
|
1918
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1919
|
+
pts += [[s1 * 1 / g, s2 * (g**2), s3 * (-1 + 3 * g)]
|
|
1920
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1921
|
+
pts += [[s1 * (-1 + 2 * g), s2 * 2 * base_ring.one(), s3 * (2 + g)]
|
|
1922
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1923
|
+
pts += [[s1 * g, s2 * 3 * base_ring.one(), s3 * 2 * g]
|
|
1924
|
+
for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
|
|
1925
|
+
# the vertices are all ever permutations of the lists in pts
|
|
1926
|
+
verts = pts
|
|
1927
|
+
verts += [[v[1], v[2], v[0]] for v in pts]
|
|
1928
|
+
verts += [[v[2], v[0], v[1]] for v in pts]
|
|
1929
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
1930
|
+
|
|
1931
|
+
def snub_dodecahedron(self, base_ring=None, backend=None, verbose=False):
|
|
1932
|
+
"""
|
|
1933
|
+
Return the snub dodecahedron.
|
|
1934
|
+
|
|
1935
|
+
The snub dodecahedron is an Archimedean solid.
|
|
1936
|
+
It has 92 faces and 60 vertices. For more information, see the
|
|
1937
|
+
:wikipedia:`Snub_dodecahedron`.
|
|
1938
|
+
|
|
1939
|
+
INPUT:
|
|
1940
|
+
|
|
1941
|
+
- ``base_ring`` -- the ring in which the coordinates will belong to; if
|
|
1942
|
+
it is not provided it will be the real double field
|
|
1943
|
+
|
|
1944
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
1945
|
+
|
|
1946
|
+
EXAMPLES:
|
|
1947
|
+
|
|
1948
|
+
Only the backend using the optional normaliz package can construct
|
|
1949
|
+
the snub dodecahedron in reasonable time::
|
|
1950
|
+
|
|
1951
|
+
sage: sd = polytopes.snub_dodecahedron(base_ring=AA, # optional - pynormaliz, long time
|
|
1952
|
+
....: backend='normaliz')
|
|
1953
|
+
sage: sd.f_vector() # optional - pynormaliz, long time
|
|
1954
|
+
(1, 60, 150, 92, 1)
|
|
1955
|
+
sage: sd.base_ring() # optional - pynormaliz, long time
|
|
1956
|
+
Algebraic Real Field
|
|
1957
|
+
|
|
1958
|
+
Its facets are 80 triangles and 12 pentagons::
|
|
1959
|
+
|
|
1960
|
+
sage: sum(1 for f in sd.facets() # optional - pynormaliz, long time
|
|
1961
|
+
....: if len(f.vertices()) == 3)
|
|
1962
|
+
80
|
|
1963
|
+
sage: sum(1 for f in sd.facets() # optional - pynormaliz, long time
|
|
1964
|
+
....: if len(f.vertices()) == 5)
|
|
1965
|
+
12
|
|
1966
|
+
|
|
1967
|
+
TESTS:
|
|
1968
|
+
|
|
1969
|
+
The cdd backend with floating point arithmetic fails for this
|
|
1970
|
+
polytope::
|
|
1971
|
+
|
|
1972
|
+
sage: sd = polytopes.snub_dodecahedron() # not tested
|
|
1973
|
+
sage: sd.f_vector() # not tested
|
|
1974
|
+
(1, 60, 150, 92, 1)
|
|
1975
|
+
sage: sd.base_ring() # not tested
|
|
1976
|
+
Real Double Field
|
|
1977
|
+
"""
|
|
1978
|
+
if base_ring is None:
|
|
1979
|
+
from sage.rings.real_double import RDF as base_ring
|
|
1980
|
+
phi = (1 + base_ring(5).sqrt()) / 2
|
|
1981
|
+
xi = ((phi/2 + (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)) +
|
|
1982
|
+
(phi/2 - (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)))
|
|
1983
|
+
|
|
1984
|
+
alpha = xi - 1 / xi
|
|
1985
|
+
beta = xi * phi + phi**2 + phi / xi
|
|
1986
|
+
signs = [[-1,-1,-1], [-1,1,1], [1,-1,1], [1,1,-1]]
|
|
1987
|
+
|
|
1988
|
+
pts = [[s1 * 2 * alpha, s2 * 2 * base_ring.one(), s3 * 2 * beta]
|
|
1989
|
+
for s1, s2, s3 in signs]
|
|
1990
|
+
pts += [[s1 * (alpha + beta/phi + phi), s2 * (-alpha * phi + beta + 1/phi), s3 * (alpha/phi + beta * phi - 1)]
|
|
1991
|
+
for s1, s2, s3 in signs]
|
|
1992
|
+
pts += [[s1 * (alpha + beta/phi - phi), s2 * (alpha * phi - beta + 1/phi), s3 * (alpha/phi + beta * phi + 1)]
|
|
1993
|
+
for s1, s2, s3 in signs]
|
|
1994
|
+
pts += [[s1 * (-alpha/phi + beta * phi + 1), s2 * (-alpha + beta/phi - phi), s3 * (alpha * phi + beta - 1/phi)]
|
|
1995
|
+
for s1, s2, s3 in signs]
|
|
1996
|
+
pts += [[s1 * (-alpha/phi + beta * phi - 1), s2 * (alpha - beta/phi - phi), s3 * (alpha * phi + beta + 1/phi)]
|
|
1997
|
+
for s1, s2, s3 in signs]
|
|
1998
|
+
|
|
1999
|
+
# the vertices are all even permutations of the lists in pts
|
|
2000
|
+
verts = pts
|
|
2001
|
+
verts += [[v[1], v[2], v[0]] for v in pts]
|
|
2002
|
+
verts += [[v[2], v[0], v[1]] for v in pts]
|
|
2003
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)
|
|
2004
|
+
|
|
2005
|
+
def twenty_four_cell(self, backend=None):
|
|
2006
|
+
"""
|
|
2007
|
+
Return the standard 24-cell polytope.
|
|
2008
|
+
|
|
2009
|
+
The 24-cell polyhedron (also called icositetrachoron or octaplex) is a
|
|
2010
|
+
regular polyhedron in 4-dimension. For more information see
|
|
2011
|
+
the :wikipedia:`24-cell`.
|
|
2012
|
+
|
|
2013
|
+
INPUT:
|
|
2014
|
+
|
|
2015
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2016
|
+
|
|
2017
|
+
EXAMPLES::
|
|
2018
|
+
|
|
2019
|
+
sage: p24 = polytopes.twenty_four_cell()
|
|
2020
|
+
sage: p24.f_vector()
|
|
2021
|
+
(1, 24, 96, 96, 24, 1)
|
|
2022
|
+
sage: v = next(p24.vertex_generator())
|
|
2023
|
+
sage: for adj in v.neighbors(): print(adj)
|
|
2024
|
+
A vertex at (-1/2, -1/2, -1/2, 1/2)
|
|
2025
|
+
A vertex at (-1/2, -1/2, 1/2, -1/2)
|
|
2026
|
+
A vertex at (-1, 0, 0, 0)
|
|
2027
|
+
A vertex at (-1/2, 1/2, -1/2, -1/2)
|
|
2028
|
+
A vertex at (0, -1, 0, 0)
|
|
2029
|
+
A vertex at (0, 0, -1, 0)
|
|
2030
|
+
A vertex at (0, 0, 0, -1)
|
|
2031
|
+
A vertex at (1/2, -1/2, -1/2, -1/2)
|
|
2032
|
+
|
|
2033
|
+
sage: p24.volume()
|
|
2034
|
+
2
|
|
2035
|
+
|
|
2036
|
+
TESTS::
|
|
2037
|
+
|
|
2038
|
+
sage: tfcell = polytopes.twenty_four_cell(backend='normaliz') # optional - pynormaliz
|
|
2039
|
+
sage: TestSuite(tfcell).run() # optional - pynormaliz
|
|
2040
|
+
"""
|
|
2041
|
+
q12 = QQ((1, 2))
|
|
2042
|
+
verts = list(itertools.product([q12, -q12], repeat=4))
|
|
2043
|
+
B4 = (ZZ**4).basis()
|
|
2044
|
+
verts.extend(B4)
|
|
2045
|
+
verts.extend(-v for v in B4)
|
|
2046
|
+
return Polyhedron(vertices=verts, backend=backend)
|
|
2047
|
+
|
|
2048
|
+
def runcitruncated_six_hundred_cell(self, exact=True, backend=None):
|
|
2049
|
+
"""
|
|
2050
|
+
Return the runcitruncated 600-cell.
|
|
2051
|
+
|
|
2052
|
+
The runcitruncated 600-cell is a 4-dimensional 4-uniform polytope in
|
|
2053
|
+
the `H_4` family. It has 7200 vertices. For more information see
|
|
2054
|
+
:wikipedia:`Runcitruncated 600-cell`.
|
|
2055
|
+
|
|
2056
|
+
.. WARNING::
|
|
2057
|
+
|
|
2058
|
+
The coordinates are exact by default. The computation with inexact
|
|
2059
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2060
|
+
inconsistency error, and thus cannot be computed.
|
|
2061
|
+
|
|
2062
|
+
INPUT:
|
|
2063
|
+
|
|
2064
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2065
|
+
coordinates instead of floating point approximations
|
|
2066
|
+
|
|
2067
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2068
|
+
|
|
2069
|
+
EXAMPLES::
|
|
2070
|
+
|
|
2071
|
+
sage: polytopes.runcitruncated_six_hundred_cell(backend='normaliz') # not tested - very long time
|
|
2072
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of
|
|
2073
|
+
7200 vertices
|
|
2074
|
+
"""
|
|
2075
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 1], exact=exact, backend=backend, regular=True)
|
|
2076
|
+
|
|
2077
|
+
def cantitruncated_six_hundred_cell(self, exact=True, backend=None):
|
|
2078
|
+
"""
|
|
2079
|
+
Return the cantitruncated 600-cell.
|
|
2080
|
+
|
|
2081
|
+
The cantitruncated 600-cell is a 4-dimensional 4-uniform polytope in
|
|
2082
|
+
the `H_4` family. It has 7200 vertices. For more information see
|
|
2083
|
+
:wikipedia:`Cantitruncated 600-cell`.
|
|
2084
|
+
|
|
2085
|
+
.. WARNING::
|
|
2086
|
+
|
|
2087
|
+
The coordinates are exact by default. The computation with inexact
|
|
2088
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2089
|
+
inconsistency error, and thus cannot be computed.
|
|
2090
|
+
|
|
2091
|
+
INPUT:
|
|
2092
|
+
|
|
2093
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2094
|
+
coordinates instead of floating point approximations
|
|
2095
|
+
|
|
2096
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2097
|
+
|
|
2098
|
+
EXAMPLES::
|
|
2099
|
+
|
|
2100
|
+
sage: polytopes.cantitruncated_six_hundred_cell(exact=True, # not tested - very long time
|
|
2101
|
+
....: backend='normaliz')
|
|
2102
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
|
|
2103
|
+
"""
|
|
2104
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 1, 1, 0], exact=exact, backend=backend, regular=True)
|
|
2105
|
+
|
|
2106
|
+
def bitruncated_six_hundred_cell(self, exact=True, backend=None):
|
|
2107
|
+
"""
|
|
2108
|
+
Return the bitruncated 600-cell.
|
|
2109
|
+
|
|
2110
|
+
The bitruncated 600-cell is a 4-dimensional 4-uniform polytope in the
|
|
2111
|
+
`H_4` family. It has 3600 vertices. For more information see
|
|
2112
|
+
:wikipedia:`Bitruncated 600-cell`.
|
|
2113
|
+
|
|
2114
|
+
.. WARNING::
|
|
2115
|
+
|
|
2116
|
+
The coordinates are exact by default. The computation with inexact
|
|
2117
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2118
|
+
inconsistency error, and thus cannot be computed.
|
|
2119
|
+
|
|
2120
|
+
INPUT:
|
|
2121
|
+
|
|
2122
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2123
|
+
coordinates instead of floating point approximations
|
|
2124
|
+
|
|
2125
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2126
|
+
|
|
2127
|
+
EXAMPLES::
|
|
2128
|
+
|
|
2129
|
+
sage: polytopes.runcinated_six_hundred_cell(exact=True, # not tested - very long time
|
|
2130
|
+
....: backend='normaliz')
|
|
2131
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
|
|
2132
|
+
"""
|
|
2133
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 0], exact=exact, backend=backend, regular=True)
|
|
2134
|
+
|
|
2135
|
+
def cantellated_six_hundred_cell(self, exact=False, backend=None):
|
|
2136
|
+
"""
|
|
2137
|
+
Return the cantellated 600-cell.
|
|
2138
|
+
|
|
2139
|
+
The cantellated 600-cell is a 4-dimensional 4-uniform polytope in the
|
|
2140
|
+
`H_4` family. It has 3600 vertices. For more information see
|
|
2141
|
+
:wikipedia:`Cantellated 600-cell`.
|
|
2142
|
+
|
|
2143
|
+
.. WARNING::
|
|
2144
|
+
|
|
2145
|
+
The coordinates are inexact by default. The computation with
|
|
2146
|
+
inexact coordinates (using the backend ``'cdd'``) issues a
|
|
2147
|
+
UserWarning on inconsistencies.
|
|
2148
|
+
|
|
2149
|
+
INPUT:
|
|
2150
|
+
|
|
2151
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
2152
|
+
coordinates instead of floating point approximations
|
|
2153
|
+
|
|
2154
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2155
|
+
|
|
2156
|
+
EXAMPLES::
|
|
2157
|
+
|
|
2158
|
+
sage: polytopes.cantellated_six_hundred_cell() # not tested - very long time
|
|
2159
|
+
doctest:warning
|
|
2160
|
+
...
|
|
2161
|
+
UserWarning: This polyhedron data is numerically complicated; cdd
|
|
2162
|
+
could not convert between the inexact V and H representation
|
|
2163
|
+
without loss of data. The resulting object might show
|
|
2164
|
+
inconsistencies.
|
|
2165
|
+
A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 3600 vertices
|
|
2166
|
+
|
|
2167
|
+
It is possible to use the backend ``'normaliz'`` to get an exact
|
|
2168
|
+
representation::
|
|
2169
|
+
|
|
2170
|
+
sage: polytopes.cantellated_six_hundred_cell(exact=True, # not tested - long time
|
|
2171
|
+
....: backend='normaliz')
|
|
2172
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
|
|
2173
|
+
"""
|
|
2174
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 0], exact=exact, backend=backend, regular=True)
|
|
2175
|
+
|
|
2176
|
+
def truncated_six_hundred_cell(self, exact=False, backend=None):
|
|
2177
|
+
"""
|
|
2178
|
+
Return the truncated 600-cell.
|
|
2179
|
+
|
|
2180
|
+
The truncated 600-cell is a 4-dimensional 4-uniform polytope in the
|
|
2181
|
+
`H_4` family. It has 1440 vertices. For more information see
|
|
2182
|
+
:wikipedia:`Truncated 600-cell`.
|
|
2183
|
+
|
|
2184
|
+
.. WARNING::
|
|
2185
|
+
|
|
2186
|
+
The coordinates are not exact by default. The computation with
|
|
2187
|
+
exact coordinates takes a huge amount of time.
|
|
2188
|
+
|
|
2189
|
+
INPUT:
|
|
2190
|
+
|
|
2191
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
2192
|
+
coordinates instead of floating point approximations
|
|
2193
|
+
|
|
2194
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2195
|
+
|
|
2196
|
+
EXAMPLES::
|
|
2197
|
+
|
|
2198
|
+
sage: polytopes.truncated_six_hundred_cell() # not tested - long time
|
|
2199
|
+
A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 1440 vertices
|
|
2200
|
+
|
|
2201
|
+
It is possible to use the backend ``'normaliz'`` to get an exact
|
|
2202
|
+
representation::
|
|
2203
|
+
|
|
2204
|
+
sage: polytopes.truncated_six_hundred_cell(exact=True,backend='normaliz') # not tested, long time (16s)
|
|
2205
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1440 vertices
|
|
2206
|
+
"""
|
|
2207
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 0], exact=exact, backend=backend, regular=True)
|
|
2208
|
+
|
|
2209
|
+
def rectified_six_hundred_cell(self, exact=True, backend=None):
|
|
2210
|
+
"""
|
|
2211
|
+
Return the rectified 600-cell.
|
|
2212
|
+
|
|
2213
|
+
The rectified 600-cell is a 4-dimensional 4-uniform polytope in the
|
|
2214
|
+
`H_4` family. It has 720 vertices. For more information see
|
|
2215
|
+
:wikipedia:`Rectified 600-cell`.
|
|
2216
|
+
|
|
2217
|
+
.. WARNING::
|
|
2218
|
+
|
|
2219
|
+
The coordinates are exact by default. The computation with inexact
|
|
2220
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2221
|
+
inconsistency error, and thus cannot be computed.
|
|
2222
|
+
|
|
2223
|
+
INPUT:
|
|
2224
|
+
|
|
2225
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2226
|
+
coordinates instead of floating point approximations
|
|
2227
|
+
|
|
2228
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2229
|
+
|
|
2230
|
+
EXAMPLES::
|
|
2231
|
+
|
|
2232
|
+
sage: polytopes.rectified_six_hundred_cell(backend='normaliz') # not tested, long time (14s)
|
|
2233
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 720 vertices
|
|
2234
|
+
"""
|
|
2235
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 0], exact=exact, backend=backend, regular=True)
|
|
2236
|
+
|
|
2237
|
+
def six_hundred_cell(self, exact=False, backend=None):
|
|
2238
|
+
"""
|
|
2239
|
+
Return the standard 600-cell polytope.
|
|
2240
|
+
|
|
2241
|
+
The 600-cell is a 4-dimensional regular polytope. In many ways this is
|
|
2242
|
+
an analogue of the icosahedron.
|
|
2243
|
+
|
|
2244
|
+
.. WARNING::
|
|
2245
|
+
|
|
2246
|
+
The coordinates are not exact by default. The computation with
|
|
2247
|
+
exact coordinates takes a huge amount of time.
|
|
2248
|
+
|
|
2249
|
+
INPUT:
|
|
2250
|
+
|
|
2251
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
2252
|
+
coordinates instead of floating point approximations
|
|
2253
|
+
|
|
2254
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2255
|
+
|
|
2256
|
+
EXAMPLES::
|
|
2257
|
+
|
|
2258
|
+
sage: p600 = polytopes.six_hundred_cell(); p600 # needs sage.groups
|
|
2259
|
+
A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 120 vertices
|
|
2260
|
+
sage: p600.f_vector() # long time (~2sec) # needs sage.groups
|
|
2261
|
+
(1, 120, 720, 1200, 600, 1)
|
|
2262
|
+
|
|
2263
|
+
Computation with exact coordinates is currently too long to be useful::
|
|
2264
|
+
|
|
2265
|
+
sage: p600 = polytopes.six_hundred_cell(exact=True) # long time, not tested, needs sage.groups
|
|
2266
|
+
sage: len(list(p600.bounded_edges())) # long time, not tested, needs sage.groups
|
|
2267
|
+
720
|
|
2268
|
+
|
|
2269
|
+
TESTS::
|
|
2270
|
+
|
|
2271
|
+
sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
|
|
2272
|
+
sage: p600 = polytopes.six_hundred_cell(exact=True,
|
|
2273
|
+
....: backend='normaliz')
|
|
2274
|
+
sage: len(list(p600.bounded_edges())) # long time
|
|
2275
|
+
720
|
|
2276
|
+
"""
|
|
2277
|
+
if exact:
|
|
2278
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
2279
|
+
K = QuadraticField(5, 'sqrt5')
|
|
2280
|
+
sqrt5 = K.gen()
|
|
2281
|
+
g = (1 + sqrt5) / 2
|
|
2282
|
+
base_ring = K
|
|
2283
|
+
else:
|
|
2284
|
+
from sage.rings.real_double import RDF as base_ring
|
|
2285
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
2286
|
+
|
|
2287
|
+
q12 = base_ring(1) / base_ring(2)
|
|
2288
|
+
z = base_ring.zero()
|
|
2289
|
+
verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in itertools.product([1,-1], repeat=4)]
|
|
2290
|
+
V = (base_ring)**4
|
|
2291
|
+
verts.extend(V.basis())
|
|
2292
|
+
verts.extend(-v for v in V.basis())
|
|
2293
|
+
pts = [[s1 * q12, s2*g/2, s3/(2*g), z] for (s1,s2,s3) in itertools.product([1,-1], repeat=3)]
|
|
2294
|
+
for p in AlternatingGroup(4):
|
|
2295
|
+
verts.extend(p(x) for x in pts)
|
|
2296
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
2297
|
+
|
|
2298
|
+
def grand_antiprism(self, exact=True, backend=None, verbose=False):
|
|
2299
|
+
"""
|
|
2300
|
+
Return the grand antiprism.
|
|
2301
|
+
|
|
2302
|
+
The grand antiprism is a 4-dimensional non-Wythoffian uniform polytope.
|
|
2303
|
+
The coordinates were taken from http://eusebeia.dyndns.org/4d/gap. For
|
|
2304
|
+
more information, see the :wikipedia:`Grand_antiprism`.
|
|
2305
|
+
|
|
2306
|
+
.. WARNING::
|
|
2307
|
+
|
|
2308
|
+
The coordinates are exact by default. The computation with exact
|
|
2309
|
+
coordinates is not as fast as with floating point approximations.
|
|
2310
|
+
If you find this method to be too slow, consider using floating
|
|
2311
|
+
point approximations
|
|
2312
|
+
|
|
2313
|
+
INPUT:
|
|
2314
|
+
|
|
2315
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use floating
|
|
2316
|
+
point approximations instead of exact coordinates
|
|
2317
|
+
|
|
2318
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2319
|
+
|
|
2320
|
+
EXAMPLES::
|
|
2321
|
+
|
|
2322
|
+
sage: gap = polytopes.grand_antiprism() # not tested - very long time
|
|
2323
|
+
sage: gap # not tested - very long time
|
|
2324
|
+
A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
|
|
2325
|
+
polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
|
|
2326
|
+
the convex hull of 100 vertices
|
|
2327
|
+
|
|
2328
|
+
Computation with the backend ``'normaliz'`` is instantaneous::
|
|
2329
|
+
|
|
2330
|
+
sage: gap_norm = polytopes.grand_antiprism(backend='normaliz') # optional - pynormaliz, needs sage.rings.number_field
|
|
2331
|
+
sage: gap_norm # optional - pynormaliz, needs sage.rings.number_field
|
|
2332
|
+
A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
|
|
2333
|
+
polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
|
|
2334
|
+
the convex hull of 100 vertices
|
|
2335
|
+
|
|
2336
|
+
Computation with approximated coordinates is also faster, but inexact::
|
|
2337
|
+
|
|
2338
|
+
sage: # needs cddexec
|
|
2339
|
+
sage: gap = polytopes.grand_antiprism(exact=False) # random
|
|
2340
|
+
sage: gap
|
|
2341
|
+
A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 100 vertices
|
|
2342
|
+
sage: gap.f_vector()
|
|
2343
|
+
(1, 100, 500, 720, 320, 1)
|
|
2344
|
+
sage: len(list(gap.bounded_edges()))
|
|
2345
|
+
500
|
|
2346
|
+
"""
|
|
2347
|
+
from itertools import product
|
|
2348
|
+
|
|
2349
|
+
if exact:
|
|
2350
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
2351
|
+
K = QuadraticField(5, 'sqrt5')
|
|
2352
|
+
sqrt5 = K.gen()
|
|
2353
|
+
g = (1 + sqrt5) / 2
|
|
2354
|
+
base_ring = K
|
|
2355
|
+
else:
|
|
2356
|
+
from sage.rings.real_double import RDF as base_ring
|
|
2357
|
+
g = (1 + base_ring(5).sqrt()) / 2
|
|
2358
|
+
|
|
2359
|
+
q12 = base_ring(1) / base_ring(2)
|
|
2360
|
+
z = base_ring.zero()
|
|
2361
|
+
verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in product([1,-1], repeat=4)]
|
|
2362
|
+
V = (base_ring)**4
|
|
2363
|
+
verts.extend(V.basis()[2:])
|
|
2364
|
+
verts.extend(-v for v in V.basis()[2:])
|
|
2365
|
+
|
|
2366
|
+
verts.extend([s1 * q12, s2/(2*g), s3*g/2, z] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2367
|
+
verts.extend([s3*g/2, s1 * q12, s2/(2*g), z] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2368
|
+
verts.extend([s2/(2*g), s3*g/2, s1 * q12, z] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2369
|
+
|
|
2370
|
+
verts.extend([s1 * q12, s2*g/2, z, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2371
|
+
verts.extend([s3/(2*g), s1 * q12, z, s2*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2372
|
+
verts.extend([s2*g/2, s3/(2*g), z, s1 * q12] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2373
|
+
|
|
2374
|
+
verts.extend([s1 * q12, z, s2/(2*g), s3*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2375
|
+
|
|
2376
|
+
verts.extend([z, s1 * q12, s2*g/2, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))
|
|
2377
|
+
|
|
2378
|
+
verts.extend([z, s1/(2*g), q12, g/2] for s1 in [1, -1])
|
|
2379
|
+
verts.extend([z, s1/(2*g), -q12, -g/2] for s1 in [1, -1])
|
|
2380
|
+
|
|
2381
|
+
verts.extend([z, s1*g/2, 1/(2*g), q12] for s1 in [1, -1])
|
|
2382
|
+
verts.extend([z, s1*g/2, -1/(2*g), -q12] for s1 in [1, -1])
|
|
2383
|
+
|
|
2384
|
+
verts.extend([s1*g/2, z, q12, -1/(2*g)] for s1 in [1, -1])
|
|
2385
|
+
verts.extend([s1*g/2, z, -q12, 1/(2*g)] for s1 in [1, -1])
|
|
2386
|
+
|
|
2387
|
+
verts.extend([s1/(2*g), z, g/2, -q12] for s1 in [1, -1])
|
|
2388
|
+
verts.extend([s1/(2*g), z, -g/2, q12] for s1 in [1, -1])
|
|
2389
|
+
|
|
2390
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)
|
|
2391
|
+
|
|
2392
|
+
def Gosset_3_21(self, backend=None):
|
|
2393
|
+
r"""
|
|
2394
|
+
Return the Gosset `3_{21}` polytope.
|
|
2395
|
+
|
|
2396
|
+
The Gosset `3_{21}` polytope is a uniform 7-polytope. It has 56
|
|
2397
|
+
vertices, and 702 facets: `126` `3_{11}` and `576` `6`-simplex. For
|
|
2398
|
+
more information, see the :wikipedia:`3_21_polytope`.
|
|
2399
|
+
|
|
2400
|
+
INPUT:
|
|
2401
|
+
|
|
2402
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2403
|
+
|
|
2404
|
+
EXAMPLES::
|
|
2405
|
+
|
|
2406
|
+
sage: g = polytopes.Gosset_3_21(); g
|
|
2407
|
+
A 7-dimensional polyhedron in ZZ^8 defined as the convex hull of 56 vertices
|
|
2408
|
+
sage: g.f_vector() # not tested (~16s)
|
|
2409
|
+
(1, 56, 756, 4032, 10080, 12096, 6048, 702, 1)
|
|
2410
|
+
|
|
2411
|
+
TESTS::
|
|
2412
|
+
|
|
2413
|
+
sage: G321 = polytopes.Gosset_3_21(backend='normaliz') # optional - pynormaliz
|
|
2414
|
+
sage: TestSuite(G321).run() # optional - pynormaliz, long time
|
|
2415
|
+
"""
|
|
2416
|
+
from itertools import combinations
|
|
2417
|
+
verts = []
|
|
2418
|
+
for i, j in combinations(range(8), 2):
|
|
2419
|
+
x = [1]*8
|
|
2420
|
+
x[i] = x[j] = -3
|
|
2421
|
+
verts.append(x)
|
|
2422
|
+
verts.append([-xx for xx in x])
|
|
2423
|
+
|
|
2424
|
+
return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)
|
|
2425
|
+
|
|
2426
|
+
def cyclic_polytope(self, dim, n, base_ring=QQ, backend=None):
|
|
2427
|
+
r"""
|
|
2428
|
+
Return a cyclic polytope.
|
|
2429
|
+
|
|
2430
|
+
A cyclic polytope of dimension ``dim`` with ``n`` vertices is the
|
|
2431
|
+
convex hull of the points ``(t,t^2,...,t^dim)`` with `t \in
|
|
2432
|
+
\{0,1,...,n-1\}` . For more information, see the
|
|
2433
|
+
:wikipedia:`Cyclic_polytope`.
|
|
2434
|
+
|
|
2435
|
+
INPUT:
|
|
2436
|
+
|
|
2437
|
+
- ``dim`` -- positive integer; the dimension of the polytope
|
|
2438
|
+
|
|
2439
|
+
- ``n`` -- positive integer; the number of vertices
|
|
2440
|
+
|
|
2441
|
+
- ``base_ring`` -- either ``QQ`` (default) or ``RDF``
|
|
2442
|
+
|
|
2443
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2444
|
+
|
|
2445
|
+
EXAMPLES::
|
|
2446
|
+
|
|
2447
|
+
sage: c = polytopes.cyclic_polytope(4,10)
|
|
2448
|
+
sage: c.f_vector()
|
|
2449
|
+
(1, 10, 45, 70, 35, 1)
|
|
2450
|
+
|
|
2451
|
+
TESTS::
|
|
2452
|
+
|
|
2453
|
+
sage: cp = polytopes.cyclic_polytope(4,10,backend='normaliz') # optional - pynormaliz
|
|
2454
|
+
sage: TestSuite(cp).run() # optional - pynormaliz
|
|
2455
|
+
"""
|
|
2456
|
+
verts = [[t**i for i in range(1, dim+1)] for t in range(n)]
|
|
2457
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
2458
|
+
|
|
2459
|
+
def hypersimplex(self, dim, k, project=False, backend=None):
|
|
2460
|
+
r"""
|
|
2461
|
+
Return the hypersimplex in dimension ``dim`` and parameter ``k``.
|
|
2462
|
+
|
|
2463
|
+
The hypersimplex `\Delta_{d,k}` is the convex hull of the vertices made
|
|
2464
|
+
of `k` ones and `d-k` zeros. It lies in the `d-1` hyperplane of vectors
|
|
2465
|
+
of sum `k`. If you want a projected version to `\RR^{d-1}` (with
|
|
2466
|
+
floating point coordinates) then set ``project=True`` in the options.
|
|
2467
|
+
|
|
2468
|
+
.. SEEALSO::
|
|
2469
|
+
|
|
2470
|
+
:meth:`simplex`
|
|
2471
|
+
|
|
2472
|
+
INPUT:
|
|
2473
|
+
|
|
2474
|
+
- ``dim`` -- the dimension
|
|
2475
|
+
|
|
2476
|
+
- ``n`` -- the numbers ``(1,...,n)`` are permuted
|
|
2477
|
+
|
|
2478
|
+
- ``project`` -- boolean (default: ``False``); if ``True``, the polytope
|
|
2479
|
+
is (isometrically) projected to a vector space of dimension
|
|
2480
|
+
``dim-1``. This operation turns the coordinates into floating point
|
|
2481
|
+
approximations and corresponds to the projection given by the matrix
|
|
2482
|
+
from :func:`zero_sum_projection`.
|
|
2483
|
+
|
|
2484
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2485
|
+
|
|
2486
|
+
EXAMPLES::
|
|
2487
|
+
|
|
2488
|
+
sage: # needs sage.combinat
|
|
2489
|
+
sage: h_4_2 = polytopes.hypersimplex(4, 2)
|
|
2490
|
+
sage: h_4_2
|
|
2491
|
+
A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices
|
|
2492
|
+
sage: h_4_2.f_vector()
|
|
2493
|
+
(1, 6, 12, 8, 1)
|
|
2494
|
+
sage: h_4_2.ehrhart_polynomial() # optional - latte_int
|
|
2495
|
+
2/3*t^3 + 2*t^2 + 7/3*t + 1
|
|
2496
|
+
sage: TestSuite(h_4_2).run()
|
|
2497
|
+
|
|
2498
|
+
sage: # needs sage.combinat
|
|
2499
|
+
sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True)
|
|
2500
|
+
sage: h_7_3
|
|
2501
|
+
A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices
|
|
2502
|
+
sage: h_7_3.f_vector()
|
|
2503
|
+
(1, 35, 210, 350, 245, 84, 14, 1)
|
|
2504
|
+
sage: TestSuite(h_7_3).run(skip=["_test_pyramid", "_test_lawrence"])
|
|
2505
|
+
"""
|
|
2506
|
+
verts = Permutations([0] * (dim - k) + [1] * k).list()
|
|
2507
|
+
if project:
|
|
2508
|
+
verts = project_points(*verts)
|
|
2509
|
+
return Polyhedron(vertices=verts, backend=backend)
|
|
2510
|
+
|
|
2511
|
+
def permutahedron(self, n, project=False, backend=None):
|
|
2512
|
+
r"""
|
|
2513
|
+
Return the standard permutahedron of (1,...,n).
|
|
2514
|
+
|
|
2515
|
+
The permutahedron (or permutohedron) is the convex hull of the
|
|
2516
|
+
permutations of `\{1,\ldots,n\}` seen as vectors. The edges
|
|
2517
|
+
between the permutations correspond to multiplication on the
|
|
2518
|
+
right by an elementary transposition in the
|
|
2519
|
+
:class:`~sage.groups.perm_gps.permgroup_named.SymmetricGroup`.
|
|
2520
|
+
|
|
2521
|
+
If we take the graph in which the vertices correspond to
|
|
2522
|
+
vertices of the polyhedron, and edges to edges, we get the
|
|
2523
|
+
:meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`.
|
|
2524
|
+
|
|
2525
|
+
INPUT:
|
|
2526
|
+
|
|
2527
|
+
- ``n`` -- integer
|
|
2528
|
+
|
|
2529
|
+
- ``project`` -- boolean (default: ``False``); if ``True``, the polytope
|
|
2530
|
+
is (isometrically) projected to a vector space of dimension
|
|
2531
|
+
``dim-1``. This operation turns the coordinates into floating point
|
|
2532
|
+
approximations and corresponds to the projection given by the matrix
|
|
2533
|
+
from :func:`zero_sum_projection`.
|
|
2534
|
+
|
|
2535
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2536
|
+
|
|
2537
|
+
EXAMPLES::
|
|
2538
|
+
|
|
2539
|
+
sage: perm4 = polytopes.permutahedron(4); perm4
|
|
2540
|
+
A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 24 vertices
|
|
2541
|
+
sage: perm4.is_lattice_polytope()
|
|
2542
|
+
True
|
|
2543
|
+
sage: perm4.ehrhart_polynomial() # optional - latte_int
|
|
2544
|
+
16*t^3 + 15*t^2 + 6*t + 1
|
|
2545
|
+
|
|
2546
|
+
sage: # needs cddexec
|
|
2547
|
+
sage: perm4 = polytopes.permutahedron(4, project=True); perm4
|
|
2548
|
+
A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
|
|
2549
|
+
sage: perm4.plot() # needs sage.plot
|
|
2550
|
+
Graphics3d Object
|
|
2551
|
+
sage: perm4.graph().is_isomorphic(graphs.BubbleSortGraph(4)) # needs sage.graphs
|
|
2552
|
+
True
|
|
2553
|
+
|
|
2554
|
+
As both Hrepresentation and Vrepresentation are known, the permutahedron can be set
|
|
2555
|
+
up with both using the backend ``field``. The following takes very very long time
|
|
2556
|
+
to recompute, e.g. with backend ``ppl``::
|
|
2557
|
+
|
|
2558
|
+
sage: polytopes.permutahedron(8, backend='field') # (~1s)
|
|
2559
|
+
A 7-dimensional polyhedron in QQ^8 defined as the convex hull of 40320 vertices
|
|
2560
|
+
sage: polytopes.permutahedron(9, backend='field') # not tested (memory consumption) # (~5s)
|
|
2561
|
+
A 8-dimensional polyhedron in QQ^9 defined as the convex hull of 362880 vertices
|
|
2562
|
+
|
|
2563
|
+
.. SEEALSO::
|
|
2564
|
+
|
|
2565
|
+
* :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`
|
|
2566
|
+
|
|
2567
|
+
TESTS::
|
|
2568
|
+
|
|
2569
|
+
sage: p4 = polytopes.permutahedron(4, backend='normaliz') # optional - pynormaliz
|
|
2570
|
+
sage: TestSuite(p4).run() # optional - pynormaliz
|
|
2571
|
+
|
|
2572
|
+
Check that precomputed data is correct::
|
|
2573
|
+
|
|
2574
|
+
sage: P = polytopes.permutahedron(5, backend='field')
|
|
2575
|
+
sage: TestSuite(P).run() # long time
|
|
2576
|
+
"""
|
|
2577
|
+
verts = itertools.permutations(range(1, n + 1))
|
|
2578
|
+
if project:
|
|
2579
|
+
verts = project_points(*verts)
|
|
2580
|
+
return Polyhedron(vertices=verts, backend=backend)
|
|
2581
|
+
else:
|
|
2582
|
+
parent = Polyhedra(ZZ, n, backend=backend)
|
|
2583
|
+
|
|
2584
|
+
def tri(m):
|
|
2585
|
+
return (m * (m + 1)) // 2
|
|
2586
|
+
|
|
2587
|
+
# Each proper `S \subset [n]` corresponds exactly to
|
|
2588
|
+
# a facet that minimizes the coordinates in `S`.
|
|
2589
|
+
# The minimal sum for `m` coordinates is `(m*(m+1))/2`.
|
|
2590
|
+
ieqs = ((-tri(sum(x)),) + x
|
|
2591
|
+
for x in itertools.product([0,1], repeat=n)
|
|
2592
|
+
if 0 < sum(x) < n)
|
|
2593
|
+
|
|
2594
|
+
# Adding the defining equality.
|
|
2595
|
+
eqns = ((-tri(n),) + tuple(1 for _ in range(n)),)
|
|
2596
|
+
|
|
2597
|
+
return parent([verts, [], []], [ieqs, eqns],
|
|
2598
|
+
Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep')
|
|
2599
|
+
|
|
2600
|
+
def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regular=False, backend=None):
|
|
2601
|
+
r"""
|
|
2602
|
+
Return the generalized permutahedron of type ``coxeter_type`` as the
|
|
2603
|
+
convex hull of the orbit of ``point`` in the fundamental cone.
|
|
2604
|
+
|
|
2605
|
+
This generalized permutahedron lies in the vector space used in the
|
|
2606
|
+
geometric representation, that is, in the default case, the dimension
|
|
2607
|
+
of generalized permutahedron equals the dimension of the space.
|
|
2608
|
+
|
|
2609
|
+
INPUT:
|
|
2610
|
+
|
|
2611
|
+
- ``coxeter_type`` -- a Coxeter type; given as a pair [type,rank],
|
|
2612
|
+
where type is a letter and rank is the number of generators
|
|
2613
|
+
|
|
2614
|
+
- ``point`` -- list (default: ``None``); a point given by its
|
|
2615
|
+
coordinates in the weight basis. If ``None`` is given, the point
|
|
2616
|
+
`(1, 1, 1, \ldots)` is used.
|
|
2617
|
+
|
|
2618
|
+
- ``exact`` -- boolean (default: ``True``); if ``False`` use floating
|
|
2619
|
+
point approximations instead of exact coordinates
|
|
2620
|
+
|
|
2621
|
+
- ``regular`` -- boolean (default: ``False``); whether to apply a
|
|
2622
|
+
linear transformation making the vertex figures isometric
|
|
2623
|
+
|
|
2624
|
+
- ``backend`` -- backend to use to create the polytope; (default:
|
|
2625
|
+
``None``)
|
|
2626
|
+
|
|
2627
|
+
EXAMPLES::
|
|
2628
|
+
|
|
2629
|
+
sage: perm_a3 = polytopes.generalized_permutahedron(['A',3]); perm_a3 # needs sage.combinat
|
|
2630
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices
|
|
2631
|
+
|
|
2632
|
+
You can put the starting point along the hyperplane of the first
|
|
2633
|
+
generator::
|
|
2634
|
+
|
|
2635
|
+
sage: # needs sage.combinat
|
|
2636
|
+
sage: perm_a3_011 = polytopes.generalized_permutahedron(['A',3], [0,1,1])
|
|
2637
|
+
sage: perm_a3_011
|
|
2638
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
|
|
2639
|
+
sage: perm_a3_110 = polytopes.generalized_permutahedron(['A',3], [1,1,0])
|
|
2640
|
+
sage: perm_a3_110
|
|
2641
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
|
|
2642
|
+
sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_011)
|
|
2643
|
+
True
|
|
2644
|
+
sage: perm_a3_101 = polytopes.generalized_permutahedron(['A',3], [1,0,1])
|
|
2645
|
+
sage: perm_a3_101
|
|
2646
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
|
|
2647
|
+
sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_101)
|
|
2648
|
+
False
|
|
2649
|
+
sage: perm_a3_011.f_vector()
|
|
2650
|
+
(1, 12, 18, 8, 1)
|
|
2651
|
+
sage: perm_a3_101.f_vector()
|
|
2652
|
+
(1, 12, 24, 14, 1)
|
|
2653
|
+
|
|
2654
|
+
The usual output does not necessarily give a polyhedron with isometric
|
|
2655
|
+
vertex figures::
|
|
2656
|
+
|
|
2657
|
+
sage: perm_a2 = polytopes.generalized_permutahedron(['A',2]) # needs sage.combinat
|
|
2658
|
+
sage: perm_a2.vertices() # needs sage.combinat
|
|
2659
|
+
(A vertex at (-1, -1),
|
|
2660
|
+
A vertex at (-1, 0),
|
|
2661
|
+
A vertex at (0, -1),
|
|
2662
|
+
A vertex at (0, 1),
|
|
2663
|
+
A vertex at (1, 0),
|
|
2664
|
+
A vertex at (1, 1))
|
|
2665
|
+
|
|
2666
|
+
It works also with Coxeter types that lead to non-rational coordinates::
|
|
2667
|
+
|
|
2668
|
+
sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]) # long time, needs sage.combinat sage.rings.number_field
|
|
2669
|
+
sage: perm_b3 # long time, needs sage.combinat sage.rings.number_field
|
|
2670
|
+
A 3-dimensional polyhedron in
|
|
2671
|
+
(Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3
|
|
2672
|
+
defined as the convex hull of 48 vertices
|
|
2673
|
+
|
|
2674
|
+
Setting ``regular=True`` applies a linear transformation to get
|
|
2675
|
+
isometric vertex figures and the result is inscribed. This cannot be done using
|
|
2676
|
+
rational coordinates. We first do the computations using floating point
|
|
2677
|
+
approximations (``RDF``)::
|
|
2678
|
+
|
|
2679
|
+
sage: perm_a2_inexact = polytopes.generalized_permutahedron( # needs sage.combinat
|
|
2680
|
+
....: ['A',2], exact=False)
|
|
2681
|
+
sage: sorted(perm_a2_inexact.vertices()) # needs sage.combinat
|
|
2682
|
+
[A vertex at (-1.0, -1.0),
|
|
2683
|
+
A vertex at (-1.0, 0.0),
|
|
2684
|
+
A vertex at (0.0, -1.0),
|
|
2685
|
+
A vertex at (0.0, 1.0),
|
|
2686
|
+
A vertex at (1.0, 0.0),
|
|
2687
|
+
A vertex at (1.0, 1.0)]
|
|
2688
|
+
|
|
2689
|
+
sage: perm_a2_inexact_reg = polytopes.generalized_permutahedron( # needs sage.combinat
|
|
2690
|
+
....: ['A',2], exact=False, regular=True)
|
|
2691
|
+
sage: sorted(perm_a2_inexact_reg.vertices()) # needs sage.combinat
|
|
2692
|
+
[A vertex at (-1.0, 0.0),
|
|
2693
|
+
A vertex at (-0.5, -0.8660254038),
|
|
2694
|
+
A vertex at (-0.5, 0.8660254038),
|
|
2695
|
+
A vertex at (0.5, -0.8660254038),
|
|
2696
|
+
A vertex at (0.5, 0.8660254038),
|
|
2697
|
+
A vertex at (1.0, 0.0)]
|
|
2698
|
+
|
|
2699
|
+
We can do the same computation using exact arithmetic with the field ``AA``::
|
|
2700
|
+
|
|
2701
|
+
sage: perm_a2_reg = polytopes.generalized_permutahedron( # needs sage.combinat sage.rings.number_field
|
|
2702
|
+
....: ['A',2], regular=True)
|
|
2703
|
+
sage: V = sorted(perm_a2_reg.vertices()); V # random # needs sage.combinat sage.rings.number_field
|
|
2704
|
+
[A vertex at (-1, 0),
|
|
2705
|
+
A vertex at (-1/2, -0.866025403784439?),
|
|
2706
|
+
A vertex at (-1/2, 0.866025403784439?),
|
|
2707
|
+
A vertex at (1/2, -0.866025403784439?),
|
|
2708
|
+
A vertex at (1/2, 0.866025403784439?),
|
|
2709
|
+
A vertex at (1.000000000000000?, 0.?e-18)]
|
|
2710
|
+
|
|
2711
|
+
Even though the numbers look like floating point approximations, the computation is
|
|
2712
|
+
actually exact. We can clean up the display a bit using ``exactify``::
|
|
2713
|
+
|
|
2714
|
+
sage: for v in V: # needs sage.combinat sage.rings.number_field
|
|
2715
|
+
....: for x in v:
|
|
2716
|
+
....: x.exactify()
|
|
2717
|
+
sage: V # needs sage.combinat sage.rings.number_field
|
|
2718
|
+
[A vertex at (-1, 0),
|
|
2719
|
+
A vertex at (-1/2, -0.866025403784439?),
|
|
2720
|
+
A vertex at (-1/2, 0.866025403784439?),
|
|
2721
|
+
A vertex at (1/2, -0.866025403784439?),
|
|
2722
|
+
A vertex at (1/2, 0.866025403784439?),
|
|
2723
|
+
A vertex at (1, 0)]
|
|
2724
|
+
sage: perm_a2_reg.is_inscribed() # needs sage.combinat sage.rings.number_field
|
|
2725
|
+
True
|
|
2726
|
+
|
|
2727
|
+
Larger examples take longer::
|
|
2728
|
+
|
|
2729
|
+
sage: # needs sage.combinat sage.rings.number_field
|
|
2730
|
+
sage: perm_a3_reg = polytopes.generalized_permutahedron( # long time
|
|
2731
|
+
....: ['A',3], regular=True); perm_a3_reg
|
|
2732
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
|
|
2733
|
+
sage: perm_a3_reg.is_inscribed() # long time
|
|
2734
|
+
True
|
|
2735
|
+
sage: perm_b3_reg = polytopes.generalized_permutahedron( # long time (12sec on 64 bits), not tested
|
|
2736
|
+
....: ['B',3], regular=True); perm_b3_reg
|
|
2737
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
|
|
2738
|
+
|
|
2739
|
+
It is faster with the backend ``'number_field'``, which internally uses an embedded
|
|
2740
|
+
number field instead of doing the computations directly with the base ring (``AA``)::
|
|
2741
|
+
|
|
2742
|
+
sage: # needs sage.combinat sage.rings.number_field
|
|
2743
|
+
sage: perm_a3_reg_nf = polytopes.generalized_permutahedron(
|
|
2744
|
+
....: ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf
|
|
2745
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
|
|
2746
|
+
sage: perm_a3_reg_nf.is_inscribed()
|
|
2747
|
+
True
|
|
2748
|
+
sage: perm_b3_reg_nf = polytopes.generalized_permutahedron( # long time
|
|
2749
|
+
....: ['B',3], regular=True, backend='number_field'); perm_b3_reg_nf
|
|
2750
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
|
|
2751
|
+
|
|
2752
|
+
It is even faster with the backend ``'normaliz'``::
|
|
2753
|
+
|
|
2754
|
+
sage: # optional - pynormaliz, needs sage.combinat sage.rings.number_field
|
|
2755
|
+
sage: perm_a3_reg_norm = polytopes.generalized_permutahedron(
|
|
2756
|
+
....: ['A',3], regular=True, backend='normaliz'); perm_a3_reg_norm
|
|
2757
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
|
|
2758
|
+
sage: perm_a3_reg_norm.is_inscribed()
|
|
2759
|
+
True
|
|
2760
|
+
sage: perm_b3_reg_norm = polytopes.generalized_permutahedron(
|
|
2761
|
+
....: ['B',3], regular=True, backend='normaliz'); perm_b3_reg_norm
|
|
2762
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
|
|
2763
|
+
|
|
2764
|
+
The speedups from using backend ``'normaliz'`` allow us to go even further::
|
|
2765
|
+
|
|
2766
|
+
sage: # optional - pynormaliz, needs sage.combinat sage.rings.number_field
|
|
2767
|
+
sage: perm_h3 = polytopes.generalized_permutahedron(
|
|
2768
|
+
....: ['H',3], backend='normaliz'); perm_h3
|
|
2769
|
+
A 3-dimensional polyhedron in
|
|
2770
|
+
(Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3
|
|
2771
|
+
defined as the convex hull of 120 vertices
|
|
2772
|
+
sage: perm_f4 = polytopes.generalized_permutahedron( # long time
|
|
2773
|
+
....: ['F',4], backend='normaliz'); perm_f4
|
|
2774
|
+
A 4-dimensional polyhedron
|
|
2775
|
+
in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4
|
|
2776
|
+
defined as the convex hull of 1152 vertices
|
|
2777
|
+
|
|
2778
|
+
.. SEEALSO::
|
|
2779
|
+
|
|
2780
|
+
* :meth:`~sage.combinat.root_system.reflection_group_real.permutahedron`
|
|
2781
|
+
* :meth:`~sage.categories.finite_coxeter_groups.permutahedron`
|
|
2782
|
+
|
|
2783
|
+
TESTS::
|
|
2784
|
+
|
|
2785
|
+
sage: TestSuite(perm_h3).run() # optional - pynormaliz # needs sage.combinat sage.rings.number_field
|
|
2786
|
+
"""
|
|
2787
|
+
from sage.combinat.root_system.coxeter_group import CoxeterGroup
|
|
2788
|
+
try:
|
|
2789
|
+
W = CoxeterGroup(coxeter_type)
|
|
2790
|
+
except (TypeError, ValueError):
|
|
2791
|
+
raise ValueError("cannot build a Coxeter group from {}".format(coxeter_type))
|
|
2792
|
+
n = W.one().canonical_matrix().rank()
|
|
2793
|
+
weights = W.fundamental_weights()
|
|
2794
|
+
if point is None:
|
|
2795
|
+
point = [ZZ.one()] * n
|
|
2796
|
+
apex = sum(point[i-1] * weights[i] for i in weights.keys())
|
|
2797
|
+
# Try to rationalize the starting point
|
|
2798
|
+
non_zero_index = list(apex).index([x for x in apex if x != 0][0])
|
|
2799
|
+
apex = (QQ(1)/apex[non_zero_index]) * apex
|
|
2800
|
+
apex.set_immutable()
|
|
2801
|
+
vertices = set()
|
|
2802
|
+
# This does not work well with UCF, so we set it to None:
|
|
2803
|
+
# br = apex.base_ring()
|
|
2804
|
+
br = None
|
|
2805
|
+
for w in W:
|
|
2806
|
+
# The apex is considered in the space on which it acts and not in
|
|
2807
|
+
# the weight space.
|
|
2808
|
+
new_point = w * apex
|
|
2809
|
+
new_point.set_immutable()
|
|
2810
|
+
vertices.add(new_point)
|
|
2811
|
+
if regular:
|
|
2812
|
+
from sage.rings.qqbar import AA
|
|
2813
|
+
from sage.matrix.constructor import matrix
|
|
2814
|
+
from sage.modules.free_module_element import vector
|
|
2815
|
+
# This transformation fixes the first root and adjust the other
|
|
2816
|
+
# roots to have the correct angles
|
|
2817
|
+
bf = W.bilinear_form()
|
|
2818
|
+
transf_col = [[1] + [0]*(n-1)]
|
|
2819
|
+
for i in range(1, n):
|
|
2820
|
+
new_col = [0]*i + [1] + [0]*(n-i-1)
|
|
2821
|
+
transf_col += [new_col]
|
|
2822
|
+
m = matrix(AA, transf_col)
|
|
2823
|
+
col = bf.column(i)
|
|
2824
|
+
rhs = vector(AA, list(col[:i+1]))
|
|
2825
|
+
adjusted_col = m.solve_right(rhs)
|
|
2826
|
+
# Then scales the images so that the polytope is inscribed
|
|
2827
|
+
c = 1 - sum(adjusted_col[j]**2 for j in range(n) if j != i)
|
|
2828
|
+
c = c.sqrt()
|
|
2829
|
+
adjusted_col[i] = c
|
|
2830
|
+
transf_col[-1] = adjusted_col
|
|
2831
|
+
# TODO: Make this matrix into the cyclotomics, the value of c is an
|
|
2832
|
+
# algebraic number not anymore in the cyclotomic field.
|
|
2833
|
+
transf = matrix(transf_col).transpose()
|
|
2834
|
+
vertices = [transf * v.change_ring(AA) for v in vertices]
|
|
2835
|
+
br = AA
|
|
2836
|
+
if not exact:
|
|
2837
|
+
from sage.rings.real_double import RDF
|
|
2838
|
+
vertices = [v.change_ring(RDF) for v in vertices]
|
|
2839
|
+
br = RDF
|
|
2840
|
+
return Polyhedron(vertices=vertices, backend=backend, base_ring=br)
|
|
2841
|
+
|
|
2842
|
+
def omnitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
|
|
2843
|
+
"""
|
|
2844
|
+
Return the omnitruncated 120-cell.
|
|
2845
|
+
|
|
2846
|
+
The omnitruncated 120-cell is a 4-dimensional 4-uniform polytope in the
|
|
2847
|
+
`H_4` family. It has 14400 vertices. For more information see
|
|
2848
|
+
:wikipedia:`Omnitruncated 120-cell`.
|
|
2849
|
+
|
|
2850
|
+
.. WARNING::
|
|
2851
|
+
|
|
2852
|
+
The coordinates are exact by default. The computation with inexact
|
|
2853
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2854
|
+
inconsistency error, and thus cannot be computed.
|
|
2855
|
+
|
|
2856
|
+
INPUT:
|
|
2857
|
+
|
|
2858
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2859
|
+
coordinates instead of floating point approximations
|
|
2860
|
+
|
|
2861
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2862
|
+
|
|
2863
|
+
EXAMPLES::
|
|
2864
|
+
|
|
2865
|
+
sage: polytopes.omnitruncated_one_hundred_twenty_cell(backend='normaliz') # not tested - very long time ~10min
|
|
2866
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 14400 vertices
|
|
2867
|
+
"""
|
|
2868
|
+
if not exact:
|
|
2869
|
+
# cdd finds a numerical inconsistency.
|
|
2870
|
+
raise NotImplementedError("cannot compute the convex hull using floating points")
|
|
2871
|
+
return self.generalized_permutahedron(['H', 4], exact=exact, backend=backend, regular=True)
|
|
2872
|
+
|
|
2873
|
+
omnitruncated_six_hundred_cell = omnitruncated_one_hundred_twenty_cell
|
|
2874
|
+
|
|
2875
|
+
def runcitruncated_one_hundred_twenty_cell(self, exact=False, backend=None):
|
|
2876
|
+
"""
|
|
2877
|
+
Return the runcitruncated 120-cell.
|
|
2878
|
+
|
|
2879
|
+
The runcitruncated 120-cell is a 4-dimensional 4-uniform polytope in
|
|
2880
|
+
the `H_4` family. It has 7200 vertices. For more information see
|
|
2881
|
+
:wikipedia:`Runcitruncated 120-cell`.
|
|
2882
|
+
|
|
2883
|
+
.. WARNING::
|
|
2884
|
+
|
|
2885
|
+
The coordinates are inexact by default. The computation with
|
|
2886
|
+
inexact coordinates (using the backend ``'cdd'``) issues a
|
|
2887
|
+
UserWarning on inconsistencies.
|
|
2888
|
+
|
|
2889
|
+
INPUT:
|
|
2890
|
+
|
|
2891
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
2892
|
+
coordinates instead of floating point approximations
|
|
2893
|
+
|
|
2894
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2895
|
+
|
|
2896
|
+
EXAMPLES::
|
|
2897
|
+
|
|
2898
|
+
sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=False) # not tested - very long time
|
|
2899
|
+
doctest:warning
|
|
2900
|
+
...
|
|
2901
|
+
UserWarning: This polyhedron data is numerically complicated; cdd
|
|
2902
|
+
could not convert between the inexact V and H representation
|
|
2903
|
+
without loss of data. The resulting object might show
|
|
2904
|
+
inconsistencies.
|
|
2905
|
+
|
|
2906
|
+
It is possible to use the backend ``'normaliz'`` to get an exact
|
|
2907
|
+
representation::
|
|
2908
|
+
|
|
2909
|
+
sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=True, # not tested - very long time
|
|
2910
|
+
....: backend='normaliz')
|
|
2911
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
|
|
2912
|
+
"""
|
|
2913
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 1], exact=exact, backend=backend, regular=True)
|
|
2914
|
+
|
|
2915
|
+
def cantitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
|
|
2916
|
+
"""
|
|
2917
|
+
Return the cantitruncated 120-cell.
|
|
2918
|
+
|
|
2919
|
+
The cantitruncated 120-cell is a 4-dimensional 4-uniform polytope in
|
|
2920
|
+
the `H_4` family. It has 7200 vertices. For more information see
|
|
2921
|
+
:wikipedia:`Cantitruncated 120-cell`.
|
|
2922
|
+
|
|
2923
|
+
.. WARNING::
|
|
2924
|
+
|
|
2925
|
+
The coordinates are exact by default. The computation with inexact
|
|
2926
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2927
|
+
inconsistency error, and thus cannot be computed.
|
|
2928
|
+
|
|
2929
|
+
INPUT:
|
|
2930
|
+
|
|
2931
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2932
|
+
coordinates instead of floating point approximations
|
|
2933
|
+
|
|
2934
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2935
|
+
|
|
2936
|
+
EXAMPLES::
|
|
2937
|
+
|
|
2938
|
+
sage: polytopes.cantitruncated_one_hundred_twenty_cell(exact=True, backend='normaliz') # not tested - very long time
|
|
2939
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
|
|
2940
|
+
"""
|
|
2941
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 1], exact=exact, backend=backend, regular=True)
|
|
2942
|
+
|
|
2943
|
+
def runcinated_one_hundred_twenty_cell(self, exact=False, backend=None):
|
|
2944
|
+
"""
|
|
2945
|
+
Return the runcinated 120-cell.
|
|
2946
|
+
|
|
2947
|
+
The runcinated 120-cell is a 4-dimensional 4-uniform polytope in the
|
|
2948
|
+
`H_4` family. It has 2400 vertices. For more information see
|
|
2949
|
+
:wikipedia:`Runcinated 120-cell`.
|
|
2950
|
+
|
|
2951
|
+
.. WARNING::
|
|
2952
|
+
|
|
2953
|
+
The coordinates are inexact by default. The computation with
|
|
2954
|
+
inexact coordinates (using the backend ``'cdd'``) issues a
|
|
2955
|
+
UserWarning on inconsistencies.
|
|
2956
|
+
|
|
2957
|
+
INPUT:
|
|
2958
|
+
|
|
2959
|
+
- ``exact`` -- boolean (default: ``False``); if ``True`` use exact
|
|
2960
|
+
coordinates instead of floating point approximations
|
|
2961
|
+
|
|
2962
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
2963
|
+
|
|
2964
|
+
EXAMPLES::
|
|
2965
|
+
|
|
2966
|
+
sage: polytopes.runcinated_one_hundred_twenty_cell(exact=False) # not tested - very long time
|
|
2967
|
+
doctest:warning ... UserWarning: This polyhedron data is
|
|
2968
|
+
numerically complicated; cdd could not convert between the inexact
|
|
2969
|
+
V and H representation without loss of data. The resulting object
|
|
2970
|
+
might show inconsistencies.
|
|
2971
|
+
A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 2400 vertices
|
|
2972
|
+
|
|
2973
|
+
It is possible to use the backend ``'normaliz'`` to get an exact
|
|
2974
|
+
representation::
|
|
2975
|
+
|
|
2976
|
+
sage: polytopes.runcinated_one_hundred_twenty_cell(exact=True, # not tested - very long time
|
|
2977
|
+
....: backend='normaliz')
|
|
2978
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
|
|
2979
|
+
"""
|
|
2980
|
+
return self.generalized_permutahedron(['H', 4], point=[1, 0, 0, 1], exact=exact, backend=backend, regular=True)
|
|
2981
|
+
|
|
2982
|
+
def cantellated_one_hundred_twenty_cell(self, exact=True, backend=None):
|
|
2983
|
+
"""
|
|
2984
|
+
Return the cantellated 120-cell.
|
|
2985
|
+
|
|
2986
|
+
The cantellated 120-cell is a 4-dimensional 4-uniform polytope in the
|
|
2987
|
+
`H_4` family. It has 3600 vertices. For more information see
|
|
2988
|
+
:wikipedia:`Cantellated 120-cell`.
|
|
2989
|
+
|
|
2990
|
+
.. WARNING::
|
|
2991
|
+
|
|
2992
|
+
The coordinates are exact by default. The computation with inexact
|
|
2993
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
2994
|
+
inconsistency error, and thus cannot be computed.
|
|
2995
|
+
|
|
2996
|
+
INPUT:
|
|
2997
|
+
|
|
2998
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
2999
|
+
coordinates instead of floating point approximations
|
|
3000
|
+
|
|
3001
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3002
|
+
|
|
3003
|
+
EXAMPLES::
|
|
3004
|
+
|
|
3005
|
+
sage: polytopes.cantellated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
|
|
3006
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
|
|
3007
|
+
"""
|
|
3008
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 1], exact=exact, backend=backend, regular=True)
|
|
3009
|
+
|
|
3010
|
+
def truncated_one_hundred_twenty_cell(self, exact=True, backend=None):
|
|
3011
|
+
"""
|
|
3012
|
+
Return the truncated 120-cell.
|
|
3013
|
+
|
|
3014
|
+
The truncated 120-cell is a 4-dimensional 4-uniform polytope in the
|
|
3015
|
+
`H_4` family. It has 2400 vertices. For more information see
|
|
3016
|
+
:wikipedia:`Truncated 120-cell`.
|
|
3017
|
+
|
|
3018
|
+
.. WARNING::
|
|
3019
|
+
|
|
3020
|
+
The coordinates are exact by default. The computation with inexact
|
|
3021
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
3022
|
+
inconsistency error, and thus cannot be computed.
|
|
3023
|
+
|
|
3024
|
+
INPUT:
|
|
3025
|
+
|
|
3026
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
3027
|
+
coordinates instead of floating point approximations
|
|
3028
|
+
|
|
3029
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3030
|
+
|
|
3031
|
+
EXAMPLES::
|
|
3032
|
+
|
|
3033
|
+
sage: polytopes.truncated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
|
|
3034
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
|
|
3035
|
+
"""
|
|
3036
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 1], exact=exact, backend=backend, regular=True)
|
|
3037
|
+
|
|
3038
|
+
def rectified_one_hundred_twenty_cell(self, exact=True, backend=None):
|
|
3039
|
+
"""
|
|
3040
|
+
Return the rectified 120-cell.
|
|
3041
|
+
|
|
3042
|
+
The rectified 120-cell is a 4-dimensional 4-uniform polytope in the
|
|
3043
|
+
`H_4` family. It has 1200 vertices. For more information see
|
|
3044
|
+
:wikipedia:`Rectified 120-cell`.
|
|
3045
|
+
|
|
3046
|
+
.. WARNING::
|
|
3047
|
+
|
|
3048
|
+
The coordinates are exact by default. The computation with inexact
|
|
3049
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
3050
|
+
inconsistency error, and thus cannot be computed.
|
|
3051
|
+
|
|
3052
|
+
INPUT:
|
|
3053
|
+
|
|
3054
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
3055
|
+
coordinates instead of floating point approximations
|
|
3056
|
+
|
|
3057
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3058
|
+
|
|
3059
|
+
EXAMPLES::
|
|
3060
|
+
|
|
3061
|
+
sage: polytopes.rectified_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
|
|
3062
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1200 vertices
|
|
3063
|
+
"""
|
|
3064
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 0], exact=exact, backend=backend, regular=True)
|
|
3065
|
+
|
|
3066
|
+
def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxeter'):
|
|
3067
|
+
"""
|
|
3068
|
+
Return the 120-cell.
|
|
3069
|
+
|
|
3070
|
+
The 120-cell is a 4-dimensional 4-uniform polytope in the `H_4` family.
|
|
3071
|
+
It has 600 vertices and 120 facets. For more information see
|
|
3072
|
+
:wikipedia:`120-cell`.
|
|
3073
|
+
|
|
3074
|
+
.. WARNING::
|
|
3075
|
+
|
|
3076
|
+
The coordinates are exact by default. The computation with inexact
|
|
3077
|
+
coordinates (using the backend ``'cdd'``) returns a numerical
|
|
3078
|
+
inconsistency error, and thus cannot be computed.
|
|
3079
|
+
|
|
3080
|
+
INPUT:
|
|
3081
|
+
|
|
3082
|
+
- ``exact`` -- boolean (default: ``True``); if ``True`` use exact
|
|
3083
|
+
coordinates instead of floating point approximations
|
|
3084
|
+
|
|
3085
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3086
|
+
|
|
3087
|
+
- ``construction`` -- string (default: ``'coxeter'``); the construction to use.
|
|
3088
|
+
The other possibility is 'as_permutahedron'.
|
|
3089
|
+
|
|
3090
|
+
EXAMPLES:
|
|
3091
|
+
|
|
3092
|
+
The classical construction given by Coxeter in [Cox1969]_ is given by::
|
|
3093
|
+
|
|
3094
|
+
sage: polytopes.one_hundred_twenty_cell() # not tested, long time (~15s)
|
|
3095
|
+
A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
|
|
3096
|
+
polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
|
|
3097
|
+
the convex hull of 600 vertices
|
|
3098
|
+
|
|
3099
|
+
The ``'normaliz'`` is faster::
|
|
3100
|
+
|
|
3101
|
+
sage: P = polytopes.one_hundred_twenty_cell(backend='normaliz'); P # optional - pynormaliz
|
|
3102
|
+
A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
|
|
3103
|
+
polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
|
|
3104
|
+
the convex hull of 600 vertices
|
|
3105
|
+
|
|
3106
|
+
It is also possible to realize it using the generalized permutahedron
|
|
3107
|
+
of type `H_4`::
|
|
3108
|
+
|
|
3109
|
+
sage: polytopes.one_hundred_twenty_cell(backend='normaliz', # not tested - long time
|
|
3110
|
+
....: construction='as_permutahedron')
|
|
3111
|
+
A 4-dimensional polyhedron in AA^4 defined as the convex hull of 600 vertices
|
|
3112
|
+
|
|
3113
|
+
TESTS::
|
|
3114
|
+
|
|
3115
|
+
sage: TestSuite(P).run() # long time, optional - pynormaliz
|
|
3116
|
+
"""
|
|
3117
|
+
if construction == 'coxeter':
|
|
3118
|
+
if not exact:
|
|
3119
|
+
raise ValueError("The 'cdd' backend produces numerical inconsistencies, use 'exact=True'.")
|
|
3120
|
+
from sage.rings.number_field.number_field import QuadraticField
|
|
3121
|
+
base_ring = QuadraticField(5, 'sqrt5')
|
|
3122
|
+
sqrt5 = base_ring.gen()
|
|
3123
|
+
phi = (1 + sqrt5) / 2
|
|
3124
|
+
phi_inv = base_ring.one() / phi
|
|
3125
|
+
|
|
3126
|
+
# The 24 permutations of [0,0,±2,±2] (the ± are independent)
|
|
3127
|
+
verts = Permutations([0,0,2,2]).list() + Permutations([0,0,-2,-2]).list() + Permutations([0,0,2,-2]).list()
|
|
3128
|
+
|
|
3129
|
+
# The 64 permutations of the following vectors:
|
|
3130
|
+
# [±1,±1,±1,±sqrt(5)]
|
|
3131
|
+
# [±1/phi^2,±phi,±phi,±phi]
|
|
3132
|
+
# [±1/phi,±1/phi,±1/phi,±phi^2]
|
|
3133
|
+
from sage.categories.cartesian_product import cartesian_product
|
|
3134
|
+
full_perm_vectors = [[[1,-1],[1,-1],[1,-1],[-sqrt5,sqrt5]],
|
|
3135
|
+
[[phi_inv**2,-phi_inv**2],[phi,-phi],[phi,-phi],[-phi,phi]],
|
|
3136
|
+
[[phi_inv,-phi_inv],[phi_inv,-phi_inv],[phi_inv,-phi_inv],[-(phi**2),phi**2]]]
|
|
3137
|
+
for vect in full_perm_vectors:
|
|
3138
|
+
cp = cartesian_product(vect)
|
|
3139
|
+
# The group action creates duplicates, so we reduce it:
|
|
3140
|
+
verts += list({tuple(p) for c in cp
|
|
3141
|
+
for p in Permutations(list(c))})
|
|
3142
|
+
|
|
3143
|
+
# The 96 even permutations of [0,±1/phi^2,±1,±phi^2]
|
|
3144
|
+
# The 96 even permutations of [0,±1/phi,±phi,±sqrt(5)]
|
|
3145
|
+
# The 192 even permutations of [±1/phi,±1,±phi,±2]
|
|
3146
|
+
even_perm_vectors = [[[0],[phi_inv**2,-phi_inv**2],[1,-1],[-(phi**2),phi**2]],
|
|
3147
|
+
[[0],[phi_inv,-phi_inv],[phi,-phi],[-sqrt5,sqrt5]],
|
|
3148
|
+
[[phi_inv,-phi_inv],[1,-1],[phi,-phi],[-2,2]]]
|
|
3149
|
+
even_perm = AlternatingGroup(4)
|
|
3150
|
+
for vect in even_perm_vectors:
|
|
3151
|
+
cp = cartesian_product(vect)
|
|
3152
|
+
verts += [p(tuple(c)) for p in even_perm for c in cp]
|
|
3153
|
+
|
|
3154
|
+
return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
|
|
3155
|
+
|
|
3156
|
+
elif construction == 'as_permutahedron':
|
|
3157
|
+
return self.generalized_permutahedron(['H', 4], point=[0, 0, 0, 1], exact=exact, backend=backend, regular=True)
|
|
3158
|
+
else:
|
|
3159
|
+
raise ValueError("construction (={}) must be either 'coxeter' or 'as_permutahedron' ".format(construction))
|
|
3160
|
+
|
|
3161
|
+
def hypercube(self, dim, intervals=None, backend=None):
|
|
3162
|
+
r"""
|
|
3163
|
+
Return a hypercube of the given dimension.
|
|
3164
|
+
|
|
3165
|
+
The ``dim``-dimensional hypercube is by default the convex hull of the
|
|
3166
|
+
`2^{\text{dim}}` `\pm 1` vectors of length ``dim``. Alternatively,
|
|
3167
|
+
it is the product of ``dim`` line segments given in the ``intervals``.
|
|
3168
|
+
For more information see the wikipedia article :wikipedia:`Hypercube`.
|
|
3169
|
+
|
|
3170
|
+
INPUT:
|
|
3171
|
+
|
|
3172
|
+
- ``dim`` -- integer; the dimension of the hypercube
|
|
3173
|
+
|
|
3174
|
+
- ``intervals`` -- (default: ``None``) it takes the following
|
|
3175
|
+
possible inputs:
|
|
3176
|
+
|
|
3177
|
+
- If ``None`` (the default), it returns the `\pm 1`-cube of
|
|
3178
|
+
dimension ``dim``.
|
|
3179
|
+
|
|
3180
|
+
- ``'zero_one'`` -- string; return the `0/1`-cube
|
|
3181
|
+
|
|
3182
|
+
- a list of length ``dim``. Its elements are pairs of
|
|
3183
|
+
numbers `(a,b)` with `a < b`. The cube will be the product of
|
|
3184
|
+
these intervals.
|
|
3185
|
+
|
|
3186
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3187
|
+
|
|
3188
|
+
EXAMPLES:
|
|
3189
|
+
|
|
3190
|
+
Create the `\pm 1`-hypercube of dimension 4::
|
|
3191
|
+
|
|
3192
|
+
sage: four_cube = polytopes.hypercube(4)
|
|
3193
|
+
sage: four_cube.is_simple()
|
|
3194
|
+
True
|
|
3195
|
+
sage: four_cube.base_ring()
|
|
3196
|
+
Integer Ring
|
|
3197
|
+
sage: four_cube.volume()
|
|
3198
|
+
16
|
|
3199
|
+
sage: four_cube.ehrhart_polynomial() # optional - latte_int
|
|
3200
|
+
16*t^4 + 32*t^3 + 24*t^2 + 8*t + 1
|
|
3201
|
+
|
|
3202
|
+
Return the `0/1`-hypercube of dimension 4::
|
|
3203
|
+
|
|
3204
|
+
sage: z_cube = polytopes.hypercube(4, intervals='zero_one')
|
|
3205
|
+
sage: z_cube.vertices()[0]
|
|
3206
|
+
A vertex at (1, 0, 1, 1)
|
|
3207
|
+
sage: z_cube.is_simple()
|
|
3208
|
+
True
|
|
3209
|
+
sage: z_cube.base_ring()
|
|
3210
|
+
Integer Ring
|
|
3211
|
+
sage: z_cube.volume()
|
|
3212
|
+
1
|
|
3213
|
+
sage: z_cube.ehrhart_polynomial() # optional - latte_int
|
|
3214
|
+
t^4 + 4*t^3 + 6*t^2 + 4*t + 1
|
|
3215
|
+
|
|
3216
|
+
Return the 4-dimensional combinatorial cube that is the product of
|
|
3217
|
+
[0,3]^4::
|
|
3218
|
+
|
|
3219
|
+
sage: t_cube = polytopes.hypercube(4, intervals=[[0,3]]*4)
|
|
3220
|
+
|
|
3221
|
+
Checking that t_cube is three times the previous `0/1`-cube::
|
|
3222
|
+
|
|
3223
|
+
sage: t_cube == 3 * z_cube
|
|
3224
|
+
True
|
|
3225
|
+
|
|
3226
|
+
TESTS::
|
|
3227
|
+
|
|
3228
|
+
sage: fc = polytopes.hypercube(4,backend='normaliz') # optional - pynormaliz
|
|
3229
|
+
sage: TestSuite(fc).run() # optional - pynormaliz
|
|
3230
|
+
|
|
3231
|
+
::
|
|
3232
|
+
|
|
3233
|
+
sage: ls = [randint(-100,100) for _ in range(4)]
|
|
3234
|
+
sage: intervals = [[x, x+randint(1,50)] for x in ls]
|
|
3235
|
+
sage: P = polytopes.hypercube(4, intervals, backend='field')
|
|
3236
|
+
sage: TestSuite(P).run()
|
|
3237
|
+
|
|
3238
|
+
Check that :issue:`29904` is fixed::
|
|
3239
|
+
|
|
3240
|
+
sage: intervals = [[-2,2]]
|
|
3241
|
+
sage: P = polytopes.hypercube(1, intervals, 'field')
|
|
3242
|
+
sage: TestSuite(P).run()
|
|
3243
|
+
|
|
3244
|
+
If the dimension ``dim`` is not equal to the length of intervals, an
|
|
3245
|
+
error is raised::
|
|
3246
|
+
|
|
3247
|
+
sage: u_cube = polytopes.hypercube(2, intervals=[[0,1],[0,2],[0,3]])
|
|
3248
|
+
Traceback (most recent call last):
|
|
3249
|
+
...
|
|
3250
|
+
ValueError: the dimension of the hypercube must match the number of intervals
|
|
3251
|
+
|
|
3252
|
+
The intervals must be pairs `(a, b)` with `a < b`::
|
|
3253
|
+
|
|
3254
|
+
sage: w_cube = polytopes.hypercube(3, intervals=[[0,1],[3,2],[0,3]])
|
|
3255
|
+
Traceback (most recent call last):
|
|
3256
|
+
...
|
|
3257
|
+
ValueError: each interval must be a pair `(a, b)` with `a < b`
|
|
3258
|
+
|
|
3259
|
+
If a string besides 'zero_one' is passed to ``intervals``, return an
|
|
3260
|
+
error::
|
|
3261
|
+
|
|
3262
|
+
sage: v_cube = polytopes.hypercube(3, intervals='a_string')
|
|
3263
|
+
Traceback (most recent call last):
|
|
3264
|
+
...
|
|
3265
|
+
ValueError: the only allowed string is 'zero_one'
|
|
3266
|
+
|
|
3267
|
+
Check that we set up the hypercube correctly::
|
|
3268
|
+
|
|
3269
|
+
sage: ls = [randint(-100,100) for _ in range(4)]
|
|
3270
|
+
sage: intervals = [[x, x+randint(1,50)] for x in ls]
|
|
3271
|
+
sage: P = polytopes.hypercube(4, intervals, backend='field')
|
|
3272
|
+
sage: P1 = polytopes.hypercube(4, intervals, backend='ppl') # needs pplpy
|
|
3273
|
+
sage: assert P == P1 # needs pplpy
|
|
3274
|
+
|
|
3275
|
+
Check that coercion for input intervals is handled correctly::
|
|
3276
|
+
|
|
3277
|
+
sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1]])
|
|
3278
|
+
sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]]) # needs cddexec
|
|
3279
|
+
sage: P = polytopes.hypercube(2, [[1/2, 2], [0, AA(2).sqrt()]]) # needs sage.rings.number_field
|
|
3280
|
+
sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]], backend='ppl') # needs pplpy
|
|
3281
|
+
Traceback (most recent call last):
|
|
3282
|
+
...
|
|
3283
|
+
ValueError: specified backend ppl cannot handle the intervals
|
|
3284
|
+
"""
|
|
3285
|
+
parent = Polyhedra(ZZ, dim, backend=backend)
|
|
3286
|
+
convert = False
|
|
3287
|
+
|
|
3288
|
+
# If the intervals are (a_1,b_1), ..., (a_dim, b_dim),
|
|
3289
|
+
# then the inequalities correspond to
|
|
3290
|
+
# b_1,b_2,...,b_dim, a_1,a_2,...,a_dim
|
|
3291
|
+
# in that order.
|
|
3292
|
+
|
|
3293
|
+
if intervals is None:
|
|
3294
|
+
cp = itertools.product((-1, 1), repeat=dim)
|
|
3295
|
+
|
|
3296
|
+
# An inequality -x_i + 1 >= 0 for i < dim
|
|
3297
|
+
# resp. x_{dim-i} + 1 >= 0 for i >= dim
|
|
3298
|
+
ieq_b = lambda i: 1
|
|
3299
|
+
|
|
3300
|
+
elif isinstance(intervals, str):
|
|
3301
|
+
if intervals == 'zero_one':
|
|
3302
|
+
cp = itertools.product((0,1), repeat=dim)
|
|
3303
|
+
|
|
3304
|
+
# An inequality -x_i + 1 >= 0 for i < dim
|
|
3305
|
+
# resp. x_{dim-i} + 0 >= 0 for i >= dim
|
|
3306
|
+
ieq_b = lambda i: 1 if i < dim else 0
|
|
3307
|
+
else:
|
|
3308
|
+
raise ValueError("the only allowed string is 'zero_one'")
|
|
3309
|
+
elif len(intervals) == dim:
|
|
3310
|
+
if not all(a < b for a,b in intervals):
|
|
3311
|
+
raise ValueError("each interval must be a pair `(a, b)` with `a < b`")
|
|
3312
|
+
parent = parent.base_extend(sum(a + b for a,b in intervals))
|
|
3313
|
+
if parent.base_ring() not in (ZZ, QQ):
|
|
3314
|
+
convert = True
|
|
3315
|
+
if backend and parent.backend() is not backend:
|
|
3316
|
+
# If the parent changed backends, but a backend was specified,
|
|
3317
|
+
# the specified backend cannot handle the intervals.
|
|
3318
|
+
raise ValueError("specified backend {} cannot handle the intervals".format(backend))
|
|
3319
|
+
|
|
3320
|
+
cp = itertools.product(*intervals)
|
|
3321
|
+
|
|
3322
|
+
# An inequality -x_i + b_i >= 0 for i < dim
|
|
3323
|
+
# resp. x_{dim-i} - a_i >= 0 for i >= dim
|
|
3324
|
+
ieq_b = lambda i: intervals[i][1] if i < dim \
|
|
3325
|
+
else -intervals[i-dim][0]
|
|
3326
|
+
else:
|
|
3327
|
+
raise ValueError("the dimension of the hypercube must match the number of intervals")
|
|
3328
|
+
|
|
3329
|
+
# An inequality -x_i + ieq_b(i) >= 0 for i < dim
|
|
3330
|
+
# resp. x_{dim-i} + ieq_b(i-dim) >= 0 for i >= dim
|
|
3331
|
+
ieq_A = lambda i, pos: -1 if i == pos \
|
|
3332
|
+
else 1 if i == pos + dim \
|
|
3333
|
+
else 0
|
|
3334
|
+
ieqs = (tuple(ieq_b(i) if pos == 0 else ieq_A(i, pos-1)
|
|
3335
|
+
for pos in range(dim+1))
|
|
3336
|
+
for i in range(2*dim))
|
|
3337
|
+
|
|
3338
|
+
return parent([cp, [], []], [ieqs, []], convert=convert, Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep')
|
|
3339
|
+
|
|
3340
|
+
def cube(self, intervals=None, backend=None):
|
|
3341
|
+
r"""
|
|
3342
|
+
Return the cube.
|
|
3343
|
+
|
|
3344
|
+
The cube is the Platonic solid that is obtained as the convex hull of
|
|
3345
|
+
the eight `\pm 1` vectors of length 3 (by default). Alternatively, the
|
|
3346
|
+
cube is the product of three intervals from ``intervals``.
|
|
3347
|
+
|
|
3348
|
+
.. SEEALSO::
|
|
3349
|
+
|
|
3350
|
+
:meth:`hypercube`
|
|
3351
|
+
|
|
3352
|
+
INPUT:
|
|
3353
|
+
|
|
3354
|
+
- ``intervals`` -- list (default=None). It takes the following
|
|
3355
|
+
possible inputs:
|
|
3356
|
+
|
|
3357
|
+
- If the input is ``None`` (the default), returns the convex hull of
|
|
3358
|
+
the eight `\pm 1` vectors of length three.
|
|
3359
|
+
|
|
3360
|
+
- ``'zero_one'`` -- string; return the `0/1`-cube
|
|
3361
|
+
|
|
3362
|
+
- a list of 3 lists of length 2. The cube will be a product of
|
|
3363
|
+
these three intervals.
|
|
3364
|
+
|
|
3365
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3366
|
+
|
|
3367
|
+
OUTPUT: a cube as a polyhedron object
|
|
3368
|
+
|
|
3369
|
+
EXAMPLES:
|
|
3370
|
+
|
|
3371
|
+
Return the `\pm 1`-cube::
|
|
3372
|
+
|
|
3373
|
+
sage: c = polytopes.cube()
|
|
3374
|
+
sage: c
|
|
3375
|
+
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
|
|
3376
|
+
sage: c.f_vector()
|
|
3377
|
+
(1, 8, 12, 6, 1)
|
|
3378
|
+
sage: c.volume()
|
|
3379
|
+
8
|
|
3380
|
+
sage: c.plot() # needs sage.plot
|
|
3381
|
+
Graphics3d Object
|
|
3382
|
+
|
|
3383
|
+
Return the `0/1`-cube::
|
|
3384
|
+
|
|
3385
|
+
sage: cc = polytopes.cube(intervals ='zero_one')
|
|
3386
|
+
sage: cc.vertices_list()
|
|
3387
|
+
[[1, 0, 0],
|
|
3388
|
+
[1, 1, 0],
|
|
3389
|
+
[1, 1, 1],
|
|
3390
|
+
[1, 0, 1],
|
|
3391
|
+
[0, 0, 1],
|
|
3392
|
+
[0, 0, 0],
|
|
3393
|
+
[0, 1, 0],
|
|
3394
|
+
[0, 1, 1]]
|
|
3395
|
+
"""
|
|
3396
|
+
return self.hypercube(3, backend=backend, intervals=intervals)
|
|
3397
|
+
|
|
3398
|
+
def cross_polytope(self, dim, backend=None):
|
|
3399
|
+
r"""
|
|
3400
|
+
Return a cross-polytope in dimension ``dim``.
|
|
3401
|
+
|
|
3402
|
+
A cross-polytope is a higher dimensional generalization of the
|
|
3403
|
+
octahedron. It is the convex hull of the `2d` points `(\pm 1, 0,
|
|
3404
|
+
\ldots, 0)`, `(0, \pm 1, \ldots, 0)`, \ldots, `(0, 0, \ldots, \pm 1)`.
|
|
3405
|
+
See the :wikipedia:`Cross-polytope` for more information.
|
|
3406
|
+
|
|
3407
|
+
INPUT:
|
|
3408
|
+
|
|
3409
|
+
- ``dim`` -- integer; the dimension of the cross-polytope
|
|
3410
|
+
|
|
3411
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3412
|
+
|
|
3413
|
+
EXAMPLES::
|
|
3414
|
+
|
|
3415
|
+
sage: four_cross = polytopes.cross_polytope(4)
|
|
3416
|
+
sage: four_cross.f_vector()
|
|
3417
|
+
(1, 8, 24, 32, 16, 1)
|
|
3418
|
+
sage: four_cross.is_simple()
|
|
3419
|
+
False
|
|
3420
|
+
|
|
3421
|
+
TESTS::
|
|
3422
|
+
|
|
3423
|
+
sage: cp = polytopes.cross_polytope(4, backend='normaliz') # optional - pynormaliz
|
|
3424
|
+
sage: TestSuite(cp).run() # optional - pynormaliz
|
|
3425
|
+
|
|
3426
|
+
::
|
|
3427
|
+
|
|
3428
|
+
sage: P = polytopes.cross_polytope(6, backend='field')
|
|
3429
|
+
sage: TestSuite(P).run() # long time
|
|
3430
|
+
|
|
3431
|
+
Check that double description is set up correctly::
|
|
3432
|
+
|
|
3433
|
+
sage: P = polytopes.cross_polytope(6, backend='ppl')
|
|
3434
|
+
sage: Q = polytopes.cross_polytope(6, backend='ppl')
|
|
3435
|
+
sage: P == Q
|
|
3436
|
+
True
|
|
3437
|
+
"""
|
|
3438
|
+
verts = tuple((ZZ**dim).basis())
|
|
3439
|
+
verts += tuple(-v for v in verts)
|
|
3440
|
+
ieqs = ((1,) + x for x in itertools.product((-1,1), repeat=dim))
|
|
3441
|
+
parent = Polyhedra(ZZ, dim, backend=backend)
|
|
3442
|
+
return parent([verts, [], []], [ieqs, []], Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep')
|
|
3443
|
+
|
|
3444
|
+
def parallelotope(self, generators, backend=None):
|
|
3445
|
+
r"""
|
|
3446
|
+
Return the zonotope, or parallelotope, spanned by the generators.
|
|
3447
|
+
|
|
3448
|
+
The parallelotope is the multi-dimensional generalization of a
|
|
3449
|
+
parallelogram (2 generators) and a parallelepiped (3 generators).
|
|
3450
|
+
|
|
3451
|
+
INPUT:
|
|
3452
|
+
|
|
3453
|
+
- ``generators`` -- list of vectors of same dimension
|
|
3454
|
+
|
|
3455
|
+
- ``backend`` -- the backend to use to create the polytope
|
|
3456
|
+
|
|
3457
|
+
EXAMPLES::
|
|
3458
|
+
|
|
3459
|
+
sage: polytopes.parallelotope([ (1,0), (0,1) ])
|
|
3460
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
|
|
3461
|
+
sage: polytopes.parallelotope([[1,2,3,4], [0,1,0,7], [3,1,0,2], [0,0,1,0]])
|
|
3462
|
+
A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices
|
|
3463
|
+
|
|
3464
|
+
sage: K = QuadraticField(2, 'sqrt2') # needs sage.rings.number_field
|
|
3465
|
+
sage: sqrt2 = K.gen() # needs sage.rings.number_field
|
|
3466
|
+
sage: P = polytopes.parallelotope([(1, sqrt2), (1, -1)]); P # needs sage.rings.number_field
|
|
3467
|
+
A 2-dimensional polyhedron in (Number Field in sqrt2 with defining
|
|
3468
|
+
polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as
|
|
3469
|
+
the convex hull of 4 vertices
|
|
3470
|
+
|
|
3471
|
+
TESTS::
|
|
3472
|
+
|
|
3473
|
+
sage: TestSuite(P).run() # needs sage.rings.number_field
|
|
3474
|
+
"""
|
|
3475
|
+
from sage.modules.free_module_element import vector
|
|
3476
|
+
generators = [vector(v) for v in generators]
|
|
3477
|
+
if not generators:
|
|
3478
|
+
return Polyhedron(backend=backend)
|
|
3479
|
+
|
|
3480
|
+
zero = generators[0] - generators[0]
|
|
3481
|
+
intervals = [Polyhedron([zero, gen], backend=backend) for gen in generators]
|
|
3482
|
+
return sum(intervals)
|
|
3483
|
+
|
|
3484
|
+
zonotope = parallelotope
|
|
3485
|
+
|
|
3486
|
+
# --------------------------------------------------------
|
|
3487
|
+
# imports from other files
|
|
3488
|
+
# --------------------------------------------------------
|
|
3489
|
+
try:
|
|
3490
|
+
associahedron = staticmethod(Associahedron)
|
|
3491
|
+
except ImportError:
|
|
3492
|
+
pass
|
|
3493
|
+
|
|
3494
|
+
try:
|
|
3495
|
+
flow_polytope = staticmethod(DiGraph.flow_polytope)
|
|
3496
|
+
edge_polytope = staticmethod(Graph.edge_polytope)
|
|
3497
|
+
symmetric_edge_polytope = staticmethod(Graph.symmetric_edge_polytope)
|
|
3498
|
+
except ImportError:
|
|
3499
|
+
pass
|
|
3500
|
+
|
|
3501
|
+
|
|
3502
|
+
polytopes = Polytopes()
|