passagemath-polyhedra 10.6.31rc3__cp314-cp314-musllinux_1_2_aarch64.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.

Files changed (208) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgcc_s-2d945d6c.so.1 +0 -0
  7. passagemath_polyhedra.libs/libgmp-28992bcb.so.10.5.0 +0 -0
  8. passagemath_polyhedra.libs/libgomp-1ede7ee7.so.1.0.0 +0 -0
  9. passagemath_polyhedra.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
  10. sage/all__sagemath_polyhedra.py +50 -0
  11. sage/game_theory/all.py +8 -0
  12. sage/game_theory/catalog.py +6 -0
  13. sage/game_theory/catalog_normal_form_games.py +923 -0
  14. sage/game_theory/cooperative_game.py +844 -0
  15. sage/game_theory/matching_game.py +1181 -0
  16. sage/game_theory/normal_form_game.py +2697 -0
  17. sage/game_theory/parser.py +275 -0
  18. sage/geometry/all__sagemath_polyhedra.py +22 -0
  19. sage/geometry/cone.py +6940 -0
  20. sage/geometry/cone_catalog.py +847 -0
  21. sage/geometry/cone_critical_angles.py +1027 -0
  22. sage/geometry/convex_set.py +1119 -0
  23. sage/geometry/fan.py +3743 -0
  24. sage/geometry/fan_isomorphism.py +389 -0
  25. sage/geometry/fan_morphism.py +1884 -0
  26. sage/geometry/hasse_diagram.py +202 -0
  27. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  28. sage/geometry/hyperplane_arrangement/all.py +1 -0
  29. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  30. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  31. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  32. sage/geometry/hyperplane_arrangement/library.py +825 -0
  33. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  34. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  35. sage/geometry/integral_points.py +35 -0
  36. sage/geometry/integral_points_generic_dense.cpython-314-aarch64-linux-musl.so +0 -0
  37. sage/geometry/integral_points_generic_dense.pyx +7 -0
  38. sage/geometry/lattice_polytope.py +5894 -0
  39. sage/geometry/linear_expression.py +773 -0
  40. sage/geometry/newton_polygon.py +767 -0
  41. sage/geometry/point_collection.cpython-314-aarch64-linux-musl.so +0 -0
  42. sage/geometry/point_collection.pyx +1008 -0
  43. sage/geometry/polyhedral_complex.py +2616 -0
  44. sage/geometry/polyhedron/all.py +8 -0
  45. sage/geometry/polyhedron/backend_cdd.py +460 -0
  46. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  47. sage/geometry/polyhedron/backend_field.py +347 -0
  48. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  49. sage/geometry/polyhedron/backend_number_field.py +168 -0
  50. sage/geometry/polyhedron/backend_polymake.py +765 -0
  51. sage/geometry/polyhedron/backend_ppl.py +582 -0
  52. sage/geometry/polyhedron/base.py +1206 -0
  53. sage/geometry/polyhedron/base0.py +1444 -0
  54. sage/geometry/polyhedron/base1.py +886 -0
  55. sage/geometry/polyhedron/base2.py +812 -0
  56. sage/geometry/polyhedron/base3.py +1845 -0
  57. sage/geometry/polyhedron/base4.py +1262 -0
  58. sage/geometry/polyhedron/base5.py +2700 -0
  59. sage/geometry/polyhedron/base6.py +1741 -0
  60. sage/geometry/polyhedron/base7.py +997 -0
  61. sage/geometry/polyhedron/base_QQ.py +1258 -0
  62. sage/geometry/polyhedron/base_RDF.py +98 -0
  63. sage/geometry/polyhedron/base_ZZ.py +934 -0
  64. sage/geometry/polyhedron/base_mutable.py +215 -0
  65. sage/geometry/polyhedron/base_number_field.py +122 -0
  66. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-aarch64-linux-musl.so +0 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-aarch64-linux-musl.so +0 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-aarch64-linux-musl.so +0 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-aarch64-linux-musl.so +0 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-aarch64-linux-musl.so +0 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-aarch64-linux-musl.so +0 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-aarch64-linux-musl.so +0 -0
  88. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  89. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  90. sage/geometry/polyhedron/constructor.py +773 -0
  91. sage/geometry/polyhedron/double_description.py +753 -0
  92. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  93. sage/geometry/polyhedron/face.py +1060 -0
  94. sage/geometry/polyhedron/generating_function.py +1810 -0
  95. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  96. sage/geometry/polyhedron/library.py +3502 -0
  97. sage/geometry/polyhedron/misc.py +121 -0
  98. sage/geometry/polyhedron/modules/all.py +1 -0
  99. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  100. sage/geometry/polyhedron/palp_database.py +447 -0
  101. sage/geometry/polyhedron/parent.py +1279 -0
  102. sage/geometry/polyhedron/plot.py +1986 -0
  103. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  104. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  105. sage/geometry/polyhedron/representation.py +1723 -0
  106. sage/geometry/pseudolines.py +515 -0
  107. sage/geometry/relative_interior.py +445 -0
  108. sage/geometry/toric_plotter.py +1103 -0
  109. sage/geometry/triangulation/all.py +2 -0
  110. sage/geometry/triangulation/base.cpython-314-aarch64-linux-musl.so +0 -0
  111. sage/geometry/triangulation/base.pyx +963 -0
  112. sage/geometry/triangulation/data.h +147 -0
  113. sage/geometry/triangulation/data.pxd +4 -0
  114. sage/geometry/triangulation/element.py +914 -0
  115. sage/geometry/triangulation/functions.h +10 -0
  116. sage/geometry/triangulation/functions.pxd +4 -0
  117. sage/geometry/triangulation/point_configuration.py +2256 -0
  118. sage/geometry/triangulation/triangulations.h +49 -0
  119. sage/geometry/triangulation/triangulations.pxd +7 -0
  120. sage/geometry/voronoi_diagram.py +319 -0
  121. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  122. sage/interfaces/polymake.py +2028 -0
  123. sage/numerical/all.py +13 -0
  124. sage/numerical/all__sagemath_polyhedra.py +11 -0
  125. sage/numerical/backends/all.py +1 -0
  126. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  127. sage/numerical/backends/cvxopt_backend.cpython-314-aarch64-linux-musl.so +0 -0
  128. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  129. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  130. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-aarch64-linux-musl.so +0 -0
  131. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  132. sage/numerical/backends/cvxpy_backend.cpython-314-aarch64-linux-musl.so +0 -0
  133. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  134. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  135. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  136. sage/numerical/backends/generic_backend_test.py +24 -0
  137. sage/numerical/backends/interactivelp_backend.cpython-314-aarch64-linux-musl.so +0 -0
  138. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  139. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  140. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  141. sage/numerical/backends/logging_backend.py +391 -0
  142. sage/numerical/backends/matrix_sdp_backend.cpython-314-aarch64-linux-musl.so +0 -0
  143. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  144. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  145. sage/numerical/backends/ppl_backend.cpython-314-aarch64-linux-musl.so +0 -0
  146. sage/numerical/backends/ppl_backend.pyx +1126 -0
  147. sage/numerical/backends/ppl_backend_test.py +13 -0
  148. sage/numerical/backends/scip_backend.cpython-314-aarch64-linux-musl.so +0 -0
  149. sage/numerical/backends/scip_backend.pxd +22 -0
  150. sage/numerical/backends/scip_backend.pyx +1289 -0
  151. sage/numerical/backends/scip_backend_test.py +13 -0
  152. sage/numerical/interactive_simplex_method.py +5338 -0
  153. sage/numerical/knapsack.py +665 -0
  154. sage/numerical/linear_functions.cpython-314-aarch64-linux-musl.so +0 -0
  155. sage/numerical/linear_functions.pxd +31 -0
  156. sage/numerical/linear_functions.pyx +1648 -0
  157. sage/numerical/linear_tensor.py +470 -0
  158. sage/numerical/linear_tensor_constraints.py +448 -0
  159. sage/numerical/linear_tensor_element.cpython-314-aarch64-linux-musl.so +0 -0
  160. sage/numerical/linear_tensor_element.pxd +6 -0
  161. sage/numerical/linear_tensor_element.pyx +459 -0
  162. sage/numerical/mip.cpython-314-aarch64-linux-musl.so +0 -0
  163. sage/numerical/mip.pxd +40 -0
  164. sage/numerical/mip.pyx +3667 -0
  165. sage/numerical/sdp.cpython-314-aarch64-linux-musl.so +0 -0
  166. sage/numerical/sdp.pxd +39 -0
  167. sage/numerical/sdp.pyx +1433 -0
  168. sage/rings/all__sagemath_polyhedra.py +3 -0
  169. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  170. sage/rings/polynomial/omega.py +982 -0
  171. sage/schemes/all__sagemath_polyhedra.py +2 -0
  172. sage/schemes/toric/all.py +10 -0
  173. sage/schemes/toric/chow_group.py +1248 -0
  174. sage/schemes/toric/divisor.py +2082 -0
  175. sage/schemes/toric/divisor_class.cpython-314-aarch64-linux-musl.so +0 -0
  176. sage/schemes/toric/divisor_class.pyx +322 -0
  177. sage/schemes/toric/fano_variety.py +1606 -0
  178. sage/schemes/toric/homset.py +650 -0
  179. sage/schemes/toric/ideal.py +451 -0
  180. sage/schemes/toric/library.py +1322 -0
  181. sage/schemes/toric/morphism.py +1958 -0
  182. sage/schemes/toric/points.py +1032 -0
  183. sage/schemes/toric/sheaf/all.py +1 -0
  184. sage/schemes/toric/sheaf/constructor.py +302 -0
  185. sage/schemes/toric/sheaf/klyachko.py +921 -0
  186. sage/schemes/toric/toric_subscheme.py +905 -0
  187. sage/schemes/toric/variety.py +3460 -0
  188. sage/schemes/toric/weierstrass.py +1078 -0
  189. sage/schemes/toric/weierstrass_covering.py +457 -0
  190. sage/schemes/toric/weierstrass_higher.py +288 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  194. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  195. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  205. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  206. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  207. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  208. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,2503 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ # sage.doctest: optional - pynormaliz
