passagemath-polyhedra 10.6.31rc3__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (206) 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 +206 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
  7. passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  8. sage/all__sagemath_polyhedra.py +50 -0
  9. sage/game_theory/all.py +8 -0
  10. sage/game_theory/catalog.py +6 -0
  11. sage/game_theory/catalog_normal_form_games.py +923 -0
  12. sage/game_theory/cooperative_game.py +844 -0
  13. sage/game_theory/matching_game.py +1181 -0
  14. sage/game_theory/normal_form_game.py +2697 -0
  15. sage/game_theory/parser.py +275 -0
  16. sage/geometry/all__sagemath_polyhedra.py +22 -0
  17. sage/geometry/cone.py +6940 -0
  18. sage/geometry/cone_catalog.py +847 -0
  19. sage/geometry/cone_critical_angles.py +1027 -0
  20. sage/geometry/convex_set.py +1119 -0
  21. sage/geometry/fan.py +3743 -0
  22. sage/geometry/fan_isomorphism.py +389 -0
  23. sage/geometry/fan_morphism.py +1884 -0
  24. sage/geometry/hasse_diagram.py +202 -0
  25. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  26. sage/geometry/hyperplane_arrangement/all.py +1 -0
  27. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  28. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  29. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  30. sage/geometry/hyperplane_arrangement/library.py +825 -0
  31. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  32. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  33. sage/geometry/integral_points.py +35 -0
  34. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
  35. sage/geometry/integral_points_generic_dense.pyx +7 -0
  36. sage/geometry/lattice_polytope.py +5894 -0
  37. sage/geometry/linear_expression.py +773 -0
  38. sage/geometry/newton_polygon.py +767 -0
  39. sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
  40. sage/geometry/point_collection.pyx +1008 -0
  41. sage/geometry/polyhedral_complex.py +2616 -0
  42. sage/geometry/polyhedron/all.py +8 -0
  43. sage/geometry/polyhedron/backend_cdd.py +460 -0
  44. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  45. sage/geometry/polyhedron/backend_field.py +347 -0
  46. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  47. sage/geometry/polyhedron/backend_number_field.py +168 -0
  48. sage/geometry/polyhedron/backend_polymake.py +765 -0
  49. sage/geometry/polyhedron/backend_ppl.py +582 -0
  50. sage/geometry/polyhedron/base.py +1206 -0
  51. sage/geometry/polyhedron/base0.py +1444 -0
  52. sage/geometry/polyhedron/base1.py +886 -0
  53. sage/geometry/polyhedron/base2.py +812 -0
  54. sage/geometry/polyhedron/base3.py +1845 -0
  55. sage/geometry/polyhedron/base4.py +1262 -0
  56. sage/geometry/polyhedron/base5.py +2700 -0
  57. sage/geometry/polyhedron/base6.py +1741 -0
  58. sage/geometry/polyhedron/base7.py +997 -0
  59. sage/geometry/polyhedron/base_QQ.py +1258 -0
  60. sage/geometry/polyhedron/base_RDF.py +98 -0
  61. sage/geometry/polyhedron/base_ZZ.py +934 -0
  62. sage/geometry/polyhedron/base_mutable.py +215 -0
  63. sage/geometry/polyhedron/base_number_field.py +122 -0
  64. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  65. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  66. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  88. sage/geometry/polyhedron/constructor.py +773 -0
  89. sage/geometry/polyhedron/double_description.py +753 -0
  90. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  91. sage/geometry/polyhedron/face.py +1060 -0
  92. sage/geometry/polyhedron/generating_function.py +1810 -0
  93. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  94. sage/geometry/polyhedron/library.py +3502 -0
  95. sage/geometry/polyhedron/misc.py +121 -0
  96. sage/geometry/polyhedron/modules/all.py +1 -0
  97. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  98. sage/geometry/polyhedron/palp_database.py +447 -0
  99. sage/geometry/polyhedron/parent.py +1279 -0
  100. sage/geometry/polyhedron/plot.py +1986 -0
  101. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  102. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  103. sage/geometry/polyhedron/representation.py +1723 -0
  104. sage/geometry/pseudolines.py +515 -0
  105. sage/geometry/relative_interior.py +445 -0
  106. sage/geometry/toric_plotter.py +1103 -0
  107. sage/geometry/triangulation/all.py +2 -0
  108. sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
  109. sage/geometry/triangulation/base.pyx +963 -0
  110. sage/geometry/triangulation/data.h +147 -0
  111. sage/geometry/triangulation/data.pxd +4 -0
  112. sage/geometry/triangulation/element.py +914 -0
  113. sage/geometry/triangulation/functions.h +10 -0
  114. sage/geometry/triangulation/functions.pxd +4 -0
  115. sage/geometry/triangulation/point_configuration.py +2256 -0
  116. sage/geometry/triangulation/triangulations.h +49 -0
  117. sage/geometry/triangulation/triangulations.pxd +7 -0
  118. sage/geometry/voronoi_diagram.py +319 -0
  119. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  120. sage/interfaces/polymake.py +2028 -0
  121. sage/numerical/all.py +13 -0
  122. sage/numerical/all__sagemath_polyhedra.py +11 -0
  123. sage/numerical/backends/all.py +1 -0
  124. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  125. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  126. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  127. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  128. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  129. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  130. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  131. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  132. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  133. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  134. sage/numerical/backends/generic_backend_test.py +24 -0
  135. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  136. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  137. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  138. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  139. sage/numerical/backends/logging_backend.py +391 -0
  140. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  141. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  142. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  143. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  144. sage/numerical/backends/ppl_backend.pyx +1126 -0
  145. sage/numerical/backends/ppl_backend_test.py +13 -0
  146. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  147. sage/numerical/backends/scip_backend.pxd +22 -0
  148. sage/numerical/backends/scip_backend.pyx +1289 -0
  149. sage/numerical/backends/scip_backend_test.py +13 -0
  150. sage/numerical/interactive_simplex_method.py +5338 -0
  151. sage/numerical/knapsack.py +665 -0
  152. sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
  153. sage/numerical/linear_functions.pxd +31 -0
  154. sage/numerical/linear_functions.pyx +1648 -0
  155. sage/numerical/linear_tensor.py +470 -0
  156. sage/numerical/linear_tensor_constraints.py +448 -0
  157. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
  158. sage/numerical/linear_tensor_element.pxd +6 -0
  159. sage/numerical/linear_tensor_element.pyx +459 -0
  160. sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
  161. sage/numerical/mip.pxd +40 -0
  162. sage/numerical/mip.pyx +3667 -0
  163. sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
  164. sage/numerical/sdp.pxd +39 -0
  165. sage/numerical/sdp.pyx +1433 -0
  166. sage/rings/all__sagemath_polyhedra.py +3 -0
  167. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  168. sage/rings/polynomial/omega.py +982 -0
  169. sage/schemes/all__sagemath_polyhedra.py +2 -0
  170. sage/schemes/toric/all.py +10 -0
  171. sage/schemes/toric/chow_group.py +1248 -0
  172. sage/schemes/toric/divisor.py +2082 -0
  173. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
  174. sage/schemes/toric/divisor_class.pyx +322 -0
  175. sage/schemes/toric/fano_variety.py +1606 -0
  176. sage/schemes/toric/homset.py +650 -0
  177. sage/schemes/toric/ideal.py +451 -0
  178. sage/schemes/toric/library.py +1322 -0
  179. sage/schemes/toric/morphism.py +1958 -0
  180. sage/schemes/toric/points.py +1032 -0
  181. sage/schemes/toric/sheaf/all.py +1 -0
  182. sage/schemes/toric/sheaf/constructor.py +302 -0
  183. sage/schemes/toric/sheaf/klyachko.py +921 -0
  184. sage/schemes/toric/toric_subscheme.py +905 -0
  185. sage/schemes/toric/variety.py +3460 -0
  186. sage/schemes/toric/weierstrass.py +1078 -0
  187. sage/schemes/toric/weierstrass_covering.py +457 -0
  188. sage/schemes/toric/weierstrass_higher.py +288 -0
  189. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  190. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  194. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  195. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  205. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  206. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,1257 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ r"""
3
+ Fast Lattice Polytopes using PPL.
4
+
5
+ The :func:`LatticePolytope_PPL` class is a thin wrapper around PPL
6
+ polyhedra. Its main purpose is to be fast to construct, at the cost of
7
+ being much less full-featured than the usual polyhedra. This makes it
8
+ possible to iterate with it over the list of all 473800776 reflexive
9
+ polytopes in 4 dimensions.
10
+
11
+ .. NOTE::
12
+
13
+ For general lattice polyhedra you should use
14
+ :func:`~sage.geometry.polyhedron.constructor.Polyhedron` with
15
+ ``base_ring=ZZ``.
16
+
17
+ The class derives from the PPL :class:`ppl.polyhedron.C_Polyhedron`
18
+ class, so you can work with the underlying generator and constraint
19
+ objects. However, integral points are generally represented by
20
+ `\ZZ`-vectors. In the following, we always use *generator* to refer
21
+ the PPL generator objects and *vertex* (or integral point) for the
22
+ corresponding `\ZZ`-vector.
23
+
24
+ EXAMPLES::
25
+
26
+ sage: vertices = [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1)]
27
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
28
+ sage: P = LatticePolytope_PPL(vertices); P
29
+ A 4-dimensional lattice polytope in ZZ^4 with 5 vertices
30
+ sage: P.integral_points()
31
+ ((-9, -6, -1, -1), (-3, -2, 0, 0), (-2, -1, 0, 0), (-1, -1, 0, 0),
32
+ (-1, 0, 0, 0), (0, 0, 0, 0), (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0))
33
+ sage: P.integral_points_not_interior_to_facets()
34
+ ((-9, -6, -1, -1), (-3, -2, 0, 0), (0, 0, 0, 0), (1, 0, 0, 0),
35
+ (0, 1, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0))
36
+
37
+ Fibrations of the lattice polytopes are defined as lattice
38
+ sub-polytopes and give rise to fibrations of toric varieties for
39
+ suitable fan refinements. We can compute them using
40
+ :meth:`~LatticePolytope_PPL.fibration_generator` ::
41
+
42
+ sage: F = next(P.fibration_generator(2))
43
+ sage: F.vertices()
44
+ ((1, 0, 0, 0), (0, 1, 0, 0), (-3, -2, 0, 0))
45
+
46
+ Finally, we can compute automorphisms and identify fibrations that
47
+ only differ by a lattice automorphism::
48
+
49
+ sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
50
+ sage: fibers = [ f.vertices() for f in square.fibration_generator(1) ]; fibers
51
+ [((1, 0), (-1, 0)), ((0, 1), (0, -1)), ((-1, -1), (1, 1)), ((-1, 1), (1, -1))]
52
+ sage: square.pointsets_mod_automorphism(fibers) # needs sage.groups
53
+ (frozenset({(-1, -1), (1, 1)}), frozenset({(-1, 0), (1, 0)}))
54
+
55
+ AUTHORS:
56
+
57
+ - Volker Braun: initial version, 2012
58
+ """
59
+
60
+ # ****************************************************************************
61
+ # Copyright (C) 2012 Volker Braun <vbraun.name@gmail.com>
62
+ #
63
+ # This program is free software: you can redistribute it and/or modify
64
+ # it under the terms of the GNU General Public License as published by
65
+ # the Free Software Foundation, either version 2 of the License, or
66
+ # (at your option) any later version.
67
+ # https://www.gnu.org/licenses/
68
+ # ****************************************************************************
69
+ import copy
70
+ from sage.rings.integer import GCD_list, Integer
71
+ from sage.rings.integer_ring import ZZ
72
+ from sage.misc.cachefunc import cached_method
73
+ from sage.modules.free_module_element import vector
74
+ from sage.matrix.constructor import matrix
75
+ from ppl import (
76
+ C_Polyhedron, Linear_Expression, Variable,
77
+ point, line, Generator, Generator_System,
78
+ Poly_Con_Relation )
79
+
80
+
81
+ ########################################################################
82
+ def _class_for_LatticePolytope(dim):
83
+ """
84
+ Return the appropriate class in the given dimension.
85
+
86
+ Helper function for :func:`LatticePolytope_PPL`. You should not
87
+ have to use this function manually.
88
+
89
+ INPUT:
90
+
91
+ - ``dim`` -- integer; the ambient space dimension
92
+
93
+ OUTPUT: the appropriate class for the lattice polytope
94
+
95
+ EXAMPLES::
96
+
97
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import _class_for_LatticePolytope
98
+ sage: _class_for_LatticePolytope(2)
99
+ <class 'sage.geometry.polyhedron.ppl_lattice_polygon.LatticePolygon_PPL_class'>
100
+ sage: _class_for_LatticePolytope(3)
101
+ <class 'sage.geometry.polyhedron.ppl_lattice_polytope.LatticePolytope_PPL_class'>
102
+ """
103
+ if dim <= 2:
104
+ from sage.geometry.polyhedron.ppl_lattice_polygon import LatticePolygon_PPL_class
105
+ return LatticePolygon_PPL_class
106
+ return LatticePolytope_PPL_class
107
+
108
+
109
+ ########################################################################
110
+ def LatticePolytope_PPL(*args):
111
+ """
112
+ Construct a new instance of the PPL-based lattice polytope class.
113
+
114
+ EXAMPLES::
115
+
116
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
117
+ sage: LatticePolytope_PPL((0,0), (1,0), (0,1))
118
+ A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
119
+
120
+ sage: from ppl import point, Generator_System, C_Polyhedron, Linear_Expression # needs pplpy
121
+ sage: p = point(Linear_Expression([2,3],0)); p # needs pplpy
122
+ point(2/1, 3/1)
123
+ sage: LatticePolytope_PPL(p) # needs pplpy
124
+ A 0-dimensional lattice polytope in ZZ^2 with 1 vertex
125
+
126
+ sage: P = C_Polyhedron(Generator_System(p)); P # needs pplpy
127
+ A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
128
+ sage: LatticePolytope_PPL(P) # needs pplpy
129
+ A 0-dimensional lattice polytope in ZZ^2 with 1 vertex
130
+
131
+ A :exc:`TypeError` is raised if the arguments do not specify a lattice polytope::
132
+
133
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
134
+ sage: LatticePolytope_PPL((0,0), (1/2,1)) # needs pplpy
135
+ Traceback (most recent call last):
136
+ ...
137
+ TypeError: unable to convert rational 1/2 to an integer
138
+
139
+ sage: from ppl import point, Generator_System, C_Polyhedron, Linear_Expression # needs pplpy
140
+ sage: p = point(Linear_Expression([2,3],0), 5); p # needs pplpy
141
+ point(2/5, 3/5)
142
+ sage: LatticePolytope_PPL(p) # needs pplpy
143
+ Traceback (most recent call last):
144
+ ...
145
+ TypeError: generator is not a lattice polytope generator
146
+
147
+ sage: P = C_Polyhedron(Generator_System(p)); P # needs pplpy
148
+ A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
149
+ sage: LatticePolytope_PPL(P) # needs pplpy
150
+ Traceback (most recent call last):
151
+ ...
152
+ TypeError: polyhedron has non-integral generators
153
+ """
154
+ polytope_class = LatticePolytope_PPL_class
155
+ if len(args) == 1 and isinstance(args[0], C_Polyhedron):
156
+ polyhedron = args[0]
157
+ polytope_class = _class_for_LatticePolytope(polyhedron.space_dimension())
158
+ if not all(p.is_point() and p.divisor() == 1 for p in polyhedron.generators()):
159
+ raise TypeError('polyhedron has non-integral generators')
160
+ return polytope_class(polyhedron)
161
+ if len(args) == 1 \
162
+ and isinstance(args[0], (list, tuple)) \
163
+ and isinstance(args[0][0], (list,tuple)):
164
+ vertices = args[0]
165
+ else:
166
+ vertices = args
167
+ gs = Generator_System()
168
+ for v in vertices:
169
+ if isinstance(v, Generator):
170
+ if (not v.is_point()) or v.divisor() != 1:
171
+ raise TypeError('generator is not a lattice polytope generator')
172
+ gs.insert(v)
173
+ else:
174
+ gs.insert(point(Linear_Expression(v, 0)))
175
+ if not gs.empty():
176
+ dim = next(iter(gs)).space_dimension()
177
+ polytope_class = _class_for_LatticePolytope(dim)
178
+ return polytope_class(gs)
179
+
180
+
181
+ ########################################################################
182
+ class LatticePolytope_PPL_class(C_Polyhedron):
183
+ """
184
+ The lattice polytope class.
185
+
186
+ You should use :func:`LatticePolytope_PPL` to construct instances.
187
+
188
+ EXAMPLES::
189
+
190
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
191
+ sage: LatticePolytope_PPL((0,0), (1,0), (0,1))
192
+ A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
193
+ """
194
+
195
+ def __repr__(self):
196
+ """
197
+ Return the string representation.
198
+
199
+ OUTPUT: string
200
+
201
+ EXAMPLES::
202
+
203
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
204
+ sage: P = LatticePolytope_PPL((0,0), (1,0), (0,1))
205
+ sage: P
206
+ A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
207
+ sage: P.__repr__()
208
+ 'A 2-dimensional lattice polytope in ZZ^2 with 3 vertices'
209
+
210
+ sage: LatticePolytope_PPL()
211
+ The empty lattice polytope in ZZ^0
212
+ """
213
+ desc = ''
214
+ if self.n_vertices() == 0:
215
+ desc += 'The empty lattice polytope'
216
+ else:
217
+ desc += 'A ' + repr(self.affine_dimension()) + '-dimensional lattice polytope'
218
+ desc += ' in ZZ^' + repr(self.space_dimension())
219
+
220
+ if self.n_vertices() > 0:
221
+ desc += ' with '
222
+ desc += repr(self.n_vertices())
223
+ if self.n_vertices() == 1:
224
+ desc += ' vertex'
225
+ else:
226
+ desc += ' vertices'
227
+ return desc
228
+
229
+ def is_bounded(self):
230
+ """
231
+ Return whether the lattice polytope is compact.
232
+
233
+ OUTPUT: always ``True``, since polytopes are by definition compact
234
+
235
+ EXAMPLES::
236
+
237
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
238
+ sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).is_bounded()
239
+ True
240
+ """
241
+ return True
242
+
243
+ @cached_method
244
+ def n_vertices(self):
245
+ """
246
+ Return the number of vertices.
247
+
248
+ OUTPUT: integer; the number of vertices
249
+
250
+ EXAMPLES::
251
+
252
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
253
+ sage: LatticePolytope_PPL((0,0,0), (1,0,0), (0,1,0)).n_vertices()
254
+ 3
255
+ """
256
+ return len(self.minimized_generators())
257
+
258
+ @cached_method
259
+ def is_simplex(self):
260
+ r"""
261
+ Return whether the polyhedron is a simplex.
262
+
263
+ OUTPUT:
264
+
265
+ Boolean, whether the polyhedron is a simplex (possibly of
266
+ strictly smaller dimension than the ambient space).
267
+
268
+ EXAMPLES::
269
+
270
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
271
+ sage: LatticePolytope_PPL((0,0,0), (1,0,0), (0,1,0)).is_simplex()
272
+ True
273
+ """
274
+ return self.affine_dimension()+1 == self.n_vertices()
275
+
276
+ @cached_method
277
+ def bounding_box(self):
278
+ r"""
279
+ Return the coordinates of a rectangular box containing the non-empty polytope.
280
+
281
+ OUTPUT:
282
+
283
+ A pair of tuples ``(box_min, box_max)`` where ``box_min`` are
284
+ the coordinates of a point bounding the coordinates of the
285
+ polytope from below and ``box_max`` bounds the coordinates
286
+ from above.
287
+
288
+ EXAMPLES::
289
+
290
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
291
+ sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).bounding_box()
292
+ ((0, 0), (1, 1))
293
+ """
294
+ box_min = []
295
+ box_max = []
296
+ if self.is_empty():
297
+ raise ValueError('empty polytope is not allowed')
298
+ for i in range(self.space_dimension()):
299
+ x = Variable(i)
300
+ coords = [Integer(v.coefficient(x)) for v in self.generators()]
301
+ max_coord = max(coords)
302
+ min_coord = min(coords)
303
+ box_max.append(max_coord)
304
+ box_min.append(min_coord)
305
+ return (tuple(box_min), tuple(box_max))
306
+
307
+ @cached_method
308
+ def n_integral_points(self):
309
+ """
310
+ Return the number of integral points.
311
+
312
+ OUTPUT:
313
+
314
+ Integer. The number of integral points contained in the
315
+ lattice polytope.
316
+
317
+ EXAMPLES::
318
+
319
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
320
+ sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).n_integral_points()
321
+ 3
322
+ """
323
+ if self.is_empty():
324
+ return tuple()
325
+ box_min, box_max = self.bounding_box()
326
+ from sage.geometry.integral_points import rectangular_box_points
327
+ return rectangular_box_points(list(box_min), list(box_max), self, count_only=True)
328
+
329
+ @cached_method
330
+ def integral_points(self):
331
+ r"""
332
+ Return the integral points in the polyhedron.
333
+
334
+ Uses the naive algorithm (iterate over a rectangular bounding
335
+ box).
336
+
337
+ OUTPUT:
338
+
339
+ The list of integral points in the polyhedron. If the
340
+ polyhedron is not compact, a :exc:`ValueError` is raised.
341
+
342
+ EXAMPLES::
343
+
344
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
345
+ sage: LatticePolytope_PPL((-1,-1), (1,0), (1,1), (0,1)).integral_points()
346
+ ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))
347
+
348
+ sage: simplex = LatticePolytope_PPL((1,2,3), (2,3,7), (-2,-3,-11))
349
+ sage: simplex.integral_points()
350
+ ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
351
+
352
+ The polyhedron need not be full-dimensional::
353
+
354
+ sage: simplex = LatticePolytope_PPL((1,2,3,5), (2,3,7,5), (-2,-3,-11,5))
355
+ sage: simplex.integral_points()
356
+ ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))
357
+
358
+ sage: point = LatticePolytope_PPL((2,3,7))
359
+ sage: point.integral_points()
360
+ ((2, 3, 7),)
361
+
362
+ sage: empty = LatticePolytope_PPL()
363
+ sage: empty.integral_points()
364
+ ()
365
+
366
+ Here is a simplex where the naive algorithm of running over
367
+ all points in a rectangular bounding box no longer works fast
368
+ enough::
369
+
370
+ sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
371
+ sage: simplex = LatticePolytope_PPL(v); simplex
372
+ A 4-dimensional lattice polytope in ZZ^4 with 5 vertices
373
+ sage: len(simplex.integral_points())
374
+ 49
375
+
376
+ Finally, the 3-d reflexive polytope number 4078::
377
+
378
+ sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
379
+ ....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
380
+ sage: P = LatticePolytope_PPL(*v)
381
+ sage: pts1 = P.integral_points() # Sage's own code
382
+ sage: pts2 = LatticePolytope(v).points() # needs palp
383
+ sage: for p in pts1: p.set_immutable()
384
+ sage: set(pts1) == set(pts2) # needs palp
385
+ True
386
+
387
+ sage: len(Polyhedron(v).integral_points()) # takes about 1 ms
388
+ 23
389
+ sage: len(LatticePolytope(v).points()) # takes about 13 ms # needs palp
390
+ 23
391
+ sage: len(LatticePolytope_PPL(*v).integral_points()) # takes about 0.5 ms
392
+ 23
393
+ """
394
+ if self.is_empty():
395
+ return tuple()
396
+ box_min, box_max = self.bounding_box()
397
+ from sage.geometry.integral_points import rectangular_box_points
398
+ points = rectangular_box_points(list(box_min), list(box_max), self)
399
+ if not self.n_integral_points.is_in_cache():
400
+ self.n_integral_points.set_cache(len(points))
401
+ return points
402
+
403
+ @cached_method
404
+ def _integral_points_saturating(self):
405
+ """
406
+ Return the integral points together with information about
407
+ which inequalities are saturated.
408
+
409
+ See :func:`~sage.geometry.integral_points.rectangular_box_points`.
410
+
411
+ OUTPUT:
412
+
413
+ A tuple of pairs (one for each integral point) consisting of a
414
+ pair ``(point, Hrep)``, where ``point`` is the coordinate
415
+ vector of the integral point and ``Hrep`` is the set of
416
+ indices of the :meth:`minimized_constraints` that are
417
+ saturated at the point.
418
+
419
+ EXAMPLES::
420
+
421
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
422
+ sage: quad = LatticePolytope_PPL((-1,-1), (0,1), (1,0), (1,1))
423
+ sage: quad._integral_points_saturating()
424
+ (((-1, -1), frozenset({0, 1})),
425
+ ((0, 0), frozenset()),
426
+ ((0, 1), frozenset({0, 3})),
427
+ ((1, 0), frozenset({1, 2})),
428
+ ((1, 1), frozenset({2, 3})))
429
+ """
430
+ if self.is_empty():
431
+ return tuple()
432
+ box_min, box_max = self.bounding_box()
433
+ from sage.geometry.integral_points import rectangular_box_points
434
+ points = rectangular_box_points(list(box_min), list(box_max), self,
435
+ return_saturated=True)
436
+ if not self.n_integral_points.is_in_cache():
437
+ self.n_integral_points.set_cache(len(points))
438
+ if not self.integral_points.is_in_cache():
439
+ self.integral_points.set_cache(tuple(p[0] for p in points))
440
+ return points
441
+
442
+ @cached_method
443
+ def integral_points_not_interior_to_facets(self):
444
+ """
445
+ Return the integral points not interior to facets.
446
+
447
+ OUTPUT:
448
+
449
+ A tuple whose entries are the coordinate vectors of integral
450
+ points not interior to facets (codimension one faces) of the
451
+ lattice polytope.
452
+
453
+ EXAMPLES::
454
+
455
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
456
+ sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
457
+ sage: square.n_integral_points()
458
+ 9
459
+ sage: square.integral_points_not_interior_to_facets()
460
+ ((-1, -1), (-1, 1), (0, 0), (1, -1), (1, 1))
461
+ """
462
+ n = 1 + self.space_dimension() - self.affine_dimension()
463
+ return tuple(p[0] for p in self._integral_points_saturating() if len(p[1]) != n)
464
+
465
+ @cached_method
466
+ def vertices(self):
467
+ r"""
468
+ Return the vertices as a tuple of `\ZZ`-vectors.
469
+
470
+ OUTPUT:
471
+
472
+ A tuple of `\ZZ`-vectors. Each entry is the coordinate vector
473
+ of an integral points of the lattice polytope.
474
+
475
+ EXAMPLES::
476
+
477
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
478
+ sage: p = LatticePolytope_PPL((-9,-6,-1,-1),
479
+ ....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
480
+ sage: p.vertices()
481
+ ((-9, -6, -1, -1), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (1, 0, 0, 0))
482
+ sage: p.minimized_generators()
483
+ Generator_System {point(-9/1, -6/1, -1/1, -1/1), point(0/1, 0/1, 0/1, 1/1),
484
+ point(0/1, 0/1, 1/1, 0/1), point(0/1, 1/1, 0/1, 0/1), point(1/1, 0/1, 0/1, 0/1)}
485
+ """
486
+ d = self.space_dimension()
487
+ v = vector(ZZ, d)
488
+ points = []
489
+ for g in self.minimized_generators():
490
+ for i in range(d):
491
+ v[i] = g.coefficient(Variable(i))
492
+ v_copy = copy.copy(v)
493
+ v_copy.set_immutable()
494
+ points.append(v_copy)
495
+ return tuple(points)
496
+
497
+ def vertices_saturating(self, constraint):
498
+ r"""
499
+ Return the vertices saturating the constraint.
500
+
501
+ INPUT:
502
+
503
+ - ``constraint`` -- a constraint (inequality or equation) of
504
+ the polytope
505
+
506
+ OUTPUT:
507
+
508
+ The tuple of vertices saturating the constraint. The vertices
509
+ are returned as `\ZZ`-vectors, as in :meth:`vertices`.
510
+
511
+ EXAMPLES::
512
+
513
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
514
+ sage: p = LatticePolytope_PPL((0,0), (0,1), (1,0))
515
+ sage: ieq = next(iter(p.constraints())); ieq
516
+ x0>=0
517
+ sage: p.vertices_saturating(ieq)
518
+ ((0, 0), (0, 1))
519
+ """
520
+ from ppl import C_Polyhedron, Poly_Con_Relation
521
+ result = []
522
+ for i,v in enumerate(self.minimized_generators()):
523
+ v = C_Polyhedron(v)
524
+ if v.relation_with(constraint).implies(Poly_Con_Relation.saturates()):
525
+ result.append(self.vertices()[i])
526
+ return tuple(result)
527
+
528
+ @cached_method
529
+ def is_full_dimensional(self):
530
+ """
531
+ Return whether the lattice polytope is full dimensional.
532
+
533
+ OUTPUT: boolean; whether the :meth:`affine_dimension` equals the
534
+ ambient space dimension
535
+
536
+ EXAMPLES::
537
+
538
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
539
+ sage: p = LatticePolytope_PPL((0,0), (0,1))
540
+ sage: p.is_full_dimensional()
541
+ False
542
+ sage: q = LatticePolytope_PPL((0,0), (0,1), (1,0))
543
+ sage: q.is_full_dimensional()
544
+ True
545
+ """
546
+
547
+ return self.affine_dimension() == self.space_dimension()
548
+
549
+ def fibration_generator(self, dim):
550
+ """
551
+ Generate the lattice polytope fibrations.
552
+
553
+ For the purposes of this function, a lattice polytope fiber is
554
+ a sub-lattice polytope. Projecting the plane spanned by the
555
+ subpolytope to a point yields another lattice polytope, the
556
+ base of the fibration.
557
+
558
+ INPUT:
559
+
560
+ - ``dim`` -- integer; the dimension of the lattice polytope
561
+ fiber
562
+
563
+ OUTPUT:
564
+
565
+ A generator yielding the distinct lattice polytope fibers of
566
+ given dimension.
567
+
568
+ EXAMPLES::
569
+
570
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
571
+ sage: p = LatticePolytope_PPL((-9,-6,-1,-1),
572
+ ....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
573
+ sage: list(p.fibration_generator(2))
574
+ [A 2-dimensional lattice polytope in ZZ^4 with 3 vertices]
575
+ """
576
+ assert self.is_full_dimensional()
577
+ # "points" are the potential vertices of the fiber. They are
578
+ # in the $codim$-skeleton of the polytope, which is contained
579
+ # in the points that saturate at least $dim$ equations.
580
+ points = [ p for p in self._integral_points_saturating() if len(p[1]) >= dim ]
581
+ points = sorted(points, key=lambda x:len(x[1]))
582
+
583
+ # iterate over point combinations subject to all points being on one facet.
584
+ def point_combinations_iterator(n, i0=0, saturated=None):
585
+ for i in range(i0, len(points)):
586
+ p, ieqs = points[i]
587
+ if saturated is None:
588
+ saturated_ieqs = ieqs
589
+ else:
590
+ saturated_ieqs = saturated.intersection(ieqs)
591
+ if len(saturated_ieqs) == 0:
592
+ continue
593
+ if n == 1:
594
+ yield [i]
595
+ else:
596
+ for c in point_combinations_iterator(n-1, i+1, saturated_ieqs):
597
+ yield [i] + c
598
+
599
+ point_lines = [ line(Linear_Expression(p[0].list(),0)) for p in points ]
600
+ origin = point()
601
+ fibers = set()
602
+ gs = Generator_System()
603
+ for indices in point_combinations_iterator(dim):
604
+ gs.clear()
605
+ gs.insert(origin)
606
+ for i in indices:
607
+ gs.insert(point_lines[i])
608
+ plane = C_Polyhedron(gs)
609
+ if plane.affine_dimension() != dim:
610
+ continue
611
+ plane.intersection_assign(self)
612
+ if (not self.is_full_dimensional()) and (plane.affine_dimension() != dim):
613
+ continue
614
+ try:
615
+ fiber = LatticePolytope_PPL(plane)
616
+ except TypeError: # not a lattice polytope
617
+ continue
618
+ fiber_vertices = tuple(sorted(fiber.vertices()))
619
+ if fiber_vertices not in fibers:
620
+ yield fiber
621
+ fibers.update([fiber_vertices])
622
+
623
+ def pointsets_mod_automorphism(self, pointsets):
624
+ """
625
+ Return ``pointsets`` modulo the automorphisms of ``self``.
626
+
627
+ INPUT:
628
+
629
+ - ``polytopes`` -- tuple/list/iterable of subsets of the
630
+ integral points of ``self``
631
+
632
+ OUTPUT:
633
+
634
+ Representatives of the point sets modulo the
635
+ :meth:`lattice_automorphism_group` of ``self``.
636
+
637
+ EXAMPLES::
638
+
639
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
640
+ sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
641
+ sage: fibers = [f.vertices() for f in square.fibration_generator(1)]
642
+ sage: square.pointsets_mod_automorphism(fibers) # needs sage.graphs sage.groups
643
+ (frozenset({(-1, -1), (1, 1)}), frozenset({(-1, 0), (1, 0)}))
644
+
645
+ sage: cell24 = LatticePolytope_PPL(
646
+ ....: (1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (1,-1,-1,1), (0,0,-1,1),
647
+ ....: (0,-1,0,1), (-1,0,0,1), (1,0,0,-1), (0,1,0,-1), (0,0,1,-1), (-1,1,1,-1),
648
+ ....: (1,-1,-1,0), (0,0,-1,0), (0,-1,0,0), (-1,0,0,0), (1,-1,0,0), (1,0,-1,0),
649
+ ....: (0,1,1,-1), (-1,1,1,0), (-1,1,0,0), (-1,0,1,0), (0,-1,-1,1), (0,0,0,-1))
650
+ sage: fibers = [f.vertices() for f in cell24.fibration_generator(2)]
651
+ sage: cell24.pointsets_mod_automorphism(fibers) # long time # needs sage.graphs sage.groups
652
+ (frozenset({(-1, 0, 0, 0),
653
+ (-1, 0, 0, 1),
654
+ (0, 0, 0, -1),
655
+ (0, 0, 0, 1),
656
+ (1, 0, 0, -1),
657
+ (1, 0, 0, 0)}),
658
+ frozenset({(-1, 0, 0, 0), (-1, 1, 1, 0), (1, -1, -1, 0), (1, 0, 0, 0)}))
659
+ """
660
+ points = set()
661
+ for ps in pointsets:
662
+ points.update(ps)
663
+ points = tuple(sorted(points))
664
+ Aut = self.lattice_automorphism_group(points,
665
+ point_labels=tuple(range(len(points))))
666
+ point_to_index = {p: i for i, p in enumerate(points)}
667
+ indexsets = set(frozenset(point_to_index[p] for p in ps)
668
+ for ps in pointsets)
669
+ orbits = []
670
+ while indexsets:
671
+ idx = indexsets.pop()
672
+ min_orb = idx
673
+ for g in Aut:
674
+ g_idx = frozenset(g(i) for i in idx)
675
+ if sorted(g_idx) < sorted(min_orb):
676
+ min_orb = g_idx
677
+ indexsets.difference_update([g_idx])
678
+ orbits.append(frozenset(points[i] for i in min_orb))
679
+ return tuple(sorted(orbits, key=sorted))
680
+
681
+ @cached_method
682
+ def ambient_space(self):
683
+ r"""
684
+ Return the ambient space.
685
+
686
+ OUTPUT:
687
+
688
+ The free module `\ZZ^d`, where `d` is the ambient space
689
+ dimension.
690
+
691
+ EXAMPLES::
692
+
693
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
694
+ sage: point = LatticePolytope_PPL((1,2,3))
695
+ sage: point.ambient_space()
696
+ Ambient free module of rank 3 over the principal ideal domain Integer Ring
697
+ """
698
+ from sage.modules.free_module import FreeModule
699
+ return FreeModule(ZZ, self.space_dimension())
700
+
701
+ def contains(self, point_coordinates):
702
+ r"""
703
+ Test whether point is contained in the polytope.
704
+
705
+ INPUT:
706
+
707
+ - ``point_coordinates`` -- list/tuple/iterable of rational
708
+ numbers; the coordinates of the point
709
+
710
+ OUTPUT: boolean
711
+
712
+ EXAMPLES::
713
+
714
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
715
+ sage: line = LatticePolytope_PPL((1,2,3), (-1,-2,-3))
716
+ sage: line.contains([0,0,0])
717
+ True
718
+ sage: line.contains([1,0,0])
719
+ False
720
+ """
721
+ p = C_Polyhedron(point(Linear_Expression(list(point_coordinates), 1)))
722
+ is_included = Poly_Con_Relation.is_included()
723
+ for c in self.constraints():
724
+ if not p.relation_with(c).implies(is_included):
725
+ return False
726
+ return True
727
+
728
+ @cached_method
729
+ def contains_origin(self):
730
+ """
731
+ Test whether the polytope contains the origin.
732
+
733
+ OUTPUT: boolean
734
+
735
+ EXAMPLES::
736
+
737
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
738
+ sage: LatticePolytope_PPL((1,2,3), (-1,-2,-3)).contains_origin()
739
+ True
740
+ sage: LatticePolytope_PPL((1,2,5), (-1,-2,-3)).contains_origin()
741
+ False
742
+ """
743
+ return self.contains(self.ambient_space().zero())
744
+
745
+ @cached_method
746
+ def affine_space(self):
747
+ r"""
748
+ Return the affine space spanned by the polytope.
749
+
750
+ OUTPUT:
751
+
752
+ The free module `\ZZ^n`, where `n` is the dimension of the
753
+ affine space spanned by the points of the polytope.
754
+
755
+ EXAMPLES::
756
+
757
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
758
+ sage: point = LatticePolytope_PPL((1,2,3))
759
+ sage: point.affine_space()
760
+ Free module of degree 3 and rank 0 over Integer Ring
761
+ Echelon basis matrix:
762
+ []
763
+ sage: line = LatticePolytope_PPL((1,1,1), (1,2,3))
764
+ sage: line.affine_space()
765
+ Free module of degree 3 and rank 1 over Integer Ring
766
+ Echelon basis matrix:
767
+ [0 1 2]
768
+ """
769
+ vertices = self.vertices()
770
+ if not self.contains_origin():
771
+ v0 = vertices[0]
772
+ vertices = [v-v0 for v in vertices]
773
+ return self.ambient_space().span(vertices).saturation()
774
+
775
+ def affine_lattice_polytope(self):
776
+ """
777
+ Return the lattice polytope restricted to
778
+ :meth:`affine_space`.
779
+
780
+ OUTPUT:
781
+
782
+ A new, full-dimensional lattice polytope.
783
+
784
+ EXAMPLES::
785
+
786
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
787
+ sage: poly_4d = LatticePolytope_PPL((-9,-6,0,0), (0,1,0,0), (1,0,0,0)); poly_4d
788
+ A 2-dimensional lattice polytope in ZZ^4 with 3 vertices
789
+ sage: poly_4d.space_dimension()
790
+ 4
791
+ sage: poly_2d = poly_4d.affine_lattice_polytope(); poly_2d
792
+ A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
793
+ sage: poly_2d.space_dimension()
794
+ 2
795
+ """
796
+ V = self.affine_space()
797
+ if self.contains_origin():
798
+ vertices = [ V.coordinates(v) for v in self.vertices() ]
799
+ else:
800
+ v0 = vertices[0]
801
+ vertices = [ V.coordinates(v-v0) for v in self.vertices() ]
802
+ return LatticePolytope_PPL(*vertices)
803
+
804
+ def base_projection(self, fiber):
805
+ """
806
+ The projection that maps the sub-polytope ``fiber`` to a
807
+ single point.
808
+
809
+ OUTPUT:
810
+
811
+ The quotient module of the ambient space modulo the
812
+ :meth:`affine_space` spanned by the fiber.
813
+
814
+ EXAMPLES::
815
+
816
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
817
+ sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
818
+ ....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
819
+ sage: fiber = next(poly.fibration_generator(2))
820
+ sage: poly.base_projection(fiber) # needs sage.libs.pari
821
+ Finitely generated module V/W over Integer Ring with invariants (0, 0)
822
+ """
823
+ return self.ambient_space().quotient(fiber.affine_space())
824
+
825
+ def base_projection_matrix(self, fiber):
826
+ """
827
+ The projection that maps the sub-polytope ``fiber`` to a
828
+ single point.
829
+
830
+ OUTPUT:
831
+
832
+ An integer matrix that represents the projection to the
833
+ base.
834
+
835
+ .. SEEALSO::
836
+
837
+ The :meth:`base_projection` yields equivalent information,
838
+ and is easier to use. However, just returning the matrix
839
+ has lower overhead.
840
+
841
+ EXAMPLES::
842
+
843
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
844
+ sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
845
+ ....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
846
+ sage: fiber = next(poly.fibration_generator(2))
847
+ sage: poly.base_projection_matrix(fiber)
848
+ [ 0 0 -1 0]
849
+ [ 0 0 0 -1]
850
+
851
+ Note that the basis choice in :meth:`base_projection` for the
852
+ quotient is usually different::
853
+
854
+ sage: proj = poly.base_projection(fiber)
855
+ sage: proj_matrix = poly.base_projection_matrix(fiber)
856
+ sage: [proj(p) for p in poly.integral_points()] # needs sage.libs.pari
857
+ [(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)]
858
+ sage: [proj_matrix*p for p in poly.integral_points()]
859
+ [(1, 1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, -1), (-1, 0)]
860
+ """
861
+ return matrix(ZZ, fiber.vertices()).right_kernel_matrix()
862
+
863
+ def base_rays(self, fiber, points):
864
+ r"""
865
+ Return the primitive lattice vectors that generate the
866
+ direction given by the base projection of points.
867
+
868
+ INPUT:
869
+
870
+ - ``fiber`` -- a sub-lattice polytope defining the
871
+ :meth:`base_projection`
872
+
873
+ - ``points`` -- the points to project to the base
874
+
875
+ OUTPUT: a tuple of primitive `\ZZ`-vectors
876
+
877
+ EXAMPLES::
878
+
879
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
880
+ sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
881
+ ....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
882
+ sage: fiber = next(poly.fibration_generator(2))
883
+ sage: poly.base_rays(fiber, poly.integral_points_not_interior_to_facets()) # needs sage.libs.pari
884
+ ((-1, -1), (0, 1), (1, 0))
885
+
886
+ sage: p = LatticePolytope_PPL((1,0), (1,2), (-1,0))
887
+ sage: f = LatticePolytope_PPL((1,0), (-1,0))
888
+ sage: p.base_rays(f, p.integral_points()) # needs sage.libs.pari
889
+ ((1),)
890
+ """
891
+ quo = self.base_projection(fiber)
892
+ vertices = set()
893
+ for p in points:
894
+ v = quo(p).vector()
895
+ if v.is_zero():
896
+ continue
897
+ d = GCD_list(v.list())
898
+ if d > 1:
899
+ v = v.__copy__()
900
+ for i in range(v.degree()):
901
+ v[i] /= d
902
+ v.set_immutable()
903
+ vertices.add(v)
904
+ return tuple(sorted(vertices))
905
+
906
+ @cached_method
907
+ def has_IP_property(self) -> bool:
908
+ """
909
+ Whether the lattice polytope has the IP property.
910
+
911
+ That is, the polytope is full-dimensional and the origin is a
912
+ interior point not on the boundary.
913
+
914
+ OUTPUT: boolean
915
+
916
+ EXAMPLES::
917
+
918
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
919
+ sage: LatticePolytope_PPL((-1,-1), (0,1), (1,0)).has_IP_property()
920
+ True
921
+ sage: LatticePolytope_PPL((-1,-1), (1,1)).has_IP_property()
922
+ False
923
+ """
924
+ origin = C_Polyhedron(point(0*Variable(self.space_dimension())))
925
+ is_included = Poly_Con_Relation.is_included()
926
+ saturates = Poly_Con_Relation.saturates()
927
+ for c in self.constraints():
928
+ rel = origin.relation_with(c)
929
+ if (not rel.implies(is_included)) or rel.implies(saturates):
930
+ return False
931
+ return True
932
+
933
+ @cached_method
934
+ def restricted_automorphism_group(self, vertex_labels=None):
935
+ r"""
936
+ Return the restricted automorphism group.
937
+
938
+ First, let the linear automorphism group be the subgroup of
939
+ the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`
940
+ preserving the `d`-dimensional polyhedron. The Euclidean group
941
+ acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
942
+ ambient space. The restricted automorphism group is the
943
+ subgroup of the linear automorphism group generated by
944
+ permutations of vertices. If the polytope is full-dimensional,
945
+ it is equal to the full (unrestricted) automorphism group.
946
+
947
+ INPUT:
948
+
949
+ - ``vertex_labels`` -- tuple or ``None`` (default). The
950
+ labels of the vertices that will be used in the output
951
+ permutation group. By default, the vertices are used
952
+ themselves.
953
+
954
+ OUTPUT:
955
+
956
+ A
957
+ :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
958
+ acting on the vertices (or the ``vertex_labels``, if
959
+ specified).
960
+
961
+ REFERENCES:
962
+
963
+ [BSS2009]_
964
+
965
+ EXAMPLES::
966
+
967
+ sage: # needs sage.graphs sage.groups
968
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
969
+ sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
970
+ sage: G1234 = Z3square.restricted_automorphism_group(
971
+ ....: vertex_labels=(1,2,3,4))
972
+ sage: G1234 == PermutationGroup([[(2,3)], [(1,2),(3,4)]])
973
+ True
974
+ sage: G = Z3square.restricted_automorphism_group()
975
+ sage: G == PermutationGroup([[((1,2),(2,1))], [((0,0),(1,2)),
976
+ ....: ((2,1),(3,3))], [((0,0),(3,3))]])
977
+ True
978
+ sage: set(G.domain()) == set(Z3square.vertices())
979
+ True
980
+ sage: (set(tuple(x) for x in G.orbit(Z3square.vertices()[0]))
981
+ ....: == set([(0, 0), (1, 2), (3, 3), (2, 1)]))
982
+ True
983
+ sage: cell24 = LatticePolytope_PPL(
984
+ ....: (1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (1,-1,-1,1), (0,0,-1,1),
985
+ ....: (0,-1,0,1), (-1,0,0,1), (1,0,0,-1), (0,1,0,-1), (0,0,1,-1), (-1,1,1,-1),
986
+ ....: (1,-1,-1,0), (0,0,-1,0), (0,-1,0,0), (-1,0,0,0), (1,-1,0,0), (1,0,-1,0),
987
+ ....: (0,1,1,-1), (-1,1,1,0), (-1,1,0,0), (-1,0,1,0), (0,-1,-1,1), (0,0,0,-1))
988
+ sage: cell24.restricted_automorphism_group().cardinality()
989
+ 1152
990
+ """
991
+ if not self.is_full_dimensional():
992
+ return self.affine_lattice_polytope().\
993
+ restricted_automorphism_group(vertex_labels=vertex_labels)
994
+ if vertex_labels is None:
995
+ vertex_labels = self.vertices()
996
+ from sage.graphs.graph import Graph
997
+ # good coordinates for the vertices
998
+ v_list = []
999
+ for v in self.minimized_generators():
1000
+ assert v.divisor() == 1
1001
+ v_coords = (1,) + tuple(Integer(mpz) for mpz in v.coefficients())
1002
+ v_list.append(vector(v_coords))
1003
+
1004
+ # Finally, construct the graph
1005
+ Qinv = sum( v.column() * v.row() for v in v_list ).inverse()
1006
+ G = Graph()
1007
+ for i in range(len(v_list)):
1008
+ for j in range(i+1,len(v_list)):
1009
+ v_i = v_list[i]
1010
+ v_j = v_list[j]
1011
+ G.add_edge(vertex_labels[i], vertex_labels[j], v_i * Qinv * v_j)
1012
+ return G.automorphism_group(edge_labels=True)
1013
+
1014
+ @cached_method
1015
+ def lattice_automorphism_group(self, points=None, point_labels=None):
1016
+ """
1017
+ The integral subgroup of the restricted automorphism group.
1018
+
1019
+ INPUT:
1020
+
1021
+ - ``points`` -- tuple of coordinate vectors or ``None``
1022
+ (default). If specified, the points must form complete
1023
+ orbits under the lattice automorphism group. If ``None`` all
1024
+ vertices are used.
1025
+
1026
+ - ``point_labels`` -- tuple of labels for the ``points`` or
1027
+ ``None`` (default). These will be used as labels for the do
1028
+ permutation group. If ``None``, the ``points`` will be used
1029
+ themselves.
1030
+
1031
+ OUTPUT:
1032
+
1033
+ The integral subgroup of the restricted automorphism group
1034
+ acting on the given ``points``, or all vertices if not
1035
+ specified.
1036
+
1037
+ EXAMPLES::
1038
+
1039
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
1040
+ sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
1041
+ sage: Z3square.lattice_automorphism_group() # needs sage.graphs sage.groups
1042
+ Permutation Group with generators [(), ((1,2),(2,1)),
1043
+ ((0,0),(3,3)), ((0,0),(3,3))((1,2),(2,1))]
1044
+
1045
+ sage: G1 = Z3square.lattice_automorphism_group(point_labels=(1,2,3,4)) # needs sage.graphs sage.groups
1046
+ sage: G1 # needs sage.graphs sage.groups
1047
+ Permutation Group with generators [(), (2,3), (1,4), (1,4)(2,3)]
1048
+ sage: G1.cardinality() # needs sage.graphs sage.groups
1049
+ 4
1050
+
1051
+ sage: G2 = Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4)) # needs sage.graphs sage.groups
1052
+ sage: G2 == PermutationGroup([[(2,3)], [(1,2),(3,4)], [(1,4)]]) # needs sage.graphs sage.groups
1053
+ True
1054
+ sage: G2.cardinality() # needs sage.graphs sage.groups
1055
+ 8
1056
+
1057
+ sage: points = Z3square.integral_points(); points
1058
+ ((0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 3))
1059
+ sage: Z3square.lattice_automorphism_group(points, # needs sage.graphs sage.groups
1060
+ ....: point_labels=(1,2,3,4,5,6))
1061
+ Permutation Group with generators [(), (3,4), (1,6)(2,5), (1,6)(2,5)(3,4)]
1062
+
1063
+ Point labels also work for lattice polytopes that are not
1064
+ full-dimensional, see :issue:`16669`::
1065
+
1066
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
1067
+ sage: lp = LatticePolytope_PPL((1,0,0), (0,1,0), (-1,-1,0))
1068
+ sage: lp.lattice_automorphism_group(point_labels=(0,1,2)) # needs sage.graphs sage.groups
1069
+ Permutation Group with generators [(), (1,2), (0,1), (0,1,2), (0,2,1), (0,2)]
1070
+ """
1071
+ if not self.is_full_dimensional():
1072
+ return self.affine_lattice_polytope().lattice_automorphism_group(
1073
+ point_labels=point_labels)
1074
+
1075
+ if points is None:
1076
+ points = self.vertices()
1077
+ if point_labels is None:
1078
+ point_labels = tuple(points)
1079
+ points = [ vector(ZZ, [1]+v.list()) for v in points ]
1080
+ for p in points:
1081
+ p.set_immutable()
1082
+
1083
+ vertices = [ vector(ZZ, [1]+v.list()) for v in self.vertices() ]
1084
+ pivots = matrix(ZZ, vertices).pivot_rows()
1085
+ basis = matrix(ZZ, [ vertices[i] for i in pivots ])
1086
+ Mat_ZZ = basis.parent()
1087
+ basis_inverse = basis.inverse()
1088
+
1089
+ from sage.groups.perm_gps.permgroup import PermutationGroup
1090
+ lattice_gens = []
1091
+ G = self.restricted_automorphism_group(
1092
+ vertex_labels=tuple(range(len(vertices))))
1093
+ for g in G:
1094
+ image = matrix(ZZ, [ vertices[g(i)] for i in pivots ])
1095
+ m = basis_inverse*image
1096
+ if m not in Mat_ZZ:
1097
+ continue
1098
+ perm_list = [ point_labels[points.index(p*m)]
1099
+ for p in points ]
1100
+ lattice_gens.append(perm_list)
1101
+ return PermutationGroup(lattice_gens, domain=point_labels)
1102
+
1103
+ def sub_polytope_generator(self):
1104
+ """
1105
+ Generate the maximal lattice sub-polytopes.
1106
+
1107
+ OUTPUT:
1108
+
1109
+ A generator yielding the maximal (with respect to inclusion)
1110
+ lattice sub polytopes. That is, each can be gotten as the
1111
+ convex hull of the integral points of ``self`` with one vertex
1112
+ removed.
1113
+
1114
+ EXAMPLES::
1115
+
1116
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
1117
+ sage: P = LatticePolytope_PPL((1,0,0), (0,1,0), (0,0,1), (-1,-1,-1))
1118
+ sage: for p in P.sub_polytope_generator():
1119
+ ....: print(p.vertices())
1120
+ ((0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0))
1121
+ ((-1, -1, -1), (0, 0, 0), (0, 1, 0), (1, 0, 0))
1122
+ ((-1, -1, -1), (0, 0, 0), (0, 0, 1), (1, 0, 0))
1123
+ ((-1, -1, -1), (0, 0, 0), (0, 0, 1), (0, 1, 0))
1124
+ """
1125
+ pointset = set(self.integral_points())
1126
+ for v in self.vertices():
1127
+ sub = list(pointset.difference([v]))
1128
+ yield LatticePolytope_PPL(*sub)
1129
+
1130
+ @cached_method
1131
+ def _find_isomorphism_to_subreflexive_polytope(self):
1132
+ """
1133
+ Find an isomorphism to a sub-polytope of a maximal reflexive
1134
+ polytope.
1135
+
1136
+ OUTPUT:
1137
+
1138
+ A tuple consisting of the ambient reflexive polytope, the
1139
+ subpolytope, and the embedding of ``self`` into the ambient
1140
+ polytope.
1141
+
1142
+ EXAMPLES::
1143
+
1144
+ sage: # needs sage.libs.pari
1145
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
1146
+ sage: polygon = LatticePolytope_PPL((0,0,2,1), (0,1,2,0), (2,3,0,0), (2,0,0,3))
1147
+ sage: polygon._find_isomorphism_to_subreflexive_polytope()
1148
+ (A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,
1149
+ A 2-dimensional lattice polytope in ZZ^2 with 4 vertices,
1150
+ The map A*x+b with
1151
+ A=
1152
+ [ 1 1]
1153
+ [ 0 1]
1154
+ [-1 -1]
1155
+ [ 1 0]
1156
+ b = (-1, 0, 3, 0))
1157
+ sage: ambient, sub, embedding = _
1158
+ sage: ambient.vertices()
1159
+ ((0, 0), (0, 3), (3, 0))
1160
+ sage: sub.vertices()
1161
+ ((0, 1), (3, 0), (0, 3), (1, 0))
1162
+ """
1163
+ from .ppl_lattice_polygon import sub_reflexive_polygons
1164
+ from sage.geometry.polyhedron.lattice_euclidean_group_element import \
1165
+ LatticePolytopesNotIsomorphicError, LatticePolytopeNoEmbeddingError
1166
+ for p, ambient in sub_reflexive_polygons():
1167
+ try:
1168
+ return (ambient, p, p.find_isomorphism(self))
1169
+ except LatticePolytopesNotIsomorphicError:
1170
+ pass
1171
+ raise LatticePolytopeNoEmbeddingError('not a sub-polytope of a reflexive polygon')
1172
+
1173
+ def embed_in_reflexive_polytope(self, output='hom'):
1174
+ """
1175
+ Find an embedding as a sub-polytope of a maximal reflexive
1176
+ polytope.
1177
+
1178
+ INPUT:
1179
+
1180
+ - ``hom`` -- string. One of ``'hom'`` (default),
1181
+ ``'polytope'``, or ``points``. How the embedding is
1182
+ returned. See the output section for details.
1183
+
1184
+ OUTPUT:
1185
+
1186
+ An embedding into a reflexive polytope. Depending on the
1187
+ ``output`` option slightly different data is returned.
1188
+
1189
+ - If ``output='hom'``, a map from a reflexive polytope onto
1190
+ ``self`` is returned.
1191
+
1192
+ - If ``output='polytope'``, a reflexive polytope that contains
1193
+ ``self`` (up to a lattice linear transformation) is
1194
+ returned. That is, the domain of the ``output='hom'`` map is
1195
+ returned. If the affine span of ``self`` is less or equal
1196
+ 2-dimensional, the output is one of the following three
1197
+ possibilities:
1198
+
1199
+ :func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P2_polytope`,
1200
+ :func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P1xP1_polytope`,
1201
+ or
1202
+ :func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P2_112_polytope`.
1203
+
1204
+ - If ``output='points'``, a dictionary containing the integral
1205
+ points of ``self`` as keys and the corresponding integral
1206
+ point of the reflexive polytope as value.
1207
+
1208
+ If there is no such embedding, a
1209
+ :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopeNoEmbeddingError`
1210
+ is raised. Even if it exists, the ambient reflexive polytope
1211
+ is usually not uniquely determined and a random but fixed
1212
+ choice will be returned.
1213
+
1214
+ EXAMPLES::
1215
+
1216
+ sage: # needs sage.libs.pari
1217
+ sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
1218
+ sage: polygon = LatticePolytope_PPL((0,0,2,1), (0,1,2,0), (2,3,0,0), (2,0,0,3))
1219
+ sage: polygon.embed_in_reflexive_polytope()
1220
+ The map A*x+b with
1221
+ A=
1222
+ [ 1 1]
1223
+ [ 0 1]
1224
+ [-1 -1]
1225
+ [ 1 0]
1226
+ b = (-1, 0, 3, 0)
1227
+ sage: polygon.embed_in_reflexive_polytope('polytope')
1228
+ A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
1229
+ sage: polygon.embed_in_reflexive_polytope('points')
1230
+ {(0, 0, 2, 1): (1, 0),
1231
+ (0, 1, 2, 0): (0, 1),
1232
+ (1, 0, 1, 2): (2, 0),
1233
+ (1, 1, 1, 1): (1, 1),
1234
+ (1, 2, 1, 0): (0, 2),
1235
+ (2, 0, 0, 3): (3, 0),
1236
+ (2, 1, 0, 2): (2, 1),
1237
+ (2, 2, 0, 1): (1, 2),
1238
+ (2, 3, 0, 0): (0, 3)}
1239
+ sage: LatticePolytope_PPL((0,0), (4,0), (0,4)).embed_in_reflexive_polytope()
1240
+ Traceback (most recent call last):
1241
+ ...
1242
+ LatticePolytopeNoEmbeddingError: not a sub-polytope of a reflexive polygon
1243
+ """
1244
+ if self.affine_dimension() > 2:
1245
+ raise NotImplementedError('can only embed in reflexive polygons')
1246
+ ambient, subreflexive, hom = self._find_isomorphism_to_subreflexive_polytope()
1247
+ if output == 'hom':
1248
+ return hom
1249
+ elif output == 'polytope':
1250
+ return ambient
1251
+ elif output == 'points':
1252
+ points = dict()
1253
+ for p in subreflexive.integral_points():
1254
+ points[ tuple(hom(p)) ] = p
1255
+ return points
1256
+ else:
1257
+ raise ValueError('output='+str(output)+' is not valid.')