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,1258 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Base class for polyhedra over `\QQ`
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from sage.rings.rational_field import QQ
|
|
7
|
+
from sage.misc.cachefunc import cached_method
|
|
8
|
+
from sage.misc.misc_c import prod
|
|
9
|
+
from .base import Polyhedron_base
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Polyhedron_QQ(Polyhedron_base):
|
|
13
|
+
r"""
|
|
14
|
+
Base class for Polyhedra over `\QQ`.
|
|
15
|
+
|
|
16
|
+
TESTS::
|
|
17
|
+
|
|
18
|
+
sage: p = Polyhedron([(0,0)], base_ring=QQ); p
|
|
19
|
+
A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex
|
|
20
|
+
sage: TestSuite(p).run()
|
|
21
|
+
"""
|
|
22
|
+
def _is_zero(self, x):
|
|
23
|
+
"""
|
|
24
|
+
Test whether ``x`` is zero.
|
|
25
|
+
|
|
26
|
+
INPUT:
|
|
27
|
+
|
|
28
|
+
- ``x`` -- a number in the base ring
|
|
29
|
+
|
|
30
|
+
OUTPUT: boolean
|
|
31
|
+
|
|
32
|
+
EXAMPLES::
|
|
33
|
+
|
|
34
|
+
sage: p = Polyhedron([(0,0)], base_ring=QQ)
|
|
35
|
+
sage: p._is_zero(0)
|
|
36
|
+
True
|
|
37
|
+
sage: p._is_zero(1/100000)
|
|
38
|
+
False
|
|
39
|
+
"""
|
|
40
|
+
return x == 0
|
|
41
|
+
|
|
42
|
+
def _is_nonneg(self, x):
|
|
43
|
+
"""
|
|
44
|
+
Test whether ``x`` is nonnegative.
|
|
45
|
+
|
|
46
|
+
INPUT:
|
|
47
|
+
|
|
48
|
+
- ``x`` -- a number in the base ring
|
|
49
|
+
|
|
50
|
+
OUTPUT: boolean
|
|
51
|
+
|
|
52
|
+
EXAMPLES::
|
|
53
|
+
|
|
54
|
+
sage: p = Polyhedron([(0,0)], base_ring=QQ)
|
|
55
|
+
sage: p._is_nonneg(1)
|
|
56
|
+
True
|
|
57
|
+
sage: p._is_nonneg(-1/100000)
|
|
58
|
+
False
|
|
59
|
+
"""
|
|
60
|
+
return x >= 0
|
|
61
|
+
|
|
62
|
+
def _is_positive(self, x):
|
|
63
|
+
"""
|
|
64
|
+
Test whether ``x`` is positive.
|
|
65
|
+
|
|
66
|
+
INPUT:
|
|
67
|
+
|
|
68
|
+
- ``x`` -- a number in the base ring
|
|
69
|
+
|
|
70
|
+
OUTPUT: boolean
|
|
71
|
+
|
|
72
|
+
EXAMPLES::
|
|
73
|
+
|
|
74
|
+
sage: p = Polyhedron([(0,0)], base_ring=QQ)
|
|
75
|
+
sage: p._is_positive(1)
|
|
76
|
+
True
|
|
77
|
+
sage: p._is_positive(0)
|
|
78
|
+
False
|
|
79
|
+
"""
|
|
80
|
+
return x > 0
|
|
81
|
+
|
|
82
|
+
_base_ring = QQ
|
|
83
|
+
|
|
84
|
+
def integral_points_count(self, verbose=False, use_Hrepresentation=False,
|
|
85
|
+
explicit_enumeration_threshold=1000, preprocess=True, **kwds):
|
|
86
|
+
r"""
|
|
87
|
+
Return the number of integral points in the polyhedron.
|
|
88
|
+
|
|
89
|
+
This method uses the optional package ``latte_int``
|
|
90
|
+
if an estimate for lattice points based on bounding boxes
|
|
91
|
+
exceeds ``explicit_enumeration_threshold``.
|
|
92
|
+
|
|
93
|
+
INPUT:
|
|
94
|
+
|
|
95
|
+
- ``verbose`` -- boolean (default: ``False``); whether to display
|
|
96
|
+
verbose output
|
|
97
|
+
|
|
98
|
+
- ``use_Hrepresentation`` -- boolean (default: ``False``); whether
|
|
99
|
+
to send the H or V representation to LattE
|
|
100
|
+
|
|
101
|
+
- ``preprocess`` -- boolean (default: ``True``); whether, if the integral hull
|
|
102
|
+
is known to lie in a coordinate hyperplane, to tighten bounds to reduce dimension
|
|
103
|
+
|
|
104
|
+
.. SEEALSO::
|
|
105
|
+
|
|
106
|
+
:mod:`~sage.interfaces.latte` the interface to LattE interfaces
|
|
107
|
+
|
|
108
|
+
EXAMPLES::
|
|
109
|
+
|
|
110
|
+
sage: P = polytopes.cube()
|
|
111
|
+
sage: P.integral_points_count()
|
|
112
|
+
27
|
|
113
|
+
sage: P.integral_points_count(explicit_enumeration_threshold=0) # optional - latte_int
|
|
114
|
+
27
|
|
115
|
+
|
|
116
|
+
We enlarge the polyhedron to force the use of the generating function methods
|
|
117
|
+
implemented in LattE integrale, rather than explicit enumeration::
|
|
118
|
+
|
|
119
|
+
sage: (1000000000*P).integral_points_count(verbose=True) # optional - latte_int
|
|
120
|
+
This is LattE integrale...
|
|
121
|
+
...
|
|
122
|
+
Total time:...
|
|
123
|
+
8000000012000000006000000001
|
|
124
|
+
|
|
125
|
+
We shrink the polyhedron a little bit::
|
|
126
|
+
|
|
127
|
+
sage: Q = P*(8/9)
|
|
128
|
+
sage: Q.integral_points_count()
|
|
129
|
+
1
|
|
130
|
+
sage: Q.integral_points_count(explicit_enumeration_threshold=0)
|
|
131
|
+
1
|
|
132
|
+
|
|
133
|
+
Unbounded polyhedra (with or without lattice points) are not supported::
|
|
134
|
+
|
|
135
|
+
sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]])
|
|
136
|
+
sage: P.integral_points_count()
|
|
137
|
+
Traceback (most recent call last):
|
|
138
|
+
...
|
|
139
|
+
NotImplementedError: ...
|
|
140
|
+
sage: P = Polyhedron(vertices=[[1, 1]], rays=[[1, 1]])
|
|
141
|
+
sage: P.integral_points_count()
|
|
142
|
+
Traceback (most recent call last):
|
|
143
|
+
...
|
|
144
|
+
NotImplementedError: ...
|
|
145
|
+
|
|
146
|
+
"Fibonacci" knapsacks (preprocessing helps a lot)::
|
|
147
|
+
|
|
148
|
+
sage: def fibonacci_knapsack(d, b, backend=None):
|
|
149
|
+
....: lp = MixedIntegerLinearProgram(base_ring=QQ)
|
|
150
|
+
....: x = lp.new_variable(nonnegative=True)
|
|
151
|
+
....: lp.add_constraint(lp.sum(fibonacci(i+3)*x[i] for i in range(d)) <= b)
|
|
152
|
+
....: return lp.polyhedron(backend=backend)
|
|
153
|
+
sage: fibonacci_knapsack(20, 12).integral_points_count() # does not finish with preprocess=False # needs sage.combinat
|
|
154
|
+
33
|
|
155
|
+
|
|
156
|
+
TESTS:
|
|
157
|
+
|
|
158
|
+
We check that :issue:`21491` is fixed::
|
|
159
|
+
|
|
160
|
+
sage: P = Polyhedron(ieqs=[], eqns=[[-10,0,1],[-10,1,0]])
|
|
161
|
+
sage: P.integral_points_count()
|
|
162
|
+
1
|
|
163
|
+
sage: P = Polyhedron(ieqs=[], eqns=[[-11,0,2],[-10,1,0]])
|
|
164
|
+
sage: P.integral_points_count()
|
|
165
|
+
0
|
|
166
|
+
"""
|
|
167
|
+
if self.is_empty():
|
|
168
|
+
return 0
|
|
169
|
+
if self.dimension() == 0:
|
|
170
|
+
return 1 if self.is_lattice_polytope() else 0
|
|
171
|
+
if not self.is_compact():
|
|
172
|
+
# LattE just prints the warning 'readCddExtFile:: Given polyhedron is unbounded!!!'
|
|
173
|
+
# but then returns 0.
|
|
174
|
+
raise NotImplementedError('Unbounded polyhedra are not supported')
|
|
175
|
+
|
|
176
|
+
# for small bounding boxes, it is faster to naively iterate over the points of the box
|
|
177
|
+
box_min, box_max = self.bounding_box(integral_hull=True)
|
|
178
|
+
if box_min is None:
|
|
179
|
+
return 0
|
|
180
|
+
box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max))
|
|
181
|
+
|
|
182
|
+
if explicit_enumeration_threshold is None or box_points <= explicit_enumeration_threshold:
|
|
183
|
+
return len(self.integral_points())
|
|
184
|
+
|
|
185
|
+
p = self
|
|
186
|
+
|
|
187
|
+
if preprocess:
|
|
188
|
+
# If integral hull is known to lie in a coordinate hyperplane,
|
|
189
|
+
# tighten bounds to reduce dimension.
|
|
190
|
+
rat_box_min, rat_box_max = self.bounding_box(integral=False)
|
|
191
|
+
if any(a == b and (ra < a or b < rb)
|
|
192
|
+
for ra, a, b, rb in zip(rat_box_min, box_min, box_max, rat_box_max)):
|
|
193
|
+
lp, x = self.to_linear_program(return_variable=True)
|
|
194
|
+
for i, a in enumerate(box_min):
|
|
195
|
+
lp.set_min(x[i], a)
|
|
196
|
+
for i, b in enumerate(box_max):
|
|
197
|
+
lp.set_max(x[i], b)
|
|
198
|
+
p = lp.polyhedron() # this recomputes the double description, which is wasteful
|
|
199
|
+
if p.is_empty():
|
|
200
|
+
return 0
|
|
201
|
+
if p.dimension() == 0:
|
|
202
|
+
return 1 if p.is_lattice_polytope() else 0
|
|
203
|
+
|
|
204
|
+
# LattE does not like V-representation of lower-dimensional polytopes.
|
|
205
|
+
if not p.is_full_dimensional():
|
|
206
|
+
use_Hrepresentation = True
|
|
207
|
+
|
|
208
|
+
from sage.interfaces.latte import count
|
|
209
|
+
return count(
|
|
210
|
+
p.cdd_Hrepresentation() if use_Hrepresentation else p.cdd_Vrepresentation(),
|
|
211
|
+
cdd=True,
|
|
212
|
+
verbose=verbose,
|
|
213
|
+
**kwds)
|
|
214
|
+
|
|
215
|
+
@cached_method(do_pickle=True)
|
|
216
|
+
def ehrhart_polynomial(self, engine=None, variable='t', verbose=False,
|
|
217
|
+
dual=None, irrational_primal=None,
|
|
218
|
+
irrational_all_primal=None, maxdet=None,
|
|
219
|
+
no_decomposition=None, compute_vertex_cones=None,
|
|
220
|
+
smith_form=None, dualization=None,
|
|
221
|
+
triangulation=None,
|
|
222
|
+
triangulation_max_height=None, **kwds):
|
|
223
|
+
r"""
|
|
224
|
+
Return the Ehrhart polynomial of this polyhedron.
|
|
225
|
+
|
|
226
|
+
The polyhedron must be a lattice polytope. Let `P` be a lattice
|
|
227
|
+
polytope in `\RR^d` and define `L(P,t) = \# (tP\cap \ZZ^d)`.
|
|
228
|
+
Then E. Ehrhart proved in 1962 that `L` coincides with a
|
|
229
|
+
rational polynomial of degree `d` for integer `t`. `L` is called the
|
|
230
|
+
*Ehrhart polynomial* of `P`. For more information see the
|
|
231
|
+
:wikipedia:`Ehrhart_polynomial`. The Ehrhart polynomial may be computed
|
|
232
|
+
using either LattE Integrale or Normaliz by setting ``engine`` to
|
|
233
|
+
'latte' or 'normaliz' respectively.
|
|
234
|
+
|
|
235
|
+
INPUT:
|
|
236
|
+
|
|
237
|
+
- ``engine`` -- string; the backend to use. Allowed values are:
|
|
238
|
+
|
|
239
|
+
* ``None`` (default); When no input is given the Ehrhart polynomial
|
|
240
|
+
is computed using LattE Integrale (optional)
|
|
241
|
+
* ``'latte'``; use LattE integrale program (optional)
|
|
242
|
+
* ``'normaliz'``; use Normaliz program (optional package pynormaliz).
|
|
243
|
+
The backend of ``self`` must be set to ``'normaliz'``.
|
|
244
|
+
|
|
245
|
+
- ``variable`` -- string (default: ``'t'``); the variable in which the
|
|
246
|
+
Ehrhart polynomial should be expressed
|
|
247
|
+
|
|
248
|
+
- When the ``engine`` is ``'latte'``, the additional input values are:
|
|
249
|
+
|
|
250
|
+
* ``verbose`` -- boolean (default: ``False``); if ``True``, print the
|
|
251
|
+
whole output of the LattE command
|
|
252
|
+
|
|
253
|
+
The following options are passed to the LattE command, for details
|
|
254
|
+
consult `the LattE documentation
|
|
255
|
+
<https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:
|
|
256
|
+
|
|
257
|
+
* ``dual`` -- boolean; triangulate and signed-decompose in the dual
|
|
258
|
+
space
|
|
259
|
+
* ``irrational_primal`` -- boolean; triangulate in the dual space,
|
|
260
|
+
signed-decompose in the primal space using irrationalization.
|
|
261
|
+
* ``irrational_all_primal`` -- boolean; triangulate and signed-decompose
|
|
262
|
+
in the primal space using irrationalization.
|
|
263
|
+
* ``maxdet`` -- integer; decompose down to an index (determinant) of
|
|
264
|
+
``maxdet`` instead of index 1 (unimodular cones).
|
|
265
|
+
* ``no_decomposition`` -- boolean; do not signed-decompose
|
|
266
|
+
simplicial cones.
|
|
267
|
+
* ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
|
|
268
|
+
* ``smith_form`` -- string; either 'ilio' or 'lidia'
|
|
269
|
+
* ``dualization`` -- string; either 'cdd' or '4ti2'
|
|
270
|
+
* ``triangulation`` -- string; 'cddlib', '4ti2' or 'topcom'
|
|
271
|
+
* ``triangulation_max_height`` -- integer; use a uniform distribution
|
|
272
|
+
of height from 1 to this number
|
|
273
|
+
|
|
274
|
+
OUTPUT: a univariate polynomial in ``variable`` over a rational field
|
|
275
|
+
|
|
276
|
+
.. SEEALSO::
|
|
277
|
+
|
|
278
|
+
:mod:`~sage.interfaces.latte` the interface to LattE Integrale
|
|
279
|
+
`PyNormaliz <https://pypi.org/project/PyNormaliz>`_
|
|
280
|
+
|
|
281
|
+
EXAMPLES:
|
|
282
|
+
|
|
283
|
+
To start, we find the Ehrhart polynomial of a three-dimensional
|
|
284
|
+
``simplex``, first using ``engine='latte'``. Leaving the engine
|
|
285
|
+
unspecified sets the ``engine`` to ``'latte'`` by default::
|
|
286
|
+
|
|
287
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
|
|
288
|
+
sage: simplex = simplex.change_ring(QQ)
|
|
289
|
+
sage: poly = simplex.ehrhart_polynomial(engine='latte') # optional - latte_int
|
|
290
|
+
sage: poly # optional - latte_int
|
|
291
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
292
|
+
sage: poly(1) # optional - latte_int
|
|
293
|
+
6
|
|
294
|
+
sage: len(simplex.integral_points())
|
|
295
|
+
6
|
|
296
|
+
sage: poly(2) # optional - latte_int
|
|
297
|
+
36
|
|
298
|
+
sage: len((2*simplex).integral_points())
|
|
299
|
+
36
|
|
300
|
+
|
|
301
|
+
Now we find the same Ehrhart polynomial, this time using
|
|
302
|
+
``engine='normaliz'``. To use the Normaliz engine, the ``simplex`` must
|
|
303
|
+
be defined with ``backend='normaliz'``::
|
|
304
|
+
|
|
305
|
+
sage: # optional - pynormaliz
|
|
306
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0), (3,3,3),
|
|
307
|
+
....: (-3,2,1), (1,-1,-2)],
|
|
308
|
+
....: backend='normaliz')
|
|
309
|
+
sage: simplex = simplex.change_ring(QQ)
|
|
310
|
+
sage: poly = simplex.ehrhart_polynomial(engine='normaliz')
|
|
311
|
+
sage: poly
|
|
312
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
313
|
+
|
|
314
|
+
If the ``engine='normaliz'``, the backend should be ``'normaliz'``, otherwise
|
|
315
|
+
it returns an error::
|
|
316
|
+
|
|
317
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0), (3,3,3),
|
|
318
|
+
....: (-3,2,1), (1,-1,-2)])
|
|
319
|
+
sage: simplex = simplex.change_ring(QQ)
|
|
320
|
+
sage: simplex.ehrhart_polynomial(engine='normaliz')
|
|
321
|
+
Traceback (most recent call last):
|
|
322
|
+
...
|
|
323
|
+
TypeError: The backend of the polyhedron should be 'normaliz'
|
|
324
|
+
|
|
325
|
+
The polyhedron should be compact::
|
|
326
|
+
|
|
327
|
+
sage: C = Polyhedron(rays=[[1,2], [2,1]], # optional - pynormaliz
|
|
328
|
+
....: backend='normaliz')
|
|
329
|
+
sage: C = C.change_ring(QQ) # optional - pynormaliz
|
|
330
|
+
sage: C.ehrhart_polynomial() # optional - pynormaliz
|
|
331
|
+
Traceback (most recent call last):
|
|
332
|
+
...
|
|
333
|
+
ValueError: Ehrhart polynomial only defined for compact polyhedra
|
|
334
|
+
|
|
335
|
+
The polyhedron should have integral vertices::
|
|
336
|
+
|
|
337
|
+
sage: L = Polyhedron(vertices=[[0], [1/2]])
|
|
338
|
+
sage: L.ehrhart_polynomial()
|
|
339
|
+
Traceback (most recent call last):
|
|
340
|
+
...
|
|
341
|
+
TypeError: the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz'
|
|
342
|
+
|
|
343
|
+
TESTS:
|
|
344
|
+
|
|
345
|
+
The cache of the Ehrhart polynomial is being pickled::
|
|
346
|
+
|
|
347
|
+
sage: # optional - latte_int
|
|
348
|
+
sage: P = polytopes.cube().change_ring(QQ)
|
|
349
|
+
sage: P.ehrhart_polynomial()
|
|
350
|
+
8*t^3 + 12*t^2 + 6*t + 1
|
|
351
|
+
sage: Q = loads(dumps(P))
|
|
352
|
+
sage: Q.ehrhart_polynomial.is_in_cache()
|
|
353
|
+
True
|
|
354
|
+
|
|
355
|
+
sage: L = Polyhedron(vertices=[[QQ(0)]])
|
|
356
|
+
sage: L.ehrhart_polynomial()
|
|
357
|
+
1
|
|
358
|
+
"""
|
|
359
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
360
|
+
from sage.rings.rational_field import QQ
|
|
361
|
+
R = PolynomialRing(QQ, variable)
|
|
362
|
+
|
|
363
|
+
# check if ``self`` is compact and has vertices in ZZ
|
|
364
|
+
if self.is_empty():
|
|
365
|
+
return R.zero()
|
|
366
|
+
|
|
367
|
+
if not self.is_compact():
|
|
368
|
+
raise ValueError("Ehrhart polynomial only defined for compact polyhedra")
|
|
369
|
+
|
|
370
|
+
if any(not v.is_integral() for v in self.vertex_generator()):
|
|
371
|
+
raise TypeError("the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz'")
|
|
372
|
+
|
|
373
|
+
if self.dimension() == 0:
|
|
374
|
+
return R.one()
|
|
375
|
+
|
|
376
|
+
# Passes to specific latte or normaliz subfunction depending on engine
|
|
377
|
+
if engine is None:
|
|
378
|
+
# set default engine to latte
|
|
379
|
+
engine = 'latte'
|
|
380
|
+
|
|
381
|
+
if engine == 'latte':
|
|
382
|
+
poly = self._ehrhart_polynomial_latte(verbose, dual,
|
|
383
|
+
irrational_primal, irrational_all_primal, maxdet,
|
|
384
|
+
no_decomposition, compute_vertex_cones, smith_form,
|
|
385
|
+
dualization, triangulation, triangulation_max_height,
|
|
386
|
+
**kwds)
|
|
387
|
+
return poly.change_variable_name(variable)
|
|
388
|
+
# TO DO: replace this change of variable by creating the appropriate
|
|
389
|
+
# polynomial ring in the latte interface.
|
|
390
|
+
|
|
391
|
+
elif engine == 'normaliz':
|
|
392
|
+
return self._ehrhart_polynomial_normaliz(variable)
|
|
393
|
+
else:
|
|
394
|
+
raise ValueError("engine must be 'latte' or 'normaliz'")
|
|
395
|
+
|
|
396
|
+
@cached_method(do_pickle=True)
|
|
397
|
+
def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False,
|
|
398
|
+
dual=None, irrational_primal=None, irrational_all_primal=None,
|
|
399
|
+
maxdet=None, no_decomposition=None, compute_vertex_cones=None,
|
|
400
|
+
smith_form=None, dualization=None, triangulation=None,
|
|
401
|
+
triangulation_max_height=None, **kwds):
|
|
402
|
+
r"""
|
|
403
|
+
Compute the Ehrhart quasipolynomial of this polyhedron with rational
|
|
404
|
+
vertices.
|
|
405
|
+
|
|
406
|
+
If the polyhedron is a lattice polytope, returns the Ehrhart polynomial,
|
|
407
|
+
a univariate polynomial in ``variable`` over a rational field.
|
|
408
|
+
If the polyhedron has rational, nonintegral vertices, returns a tuple
|
|
409
|
+
of polynomials in ``variable`` over a rational field.
|
|
410
|
+
The Ehrhart counting function of a polytope `P` with rational
|
|
411
|
+
vertices is given by a *quasipolynomial*. That is, there exists a
|
|
412
|
+
positive integer `l` and `l` polynomials
|
|
413
|
+
`ehr_{P,i} \text{ for } i \in \{1,\dots,l \}` such that if `t` is
|
|
414
|
+
equivalent to `i` mod `l` then `tP \cap \mathbb Z^d = ehr_{P,i}(t)`.
|
|
415
|
+
|
|
416
|
+
INPUT:
|
|
417
|
+
|
|
418
|
+
- ``variable`` -- string (default: ``'t'``); the variable in which the
|
|
419
|
+
Ehrhart polynomial should be expressed
|
|
420
|
+
|
|
421
|
+
- ``engine`` -- string; the backend to use. Allowed values are:
|
|
422
|
+
|
|
423
|
+
* ``None`` (default); When no input is given the Ehrhart polynomial
|
|
424
|
+
is computed using Normaliz (optional)
|
|
425
|
+
* ``'latte'``; use LattE Integrale program (requires optional package
|
|
426
|
+
'latte_int')
|
|
427
|
+
* ``'normaliz'``; use the Normaliz program (requires optional package
|
|
428
|
+
'pynormaliz'). The backend of ``self`` must be set to 'normaliz'.
|
|
429
|
+
|
|
430
|
+
- When the ``engine`` is 'latte', the additional input values are:
|
|
431
|
+
|
|
432
|
+
* ``verbose`` -- boolean (default: ``False``); if ``True``, print the
|
|
433
|
+
whole output of the LattE command
|
|
434
|
+
|
|
435
|
+
The following options are passed to the LattE command, for details
|
|
436
|
+
consult `the LattE documentation
|
|
437
|
+
<https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:
|
|
438
|
+
|
|
439
|
+
* ``dual`` -- boolean; triangulate and signed-decompose in the dual
|
|
440
|
+
space
|
|
441
|
+
* ``irrational_primal`` -- boolean; triangulate in the dual space,
|
|
442
|
+
signed-decompose in the primal space using irrationalization.
|
|
443
|
+
* ``irrational_all_primal`` -- boolean; triangulate and signed-decompose
|
|
444
|
+
in the primal space using irrationalization.
|
|
445
|
+
* ``maxdet`` -- integer; decompose down to an index (determinant) of
|
|
446
|
+
``maxdet`` instead of index 1 (unimodular cones).
|
|
447
|
+
* ``no_decomposition`` -- boolean; do not signed-decompose
|
|
448
|
+
simplicial cones.
|
|
449
|
+
* ``compute_vertex_cones`` -- string; either ``'cdd'`` or ``'lrs'`` or ``'4ti2'``
|
|
450
|
+
* ``smith_form`` -- string; either ``'ilio'`` or ``'lidia'``
|
|
451
|
+
* ``dualization`` -- string; either ``'cdd'`` or ``'4ti2'``
|
|
452
|
+
* ``triangulation`` -- string; ``'cddlib'``, ``'4ti2'`` or ``'topcom'``
|
|
453
|
+
* ``triangulation_max_height`` -- integer; use a uniform distribution of
|
|
454
|
+
height from 1 to this number
|
|
455
|
+
|
|
456
|
+
OUTPUT:
|
|
457
|
+
|
|
458
|
+
A univariate polynomial over a rational field or a tuple of such
|
|
459
|
+
polynomials.
|
|
460
|
+
|
|
461
|
+
.. SEEALSO::
|
|
462
|
+
|
|
463
|
+
:mod:`~sage.interfaces.latte` the interface to LattE Integrale
|
|
464
|
+
`PyNormaliz <https://pypi.org/project/PyNormaliz>`_
|
|
465
|
+
|
|
466
|
+
.. WARNING::
|
|
467
|
+
|
|
468
|
+
If the polytope has rational, non integral vertices,
|
|
469
|
+
it must have ``backend='normaliz'``.
|
|
470
|
+
|
|
471
|
+
EXAMPLES:
|
|
472
|
+
|
|
473
|
+
As a first example, consider the line segment [0,1/2]. If we
|
|
474
|
+
dilate this line segment by an even integral factor `k`,
|
|
475
|
+
then the dilated line segment will contain `k/2 +1` lattice points.
|
|
476
|
+
If `k` is odd then there will be `k/2+1/2` lattice points in
|
|
477
|
+
the dilated line segment. Note that it is necessary to set the
|
|
478
|
+
backend of the polytope to 'normaliz'::
|
|
479
|
+
|
|
480
|
+
sage: line_seg = Polyhedron(vertices=[[0], [1/2]], # optional - pynormaliz
|
|
481
|
+
....: backend='normaliz'); line_seg
|
|
482
|
+
A 1-dimensional polyhedron in QQ^1 defined as the convex hull of 2 vertices
|
|
483
|
+
sage: line_seg.ehrhart_quasipolynomial() # optional - pynormaliz
|
|
484
|
+
(1/2*t + 1, 1/2*t + 1/2)
|
|
485
|
+
|
|
486
|
+
For a more exciting example, let us look at the subpolytope of the
|
|
487
|
+
3 dimensional permutahedron fixed by the reflection
|
|
488
|
+
across the hyperplane `x_1 = x_4`::
|
|
489
|
+
|
|
490
|
+
sage: verts = [[3/2, 3, 4, 3/2],
|
|
491
|
+
....: [3/2, 4, 3, 3/2],
|
|
492
|
+
....: [5/2, 1, 4, 5/2],
|
|
493
|
+
....: [5/2, 4, 1, 5/2],
|
|
494
|
+
....: [7/2, 1, 2, 7/2],
|
|
495
|
+
....: [7/2, 2, 1, 7/2]]
|
|
496
|
+
sage: subpoly = Polyhedron(vertices=verts, # optional - pynormaliz
|
|
497
|
+
....: backend='normaliz')
|
|
498
|
+
sage: eq = subpoly.ehrhart_quasipolynomial(); eq # optional - pynormaliz
|
|
499
|
+
(4*t^2 + 3*t + 1, 4*t^2 + 2*t)
|
|
500
|
+
sage: eq = subpoly.ehrhart_quasipolynomial(); eq # optional - pynormaliz
|
|
501
|
+
(4*t^2 + 3*t + 1, 4*t^2 + 2*t)
|
|
502
|
+
sage: even_ep = eq[0] # optional - pynormaliz
|
|
503
|
+
sage: odd_ep = eq[1] # optional - pynormaliz
|
|
504
|
+
sage: even_ep(2) # optional - pynormaliz
|
|
505
|
+
23
|
|
506
|
+
sage: ts = 2*subpoly # optional - pynormaliz
|
|
507
|
+
sage: ts.integral_points_count() # optional - pynormaliz latte_int
|
|
508
|
+
23
|
|
509
|
+
sage: odd_ep(1) # optional - pynormaliz
|
|
510
|
+
6
|
|
511
|
+
sage: subpoly.integral_points_count() # optional - pynormaliz latte_int
|
|
512
|
+
6
|
|
513
|
+
|
|
514
|
+
A polytope with rational nonintegral vertices must have
|
|
515
|
+
``backend='normaliz'``::
|
|
516
|
+
|
|
517
|
+
sage: line_seg = Polyhedron(vertices=[[0], [1/2]])
|
|
518
|
+
sage: line_seg.ehrhart_quasipolynomial()
|
|
519
|
+
Traceback (most recent call last):
|
|
520
|
+
...
|
|
521
|
+
TypeError: The backend of the polyhedron should be 'normaliz'
|
|
522
|
+
|
|
523
|
+
The polyhedron should be compact::
|
|
524
|
+
|
|
525
|
+
sage: C = Polyhedron(rays=[[1/2,2], [2,1]], # optional - pynormaliz
|
|
526
|
+
....: backend='normaliz')
|
|
527
|
+
sage: C.ehrhart_quasipolynomial() # optional - pynormaliz
|
|
528
|
+
Traceback (most recent call last):
|
|
529
|
+
...
|
|
530
|
+
ValueError: Ehrhart quasipolynomial only defined for compact polyhedra
|
|
531
|
+
|
|
532
|
+
If the polytope happens to be a lattice polytope, the Ehrhart
|
|
533
|
+
polynomial is returned::
|
|
534
|
+
|
|
535
|
+
sage: # optional - pynormaliz
|
|
536
|
+
sage: simplex = Polyhedron(vertices=[(0,0,0), (3,3,3),
|
|
537
|
+
....: (-3,2,1), (1,-1,-2)],
|
|
538
|
+
....: backend='normaliz')
|
|
539
|
+
sage: simplex = simplex.change_ring(QQ)
|
|
540
|
+
sage: poly = simplex.ehrhart_quasipolynomial(
|
|
541
|
+
....: engine='normaliz'); poly
|
|
542
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
543
|
+
sage: simplex.ehrhart_polynomial() # optional - latte_int
|
|
544
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
545
|
+
|
|
546
|
+
TESTS:
|
|
547
|
+
|
|
548
|
+
The cache of the Ehrhart quasipolynomial is being pickled::
|
|
549
|
+
|
|
550
|
+
sage: # optional - pynormaliz
|
|
551
|
+
sage: P = polytopes.cuboctahedron(backend='normaliz')/2
|
|
552
|
+
sage: P.ehrhart_quasipolynomial()
|
|
553
|
+
(5/6*t^3 + 2*t^2 + 5/3*t + 1, 5/6*t^3 + 1/2*t^2 + 1/6*t - 1/2)
|
|
554
|
+
sage: Q = loads(dumps(P))
|
|
555
|
+
sage: Q.ehrhart_quasipolynomial.is_in_cache()
|
|
556
|
+
True
|
|
557
|
+
|
|
558
|
+
sage: # optional - latte_int
|
|
559
|
+
sage: P = polytopes.cuboctahedron().change_ring(QQ)
|
|
560
|
+
sage: P.ehrhart_quasipolynomial(engine='latte')
|
|
561
|
+
20/3*t^3 + 8*t^2 + 10/3*t + 1
|
|
562
|
+
sage: Q = loads(dumps(P))
|
|
563
|
+
sage: Q.ehrhart_quasipolynomial.is_in_cache(engine='latte')
|
|
564
|
+
True
|
|
565
|
+
"""
|
|
566
|
+
if self.is_empty():
|
|
567
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
568
|
+
from sage.rings.rational_field import QQ
|
|
569
|
+
R = PolynomialRing(QQ, 't')
|
|
570
|
+
return R.zero()
|
|
571
|
+
|
|
572
|
+
if not self.is_compact():
|
|
573
|
+
raise ValueError("Ehrhart quasipolynomial only defined for compact polyhedra")
|
|
574
|
+
|
|
575
|
+
if engine is None:
|
|
576
|
+
# setting the default to 'normaliz'
|
|
577
|
+
engine = 'normaliz'
|
|
578
|
+
if engine == 'normaliz':
|
|
579
|
+
return self._ehrhart_quasipolynomial_normaliz(variable)
|
|
580
|
+
if engine == 'latte':
|
|
581
|
+
if any(not v.is_integral() for v in self.vertex_generator()):
|
|
582
|
+
raise TypeError("the polytope has nonintegral vertices, the engine and backend of self should be 'normaliz'")
|
|
583
|
+
poly = self._ehrhart_polynomial_latte(verbose, dual,
|
|
584
|
+
irrational_primal, irrational_all_primal, maxdet,
|
|
585
|
+
no_decomposition, compute_vertex_cones, smith_form,
|
|
586
|
+
dualization, triangulation, triangulation_max_height,
|
|
587
|
+
**kwds)
|
|
588
|
+
return poly.change_variable_name(variable)
|
|
589
|
+
# TO DO: replace this change of variable by creating the appropriate
|
|
590
|
+
# polynomial ring in the latte interface.
|
|
591
|
+
else:
|
|
592
|
+
raise TypeError("the engine should be 'latte' or 'normaliz'")
|
|
593
|
+
|
|
594
|
+
def _ehrhart_quasipolynomial_normaliz(self, variable='t'):
|
|
595
|
+
r"""
|
|
596
|
+
Compute the Ehrhart quasipolynomial of a lattice or rational polytope
|
|
597
|
+
using the Normaliz engine.
|
|
598
|
+
|
|
599
|
+
INPUT:
|
|
600
|
+
|
|
601
|
+
- ``variable`` -- string (default: ``'t'``); the variable in which the
|
|
602
|
+
Ehrhart polynomial is expressed
|
|
603
|
+
|
|
604
|
+
OUTPUT:
|
|
605
|
+
|
|
606
|
+
A univariate polynomial over a rational field or a tuple of such
|
|
607
|
+
polynomials.
|
|
608
|
+
|
|
609
|
+
EXAMPLES:
|
|
610
|
+
|
|
611
|
+
The subpolytope of the 3 dimensional permutahedron fixed by the
|
|
612
|
+
reflection across the hyperplane `x_1 = x_4`::
|
|
613
|
+
|
|
614
|
+
sage: verts = [[3/2, 3, 4, 3/2],
|
|
615
|
+
....: [3/2, 4, 3, 3/2],
|
|
616
|
+
....: [5/2, 1, 4, 5/2],
|
|
617
|
+
....: [5/2, 4, 1, 5/2],
|
|
618
|
+
....: [7/2, 1, 2, 7/2],
|
|
619
|
+
....: [7/2, 2, 1, 7/2]]
|
|
620
|
+
sage: subpoly = Polyhedron(vertices=verts, # optional - pynormaliz
|
|
621
|
+
....: backend='normaliz')
|
|
622
|
+
sage: eq = subpoly._ehrhart_quasipolynomial_normaliz(); eq # optional - pynormaliz
|
|
623
|
+
(4*t^2 + 3*t + 1, 4*t^2 + 2*t)
|
|
624
|
+
sage: even_ep = eq[0] # optional - pynormaliz
|
|
625
|
+
sage: odd_ep = eq[1] # optional - pynormaliz
|
|
626
|
+
sage: even_ep(2) # optional - pynormaliz
|
|
627
|
+
23
|
|
628
|
+
sage: ts = 2*subpoly # optional - pynormaliz
|
|
629
|
+
sage: ts.integral_points_count() # optional - pynormaliz latte_int
|
|
630
|
+
23
|
|
631
|
+
sage: odd_ep(1) # optional - pynormaliz
|
|
632
|
+
6
|
|
633
|
+
sage: subpoly.integral_points_count() # optional - pynormaliz latte_int
|
|
634
|
+
6
|
|
635
|
+
|
|
636
|
+
TESTS::
|
|
637
|
+
|
|
638
|
+
sage: line_seg = Polyhedron(vertices=[[0],[1/2]])
|
|
639
|
+
sage: line_seg._ehrhart_quasipolynomial_normaliz()
|
|
640
|
+
Traceback (most recent call last):
|
|
641
|
+
...
|
|
642
|
+
TypeError: The backend of the polyhedron should be 'normaliz'
|
|
643
|
+
"""
|
|
644
|
+
raise TypeError("The backend of the polyhedron should be 'normaliz'")
|
|
645
|
+
|
|
646
|
+
_ehrhart_polynomial_normaliz = _ehrhart_quasipolynomial_normaliz
|
|
647
|
+
|
|
648
|
+
def _ehrhart_polynomial_latte(self, verbose=False, dual=None,
|
|
649
|
+
irrational_primal=None, irrational_all_primal=None, maxdet=None,
|
|
650
|
+
no_decomposition=None, compute_vertex_cones=None, smith_form=None,
|
|
651
|
+
dualization=None, triangulation=None, triangulation_max_height=None,
|
|
652
|
+
**kwds):
|
|
653
|
+
r"""
|
|
654
|
+
Return the Ehrhart polynomial of this polyhedron using LattE integrale.
|
|
655
|
+
|
|
656
|
+
Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
|
|
657
|
+
\cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
|
|
658
|
+
rational polynomial of degree `d` for integer `t`. `L` is called the
|
|
659
|
+
*Ehrhart polynomial* of `P`. For more information see the
|
|
660
|
+
:wikipedia:`Ehrhart_polynomial`.
|
|
661
|
+
|
|
662
|
+
INPUT:
|
|
663
|
+
|
|
664
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, print the
|
|
665
|
+
whole output of the LattE command
|
|
666
|
+
|
|
667
|
+
The following options are passed to the LattE command, for details you
|
|
668
|
+
should consult `the LattE documentation
|
|
669
|
+
<https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:
|
|
670
|
+
|
|
671
|
+
- ``dual`` -- boolean; triangulate and signed-decompose in the dual
|
|
672
|
+
space
|
|
673
|
+
|
|
674
|
+
- ``irrational_primal`` -- boolean; triangulate in the dual space,
|
|
675
|
+
signed-decompose in the primal space using irrationalization
|
|
676
|
+
|
|
677
|
+
- ``irrational_all_primal`` -- boolean; triangulate and signed-decompose
|
|
678
|
+
in the primal space using irrationalization
|
|
679
|
+
|
|
680
|
+
- ``maxdet`` -- integer; decompose down to an index (determinant) of
|
|
681
|
+
``maxdet`` instead of index 1 (unimodular cones)
|
|
682
|
+
|
|
683
|
+
- ``no_decomposition`` -- boolean; do not signed-decompose simplicial cones
|
|
684
|
+
|
|
685
|
+
- ``compute_vertex_cones`` -- string; either ``'cdd'`` or ``'lrs'`` or ``'4ti2'``
|
|
686
|
+
|
|
687
|
+
- ``smith_form`` -- string; either ``'ilio'`` or ``'lidia'``
|
|
688
|
+
|
|
689
|
+
- ``dualization`` -- string; either ``'cdd'`` or ``'4ti2'``
|
|
690
|
+
|
|
691
|
+
- ``triangulation`` -- string; ``'cddlib'``, ``'4ti2'`` or ``'topcom'``
|
|
692
|
+
|
|
693
|
+
- ``triangulation_max_height`` -- integer; use a uniform distribution of
|
|
694
|
+
height from 1 to this number
|
|
695
|
+
|
|
696
|
+
.. NOTE::
|
|
697
|
+
|
|
698
|
+
Any additional argument is forwarded to LattE's executable
|
|
699
|
+
``count``. All occurrences of '_' will be replaced with a '-'.
|
|
700
|
+
|
|
701
|
+
OUTPUT: a univariate polynomial over a rational field
|
|
702
|
+
|
|
703
|
+
ALGORITHM:
|
|
704
|
+
|
|
705
|
+
This method calls the program ``count`` from LattE integrale, a program
|
|
706
|
+
for lattice point enumeration (see
|
|
707
|
+
https://www.math.ucdavis.edu/~latte/).
|
|
708
|
+
|
|
709
|
+
.. SEEALSO::
|
|
710
|
+
|
|
711
|
+
:mod:`~sage.interfaces.latte` the interface to LattE integrale
|
|
712
|
+
|
|
713
|
+
EXAMPLES::
|
|
714
|
+
|
|
715
|
+
sage: P = Polyhedron(vertices=[(0,0,0), (3,3,3), (-3,2,1), (1,-1,-2)])
|
|
716
|
+
sage: p = P._ehrhart_polynomial_latte(); p # optional - latte_int
|
|
717
|
+
7/2*t^3 + 2*t^2 - 1/2*t + 1
|
|
718
|
+
sage: p(1) # optional - latte_int
|
|
719
|
+
6
|
|
720
|
+
sage: len(P.integral_points())
|
|
721
|
+
6
|
|
722
|
+
sage: p(2) # optional - latte_int
|
|
723
|
+
36
|
|
724
|
+
sage: len((2*P).integral_points())
|
|
725
|
+
36
|
|
726
|
+
|
|
727
|
+
The unit hypercubes::
|
|
728
|
+
|
|
729
|
+
sage: # optional - latte_int
|
|
730
|
+
sage: from itertools import product
|
|
731
|
+
sage: def hypercube(d):
|
|
732
|
+
....: return Polyhedron(vertices=list(product([0,1], repeat=d)))
|
|
733
|
+
sage: hypercube(3)._ehrhart_polynomial_latte()
|
|
734
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
735
|
+
sage: hypercube(4)._ehrhart_polynomial_latte()
|
|
736
|
+
t^4 + 4*t^3 + 6*t^2 + 4*t + 1
|
|
737
|
+
sage: hypercube(5)._ehrhart_polynomial_latte()
|
|
738
|
+
t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
|
|
739
|
+
sage: hypercube(6)._ehrhart_polynomial_latte()
|
|
740
|
+
t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1
|
|
741
|
+
|
|
742
|
+
TESTS:
|
|
743
|
+
|
|
744
|
+
Test options::
|
|
745
|
+
|
|
746
|
+
sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)
|
|
747
|
+
|
|
748
|
+
sage: p = P._ehrhart_polynomial_latte(maxdet=5, verbose=True) # optional - latte_int
|
|
749
|
+
This is LattE integrale ...
|
|
750
|
+
...
|
|
751
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin
|
|
752
|
+
...
|
|
753
|
+
sage: p # optional - latte_int
|
|
754
|
+
1/2*t^2 + 3/2*t + 1
|
|
755
|
+
|
|
756
|
+
sage: p = P._ehrhart_polynomial_latte(dual=True, verbose=True) # optional - latte_int
|
|
757
|
+
This is LattE integrale ...
|
|
758
|
+
...
|
|
759
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin
|
|
760
|
+
...
|
|
761
|
+
sage: p # optional - latte_int
|
|
762
|
+
1/2*t^2 + 3/2*t + 1
|
|
763
|
+
|
|
764
|
+
sage: p = P._ehrhart_polynomial_latte(irrational_primal=True, verbose=True) # optional - latte_int
|
|
765
|
+
This is LattE integrale ...
|
|
766
|
+
...
|
|
767
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin
|
|
768
|
+
...
|
|
769
|
+
sage: p # optional - latte_int
|
|
770
|
+
1/2*t^2 + 3/2*t + 1
|
|
771
|
+
|
|
772
|
+
sage: p = P._ehrhart_polynomial_latte(irrational_all_primal=True, verbose=True) # optional - latte_int
|
|
773
|
+
This is LattE integrale ...
|
|
774
|
+
...
|
|
775
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin
|
|
776
|
+
...
|
|
777
|
+
sage: p # optional - latte_int
|
|
778
|
+
1/2*t^2 + 3/2*t + 1
|
|
779
|
+
|
|
780
|
+
Test bad options::
|
|
781
|
+
|
|
782
|
+
sage: P._ehrhart_polynomial_latte(bim_bam_boum=19) # optional - latte_int
|
|
783
|
+
Traceback (most recent call last):
|
|
784
|
+
...
|
|
785
|
+
RuntimeError: LattE integrale program failed (exit code 1):
|
|
786
|
+
...
|
|
787
|
+
Invocation: ...count... --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin
|
|
788
|
+
Unknown command/option --bim-bam-boum=19
|
|
789
|
+
"""
|
|
790
|
+
|
|
791
|
+
# note: the options below are explicitly written in the function
|
|
792
|
+
# declaration in order to keep tab completion (see #18211).
|
|
793
|
+
kwds.update({
|
|
794
|
+
'dual' : dual,
|
|
795
|
+
'irrational_primal' : irrational_primal,
|
|
796
|
+
'irrational_all_primal' : irrational_all_primal,
|
|
797
|
+
'maxdet' : maxdet,
|
|
798
|
+
'no_decomposition' : no_decomposition,
|
|
799
|
+
'compute_vertex_cones' : compute_vertex_cones,
|
|
800
|
+
'smith_form' : smith_form,
|
|
801
|
+
'dualization' : dualization,
|
|
802
|
+
'triangulation' : triangulation,
|
|
803
|
+
'triangulation_max_height': triangulation_max_height})
|
|
804
|
+
|
|
805
|
+
from sage.interfaces.latte import count
|
|
806
|
+
ine = self.cdd_Hrepresentation()
|
|
807
|
+
return count(ine, cdd=True, ehrhart_polynomial=True, verbose=verbose, **kwds)
|
|
808
|
+
|
|
809
|
+
def fixed_subpolytope(self, vertex_permutation):
|
|
810
|
+
r"""
|
|
811
|
+
Return the fixed subpolytope of this polytope by the cyclic action of
|
|
812
|
+
``vertex_permutation``.
|
|
813
|
+
|
|
814
|
+
The fixed subpolytope of this polytope under the ``vertex_permutation``
|
|
815
|
+
is the subset of this polytope that is fixed pointwise.
|
|
816
|
+
|
|
817
|
+
INPUT:
|
|
818
|
+
|
|
819
|
+
- ``vertex_permutation`` -- permutation; a permutation of the vertices
|
|
820
|
+
of ``self``
|
|
821
|
+
|
|
822
|
+
OUTPUT: a subpolytope of ``self``
|
|
823
|
+
|
|
824
|
+
.. NOTE::
|
|
825
|
+
|
|
826
|
+
The vertex_permutation is obtained as a permutation of the vertices
|
|
827
|
+
represented as a permutation. For example, vertex_permutation =
|
|
828
|
+
self.restricted_automorphism_group(output='permutation').
|
|
829
|
+
|
|
830
|
+
Requiring a lattice polytope as opposed to a rational polytope as
|
|
831
|
+
input is purely conventional.
|
|
832
|
+
|
|
833
|
+
EXAMPLES:
|
|
834
|
+
|
|
835
|
+
The fixed subpolytopes of the cube can be obtained as follows::
|
|
836
|
+
|
|
837
|
+
sage: Cube = polytopes.cube(backend = 'normaliz') # optional - pynormaliz
|
|
838
|
+
sage: AG = Cube.restricted_automorphism_group( # optional - pynormaliz
|
|
839
|
+
....: output='permutation')
|
|
840
|
+
sage: reprs = AG.conjugacy_classes_representatives() # optional - pynormaliz
|
|
841
|
+
|
|
842
|
+
The fixed subpolytope of the identity element of the group is the entire
|
|
843
|
+
cube::
|
|
844
|
+
|
|
845
|
+
sage: reprs[0] # optional - pynormaliz
|
|
846
|
+
()
|
|
847
|
+
sage: Cube.fixed_subpolytope(vertex_permutation=reprs[0]) # optional - pynormaliz
|
|
848
|
+
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8
|
|
849
|
+
vertices
|
|
850
|
+
sage: _.vertices() # optional - pynormaliz
|
|
851
|
+
(A vertex at (-1, -1, -1),
|
|
852
|
+
A vertex at (-1, -1, 1),
|
|
853
|
+
A vertex at (-1, 1, -1),
|
|
854
|
+
A vertex at (-1, 1, 1),
|
|
855
|
+
A vertex at (1, -1, -1),
|
|
856
|
+
A vertex at (1, -1, 1),
|
|
857
|
+
A vertex at (1, 1, -1),
|
|
858
|
+
A vertex at (1, 1, 1))
|
|
859
|
+
|
|
860
|
+
You can obtain non-trivial examples::
|
|
861
|
+
|
|
862
|
+
sage: G = AG([(0,1),(2,3),(4,5),(6,7)]) # optional - pynormaliz
|
|
863
|
+
sage: fsp = Cube.fixed_subpolytope(G); fsp # optional - pynormaliz
|
|
864
|
+
A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices
|
|
865
|
+
sage: fsp.vertices() # optional - pynormaliz
|
|
866
|
+
(A vertex at (-1, -1, 0),
|
|
867
|
+
A vertex at (-1, 1, 0),
|
|
868
|
+
A vertex at (1, -1, 0),
|
|
869
|
+
A vertex at (1, 1, 0))
|
|
870
|
+
|
|
871
|
+
The next example shows that :meth:`fixed_subpolytope` works for rational polytopes::
|
|
872
|
+
|
|
873
|
+
sage: # optional - pynormaliz
|
|
874
|
+
sage: P = Polyhedron(vertices=[[0], [1/2]],
|
|
875
|
+
....: backend='normaliz')
|
|
876
|
+
sage: P.vertices()
|
|
877
|
+
(A vertex at (0), A vertex at (1/2))
|
|
878
|
+
sage: G = P.restricted_automorphism_group(
|
|
879
|
+
....: output='permutation'); G
|
|
880
|
+
Permutation Group with generators [(0,1)]
|
|
881
|
+
sage: len(G)
|
|
882
|
+
2
|
|
883
|
+
sage: fixed_set = P.fixed_subpolytope(G.gens()[0])
|
|
884
|
+
sage: fixed_set
|
|
885
|
+
A 0-dimensional polyhedron in QQ^1 defined as the convex hull of 1 vertex
|
|
886
|
+
sage: fixed_set.vertices_list()
|
|
887
|
+
[[1/4]]
|
|
888
|
+
"""
|
|
889
|
+
if self.is_empty():
|
|
890
|
+
raise NotImplementedError('empty polyhedra are not supported')
|
|
891
|
+
if not self.is_compact():
|
|
892
|
+
raise NotImplementedError('unbounded polyhedra are not supported')
|
|
893
|
+
|
|
894
|
+
orbits = frozenset([frozenset(i) for i in vertex_permutation.cycle_tuples(singletons=True)])
|
|
895
|
+
|
|
896
|
+
# If its the identity, returns the polytope
|
|
897
|
+
if not orbits:
|
|
898
|
+
return self
|
|
899
|
+
|
|
900
|
+
# Make an index shift flag
|
|
901
|
+
shift = True
|
|
902
|
+
if 0 in vertex_permutation.domain():
|
|
903
|
+
shift = False
|
|
904
|
+
|
|
905
|
+
vertices = []
|
|
906
|
+
for orbit in orbits:
|
|
907
|
+
size = len(orbit)
|
|
908
|
+
if shift:
|
|
909
|
+
# in this case, the indices in the orbit are 1 more than the index in the V
|
|
910
|
+
s = sum([(self.Vrepresentation()[i-1]).vector() for i in orbit])
|
|
911
|
+
else:
|
|
912
|
+
s = sum([(self.Vrepresentation()[i]).vector() for i in orbit])
|
|
913
|
+
orbit_barycenter = (1/QQ(size)) * s
|
|
914
|
+
vertices += [orbit_barycenter]
|
|
915
|
+
|
|
916
|
+
P = self.parent().change_ring(self.base_ring().fraction_field(),backend='normaliz')
|
|
917
|
+
return P.element_class(P, [vertices,[],[]], None)
|
|
918
|
+
|
|
919
|
+
def fixed_subpolytopes(self, conj_class_reps):
|
|
920
|
+
r"""
|
|
921
|
+
Return the fixed subpolytopes of this polytope under the actions of the
|
|
922
|
+
given conjugacy class representatives.
|
|
923
|
+
|
|
924
|
+
The ``conj_class_reps`` are representatives of the conjugacy classes of
|
|
925
|
+
a subgroup of the automorphism group of this polytope.
|
|
926
|
+
For an element of the automorphism group, the fixed subpolytope
|
|
927
|
+
is the subset of this polytope that is fixed pointwise.
|
|
928
|
+
|
|
929
|
+
INPUT:
|
|
930
|
+
|
|
931
|
+
- ``conj_class_reps`` -- list of representatives of the conjugacy
|
|
932
|
+
classes of the subgroup of the :meth:`restricted_automorphism_group` of
|
|
933
|
+
the polytope. Each element is written as a permutation of the vertices
|
|
934
|
+
of the polytope.
|
|
935
|
+
|
|
936
|
+
OUTPUT:
|
|
937
|
+
|
|
938
|
+
A dictionary where the elements of ``conj_class_reps`` are keys and the
|
|
939
|
+
fixed subpolytopes are values.
|
|
940
|
+
|
|
941
|
+
.. NOTE::
|
|
942
|
+
|
|
943
|
+
Two elements in the same conjugacy class fix lattice-isomorphic
|
|
944
|
+
subpolytopes.
|
|
945
|
+
|
|
946
|
+
EXAMPLES:
|
|
947
|
+
|
|
948
|
+
Here is an example for the square::
|
|
949
|
+
|
|
950
|
+
sage: # optional - pynormaliz, needs sage.groups
|
|
951
|
+
sage: p = polytopes.hypercube(2, backend='normaliz'); p
|
|
952
|
+
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
|
|
953
|
+
sage: aut_p = p.restricted_automorphism_group(
|
|
954
|
+
....: output='permutation')
|
|
955
|
+
sage: aut_p.order()
|
|
956
|
+
8
|
|
957
|
+
sage: conj_list = aut_p.conjugacy_classes_representatives()
|
|
958
|
+
sage: fixedpolytopes_dict = p.fixed_subpolytopes(conj_list)
|
|
959
|
+
sage: fixedpolytopes_dict[aut_p([(0,3),(1,2)])]
|
|
960
|
+
A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex
|
|
961
|
+
|
|
962
|
+
TESTS::
|
|
963
|
+
|
|
964
|
+
sage: P = Polyhedron(vertices=[[1, 1]], rays=[[1, 1]])
|
|
965
|
+
sage: aut_P = P.restricted_automorphism_group(output='permutation') # needs sage.groups
|
|
966
|
+
sage: conj_list = aut_P.conjugacy_classes_representatives() # needs sage.groups
|
|
967
|
+
sage: P.fixed_subpolytopes(conj_list) # needs sage.groups
|
|
968
|
+
Traceback (most recent call last):
|
|
969
|
+
...
|
|
970
|
+
NotImplementedError: unbounded polyhedra are not supported
|
|
971
|
+
"""
|
|
972
|
+
if self.is_empty():
|
|
973
|
+
raise NotImplementedError('empty polyhedra are not supported')
|
|
974
|
+
if not self.is_compact():
|
|
975
|
+
raise NotImplementedError('unbounded polyhedra are not supported')
|
|
976
|
+
fixed_subpolytopes = {}
|
|
977
|
+
|
|
978
|
+
for element in conj_class_reps:
|
|
979
|
+
fixed_subpoly = self.fixed_subpolytope(element)
|
|
980
|
+
fixed_subpolytopes[element] = fixed_subpoly
|
|
981
|
+
return fixed_subpolytopes
|
|
982
|
+
|
|
983
|
+
def Hstar_function(self, acting_group=None, output=None):
|
|
984
|
+
r"""
|
|
985
|
+
Return `H^*` as a rational function in `t` with coefficients in
|
|
986
|
+
the ring of class functions of the ``acting_group``
|
|
987
|
+
of this polytope.
|
|
988
|
+
|
|
989
|
+
Here, `H^*(t) = \sum_{m} \chi_{m\text{self}}t^m \det(Id-\rho(t))`.
|
|
990
|
+
The irreducible characters of ``acting_group`` form an orthonormal basis
|
|
991
|
+
for the ring of class functions with values in `\CC`.
|
|
992
|
+
The coefficients of `H^*(t)` are expressed in this basis.
|
|
993
|
+
|
|
994
|
+
INPUT:
|
|
995
|
+
|
|
996
|
+
- ``acting_group`` -- (default=None) a permgroup object. A subgroup of
|
|
997
|
+
the polytope's ``restricted_automorphism_group``. If
|
|
998
|
+
``None``, it is set to the full ``restricted_automorphism_group`` of the
|
|
999
|
+
polytope. The acting group should always use ``output='permutation'``.
|
|
1000
|
+
|
|
1001
|
+
- ``output`` -- string. an output option. The allowed values are:
|
|
1002
|
+
|
|
1003
|
+
* ``None`` (default): returns the rational function `H^*(t)`. `H^*`
|
|
1004
|
+
is a rational function in `t` with coefficients in the ring of
|
|
1005
|
+
class functions.
|
|
1006
|
+
* ``'e_series_list'``: Returns a list of the ehrhart_series for the
|
|
1007
|
+
fixed_subpolytopes of each conjugacy class representative.
|
|
1008
|
+
* ``'determinant_vec'``: Returns a list of the determinants
|
|
1009
|
+
of `Id-\rho*t` for each conjugacy class representative.
|
|
1010
|
+
* ``'Hstar_as_lin_comb'``: Returns a vector of the coefficients
|
|
1011
|
+
of the irreducible representations in the expression of `H^*`.
|
|
1012
|
+
* ``'prod_det_es'``: Returns a vector of the product of
|
|
1013
|
+
determinants and the Ehrhart series.
|
|
1014
|
+
* ``'complete'``: Returns a list with ``Hstar``,
|
|
1015
|
+
``Hstar_as_lin_comb``, character table of the acting group, and
|
|
1016
|
+
whether ``Hstar`` is effective.
|
|
1017
|
+
|
|
1018
|
+
OUTPUT:
|
|
1019
|
+
|
|
1020
|
+
The default output is the rational function `H^*`. `H^*` is a rational
|
|
1021
|
+
function in `t` with coefficients in the ring of class functions.
|
|
1022
|
+
There are several output options to see the intermediary outputs of the
|
|
1023
|
+
function.
|
|
1024
|
+
|
|
1025
|
+
EXAMPLES:
|
|
1026
|
+
|
|
1027
|
+
The `H^*`-polynomial of the standard (`d-1`)-dimensional simplex
|
|
1028
|
+
`S = conv(e_1, \dots, e_d)` under its :meth:`restricted_automorphism_group`
|
|
1029
|
+
is equal to 1 = `\chi_{trivial}` (Prop 6.1 [Stap2011]_).
|
|
1030
|
+
Here is the computation for the 3-dimensional standard simplex::
|
|
1031
|
+
|
|
1032
|
+
sage: # optional - pynormaliz
|
|
1033
|
+
sage: S = polytopes.simplex(3, backend='normaliz'); S
|
|
1034
|
+
A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices
|
|
1035
|
+
sage: G = S.restricted_automorphism_group(
|
|
1036
|
+
....: output='permutation')
|
|
1037
|
+
sage: G.is_isomorphic(SymmetricGroup(4))
|
|
1038
|
+
True
|
|
1039
|
+
sage: Hstar = S._Hstar_function_normaliz(G); Hstar
|
|
1040
|
+
chi_4
|
|
1041
|
+
sage: G.character_table()
|
|
1042
|
+
[ 1 -1 1 1 -1]
|
|
1043
|
+
[ 3 -1 0 -1 1]
|
|
1044
|
+
[ 2 0 -1 2 0]
|
|
1045
|
+
[ 3 1 0 -1 -1]
|
|
1046
|
+
[ 1 1 1 1 1]
|
|
1047
|
+
|
|
1048
|
+
The next example is Example 7.6 in [Stap2011]_, and shows that `H^*` is not always
|
|
1049
|
+
a polynomial. Let P be the polytope with vertices
|
|
1050
|
+
`\pm(0,0,1),\pm(1,0,1), \pm(0,1,1), \pm(1,1,1)` and let
|
|
1051
|
+
G = `\Zmod{2}` act on P as follows::
|
|
1052
|
+
|
|
1053
|
+
sage: # optional - pynormaliz
|
|
1054
|
+
sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1],
|
|
1055
|
+
....: [-1,0,-1], [0,1,1],
|
|
1056
|
+
....: [0,-1,-1], [1,1,1], [-1,-1,-1]],
|
|
1057
|
+
....: backend='normaliz')
|
|
1058
|
+
sage: K = P.restricted_automorphism_group(
|
|
1059
|
+
....: output='permutation')
|
|
1060
|
+
sage: G = K.subgroup(gens=[K([(0,2),(1,3),(4,6),(5,7)])])
|
|
1061
|
+
sage: conj_reps = G.conjugacy_classes_representatives()
|
|
1062
|
+
sage: Dict = P.permutations_to_matrices(conj_reps,
|
|
1063
|
+
....: acting_group=G)
|
|
1064
|
+
sage: list(Dict.keys())[0]
|
|
1065
|
+
(0,2)(1,3)(4,6)(5,7)
|
|
1066
|
+
sage: list(Dict.values())[0]
|
|
1067
|
+
[-1 0 1 0]
|
|
1068
|
+
[ 0 1 0 0]
|
|
1069
|
+
[ 0 0 1 0]
|
|
1070
|
+
[ 0 0 0 1]
|
|
1071
|
+
sage: len(G)
|
|
1072
|
+
2
|
|
1073
|
+
sage: G.character_table()
|
|
1074
|
+
[ 1 1]
|
|
1075
|
+
[ 1 -1]
|
|
1076
|
+
|
|
1077
|
+
Then we calculate the rational function `H^*(t)`::
|
|
1078
|
+
|
|
1079
|
+
sage: Hst = P._Hstar_function_normaliz(G); Hst # optional - pynormaliz
|
|
1080
|
+
(chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
|
|
1081
|
+
+ (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
|
|
1082
|
+
|
|
1083
|
+
To see the exact as written in [Stap2011]_, we can format it as
|
|
1084
|
+
``'Hstar_as_lin_comb'``. The first coordinate is the coefficient of the
|
|
1085
|
+
trivial character; the second is the coefficient of the sign character::
|
|
1086
|
+
|
|
1087
|
+
sage: lin = P._Hstar_function_normaliz( # optional - pynormaliz
|
|
1088
|
+
....: G, output='Hstar_as_lin_comb'); lin
|
|
1089
|
+
((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1),
|
|
1090
|
+
(3*t^3 + 2*t^2 + 3*t)/(t + 1))
|
|
1091
|
+
"""
|
|
1092
|
+
if self.is_empty():
|
|
1093
|
+
raise NotImplementedError('empty polyhedra are not supported')
|
|
1094
|
+
if not self.is_compact():
|
|
1095
|
+
raise NotImplementedError('unbounded polyhedra are not supported')
|
|
1096
|
+
if not self.backend() == 'normaliz':
|
|
1097
|
+
raise TypeError("the backend of the polyhedron should be 'normaliz'")
|
|
1098
|
+
else:
|
|
1099
|
+
return self._Hstar_function_normaliz(acting_group, output)
|
|
1100
|
+
|
|
1101
|
+
def _Hstar_function_normaliz(self, acting_group=None, output=None):
|
|
1102
|
+
r"""
|
|
1103
|
+
Return `H^*` as a rational function in `t` with coefficients in
|
|
1104
|
+
the ring of class functions of the ``acting_group`` of ``self``.
|
|
1105
|
+
|
|
1106
|
+
INPUT:
|
|
1107
|
+
|
|
1108
|
+
- ``acting_group`` -- (default=None) a permgroup object. A subgroup of
|
|
1109
|
+
``self``'s ``restricted_automorphism_group`` output as a permutation.
|
|
1110
|
+
If ``None``, it is set to the full ``restricted_automorphism_group``
|
|
1111
|
+
of ``self``. The acting group should always use ``output='permutation'``.
|
|
1112
|
+
|
|
1113
|
+
- ``output`` -- string. an output option. The allowed values are:
|
|
1114
|
+
|
|
1115
|
+
* ``None`` (default): Returns the rational function `H^*(t)`. `H^*`
|
|
1116
|
+
is a rational function in `t` with coefficients in the ring of
|
|
1117
|
+
class functions.
|
|
1118
|
+
* ``'e_series_list'``: Returns a list of the ehrhart_series
|
|
1119
|
+
for the fixed_subpolytopes of each conjugacy class representative.
|
|
1120
|
+
* ``'determinant_vec'``: Returns a list of the determinants
|
|
1121
|
+
of `Id-\rho*t` for each conjugacy class representative.
|
|
1122
|
+
* ``'Hstar_as_lin_comb'``: Returns a vector of the coefficients
|
|
1123
|
+
of the irreducible representations in the expression of `H^*`.
|
|
1124
|
+
* ``'prod_det_es'``: Returns a vector of the product of
|
|
1125
|
+
determinants and the Ehrhart series.
|
|
1126
|
+
* ``'complete'``: Returns a list with ``Hstar``,
|
|
1127
|
+
``Hstar_as_lin_comb``, character table of the acting group, and
|
|
1128
|
+
whether ``Hstar`` is effective.
|
|
1129
|
+
|
|
1130
|
+
OUTPUT:
|
|
1131
|
+
|
|
1132
|
+
The default output is the rational function `H^*`. `H^*` is a rational
|
|
1133
|
+
function in `t` with coefficients in the ring of class functions.
|
|
1134
|
+
There are several output options to see the intermediary outputs of the
|
|
1135
|
+
function.
|
|
1136
|
+
|
|
1137
|
+
TESTS::
|
|
1138
|
+
|
|
1139
|
+
sage: p = Polyhedron(vertices = [[0],[1/2]])
|
|
1140
|
+
sage: p._Hstar_function_normaliz()
|
|
1141
|
+
Traceback (most recent call last):
|
|
1142
|
+
...
|
|
1143
|
+
TypeError: the backend of the polyhedron should be 'normaliz'
|
|
1144
|
+
"""
|
|
1145
|
+
raise TypeError("the backend of the polyhedron should be 'normaliz'")
|
|
1146
|
+
|
|
1147
|
+
def is_effective(self, Hstar, Hstar_as_lin_comb):
|
|
1148
|
+
r"""
|
|
1149
|
+
Test for the effectiveness of the ``Hstar`` series of this polytope.
|
|
1150
|
+
|
|
1151
|
+
The ``Hstar`` series of the polytope is determined by the action of a
|
|
1152
|
+
subgroup of the polytope's :meth:`restricted_automorphism_group`. The
|
|
1153
|
+
``Hstar`` series is effective if it is a polynomial in `t` and the
|
|
1154
|
+
coefficient of each `t^i` is an effective character in the ring of
|
|
1155
|
+
class functions of the acting group. A character `\rho` is effective if
|
|
1156
|
+
the coefficients of the irreducible representations in the expression
|
|
1157
|
+
of `\rho` are nonnegative integers.
|
|
1158
|
+
|
|
1159
|
+
INPUT:
|
|
1160
|
+
|
|
1161
|
+
- ``Hstar`` -- a rational function in `t` with coefficients in the ring
|
|
1162
|
+
of class functions
|
|
1163
|
+
|
|
1164
|
+
- ``Hstar_as_lin_comb`` -- vector. The coefficients of the irreducible
|
|
1165
|
+
representations of the acting group in the expression of ``Hstar`` as
|
|
1166
|
+
a linear combination of irreducible representations with coefficients
|
|
1167
|
+
in the field of rational functions in `t`.
|
|
1168
|
+
|
|
1169
|
+
OUTPUT: boolean; whether the ``Hstar`` series is effective
|
|
1170
|
+
|
|
1171
|
+
.. SEEALSO::
|
|
1172
|
+
|
|
1173
|
+
:meth:`Hstar_function`
|
|
1174
|
+
|
|
1175
|
+
EXAMPLES:
|
|
1176
|
+
|
|
1177
|
+
The `H^*` series of the two-dimensional permutahedron under the action
|
|
1178
|
+
of the symmetric group is effective::
|
|
1179
|
+
|
|
1180
|
+
sage: # optional - pynormaliz
|
|
1181
|
+
sage: p3 = polytopes.permutahedron(3, backend='normaliz')
|
|
1182
|
+
sage: G = p3.restricted_automorphism_group(
|
|
1183
|
+
....: output='permutation')
|
|
1184
|
+
sage: reflection12 = G([(0,2),(1,4),(3,5)])
|
|
1185
|
+
sage: reflection23 = G([(0,1),(2,3),(4,5)])
|
|
1186
|
+
sage: S3 = G.subgroup(gens=[reflection12, reflection23])
|
|
1187
|
+
sage: S3.is_isomorphic(SymmetricGroup(3))
|
|
1188
|
+
True
|
|
1189
|
+
sage: Hstar = p3.Hstar_function(S3)
|
|
1190
|
+
sage: Hlin = p3.Hstar_function(S3,
|
|
1191
|
+
....: output='Hstar_as_lin_comb')
|
|
1192
|
+
sage: p3.is_effective(Hstar, Hlin)
|
|
1193
|
+
True
|
|
1194
|
+
|
|
1195
|
+
If the `H^*`-series is not polynomial, then it is not effective::
|
|
1196
|
+
|
|
1197
|
+
sage: # optional - pynormaliz
|
|
1198
|
+
sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1],
|
|
1199
|
+
....: [-1,0,-1], [0,1,1],
|
|
1200
|
+
....: [0,-1,-1], [1,1,1], [-1,-1,-1]],
|
|
1201
|
+
....: backend='normaliz')
|
|
1202
|
+
sage: G = P.restricted_automorphism_group(
|
|
1203
|
+
....: output='permutation')
|
|
1204
|
+
sage: H = G.subgroup(gens=[G([(0,2),(1,3),(4,6),(5,7)])])
|
|
1205
|
+
sage: Hstar = P.Hstar_function(H); Hstar
|
|
1206
|
+
(chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
|
|
1207
|
+
+ (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
|
|
1208
|
+
sage: Hstar_lin = P.Hstar_function(H,
|
|
1209
|
+
....: output='Hstar_as_lin_comb')
|
|
1210
|
+
sage: P.is_effective(Hstar, Hstar_lin)
|
|
1211
|
+
False
|
|
1212
|
+
"""
|
|
1213
|
+
if self.is_empty():
|
|
1214
|
+
raise NotImplementedError('empty polyhedra are not supported')
|
|
1215
|
+
if not self.is_compact():
|
|
1216
|
+
raise NotImplementedError('unbounded polyhedra are not supported')
|
|
1217
|
+
if self.backend() == 'normaliz':
|
|
1218
|
+
return self._is_effective_normaliz(Hstar, Hstar_as_lin_comb)
|
|
1219
|
+
else:
|
|
1220
|
+
raise TypeError("the backend of the polyhedron should be 'normaliz'")
|
|
1221
|
+
|
|
1222
|
+
def _is_effective_normaliz(self, Hstar, Hstar_as_lin_comb):
|
|
1223
|
+
r"""
|
|
1224
|
+
Test for the effectiveness of the ``Hstar`` series of this polytope.
|
|
1225
|
+
|
|
1226
|
+
The ``Hstar`` series of the polytope is determined by the action of a
|
|
1227
|
+
subgroup of the polytope's :meth:`restricted_automorphism_group`. The
|
|
1228
|
+
``Hstar`` series is effective if it is a polynomial in `t` and the
|
|
1229
|
+
coefficient of each `t^i` is an effective character in the ring of
|
|
1230
|
+
class functions of the acting group. A character `\rho` is effective if
|
|
1231
|
+
the coefficients of the irreducible representations in the expression
|
|
1232
|
+
of `\rho` are nonnegative integers.
|
|
1233
|
+
|
|
1234
|
+
INPUT:
|
|
1235
|
+
|
|
1236
|
+
- ``Hstar`` -- a rational function in `t` with coefficients in the ring
|
|
1237
|
+
of class functions
|
|
1238
|
+
|
|
1239
|
+
- ``Hstar_as_lin_comb`` -- vector. The coefficients of the irreducible
|
|
1240
|
+
representations of the acting group in the expression of ``Hstar`` as
|
|
1241
|
+
a linear combination of irreducible representations with coefficients
|
|
1242
|
+
in the field of rational functions in `t`.
|
|
1243
|
+
|
|
1244
|
+
OUTPUT: boolean; whether the ``Hstar`` series is effective
|
|
1245
|
+
|
|
1246
|
+
TESTS::
|
|
1247
|
+
|
|
1248
|
+
sage: p1 = Polyhedron(vertices=[[0], [1/2]])
|
|
1249
|
+
sage: p2 = Polyhedron(vertices=[[0], [1/2]], # optional - pynormaliz
|
|
1250
|
+
....: backend='normaliz')
|
|
1251
|
+
sage: Hstar = p2.Hstar_function() # optional - pynormaliz
|
|
1252
|
+
sage: Hstarlin = p2.Hstar_function(output='Hstar_as_lin_comb') # optional - pynormaliz
|
|
1253
|
+
sage: p1._is_effective_normaliz(Hstar, Hstarlin) # optional - pynormaliz
|
|
1254
|
+
Traceback (most recent call last):
|
|
1255
|
+
...
|
|
1256
|
+
TypeError: the backend of the polyhedron should be 'normaliz'
|
|
1257
|
+
"""
|
|
1258
|
+
raise TypeError("the backend of the polyhedron should be 'normaliz'")
|