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,934 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Base class for polyhedra over `\ZZ`
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# ****************************************************************************
|
|
7
|
+
# Copyright (C) 2011-2013 Volker Braun <vbraun.name@gmail.com>
|
|
8
|
+
# 2015 Nathann Cohen
|
|
9
|
+
# 2015 Vincent Delecroix
|
|
10
|
+
# 2017-2018 Frédéric Chapoton
|
|
11
|
+
# 2019 Sophia Elia
|
|
12
|
+
# 2019-2020 Jonathan Kliem
|
|
13
|
+
# 2023 Luze Xu
|
|
14
|
+
# 2023 Matthias Koeppe
|
|
15
|
+
#
|
|
16
|
+
# This program is free software: you can redistribute it and/or modify
|
|
17
|
+
# it under the terms of the GNU General Public License as published by
|
|
18
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
19
|
+
# (at your option) any later version.
|
|
20
|
+
# https://www.gnu.org/licenses/
|
|
21
|
+
# ****************************************************************************
|
|
22
|
+
|
|
23
|
+
from sage.rings.integer_ring import ZZ
|
|
24
|
+
from sage.rings.rational_field import QQ
|
|
25
|
+
from sage.misc.cachefunc import cached_method
|
|
26
|
+
from sage.modules.free_module_element import vector
|
|
27
|
+
from .base_QQ import Polyhedron_QQ
|
|
28
|
+
from sage.arith.misc import gcd
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#########################################################################
|
|
32
|
+
class Polyhedron_ZZ(Polyhedron_QQ):
|
|
33
|
+
r"""
|
|
34
|
+
Base class for Polyhedra over `\ZZ`.
|
|
35
|
+
|
|
36
|
+
TESTS::
|
|
37
|
+
|
|
38
|
+
sage: p = Polyhedron([(0,0)], base_ring=ZZ); p
|
|
39
|
+
A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex
|
|
40
|
+
sage: TestSuite(p).run()
|
|
41
|
+
"""
|
|
42
|
+
_base_ring = ZZ
|
|
43
|
+
|
|
44
|
+
def __getattribute__(self, name):
|
|
45
|
+
r"""
|
|
46
|
+
TESTS:
|
|
47
|
+
|
|
48
|
+
A lattice polytope does not have a Ehrhart quasipolynomial because it
|
|
49
|
+
is always a polynomial::
|
|
50
|
+
|
|
51
|
+
sage: P = polytopes.cube()
|
|
52
|
+
sage: P.__getattribute__(name='ehrhart_quasipolynomial')
|
|
53
|
+
Traceback (most recent call last):
|
|
54
|
+
...
|
|
55
|
+
AttributeError: ehrhart_quasipolynomial
|
|
56
|
+
"""
|
|
57
|
+
if name in ['ehrhart_quasipolynomial']:
|
|
58
|
+
raise AttributeError(name)
|
|
59
|
+
return super().__getattribute__(name)
|
|
60
|
+
|
|
61
|
+
def __dir__(self):
|
|
62
|
+
r"""
|
|
63
|
+
TESTS:
|
|
64
|
+
|
|
65
|
+
Removes the Ehrhart quasipolynomial from the list of methods for the
|
|
66
|
+
lattice polyhedron::
|
|
67
|
+
|
|
68
|
+
sage: P = polytopes.cube()
|
|
69
|
+
sage: 'ehrhart_polynomial' in P.__dir__()
|
|
70
|
+
True
|
|
71
|
+
sage: 'ehrhart_quasipolynomial' in P.__dir__()
|
|
72
|
+
False
|
|
73
|
+
"""
|
|
74
|
+
orig_dir = (set(dir(self.__class__)) | set(self.__dict__.keys()))
|
|
75
|
+
return sorted(orig_dir - set(['ehrhart_quasipolynomial']))
|
|
76
|
+
|
|
77
|
+
def is_lattice_polytope(self):
|
|
78
|
+
r"""
|
|
79
|
+
Return whether the polyhedron is a lattice polytope.
|
|
80
|
+
|
|
81
|
+
OUTPUT:
|
|
82
|
+
|
|
83
|
+
``True`` if the polyhedron is compact and has only integral
|
|
84
|
+
vertices, ``False`` otherwise.
|
|
85
|
+
|
|
86
|
+
EXAMPLES::
|
|
87
|
+
|
|
88
|
+
sage: polytopes.cross_polytope(3).is_lattice_polytope()
|
|
89
|
+
True
|
|
90
|
+
sage: polytopes.regular_polygon(5).is_lattice_polytope() # needs sage.rings.number_field
|
|
91
|
+
False
|
|
92
|
+
|
|
93
|
+
TESTS:
|
|
94
|
+
|
|
95
|
+
Check :issue:`22622`::
|
|
96
|
+
|
|
97
|
+
sage: P1 = Polyhedron(vertices = [[1, 0], [0, 1]], rays = [[1, 1]])
|
|
98
|
+
sage: P1.is_lattice_polytope()
|
|
99
|
+
False
|
|
100
|
+
"""
|
|
101
|
+
return self.is_compact()
|
|
102
|
+
|
|
103
|
+
def _ehrhart_polynomial_latte(self, verbose=False, dual=None,
|
|
104
|
+
irrational_primal=None, irrational_all_primal=None, maxdet=None,
|
|
105
|
+
no_decomposition=None, compute_vertex_cones=None, smith_form=None,
|
|
106
|
+
dualization=None, triangulation=None, triangulation_max_height=None,
|
|
107
|
+
**kwds):
|
|
108
|
+
r"""
|
|
109
|
+
Return the Ehrhart polynomial of this polyhedron using LattE integrale.
|
|
110
|
+
|
|
111
|
+
Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
|
|
112
|
+
\cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
|
|
113
|
+
rational polynomial of degree `d` for integer `t`. `L` is called the
|
|
114
|
+
*Ehrhart polynomial* of `P`. For more information see the
|
|
115
|
+
:wikipedia:`Ehrhart_polynomial`.
|
|
116
|
+
|
|
117
|
+
INPUT:
|
|
118
|
+
|
|
119
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, print the
|
|
120
|
+
whole output of the LattE command
|
|
121
|
+
|
|
122
|
+
The following options are passed to the LattE command, for details you
|
|
123
|
+
should consult `the LattE documentation
|
|
124
|
+
<https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:
|
|
125
|
+
|
|
126
|
+
- ``dual`` -- boolean; triangulate and signed-decompose in the dual
|
|
127
|
+
space
|
|
128
|
+
|
|
129
|
+
- ``irrational_primal`` -- boolean; triangulate in the dual space,
|
|
130
|
+
signed-decompose in the primal space using irrationalization
|
|
131
|
+
|
|
132
|
+
- ``irrational_all_primal`` -- boolean; triangulate and signed-decompose
|
|
133
|
+
in the primal space using irrationalization
|
|
134
|
+
|
|
135
|
+
- ``maxdet`` -- integer; decompose down to an index (determinant) of
|
|
136
|
+
``maxdet`` instead of index 1 (unimodular cones)
|
|
137
|
+
|
|
138
|
+
- ``no_decomposition`` -- boolean; do not signed-decompose simplicial cones
|
|
139
|
+
|
|
140
|
+
- ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
|
|
141
|
+
|
|
142
|
+
- ``smith_form`` -- string; either 'ilio' or 'lidia'
|
|
143
|
+
|
|
144
|
+
- ``dualization`` -- string; either 'cdd' or '4ti2'
|
|
145
|
+
|
|
146
|
+
- ``triangulation`` -- string; 'cddlib', '4ti2' or 'topcom'
|
|
147
|
+
|
|
148
|
+
- ``triangulation_max_height`` -- integer; use a uniform distribution of
|
|
149
|
+
height from 1 to this number
|
|
150
|
+
|
|
151
|
+
.. NOTE::
|
|
152
|
+
|
|
153
|
+
Any additional argument is forwarded to LattE's executable
|
|
154
|
+
``count``. All occurrences of '_' will be replaced with a '-'.
|
|
155
|
+
|
|
156
|
+
ALGORITHM:
|
|
157
|
+
|
|
158
|
+
This method calls the program ``count`` from LattE integrale, a program
|
|
159
|
+
for lattice point enumeration (see
|
|
160
|
+
https://www.math.ucdavis.edu/~latte/).
|
|
161
|
+
|
|
162
|
+
.. SEEALSO::
|
|
163
|
+
|
|
164
|
+
:mod:`~sage.interfaces.latte` the interface to LattE integrale
|
|
165
|
+
|
|
166
|
+
EXAMPLES::
|
|
167
|
+
|
|
168
|
+
sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
|
|
169
|
+
sage: p = P._ehrhart_polynomial_latte() # optional - latte_int
|
|
170
|
+
sage: p # optional - latte_int
|
|
171
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
172
|
+
sage: p(1) # optional - latte_int
|
|
173
|
+
6
|
|
174
|
+
sage: len(P.integral_points())
|
|
175
|
+
6
|
|
176
|
+
sage: p(2) # optional - latte_int
|
|
177
|
+
36
|
|
178
|
+
sage: len((2*P).integral_points())
|
|
179
|
+
36
|
|
180
|
+
|
|
181
|
+
The unit hypercubes::
|
|
182
|
+
|
|
183
|
+
sage: # optional - latte_int
|
|
184
|
+
sage: from itertools import product
|
|
185
|
+
sage: def hypercube(d):
|
|
186
|
+
....: return Polyhedron(vertices=list(product([0,1],repeat=d)))
|
|
187
|
+
sage: hypercube(3)._ehrhart_polynomial_latte()
|
|
188
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
189
|
+
sage: hypercube(4)._ehrhart_polynomial_latte()
|
|
190
|
+
t^4 + 4*t^3 + 6*t^2 + 4*t + 1
|
|
191
|
+
sage: hypercube(5)._ehrhart_polynomial_latte()
|
|
192
|
+
t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
|
|
193
|
+
sage: hypercube(6)._ehrhart_polynomial_latte()
|
|
194
|
+
t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1
|
|
195
|
+
|
|
196
|
+
TESTS:
|
|
197
|
+
|
|
198
|
+
Test options::
|
|
199
|
+
|
|
200
|
+
sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)
|
|
201
|
+
|
|
202
|
+
sage: p = P._ehrhart_polynomial_latte(maxdet=5, verbose=True) # optional - latte_int
|
|
203
|
+
This is LattE integrale ...
|
|
204
|
+
...
|
|
205
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin
|
|
206
|
+
...
|
|
207
|
+
sage: p # optional - latte_int
|
|
208
|
+
1/2*t^2 + 3/2*t + 1
|
|
209
|
+
|
|
210
|
+
sage: p = P._ehrhart_polynomial_latte(dual=True, verbose=True) # optional - latte_int
|
|
211
|
+
This is LattE integrale ...
|
|
212
|
+
...
|
|
213
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin
|
|
214
|
+
...
|
|
215
|
+
sage: p # optional - latte_int
|
|
216
|
+
1/2*t^2 + 3/2*t + 1
|
|
217
|
+
|
|
218
|
+
sage: p = P._ehrhart_polynomial_latte(irrational_primal=True, verbose=True) # optional - latte_int
|
|
219
|
+
This is LattE integrale ...
|
|
220
|
+
...
|
|
221
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin
|
|
222
|
+
...
|
|
223
|
+
sage: p # optional - latte_int
|
|
224
|
+
1/2*t^2 + 3/2*t + 1
|
|
225
|
+
|
|
226
|
+
sage: p = P._ehrhart_polynomial_latte(irrational_all_primal=True, verbose=True) # optional - latte_int
|
|
227
|
+
This is LattE integrale ...
|
|
228
|
+
...
|
|
229
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin
|
|
230
|
+
...
|
|
231
|
+
sage: p # optional - latte_int
|
|
232
|
+
1/2*t^2 + 3/2*t + 1
|
|
233
|
+
|
|
234
|
+
Test bad options::
|
|
235
|
+
|
|
236
|
+
sage: P._ehrhart_polynomial_latte(bim_bam_boum=19) # optional - latte_int
|
|
237
|
+
Traceback (most recent call last):
|
|
238
|
+
...
|
|
239
|
+
RuntimeError: LattE integrale program failed (exit code 1):
|
|
240
|
+
...
|
|
241
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin
|
|
242
|
+
Unknown command/option --bim-bam-boum=19
|
|
243
|
+
"""
|
|
244
|
+
# note: the options below are explicitly written in the function
|
|
245
|
+
# declaration in order to keep tab completion (see #18211).
|
|
246
|
+
kwds.update({
|
|
247
|
+
'dual' : dual,
|
|
248
|
+
'irrational_primal' : irrational_primal,
|
|
249
|
+
'irrational_all_primal' : irrational_all_primal,
|
|
250
|
+
'maxdet' : maxdet,
|
|
251
|
+
'no_decomposition' : no_decomposition,
|
|
252
|
+
'compute_vertex_cones' : compute_vertex_cones,
|
|
253
|
+
'smith_form' : smith_form,
|
|
254
|
+
'dualization' : dualization,
|
|
255
|
+
'triangulation' : triangulation,
|
|
256
|
+
'triangulation_max_height': triangulation_max_height})
|
|
257
|
+
|
|
258
|
+
from sage.interfaces.latte import count
|
|
259
|
+
ine = self.cdd_Hrepresentation()
|
|
260
|
+
return count(ine, cdd=True, ehrhart_polynomial=True, verbose=verbose, **kwds)
|
|
261
|
+
|
|
262
|
+
def _ehrhart_polynomial_normaliz(self, variable='t'):
|
|
263
|
+
r"""
|
|
264
|
+
Compute the Ehrhart polynomial of a lattice polytope using Normaliz.
|
|
265
|
+
|
|
266
|
+
The backend of ``self`` must be 'normaliz'.
|
|
267
|
+
|
|
268
|
+
INPUT:
|
|
269
|
+
|
|
270
|
+
- ``variable`` -- string (default: ``'t'``); the variable in which the
|
|
271
|
+
Ehrhart polynomial is expressed
|
|
272
|
+
|
|
273
|
+
OUTPUT: a univariate polynomial over a rational field
|
|
274
|
+
|
|
275
|
+
EXAMPLES::
|
|
276
|
+
|
|
277
|
+
sage: c = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]],backend='normaliz') # optional - pynormaliz
|
|
278
|
+
sage: c._ehrhart_polynomial_normaliz() # optional - pynormaliz
|
|
279
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
280
|
+
|
|
281
|
+
Changing the variable works::
|
|
282
|
+
|
|
283
|
+
sage: c._ehrhart_polynomial_normaliz(variable='k') # optional - pynormaliz
|
|
284
|
+
k^3 + 3*k^2 + 3*k + 1
|
|
285
|
+
|
|
286
|
+
TESTS:
|
|
287
|
+
|
|
288
|
+
Receive a type error if the backend is not normaliz::
|
|
289
|
+
|
|
290
|
+
sage: c = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]])
|
|
291
|
+
sage: c._ehrhart_polynomial_normaliz()
|
|
292
|
+
Traceback (most recent call last):
|
|
293
|
+
...
|
|
294
|
+
TypeError: The polyhedron's backend should be 'normaliz'
|
|
295
|
+
"""
|
|
296
|
+
raise TypeError("The polyhedron's backend should be 'normaliz'")
|
|
297
|
+
|
|
298
|
+
@cached_method(do_pickle=True)
|
|
299
|
+
def ehrhart_polynomial(self, engine=None, variable='t', verbose=False,
|
|
300
|
+
dual=None, irrational_primal=None,
|
|
301
|
+
irrational_all_primal=None, maxdet=None,
|
|
302
|
+
no_decomposition=None, compute_vertex_cones=None,
|
|
303
|
+
smith_form=None, dualization=None,
|
|
304
|
+
triangulation=None, triangulation_max_height=None,
|
|
305
|
+
**kwds):
|
|
306
|
+
r"""
|
|
307
|
+
Return the Ehrhart polynomial of this polyhedron.
|
|
308
|
+
|
|
309
|
+
Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
|
|
310
|
+
\cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
|
|
311
|
+
rational polynomial of degree `d` for integer `t`. `L` is called the
|
|
312
|
+
*Ehrhart polynomial* of `P`. For more information see the
|
|
313
|
+
:wikipedia:`Ehrhart_polynomial`.
|
|
314
|
+
|
|
315
|
+
The Ehrhart polynomial may be computed using either LattE Integrale
|
|
316
|
+
or Normaliz by setting ``engine`` to 'latte' or 'normaliz' respectively.
|
|
317
|
+
|
|
318
|
+
INPUT:
|
|
319
|
+
|
|
320
|
+
- ``engine`` -- string; the backend to use. Allowed values are:
|
|
321
|
+
|
|
322
|
+
* ``None`` (default); When no input is given the Ehrhart polynomial
|
|
323
|
+
is computed using LattE Integrale (optional)
|
|
324
|
+
* ``'latte'``; use LattE integrale program (optional)
|
|
325
|
+
* ``'normaliz'``; use Normaliz program (optional). The backend of
|
|
326
|
+
``self`` must be set to 'normaliz'.
|
|
327
|
+
|
|
328
|
+
- ``variable`` -- string (default: ``'t'``); the variable in which the
|
|
329
|
+
Ehrhart polynomial should be expressed
|
|
330
|
+
|
|
331
|
+
- When the ``engine`` is 'latte' or None, the additional input values are:
|
|
332
|
+
|
|
333
|
+
* ``verbose`` -- boolean (default: ``False``); if ``True``, print the
|
|
334
|
+
whole output of the LattE command.
|
|
335
|
+
|
|
336
|
+
The following options are passed to the LattE command, for details
|
|
337
|
+
consult `the LattE documentation
|
|
338
|
+
<https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:
|
|
339
|
+
|
|
340
|
+
* ``dual`` -- boolean; triangulate and signed-decompose in the dual
|
|
341
|
+
space
|
|
342
|
+
* ``irrational_primal`` -- boolean; triangulate in the dual space,
|
|
343
|
+
signed-decompose in the primal space using irrationalization.
|
|
344
|
+
* ``irrational_all_primal`` -- boolean; triangulate and signed-decompose
|
|
345
|
+
in the primal space using irrationalization.
|
|
346
|
+
* ``maxdet`` -- integer; decompose down to an index (determinant) of
|
|
347
|
+
``maxdet`` instead of index 1 (unimodular cones).
|
|
348
|
+
* ``no_decomposition`` -- boolean; do not signed-decompose
|
|
349
|
+
simplicial cones.
|
|
350
|
+
* ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
|
|
351
|
+
* ``smith_form`` -- string; either 'ilio' or 'lidia'
|
|
352
|
+
* ``dualization`` -- string; either 'cdd' or '4ti2'
|
|
353
|
+
* ``triangulation`` -- string; 'cddlib', '4ti2' or 'topcom'
|
|
354
|
+
* ``triangulation_max_height`` -- integer; use a uniform distribution of
|
|
355
|
+
height from 1 to this number
|
|
356
|
+
|
|
357
|
+
OUTPUT:
|
|
358
|
+
|
|
359
|
+
The Ehrhart polynomial as a univariate polynomial in ``variable``
|
|
360
|
+
over a rational field.
|
|
361
|
+
|
|
362
|
+
.. SEEALSO::
|
|
363
|
+
|
|
364
|
+
:mod:`~sage.interfaces.latte` the interface to LattE Integrale
|
|
365
|
+
`PyNormaliz <https://pypi.org/project/PyNormaliz>`_
|
|
366
|
+
|
|
367
|
+
EXAMPLES:
|
|
368
|
+
|
|
369
|
+
To start, we find the Ehrhart polynomial of a three-dimensional
|
|
370
|
+
``simplex``, first using ``engine='latte'``. Leaving the engine
|
|
371
|
+
unspecified sets the ``engine`` to 'latte' by default::
|
|
372
|
+
|
|
373
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
|
|
374
|
+
sage: poly = simplex.ehrhart_polynomial(engine = 'latte') # optional - latte_int
|
|
375
|
+
sage: poly # optional - latte_int
|
|
376
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
377
|
+
sage: poly(1) # optional - latte_int
|
|
378
|
+
6
|
|
379
|
+
sage: len(simplex.integral_points())
|
|
380
|
+
6
|
|
381
|
+
sage: poly(2) # optional - latte_int
|
|
382
|
+
36
|
|
383
|
+
sage: len((2*simplex).integral_points())
|
|
384
|
+
36
|
|
385
|
+
|
|
386
|
+
Now we find the same Ehrhart polynomial, this time using
|
|
387
|
+
``engine='normaliz'``. To use the Normaliz engine, the ``simplex`` must
|
|
388
|
+
be defined with ``backend='normaliz'``::
|
|
389
|
+
|
|
390
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz
|
|
391
|
+
sage: poly = simplex.ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
|
|
392
|
+
sage: poly # optional - pynormaliz
|
|
393
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
394
|
+
|
|
395
|
+
If the ``engine='normaliz'``, the backend should be ``'normaliz'``, otherwise
|
|
396
|
+
it returns an error::
|
|
397
|
+
|
|
398
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
|
|
399
|
+
sage: simplex.ehrhart_polynomial(engine='normaliz')
|
|
400
|
+
Traceback (most recent call last):
|
|
401
|
+
...
|
|
402
|
+
TypeError: The polyhedron's backend should be 'normaliz'
|
|
403
|
+
|
|
404
|
+
Now we find the Ehrhart polynomials of the unit hypercubes of
|
|
405
|
+
dimensions three through six. They are computed first with
|
|
406
|
+
``engine='latte'`` and then with ``engine='normaliz'``.
|
|
407
|
+
The degree of the Ehrhart polynomial matches the dimension of the
|
|
408
|
+
hypercube, and the coefficient of the leading monomial equals the
|
|
409
|
+
volume of the unit hypercube::
|
|
410
|
+
|
|
411
|
+
sage: # optional - latte_int
|
|
412
|
+
sage: from itertools import product
|
|
413
|
+
sage: def hypercube(d):
|
|
414
|
+
....: return Polyhedron(vertices=list(product([0,1],repeat=d)))
|
|
415
|
+
sage: hypercube(3).ehrhart_polynomial()
|
|
416
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
417
|
+
sage: hypercube(4).ehrhart_polynomial()
|
|
418
|
+
t^4 + 4*t^3 + 6*t^2 + 4*t + 1
|
|
419
|
+
sage: hypercube(5).ehrhart_polynomial()
|
|
420
|
+
t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
|
|
421
|
+
sage: hypercube(6).ehrhart_polynomial()
|
|
422
|
+
t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1
|
|
423
|
+
|
|
424
|
+
sage: # optional - pynormaliz
|
|
425
|
+
sage: from itertools import product
|
|
426
|
+
sage: def hypercube(d):
|
|
427
|
+
....: return Polyhedron(vertices=list(product([0,1],repeat=d)),backend='normaliz')
|
|
428
|
+
sage: hypercube(3).ehrhart_polynomial(engine='normaliz')
|
|
429
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
430
|
+
sage: hypercube(4).ehrhart_polynomial(engine='normaliz')
|
|
431
|
+
t^4 + 4*t^3 + 6*t^2 + 4*t + 1
|
|
432
|
+
sage: hypercube(5).ehrhart_polynomial(engine='normaliz')
|
|
433
|
+
t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
|
|
434
|
+
sage: hypercube(6).ehrhart_polynomial(engine='normaliz')
|
|
435
|
+
t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1
|
|
436
|
+
|
|
437
|
+
An empty polyhedron::
|
|
438
|
+
|
|
439
|
+
sage: p = Polyhedron(ambient_dim=3, vertices=[])
|
|
440
|
+
sage: p.ehrhart_polynomial()
|
|
441
|
+
0
|
|
442
|
+
sage: parent(_)
|
|
443
|
+
Univariate Polynomial Ring in t over Rational Field
|
|
444
|
+
|
|
445
|
+
The polyhedron should be compact::
|
|
446
|
+
|
|
447
|
+
sage: C = Polyhedron(rays=[[1,2],[2,1]])
|
|
448
|
+
sage: C.ehrhart_polynomial()
|
|
449
|
+
Traceback (most recent call last):
|
|
450
|
+
...
|
|
451
|
+
ValueError: Ehrhart polynomial only defined for compact polyhedra
|
|
452
|
+
|
|
453
|
+
TESTS:
|
|
454
|
+
|
|
455
|
+
The cache of the Ehrhart polynomial is being pickled::
|
|
456
|
+
|
|
457
|
+
sage: P = polytopes.cube()
|
|
458
|
+
sage: P.ehrhart_polynomial() # optional - latte_int
|
|
459
|
+
8*t^3 + 12*t^2 + 6*t + 1
|
|
460
|
+
sage: Q = loads(dumps(P))
|
|
461
|
+
sage: Q.ehrhart_polynomial.is_in_cache() # optional - latte_int
|
|
462
|
+
True
|
|
463
|
+
"""
|
|
464
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
465
|
+
from sage.rings.rational_field import QQ
|
|
466
|
+
R = PolynomialRing(QQ, variable)
|
|
467
|
+
|
|
468
|
+
if self.is_empty():
|
|
469
|
+
return R.zero()
|
|
470
|
+
|
|
471
|
+
if self.dimension() == 0:
|
|
472
|
+
return R.one()
|
|
473
|
+
|
|
474
|
+
if not self.is_compact():
|
|
475
|
+
raise ValueError("Ehrhart polynomial only defined for compact polyhedra")
|
|
476
|
+
|
|
477
|
+
if engine is None:
|
|
478
|
+
# setting the default to 'latte'
|
|
479
|
+
engine = 'latte'
|
|
480
|
+
if engine == 'latte':
|
|
481
|
+
poly = self._ehrhart_polynomial_latte(verbose, dual,
|
|
482
|
+
irrational_primal, irrational_all_primal, maxdet,
|
|
483
|
+
no_decomposition, compute_vertex_cones, smith_form,
|
|
484
|
+
dualization, triangulation, triangulation_max_height,
|
|
485
|
+
**kwds)
|
|
486
|
+
return poly.change_variable_name(variable)
|
|
487
|
+
# TO DO: replace this change of variable by creating the appropriate
|
|
488
|
+
# polynomial ring in the latte interface.
|
|
489
|
+
|
|
490
|
+
elif engine == 'normaliz':
|
|
491
|
+
return self._ehrhart_polynomial_normaliz(variable)
|
|
492
|
+
else:
|
|
493
|
+
raise ValueError("engine must be 'latte' or 'normaliz'")
|
|
494
|
+
|
|
495
|
+
@cached_method
|
|
496
|
+
def polar(self):
|
|
497
|
+
"""
|
|
498
|
+
Return the polar (dual) polytope.
|
|
499
|
+
|
|
500
|
+
The polytope must have the IP-property (see
|
|
501
|
+
:meth:`has_IP_property`), that is, the origin must be an
|
|
502
|
+
interior point. In particular, it must be full-dimensional.
|
|
503
|
+
|
|
504
|
+
OUTPUT:
|
|
505
|
+
|
|
506
|
+
The polytope whose vertices are the coefficient vectors of the
|
|
507
|
+
inequalities of ``self`` with inhomogeneous term normalized to
|
|
508
|
+
unity.
|
|
509
|
+
|
|
510
|
+
EXAMPLES::
|
|
511
|
+
|
|
512
|
+
sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
|
|
513
|
+
sage: p.polar()
|
|
514
|
+
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices
|
|
515
|
+
sage: type(_)
|
|
516
|
+
<class 'sage.geometry.polyhedron.parent.Polyhedra_ZZ_ppl_with_category.element_class'>
|
|
517
|
+
sage: p.polar().base_ring()
|
|
518
|
+
Integer Ring
|
|
519
|
+
|
|
520
|
+
TESTS:
|
|
521
|
+
|
|
522
|
+
Test that :issue:`28551` is fixed::
|
|
523
|
+
|
|
524
|
+
sage: polytopes.cube(backend='normaliz').polar().backend() # optional - pynormaliz
|
|
525
|
+
'normaliz'
|
|
526
|
+
"""
|
|
527
|
+
if not self.has_IP_property():
|
|
528
|
+
raise ValueError('The polytope must have the IP property.')
|
|
529
|
+
|
|
530
|
+
vertices = tuple(ieq.A() / ieq.b() for
|
|
531
|
+
ieq in self.inequality_generator())
|
|
532
|
+
|
|
533
|
+
ieqs = ((1,) + tuple(v[:]) for v in self.vertices())
|
|
534
|
+
|
|
535
|
+
pref_rep = 'Hrep' if self.n_vertices() <= self.n_inequalities() else 'Vrep'
|
|
536
|
+
|
|
537
|
+
if all(v_i in ZZ for v in vertices for v_i in v):
|
|
538
|
+
parent = self.parent()
|
|
539
|
+
vertices = (v.change_ring(ZZ) for v in vertices)
|
|
540
|
+
else:
|
|
541
|
+
parent = self.parent().change_ring(QQ)
|
|
542
|
+
|
|
543
|
+
return parent.element_class(parent, [vertices, [], []], [ieqs, []],
|
|
544
|
+
Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep)
|
|
545
|
+
|
|
546
|
+
@cached_method
|
|
547
|
+
def is_reflexive(self):
|
|
548
|
+
r"""
|
|
549
|
+
A lattice polytope is reflexive if it contains the origin in its interior
|
|
550
|
+
and its polar with respect to the origin is a lattice polytope.
|
|
551
|
+
|
|
552
|
+
Equivalently, it is reflexive if it is of the form `\{x \in \mathbb{R}^d: Ax \leq 1\}`
|
|
553
|
+
for some integer matrix `A` and `d` the ambient dimension.
|
|
554
|
+
|
|
555
|
+
EXAMPLES::
|
|
556
|
+
|
|
557
|
+
sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
|
|
558
|
+
sage: p.is_reflexive()
|
|
559
|
+
True
|
|
560
|
+
sage: polytopes.hypercube(4).is_reflexive()
|
|
561
|
+
True
|
|
562
|
+
sage: p = Polyhedron(vertices=[(1,0), (0,2), (-1,0), (0,-1)], base_ring=ZZ)
|
|
563
|
+
sage: p.is_reflexive()
|
|
564
|
+
False
|
|
565
|
+
sage: p = Polyhedron(vertices=[(1,0), (0,2), (-1,0)], base_ring=ZZ)
|
|
566
|
+
sage: p.is_reflexive()
|
|
567
|
+
False
|
|
568
|
+
|
|
569
|
+
An error is raised, if the polyhedron is not compact::
|
|
570
|
+
|
|
571
|
+
sage: p = Polyhedron(rays=[(1,)], base_ring=ZZ)
|
|
572
|
+
sage: p.is_reflexive()
|
|
573
|
+
Traceback (most recent call last):
|
|
574
|
+
...
|
|
575
|
+
ValueError: the polyhedron is not compact
|
|
576
|
+
"""
|
|
577
|
+
if not self.is_compact():
|
|
578
|
+
raise ValueError("the polyhedron is not compact")
|
|
579
|
+
|
|
580
|
+
for H in self.Hrepresentation():
|
|
581
|
+
if H.is_equation():
|
|
582
|
+
return False
|
|
583
|
+
b = H.b()
|
|
584
|
+
if b < 1:
|
|
585
|
+
return False
|
|
586
|
+
if not all(v_i/b in ZZ for v_i in H.A()):
|
|
587
|
+
return False
|
|
588
|
+
|
|
589
|
+
return True
|
|
590
|
+
|
|
591
|
+
@cached_method
|
|
592
|
+
def has_IP_property(self) -> bool:
|
|
593
|
+
"""
|
|
594
|
+
Test whether the polyhedron has the IP property.
|
|
595
|
+
|
|
596
|
+
The IP (interior point) property means that
|
|
597
|
+
|
|
598
|
+
* ``self`` is compact (a polytope).
|
|
599
|
+
|
|
600
|
+
* ``self`` contains the origin as an interior point.
|
|
601
|
+
|
|
602
|
+
This implies that
|
|
603
|
+
|
|
604
|
+
* ``self`` is full-dimensional.
|
|
605
|
+
|
|
606
|
+
* The dual polyhedron is again a polytope (that is, a compact
|
|
607
|
+
polyhedron), though not necessarily a lattice polytope.
|
|
608
|
+
|
|
609
|
+
EXAMPLES::
|
|
610
|
+
|
|
611
|
+
sage: Polyhedron([(1,1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
|
|
612
|
+
False
|
|
613
|
+
sage: Polyhedron([(0,0),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
|
|
614
|
+
False
|
|
615
|
+
sage: Polyhedron([(-1,-1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
|
|
616
|
+
True
|
|
617
|
+
|
|
618
|
+
REFERENCES:
|
|
619
|
+
|
|
620
|
+
- [PALP]_
|
|
621
|
+
"""
|
|
622
|
+
return self.is_compact() and self.interior_contains(self.ambient_space().zero())
|
|
623
|
+
|
|
624
|
+
def fibration_generator(self, dim):
|
|
625
|
+
"""
|
|
626
|
+
Generate the lattice polytope fibrations.
|
|
627
|
+
|
|
628
|
+
For the purposes of this function, a lattice polytope fiber is
|
|
629
|
+
a sub-lattice polytope. Projecting the plane spanned by the
|
|
630
|
+
subpolytope to a point yields another lattice polytope, the
|
|
631
|
+
base of the fibration.
|
|
632
|
+
|
|
633
|
+
INPUT:
|
|
634
|
+
|
|
635
|
+
- ``dim`` -- integer; the dimension of the lattice polytope
|
|
636
|
+
fiber
|
|
637
|
+
|
|
638
|
+
OUTPUT:
|
|
639
|
+
|
|
640
|
+
A generator yielding the distinct lattice polytope fibers of
|
|
641
|
+
given dimension.
|
|
642
|
+
|
|
643
|
+
EXAMPLES::
|
|
644
|
+
|
|
645
|
+
sage: P = Polyhedron(toric_varieties.P4_11169().fan().rays(), base_ring=ZZ) # needs palp sage.graphs
|
|
646
|
+
sage: list(P.fibration_generator(2)) # needs palp sage.graphs
|
|
647
|
+
[A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices]
|
|
648
|
+
"""
|
|
649
|
+
from sage.combinat.combination import Combinations
|
|
650
|
+
if not self.is_compact():
|
|
651
|
+
raise ValueError('Only polytopes (compact polyhedra) are allowed.')
|
|
652
|
+
|
|
653
|
+
nonzero_points = [p for p in self.integral_points() if not p.is_zero()]
|
|
654
|
+
origin = [[0]*self.ambient_dim()]
|
|
655
|
+
fibers = set()
|
|
656
|
+
parent = self.parent()
|
|
657
|
+
|
|
658
|
+
for points in Combinations(nonzero_points, dim):
|
|
659
|
+
plane = parent.element_class(parent, [origin,[],points], None)
|
|
660
|
+
if plane.dim() != dim:
|
|
661
|
+
continue
|
|
662
|
+
fiber = self.intersection(plane)
|
|
663
|
+
if fiber.base_ring() is not ZZ:
|
|
664
|
+
continue
|
|
665
|
+
fiber_vertices = tuple(sorted(tuple(v) for v in fiber.vertex_generator()))
|
|
666
|
+
if fiber_vertices not in fibers:
|
|
667
|
+
yield fiber
|
|
668
|
+
fibers.update([fiber_vertices])
|
|
669
|
+
plane._delete()
|
|
670
|
+
|
|
671
|
+
def find_translation(self, translated_polyhedron):
|
|
672
|
+
r"""
|
|
673
|
+
Return the translation vector to ``translated_polyhedron``.
|
|
674
|
+
|
|
675
|
+
INPUT:
|
|
676
|
+
|
|
677
|
+
- ``translated_polyhedron`` -- a polyhedron
|
|
678
|
+
|
|
679
|
+
OUTPUT:
|
|
680
|
+
|
|
681
|
+
A `\ZZ`-vector that translates ``self`` to
|
|
682
|
+
``translated_polyhedron``. A :exc:`ValueError` is raised if
|
|
683
|
+
``translated_polyhedron`` is not a translation of ``self``,
|
|
684
|
+
this can be used to check that two polyhedra are not
|
|
685
|
+
translates of each other.
|
|
686
|
+
|
|
687
|
+
EXAMPLES::
|
|
688
|
+
|
|
689
|
+
sage: X = polytopes.cube()
|
|
690
|
+
sage: X.find_translation(X + vector([2,3,5]))
|
|
691
|
+
(2, 3, 5)
|
|
692
|
+
sage: X.find_translation(2*X)
|
|
693
|
+
Traceback (most recent call last):
|
|
694
|
+
...
|
|
695
|
+
ValueError: polyhedron is not a translation of self
|
|
696
|
+
"""
|
|
697
|
+
no_translation_exception = ValueError('polyhedron is not a translation of self')
|
|
698
|
+
if ( set(self.rays()) != set(translated_polyhedron.rays()) or
|
|
699
|
+
set(self.lines()) != set(translated_polyhedron.lines()) or
|
|
700
|
+
self.n_vertices() != translated_polyhedron.n_vertices() ):
|
|
701
|
+
raise no_translation_exception
|
|
702
|
+
sorted_vertices = sorted(map(vector, self.vertices()))
|
|
703
|
+
sorted_translated_vertices = sorted(map(vector, translated_polyhedron.vertices()))
|
|
704
|
+
v = sorted_translated_vertices[0] - sorted_vertices[0]
|
|
705
|
+
if any(vertex+v != translated_vertex
|
|
706
|
+
for vertex, translated_vertex in zip(sorted_vertices, sorted_translated_vertices)):
|
|
707
|
+
raise no_translation_exception
|
|
708
|
+
return v
|
|
709
|
+
|
|
710
|
+
def _subpoly_parallel_facets(self):
|
|
711
|
+
r"""
|
|
712
|
+
Generator for all lattice sub-polyhedra with parallel facets.
|
|
713
|
+
|
|
714
|
+
In a sub-polyhedron `Y\subset X` not all edges of `Y` need to
|
|
715
|
+
be parallel to `X`. This method iterates over all
|
|
716
|
+
sub-polyhedra where they are parallel, up to an overall
|
|
717
|
+
translation of the sub-polyhedron. Degenerate sub-polyhedra of
|
|
718
|
+
dimension strictly smaller are included.
|
|
719
|
+
|
|
720
|
+
OUTPUT:
|
|
721
|
+
|
|
722
|
+
A generator yielding `\ZZ`-polyhedra. By construction, each
|
|
723
|
+
facet of the returned polyhedron is parallel to one of the
|
|
724
|
+
facets of ``self``.
|
|
725
|
+
|
|
726
|
+
EXAMPLES::
|
|
727
|
+
|
|
728
|
+
sage: X = Polyhedron(vertices=[(0,0), (0,1), (1,0), (1,1)])
|
|
729
|
+
sage: X._subpoly_parallel_facets()
|
|
730
|
+
<generator object ..._subpoly_parallel_facets at 0x...>
|
|
731
|
+
sage: for p in X._subpoly_parallel_facets():
|
|
732
|
+
....: print(p.Vrepresentation())
|
|
733
|
+
(A vertex at (0, 0),)
|
|
734
|
+
(A vertex at (0, -1), A vertex at (0, 0))
|
|
735
|
+
(A vertex at (-1, 0), A vertex at (0, 0))
|
|
736
|
+
(A vertex at (-1, -1), A vertex at (-1, 0), A vertex at (0, -1), A vertex at (0, 0))
|
|
737
|
+
|
|
738
|
+
TESTS::
|
|
739
|
+
|
|
740
|
+
sage: X = Polyhedron(vertices=[(0,), (3,)])
|
|
741
|
+
sage: [ p.vertices() for p in X._subpoly_parallel_facets() ]
|
|
742
|
+
[(A vertex at (0),),
|
|
743
|
+
(A vertex at (-1), A vertex at (0)),
|
|
744
|
+
(A vertex at (-2), A vertex at (0)),
|
|
745
|
+
(A vertex at (-3), A vertex at (0))]
|
|
746
|
+
sage: list( Polyhedron(vertices=[[0,0]])._subpoly_parallel_facets() )
|
|
747
|
+
[A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex]
|
|
748
|
+
sage: list( Polyhedron()._subpoly_parallel_facets() )
|
|
749
|
+
[The empty polyhedron in ZZ^0]
|
|
750
|
+
"""
|
|
751
|
+
if self.dim() > 2 or not self.is_compact():
|
|
752
|
+
raise NotImplementedError('only implemented for bounded polygons')
|
|
753
|
+
from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d
|
|
754
|
+
vertices = cyclic_sort_vertices_2d(self.vertices())
|
|
755
|
+
n = len(vertices)
|
|
756
|
+
if n == 1: # single point
|
|
757
|
+
yield self
|
|
758
|
+
return
|
|
759
|
+
edge_vectors = []
|
|
760
|
+
for i in range(n):
|
|
761
|
+
v = vertices[(i+1) % n].vector() - vertices[i].vector()
|
|
762
|
+
d = gcd(list(v))
|
|
763
|
+
v_prim = (v/d).change_ring(ZZ)
|
|
764
|
+
edge_vectors.append([ v_prim*i for i in range(d+1) ])
|
|
765
|
+
origin = self.ambient_space().zero()
|
|
766
|
+
parent = self.parent()
|
|
767
|
+
from itertools import product
|
|
768
|
+
for edges in product(*edge_vectors):
|
|
769
|
+
v = []
|
|
770
|
+
point = origin
|
|
771
|
+
for e in edges:
|
|
772
|
+
point += e
|
|
773
|
+
v.append(point)
|
|
774
|
+
if point != origin: # does not close up, not a subpolygon
|
|
775
|
+
continue
|
|
776
|
+
yield parent([v, [], []], None)
|
|
777
|
+
|
|
778
|
+
@cached_method
|
|
779
|
+
def minkowski_decompositions(self):
|
|
780
|
+
r"""
|
|
781
|
+
Return all Minkowski sums that add up to the polyhedron.
|
|
782
|
+
|
|
783
|
+
OUTPUT:
|
|
784
|
+
|
|
785
|
+
A tuple consisting of pairs `(X,Y)` of `\ZZ`-polyhedra that
|
|
786
|
+
add up to ``self``. All pairs up to exchange of the summands
|
|
787
|
+
are returned, that is, `(Y,X)` is not included if `(X,Y)`
|
|
788
|
+
already is.
|
|
789
|
+
|
|
790
|
+
EXAMPLES::
|
|
791
|
+
|
|
792
|
+
sage: square = Polyhedron(vertices=[(0,0),(1,0),(0,1),(1,1)])
|
|
793
|
+
sage: square.minkowski_decompositions()
|
|
794
|
+
((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
|
|
795
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
|
|
796
|
+
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
|
|
797
|
+
A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices))
|
|
798
|
+
|
|
799
|
+
Example from http://cgi.di.uoa.gr/~amantzaf/geo/ ::
|
|
800
|
+
|
|
801
|
+
sage: Q = Polyhedron(vertices=[(4,0), (6,0), (0,3), (4,3)])
|
|
802
|
+
sage: R = Polyhedron(vertices=[(0,0), (5,0), (8,4), (3,2)])
|
|
803
|
+
sage: (Q+R).minkowski_decompositions()
|
|
804
|
+
((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
|
|
805
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
|
|
806
|
+
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices,
|
|
807
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
|
|
808
|
+
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
|
|
809
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
|
|
810
|
+
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
|
|
811
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
|
|
812
|
+
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
|
|
813
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
|
|
814
|
+
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
|
|
815
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices),
|
|
816
|
+
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
|
|
817
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
|
|
818
|
+
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
|
|
819
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 6 vertices))
|
|
820
|
+
|
|
821
|
+
sage: [ len(square.dilation(i).minkowski_decompositions())
|
|
822
|
+
....: for i in range(6) ]
|
|
823
|
+
[1, 2, 5, 8, 13, 18]
|
|
824
|
+
sage: [ integer_ceil((i^2 + 2*i - 1) / 2) + 1 for i in range(10) ]
|
|
825
|
+
[1, 2, 5, 8, 13, 18, 25, 32, 41, 50]
|
|
826
|
+
"""
|
|
827
|
+
if self.dim() > 2 or not self.is_compact():
|
|
828
|
+
raise NotImplementedError('only implemented for bounded polygons')
|
|
829
|
+
summands = []
|
|
830
|
+
|
|
831
|
+
def is_known_summand(poly):
|
|
832
|
+
for summand in summands:
|
|
833
|
+
try:
|
|
834
|
+
poly.find_translation(summand)
|
|
835
|
+
return True
|
|
836
|
+
except ValueError:
|
|
837
|
+
pass
|
|
838
|
+
decompositions = []
|
|
839
|
+
for X in self._subpoly_parallel_facets():
|
|
840
|
+
if is_known_summand(X):
|
|
841
|
+
continue
|
|
842
|
+
Y = self - X
|
|
843
|
+
Y = Y.change_ring(ZZ) # Minkowski difference returns QQ-polyhedron
|
|
844
|
+
if X + Y != self:
|
|
845
|
+
continue
|
|
846
|
+
decompositions.append((X, Y))
|
|
847
|
+
summands += [X, Y]
|
|
848
|
+
return tuple(decompositions)
|
|
849
|
+
|
|
850
|
+
def normal_form(self, algorithm='palp_native', permutation=False):
|
|
851
|
+
r"""
|
|
852
|
+
Return the normal form of vertices of the lattice polytope ``self``.
|
|
853
|
+
|
|
854
|
+
INPUT:
|
|
855
|
+
|
|
856
|
+
- ``algorithm`` -- must be ``'palp_native'``, the default
|
|
857
|
+
|
|
858
|
+
- ``permutation`` -- boolean (default: ``False``); if ``True``, the permutation
|
|
859
|
+
applied to vertices to obtain the normal form is returned as well
|
|
860
|
+
|
|
861
|
+
For more more detail,
|
|
862
|
+
see :meth:`~sage.geometry.lattice_polytope.LatticePolytopeClass.normal_form`.
|
|
863
|
+
|
|
864
|
+
EXAMPLES:
|
|
865
|
+
|
|
866
|
+
We compute the normal form of the "diamond"::
|
|
867
|
+
|
|
868
|
+
sage: d = Polyhedron([(1,0), (0,1), (-1,0), (0,-1)])
|
|
869
|
+
sage: d.vertices()
|
|
870
|
+
(A vertex at (-1, 0),
|
|
871
|
+
A vertex at (0, -1),
|
|
872
|
+
A vertex at (0, 1),
|
|
873
|
+
A vertex at (1, 0))
|
|
874
|
+
sage: d.normal_form() # needs sage.groups
|
|
875
|
+
[(1, 0), (0, 1), (0, -1), (-1, 0)]
|
|
876
|
+
sage: d.lattice_polytope().normal_form("palp_native") # needs sage.groups
|
|
877
|
+
M( 1, 0),
|
|
878
|
+
M( 0, 1),
|
|
879
|
+
M( 0, -1),
|
|
880
|
+
M(-1, 0)
|
|
881
|
+
in 2-d lattice M
|
|
882
|
+
|
|
883
|
+
Using ``permutation=True``::
|
|
884
|
+
|
|
885
|
+
sage: d.normal_form(permutation=True) # needs sage.groups
|
|
886
|
+
([(1, 0), (0, 1), (0, -1), (-1, 0)], ())
|
|
887
|
+
|
|
888
|
+
It is not possible to compute normal forms for polytopes which do not
|
|
889
|
+
span the space::
|
|
890
|
+
|
|
891
|
+
sage: p = Polyhedron([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)])
|
|
892
|
+
sage: p.normal_form() # needs sage.groups
|
|
893
|
+
Traceback (most recent call last):
|
|
894
|
+
...
|
|
895
|
+
ValueError: normal form is not defined for lower-dimensional polyhedra, got
|
|
896
|
+
A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices
|
|
897
|
+
|
|
898
|
+
The normal form is also not defined for unbounded polyhedra::
|
|
899
|
+
|
|
900
|
+
sage: p = Polyhedron(vertices=[[1, 1]], rays=[[1, 0], [0, 1]], base_ring=ZZ)
|
|
901
|
+
sage: p.normal_form() # needs sage.groups
|
|
902
|
+
Traceback (most recent call last):
|
|
903
|
+
...
|
|
904
|
+
ValueError: normal form is not defined for unbounded polyhedra, got
|
|
905
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays
|
|
906
|
+
|
|
907
|
+
See :issue:`15280` for proposed extensions to these cases.
|
|
908
|
+
|
|
909
|
+
TESTS::
|
|
910
|
+
|
|
911
|
+
sage: d.normal_form(algorithm='palp_fiction') # needs sage.groups
|
|
912
|
+
Traceback (most recent call last):
|
|
913
|
+
...
|
|
914
|
+
ValueError: algorithm must be 'palp_native'
|
|
915
|
+
"""
|
|
916
|
+
from sage.geometry.palp_normal_form import _palp_PM_max, _palp_canonical_order
|
|
917
|
+
|
|
918
|
+
if algorithm != "palp_native":
|
|
919
|
+
raise ValueError("algorithm must be 'palp_native'")
|
|
920
|
+
|
|
921
|
+
if self.dim() < self.ambient_dim():
|
|
922
|
+
raise ValueError("normal form is not defined for lower-dimensional polyhedra, got %s" % self)
|
|
923
|
+
|
|
924
|
+
if not self.is_compact():
|
|
925
|
+
raise ValueError("normal form is not defined for unbounded polyhedra, got %s" % self)
|
|
926
|
+
|
|
927
|
+
PM = self.slack_matrix().transpose()
|
|
928
|
+
PM_max, permutations = _palp_PM_max(PM, check=True)
|
|
929
|
+
out = _palp_canonical_order(self.vertices(), PM_max, permutations)
|
|
930
|
+
|
|
931
|
+
if permutation:
|
|
932
|
+
return out
|
|
933
|
+
else:
|
|
934
|
+
return out[0]
|