3
+ """
4
+ The Normaliz backend for polyhedral computations
5
+
6
+ .. NOTE::
7
+
8
+ This backend requires `PyNormaliz <https://pypi.org/project/PyNormaliz>`_.
9
+ To install PyNormaliz, type :code:`sage -i pynormaliz` in the terminal.
10
+
11
+ AUTHORS:
12
+
13
+ - Matthias Köppe (2016-12): initial version
14
+ - Jean-Philippe Labbé (2019-04): Expose normaliz features and added functionalities
15
+ """
16
+ # ****************************************************************************
17
+ # Copyright (C) 2016-2022 Matthias Köppe <mkoeppe at math.ucdavis.edu>
18
+ # 2016-2018 Travis Scrimshaw
19
+ # 2017 Jeroen Demeyer
20
+ # 2018-2020 Jean-Philippe Labbé
21
+ # 2019 Vincent Delecroix
22
+ # 2019-2021 Jonathan Kliem
23
+ # 2019-2021 Sophia Elia
24
+ # 2020 Frédéric Chapoton
25
+ # 2022 Yuan Zhou
26
+ #
27
+ # This program is free software: you can redistribute it and/or modify
28
+ # it under the terms of the GNU General Public License as published by
29
+ # the Free Software Foundation, either version 2 of the License, or
30
+ # (at your option) any later version.
31
+ # https://www.gnu.org/licenses/
32
+ # ****************************************************************************
33
+
34
+ from sage.structure.element import Element
35
+ from sage.misc.cachefunc import cached_method
36
+ from sage.misc.misc_c import prod
37
+ from sage.misc.lazy_import import lazy_import
38
+ import sage.features.normaliz
39
+ lazy_import('PyNormaliz', ['NmzResult', 'NmzCompute', 'NmzCone', 'NmzConeCopy'],
40
+ feature=sage.features.normaliz.PyNormaliz())
41
+
42
+ from sage.rings.integer_ring import ZZ
43
+ from sage.rings.rational_field import QQ
44
+ from sage.arith.functions import LCM_list
45
+ from sage.misc.functional import denominator
46
+ from sage.matrix.constructor import vector
47
+
48
+ from .base_QQ import Polyhedron_QQ
49
+ from .base_ZZ import Polyhedron_ZZ
50
+ from .base_number_field import Polyhedron_base_number_field
51
+
52
+
53
+ def _format_function_call(fn_name, *v, **k):
54
+ """
55
+ Return a Python function call as a string.
56
+
57
+ Keywords are sorted.
58
+
59
+ EXAMPLES::
60
+
61
+ sage: from sage.geometry.polyhedron.backend_normaliz import _format_function_call
62
+ sage: _format_function_call('foo', 17, hellooooo='goodbyeeee')
63
+ "foo(17, hellooooo='goodbyeeee')"
64
+ """
65
+ args = [repr(a) for a in v] + ["%s=%r" % (arg, val) for arg, val in sorted(k.items())]
66
+ return "{}({})".format(fn_name, ", ".join(args))
67
+
68
+
69
+ #########################################################################
70
+ class Polyhedron_normaliz(Polyhedron_base_number_field):
71
+ """
72
+ Polyhedra with normaliz.
73
+
74
+ INPUT:
75
+
76
+ - ``parent`` -- :class:`~sage.geometry.polyhedron.parent.Polyhedra`
77
+ the parent
78
+
79
+ - ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``; the
80
+ V-representation of the polyhedron; if ``None``, the polyhedron
81
+ is determined by the H-representation
82
+
83
+ - ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``; the
84
+ H-representation of the polyhedron; if ``None``, the polyhedron
85
+ is determined by the V-representation
86
+
87
+ - ``normaliz_cone`` -- a PyNormaliz wrapper of a normaliz cone
88
+
89
+ Only one of ``Vrep``, ``Hrep``, or ``normaliz_cone`` can be different
90
+ from ``None``.
91
+
92
+ EXAMPLES::
93
+
94
+ sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
95
+ ....: rays=[(1,1)], lines=[],
96
+ ....: backend='normaliz')
97
+ sage: TestSuite(p).run()
98
+
99
+ Two ways to get the full space::
100
+
101
+ sage: Polyhedron(eqns=[[0, 0, 0]], backend='normaliz')
102
+ A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 lines
103
+ sage: Polyhedron(ieqs=[[0, 0, 0]], backend='normaliz')
104
+ A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 lines
105
+
106
+ A lower-dimensional affine cone; we test that there are no mysterious
107
+ inequalities coming in from the homogenization::
108
+
109
+ sage: P = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)],
110
+ ....: backend='normaliz')
111
+ sage: P.n_inequalities()
112
+ 1
113
+ sage: P.equations()
114
+ (An equation (1, 0) x - 1 == 0,)
115
+
116
+ The empty polyhedron::
117
+
118
+ sage: P = Polyhedron(ieqs=[[-2, 1, 1], [-3, -1, -1], [-4, 1, -2]],
119
+ ....: backend='normaliz')
120
+ sage: P
121
+ The empty polyhedron in QQ^2
122
+ sage: P.Vrepresentation()
123
+ ()
124
+ sage: P.Hrepresentation()
125
+ (An equation -1 == 0,)
126
+
127
+ TESTS:
128
+
129
+ Tests copied from various methods in :mod:`sage.geometry.polyhedron.base`::
130
+
131
+ sage: p = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]],
132
+ ....: backend='normaliz')
133
+ sage: p.n_equations()
134
+ 1
135
+ sage: p.n_inequalities()
136
+ 3
137
+
138
+ sage: p = Polyhedron(vertices=[[t,t^2,t^3] for t in range(6)],
139
+ ....: backend='normaliz')
140
+ sage: p.n_facets()
141
+ 8
142
+
143
+ sage: p = Polyhedron(vertices=[[1,0], [0,1], [1,1]], rays=[[1,1]],
144
+ ....: backend='normaliz')
145
+ sage: p.n_vertices()
146
+ 2
147
+
148
+ sage: p = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]],
149
+ ....: backend='normaliz')
150
+ sage: p.n_rays()
151
+ 1
152
+
153
+ sage: p = Polyhedron(vertices=[[0,0]], rays=[[0,1], [0,-1]],
154
+ ....: backend='normaliz')
155
+ sage: p.n_lines()
156
+ 1
157
+
158
+ Algebraic polyhedra::
159
+
160
+ sage: P = Polyhedron(vertices=[[1], [sqrt(2)]], # needs sage.rings.number_field sage.symbolic
161
+ ....: backend='normaliz', verbose=True)
162
+ # ----8<---- Equivalent Normaliz input file ----8<----
163
+ amb_space 1
164
+ number_field min_poly (a^2 - 2) embedding [1.414213562373095 +/- 2.99e-16]
165
+ cone 0
166
+ subspace 0
167
+ vertices 2
168
+ 1 1
169
+ (a) 1
170
+ # ----8<-------------------8<-------------------8<----
171
+ # Calling PyNormaliz.NmzCone(cone=[], number_field=['a^2 - 2', 'a', '[1.414213562373095 +/- 2.99e-16]'], subspace=[], vertices=[[1, 1], [[[0, 1], [1, 1]], 1]])
172
+ sage: P # needs sage.rings.number_field sage.symbolic
173
+ A 1-dimensional polyhedron in (Symbolic Ring)^1 defined as
174
+ the convex hull of 2 vertices
175
+ sage: P.vertices() # needs sage.rings.number_field sage.symbolic
176
+ (A vertex at (1), A vertex at (sqrt(2)))
177
+
178
+ sage: P = polytopes.icosahedron(exact=True, # needs sage.rings.number_field
179
+ ....: backend='normaliz'); P
180
+ A 3-dimensional polyhedron in
181
+ (Number Field in sqrt5 with defining polynomial x^2 - 5
182
+ with sqrt5 = 2.236067977499790?)^3
183
+ defined as the convex hull of 12 vertices
184
+
185
+ sage: x = polygen(ZZ)
186
+ sage: P = Polyhedron(vertices=[[sqrt(2)], # needs sage.rings.number_field sage.symbolic
187
+ ....: [AA.polynomial_root(x^3 - 2, RIF(0,3))]],
188
+ ....: backend='normaliz', verbose=True)
189
+ # ----8<---- Equivalent Normaliz input file ----8<----
190
+ amb_space 1
191
+ number_field min_poly (a^6 - 2) embedding [1.122462048309373 +/- 5.38e-16]
192
+ cone 0
193
+ subspace 0
194
+ vertices 2
195
+ (a^3) 1
196
+ (a^2) 1
197
+ # ----8<-------------------8<-------------------8<----
198
+ # Calling PyNormaliz.NmzCone(cone=[], number_field=['a^6 - 2', 'a', '[1.122462048309373 +/- 5.38e-16]'], subspace=[], vertices=[[[[0, 1], [0, 1], [0, 1], [1, 1], [0, 1], [0, 1]], 1], [[[0, 1], [0, 1], [1, 1], [0, 1], [0, 1], [0, 1]], 1]])
199
+ sage: P # needs sage.rings.number_field sage.symbolic
200
+ A 1-dimensional polyhedron in (Symbolic Ring)^1 defined as
201
+ the convex hull of 2 vertices
202
+ sage: P.vertices() # needs sage.rings.number_field sage.symbolic
203
+ (A vertex at (2^(1/3)), A vertex at (sqrt(2)))
204
+ """
205
+ def __init__(self, parent, Vrep, Hrep, normaliz_cone=None, normaliz_data=None, internal_base_ring=None, **kwds):
206
+ """
207
+ Initialize the polyhedron.
208
+
209
+ See :class:`Polyhedron_normaliz` for a description of the input
210
+ data.
211
+
212
+ TESTS::
213
+
214
+ sage: p = Polyhedron(backend='normaliz')
215
+ sage: TestSuite(p).run()
216
+ sage: p = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)],
217
+ ....: backend='normaliz')
218
+ sage: TestSuite(p).run()
219
+ sage: p = Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],
220
+ ....: backend='normaliz')
221
+ sage: TestSuite(p).run()
222
+ """
223
+ if normaliz_cone:
224
+ if Hrep is not None or Vrep is not None or normaliz_data is not None:
225
+ raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None")
226
+ Element.__init__(self, parent=parent)
227
+ self._init_from_normaliz_cone(normaliz_cone, internal_base_ring)
228
+ elif normaliz_data:
229
+ if Hrep is not None or Vrep is not None:
230
+ raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None")
231
+ Element.__init__(self, parent=parent)
232
+ self._init_from_normaliz_data(normaliz_data, internal_base_ring)
233
+ else:
234
+ if internal_base_ring:
235
+ raise ValueError("if Vrep or Hrep are given, cannot provide internal_base_ring")
236
+ Polyhedron_base_number_field.__init__(self, parent, Vrep, Hrep, **kwds)
237
+
238
+ def _nmz_result(self, normaliz_cone, property):
239
+ """
240
+ Call PyNormaliz's NmzResult function with appropriate conversion between number format.
241
+
242
+ TESTS::
243
+
244
+ sage: p = Polyhedron(vertices=[(0, 0), (1, 0), (0, 1)], rays=[(1,1)],
245
+ ....: lines=[], backend='normaliz')
246
+ sage: p._nmz_result(p._normaliz_cone, 'EquivariantXyzzyModuleSeries')
247
+ Traceback (most recent call last):
248
+ ...
249
+ NormalizError: Some error in the normaliz input data detected: Unknown ConeProperty...
250
+
251
+ sage: # needs sage.rings.number_field
252
+ sage: x = polygen(QQ, 'x')
253
+ sage: K.<a> = NumberField(x^3 - 3, embedding=AA(3)**(1/3))
254
+ sage: p = Polyhedron(vertices=[(0, 0), (1, 1), (a, 3), (-1, a**2)],
255
+ ....: rays=[(-1,-a)], backend='normaliz')
256
+ sage: sorted(p._nmz_result(p._normaliz_cone, 'VerticesOfPolyhedron'))
257
+ [[-1, a^2, 1], [1, 1, 1], [a, 3, 1]]
258
+ sage: triangulation_generators = p._nmz_result(p._normaliz_cone,
259
+ ....: 'Triangulation')[1]
260
+ sage: sorted(triangulation_generators)
261
+ [[-a^2, -3, 0], [-1, a^2, 1], [0, 0, 1], [1, 1, 1], [a, 3, 1]]
262
+ sage: p._nmz_result(p._normaliz_cone, 'AffineDim') == 2
263
+ True
264
+ sage: p._nmz_result(p._normaliz_cone, 'EmbeddingDim') == 3
265
+ True
266
+ sage: p._nmz_result(p._normaliz_cone, 'ExtremeRays')
267
+ [[-1/3*a^2, -1, 0]]
268
+ sage: p._nmz_result(p._normaliz_cone, 'MaximalSubspace')
269
+ []
270
+ """
271
+ def rational_handler(list):
272
+ return QQ(tuple(list))
273
+
274
+ def nfelem_handler(coords):
275
+ # coords might be too short which is not accepted by Sage number field
276
+ v = list(coords) + [0] * (self._internal_base_ring.degree() - len(coords))
277
+ return self._internal_base_ring(v)
278
+ return NmzResult(normaliz_cone, property,
279
+ RationalHandler=rational_handler,
280
+ NumberfieldElementHandler=nfelem_handler)
281
+
282
+ def _init_from_normaliz_cone(self, normaliz_cone, internal_base_ring):
283
+ """
284
+ Construct polyhedron from a PyNormaliz wrapper of a normaliz cone.
285
+
286
+ TESTS::
287
+
288
+ sage: p = Polyhedron(backend='normaliz')
289
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
290
+ sage: Polyhedron_normaliz._init_from_Hrepresentation(p, [], []) # indirect doctest
291
+ """
292
+ if internal_base_ring is None:
293
+ internal_base_ring = QQ
294
+ self._internal_base_ring = internal_base_ring
295
+
296
+ if normaliz_cone and self._nmz_result(normaliz_cone, "AffineDim") < 0:
297
+ # Empty polyhedron. Special case because Normaliz defines the
298
+ # recession cone of an empty polyhedron given by an
299
+ # H-representation as the cone defined by the homogenized system.
300
+ self._init_empty_polyhedron()
301
+ else:
302
+ self._normaliz_cone = normaliz_cone
303
+ self._init_Vrepresentation_from_normaliz()
304
+ self._init_Hrepresentation_from_normaliz()
305
+
306
+ @staticmethod
307
+ def _convert_to_pynormaliz(x):
308
+ """
309
+ Convert a number or nested lists and tuples of numbers to pynormaliz input format.
310
+
311
+ TESTS::
312
+
313
+ sage: K.<sqrt2> = QuadraticField(2) # needs sage.rings.number_field
314
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz as Pn
315
+ sage: Pn._convert_to_pynormaliz(17)
316
+ 17
317
+ sage: Pn._convert_to_pynormaliz(901824309821093821093812093810928309183091832091)
318
+ 901824309821093821093812093810928309183091832091
319
+ sage: Pn._convert_to_pynormaliz(QQ(17))
320
+ 17
321
+ sage: Pn._convert_to_pynormaliz(28/5)
322
+ [[28, 5]]
323
+ sage: Pn._convert_to_pynormaliz(28901824309821093821093812093810928309183091832091/5234573685674784567853456543456456786543456765)
324
+ [[28901824309821093821093812093810928309183091832091, 5234573685674784567853456543456456786543456765]]
325
+ sage: Pn._convert_to_pynormaliz(7 + sqrt2) # needs sage.rings.number_field
326
+ [[7, 1], [1, 1]]
327
+ sage: Pn._convert_to_pynormaliz(7/2 + sqrt2) # needs sage.rings.number_field
328
+ [[7, 2], [1, 1]]
329
+ sage: Pn._convert_to_pynormaliz([[1, 2], (3, 4)])
330
+ [[1, 2], [3, 4]]
331
+
332
+ Check that :issue:`29836` is fixed::
333
+
334
+ sage: P = polytopes.simplex(backend='normaliz')
335
+ sage: K.<sqrt2> = QuadraticField(2) # needs sage.rings.number_field
336
+ sage: P.dilation(sqrt2) # needs sage.rings.number_field
337
+ A 3-dimensional polyhedron in
338
+ (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.41...)^4
339
+ defined as the convex hull of 4 vertices
340
+ """
341
+ def _QQ_pair(x):
342
+ x = QQ(x)
343
+ return [int(x.numerator()), int(x.denominator())]
344
+ from sage.rings.rational import Rational
345
+ from types import GeneratorType
346
+ if isinstance(x, (list, tuple, GeneratorType)):
347
+ return [Polyhedron_normaliz._convert_to_pynormaliz(y) for y in x]
348
+ try:
349
+ return int(ZZ(x))
350
+ except TypeError:
351
+ pass
352
+
353
+ if isinstance(x, Rational):
354
+ return [_QQ_pair(x)] # need extra brackets to distinguish from quadratic numberfield element
355
+ # number field
356
+ return [_QQ_pair(c) for c in x.list()]
357
+
358
+ def _init_from_normaliz_data(self, data, internal_base_ring=None, verbose=False):
359
+ """
360
+ Construct polyhedron from normaliz ``data`` (a dictionary).
361
+
362
+ TESTS::
363
+
364
+ sage: p = Polyhedron(backend='normaliz', ambient_dim=2)
365
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz
366
+ sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
367
+ sage: Polyhedron_QQ_normaliz._init_from_normaliz_data(p, data)
368
+ sage: p.inequalities_list()
369
+ [[0, -1, 2], [0, 2, -1]]
370
+
371
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
372
+ sage: from sage.rings.qqbar import AA # needs sage.rings.number_field
373
+ sage: from sage.rings.number_field.number_field import QuadraticField # needs sage.rings.number_field
374
+ sage: data = {'number_field': ['a^2 - 2', 'a', '[1.4 +/- 0.1]'],
375
+ ....: 'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
376
+ sage: from sage.geometry.polyhedron.parent import Polyhedra_normaliz
377
+ sage: parent = Polyhedra_normaliz(AA, 2, 'normaliz') # needs sage.rings.number_field
378
+ sage: Polyhedron_normaliz(parent, None, None, # needs sage.rings.number_field
379
+ ....: normaliz_data=data,
380
+ ....: internal_base_ring=QuadraticField(2))
381
+ A 2-dimensional polyhedron in AA^2 defined as the convex hull of 1 vertex and 2 rays
382
+ sage: _.inequalities_list() # needs sage.rings.number_field
383
+ [[0, -1/2, 1], [0, 2, -1]]
384
+ """
385
+ if internal_base_ring is None:
386
+ internal_base_ring = QQ
387
+ cone = self._cone_from_normaliz_data(data, verbose)
388
+ self._init_from_normaliz_cone(cone, internal_base_ring)
389
+
390
+ def _cone_from_normaliz_data(self, data, verbose=False):
391
+ """
392
+ Construct a normaliz cone from ``data`` (a dictionary).
393
+
394
+ EXAMPLES::
395
+
396
+ sage: p = Polyhedron(backend='normaliz', ambient_dim=2)
397
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz
398
+ sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
399
+ sage: cone = Polyhedron_QQ_normaliz._cone_from_normaliz_data(p, data)
400
+ sage: p._nmz_result(cone,'SupportHyperplanes')
401
+ [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]
402
+ """
403
+ if verbose:
404
+ if isinstance(verbose, str):
405
+ print("# Wrote equivalent Normaliz input file to {}".format(verbose))
406
+ self._normaliz_format(data, file_output=verbose)
407
+ else:
408
+ print("# ----8<---- Equivalent Normaliz input file ----8<----")
409
+ print(self._normaliz_format(data), end='')
410
+ print("# ----8<-------------------8<-------------------8<----")
411
+
412
+ for key, value in data.items():
413
+ if key != 'number_field':
414
+ data[key] = self._convert_to_pynormaliz(value)
415
+
416
+ if verbose:
417
+ print("# Calling {}".format(_format_function_call('PyNormaliz.NmzCone', **data)))
418
+
419
+ cone = NmzCone(**data)
420
+ assert cone, "{} did not return a cone".format(_format_function_call('PyNormaliz.NmzCone', **data))
421
+ return cone
422
+
423
+ def _is_zero(self, x) -> bool:
424
+ """
425
+ Test whether ``x`` is zero.
426
+
427
+ INPUT:
428
+
429
+ - ``x`` -- a number in the base ring
430
+
431
+ OUTPUT: boolean
432
+
433
+ EXAMPLES::
434
+
435
+ sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
436
+ sage: p._is_zero(0) # needs sage.rings.number_field sage.symbolic
437
+ True
438
+ sage: p._is_zero(1/100000) # needs sage.rings.number_field sage.symbolic
439
+ False
440
+ """
441
+ return x == 0
442
+
443
+ def _is_nonneg(self, x) -> bool:
444
+ """
445
+ Test whether ``x`` is nonnegative.
446
+
447
+ INPUT:
448
+
449
+ - ``x`` -- a number in the base ring
450
+
451
+ OUTPUT: boolean
452
+
453
+ EXAMPLES::
454
+
455
+ sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
456
+ sage: p._is_nonneg(1) # needs sage.rings.number_field sage.symbolic
457
+ True
458
+ sage: p._is_nonneg(-1/100000) # needs sage.rings.number_field sage.symbolic
459
+ False
460
+ """
461
+ return x >= 0
462
+
463
+ def _is_positive(self, x) -> bool:
464
+ """
465
+ Test whether ``x`` is positive.
466
+
467
+ INPUT:
468
+
469
+ - ``x`` -- a number in the base ring
470
+
471
+ OUTPUT: boolean
472
+
473
+ EXAMPLES::
474
+
475
+ sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
476
+ sage: p._is_positive(1) # needs sage.rings.number_field sage.symbolic
477
+ True
478
+ sage: p._is_positive(0) # needs sage.rings.number_field sage.symbolic
479
+ False
480
+ """
481
+ return x > 0
482
+
483
+ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False):
484
+ r"""
485
+ Construct polyhedron from V-representation data.
486
+
487
+ INPUT:
488
+
489
+ - ``vertices`` -- list of point; each point can be specified
490
+ as any iterable container of
491
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
492
+
493
+ - ``rays`` -- list of rays; each ray can be specified as any
494
+ iterable container of
495
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
496
+
497
+ - ``lines`` -- list of lines; each line can be specified as
498
+ any iterable container of
499
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
500
+
501
+ - ``verbose`` -- boolean (default: ``False``); whether to print
502
+ verbose output for debugging purposes
503
+
504
+ EXAMPLES::
505
+
506
+ sage: p = Polyhedron(backend='normaliz')
507
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
508
+ sage: Polyhedron_normaliz._init_from_Vrepresentation(p, [], [], [])
509
+ """
510
+
511
+ def vert_ray_line_QQ(vertices, rays, lines):
512
+ nmz_vertices = []
513
+ for v in vertices:
514
+ d = LCM_list([denominator(v_i) for v_i in v])
515
+ dv = [d * v_i for v_i in v]
516
+ nmz_vertices.append(dv + [d])
517
+ nmz_rays = []
518
+ for r in rays:
519
+ d = LCM_list([denominator(r_i) for r_i in r])
520
+ dr = [d * r_i for r_i in r]
521
+ nmz_rays.append(dr)
522
+ nmz_lines = []
523
+ for l in lines:
524
+ d = LCM_list([denominator(l_i) for l_i in l])
525
+ dl = [d * l_i for l_i in l]
526
+ nmz_lines.append(dl)
527
+ return nmz_vertices, nmz_rays, nmz_lines
528
+
529
+ def vert_ray_line_NF(vertices, rays, lines):
530
+ h_vertices = [list(v) + [1] for v in vertices]
531
+ return h_vertices, rays, lines
532
+
533
+ if vertices is None:
534
+ vertices = []
535
+ if rays is None:
536
+ rays = []
537
+ if lines is None:
538
+ lines = []
539
+
540
+ (nmz_vertices, nmz_rays, nmz_lines), internal_base_ring \
541
+ = self._compute_data_lists_and_internal_base_ring(
542
+ (vertices, rays, lines), vert_ray_line_QQ, vert_ray_line_NF)
543
+
544
+ if not nmz_vertices and not nmz_rays and not nmz_lines:
545
+ # Special case to avoid:
546
+ # error: Some error in the normaliz input data detected:
547
+ # All input matrices empty!
548
+ self._init_empty_polyhedron()
549
+ else:
550
+ data = {"vertices": nmz_vertices,
551
+ "cone": nmz_rays,
552
+ "subspace": nmz_lines}
553
+ number_field_data = self._number_field_triple(internal_base_ring)
554
+ if number_field_data:
555
+ data["number_field"] = number_field_data
556
+ self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose)
557
+
558
+ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False):
559
+ r"""
560
+ Construct polyhedron from H-representation data.
561
+
562
+ INPUT:
563
+
564
+ - ``ieqs`` -- list of inequalities; each line can be specified
565
+ as any iterable container of
566
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
567
+
568
+ - ``eqns`` -- list of equalities; each line can be specified
569
+ as any iterable container of
570
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
571
+
572
+ - ``minimize`` -- boolean (default: ``True``); ignored
573
+
574
+ - ``verbose`` -- boolean (default: ``False``); whether to print
575
+ verbose output for debugging purposes
576
+
577
+ EXAMPLES::
578
+
579
+ sage: p = Polyhedron(backend='normaliz')
580
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
581
+ sage: Polyhedron_normaliz._init_from_Hrepresentation(p, [], [])
582
+
583
+ TESTS::
584
+
585
+ sage: K.<a> = QuadraticField(2) # needs sage.rings.number_field
586
+ sage: p = Polyhedron(ieqs=[(1, a, 0)], backend='normaliz') # needs sage.rings.number_field
587
+ sage: p & p == p # needs sage.rings.number_field
588
+ True
589
+
590
+ Check that :issue:`30248` is fixed, that maps as input works::
591
+
592
+ sage: # needs sage.rings.number_field
593
+ sage: q = Polyhedron(backend='normaliz', base_ring=AA,
594
+ ....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)])
595
+ sage: def make_new_Hrep(h):
596
+ ....: return tuple(x if i == 0 else -1*x
597
+ ....: for i, x in enumerate(h._vector))
598
+ sage: new_inequalities = map(make_new_Hrep, q.inequality_generator())
599
+ sage: new_equations = map(make_new_Hrep, q.equation_generator())
600
+ sage: parent = q.parent()
601
+ sage: new_q = parent.element_class(parent, None,
602
+ ....: [new_inequalities, new_equations])
603
+ sage: new_q
604
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays
605
+ """
606
+
607
+ def nmz_ieqs_eqns_NF(ieqs, eqns):
608
+ nmz_ieqs = [list(ieq[1:]) + [ieq[0]] for ieq in ieqs]
609
+ nmz_eqns = [list(eqn[1:]) + [eqn[0]] for eqn in eqns]
610
+ return nmz_ieqs, nmz_eqns
611
+
612
+ def nmz_ieqs_eqns_QQ(ieqs, eqns):
613
+ nmz_ieqs = []
614
+ for ieq in ieqs:
615
+ d = LCM_list([denominator(ieq_i) for ieq_i in ieq])
616
+ dieq = [ZZ(d * ieq_i) for ieq_i in ieq]
617
+ b = dieq[0]
618
+ A = dieq[1:]
619
+ nmz_ieqs.append(A + [b])
620
+ nmz_eqns = []
621
+ for eqn in eqns:
622
+ d = LCM_list([denominator(eqn_i) for eqn_i in eqn])
623
+ deqn = [ZZ(d * eqn_i) for eqn_i in eqn]
624
+ b = deqn[0]
625
+ A = deqn[1:]
626
+ nmz_eqns.append(A + [b])
627
+ return nmz_ieqs, nmz_eqns
628
+
629
+ if ieqs is None:
630
+ ieqs = []
631
+ if eqns is None:
632
+ eqns = []
633
+
634
+ (nmz_ieqs, nmz_eqns), internal_base_ring \
635
+ = self._compute_data_lists_and_internal_base_ring(
636
+ (ieqs, eqns), nmz_ieqs_eqns_QQ, nmz_ieqs_eqns_NF)
637
+ if not nmz_ieqs:
638
+ # If normaliz gets an empty list of inequalities, it adds
639
+ # nonnegativities. So let's add a tautological inequality to work
640
+ # around this.
641
+ nmz_ieqs.append([0] * self.ambient_dim() + [0])
642
+ data = {"inhom_equations": nmz_eqns,
643
+ "inhom_inequalities": nmz_ieqs}
644
+ number_field_data = self._number_field_triple(internal_base_ring)
645
+ if number_field_data:
646
+ data["number_field"] = number_field_data
647
+ self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose)
648
+
649
+ def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, ieqs, eqns=None, verbose=False, homogeneous=False):
650
+ r"""
651
+ Construct cone from V-representation data and H-representation data.
652
+
653
+ INPUT:
654
+
655
+ - ``vertices`` -- list of point; each point can be specified
656
+ as any iterable container of
657
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
658
+
659
+ - ``rays`` -- list of rays; each ray can be specified as any
660
+ iterable container of
661
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
662
+
663
+ - ``ieqs`` -- list of inequalities; each line can be specified
664
+ as any iterable container of
665
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
666
+
667
+ - ``eqns`` -- list of equalities; each line can be specified
668
+ as any iterable container of
669
+ :meth:`~sage.geometry.polyhedron.base.base_ring` elements
670
+
671
+ - ``verbose`` -- boolean (default: ``False``); whether to print
672
+ verbose output for debugging purposes
673
+
674
+ - ``homogeneous`` -- boolean (default: ``False``); if ``True`` set
675
+ up the cone without explicit inhomogenization
676
+
677
+ EXAMPLES::
678
+
679
+ sage: P = (polytopes.hypercube(4, backend='normaliz')
680
+ ....: * Polyhedron(rays=[[0,1]])
681
+ ....: * Polyhedron(lines=[[1,0]])); P
682
+ A 6-dimensional polyhedron in ZZ^8 defined as the convex hull of 16 vertices, 1 ray, 1 line
683
+
684
+ sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
685
+ ....: P.vertices(), P.rays(), P.lines(),
686
+ ....: P.inequalities(), P.equations())
687
+ sage: import PyNormaliz
688
+ sage: PyNormaliz.NmzIsComputed(cone, "VerticesOfPolyhedron")
689
+ True
690
+ sage: PyNormaliz.NmzIsComputed(cone, "ExtremeRays")
691
+ True
692
+ sage: PyNormaliz.NmzIsComputed(cone, "MaximalSubspace")
693
+ True
694
+ sage: PyNormaliz.NmzIsComputed(cone, "SupportHyperplanes")
695
+ True
696
+ sage: PyNormaliz.NmzIsComputed(cone, "Equations")
697
+ False
698
+
699
+ All values must be specified::
700
+
701
+ sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
702
+ ....: P.vertices(), None, P.lines(),
703
+ ....: P.inequalities(), P.equations())
704
+ Traceback (most recent call last):
705
+ ...
706
+ ValueError: please specify vertices, rays, lines, inequalities and equations completely
707
+
708
+ This method cannot be used for the empty cone::
709
+
710
+ sage: P = Polyhedron(backend='normaliz')
711
+ sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
712
+ ....: P.vertices(), P.rays(), P.lines(),
713
+ ....: P.inequalities(), P.equations())
714
+ Traceback (most recent call last):
715
+ ...
716
+ ValueError: this method cannot be used to initialize the empty cone
717
+
718
+ TESTS::
719
+
720
+ sage: def test_poly(P):
721
+ ....: cone = P._cone_from_Vrepresentation_and_Hrepresentation(P.vertices(),P.rays(),P.lines(),P.inequalities(),P.equations())
722
+ ....: cone2 = P._normaliz_cone
723
+ ....: args = ['Equations','VerticesOfPolyhedron','ExtremeRays','SupportHyperplanes','MaximalSubspace']
724
+ ....: return all(P._nmz_result(cone,arg) == P._nmz_result(cone2, arg)
725
+ ....: for arg in args)
726
+ sage: test_poly(polytopes.simplex(backend='normaliz'))
727
+ True
728
+ sage: test_poly(polytopes.dodecahedron(backend='normaliz')) # needs sage.rings.number_field
729
+ True
730
+ sage: test_poly(Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]],
731
+ ....: backend='normaliz'))
732
+ True
733
+ sage: test_poly(Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]],
734
+ ....: backend='normaliz'))
735
+ True
736
+ sage: test_poly(Polyhedron(rays=[[1,0,0],[0,1,0]],
737
+ ....: backend='normaliz'))
738
+ True
739
+ sage: test_poly(Polyhedron(vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0], [0,1,0]],
740
+ ....: backend='normaliz'))
741
+ True
742
+ sage: test_poly(Polyhedron(vertices=[[0,0,0], [0,1,1], [1,0,1], [-1,-1,1]],
743
+ ....: rays=[[0,0,1]],
744
+ ....: backend='normaliz'))
745
+ True
746
+
747
+ Old input format will give a meaningful error message::
748
+
749
+ sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
750
+ ....: P.vertices(), P.rays(),
751
+ ....: P.inequalities(), P.equations())
752
+ Traceback (most recent call last):
753
+ ...
754
+ ValueError: the specification of this method has changed; please specify the lines as well
755
+
756
+ sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
757
+ ....: P.vertices(), P.rays(),
758
+ ....: P.inequalities(), P.equations(), True)
759
+ Traceback (most recent call last):
760
+ ...
761
+ ValueError: the specification of this method has changed; please specify the lines as well
762
+
763
+ Check that :issue:`30891` is fixed::
764
+
765
+ sage: p = Polyhedron(vertices=[(-3,-3), (3,0), (3,3), (0,3)],
766
+ ....: backend='normaliz')
767
+ sage: q = loads(p.dumps())
768
+ sage: q.volume()
769
+ 18
770
+ sage: q.ehrhart_series()
771
+ (13*t^2 + 22*t + 1)/(-t^3 + 3*t^2 - 3*t + 1)
772
+ """
773
+ if eqns in (True, False, None):
774
+ # Previously, the method had input ``vertices, rays, ieqs, eqns`` (optionally ``verbose``).
775
+ # Now it requires ``vertices, rays, lines, ieqs, eqns``.
776
+ # Actually, ``eqns`` wouldn't be required, but we keep it to catch deprecated calls.
777
+ # (And it's more stable against changes of normaliz now.)
778
+ raise ValueError("the specification of this method has changed; please specify the lines as well")
779
+ if None in (vertices, rays, lines, ieqs, eqns):
780
+ raise ValueError("please specify vertices, rays, lines, inequalities and equations completely")
781
+ if not vertices:
782
+ raise ValueError("this method cannot be used to initialize the empty cone")
783
+
784
+ def rays_subspace_lattice_ieqs_QQ(vertices, rays, lines, ieqs):
785
+ nmz_vertices = []
786
+ for v in vertices:
787
+ d = LCM_list([denominator(v_i) for v_i in v])
788
+ dv = [d * v_i for v_i in v]
789
+ nmz_vertices.append(dv + [d])
790
+ nmz_rays = []
791
+ for r in rays:
792
+ d = LCM_list([denominator(r_i) for r_i in r])
793
+ dr = [d * r_i for r_i in r]
794
+ nmz_rays.append(dr + [0])
795
+ nmz_lines = []
796
+ for l in lines:
797
+ d = LCM_list([denominator(l_i) for l_i in l])
798
+ dl = [d * l_i for l_i in l]
799
+ nmz_lines.append(dl + [0])
800
+
801
+ nmz_ieqs = []
802
+ for ieq in ieqs:
803
+ d = LCM_list([denominator(ieq_i) for ieq_i in ieq])
804
+ dieq = [ZZ(d * ieq_i) for ieq_i in ieq]
805
+ b = dieq[0]
806
+ A = dieq[1:]
807
+ nmz_ieqs.append(A + [b])
808
+
809
+ from sage.matrix.constructor import Matrix
810
+ lattice = Matrix(ZZ, nmz_vertices + nmz_rays + nmz_lines).saturation()
811
+ nmz_lattice = [list(y) for y in lattice]
812
+
813
+ if Matrix(ZZ, nmz_vertices + nmz_rays).rank() == Matrix(ZZ, nmz_rays).rank() + 1:
814
+ # The recession cone is full-dimensional.
815
+ # In this case the homogenized inequalities
816
+ # do not ensure nonnegativy in the last coordinate.
817
+ # In the homogeneous cone the far face is a facet.
818
+ pos_ieq = [ZZ.zero()] * len(nmz_vertices[0])
819
+ pos_ieq[-1] = ZZ.one()
820
+ nmz_ieqs.append(pos_ieq)
821
+
822
+ return nmz_vertices + nmz_rays, nmz_lines, nmz_lattice, nmz_ieqs
823
+
824
+ def rays_subspace_lattice_ieqs_NF(vertices, rays, lines, ieqs):
825
+ nmz_vertices = [list(v) + [1] for v in vertices]
826
+ nmz_rays = [list(r) + [0] for r in rays]
827
+ nmz_lines = [list(l) + [1] for l in lines]
828
+
829
+ nmz_ieqs = []
830
+ for ieq in ieqs:
831
+ b = ieq[0]
832
+ A = ieq[1:]
833
+ nmz_ieqs.append(list(A) + [b])
834
+
835
+ from sage.matrix.constructor import Matrix
836
+ lattice = Matrix(nmz_vertices + nmz_rays + nmz_lines).row_space().basis()
837
+ nmz_lattice = [list(y) for y in lattice]
838
+
839
+ if Matrix(nmz_vertices + nmz_rays).rank() == Matrix(nmz_rays).rank() + 1:
840
+ # The recession cone is full-dimensional.
841
+ # In this case the homogenized inequalities
842
+ # do not ensure nonnegativy in the last coordinate.
843
+ # In the homogeneous cone the far face is a facet.
844
+ pos_ieq = [0] * len(nmz_vertices[0])
845
+ pos_ieq[-1] = 1
846
+ nmz_ieqs.append(pos_ieq)
847
+
848
+ return nmz_vertices + nmz_rays, nmz_lines, nmz_lattice, nmz_ieqs
849
+
850
+ (nmz_extreme_rays, nmz_subspace, nmz_lattice, nmz_ieqs), internal_base_ring \
851
+ = self._compute_data_lists_and_internal_base_ring(
852
+ (vertices, rays, lines, ieqs), rays_subspace_lattice_ieqs_QQ,
853
+ rays_subspace_lattice_ieqs_NF)
854
+
855
+ data = {"extreme_rays": nmz_extreme_rays,
856
+ "maximal_subspace": nmz_subspace,
857
+ "generated_lattice": nmz_lattice,
858
+ "support_hyperplanes": nmz_ieqs}
859
+
860
+ ambient_dim = len(data["extreme_rays"][0])
861
+ if not homogeneous:
862
+ data["dehomogenization"] = [[0] * (ambient_dim - 1) + [1]]
863
+
864
+ number_field_data = self._number_field_triple(internal_base_ring)
865
+ if number_field_data:
866
+ data["number_field"] = number_field_data
867
+ return self._cone_from_normaliz_data(data, verbose=verbose)
868
+
869
+ def _test_far_facet_condition(self, tester=None, **options):
870
+ """
871
+ Test that we add an extra inequality in the correct cases.
872
+
873
+ TESTS::
874
+
875
+ sage: P = Polyhedron(rays=[[1,1]], backend='normaliz')
876
+ sage: P._test_far_facet_condition()
877
+
878
+ sage: P = Polyhedron(vertices=[[1,0], [0,1]],
879
+ ....: rays=[[1,1]], backend='normaliz')
880
+ sage: P._test_far_facet_condition()
881
+
882
+ sage: P = Polyhedron(rays=[[1,1,0]],
883
+ ....: lines=[[0,0,1]], backend='normaliz')
884
+ sage: P._test_far_facet_condition()
885
+
886
+ sage: P = Polyhedron(vertices=[[1,0,0], [0,1,0]],
887
+ ....: rays=[[1,1,0]],
888
+ ....: lines=[[0,0,1]], backend='normaliz')
889
+ sage: P._test_far_facet_condition()
890
+ """
891
+ if tester is None:
892
+ tester = self._tester(**options)
893
+
894
+ if self.is_empty():
895
+ return
896
+
897
+ nmz_vertices = self._nmz_result(self._normaliz_cone, "VerticesOfPolyhedron")
898
+ nmz_rays = self._nmz_result(self._normaliz_cone, "ExtremeRays")
899
+ nmz_ieqs = self._nmz_result(self._normaliz_cone, "SupportHyperplanes")
900
+
901
+ from sage.matrix.constructor import Matrix
902
+ far_facet_condition = Matrix(nmz_vertices + nmz_rays).rank() == Matrix(nmz_rays).rank() + 1
903
+
904
+ tester.assertEqual(far_facet_condition, self.n_inequalities() != len(nmz_ieqs))
905
+
906
+ if far_facet_condition:
907
+ tester.assertEqual(self.n_inequalities() + 1, len(nmz_ieqs))
908
+ tester.assertTrue(any(ieq == [0] * self.ambient_dim() + [1] for ieq in nmz_ieqs))
909
+
910
+ def _init_Vrepresentation_from_normaliz(self):
911
+ r"""
912
+ Create the Vrepresentation objects from the normaliz polyhedron.
913
+
914
+ EXAMPLES::
915
+
916
+ sage: p = Polyhedron(vertices=[(0,1/2), (2,0), (4,5/6)], # indirect doctest
917
+ ....: backend='normaliz')
918
+ sage: p.Hrepresentation()
919
+ (An inequality (-5, 12) x + 10 >= 0,
920
+ An inequality (1, -12) x + 6 >= 0,
921
+ An inequality (1, 4) x - 2 >= 0)
922
+ sage: p.Vrepresentation()
923
+ (A vertex at (0, 1/2), A vertex at (2, 0), A vertex at (4, 5/6))
924
+ """
925
+ self._Vrepresentation = []
926
+ parent = self.parent()
927
+ base_ring = self.base_ring()
928
+ cone = self._normaliz_cone
929
+ for g in self._nmz_result(cone, "VerticesOfPolyhedron"):
930
+ d = g[-1]
931
+ if d == 1:
932
+ parent._make_Vertex(self, g[:-1])
933
+ else:
934
+ parent._make_Vertex(self, [base_ring(x) / d for x in g[:-1]])
935
+ for g in self._nmz_result(cone, "ExtremeRays"):
936
+ parent._make_Ray(self, g[:-1])
937
+ for g in self._nmz_result(cone, "MaximalSubspace"):
938
+ parent._make_Line(self, g[:-1])
939
+ self._Vrepresentation = tuple(self._Vrepresentation)
940
+
941
+ def _init_Hrepresentation_from_normaliz(self):
942
+ r"""
943
+ Create the Hrepresentation objects from the normaliz polyhedron.
944
+
945
+ EXAMPLES::
946
+
947
+ sage: p = Polyhedron(vertices=[(0,1/2), (2,0), (4,5/6)], # indirect doctest
948
+ ....: backend='normaliz')
949
+ sage: p.Hrepresentation()
950
+ (An inequality (-5, 12) x + 10 >= 0,
951
+ An inequality (1, -12) x + 6 >= 0,
952
+ An inequality (1, 4) x - 2 >= 0)
953
+ sage: p.Vrepresentation()
954
+ (A vertex at (0, 1/2), A vertex at (2, 0), A vertex at (4, 5/6))
955
+ """
956
+ self._Hrepresentation = []
957
+ cone = self._normaliz_cone
958
+ parent = self.parent()
959
+ for g in self._nmz_result(cone, "SupportHyperplanes"):
960
+ if all(x == 0 for x in g[:-1]):
961
+ # Ignore vertical inequality
962
+ pass
963
+ else:
964
+ parent._make_Inequality(self, (g[-1],) + tuple(g[:-1]))
965
+ for g in self._nmz_result(cone, "Equations"):
966
+ parent._make_Equation(self, (g[-1],) + tuple(g[:-1]))
967
+ self._Hrepresentation = tuple(self._Hrepresentation)
968
+
969
+ def _init_empty_polyhedron(self):
970
+ r"""
971
+ Initialize an empty polyhedron.
972
+
973
+ TESTS::
974
+
975
+ sage: empty = Polyhedron(backend='normaliz'); empty
976
+ The empty polyhedron in ZZ^0
977
+ sage: empty.Vrepresentation()
978
+ ()
979
+ sage: empty.Hrepresentation()
980
+ (An equation -1 == 0,)
981
+ sage: Polyhedron(vertices=[], backend='normaliz')
982
+ The empty polyhedron in ZZ^0
983
+ sage: Polyhedron(backend='normaliz')._init_empty_polyhedron()
984
+ """
985
+ super()._init_empty_polyhedron()
986
+ # Can't seem to set up an empty _normaliz_cone.
987
+ # For example, PyNormaliz.NmzCone(vertices=[]) gives
988
+ # error: Some error in the normaliz input data detected: All input matrices empty!
989
+ self._normaliz_cone = None
990
+
991
+ @classmethod
992
+ def _from_normaliz_cone(cls, parent, normaliz_cone, internal_base_ring=None):
993
+ r"""
994
+ Initialize a polyhedron from a PyNormaliz wrapper of a normaliz cone.
995
+
996
+ TESTS::
997
+
998
+ sage: P=Polyhedron(ieqs=[[1, 0, 2], [3, 0, -2], [3, 2, -2]],
999
+ ....: backend='normaliz')
1000
+ sage: PI = P.integral_hull() # indirect doctest
1001
+ """
1002
+ return cls(parent, None, None, normaliz_cone=normaliz_cone, internal_base_ring=internal_base_ring)
1003
+
1004
+ @staticmethod
1005
+ def _number_field_triple(internal_base_ring) -> list:
1006
+ r"""
1007
+ Construct the PyNormaliz triple that describes ``internal_base_ring``.
1008
+
1009
+ TESTS::
1010
+
1011
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz as Pn
1012
+ sage: Pn._number_field_triple(QQ) is None
1013
+ True
1014
+ sage: Pn._number_field_triple(QuadraticField(5)) # needs sage.rings.number_field
1015
+ ['a^2 - 5', 'a', '[2.236067977499789 +/- 8.06e-16]']
1016
+ """
1017
+ R = internal_base_ring
1018
+ if R is QQ:
1019
+ return None
1020
+ from sage.rings.real_arb import RealBallField
1021
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1022
+ emb = RealBallField(53)(R.gen(0))
1023
+ gen = 'a'
1024
+ R_a = PolynomialRing(QQ, gen)
1025
+ min_poly = R_a(R.polynomial())
1026
+ return [str(min_poly), gen, str(emb)]
1027
+
1028
+ @staticmethod
1029
+ def _make_normaliz_cone(data, verbose=False):
1030
+ r"""
1031
+ Return a normaliz cone from ``data``.
1032
+
1033
+ INPUT:
1034
+
1035
+ - ``data`` -- dictionary
1036
+
1037
+ - ``verbose`` -- boolean (default: ``False``)
1038
+
1039
+ TESTS::
1040
+
1041
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
1042
+ sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
1043
+ sage: nmz_cone = Polyhedron_normaliz._make_normaliz_cone(data,verbose=False)
1044
+ sage: from PyNormaliz import NmzResult
1045
+ sage: NmzResult(nmz_cone, "ExtremeRays")
1046
+ [[1, 2, 0], [2, 1, 0]]
1047
+ """
1048
+ if verbose:
1049
+ print("# Calling PyNormaliz.NmzCone(**{})".format(data))
1050
+ cone = NmzCone(**data)
1051
+ assert cone, "NmzCone(**{}) did not return a cone".format(data)
1052
+ return cone
1053
+
1054
+ def _get_nmzcone_data(self) -> dict:
1055
+ r"""
1056
+ Get the data necessary to reproduce the normaliz cone.
1057
+
1058
+ OUTPUT: ``data`` -- dictionary
1059
+
1060
+ TESTS:
1061
+
1062
+ The empty polyhedron::
1063
+
1064
+ sage: P = Polyhedron(backend='normaliz')
1065
+ sage: P._get_nmzcone_data()
1066
+ {}
1067
+
1068
+ Another simple example::
1069
+
1070
+ sage: C = Polyhedron(backend='normaliz', rays=[[1, 2], [2, 1]])
1071
+ sage: C._get_nmzcone_data()
1072
+ {'cone': [[1, 2], [2, 1]],
1073
+ 'inhom_equations': [],
1074
+ 'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]],
1075
+ 'subspace': [],
1076
+ 'vertices': [[0, 0, 1]]}
1077
+ """
1078
+ if self.is_empty():
1079
+ return {}
1080
+
1081
+ vertices = self._nmz_result(self._normaliz_cone, "VerticesOfPolyhedron")
1082
+ # get rid of the last 0 in rays:
1083
+ rays = [r[:-1] for r in self._nmz_result(self._normaliz_cone, "ExtremeRays")]
1084
+ lines = self._nmz_result(self._normaliz_cone, "MaximalSubspace")
1085
+ ineqs = self._nmz_result(self._normaliz_cone, "SupportHyperplanes")
1086
+ eqs = self._nmz_result(self._normaliz_cone, "Equations")
1087
+
1088
+ return {'vertices': vertices,
1089
+ 'cone': rays,
1090
+ 'subspace': lines,
1091
+ 'inhom_equations': eqs,
1092
+ 'inhom_inequalities': ineqs}
1093
+
1094
+ def _normaliz_format(self, data, file_output=None):
1095
+ r"""
1096
+ Return a string containing normaliz format.
1097
+
1098
+ INPUT:
1099
+
1100
+ - ``data`` -- dictionary of PyNormaliz cone input properties
1101
+
1102
+ - ``file_output`` -- string (optional); a filename to which the
1103
+ representation should be written. If set to ``None`` (default),
1104
+ representation is returned as a string.
1105
+
1106
+ EXAMPLES::
1107
+
1108
+ sage: P = Polyhedron(vertices=[[0, 0], [0, 1], [1, 0]], # indirect doctest
1109
+ ....: backend='normaliz', verbose=True)
1110
+ # ----8<---- Equivalent Normaliz input file ----8<----
1111
+ amb_space 2
1112
+ cone 0
1113
+ subspace 0
1114
+ vertices 3
1115
+ 0 0 1
1116
+ 0 1 1
1117
+ 1 0 1
1118
+ # ----8<-------------------8<-------------------8<----
1119
+ # Calling ...
1120
+ """
1121
+ def format_number(x):
1122
+ try:
1123
+ return '{}'.format(QQ(x))
1124
+ except (ValueError, TypeError):
1125
+ return '({})'.format(x.polynomial('a'))
1126
+
1127
+ def format_field(key, value):
1128
+ if isinstance(value, (list, tuple)):
1129
+ s = '{} {}\n'.format(key, len(value))
1130
+ for e in value:
1131
+ for x in e:
1132
+ s += ' ' + format_number(x)
1133
+ s += '\n'
1134
+ return s
1135
+ return '{} {}\n'.format(key, value)
1136
+
1137
+ def format_number_field_data(nf_triple):
1138
+ min_poly, gen, emb = nf_triple
1139
+ return 'min_poly ({}) embedding {}'.format(min_poly, emb)
1140
+
1141
+ s = format_field('amb_space', self.ambient_dim())
1142
+ if 'number_field' in data:
1143
+ from copy import copy
1144
+ data = copy(data)
1145
+ s += 'number_field {}\n'.format(format_number_field_data(data['number_field']))
1146
+ del data['number_field']
1147
+ for key, value in sorted(data.items()):
1148
+ s += format_field(key, value)
1149
+ if file_output is not None:
1150
+ with open(file_output, 'w') as in_file:
1151
+ in_file.write(s)
1152
+ else:
1153
+ return s
1154
+
1155
+ def __copy__(self):
1156
+ r"""
1157
+ Return a copy of ``self``.
1158
+
1159
+ TESTS::
1160
+
1161
+ sage: P = polytopes.cube(backend='normaliz')
1162
+ sage: Q = copy(P)
1163
+ sage: P._normaliz_cone is Q._normaliz_cone
1164
+ False
1165
+ """
1166
+ other = super().__copy__()
1167
+
1168
+ # Make a copy of the cone.
1169
+ cone = self._normaliz_cone
1170
+ conecopy = NmzConeCopy(cone)
1171
+ other._normaliz_cone = conecopy
1172
+ return other
1173
+
1174
+ def __getstate__(self):
1175
+ r"""
1176
+ Remove the normaliz cone for pickling.
1177
+
1178
+ TESTS::
1179
+
1180
+ sage: P = polytopes.simplex(backend='normaliz')
1181
+ sage: P.__getstate__()
1182
+ (Polyhedra in ZZ^4,
1183
+ {'_Hrepresentation': (An inequality (0, 0, 0, 1) x + 0 >= 0,
1184
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
1185
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
1186
+ An inequality (1, 0, 0, 0) x + 0 >= 0,
1187
+ An equation (1, 1, 1, 1) x - 1 == 0),
1188
+ '_Vrepresentation': (A vertex at (0, 0, 0, 1),
1189
+ A vertex at (0, 0, 1, 0),
1190
+ A vertex at (0, 1, 0, 0),
1191
+ A vertex at (1, 0, 0, 0)),
1192
+ '_internal_base_ring': Rational Field,
1193
+ '_pickle_equations': [(-1, 1, 1, 1, 1)],
1194
+ '_pickle_inequalities': [(0, 0, 0, 0, 1),
1195
+ (0, 0, 0, 1, 0),
1196
+ (0, 0, 1, 0, 0),
1197
+ (0, 1, 0, 0, 0)],
1198
+ '_pickle_lines': [],
1199
+ '_pickle_rays': [],
1200
+ '_pickle_vertices': [(0, 0, 0, 1),
1201
+ (0, 0, 1, 0),
1202
+ (0, 1, 0, 0),
1203
+ (1, 0, 0, 0)]})
1204
+ """
1205
+ state = super().__getstate__()
1206
+ state = (state[0], state[1].copy())
1207
+ # Remove the unpicklable entries.
1208
+ del state[1]['_normaliz_cone']
1209
+ state[1]["_pickle_vertices"] = [v._vector for v in self.vertices()]
1210
+ state[1]["_pickle_rays"] = [v._vector for v in self.rays()]
1211
+ state[1]["_pickle_lines"] = [v._vector for v in self.lines()]
1212
+ state[1]["_pickle_inequalities"] = [v._vector for v in self.inequalities()]
1213
+ state[1]["_pickle_equations"] = [v._vector for v in self.equations()]
1214
+ return state
1215
+
1216
+ def __setstate__(self, state):
1217
+ r"""
1218
+ Initialize the normaliz cone after pickling.
1219
+
1220
+ TESTS::
1221
+
1222
+ sage: P = polytopes.permutahedron(4, backend='normaliz')
1223
+ sage: P.volume(measure='induced_lattice', engine='normaliz')
1224
+ 96
1225
+ sage: P.volume.clear_cache()
1226
+ sage: P1 = loads(dumps(P)) # indirect doctest
1227
+ sage: P1.volume(measure='induced_lattice', engine='normaliz')
1228
+ 96
1229
+
1230
+ Test that the obtained cone is valid::
1231
+
1232
+ sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
1233
+ sage: P = polytopes.permutahedron(4, backend='normaliz')
1234
+ sage: P1 = loads(dumps(P))
1235
+ sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
1236
+ sage: P2 == P
1237
+ True
1238
+
1239
+ sage: P = Polyhedron(lines=[[1,0], [0,1]], backend='normaliz')
1240
+ sage: P1 = loads(dumps(P))
1241
+ sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
1242
+ sage: P2 == P
1243
+ True
1244
+
1245
+ sage: P = Polyhedron(backend='normaliz')
1246
+ sage: P1 = loads(dumps(P))
1247
+ sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
1248
+ sage: P2 == P
1249
+ True
1250
+
1251
+ sage: P = polytopes.permutahedron(4, backend='normaliz') * Polyhedron(lines=[[1]], backend='normaliz')
1252
+ sage: P1 = loads(dumps(P))
1253
+ sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
1254
+ sage: P2 == P
1255
+ True
1256
+
1257
+ sage: # needs sage.rings.number_field
1258
+ sage: P = polytopes.dodecahedron(backend='normaliz')
1259
+ sage: P1 = loads(dumps(P))
1260
+ sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone,
1261
+ ....: internal_base_ring=P1._internal_base_ring)
1262
+ sage: P == P2
1263
+ True
1264
+
1265
+ Test that :issue:`31820` is fixed::
1266
+
1267
+ sage: P = polytopes.cube(backend='normaliz')
1268
+ sage: v = P.Vrepresentation()[0]
1269
+ sage: v1 = loads(v.dumps())
1270
+ """
1271
+ if "_pickle_vertices" in state[1]:
1272
+ vertices = state[1].pop("_pickle_vertices")
1273
+ rays = state[1].pop("_pickle_rays")
1274
+ lines = state[1].pop("_pickle_lines")
1275
+ inequalities = state[1].pop("_pickle_inequalities")
1276
+ equations = state[1].pop("_pickle_equations")
1277
+ else:
1278
+ vertices = None
1279
+
1280
+ super().__setstate__(state)
1281
+
1282
+ if self.is_empty():
1283
+ # Special case to avoid.
1284
+ self._normaliz_cone = None
1285
+ return
1286
+
1287
+ if vertices is None:
1288
+ vertices = self.vertices()
1289
+ rays = self.rays()
1290
+ lines = self.lines()
1291
+ inequalities = self.inequalities()
1292
+ equations = self.equations()
1293
+
1294
+ self._normaliz_cone = \
1295
+ self._cone_from_Vrepresentation_and_Hrepresentation(
1296
+ vertices, rays, lines, inequalities, equations)
1297
+
1298
+ def integral_hull(self):
1299
+ r"""
1300
+ Return the integral hull in the polyhedron.
1301
+
1302
+ This is a new polyhedron that is the convex hull of all integral
1303
+ points.
1304
+
1305
+ EXAMPLES:
1306
+
1307
+ Unbounded example from Normaliz manual, "a dull polyhedron"::
1308
+
1309
+ sage: P = Polyhedron(ieqs=[[1, 0, 2], [3, 0, -2], [3, 2, -2]],
1310
+ ....: backend='normaliz')
1311
+ sage: PI = P.integral_hull()
1312
+ sage: P.plot(color='yellow') + PI.plot(color='green') # needs sage.plot
1313
+ Graphics object consisting of 10 graphics primitives
1314
+ sage: PI.Vrepresentation()
1315
+ (A vertex at (-1, 0),
1316
+ A vertex at (0, 1),
1317
+ A ray in the direction (1, 0))
1318
+
1319
+ Nonpointed case::
1320
+
1321
+ sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]],
1322
+ ....: lines=[[-1, 1]], backend='normaliz')
1323
+ sage: PI = P.integral_hull()
1324
+ sage: PI.Vrepresentation()
1325
+ (A vertex at (1, 0),
1326
+ A ray in the direction (1, 0),
1327
+ A line in the direction (1, -1))
1328
+
1329
+ Empty polyhedron::
1330
+
1331
+ sage: P = Polyhedron(backend='normaliz')
1332
+ sage: PI = P.integral_hull()
1333
+ sage: PI.Vrepresentation()
1334
+ ()
1335
+ """
1336
+ if self.is_empty():
1337
+ return self
1338
+ cone = self._nmz_result(self._normaliz_cone, "IntegerHull")
1339
+ return self.parent().element_class._from_normaliz_cone(parent=self.parent(),
1340
+ normaliz_cone=cone)
1341
+
1342
+ def _h_star_vector_normaliz(self) -> list:
1343
+ r"""
1344
+ Return the `h^*`-vector of the lattice polytope.
1345
+
1346
+ INPUT:
1347
+
1348
+ - ``self`` -- a lattice polytope with backend ``'normaliz'``
1349
+
1350
+ OUTPUT:
1351
+
1352
+ The `h^*`-vector as a list.
1353
+
1354
+ EXAMPLES:
1355
+
1356
+ The `h^*`-vector of a unimodular simplex is 1::
1357
+
1358
+ sage: s3 = polytopes.simplex(3, backend='normaliz')
1359
+ sage: s3._h_star_vector_normaliz()
1360
+ [1]
1361
+
1362
+ The `h^*`-vector of the `0/1`-cube is [1,4,1]::
1363
+
1364
+ sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz')
1365
+ sage: cube.h_star_vector()
1366
+ [1, 4, 1]
1367
+
1368
+ TESTS:
1369
+
1370
+ Check that :issue:`33847` is fixed::
1371
+
1372
+ sage: L = [[1, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0],
1373
+ ....: [1, 0, 0, 1, 0, 0], [1, 0, 0, 0, 1, 0], [1, 0, 0, 1, 2, 3]]
1374
+ sage: P = Polyhedron(vertices=L, backend='normaliz')
1375
+ sage: P.h_star_vector()
1376
+ [1, 0, 2]
1377
+ """
1378
+ return self.ehrhart_series().numerator().list()
1379
+
1380
+ def _volume_normaliz(self, measure='euclidean'):
1381
+ r"""
1382
+ Compute the volume of a polytope using normaliz.
1383
+
1384
+ INPUT:
1385
+
1386
+ - ``measure`` -- string. The measure to use. Allowed values are:
1387
+
1388
+ * ``'euclidean'`` (default): corresponds to ``'EuclideanVolume`` in normaliz
1389
+ * ``'induced_lattice'``: corresponds to ``'Volume'`` in normaliz
1390
+ * ``'ambient'``: Lebesgue measure of ambient space (volume)
1391
+
1392
+ OUTPUT:
1393
+
1394
+ A float value (when ``measure`` is 'euclidean'),
1395
+ a rational number (when ``measure`` is 'induced_lattice'),
1396
+ a rational number or symbolic number otherwise (dependent on base ring).
1397
+
1398
+ .. NOTE::
1399
+
1400
+ This function depends on Normaliz (i.e., the ``pynormaliz`` optional
1401
+ package).
1402
+
1403
+ REFERENCES:
1404
+
1405
+ See section 6.1.1 of [NormalizMan]_.
1406
+
1407
+ EXAMPLES:
1408
+
1409
+ For normaliz, the default is the euclidean volume in the ambient
1410
+ space and the result is a float::
1411
+
1412
+ sage: s = polytopes.simplex(3, backend='normaliz')
1413
+ sage: s._volume_normaliz()
1414
+ 0.3333333333333333
1415
+
1416
+ One other possibility is to compute the scaled volume where a unimodular
1417
+ simplex has volume 1::
1418
+
1419
+ sage: s._volume_normaliz(measure='induced_lattice')
1420
+ 1
1421
+ sage: v = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
1422
+ sage: cube = Polyhedron(vertices=v, backend='normaliz')
1423
+ sage: cube._volume_normaliz()
1424
+ 1.0
1425
+ sage: cube._volume_normaliz(measure='induced_lattice')
1426
+ 6
1427
+
1428
+ Or one can calculate the ambient volume, which is the above multiplied by the
1429
+ volume of the unimodular simplex (or zero if not full-dimensional)::
1430
+
1431
+ sage: cube._volume_normaliz(measure='ambient')
1432
+ 1
1433
+ sage: s._volume_normaliz(measure='ambient')
1434
+ 0
1435
+
1436
+ TESTS:
1437
+
1438
+ Check that :issue:`28872` is fixed::
1439
+
1440
+ sage: P = polytopes.dodecahedron(backend='normaliz') # needs sage.rings.number_field
1441
+ sage: P.volume(measure='induced_lattice') # needs sage.rings.number_field
1442
+ -1056*sqrt5 + 2400
1443
+
1444
+ Some sanity checks that the ambient volume works correctly::
1445
+
1446
+ sage: (2*cube)._volume_normaliz(measure='ambient')
1447
+ 8
1448
+ sage: (1/2*cube)._volume_normaliz(measure='ambient')
1449
+ 1/8
1450
+ sage: s._volume_normaliz(measure='ambient')
1451
+ 0
1452
+
1453
+ sage: P = polytopes.regular_polygon(3, backend='normaliz') # needs sage.rings.number_field
1454
+ sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # needs sage.rings.number_field
1455
+ True
1456
+
1457
+ sage: P = polytopes.dodecahedron(backend='normaliz') # needs sage.rings.number_field
1458
+ sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # needs sage.rings.number_field
1459
+ True
1460
+
1461
+ sage: P = Polyhedron(rays=[[1]], backend='normaliz')
1462
+ sage: P.volume()
1463
+ +Infinity
1464
+ """
1465
+ cone = self._normaliz_cone
1466
+ assert cone
1467
+ if measure == 'euclidean':
1468
+ return self._nmz_result(cone, 'EuclideanVolume')
1469
+
1470
+ if measure == 'induced_lattice':
1471
+ if self._internal_base_ring in (ZZ, QQ):
1472
+ return self._nmz_result(cone, 'Volume')
1473
+ return self._nmz_result(cone, 'RenfVolume')
1474
+
1475
+ if measure == 'ambient':
1476
+ if self.dim() < self.ambient_dim():
1477
+ return self.base_ring().zero()
1478
+ if not self.is_compact():
1479
+ from sage.rings.infinity import infinity
1480
+ return infinity
1481
+
1482
+ from sage.arith.misc import factorial
1483
+ return self._volume_normaliz('induced_lattice') / factorial(self.dim())
1484
+
1485
+ raise TypeError("the measure should be `ambient`, `euclidean`, or `induced_lattice`")
1486
+
1487
+ def _triangulate_normaliz(self):
1488
+ r"""
1489
+ Give a triangulation of the polyhedron using normaliz.
1490
+
1491
+ OUTPUT:
1492
+
1493
+ For compact polyhedra a list of simplices
1494
+ each represented by indices of their vertices.
1495
+
1496
+ For cones a list of simplicial cones
1497
+ each represented by indices of their rays.
1498
+
1499
+ .. NOTE::
1500
+
1501
+ This function depends on Normaliz (i.e. the ``pynormaliz`` optional
1502
+ package). See the Normaliz documentation for further details.
1503
+
1504
+ EXAMPLES::
1505
+
1506
+ sage: P = Polyhedron(vertices=[[0,0,1], [1,0,1], [0,1,1], [1,1,1]], backend='normaliz')
1507
+ sage: P._triangulate_normaliz()
1508
+ [(0, 1, 2), (1, 2, 3)]
1509
+ sage: C1 = Polyhedron(rays=[[0,0,1], [1,0,1], [0,1,1], [1,1,1]], backend='normaliz')
1510
+ sage: C1._triangulate_normaliz()
1511
+ [(0, 1, 2), (1, 2, 3)]
1512
+ sage: C2 = Polyhedron(rays=[[1,0,1], [0,0,1], [0,1,1], [1,1,10/9]], backend='normaliz')
1513
+ sage: C2._triangulate_normaliz()
1514
+ [(0, 1, 2), (1, 2, 3)]
1515
+
1516
+ Works only for cones and compact polyhedra::
1517
+
1518
+ sage: P = polytopes.cube(backend='normaliz')
1519
+ sage: Q = Polyhedron(rays=[[0,1]], backend='normaliz')
1520
+ sage: R = Polyhedron(lines=[[0,1]], backend='normaliz')
1521
+ sage: (P*Q)._triangulate_normaliz()
1522
+ Traceback (most recent call last):
1523
+ ...
1524
+ NotImplementedError: triangulation of non-compact polyhedra that are not cones is not supported
1525
+ sage: (P*R)._triangulate_normaliz()
1526
+ Traceback (most recent call last):
1527
+ ...
1528
+ NotImplementedError: triangulation of non-compact not pointed polyhedron is not supported
1529
+
1530
+ TESTS:
1531
+
1532
+ Check that :issue:`30531` is fixed::
1533
+
1534
+ sage: P = polytopes.cube(backend='normaliz')*AA(2).sqrt()
1535
+ sage: P._triangulate_normaliz()
1536
+ [(0, 1, 2, 4),
1537
+ (1, 2, 4, 3),
1538
+ (1, 3, 4, 5),
1539
+ (3, 5, 6, 7),
1540
+ (6, 2, 4, 3),
1541
+ (6, 3, 4, 5)]
1542
+
1543
+ ::
1544
+
1545
+ sage: C1 = Polyhedron(rays=[[0,0,1], [1,0,AA(2).sqrt()], [0,1,1], [1,1,1]],
1546
+ ....: backend='normaliz')
1547
+ sage: C1._triangulate_normaliz()
1548
+ [(0, 1, 3), (0, 3, 2)]
1549
+ """
1550
+ if self.lines():
1551
+ raise NotImplementedError("triangulation of non-compact not pointed polyhedron is not supported")
1552
+ if len(self.vertices_list()) >= 2 and self.rays_list(): # A mix of polytope and cone
1553
+ raise NotImplementedError("triangulation of non-compact polyhedra that are not cones is not supported")
1554
+
1555
+ if self.is_compact():
1556
+ cone = self._normaliz_cone
1557
+ else:
1558
+ # Make a inhomogeneous copy of the cone.
1559
+ cone = self._cone_from_Vrepresentation_and_Hrepresentation(
1560
+ self.vertices(), self.rays(), self.lines(),
1561
+ self.inequalities(), self.equations(), homogeneous=True)
1562
+
1563
+ # Compute the triangulation.
1564
+ assert cone
1565
+
1566
+ # Normaliz does not guarantee that the order of generators is kept during
1567
+ # computation of the triangulation.
1568
+ # Those are the generators that the indices of the triangulation correspond to:
1569
+ nmz_triangulation, nmz_triangulation_generators = self._nmz_result(cone, "Triangulation")
1570
+
1571
+ base_ring = self.base_ring()
1572
+ v_list = self.vertices_list()
1573
+ r_list = self.rays_list()
1574
+
1575
+ new_to_old = {}
1576
+ for i, g in enumerate(nmz_triangulation_generators):
1577
+ if self.is_compact():
1578
+ d = base_ring(g[-1])
1579
+ vertex = [base_ring(x) / d for x in g[:-1]]
1580
+ new_to_old[i] = v_list.index(vertex)
1581
+ else:
1582
+ if g[-1] > 0:
1583
+ new_to_old[i] = None
1584
+ else:
1585
+ try:
1586
+ new_to_old[i] = r_list.index([base_ring(x) for x in g[:-1]])
1587
+ except ValueError:
1588
+ # Rays are only unique up to scaling.
1589
+ new_ray = vector(base_ring, g[:-1])
1590
+
1591
+ for j, r in enumerate(self.rays()):
1592
+ ray = r.vector()
1593
+ try:
1594
+ # Check for colinearity.
1595
+ _ = new_ray / ray
1596
+ new_to_old[i] = j
1597
+ break
1598
+ except (TypeError, ArithmeticError):
1599
+ pass
1600
+ else:
1601
+ raise ValueError("could not match rays after computing triangulation with original rays")
1602
+
1603
+ def new_indices(old_indices):
1604
+ for i in old_indices:
1605
+ if new_to_old[i] is not None:
1606
+ yield new_to_old[i]
1607
+
1608
+ return [tuple(new_indices(x[0])) for x in nmz_triangulation]
1609
+
1610
+
1611
+ #########################################################################
1612
+ class Polyhedron_QQ_normaliz(Polyhedron_normaliz, Polyhedron_QQ):
1613
+ r"""
1614
+ Polyhedra over `\QQ` with normaliz.
1615
+
1616
+ INPUT:
1617
+
1618
+ - ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``
1619
+ - ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``
1620
+
1621
+ EXAMPLES::
1622
+
1623
+ sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
1624
+ ....: rays=[(1,1)], lines=[],
1625
+ ....: backend='normaliz', base_ring=QQ)
1626
+ sage: TestSuite(p).run()
1627
+ """
1628
+
1629
+ @cached_method(do_pickle=True)
1630
+ def ehrhart_series(self, variable='t'):
1631
+ r"""
1632
+ Return the Ehrhart series of a compact rational polyhedron.
1633
+
1634
+ The Ehrhart series is the generating function where the coefficient of
1635
+ `t^k` is number of integer lattice points inside the `k`-th dilation of
1636
+ the polytope.
1637
+
1638
+ INPUT:
1639
+
1640
+ - ``variable`` -- string (default: ``'t'``)
1641
+
1642
+ OUTPUT: a rational function
1643
+
1644
+ EXAMPLES::
1645
+
1646
+ sage: S = Polyhedron(vertices=[[0,1], [1,0]], backend='normaliz')
1647
+ sage: ES = S.ehrhart_series()
1648
+ sage: ES.numerator()
1649
+ 1
1650
+ sage: ES.denominator().factor()
1651
+ (t - 1)^2
1652
+
1653
+ sage: C = Polyhedron(vertices=[[0,0,0], [0,0,1], [0,1,0], [0,1,1],
1654
+ ....: [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
1655
+ ....: backend='normaliz')
1656
+ sage: ES = C.ehrhart_series()
1657
+ sage: ES.numerator()
1658
+ t^2 + 4*t + 1
1659
+ sage: ES.denominator().factor()
1660
+ (t - 1)^4
1661
+
1662
+ The following example is from the Normaliz manual contained in the file
1663
+ ``rational.in``::
1664
+
1665
+ sage: rat_poly = Polyhedron(vertices=[[1/2,1/2], [-1/3,-1/3], [1/4,-1/2]],
1666
+ ....: backend='normaliz')
1667
+ sage: ES = rat_poly.ehrhart_series()
1668
+ sage: ES.numerator()
1669
+ 2*t^6 + 3*t^5 + 4*t^4 + 3*t^3 + t^2 + t + 1
1670
+ sage: ES.denominator().factor()
1671
+ (-1) * (t + 1)^2 * (t - 1)^3 * (t^2 + 1) * (t^2 + t + 1)
1672
+
1673
+ The polyhedron should be compact::
1674
+
1675
+ sage: C = Polyhedron(rays=[[1,2], [2,1]], backend='normaliz')
1676
+ sage: C.ehrhart_series()
1677
+ Traceback (most recent call last):
1678
+ ...
1679
+ NotImplementedError: Ehrhart series can only be computed for compact polyhedron
1680
+
1681
+ .. SEEALSO::
1682
+
1683
+ :meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`
1684
+
1685
+ TESTS:
1686
+
1687
+ Check that the Ehrhart series is pickled::
1688
+
1689
+ sage: new_poly = loads(dumps(rat_poly))
1690
+ sage: new_poly.ehrhart_series.is_in_cache()
1691
+ True
1692
+ """
1693
+ if self.is_empty():
1694
+ return 0
1695
+
1696
+ if not self.is_compact():
1697
+ raise NotImplementedError("Ehrhart series can only be computed for compact polyhedron")
1698
+
1699
+ cone = self._normaliz_cone
1700
+ e = self._nmz_result(cone, "EhrhartSeries")
1701
+ # The output format of PyNormaliz is a list with 3 things:
1702
+ # 1) the coefficients of the h^*-polynomial
1703
+ # 2) a list of the exponents e such that (1-t^e) appears as a factor in
1704
+ # the denominator
1705
+ # 3) a shifting of the generating function.
1706
+
1707
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1708
+ poly_ring = PolynomialRing(ZZ, variable).fraction_field()
1709
+ t = poly_ring.gens()[0]
1710
+ es = sum([e[0][i] * t**i for i in range(len(e[0]))])
1711
+ for expo in range(len(e[1])):
1712
+ es = es / (1 - t**e[1][expo])
1713
+
1714
+ # The shift:
1715
+ return es * t**e[2]
1716
+
1717
+ def _ehrhart_quasipolynomial_normaliz(self, variable='t'):
1718
+ r"""
1719
+ Return the Ehrhart quasipolynomial of a compact rational polyhedron
1720
+ using Normaliz.
1721
+
1722
+ If it is a polynomial, returns the polynomial. Otherwise, returns a
1723
+ tuple of rational polynomials whose length is the quasi-period of the
1724
+ quasipolynomial and each rational polynomial describes a residue class.
1725
+
1726
+ INPUT:
1727
+
1728
+ - ``variable`` -- string (default: ``'t'``)
1729
+
1730
+ OUTPUT: a polynomial or tuple of polynomials
1731
+
1732
+ EXAMPLES::
1733
+
1734
+ sage: C = Polyhedron(vertices=[[0,0,0], [0,0,1], [0,1,0], [0,1,1],
1735
+ ....: [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
1736
+ ....: backend='normaliz')
1737
+ sage: C._ehrhart_quasipolynomial_normaliz()
1738
+ t^3 + 3*t^2 + 3*t + 1
1739
+
1740
+ sage: P = Polyhedron(vertices=[[0,0], [3/2,0], [0,3/2], [1,1]], backend='normaliz')
1741
+ sage: P._ehrhart_quasipolynomial_normaliz()
1742
+ (3/2*t^2 + 2*t + 1, 3/2*t^2 + 2*t + 1/2)
1743
+ sage: P._ehrhart_quasipolynomial_normaliz('x')
1744
+ (3/2*x^2 + 2*x + 1, 3/2*x^2 + 2*x + 1/2)
1745
+
1746
+ The quasipolynomial evaluated at ``i`` counts the integral points
1747
+ in the ``i``-th dilate::
1748
+
1749
+ sage: Q = Polyhedron(vertices=[[-1/3], [2/3]], backend='normaliz')
1750
+ sage: p0,p1,p2 = Q._ehrhart_quasipolynomial_normaliz()
1751
+ sage: r0 = [p0(i) for i in range(15)]
1752
+ sage: r1 = [p1(i) for i in range(15)]
1753
+ sage: r2 = [p2(i) for i in range(15)]
1754
+ sage: result = [None]*15
1755
+ sage: result[::3] = r0[::3]
1756
+ sage: result[1::3] = r1[1::3]
1757
+ sage: result[2::3] = r2[2::3]
1758
+ sage: result == [(i*Q).integral_points_count() for i in range(15)]
1759
+ True
1760
+
1761
+
1762
+ .. SEEALSO::
1763
+
1764
+ :meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`,
1765
+ :meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
1766
+ """
1767
+ cone = self._normaliz_cone
1768
+ # Normaliz needs to compute the EhrhartSeries first
1769
+ assert NmzCompute(cone, ["EhrhartSeries"])
1770
+ e = self._nmz_result(cone, "EhrhartQuasiPolynomial")
1771
+
1772
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1773
+ poly_ring = PolynomialRing(QQ, variable)
1774
+ t = poly_ring.gens()[0]
1775
+ if len(e) == 2:
1776
+ # It is a polynomial
1777
+ es = sum([e[0][i] * t**i for i in range(len(e[0]))])
1778
+ return es / ZZ(e[1])
1779
+
1780
+ # It is a quasipolynomial
1781
+ polynomials = []
1782
+ for p in e[:-1]:
1783
+ es = sum([p[i] * t**i for i in range(len(p))]) / ZZ(e[-1])
1784
+ polynomials += [es]
1785
+
1786
+ return tuple(polynomials)
1787
+
1788
+ _ehrhart_polynomial_normaliz = _ehrhart_quasipolynomial_normaliz
1789
+
1790
+ @cached_method(do_pickle=True, key=lambda self, g, v: (tuple(g), v))
1791
+ def hilbert_series(self, grading, variable='t'):
1792
+ r"""
1793
+ Return the Hilbert series of the polyhedron with respect to ``grading``.
1794
+
1795
+ INPUT:
1796
+
1797
+ - ``grading`` -- vector. The grading to use to form the Hilbert series
1798
+
1799
+ - ``variable`` -- string (default: ``'t'``)
1800
+
1801
+ OUTPUT: a rational function
1802
+
1803
+ EXAMPLES::
1804
+
1805
+ sage: C = Polyhedron(backend='normaliz',
1806
+ ....: rays=[[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
1807
+ sage: HS = C.hilbert_series([1,1,1])
1808
+ sage: HS.numerator()
1809
+ t^2 + 1
1810
+ sage: HS.denominator().factor()
1811
+ (-1) * (t + 1) * (t - 1)^3 * (t^2 + t + 1)
1812
+
1813
+ By changing the grading, you can get the Ehrhart series of the square
1814
+ lifted at height 1::
1815
+
1816
+ sage: C.hilbert_series([0,0,1])
1817
+ (t + 1)/(-t^3 + 3*t^2 - 3*t + 1)
1818
+
1819
+ Here is an example ``2cone.in`` from the Normaliz manual::
1820
+
1821
+ sage: C = Polyhedron(backend='normaliz', rays=[[1,3], [2,1]])
1822
+ sage: HS = C.hilbert_series([1,1])
1823
+ sage: HS.numerator()
1824
+ t^5 + t^4 + t^3 + t^2 + 1
1825
+ sage: HS.denominator().factor()
1826
+ (t + 1) * (t - 1)^2 * (t^2 + 1) * (t^2 + t + 1)
1827
+
1828
+ sage: HS = C.hilbert_series([1,2])
1829
+ sage: HS.numerator()
1830
+ t^8 + t^6 + t^5 + t^3 + 1
1831
+ sage: HS.denominator().factor()
1832
+ (t + 1) * (t - 1)^2 * (t^2 + 1) * (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)
1833
+
1834
+ Here is the magic square example form the Normaliz manual::
1835
+
1836
+ sage: eq = [[0,1,1,1,-1,-1,-1, 0, 0, 0],
1837
+ ....: [0,1,1,1, 0, 0, 0,-1,-1,-1],
1838
+ ....: [0,0,1,1,-1, 0, 0,-1, 0, 0],
1839
+ ....: [0,1,0,1, 0,-1, 0, 0,-1, 0],
1840
+ ....: [0,1,1,0, 0, 0,-1, 0, 0,-1],
1841
+ ....: [0,0,1,1, 0,-1, 0, 0, 0,-1],
1842
+ ....: [0,1,1,0, 0,-1, 0,-1, 0, 0]]
1843
+ sage: magic_square = (Polyhedron(eqns=eq, backend='normaliz')
1844
+ ....: & Polyhedron(rays=identity_matrix(9).rows()))
1845
+ sage: grading = [1,1,1,0,0,0,0,0,0]
1846
+ sage: magic_square.hilbert_series(grading)
1847
+ (t^6 + 2*t^3 + 1)/(-t^9 + 3*t^6 - 3*t^3 + 1)
1848
+
1849
+ .. SEEALSO::
1850
+
1851
+ :meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
1852
+
1853
+ TESTS:
1854
+
1855
+ Check that the Hilbert series is pickled::
1856
+
1857
+ sage: new_magic = loads(dumps(magic_square))
1858
+ sage: new_magic.hilbert_series.is_in_cache(grading)
1859
+ True
1860
+ """
1861
+ if self.is_empty():
1862
+ return 0
1863
+
1864
+ data = self._get_nmzcone_data()
1865
+ data['grading'] = [grading]
1866
+ new_cone = self._make_normaliz_cone(data)
1867
+ h = self._nmz_result(new_cone, "HilbertSeries")
1868
+
1869
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1870
+ poly_ring = PolynomialRing(ZZ, variable).fraction_field()
1871
+ t = poly_ring.gens()[0]
1872
+ hs = sum([h[0][i] * t**i for i in range(len(h[0]))])
1873
+ for expo in range(len(h[1])):
1874
+ hs = hs / (1 - t**h[1][expo])
1875
+
1876
+ # The shift:
1877
+ return hs * t**h[2]
1878
+
1879
+ def integral_points(self, threshold=10000) -> tuple:
1880
+ r"""
1881
+ Return the integral points in the polyhedron.
1882
+
1883
+ Uses either the naive algorithm (iterate over a rectangular
1884
+ bounding box) or triangulation + Smith form.
1885
+
1886
+ INPUT:
1887
+
1888
+ - ``threshold`` -- integer (default: 10000); use the naïve
1889
+ algorithm as long as the bounding box is smaller than this
1890
+
1891
+ OUTPUT:
1892
+
1893
+ The list of integral points in the polyhedron. If the
1894
+ polyhedron is not compact, a :exc:`ValueError` is raised.
1895
+
1896
+ EXAMPLES::
1897
+
1898
+ sage: Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],
1899
+ ....: backend='normaliz').integral_points()
1900
+ ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))
1901
+
1902
+ sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)],
1903
+ ....: backend='normaliz')
1904
+ sage: simplex.integral_points()
1905
+ ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
1906
+
1907
+ The polyhedron need not be full-dimensional::
1908
+
1909
+ sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)],
1910
+ ....: backend='normaliz')
1911
+ sage: simplex.integral_points()
1912
+ ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))
1913
+
1914
+ sage: point = Polyhedron([(2,3,7)],
1915
+ ....: backend='normaliz')
1916
+ sage: point.integral_points()
1917
+ ((2, 3, 7),)
1918
+
1919
+ sage: empty = Polyhedron(backend='normaliz')
1920
+ sage: empty.integral_points()
1921
+ ()
1922
+
1923
+ Here is a simplex where the naive algorithm of running over
1924
+ all points in a rectangular bounding box no longer works fast
1925
+ enough::
1926
+
1927
+ sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
1928
+ sage: simplex = Polyhedron(v, backend='normaliz'); simplex
1929
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
1930
+ sage: len(simplex.integral_points())
1931
+ 49
1932
+
1933
+ A rather thin polytope for which the bounding box method would
1934
+ be a very bad idea (note this is a rational (non-lattice)
1935
+ polytope, so the other backends use the bounding box method)::
1936
+
1937
+ sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2)
1938
+ sage: P = Polyhedron(vertices=P.vertices_list(),
1939
+ ....: backend='normaliz')
1940
+ sage: len(P.integral_points())
1941
+ 434
1942
+
1943
+ Finally, the 3-d reflexive polytope number 4078::
1944
+
1945
+ sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
1946
+ ....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
1947
+ sage: P = Polyhedron(v, backend='normaliz')
1948
+ sage: pts1 = P.integral_points()
1949
+ sage: all(P.contains(p) for p in pts1)
1950
+ True
1951
+ sage: pts2 = LatticePolytope(v).points() # needs palp
1952
+ sage: for p in pts1: p.set_immutable()
1953
+ sage: set(pts1) == set(pts2) # needs palp
1954
+ True
1955
+
1956
+ sage: timeit('Polyhedron(v, backend='normaliz').integral_points()') # not tested - random
1957
+ 625 loops, best of 3: 1.41 ms per loop
1958
+ sage: timeit('LatticePolytope(v).points()') # not tested - random
1959
+ 25 loops, best of 3: 17.2 ms per loop
1960
+
1961
+ TESTS:
1962
+
1963
+ Test some trivial cases (see :issue:`17937`):
1964
+
1965
+ Empty polyhedron in 1 dimension::
1966
+
1967
+ sage: P = Polyhedron(ambient_dim=1, backend='normaliz')
1968
+ sage: P.integral_points()
1969
+ ()
1970
+
1971
+ Empty polyhedron in 0 dimensions::
1972
+
1973
+ sage: P = Polyhedron(ambient_dim=0, backend='normaliz')
1974
+ sage: P.integral_points()
1975
+ ()
1976
+
1977
+ Single point in 1 dimension::
1978
+
1979
+ sage: P = Polyhedron([[3]], backend='normaliz')
1980
+ sage: P.integral_points()
1981
+ ((3),)
1982
+
1983
+ Single non-integral point in 1 dimension::
1984
+
1985
+ sage: P = Polyhedron([[1/2]], backend='normaliz')
1986
+ sage: P.integral_points()
1987
+ ()
1988
+
1989
+ Single point in 0 dimensions::
1990
+
1991
+ sage: P = Polyhedron([[]], backend='normaliz')
1992
+ sage: P.integral_points()
1993
+ ((),)
1994
+
1995
+ A polytope with no integral points (:issue:`22938`)::
1996
+
1997
+ sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2],
1998
+ ....: [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1],
1999
+ ....: [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0],
2000
+ ....: [1, 0, 0, 1]]
2001
+ sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
2002
+ sage: P.bounding_box()
2003
+ ((-3/4, -1/2, -1/4), (-1/2, -1/4, 0))
2004
+ sage: P.bounding_box(integral_hull=True)
2005
+ (None, None)
2006
+ sage: P.integral_points()
2007
+ ()
2008
+
2009
+ Check the polytopes from :issue:`22984`::
2010
+
2011
+ sage: base = [[0, 2, 0, -1, 0, 0, 0, 0, 0],
2012
+ ....: [0, 0, 2, 0, -1, 0, 0, 0, 0],
2013
+ ....: [1, -1, 0, 2, -1, 0, 0, 0, 0],
2014
+ ....: [0, 0, -1, -1, 2, -1, 0, 0, 0],
2015
+ ....: [0, 0, 0, 0, -1, 2, -1, 0, 0],
2016
+ ....: [0, 0, 0, 0, 0, -1, 2, -1, 0],
2017
+ ....: [1, 0, 0, 0, 0, 0, -1, 2, -1],
2018
+ ....: [0, 0, 0, 0, 0, 0, 0, -1, 2],
2019
+ ....: [0, -1, 0, 0, 0, 0, 0, 0, 0],
2020
+ ....: [0, 0, -1, 0, 0, 0, 0, 0, 0],
2021
+ ....: [0, 0, 0, -1, 0, 0, 0, 0, 0],
2022
+ ....: [0, 0, 0, 0, -1, 0, 0, 0, 0],
2023
+ ....: [0, 0, 0, 0, 0, -1, 0, 0, 0],
2024
+ ....: [0, 0, 0, 0, 0, 0, -1, 0, 0],
2025
+ ....: [0, 0, 0, 0, 0, 0, 0, -1, 0],
2026
+ ....: [0, 0, 0, 0, 0, 0, 0, 0, -1],
2027
+ ....: [-1, -1, -1, -1, -1, -1, -1, -1, -1]]
2028
+
2029
+ sage: ieqs = base + [
2030
+ ....: [2, 1, 0, 0, 0, 0, 0, 0, 0],
2031
+ ....: [4, 0, 1, 0, 0, 0, 0, 0, 0],
2032
+ ....: [4, 0, 0, 1, 0, 0, 0, 0, 0],
2033
+ ....: [7, 0, 0, 0, 1, 0, 0, 0, 0],
2034
+ ....: [6, 0, 0, 0, 0, 1, 0, 0, 0],
2035
+ ....: [4, 0, 0, 0, 0, 0, 1, 0, 0],
2036
+ ....: [2, 0, 0, 0, 0, 0, 0, 1, 0],
2037
+ ....: [1, 0, 0, 0, 0, 0, 0, 0, 1]]
2038
+ sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
2039
+ sage: P.integral_points()
2040
+ ((-2, -2, -4, -5, -4, -3, -2, -1),
2041
+ (-2, -2, -4, -5, -4, -3, -2, 0),
2042
+ (-1, -2, -3, -4, -3, -2, -2, -1),
2043
+ (-1, -2, -3, -4, -3, -2, -1, 0),
2044
+ (-1, -1, -2, -2, -2, -2, -2, -1),
2045
+ (-1, -1, -2, -2, -1, -1, -1, 0),
2046
+ (-1, -1, -2, -2, -1, 0, 0, 0),
2047
+ (-1, 0, -2, -2, -2, -2, -2, -1),
2048
+ (0, -1, -1, -2, -2, -2, -2, -1),
2049
+ (0, 0, -1, -1, -1, -1, -1, 0))
2050
+
2051
+ sage: ieqs = base + [
2052
+ ....: [3, 1, 0, 0, 0, 0, 0, 0, 0],
2053
+ ....: [4, 0, 1, 0, 0, 0, 0, 0, 0],
2054
+ ....: [6, 0, 0, 1, 0, 0, 0, 0, 0],
2055
+ ....: [8, 0, 0, 0, 1, 0, 0, 0, 0],
2056
+ ....: [6, 0, 0, 0, 0, 1, 0, 0, 0],
2057
+ ....: [4, 0, 0, 0, 0, 0, 1, 0, 0],
2058
+ ....: [2, 0, 0, 0, 0, 0, 0, 1, 0],
2059
+ ....: [1, 0, 0, 0, 0, 0, 0, 0, 1]]
2060
+ sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
2061
+ sage: P.integral_points()
2062
+ ((-3, -4, -6, -8, -6, -4, -2, -1),
2063
+ (-3, -4, -6, -8, -6, -4, -2, 0),
2064
+ (-2, -2, -4, -5, -4, -3, -2, -1),
2065
+ (-2, -2, -4, -5, -4, -3, -2, 0),
2066
+ (-1, -2, -3, -4, -3, -2, -2, -1),
2067
+ (-1, -2, -3, -4, -3, -2, -1, 0),
2068
+ (-1, -1, -2, -2, -2, -2, -2, -1),
2069
+ (-1, -1, -2, -2, -1, -1, -1, 0),
2070
+ (-1, -1, -2, -2, -1, 0, 0, 0),
2071
+ (-1, 0, -2, -2, -2, -2, -2, -1),
2072
+ (0, -1, -1, -2, -2, -2, -2, -1),
2073
+ (0, 0, -1, -1, -1, -1, -1, 0))
2074
+ """
2075
+ if not self.is_compact():
2076
+ raise ValueError('can only enumerate points in a compact polyhedron')
2077
+ # Trivial cases: polyhedron with 0 or 1 vertices
2078
+ if self.n_vertices() == 0:
2079
+ return ()
2080
+ if self.n_vertices() == 1:
2081
+ v = self.vertices_list()[0]
2082
+ try:
2083
+ return (vector(ZZ, v),)
2084
+ except TypeError: # vertex not integral
2085
+ return ()
2086
+ # for small bounding boxes, it is faster to naively iterate over the points of the box
2087
+ if threshold > 1:
2088
+ box_min, box_max = self.bounding_box(integral_hull=True)
2089
+ if box_min is None:
2090
+ return ()
2091
+ box_points = prod(max_coord - min_coord + 1
2092
+ for min_coord, max_coord in zip(box_min, box_max))
2093
+ if box_points < threshold:
2094
+ from sage.geometry.integral_points import rectangular_box_points
2095
+ return rectangular_box_points(list(box_min), list(box_max), self)
2096
+ # Compute with normaliz
2097
+ points = []
2098
+ cone = self._normaliz_cone
2099
+ assert cone
2100
+ for g in self._nmz_result(cone, "ModuleGenerators"):
2101
+ assert g[-1] == 1
2102
+ points.append(vector(ZZ, g[:-1]))
2103
+ return tuple(points)
2104
+
2105
+ def integral_points_generators(self):
2106
+ r"""
2107
+ Return the integral points generators of the polyhedron.
2108
+
2109
+ Every integral point in the polyhedron can be written as a (unique)
2110
+ nonnegative linear combination of integral points contained in the three
2111
+ defining parts of the polyhedron: the integral points (the compact
2112
+ part), the recession cone, and the lineality space.
2113
+
2114
+ OUTPUT:
2115
+
2116
+ A tuple consisting of the integral points, the Hilbert basis of the
2117
+ recession cone, and an integral basis for the lineality space.
2118
+
2119
+ EXAMPLES:
2120
+
2121
+ Normaliz gives a nonnegative integer basis of the lineality space::
2122
+
2123
+ sage: P = Polyhedron(backend='normaliz', lines=[[2,2]])
2124
+ sage: P.integral_points_generators()
2125
+ (((0, 0),), (), ((1, 1),))
2126
+
2127
+ A recession cone generated by two rays::
2128
+
2129
+ sage: C = Polyhedron(backend='normaliz', rays=[[1,2], [2,1]])
2130
+ sage: C.integral_points_generators()
2131
+ (((0, 0),), ((1, 1), (1, 2), (2, 1)), ())
2132
+
2133
+ Empty polyhedron::
2134
+
2135
+ sage: P = Polyhedron(backend='normaliz')
2136
+ sage: P.integral_points_generators()
2137
+ ((), (), ())
2138
+ """
2139
+ # Trivial cases: polyhedron with 0 vertices
2140
+ if self.n_vertices() == 0:
2141
+ return ((), (), ())
2142
+ # Compute with normaliz
2143
+ cone = self._normaliz_cone
2144
+ compact_part = []
2145
+ recession_cone_part = []
2146
+ lineality_part = []
2147
+ assert cone
2148
+ for g in self._nmz_result(cone, "ModuleGenerators"):
2149
+ assert g[-1] == 1
2150
+ compact_part.append(vector(ZZ, g[:-1]))
2151
+
2152
+ for g in self._nmz_result(cone, "HilbertBasis"):
2153
+ assert g[-1] == 0
2154
+ recession_cone_part.append(vector(ZZ, g[:-1]))
2155
+
2156
+ for g in self._nmz_result(cone, "MaximalSubspace"):
2157
+ assert g[-1] == 0
2158
+ lineality_part.append(vector(ZZ, g[:-1]))
2159
+
2160
+ return tuple(compact_part), tuple(recession_cone_part), tuple(lineality_part)
2161
+
2162
+ def _Hstar_function_normaliz(self, acting_group=None, output=None):
2163
+ r"""
2164
+ Return `H^*` as a rational function in `t` with coefficients in
2165
+ the ring of class functions of the ``acting_group`` of the polytope.
2166
+
2167
+ As in [Stap2011]_, when ``self`` is the polytope `P`,
2168
+ `H^*(t) = (\sum_{m \geq 0} \chi_{mP} t^m)(\det(I-\rho(t)))`.
2169
+ The irreducible characters of ``acting_group`` form an orthonormal basis
2170
+ for the ring of class functions with values in `\CC`.
2171
+ The coefficients of `H^*(t)` are expressed in this basis.
2172
+
2173
+ INPUT:
2174
+
2175
+ - ``acting_group`` -- (default=None) a permgroup object. A subgroup of
2176
+ ``self``'s ``restricted_automorphism_group`` output as a permutation.
2177
+ If ``None``, it is set to the full ``restricted_automorphism_group``
2178
+ of ``self``. The acting group should always use output='permutation'.
2179
+
2180
+ - ``output`` -- string. an output option. The allowed values are:
2181
+
2182
+ * ``None`` (default): returns the rational function `H^*(t)`. `H^*` is
2183
+ a rational function in `t` with coefficients in the ring of
2184
+ class functions.
2185
+ * ``'e_series_list'``: string. Returns a list of the ehrhart series
2186
+ for the fixed subpolytopes of each conjugacy class representative.
2187
+ * ``'determinant_vec'``: string. Returns a list of the determinants
2188
+ of `Id-\rho*t` for each conjugacy class representative.
2189
+ * ``'Hstar_as_lin_comb'``: string. Returns a vector of the coefficients
2190
+ of the irreducible representations in the expression of `H^*`.
2191
+ * ``'prod_det_es'``: string. Returns a vector of the product of
2192
+ determinants and the Ehrhart series.
2193
+ * ``'complete'``: string. Returns a dictionary with Hstar,
2194
+ Hstar_as_lin_comb, the conjugacy class representatives,
2195
+ the character table of the acting group, and
2196
+ whether Hstar is effective.
2197
+
2198
+ OUTPUT:
2199
+
2200
+ The default output is the rational function `H^*`. `H^*` is a rational
2201
+ function in `t` with coefficients in the ring of class functions.
2202
+ There are several output options to see the intermediary outputs of the
2203
+ function.
2204
+
2205
+ EXAMPLES:
2206
+
2207
+ The `H^*`-polynomial of the standard `d-1` dimensional simplex
2208
+ `S = conv(e_1, \dots, e_d)` under its ``restricted_automorphism_group``
2209
+ is equal to 1 = `\chi_{trivial}` (Prop 6.1 [Stap2011]_).
2210
+ Here is the computation for the 3-dimensional standard simplex::
2211
+
2212
+ sage: # needs sage.groups
2213
+ sage: S = polytopes.simplex(3, backend='normaliz'); S
2214
+ A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices
2215
+ sage: G = S.restricted_automorphism_group(output='permutation')
2216
+ sage: G.is_isomorphic(SymmetricGroup(4))
2217
+ True
2218
+ sage: len(G)
2219
+ 24
2220
+ sage: Hstar = S._Hstar_function_normaliz(G); Hstar
2221
+ chi_4
2222
+ sage: G.character_table()
2223
+ [ 1 -1 1 1 -1]
2224
+ [ 3 -1 0 -1 1]
2225
+ [ 2 0 -1 2 0]
2226
+ [ 3 1 0 -1 -1]
2227
+ [ 1 1 1 1 1]
2228
+
2229
+ The next example is Example 7.6 in [Stap2011]_, and shows that `H^*`
2230
+ is not always a polynomial. Let P be the polytope with vertices
2231
+ `\pm(0,0,1),\pm(1,0,1), \pm(0,1,1), \pm(1,1,1)` and let
2232
+ G = `\Zmod{2}` act on P as follows::
2233
+
2234
+ sage: # needs sage.groups
2235
+ sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1], [-1,0,-1],
2236
+ ....: [0,1,1], [0,-1,-1], [1,1,1], [-1,-1,-1]],
2237
+ ....: backend='normaliz')
2238
+ sage: K = P.restricted_automorphism_group(output='permutation')
2239
+ sage: G = K.subgroup(gens=[K([(0,2),(1,3),(4,6),(5,7)])])
2240
+ sage: conj_reps = G.conjugacy_classes_representatives()
2241
+ sage: Dict = P.permutations_to_matrices(conj_reps, acting_group=G)
2242
+ sage: list(Dict.keys())[0]
2243
+ (0,2)(1,3)(4,6)(5,7)
2244
+ sage: list(Dict.values())[0]
2245
+ [-1 0 1 0]
2246
+ [ 0 1 0 0]
2247
+ [ 0 0 1 0]
2248
+ [ 0 0 0 1]
2249
+ sage: len(G)
2250
+ 2
2251
+ sage: G.character_table()
2252
+ [ 1 1]
2253
+ [ 1 -1]
2254
+
2255
+ Then we calculate the rational function `H^*(t)`::
2256
+
2257
+ sage: Hst = P._Hstar_function_normaliz(G); Hst # needs sage.groups
2258
+ (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
2259
+ + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
2260
+
2261
+ To see the exact as written in [Stap2011]_, we can format it as
2262
+ ``'Hstar_as_lin_comb'``. The first coordinate is the coefficient of the
2263
+ trivial character; the second is the coefficient of the sign character::
2264
+
2265
+ sage: lin = P._Hstar_function_normaliz(G, output='Hstar_as_lin_comb'); lin # needs sage.groups
2266
+ ((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1), (3*t^3 + 2*t^2 + 3*t)/(t + 1))
2267
+ """
2268
+ from sage.groups.conjugacy_classes import ConjugacyClassGAP
2269
+ from sage.rings.number_field.number_field import CyclotomicField
2270
+ from sage.rings.qqbar import QQbar
2271
+ from sage.matrix.matrix_space import MatrixSpace
2272
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
2273
+ from sage.matrix.special import identity_matrix
2274
+ # Setting the group
2275
+ G_perm = self.restricted_automorphism_group(output='permutation')
2276
+
2277
+ if acting_group is not None:
2278
+ if not acting_group.is_subgroup(G_perm):
2279
+ raise TypeError("the 'acting_group' should be a subgroup of the 'restricted_automorphism_group'.")
2280
+ G_perm = acting_group
2281
+ # Create the Gap group one time only (each creation has different conj reps)
2282
+ G_perm_gap = G_perm._libgap_()
2283
+
2284
+ # Fixing the conjugacy classes representatives once and for all
2285
+ cls = G_perm_gap.ConjugacyClasses()
2286
+ L = [cl.Representative() for cl in cls]
2287
+ conj_classes = [ConjugacyClassGAP(G_perm, G_perm.element_class(rep, G_perm, check=False)) for rep in L]
2288
+ conj_reps = [cl[0] for cl in conj_classes]
2289
+
2290
+ # Creating the Character Table
2291
+ n_classes = len(conj_reps)
2292
+ irrG_perm_gap = G_perm_gap.Irr()
2293
+ ct = [[irrG_perm_gap[i, j] for j in range(n_classes)] for i in range(n_classes)]
2294
+ e = irrG_perm_gap.Flat().Conductor()
2295
+ K = CyclotomicField(e)
2296
+ ct = [[K(x) for x in v] for v in ct]
2297
+ # Finally return the result as a matrix.
2298
+ MS = MatrixSpace(K, n_classes)
2299
+ char_initial = MS(ct)
2300
+
2301
+ # A check on whether the character table has permuted columns
2302
+ tbl = G_perm_gap.CharacterTable()
2303
+ perm = tbl.IdentificationOfConjugacyClasses()
2304
+ ident_perm = list(range(1, 1 + n_classes))
2305
+ assert perm == ident_perm, "The conjugacy classes don't match with the character table"
2306
+
2307
+ # Create fixed subpolytopes and their Ehrhart series
2308
+ group_dict = self.permutations_to_matrices(conj_reps, acting_group)
2309
+ fix_polys = self.fixed_subpolytopes(conj_reps)
2310
+ list_es = [fix_polys[g].ehrhart_series() for g in conj_reps]
2311
+
2312
+ # get the list of the denominators det([Id - rho (t)])
2313
+ ring = PolynomialRing(QQbar, 't')
2314
+ det_vector = list()
2315
+ dim = group_dict[G_perm.gens()[0]].dimensions()[0]
2316
+ t = ring.gens()[0]
2317
+ ts_matrix = t * identity_matrix(ring, dim)
2318
+ identity = identity_matrix(ring, dim)
2319
+
2320
+ # ix the determinant if polytope isn't full dimensional
2321
+ codim = self.ambient_dim() - self.dim()
2322
+ for perm in conj_reps:
2323
+ mat = group_dict[perm]
2324
+ mat = mat.change_ring(ring)
2325
+ new_matrix = identity - mat * ts_matrix
2326
+ det = (1 - t)**-codim * (new_matrix.determinant())
2327
+ det_vector.append(det)
2328
+
2329
+ FF = ring.fraction_field()
2330
+ initial_result = vector(FF, [a * b for a, b in zip(det_vector, list_es)])
2331
+ Char = char_initial.change_ring(FF)
2332
+ new_result = Char.solve_left(initial_result)
2333
+
2334
+ new_new_result = self._Hstar_as_rat_fct(new_result)
2335
+ if output is None:
2336
+ return new_new_result
2337
+ if output == 'e_series_list':
2338
+ return list_es
2339
+ if output == 'determinant_vec':
2340
+ return det_vector
2341
+ if output == 'Hstar_as_lin_comb':
2342
+ return new_result
2343
+ if output == 'prod_det_es':
2344
+ return initial_result
2345
+ if output == 'complete':
2346
+ results_dictionary = {}
2347
+ results_dictionary['Hstar'] = new_new_result
2348
+ results_dictionary['Hstar_as_lin_comb'] = new_result
2349
+ results_dictionary['conjugacy_class_reps'] = conj_reps
2350
+ results_dictionary['character_table'] = char_initial
2351
+ results_dictionary['is_effective'] = self._is_effective_normaliz(new_new_result, new_result)
2352
+ return results_dictionary
2353
+
2354
+ def _Hstar_as_rat_fct(self, initial_Hstar):
2355
+ r"""
2356
+ Rewrite the vector representing `H^*(t)` given as a linear combination
2357
+ of the irreducible representations of the acting group as a rational
2358
+ function in `t`.
2359
+
2360
+ INPUT:
2361
+
2362
+ - ``initial_Hstar`` -- a vector of rational functions in `t`
2363
+
2364
+ OUTPUT:
2365
+
2366
+ A rational function in `t` with coefficients in the ring of class functions
2367
+ of ``self.restricted_automorphism_group()``.
2368
+
2369
+ EXAMPLES:
2370
+
2371
+ The expression of `H^*` as a polynomial in `t` for a 3-dimensional simplex
2372
+ is computed as follows::
2373
+
2374
+ sage: simplex = Polyhedron(vertices=[[0,0,0], [1,0,0],
2375
+ ....: [0,1,0], [0,0,1]], backend='normaliz')
2376
+ sage: Hstar = simplex.Hstar_function(); Hstar # indirect doctest # needs sage.rings.number_field
2377
+ chi_4
2378
+
2379
+ The polynomial is `\chi_4 \cdot t^0`. We can see which irreducible
2380
+ representation `\chi_4` corresponds to by looking at the character table::
2381
+
2382
+ sage: G = simplex.restricted_automorphism_group(output='permutation') # needs sage.groups
2383
+ sage: char = G.character_table(); char # needs sage.groups
2384
+ [ 1 -1 1 1 -1]
2385
+ [ 3 -1 0 -1 1]
2386
+ [ 2 0 -1 2 0]
2387
+ [ 3 1 0 -1 -1]
2388
+ [ 1 1 1 1 1]
2389
+
2390
+ Thus `\chi_4` corresponds to the trivial representation of the group, and
2391
+ for every element in the group, it evaluates to 1.
2392
+
2393
+ As another example, we can look at `H^*(t)` for the `\pm 1` square::
2394
+
2395
+ sage: square = Polyhedron(vertices=[[1,1], [-1,1], [-1,-1], [1,-1]],
2396
+ ....: backend='normaliz')
2397
+ sage: Hstar = square.Hstar_function(); Hstar # needs sage.rings.number_field
2398
+ chi_0*t^2 + (2*chi_0 + chi_2 + chi_3 + chi_4)*t + chi_0
2399
+
2400
+ Plugging in the values from the first column of the character table below
2401
+ yields the `h^*`-polynomial of the square, `t^2+6t+1`::
2402
+
2403
+ sage: G = square.restricted_automorphism_group(output='permutation') # needs sage.groups
2404
+ sage: G.character_table() # needs sage.groups
2405
+ [ 1 1 1 1 1]
2406
+ [ 1 -1 -1 1 1]
2407
+ [ 1 -1 1 -1 1]
2408
+ [ 1 1 -1 -1 1]
2409
+ [ 2 0 0 0 -2]
2410
+ """
2411
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
2412
+ from sage.rings.qqbar import QQbar
2413
+ chi_vars = ','.join(f'chi_{i}' for i in range(len(initial_Hstar)))
2414
+ Chi_ring = PolynomialRing(QQbar, chi_vars)
2415
+ virtual_ring = PolynomialRing(Chi_ring, initial_Hstar.base_ring().gens())
2416
+ fraction_virtual_ring = virtual_ring.fraction_field()
2417
+ return initial_Hstar.change_ring(fraction_virtual_ring) * vector(fraction_virtual_ring, Chi_ring.gens())
2418
+
2419
+ def _is_effective_normaliz(self, Hstar, Hstar_as_lin_comb):
2420
+ r"""
2421
+ Test for the effectiveness of the ``Hstar`` series of this polytope.
2422
+
2423
+ The ``Hstar`` series of the polytope is determined by the action of a
2424
+ subgroup of the polytope's ``restricted_automorphism_group``. The
2425
+ ``Hstar`` series is effective if it is a polynomial in `t` and the
2426
+ coefficient of each `t^i` is an effective character in the ring of
2427
+ class functions of the acting group. A character `\rho` is effective if
2428
+ the coefficients of the irreducible representations in the expression
2429
+ of `\rho` are nonnegative integers.
2430
+
2431
+ INPUT:
2432
+
2433
+ - ``Hstar`` -- a rational function in `t` with coefficients in the ring
2434
+ of class functions
2435
+
2436
+ - ``Hstar_as_lin_comb`` -- vector. The coefficients of the irreducible
2437
+ representations of the acting group in the expression of ``Hstar`` as
2438
+ a linear combination of irreducible representations with coefficients
2439
+ in the field of rational functions in `t`.
2440
+
2441
+ OUTPUT: boolean; whether the ``Hstar`` series is effective
2442
+
2443
+ EXAMPLES:
2444
+
2445
+ The `H^*` series of the two-dimensional permutahedron under the action
2446
+ of the symmetric group is effective::
2447
+
2448
+ sage: # needs sage.groups
2449
+ sage: p3 = polytopes.permutahedron(3, backend='normaliz')
2450
+ sage: G = p3.restricted_automorphism_group(output='permutation')
2451
+ sage: reflection12 = G([(0,2),(1,4),(3,5)])
2452
+ sage: reflection23 = G([(0,1),(2,3),(4,5)])
2453
+ sage: S3 = G.subgroup(gens=[reflection12, reflection23])
2454
+ sage: S3.is_isomorphic(SymmetricGroup(3))
2455
+ True
2456
+ sage: Hstar = p3.Hstar_function(S3) # needs sage.rings.number_field
2457
+ sage: Hlin = p3.Hstar_function(S3, output='Hstar_as_lin_comb') # needs sage.rings.number_field
2458
+ sage: p3._is_effective_normaliz(Hstar, Hlin) # needs sage.rings.number_field
2459
+ True
2460
+
2461
+ If the `H^*`-series is not polynomial, then it is not effective::
2462
+
2463
+ sage: # needs sage.groups
2464
+ sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1], [-1,0,-1],
2465
+ ....: [0,1,1], [0,-1,-1], [1,1,1], [-1,-1,-1]],
2466
+ ....: backend='normaliz')
2467
+ sage: G = P.restricted_automorphism_group(output='permutation')
2468
+ sage: H = G.subgroup(gens = [G([(0,2),(1,3),(4,6),(5,7)])])
2469
+ sage: Hstar = P.Hstar_function(H); Hstar # needs sage.rings.number_field
2470
+ (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
2471
+ + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
2472
+ sage: Hstar_lin = P.Hstar_function(H, output='Hstar_as_lin_comb') # needs sage.rings.number_field
2473
+ sage: P._is_effective_normaliz(Hstar, Hstar_lin) # needs sage.rings.number_field
2474
+ False
2475
+ """
2476
+ if not Hstar.denominator().is_unit():
2477
+ return False
2478
+ for irrep in range(len(Hstar_as_lin_comb)):
2479
+ coeffs = Hstar_as_lin_comb[irrep].numerator().coefficients()
2480
+ for i in coeffs:
2481
+ if not i.is_integer() or i < 0:
2482
+ return False
2483
+ return True
2484
+
2485
+
2486
+ #########################################################################
2487
+ class Polyhedron_ZZ_normaliz(Polyhedron_QQ_normaliz, Polyhedron_ZZ):
2488
+ r"""
2489
+ Polyhedra over `\ZZ` with normaliz.
2490
+
2491
+ INPUT:
2492
+
2493
+ - ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``
2494
+ - ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``
2495
+
2496
+ EXAMPLES::
2497
+
2498
+ sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
2499
+ ....: rays=[(1,1)], lines=[],
2500
+ ....: backend='normaliz', base_ring=ZZ)
2501
+ sage: TestSuite(p).run()
2502
+ """
2503
+ pass