passagemath-polyhedra 10.6.31rc3__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.
Potentially problematic release.
This version of passagemath-polyhedra might be problematic. Click here for more details.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -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 +3895 -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,1206 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Base class for polyhedra: Miscellaneous methods
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# ****************************************************************************
|
|
7
|
+
# Copyright (C) 2008-2012 Marshall Hampton <hamptonio@gmail.com>
|
|
8
|
+
# Copyright (C) 2011-2015 Volker Braun <vbraun.name@gmail.com>
|
|
9
|
+
# Copyright (C) 2012-2018 Frederic Chapoton
|
|
10
|
+
# Copyright (C) 2013 Andrey Novoseltsev
|
|
11
|
+
# Copyright (C) 2014-2017 Moritz Firsching
|
|
12
|
+
# Copyright (C) 2014-2019 Thierry Monteil
|
|
13
|
+
# Copyright (C) 2015 Nathann Cohen
|
|
14
|
+
# Copyright (C) 2015-2017 Jeroen Demeyer
|
|
15
|
+
# Copyright (C) 2015-2017 Vincent Delecroix
|
|
16
|
+
# Copyright (C) 2015-2018 Dima Pasechnik
|
|
17
|
+
# Copyright (C) 2015-2020 Jean-Philippe Labbe <labbe at math.huji.ac.il>
|
|
18
|
+
# Copyright (C) 2015-2021 Matthias Koeppe
|
|
19
|
+
# Copyright (C) 2016-2019 Daniel Krenn
|
|
20
|
+
# Copyright (C) 2017 Marcelo Forets
|
|
21
|
+
# Copyright (C) 2017-2018 Mark Bell
|
|
22
|
+
# Copyright (C) 2019 Julian Ritter
|
|
23
|
+
# Copyright (C) 2019-2020 Laith Rastanawi
|
|
24
|
+
# Copyright (C) 2019-2020 Sophia Elia
|
|
25
|
+
# Copyright (C) 2019-2021 Jonathan Kliem <jonathan.kliem@gmail.com>
|
|
26
|
+
#
|
|
27
|
+
# This program is free software: you can redistribute it and/or modify
|
|
28
|
+
# it under the terms of the GNU General Public License as published by
|
|
29
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
30
|
+
# (at your option) any later version.
|
|
31
|
+
# https://www.gnu.org/licenses/
|
|
32
|
+
# ****************************************************************************
|
|
33
|
+
|
|
34
|
+
from sage.misc.cachefunc import cached_method
|
|
35
|
+
|
|
36
|
+
from sage.rings.integer_ring import ZZ
|
|
37
|
+
from sage.rings.rational_field import QQ
|
|
38
|
+
from sage.matrix.constructor import matrix
|
|
39
|
+
from sage.modules.free_module_element import vector
|
|
40
|
+
|
|
41
|
+
from .base7 import Polyhedron_base7
|
|
42
|
+
|
|
43
|
+
#########################################################################
|
|
44
|
+
# Notes if you want to implement your own backend:
|
|
45
|
+
#
|
|
46
|
+
# * derive from Polyhedron_base
|
|
47
|
+
#
|
|
48
|
+
# * you must implement _init_from_Vrepresentation and
|
|
49
|
+
# _init_from_Hrepresentation
|
|
50
|
+
#
|
|
51
|
+
# * You might want to override _init_empty_polyhedron
|
|
52
|
+
#
|
|
53
|
+
# * You may implement _init_from_Vrepresentation_and_Hrepresentation
|
|
54
|
+
#
|
|
55
|
+
# * You can of course also override any other method for which you
|
|
56
|
+
# have a faster implementation.
|
|
57
|
+
#########################################################################
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
#########################################################################
|
|
61
|
+
def is_Polyhedron(X):
|
|
62
|
+
"""
|
|
63
|
+
Test whether ``X`` is a Polyhedron.
|
|
64
|
+
|
|
65
|
+
INPUT:
|
|
66
|
+
|
|
67
|
+
- ``X`` -- anything
|
|
68
|
+
|
|
69
|
+
OUTPUT: boolean
|
|
70
|
+
|
|
71
|
+
EXAMPLES::
|
|
72
|
+
|
|
73
|
+
sage: p = polytopes.hypercube(2)
|
|
74
|
+
sage: from sage.geometry.polyhedron.base import is_Polyhedron
|
|
75
|
+
sage: is_Polyhedron(p)
|
|
76
|
+
doctest:warning...
|
|
77
|
+
DeprecationWarning: is_Polyhedron is deprecated, use isinstance instead
|
|
78
|
+
See https://github.com/sagemath/sage/issues/34307 for details.
|
|
79
|
+
True
|
|
80
|
+
sage: is_Polyhedron(123456)
|
|
81
|
+
False
|
|
82
|
+
"""
|
|
83
|
+
from sage.misc.superseded import deprecation
|
|
84
|
+
deprecation(34307, "is_Polyhedron is deprecated, use isinstance instead")
|
|
85
|
+
return isinstance(X, Polyhedron_base)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
#########################################################################
|
|
89
|
+
class Polyhedron_base(Polyhedron_base7):
|
|
90
|
+
"""
|
|
91
|
+
Base class for Polyhedron objects.
|
|
92
|
+
|
|
93
|
+
INPUT:
|
|
94
|
+
|
|
95
|
+
- ``parent`` -- the parent, an instance of
|
|
96
|
+
:class:`~sage.geometry.polyhedron.parent.Polyhedra`
|
|
97
|
+
|
|
98
|
+
- ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``. The
|
|
99
|
+
V-representation of the polyhedron; if ``None``, the polyhedron
|
|
100
|
+
is determined by the H-representation
|
|
101
|
+
|
|
102
|
+
- ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``. The
|
|
103
|
+
H-representation of the polyhedron; if ``None``, the polyhedron
|
|
104
|
+
is determined by the V-representation
|
|
105
|
+
|
|
106
|
+
- ``Vrep_minimal`` -- (optional) see below
|
|
107
|
+
|
|
108
|
+
- ``Hrep_minimal`` -- (optional) see below
|
|
109
|
+
|
|
110
|
+
- ``pref_rep`` -- string (default: ``None``);
|
|
111
|
+
one of ``Vrep`` or ``Hrep`` to pick this in case the backend
|
|
112
|
+
cannot initialize from complete double description
|
|
113
|
+
|
|
114
|
+
- ``mutable`` -- ignored
|
|
115
|
+
|
|
116
|
+
If both ``Vrep`` and ``Hrep`` are provided, then
|
|
117
|
+
``Vrep_minimal`` and ``Hrep_minimal`` must be set to ``True``.
|
|
118
|
+
|
|
119
|
+
TESTS::
|
|
120
|
+
|
|
121
|
+
sage: p = Polyhedron()
|
|
122
|
+
sage: TestSuite(p).run()
|
|
123
|
+
|
|
124
|
+
::
|
|
125
|
+
|
|
126
|
+
sage: p = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ)
|
|
127
|
+
sage: TestSuite(p).run()
|
|
128
|
+
|
|
129
|
+
::
|
|
130
|
+
|
|
131
|
+
sage: p = polytopes.flow_polytope(digraphs.DeBruijn(3,2)) # needs sage.combinat sage.graphs
|
|
132
|
+
sage: TestSuite(p).run()
|
|
133
|
+
|
|
134
|
+
::
|
|
135
|
+
|
|
136
|
+
sage: TestSuite(Polyhedron([[]])).run()
|
|
137
|
+
sage: TestSuite(Polyhedron([[0]])).run()
|
|
138
|
+
sage: TestSuite(Polyhedron([[1]])).run()
|
|
139
|
+
|
|
140
|
+
::
|
|
141
|
+
|
|
142
|
+
sage: P3 = polytopes.permutahedron(3)
|
|
143
|
+
sage: P = P3 * Polyhedron(rays=[[0,0,1], [0,1,1], [1,2,3]])
|
|
144
|
+
sage: TestSuite(P).run()
|
|
145
|
+
|
|
146
|
+
::
|
|
147
|
+
|
|
148
|
+
sage: P = P3 * Polyhedron(rays=[[0,0,1], [0,1,1]], lines=[[1,0,0]])
|
|
149
|
+
sage: TestSuite(P).run()
|
|
150
|
+
|
|
151
|
+
::
|
|
152
|
+
|
|
153
|
+
sage: M = random_matrix(ZZ, 5, 5, distribution='uniform')
|
|
154
|
+
sage: while True:
|
|
155
|
+
....: M = random_matrix(ZZ, 5, 5, distribution='uniform')
|
|
156
|
+
....: if M.rank() != 5:
|
|
157
|
+
....: break
|
|
158
|
+
....:
|
|
159
|
+
sage: P = Polyhedron(M)
|
|
160
|
+
sage: TestSuite(P).run()
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def _test_basic_properties(self, tester=None, **options):
|
|
164
|
+
"""
|
|
165
|
+
Run some basic tests to see, that some general assertion on polyhedra hold.
|
|
166
|
+
|
|
167
|
+
TESTS::
|
|
168
|
+
|
|
169
|
+
sage: polytopes.cross_polytope(3)._test_basic_properties()
|
|
170
|
+
"""
|
|
171
|
+
from .constructor import Polyhedron
|
|
172
|
+
|
|
173
|
+
if tester is None:
|
|
174
|
+
tester = self._tester(**options)
|
|
175
|
+
|
|
176
|
+
tester.assertEqual(self.n_vertices() + self.n_rays() + self.n_lines(), self.n_Vrepresentation())
|
|
177
|
+
tester.assertEqual(self.n_inequalities() + self.n_equations(), self.n_Hrepresentation())
|
|
178
|
+
if self.n_vertices():
|
|
179
|
+
# Depending on the backend, this does not hold for the empty polyhedron.
|
|
180
|
+
tester.assertEqual(self.dim() + self.n_equations(), self.ambient_dim())
|
|
181
|
+
|
|
182
|
+
tester.assertTrue(all(len(v[::]) == self.ambient_dim() for v in self.Vrep_generator()))
|
|
183
|
+
tester.assertTrue(all(len(h[::]) == self.ambient_dim() + 1 for h in self.Hrep_generator()))
|
|
184
|
+
|
|
185
|
+
if self.n_vertices() + self.n_rays() < 40:
|
|
186
|
+
tester.assertEqual(self, Polyhedron(vertices=self.vertices(), rays=self.rays(), lines=self.lines(), ambient_dim=self.ambient_dim()))
|
|
187
|
+
if self.n_inequalities() < 40:
|
|
188
|
+
tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations(), ambient_dim=self.ambient_dim()))
|
|
189
|
+
|
|
190
|
+
def to_linear_program(self, solver=None, return_variable=False, base_ring=None):
|
|
191
|
+
r"""
|
|
192
|
+
Return a linear optimization problem over the polyhedron in the form of
|
|
193
|
+
a :class:`MixedIntegerLinearProgram`.
|
|
194
|
+
|
|
195
|
+
INPUT:
|
|
196
|
+
|
|
197
|
+
- ``solver`` -- select a solver (MIP backend). See the documentation
|
|
198
|
+
of for :class:`MixedIntegerLinearProgram`. Set to ``None`` by default.
|
|
199
|
+
|
|
200
|
+
- ``return_variable`` -- boolean (default: ``False``); if ``True``, return a tuple
|
|
201
|
+
``(p, x)``, where ``p`` is the :class:`MixedIntegerLinearProgram` object
|
|
202
|
+
and ``x`` is the vector-valued MIP variable in this problem, indexed
|
|
203
|
+
from 0. If ``False``, only return ``p``.
|
|
204
|
+
|
|
205
|
+
- ``base_ring`` -- select a field over which the linear program should be
|
|
206
|
+
set up. Use ``RDF`` to request a fast inexact (floating point) solver
|
|
207
|
+
even if ``self`` is exact.
|
|
208
|
+
|
|
209
|
+
Note that the :class:`MixedIntegerLinearProgram` object will have the
|
|
210
|
+
null function as an objective to be maximized.
|
|
211
|
+
|
|
212
|
+
.. SEEALSO::
|
|
213
|
+
|
|
214
|
+
:meth:`~MixedIntegerLinearProgram.polyhedron` -- return the
|
|
215
|
+
polyhedron associated with a :class:`MixedIntegerLinearProgram`
|
|
216
|
+
object.
|
|
217
|
+
|
|
218
|
+
EXAMPLES:
|
|
219
|
+
|
|
220
|
+
Exact rational linear program::
|
|
221
|
+
|
|
222
|
+
sage: p = polytopes.cube()
|
|
223
|
+
sage: p.to_linear_program()
|
|
224
|
+
Linear Program (no objective, 3 variables, 6 constraints)
|
|
225
|
+
sage: lp, x = p.to_linear_program(return_variable=True)
|
|
226
|
+
sage: lp.set_objective(2*x[0] + 1*x[1] + 39*x[2])
|
|
227
|
+
sage: lp.solve()
|
|
228
|
+
42
|
|
229
|
+
sage: lp.get_values(x[0], x[1], x[2])
|
|
230
|
+
[1, 1, 1]
|
|
231
|
+
|
|
232
|
+
Floating-point linear program::
|
|
233
|
+
|
|
234
|
+
sage: lp, x = p.to_linear_program(return_variable=True, base_ring=RDF)
|
|
235
|
+
sage: lp.set_objective(2*x[0] + 1*x[1] + 39*x[2])
|
|
236
|
+
sage: lp.solve()
|
|
237
|
+
42.0
|
|
238
|
+
|
|
239
|
+
Irrational algebraic linear program over an embedded number field::
|
|
240
|
+
|
|
241
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
242
|
+
sage: p = polytopes.icosahedron()
|
|
243
|
+
sage: lp, x = p.to_linear_program(return_variable=True)
|
|
244
|
+
sage: lp.set_objective(x[0] + x[1] + x[2])
|
|
245
|
+
sage: lp.solve()
|
|
246
|
+
1/4*sqrt5 + 3/4
|
|
247
|
+
|
|
248
|
+
Same example with floating point::
|
|
249
|
+
|
|
250
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
251
|
+
sage: lp, x = p.to_linear_program(return_variable=True, base_ring=RDF)
|
|
252
|
+
sage: lp.set_objective(x[0] + x[1] + x[2])
|
|
253
|
+
sage: lp.solve() # tol 1e-5
|
|
254
|
+
1.3090169943749475
|
|
255
|
+
|
|
256
|
+
Same example with a specific floating point solver::
|
|
257
|
+
|
|
258
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
259
|
+
sage: lp, x = p.to_linear_program(return_variable=True, solver='GLPK')
|
|
260
|
+
sage: lp.set_objective(x[0] + x[1] + x[2])
|
|
261
|
+
sage: lp.solve() # tol 1e-8
|
|
262
|
+
1.3090169943749475
|
|
263
|
+
|
|
264
|
+
Irrational algebraic linear program over `AA`::
|
|
265
|
+
|
|
266
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
267
|
+
sage: p = polytopes.icosahedron(base_ring=AA)
|
|
268
|
+
sage: lp, x = p.to_linear_program(return_variable=True)
|
|
269
|
+
sage: lp.set_objective(x[0] + x[1] + x[2])
|
|
270
|
+
sage: lp.solve() # long time
|
|
271
|
+
1.309016994374948?
|
|
272
|
+
|
|
273
|
+
TESTS::
|
|
274
|
+
|
|
275
|
+
sage: p = polytopes.flow_polytope(digraphs.DeBruijn(3,2)); p # needs sage.combinat sage.graphs
|
|
276
|
+
A 19-dimensional polyhedron in QQ^27
|
|
277
|
+
defined as the convex hull of 1 vertex and 148 rays
|
|
278
|
+
sage: p.to_linear_program().polyhedron() == p
|
|
279
|
+
True
|
|
280
|
+
|
|
281
|
+
sage: p = polytopes.icosahedron() # needs sage.groups sage.rings.number_field
|
|
282
|
+
sage: p.to_linear_program(solver='PPL') # needs sage.groups sage.rings.number_field
|
|
283
|
+
Traceback (most recent call last):
|
|
284
|
+
...
|
|
285
|
+
TypeError: The PPL backend only supports rational data.
|
|
286
|
+
|
|
287
|
+
Test that equations are handled correctly (:issue:`24154`)::
|
|
288
|
+
|
|
289
|
+
sage: p = Polyhedron(vertices=[[19]])
|
|
290
|
+
sage: lp, x = p.to_linear_program(return_variable=True)
|
|
291
|
+
sage: lp.set_objective(x[0])
|
|
292
|
+
sage: lp.solve()
|
|
293
|
+
19
|
|
294
|
+
"""
|
|
295
|
+
if base_ring is None:
|
|
296
|
+
base_ring = self.base_ring()
|
|
297
|
+
base_ring = base_ring.fraction_field()
|
|
298
|
+
from sage.numerical.mip import MixedIntegerLinearProgram
|
|
299
|
+
p = MixedIntegerLinearProgram(solver=solver, base_ring=base_ring)
|
|
300
|
+
x = p.new_variable(real=True, nonnegative=False)
|
|
301
|
+
|
|
302
|
+
for ineqn in self.inequalities_list():
|
|
303
|
+
b = -ineqn.pop(0)
|
|
304
|
+
p.add_constraint(p.sum([x[i] * ineqn[i] for i in range(len(ineqn))]) >= b)
|
|
305
|
+
|
|
306
|
+
for eqn in self.equations_list():
|
|
307
|
+
b = -eqn.pop(0)
|
|
308
|
+
p.add_constraint(p.sum([x[i] * eqn[i] for i in range(len(eqn))]) == b)
|
|
309
|
+
|
|
310
|
+
if return_variable:
|
|
311
|
+
return p, x
|
|
312
|
+
else:
|
|
313
|
+
return p
|
|
314
|
+
|
|
315
|
+
def boundary_complex(self):
|
|
316
|
+
"""
|
|
317
|
+
Return the simplicial complex given by the boundary faces of ``self``,
|
|
318
|
+
if it is simplicial.
|
|
319
|
+
|
|
320
|
+
OUTPUT:
|
|
321
|
+
|
|
322
|
+
A (spherical) simplicial complex
|
|
323
|
+
|
|
324
|
+
EXAMPLES:
|
|
325
|
+
|
|
326
|
+
The boundary complex of the octahedron::
|
|
327
|
+
|
|
328
|
+
sage: # needs sage.graphs
|
|
329
|
+
sage: oc = polytopes.octahedron()
|
|
330
|
+
sage: sc_oc = oc.boundary_complex()
|
|
331
|
+
sage: fl_oc = oc.face_lattice() # needs sage.combinat
|
|
332
|
+
sage: fl_sc = sc_oc.face_poset() # needs sage.combinat
|
|
333
|
+
sage: [len(x) for x in fl_oc.level_sets()] # needs sage.combinat
|
|
334
|
+
[1, 6, 12, 8, 1]
|
|
335
|
+
sage: [len(x) for x in fl_sc.level_sets()] # needs sage.combinat
|
|
336
|
+
[6, 12, 8]
|
|
337
|
+
sage: sc_oc.euler_characteristic()
|
|
338
|
+
2
|
|
339
|
+
sage: sc_oc.homology()
|
|
340
|
+
{0: 0, 1: 0, 2: Z}
|
|
341
|
+
|
|
342
|
+
The polyhedron should be simplicial::
|
|
343
|
+
|
|
344
|
+
sage: c = polytopes.cube()
|
|
345
|
+
sage: c.boundary_complex()
|
|
346
|
+
Traceback (most recent call last):
|
|
347
|
+
...
|
|
348
|
+
NotImplementedError: this function is only implemented for simplicial polytopes
|
|
349
|
+
|
|
350
|
+
TESTS::
|
|
351
|
+
|
|
352
|
+
sage: p = Polyhedron(rays=[[1,1]])
|
|
353
|
+
sage: p.boundary_complex()
|
|
354
|
+
Traceback (most recent call last):
|
|
355
|
+
...
|
|
356
|
+
ValueError: self should be compact
|
|
357
|
+
"""
|
|
358
|
+
if not self.is_compact():
|
|
359
|
+
raise ValueError("self should be compact")
|
|
360
|
+
|
|
361
|
+
if self.is_simplicial():
|
|
362
|
+
from sage.topology.simplicial_complex import SimplicialComplex
|
|
363
|
+
inc_mat_cols = self.incidence_matrix().columns()
|
|
364
|
+
ineq_indices = [inc_mat_cols[i].nonzero_positions()
|
|
365
|
+
for i in range(self.n_Hrepresentation())
|
|
366
|
+
if self.Hrepresentation()[i].is_inequality()]
|
|
367
|
+
return SimplicialComplex(ineq_indices, maximality_check=False)
|
|
368
|
+
else:
|
|
369
|
+
raise NotImplementedError("this function is only implemented for simplicial polytopes")
|
|
370
|
+
|
|
371
|
+
@cached_method
|
|
372
|
+
def center(self):
|
|
373
|
+
"""
|
|
374
|
+
Return the average of the vertices.
|
|
375
|
+
|
|
376
|
+
.. SEEALSO::
|
|
377
|
+
|
|
378
|
+
:meth:`sage.geometry.polyhedron.base1.Polyhedron_base1.representative_point`.
|
|
379
|
+
|
|
380
|
+
OUTPUT:
|
|
381
|
+
|
|
382
|
+
The center of the polyhedron. All rays and lines are
|
|
383
|
+
ignored. Raises a :exc:`ZeroDivisionError` for the empty
|
|
384
|
+
polytope.
|
|
385
|
+
|
|
386
|
+
EXAMPLES::
|
|
387
|
+
|
|
388
|
+
sage: p = polytopes.hypercube(3)
|
|
389
|
+
sage: p = p + vector([1,0,0])
|
|
390
|
+
sage: p.center()
|
|
391
|
+
(1, 0, 0)
|
|
392
|
+
"""
|
|
393
|
+
if self.dim() == 0:
|
|
394
|
+
return self.vertices()[0].vector()
|
|
395
|
+
else:
|
|
396
|
+
vertex_sum = vector(self.base_ring(), [0] * self.ambient_dim())
|
|
397
|
+
for v in self.vertex_generator():
|
|
398
|
+
vertex_sum += v.vector()
|
|
399
|
+
vertex_sum.set_immutable()
|
|
400
|
+
return vertex_sum / self.n_vertices()
|
|
401
|
+
|
|
402
|
+
@cached_method
|
|
403
|
+
def radius_square(self):
|
|
404
|
+
"""
|
|
405
|
+
Return the square of the maximal distance from the
|
|
406
|
+
:meth:`center` to a vertex. All rays and lines are ignored.
|
|
407
|
+
|
|
408
|
+
OUTPUT:
|
|
409
|
+
|
|
410
|
+
The square of the radius, which is in
|
|
411
|
+
:meth:`~sage.geometry.polyhedron.base0.Polyhedron_base0.base_ring`.
|
|
412
|
+
|
|
413
|
+
EXAMPLES::
|
|
414
|
+
|
|
415
|
+
sage: p = polytopes.permutahedron(4, project = False)
|
|
416
|
+
sage: p.radius_square()
|
|
417
|
+
5
|
|
418
|
+
"""
|
|
419
|
+
vertices = [v.vector() - self.center() for v in self.vertex_generator()]
|
|
420
|
+
return max(v.dot_product(v) for v in vertices)
|
|
421
|
+
|
|
422
|
+
def radius(self):
|
|
423
|
+
"""
|
|
424
|
+
Return the maximal distance from the center to a vertex. All
|
|
425
|
+
rays and lines are ignored.
|
|
426
|
+
|
|
427
|
+
OUTPUT:
|
|
428
|
+
|
|
429
|
+
The radius for a rational polyhedron is, in general, not
|
|
430
|
+
rational. use :meth:`radius_square` if you need a rational
|
|
431
|
+
distance measure.
|
|
432
|
+
|
|
433
|
+
EXAMPLES::
|
|
434
|
+
|
|
435
|
+
sage: p = polytopes.hypercube(4)
|
|
436
|
+
sage: p.radius()
|
|
437
|
+
2
|
|
438
|
+
"""
|
|
439
|
+
return self.radius_square().sqrt()
|
|
440
|
+
|
|
441
|
+
def is_inscribed(self, certificate=False):
|
|
442
|
+
"""
|
|
443
|
+
This function tests whether the vertices of the polyhedron are
|
|
444
|
+
inscribed on a sphere.
|
|
445
|
+
|
|
446
|
+
The polyhedron is expected to be compact and full-dimensional.
|
|
447
|
+
A full-dimensional compact polytope is inscribed if there exists
|
|
448
|
+
a point in space which is equidistant to all its vertices.
|
|
449
|
+
|
|
450
|
+
ALGORITHM:
|
|
451
|
+
|
|
452
|
+
The function first computes the circumsphere of a full-dimensional
|
|
453
|
+
simplex with vertices of ``self``. It is found by lifting the points on a
|
|
454
|
+
paraboloid to find the hyperplane on which the circumsphere is lifted.
|
|
455
|
+
Then, it checks if all other vertices are equidistant to the
|
|
456
|
+
circumcenter of that simplex.
|
|
457
|
+
|
|
458
|
+
INPUT:
|
|
459
|
+
|
|
460
|
+
- ``certificate`` -- boolean (default: ``False``); specifies whether to
|
|
461
|
+
return the circumcenter, if found
|
|
462
|
+
|
|
463
|
+
OUTPUT: if ``certificate`` is true, returns a tuple containing:
|
|
464
|
+
|
|
465
|
+
1. Boolean.
|
|
466
|
+
2. The circumcenter of the polytope or None.
|
|
467
|
+
|
|
468
|
+
If ``certificate`` is false:
|
|
469
|
+
|
|
470
|
+
- a Boolean.
|
|
471
|
+
|
|
472
|
+
EXAMPLES::
|
|
473
|
+
|
|
474
|
+
sage: q = Polyhedron(vertices=[[1,1,1,1],[-1,-1,1,1],[1,-1,-1,1],
|
|
475
|
+
....: [-1,1,-1,1],[1,1,1,-1],[-1,-1,1,-1],
|
|
476
|
+
....: [1,-1,-1,-1],[-1,1,-1,-1],[0,0,10/13,-24/13],
|
|
477
|
+
....: [0,0,-10/13,-24/13]])
|
|
478
|
+
sage: q.is_inscribed(certificate=True)
|
|
479
|
+
(True, (0, 0, 0, 0))
|
|
480
|
+
|
|
481
|
+
sage: cube = polytopes.cube()
|
|
482
|
+
sage: cube.is_inscribed()
|
|
483
|
+
True
|
|
484
|
+
|
|
485
|
+
sage: translated_cube = Polyhedron(vertices=[v.vector() + vector([1,2,3])
|
|
486
|
+
....: for v in cube.vertices()])
|
|
487
|
+
sage: translated_cube.is_inscribed(certificate=True)
|
|
488
|
+
(True, (1, 2, 3))
|
|
489
|
+
|
|
490
|
+
sage: truncated_cube = cube.face_truncation(cube.faces(0)[0])
|
|
491
|
+
sage: truncated_cube.is_inscribed()
|
|
492
|
+
False
|
|
493
|
+
|
|
494
|
+
The method is not implemented for non-full-dimensional polytope or
|
|
495
|
+
unbounded polyhedra::
|
|
496
|
+
|
|
497
|
+
sage: square = Polyhedron(vertices=[[1,0,0],[0,1,0],[1,1,0],[0,0,0]])
|
|
498
|
+
sage: square.is_inscribed()
|
|
499
|
+
Traceback (most recent call last):
|
|
500
|
+
...
|
|
501
|
+
NotImplementedError: this function is implemented for full-dimensional polyhedra only
|
|
502
|
+
|
|
503
|
+
sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)])
|
|
504
|
+
sage: p.is_inscribed()
|
|
505
|
+
Traceback (most recent call last):
|
|
506
|
+
...
|
|
507
|
+
NotImplementedError: this function is not implemented for unbounded polyhedra
|
|
508
|
+
|
|
509
|
+
TESTS:
|
|
510
|
+
|
|
511
|
+
We check that :issue:`28464` is fixed::
|
|
512
|
+
|
|
513
|
+
sage: P = Polyhedron(vertices=[(-130658298093891402635075/416049251842505144482473,
|
|
514
|
+
....: 177469511761879509172000/1248147755527515433447419,
|
|
515
|
+
....: 485550543257132133136169/2496295511055030866894838,
|
|
516
|
+
....: 2010744967797898733758669/2496295511055030866894838),
|
|
517
|
+
....: (-146945725603929909850/706333405676769433081,
|
|
518
|
+
....: -84939725782618445000/706333405676769433081,
|
|
519
|
+
....: 560600045283000988081/1412666811353538866162,
|
|
520
|
+
....: 969778382942371268081/1412666811353538866162),
|
|
521
|
+
....: (-46275018824497300/140422338198040641,
|
|
522
|
+
....: -5747688262110000/46807446066013547, 1939357556329/7033601552658,
|
|
523
|
+
....: 1939357556329/7033601552658), (-17300/59929, -10000/59929, 39929/119858,
|
|
524
|
+
....: 39929/119858), (-4700/32209, -10000/32209, 12209/64418, 12209/64418),
|
|
525
|
+
....: (QQ(0), QQ(0), QQ(0), QQ(1)), (QQ(0), QQ(0), 1/2, 1/2), (300/10027,
|
|
526
|
+
....: -10000/30081, 10081/60162, 10081/60162), (112393975400/1900567733649,
|
|
527
|
+
....: 117311600000/633522577883, 43678681/95197362, 43678681/95197362),
|
|
528
|
+
....: (6109749955400/133380598418321, 37106807920000/133380598418321,
|
|
529
|
+
....: 2677964249/6680888498, 2677964249/6680888498),
|
|
530
|
+
....: (29197890764005600/402876806828660641,
|
|
531
|
+
....: -2150510776960000/402876806828660641,
|
|
532
|
+
....: 398575785274740641/805753613657321282,
|
|
533
|
+
....: 398575785274740641/805753613657321282),
|
|
534
|
+
....: (5576946899441759759983005325/110078073300232813237456943251,
|
|
535
|
+
....: -29071211718677797926570478000/110078073300232813237456943251,
|
|
536
|
+
....: 59439312069347378584317232001/220156146600465626474913886502,
|
|
537
|
+
....: 181346577228466312205473034501/220156146600465626474913886502),
|
|
538
|
+
....: (150040732779124914266530235300/6774574358246204311268446913881,
|
|
539
|
+
....: -2813827375989039189507000218000/6774574358246204311268446913881,
|
|
540
|
+
....: 1260217414021285074925933133881/13549148716492408622536893827762,
|
|
541
|
+
....: 3232518047094242684574253773881/13549148716492408622536893827762),
|
|
542
|
+
....: (3816349407976279597850158016285000/88842127448735433741180809504357161,
|
|
543
|
+
....: 27965821247423216557301387453968000/88842127448735433741180809504357161,
|
|
544
|
+
....: 68546256000224819256028677086357161/177684254897470867482361619008714322,
|
|
545
|
+
....: 86062257922545755787315412690197161/177684254897470867482361619008714322)])
|
|
546
|
+
sage: P.is_inscribed()
|
|
547
|
+
True
|
|
548
|
+
|
|
549
|
+
sage: P = Polyhedron(vertices=[[0, -1, 0, 0],
|
|
550
|
+
....: [0, 0, -1, 0],
|
|
551
|
+
....: [0, 0, 0, -1],
|
|
552
|
+
....: [0, 0, +1, 0],
|
|
553
|
+
....: [0, 0, 0, +1],
|
|
554
|
+
....: [+1, 0, 0, 0]])
|
|
555
|
+
sage: P.is_inscribed()
|
|
556
|
+
True
|
|
557
|
+
|
|
558
|
+
We check that :issue:`29125` is fixed::
|
|
559
|
+
|
|
560
|
+
sage: P = Polyhedron(vertices=[[-2,-1], [-2,1], [0,-1], [0,1]], backend='field')
|
|
561
|
+
sage: P.is_inscribed()
|
|
562
|
+
True
|
|
563
|
+
sage: V = P.Vrepresentation()
|
|
564
|
+
sage: H = P.Hrepresentation()
|
|
565
|
+
sage: parent = P.parent()
|
|
566
|
+
sage: for V1 in Permutations(V):
|
|
567
|
+
....: P1 = parent._element_constructor_(
|
|
568
|
+
....: [V1, [], []], [H, []], Vrep_minimal=True, Hrep_minimal=True)
|
|
569
|
+
....: assert P1.is_inscribed()
|
|
570
|
+
"""
|
|
571
|
+
|
|
572
|
+
if not self.is_compact():
|
|
573
|
+
raise NotImplementedError("this function is not implemented for unbounded polyhedra")
|
|
574
|
+
|
|
575
|
+
if not self.is_full_dimensional():
|
|
576
|
+
raise NotImplementedError("this function is implemented for full-dimensional polyhedra only")
|
|
577
|
+
|
|
578
|
+
dimension = self.dimension()
|
|
579
|
+
vertices = self.vertices()
|
|
580
|
+
|
|
581
|
+
# We obtain vertices that are an affine basis of the affine hull.
|
|
582
|
+
affine_basis = self.an_affine_basis()
|
|
583
|
+
raw_data = []
|
|
584
|
+
for vertex in affine_basis:
|
|
585
|
+
vertex_vector = vertex.vector()
|
|
586
|
+
raw_data += [[sum(i**2 for i in vertex_vector)] +
|
|
587
|
+
list(vertex_vector) + [1]]
|
|
588
|
+
matrix_data = matrix(raw_data)
|
|
589
|
+
|
|
590
|
+
# The determinant "a" should not be zero because
|
|
591
|
+
# the vertices in ``affine_basis`` are an affine basis.
|
|
592
|
+
a = matrix_data.matrix_from_columns(range(1, dimension+2)).determinant()
|
|
593
|
+
|
|
594
|
+
minors = [(-1)**(i)*matrix_data.matrix_from_columns([j for j in range(dimension+2) if j != i]).determinant()
|
|
595
|
+
for i in range(1, dimension+1)]
|
|
596
|
+
c = (-1)**(dimension+1)*matrix_data.matrix_from_columns(range(dimension+1)).determinant()
|
|
597
|
+
|
|
598
|
+
circumcenter = vector([minors[i]/(2*a) for i in range(dimension)])
|
|
599
|
+
squared_circumradius = (sum(m**2 for m in minors) - 4 * a * c) / (4*a**2)
|
|
600
|
+
|
|
601
|
+
# Checking if the circumcenter has the correct sign
|
|
602
|
+
if not all(sum(i**2 for i in v.vector() - circumcenter) == squared_circumradius
|
|
603
|
+
for v in vertices if v in affine_basis):
|
|
604
|
+
circumcenter = - circumcenter
|
|
605
|
+
|
|
606
|
+
is_inscribed = all(sum(i**2 for i in v.vector() - circumcenter) == squared_circumradius
|
|
607
|
+
for v in vertices if v not in affine_basis)
|
|
608
|
+
|
|
609
|
+
if certificate:
|
|
610
|
+
if is_inscribed:
|
|
611
|
+
return (True, circumcenter)
|
|
612
|
+
else:
|
|
613
|
+
return (False, None)
|
|
614
|
+
else:
|
|
615
|
+
return is_inscribed
|
|
616
|
+
|
|
617
|
+
def hyperplane_arrangement(self):
|
|
618
|
+
"""
|
|
619
|
+
Return the hyperplane arrangement defined by the equations and
|
|
620
|
+
inequalities.
|
|
621
|
+
|
|
622
|
+
OUTPUT:
|
|
623
|
+
|
|
624
|
+
A :class:`hyperplane arrangement
|
|
625
|
+
<sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement>`
|
|
626
|
+
consisting of the hyperplanes defined by the
|
|
627
|
+
:meth:`~sage.geometry.polyhedron.base0.Polyhedron_base0.Hrepresentation`.
|
|
628
|
+
If the polytope is full-dimensional, this is the hyperplane
|
|
629
|
+
arrangement spanned by the facets of the polyhedron.
|
|
630
|
+
|
|
631
|
+
EXAMPLES::
|
|
632
|
+
|
|
633
|
+
sage: p = polytopes.hypercube(2)
|
|
634
|
+
sage: p.hyperplane_arrangement()
|
|
635
|
+
Arrangement <-t0 + 1 | -t1 + 1 | t1 + 1 | t0 + 1>
|
|
636
|
+
"""
|
|
637
|
+
names = tuple('t' + str(i) for i in range(self.ambient_dim()))
|
|
638
|
+
from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements
|
|
639
|
+
field = self.base_ring().fraction_field()
|
|
640
|
+
H = HyperplaneArrangements(field, names)
|
|
641
|
+
return H(self)
|
|
642
|
+
|
|
643
|
+
@cached_method
|
|
644
|
+
def normal_fan(self, direction='inner'):
|
|
645
|
+
r"""
|
|
646
|
+
Return the normal fan of a compact full-dimensional rational polyhedron.
|
|
647
|
+
|
|
648
|
+
This returns the inner normal fan of ``self``. For the outer normal fan,
|
|
649
|
+
use ``direction='outer'``.
|
|
650
|
+
|
|
651
|
+
INPUT:
|
|
652
|
+
|
|
653
|
+
- ``direction`` -- either ``'inner'`` (default) or ``'outer'``; if
|
|
654
|
+
set to ``'inner'``, use the inner normal vectors to span the cones of
|
|
655
|
+
the fan, if set to ``'outer'``, use the outer normal vectors.
|
|
656
|
+
|
|
657
|
+
OUTPUT:
|
|
658
|
+
|
|
659
|
+
A complete fan of the ambient space as a
|
|
660
|
+
:class:`~sage.geometry.fan.RationalPolyhedralFan`.
|
|
661
|
+
|
|
662
|
+
.. SEEALSO::
|
|
663
|
+
|
|
664
|
+
:meth:`face_fan`.
|
|
665
|
+
|
|
666
|
+
EXAMPLES::
|
|
667
|
+
|
|
668
|
+
sage: S = Polyhedron(vertices=[[0, 0], [1, 0], [0, 1]])
|
|
669
|
+
sage: S.normal_fan()
|
|
670
|
+
Rational polyhedral fan in 2-d lattice N
|
|
671
|
+
|
|
672
|
+
sage: C = polytopes.hypercube(4)
|
|
673
|
+
sage: NF = C.normal_fan(); NF
|
|
674
|
+
Rational polyhedral fan in 4-d lattice N
|
|
675
|
+
|
|
676
|
+
Currently, it is only possible to get the normal fan of a bounded rational polytope::
|
|
677
|
+
|
|
678
|
+
sage: P = Polyhedron(rays=[[1, 0], [0, 1]])
|
|
679
|
+
sage: P.normal_fan()
|
|
680
|
+
Traceback (most recent call last):
|
|
681
|
+
...
|
|
682
|
+
NotImplementedError: the normal fan is only supported for polytopes (compact polyhedra).
|
|
683
|
+
|
|
684
|
+
sage: Q = Polyhedron(vertices=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
|
685
|
+
sage: Q.normal_fan()
|
|
686
|
+
Traceback (most recent call last):
|
|
687
|
+
...
|
|
688
|
+
ValueError: the normal fan is only defined for full-dimensional polytopes
|
|
689
|
+
|
|
690
|
+
sage: R = Polyhedron(vertices=[[0, 0], # needs sage.rings.number_field sage.symbolic
|
|
691
|
+
....: [AA(sqrt(2)), 0],
|
|
692
|
+
....: [0, AA(sqrt(2))]])
|
|
693
|
+
sage: R.normal_fan() # needs sage.rings.number_field sage.symbolic
|
|
694
|
+
Traceback (most recent call last):
|
|
695
|
+
...
|
|
696
|
+
NotImplementedError: normal fan handles only polytopes over the rationals
|
|
697
|
+
|
|
698
|
+
sage: P = Polyhedron(vertices=[[0,0], [2,0], [0,2], [2,1], [1,2]])
|
|
699
|
+
sage: P.normal_fan(direction=None)
|
|
700
|
+
Traceback (most recent call last):
|
|
701
|
+
...
|
|
702
|
+
TypeError: the direction should be 'inner' or 'outer'
|
|
703
|
+
|
|
704
|
+
sage: inner_nf = P.normal_fan()
|
|
705
|
+
sage: inner_nf.rays()
|
|
706
|
+
N( 1, 0),
|
|
707
|
+
N( 0, -1),
|
|
708
|
+
N( 0, 1),
|
|
709
|
+
N(-1, 0),
|
|
710
|
+
N(-1, -1)
|
|
711
|
+
in 2-d lattice N
|
|
712
|
+
|
|
713
|
+
sage: outer_nf = P.normal_fan(direction='outer')
|
|
714
|
+
sage: outer_nf.rays()
|
|
715
|
+
N( 1, 0),
|
|
716
|
+
N( 1, 1),
|
|
717
|
+
N( 0, 1),
|
|
718
|
+
N(-1, 0),
|
|
719
|
+
N( 0, -1)
|
|
720
|
+
in 2-d lattice N
|
|
721
|
+
|
|
722
|
+
REFERENCES:
|
|
723
|
+
|
|
724
|
+
For more information, see Chapter 7 of [Zie2007]_.
|
|
725
|
+
"""
|
|
726
|
+
from sage.geometry.fan import NormalFan
|
|
727
|
+
|
|
728
|
+
if not QQ.has_coerce_map_from(self.base_ring()):
|
|
729
|
+
raise NotImplementedError('normal fan handles only polytopes over the rationals')
|
|
730
|
+
if direction == 'inner':
|
|
731
|
+
return NormalFan(self)
|
|
732
|
+
elif direction == 'outer':
|
|
733
|
+
return NormalFan(-self)
|
|
734
|
+
else:
|
|
735
|
+
raise TypeError("the direction should be 'inner' or 'outer'")
|
|
736
|
+
|
|
737
|
+
@cached_method
|
|
738
|
+
def face_fan(self):
|
|
739
|
+
r"""
|
|
740
|
+
Return the face fan of a compact rational polyhedron.
|
|
741
|
+
|
|
742
|
+
OUTPUT:
|
|
743
|
+
|
|
744
|
+
A fan of the ambient space as a
|
|
745
|
+
:class:`~sage.geometry.fan.RationalPolyhedralFan`.
|
|
746
|
+
|
|
747
|
+
.. SEEALSO::
|
|
748
|
+
|
|
749
|
+
:meth:`normal_fan`.
|
|
750
|
+
|
|
751
|
+
EXAMPLES::
|
|
752
|
+
|
|
753
|
+
sage: T = polytopes.cuboctahedron()
|
|
754
|
+
sage: T.face_fan()
|
|
755
|
+
Rational polyhedral fan in 3-d lattice M
|
|
756
|
+
|
|
757
|
+
The polytope should contain the origin in the interior::
|
|
758
|
+
|
|
759
|
+
sage: P = Polyhedron(vertices=[[1/2, 1], [1, 1/2]])
|
|
760
|
+
sage: P.face_fan()
|
|
761
|
+
Traceback (most recent call last):
|
|
762
|
+
...
|
|
763
|
+
ValueError: face fans are defined only for polytopes
|
|
764
|
+
containing the origin as an interior point!
|
|
765
|
+
|
|
766
|
+
sage: Q = Polyhedron(vertices=[[-1, 1/2], [1, -1/2]])
|
|
767
|
+
sage: Q.contains([0,0])
|
|
768
|
+
True
|
|
769
|
+
sage: FF = Q.face_fan(); FF
|
|
770
|
+
Rational polyhedral fan in 2-d lattice M
|
|
771
|
+
|
|
772
|
+
The polytope has to have rational coordinates::
|
|
773
|
+
|
|
774
|
+
sage: S = polytopes.dodecahedron() # needs sage.groups sage.rings.number_field
|
|
775
|
+
sage: S.face_fan() # needs sage.groups sage.rings.number_field
|
|
776
|
+
Traceback (most recent call last):
|
|
777
|
+
...
|
|
778
|
+
NotImplementedError: face fan handles only polytopes over the rationals
|
|
779
|
+
|
|
780
|
+
REFERENCES:
|
|
781
|
+
|
|
782
|
+
For more information, see Chapter 7 of [Zie2007]_.
|
|
783
|
+
"""
|
|
784
|
+
from sage.geometry.fan import FaceFan
|
|
785
|
+
|
|
786
|
+
if not QQ.has_coerce_map_from(self.base_ring()):
|
|
787
|
+
raise NotImplementedError('face fan handles only polytopes over the rationals')
|
|
788
|
+
|
|
789
|
+
return FaceFan(self)
|
|
790
|
+
|
|
791
|
+
def is_minkowski_summand(self, Y):
|
|
792
|
+
r"""
|
|
793
|
+
Test whether ``Y`` is a Minkowski summand.
|
|
794
|
+
|
|
795
|
+
See :meth:`~sage.geometry.polyhedron.base5.Polyhedron_base5.minkowski_sum`.
|
|
796
|
+
|
|
797
|
+
OUTPUT: boolean; whether there exists another polyhedron `Z` such that
|
|
798
|
+
``self`` can be written as `Y\oplus Z`
|
|
799
|
+
|
|
800
|
+
EXAMPLES::
|
|
801
|
+
|
|
802
|
+
sage: A = polytopes.hypercube(2)
|
|
803
|
+
sage: B = Polyhedron(vertices=[(0,1), (1/2,1)])
|
|
804
|
+
sage: C = Polyhedron(vertices=[(1,1)])
|
|
805
|
+
sage: A.is_minkowski_summand(B)
|
|
806
|
+
True
|
|
807
|
+
sage: A.is_minkowski_summand(C)
|
|
808
|
+
True
|
|
809
|
+
sage: B.is_minkowski_summand(C)
|
|
810
|
+
True
|
|
811
|
+
sage: B.is_minkowski_summand(A)
|
|
812
|
+
False
|
|
813
|
+
sage: C.is_minkowski_summand(A)
|
|
814
|
+
False
|
|
815
|
+
sage: C.is_minkowski_summand(B)
|
|
816
|
+
False
|
|
817
|
+
"""
|
|
818
|
+
return self.minkowski_difference(Y).minkowski_sum(Y) == self
|
|
819
|
+
|
|
820
|
+
def barycentric_subdivision(self, subdivision_frac=None):
|
|
821
|
+
r"""
|
|
822
|
+
Return the barycentric subdivision of a compact polyhedron.
|
|
823
|
+
|
|
824
|
+
DEFINITION:
|
|
825
|
+
|
|
826
|
+
The barycentric subdivision of a compact polyhedron is a standard way
|
|
827
|
+
to triangulate its faces in such a way that maximal faces correspond to
|
|
828
|
+
flags of faces of the starting polyhedron (i.e. a maximal chain in the
|
|
829
|
+
face lattice of the polyhedron). As a simplicial complex, this is known
|
|
830
|
+
as the order complex of the face lattice of the polyhedron.
|
|
831
|
+
|
|
832
|
+
REFERENCE:
|
|
833
|
+
|
|
834
|
+
See :wikipedia:`Barycentric_subdivision`
|
|
835
|
+
|
|
836
|
+
Section 6.6, Handbook of Convex Geometry, Volume A, edited by P.M. Gruber and J.M.
|
|
837
|
+
Wills. 1993, North-Holland Publishing Co..
|
|
838
|
+
|
|
839
|
+
INPUT:
|
|
840
|
+
|
|
841
|
+
- ``subdivision_frac`` -- number. Gives the proportion how far the new
|
|
842
|
+
vertices are pulled out of the polytope. Default is `\frac{1}{3}` and
|
|
843
|
+
the value should be smaller than `\frac{1}{2}`. The subdivision is
|
|
844
|
+
computed on the polar polyhedron.
|
|
845
|
+
|
|
846
|
+
OUTPUT: a Polyhedron object, subdivided as described above
|
|
847
|
+
|
|
848
|
+
EXAMPLES::
|
|
849
|
+
|
|
850
|
+
sage: P = polytopes.hypercube(3)
|
|
851
|
+
sage: P.barycentric_subdivision()
|
|
852
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull
|
|
853
|
+
of 26 vertices
|
|
854
|
+
sage: P = Polyhedron(vertices=[[0,0,0],[0,1,0],[1,0,0],[0,0,1]])
|
|
855
|
+
sage: P.barycentric_subdivision()
|
|
856
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull
|
|
857
|
+
of 14 vertices
|
|
858
|
+
sage: P = Polyhedron(vertices=[[0,1,0],[0,0,1],[1,0,0]])
|
|
859
|
+
sage: P.barycentric_subdivision()
|
|
860
|
+
A 2-dimensional polyhedron in QQ^3 defined as the convex hull
|
|
861
|
+
of 6 vertices
|
|
862
|
+
sage: P = polytopes.regular_polygon(4, base_ring=QQ) # needs sage.rings.number_field
|
|
863
|
+
sage: P.barycentric_subdivision() # needs sage.rings.number_field
|
|
864
|
+
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 8
|
|
865
|
+
vertices
|
|
866
|
+
|
|
867
|
+
TESTS::
|
|
868
|
+
|
|
869
|
+
sage: P.barycentric_subdivision(1/2)
|
|
870
|
+
Traceback (most recent call last):
|
|
871
|
+
...
|
|
872
|
+
ValueError: the subdivision fraction should be between 0 and 1/2
|
|
873
|
+
sage: P = Polyhedron(ieqs=[[1,0,1],[0,1,0],[1,0,0],[0,0,1]])
|
|
874
|
+
sage: P.barycentric_subdivision()
|
|
875
|
+
Traceback (most recent call last):
|
|
876
|
+
...
|
|
877
|
+
ValueError: the polytope has to be compact
|
|
878
|
+
sage: P = Polyhedron(vertices=[[0,0,0],[0,1,0],[1,0,0],[0,0,1]], backend='field')
|
|
879
|
+
sage: P.barycentric_subdivision()
|
|
880
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 14 vertices
|
|
881
|
+
|
|
882
|
+
sage: polytopes.simplex(backend='field').barycentric_subdivision().backend()
|
|
883
|
+
'field'
|
|
884
|
+
sage: polytopes.cube(backend='cdd').barycentric_subdivision().backend() # needs cddexec_gmp
|
|
885
|
+
'cdd'
|
|
886
|
+
"""
|
|
887
|
+
if subdivision_frac is None:
|
|
888
|
+
subdivision_frac = ZZ.one() / 3
|
|
889
|
+
|
|
890
|
+
if not self.is_compact():
|
|
891
|
+
raise ValueError("the polytope has to be compact")
|
|
892
|
+
if not (0 < subdivision_frac < ZZ.one() / 2):
|
|
893
|
+
raise ValueError("the subdivision fraction should be "
|
|
894
|
+
"between 0 and 1/2")
|
|
895
|
+
|
|
896
|
+
barycenter = self.center()
|
|
897
|
+
parent = self.parent().base_extend(subdivision_frac)
|
|
898
|
+
|
|
899
|
+
start_polar = (self - barycenter).polar(in_affine_span=True)
|
|
900
|
+
polar = (self - barycenter).polar(in_affine_span=True)
|
|
901
|
+
|
|
902
|
+
for i in range(self.dimension() - 1):
|
|
903
|
+
|
|
904
|
+
new_ineq = []
|
|
905
|
+
subdivided_faces = list(start_polar.faces(i))
|
|
906
|
+
Hrep = polar.Hrepresentation()
|
|
907
|
+
|
|
908
|
+
for face in subdivided_faces:
|
|
909
|
+
|
|
910
|
+
face_vertices = face.vertices()
|
|
911
|
+
normal_vectors = []
|
|
912
|
+
|
|
913
|
+
for facet in Hrep:
|
|
914
|
+
if all(facet.contains(v) and not facet.interior_contains(v)
|
|
915
|
+
for v in face_vertices):
|
|
916
|
+
# The facet contains the face
|
|
917
|
+
normal_vectors.append(facet.A())
|
|
918
|
+
|
|
919
|
+
normal_vector = sum(normal_vectors)
|
|
920
|
+
B = - normal_vector * (face_vertices[0].vector())
|
|
921
|
+
linear_evaluation = {-normal_vector * v.vector()
|
|
922
|
+
for v in polar.vertices()}
|
|
923
|
+
|
|
924
|
+
if B == max(linear_evaluation):
|
|
925
|
+
C = max(linear_evaluation.difference(set([B])))
|
|
926
|
+
else:
|
|
927
|
+
C = min(linear_evaluation.difference(set([B])))
|
|
928
|
+
|
|
929
|
+
ineq_vector = [(1 - subdivision_frac) * B + subdivision_frac * C] + list(normal_vector)
|
|
930
|
+
new_ineq += [ineq_vector]
|
|
931
|
+
|
|
932
|
+
new_ieqs = polar.inequalities_list() + new_ineq
|
|
933
|
+
new_eqns = polar.equations_list()
|
|
934
|
+
|
|
935
|
+
polar = parent.element_class(parent, None, [new_ieqs, new_eqns])
|
|
936
|
+
|
|
937
|
+
return (polar.polar(in_affine_span=True)) + barycenter
|
|
938
|
+
|
|
939
|
+
def permutations_to_matrices(self, conj_class_reps, acting_group=None, additional_elts=None):
|
|
940
|
+
r"""
|
|
941
|
+
Return a dictionary between different representations of elements in
|
|
942
|
+
the ``acting_group``, with group elements represented as permutations
|
|
943
|
+
of the vertices of this polytope (keys) or matrices (values).
|
|
944
|
+
|
|
945
|
+
The dictionary has entries for the generators of the ``acting_group``
|
|
946
|
+
and the representatives of conjugacy classes in ``conj_class_reps``. By
|
|
947
|
+
default, the ``acting_group`` is the
|
|
948
|
+
:meth:`~sage.geometry.polyhedron.base4.Polyhedron_base4.restricted_automorphism_group`
|
|
949
|
+
of the polytope. Each element in ``additional_elts`` also becomes a key.
|
|
950
|
+
|
|
951
|
+
INPUT:
|
|
952
|
+
|
|
953
|
+
- ``conj_class_reps`` -- list; a list of representatives of the
|
|
954
|
+
conjugacy classes of the ``acting_group``
|
|
955
|
+
|
|
956
|
+
- ``acting_group`` -- a subgroup of polytope's
|
|
957
|
+
:meth:`~sage.geometry.polyhedron.base4.Polyhedron_base4.restricted_automorphism_group`
|
|
958
|
+
|
|
959
|
+
- ``additional_elts`` -- list (default: ``None``); a subset of the
|
|
960
|
+
:meth:`~sage.geometry.polyhedron.base4.Polyhedron_base4.restricted_automorphism_group`
|
|
961
|
+
of the polytope expressed as permutations.
|
|
962
|
+
|
|
963
|
+
OUTPUT:
|
|
964
|
+
|
|
965
|
+
A dictionary between elements of the ``acting_group`` expressed as permutations
|
|
966
|
+
(keys) and matrices (values).
|
|
967
|
+
|
|
968
|
+
EXAMPLES:
|
|
969
|
+
|
|
970
|
+
This example shows the dictionary between permutations and matrices
|
|
971
|
+
for the generators of the ``restricted_automorphism_group`` of the
|
|
972
|
+
`\pm 1` 2-dimensional square. The permutations are written in terms
|
|
973
|
+
of the vertices of the square::
|
|
974
|
+
|
|
975
|
+
sage: # optional - pynormaliz, needs sage.groups
|
|
976
|
+
sage: square = Polyhedron(vertices=[[1,1], [-1,1],
|
|
977
|
+
....: [-1,-1], [1,-1]],
|
|
978
|
+
....: backend='normaliz')
|
|
979
|
+
sage: square.vertices()
|
|
980
|
+
(A vertex at (-1, -1),
|
|
981
|
+
A vertex at (-1, 1),
|
|
982
|
+
A vertex at (1, -1),
|
|
983
|
+
A vertex at (1, 1))
|
|
984
|
+
sage: aut_square = square.restricted_automorphism_group(output='permutation')
|
|
985
|
+
sage: conj_reps = aut_square.conjugacy_classes_representatives()
|
|
986
|
+
sage: gens_dict = square.permutations_to_matrices(conj_reps)
|
|
987
|
+
sage: rotation_180 = aut_square([(0,3),(1,2)])
|
|
988
|
+
sage: rotation_180, gens_dict[rotation_180]
|
|
989
|
+
(
|
|
990
|
+
[-1 0 0]
|
|
991
|
+
[ 0 -1 0]
|
|
992
|
+
(0,3)(1,2), [ 0 0 1]
|
|
993
|
+
)
|
|
994
|
+
|
|
995
|
+
This example tests the functionality for additional elements::
|
|
996
|
+
|
|
997
|
+
sage: # needs sage.groups sage.rings.real_mpfr
|
|
998
|
+
sage: C = polytopes.cross_polytope(2)
|
|
999
|
+
sage: G = C.restricted_automorphism_group(output='permutation')
|
|
1000
|
+
sage: conj_reps = G.conjugacy_classes_representatives()
|
|
1001
|
+
sage: add_elt = G([(0, 2, 3, 1)])
|
|
1002
|
+
sage: dict = C.permutations_to_matrices(conj_reps,
|
|
1003
|
+
....: additional_elts=[add_elt])
|
|
1004
|
+
sage: dict[add_elt]
|
|
1005
|
+
[ 0 1 0]
|
|
1006
|
+
[-1 0 0]
|
|
1007
|
+
[ 0 0 1]
|
|
1008
|
+
"""
|
|
1009
|
+
if self.is_empty():
|
|
1010
|
+
raise NotImplementedError('empty polyhedra are not supported')
|
|
1011
|
+
if not self.is_compact():
|
|
1012
|
+
raise NotImplementedError('unbounded polyhedra are not supported')
|
|
1013
|
+
V = [v.homogeneous_vector() for v in self.Vrepresentation()]
|
|
1014
|
+
Qplus = sum(v.column() * v.row() for v in V).pseudoinverse()
|
|
1015
|
+
Vplus = list(matrix(V) * Qplus)
|
|
1016
|
+
W = 1 - sum(V[i].column() * Vplus[i].row() for i in range(len(V)))
|
|
1017
|
+
|
|
1018
|
+
G = self.restricted_automorphism_group(output='permutation')
|
|
1019
|
+
if acting_group is not None:
|
|
1020
|
+
G = acting_group
|
|
1021
|
+
|
|
1022
|
+
group_dict = {}
|
|
1023
|
+
|
|
1024
|
+
def permutation_to_matrix(permutation, V, Vplus, W):
|
|
1025
|
+
A = sum(V[permutation(i)].column() * Vplus[i].row()
|
|
1026
|
+
for i in range(len(V)))
|
|
1027
|
+
return A + W
|
|
1028
|
+
|
|
1029
|
+
for perm in G.gens():
|
|
1030
|
+
group_dict[perm] = permutation_to_matrix(perm, V, Vplus, W)
|
|
1031
|
+
|
|
1032
|
+
for perm in conj_class_reps:
|
|
1033
|
+
group_dict[perm] = permutation_to_matrix(perm, V, Vplus, W)
|
|
1034
|
+
|
|
1035
|
+
if additional_elts is not None:
|
|
1036
|
+
for perm in additional_elts:
|
|
1037
|
+
group_dict[perm] = permutation_to_matrix(perm, V, Vplus, W)
|
|
1038
|
+
return group_dict
|
|
1039
|
+
|
|
1040
|
+
@cached_method
|
|
1041
|
+
def bounding_box(self, integral=False, integral_hull=False):
|
|
1042
|
+
r"""
|
|
1043
|
+
Return the coordinates of a rectangular box containing the non-empty polytope.
|
|
1044
|
+
|
|
1045
|
+
INPUT:
|
|
1046
|
+
|
|
1047
|
+
- ``integral`` -- boolean (default: ``False``); whether to
|
|
1048
|
+
only allow integral coordinates in the bounding box
|
|
1049
|
+
|
|
1050
|
+
- ``integral_hull`` -- boolean (default: ``False``); if ``True``, return a
|
|
1051
|
+
box containing the integral points of the polytope, or ``None, None`` if it
|
|
1052
|
+
is known that the polytope has no integral points
|
|
1053
|
+
|
|
1054
|
+
OUTPUT:
|
|
1055
|
+
|
|
1056
|
+
A pair of tuples ``(box_min, box_max)`` where ``box_min`` are
|
|
1057
|
+
the coordinates of a point bounding the coordinates of the
|
|
1058
|
+
polytope from below and ``box_max`` bounds the coordinates
|
|
1059
|
+
from above.
|
|
1060
|
+
|
|
1061
|
+
EXAMPLES::
|
|
1062
|
+
|
|
1063
|
+
sage: Polyhedron([(1/3,2/3), (2/3, 1/3)]).bounding_box()
|
|
1064
|
+
((1/3, 1/3), (2/3, 2/3))
|
|
1065
|
+
sage: Polyhedron([(1/3,2/3), (2/3, 1/3)]).bounding_box(integral=True)
|
|
1066
|
+
((0, 0), (1, 1))
|
|
1067
|
+
sage: Polyhedron([(1/3,2/3), (2/3, 1/3)]).bounding_box(integral_hull=True)
|
|
1068
|
+
(None, None)
|
|
1069
|
+
sage: Polyhedron([(1/3,2/3), (3/3, 4/3)]).bounding_box(integral_hull=True)
|
|
1070
|
+
((1, 1), (1, 1))
|
|
1071
|
+
sage: polytopes.buckyball(exact=False).bounding_box() # needs sage.groups
|
|
1072
|
+
((-0.8090169944, -0.8090169944, -0.8090169944),
|
|
1073
|
+
(0.8090169944, 0.8090169944, 0.8090169944))
|
|
1074
|
+
|
|
1075
|
+
TESTS::
|
|
1076
|
+
|
|
1077
|
+
sage: Polyhedron().bounding_box()
|
|
1078
|
+
Traceback (most recent call last):
|
|
1079
|
+
...
|
|
1080
|
+
ValueError: empty polytope is not allowed
|
|
1081
|
+
"""
|
|
1082
|
+
from sage.arith.misc import integer_ceil as ceil
|
|
1083
|
+
from sage.arith.misc import integer_floor as floor
|
|
1084
|
+
box_min = []
|
|
1085
|
+
box_max = []
|
|
1086
|
+
if not self.is_compact():
|
|
1087
|
+
raise ValueError("only polytopes (compact polyhedra) are allowed")
|
|
1088
|
+
if self.n_vertices() == 0:
|
|
1089
|
+
raise ValueError("empty polytope is not allowed")
|
|
1090
|
+
for i in range(self.ambient_dim()):
|
|
1091
|
+
coords = [v[i] for v in self.vertex_generator()]
|
|
1092
|
+
max_coord = max(coords)
|
|
1093
|
+
min_coord = min(coords)
|
|
1094
|
+
if integral_hull:
|
|
1095
|
+
a = ceil(min_coord)
|
|
1096
|
+
b = floor(max_coord)
|
|
1097
|
+
if a > b:
|
|
1098
|
+
return None, None
|
|
1099
|
+
box_max.append(b)
|
|
1100
|
+
box_min.append(a)
|
|
1101
|
+
elif integral:
|
|
1102
|
+
box_max.append(ceil(max_coord))
|
|
1103
|
+
box_min.append(floor(min_coord))
|
|
1104
|
+
else:
|
|
1105
|
+
box_max.append(max_coord)
|
|
1106
|
+
box_min.append(min_coord)
|
|
1107
|
+
return (tuple(box_min), tuple(box_max))
|
|
1108
|
+
|
|
1109
|
+
def _polymake_init_(self):
|
|
1110
|
+
"""
|
|
1111
|
+
Return a polymake "Polytope" object corresponding to ``self``.
|
|
1112
|
+
|
|
1113
|
+
EXAMPLES::
|
|
1114
|
+
|
|
1115
|
+
sage: P = polytopes.cube()
|
|
1116
|
+
sage: PP = polymake(P) # optional - jupymake
|
|
1117
|
+
sage: PP.N_VERTICES # optional - jupymake
|
|
1118
|
+
8
|
|
1119
|
+
|
|
1120
|
+
Lower-dimensional polyhedron::
|
|
1121
|
+
|
|
1122
|
+
sage: P = Polyhedron(vertices=[[1, 0], [0, 1]])
|
|
1123
|
+
sage: PP = polymake(P) # optional - jupymake
|
|
1124
|
+
sage: PP.COMBINATORIAL_DIM # optional - jupymake
|
|
1125
|
+
1
|
|
1126
|
+
sage: PP.AFFINE_HULL # optional - jupymake
|
|
1127
|
+
-1 1 1
|
|
1128
|
+
|
|
1129
|
+
Empty polyhedron::
|
|
1130
|
+
|
|
1131
|
+
sage: P = Polyhedron(ambient_dim=2, vertices=[])
|
|
1132
|
+
sage: PP = polymake(P) # optional - jupymake
|
|
1133
|
+
sage: PP.COMBINATORIAL_DIM # optional - jupymake
|
|
1134
|
+
-1
|
|
1135
|
+
|
|
1136
|
+
Pointed unbounded polyhedron::
|
|
1137
|
+
|
|
1138
|
+
sage: P = Polyhedron(vertices=[[1, 0], [0, 1]], rays=[[1, 0]])
|
|
1139
|
+
sage: PP = polymake(P) # optional - jupymake
|
|
1140
|
+
sage: PP.VERTICES # optional - jupymake
|
|
1141
|
+
1 0 1
|
|
1142
|
+
1 1 0
|
|
1143
|
+
0 1 0
|
|
1144
|
+
sage: PP.FACETS # optional - jupymake
|
|
1145
|
+
1 0 -1
|
|
1146
|
+
-1 1 1
|
|
1147
|
+
0 0 1
|
|
1148
|
+
|
|
1149
|
+
Non-pointed polyhedron::
|
|
1150
|
+
|
|
1151
|
+
sage: # optional - jupymake
|
|
1152
|
+
sage: P = Polyhedron(vertices=[[1, 0], [0, 1]], lines=[[1, 0]])
|
|
1153
|
+
sage: PP = polymake(P)
|
|
1154
|
+
sage: PP.VERTICES
|
|
1155
|
+
1 0 1
|
|
1156
|
+
1 0 0
|
|
1157
|
+
sage: PP.FACETS
|
|
1158
|
+
1 0 -1
|
|
1159
|
+
0 0 1
|
|
1160
|
+
sage: PP.LINEALITY_SPACE
|
|
1161
|
+
0 1 0
|
|
1162
|
+
|
|
1163
|
+
Algebraic polyhedron::
|
|
1164
|
+
|
|
1165
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
1166
|
+
sage: P = polytopes.dodecahedron(); P
|
|
1167
|
+
A 3-dimensional polyhedron
|
|
1168
|
+
in (Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
1169
|
+
with sqrt5 = 2.236067977499790?)^3
|
|
1170
|
+
defined as the convex hull of 20 vertices
|
|
1171
|
+
sage: print("Maybe recompile warning"); PP = polymake(P); PP # optional - jupymake
|
|
1172
|
+
Maybe recompile warning...
|
|
1173
|
+
Polytope<QuadraticExtension<Rational>>[...]
|
|
1174
|
+
sage: sorted(PP.VERTICES[:], key=repr)[0] # optional - jupymake
|
|
1175
|
+
1 -1+1r5 -4+2r5 0
|
|
1176
|
+
|
|
1177
|
+
Floating-point polyhedron::
|
|
1178
|
+
|
|
1179
|
+
sage: # optional - jupymake, needs sage.groups
|
|
1180
|
+
sage: P = polytopes.dodecahedron(exact=False); P
|
|
1181
|
+
A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 20 vertices
|
|
1182
|
+
sage: print("There may be a recompilation warning"); PP = polymake(P); PP
|
|
1183
|
+
There may be a recompilation warning...
|
|
1184
|
+
Polytope<Float>[...]
|
|
1185
|
+
sage: sorted(PP.VERTICES[:], key=repr)[0]
|
|
1186
|
+
1 -0.472135955 0 -1.236067978
|
|
1187
|
+
"""
|
|
1188
|
+
from sage.interfaces.polymake import polymake
|
|
1189
|
+
polymake_field = polymake(self.base_ring().fraction_field())
|
|
1190
|
+
polymake_class = "Polytope<{}>".format(polymake_field)
|
|
1191
|
+
if self.is_empty():
|
|
1192
|
+
# Polymake 3.1 cannot enter an empty polyhedron using
|
|
1193
|
+
# FACETS and AFFINE_HULL.
|
|
1194
|
+
# Use corresponding input properties instead.
|
|
1195
|
+
# https://forum.polymake.org/viewtopic.php?f=8&t=545
|
|
1196
|
+
return polymake.new_object(polymake_class,
|
|
1197
|
+
INEQUALITIES=self.inequalities_list(),
|
|
1198
|
+
EQUATIONS=self.equations_list())
|
|
1199
|
+
|
|
1200
|
+
verts_and_rays = [[1] + v for v in self.vertices_list()]
|
|
1201
|
+
verts_and_rays += [[0] + r for r in self.rays_list()]
|
|
1202
|
+
return polymake.new_object(polymake_class,
|
|
1203
|
+
FACETS=self.inequalities_list(),
|
|
1204
|
+
AFFINE_HULL=self.equations_list(),
|
|
1205
|
+
VERTICES=verts_and_rays,
|
|
1206
|
+
LINEALITY_SPACE=[[0] + l for l in self.lines_list()])
|