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,1810 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ # sage.doctest: needs sage.combinat
3
+ r"""
4
+ Generating Function of Polyhedron's Integral Points
5
+
6
+ This module provides :func:`generating_function_of_integral_points` which
7
+ computes the generating function of the integral points of a polyhedron.
8
+
9
+ The main function is accessible via
10
+ :meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points`
11
+ as well.
12
+
13
+ Various
14
+ =======
15
+
16
+ AUTHORS:
17
+
18
+ - Daniel Krenn (2016, 2021)
19
+
20
+ ACKNOWLEDGEMENT:
21
+
22
+ - Daniel Krenn is supported by the Austrian Science Fund (FWF): P 24644-N26
23
+ and by the Austrian Science Fund (FWF): P 28466-N35.
24
+
25
+
26
+ Functions
27
+ =========
28
+ """
29
+
30
+ # *****************************************************************************
31
+ # Copyright (C) 2016, 2021 Daniel Krenn <dev@danielkrenn.at>
32
+ #
33
+ # This program is free software: you can redistribute it and/or modify
34
+ # it under the terms of the GNU General Public License as published by
35
+ # the Free Software Foundation, either version 2 of the License, or
36
+ # (at your option) any later version.
37
+ # http://www.gnu.org/licenses/
38
+ # *****************************************************************************
39
+
40
+ Hrepresentation_str_options = {'prefix': 'b', 'style': 'positive'}
41
+
42
+
43
+ def generating_function_of_integral_points(polyhedron, split=False,
44
+ result_as_tuple=None,
45
+ name=None, names=None,
46
+ algorithm="omega",
47
+ **kwds):
48
+ r"""
49
+ Return the multivariate generating function of the
50
+ integral points of the ``polyhedron``.
51
+
52
+ To be precise, this returns
53
+
54
+ .. MATH::
55
+
56
+ \sum_{(r_0,\dots,r_{d-1}) \in \mathit{polyhedron}\cap \ZZ^d}
57
+ y_0^{r_0} \dots y_{d-1}^{r_{d-1}}.
58
+
59
+ INPUT:
60
+
61
+ - ``polyhedron`` -- an instance of
62
+ :class:`~sage.geometry.polyhedron.base.Polyhedron_base`
63
+ (see also :mod:`sage.geometry.polyhedron.constructor`)
64
+
65
+ - ``split`` -- (default: ``False``) a boolean or list
66
+
67
+ - ``split=False`` computes the generating function directly,
68
+ without any splitting.
69
+
70
+ - When ``split`` is a list of disjoint polyhedra, then
71
+ for each of these polyhedra, ``polyhedron`` is intersected with it,
72
+ its generating function computed and all these generating functions
73
+ are summed up.
74
+
75
+ - ``split=True`` splits into `d!` disjoint polyhedra.
76
+
77
+ - ``result_as_tuple`` -- (default: ``None``) a boolean or ``None``
78
+
79
+ This specifies whether the output is a (partial) factorization
80
+ (``result_as_tuple=False``) or a sum of such (partial)
81
+ factorizations (``result_as_tuple=True``). By default
82
+ (``result_as_tuple=None``), this is automatically determined.
83
+ If the output is a sum, it is represented as a tuple whose
84
+ entries are the summands.
85
+
86
+ - ``indices`` -- (default: ``None``) a list or tuple
87
+
88
+ If this is ``None``, this is automatically determined.
89
+
90
+ - ``name`` -- (default: ``'y'``) a string
91
+
92
+ The variable names of the Laurent polynomial ring of the output
93
+ are this string followed by an integer.
94
+
95
+ - ``names`` -- list or tuple of names (strings), or a comma separated string
96
+
97
+ ``name`` is extracted from ``names``, therefore ``names`` has to contain
98
+ exactly one variable name, and ``name`` and``names`` cannot be specified
99
+ both at the same time.
100
+
101
+ - ``algorithm`` -- (default:``"omega"``) The algorithm which is used
102
+ to compute the multivariate generating function. Options are:
103
+
104
+ * ``"omega"`` -- Run the algorithm via
105
+ :func:`MacMahon's Omega operator <sage.rings.polynomial.omega.MacMahonOmega>`.
106
+
107
+ * ``"latte"`` -- Run the corresponding algorithm of ``LattE``.
108
+ This algorithm requires LattE (Lattice point Enumeration) Integrale.
109
+ To install LattE Integrale, type :code:`sage -i latte_int` in the terminal.
110
+
111
+ * ``"naive"`` -- Only works for bounded polyhedra. Collect the monomials
112
+ corresponding to each integral point.
113
+
114
+ - ``Factorization_sort`` (default: ``False``) and
115
+ ``Factorization_simplify`` (default: ``True``) -- booleans
116
+
117
+ These are passed on to
118
+ :class:`sage.structure.factorization.Factorization` when creating
119
+ the result.
120
+
121
+ - ``sort_factors`` -- (default: ``False``) a boolean
122
+
123
+ If set, then
124
+ the factors of the output are sorted such that the numerator is
125
+ first and only then all factors of the denominator. It is ensured
126
+ that the sorting is always the same; use this for doctesting.
127
+
128
+ OUTPUT:
129
+
130
+ The generating function as a (partial)
131
+ :class:`~sage.structure.factorization.Factorization`
132
+ of the result whose factors are Laurent polynomials
133
+
134
+ The result might be a tuple of such factorizations
135
+ (depending on the parameter ``result_as_tuple``) as well.
136
+
137
+ .. NOTE::
138
+
139
+ At the moment, ``algorithm="omega"`` only handles polyhedra with
140
+ nonnegative coordinates (i.e., polyhedra in the nonnegative orthant).
141
+
142
+ ``algorithm="latte"`` option can handle general polyhedra,
143
+ but has no loggings for the decompositions and
144
+ only supports a tuple output unless the tuple has only one element.
145
+
146
+ EXAMPLES::
147
+
148
+ sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points
149
+
150
+ ::
151
+
152
+ sage: P2 = (Polyhedron(ieqs=[(0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, -1)]),
153
+ ....: Polyhedron(ieqs=[(0, -1, 0, 1), (0, 1, 0, 0), (0, 0, 1, 0)]))
154
+ sage: generating_function_of_integral_points(P2[0], sort_factors=True)
155
+ 1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
156
+ sage: generating_function_of_integral_points(P2[1], sort_factors=True)
157
+ 1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1
158
+ sage: (P2[0] & P2[1]).Hrepresentation()
159
+ (An equation (1, 0, -1) x + 0 == 0,
160
+ An inequality (1, 0, 0) x + 0 >= 0,
161
+ An inequality (0, 1, 0) x + 0 >= 0)
162
+ sage: generating_function_of_integral_points(P2[0] & P2[1], sort_factors=True)
163
+ 1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
164
+
165
+ sage: # optional - latte_int
166
+ sage: generating_function_of_integral_points(P2[0], sort_factors=True,
167
+ ....: algorithm="latte")
168
+ 1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
169
+ sage: generating_function_of_integral_points(P2[1], sort_factors=True,
170
+ ....: algorithm="latte")
171
+ 1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1
172
+ sage: generating_function_of_integral_points(P2[0] & P2[1], sort_factors=True,
173
+ ....: algorithm="latte")
174
+ 1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
175
+
176
+ sage: generating_function_of_integral_points(P2[0], sort_factors=True,
177
+ ....: algorithm="naive")
178
+ Traceback (most recent call last):
179
+ ...
180
+ ValueError: Polyhedron must be bounded for the naive algorithm
181
+
182
+ ::
183
+
184
+ sage: P3 = (
185
+ ....: Polyhedron(
186
+ ....: ieqs=[(0, 0, 0, 0, 1), (0, 0, 0, 1, 0),
187
+ ....: (0, 0, 1, 0, -1), (-1, 1, 0, -1, -1)]),
188
+ ....: Polyhedron(
189
+ ....: ieqs=[(0, 0, -1, 0, 1), (0, 1, 0, 0, -1),
190
+ ....: (0, 0, 0, 1, 0), (0, 0, 1, 0, 0), (-1, 1, -1, -1, 0)]),
191
+ ....: Polyhedron(
192
+ ....: ieqs=[(1, -1, 0, 1, 1), (1, -1, 1, 1, 0),
193
+ ....: (0, 0, 0, 0, 1), (0, 0, 0, 1, 0), (0, 0, 1, 0, 0),
194
+ ....: (1, 0, 1, 1, -1), (0, 1, 0, 0, 0), (1, 1, 1, 0, -1)]),
195
+ ....: Polyhedron(
196
+ ....: ieqs=[(0, 1, 0, -1, 0), (0, -1, 0, 0, 1),
197
+ ....: (-1, 0, -1, -1, 1), (0, 0, 1, 0, 0), (0, 0, 0, 1, 0)]),
198
+ ....: Polyhedron(
199
+ ....: ieqs=[(0, 1, 0, 0, 0), (0, 0, 1, 0, 0),
200
+ ....: (-1, -1, -1, 0, 1), (0, -1, 0, 1, 0)]))
201
+ sage: def intersect(I):
202
+ ....: I = iter(I)
203
+ ....: result = next(I)
204
+ ....: for i in I:
205
+ ....: result &= i
206
+ ....: return result
207
+ sage: for J in subsets(range(len(P3))):
208
+ ....: if not J:
209
+ ....: continue
210
+ ....: P = intersect([P3[j] for j in J])
211
+ ....: print('{}: {}'.format(J, P.Hrepresentation()))
212
+ ....: print(generating_function_of_integral_points(P, sort_factors=True))
213
+ [0]: (An inequality (0, 0, 0, 1) x + 0 >= 0,
214
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
215
+ An inequality (0, 1, 0, -1) x + 0 >= 0,
216
+ An inequality (1, 0, -1, -1) x - 1 >= 0)
217
+ y0 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
218
+ [1]: (An inequality (0, -1, 0, 1) x + 0 >= 0,
219
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
220
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
221
+ An inequality (1, -1, -1, 0) x - 1 >= 0,
222
+ An inequality (1, 0, 0, -1) x + 0 >= 0)
223
+ (-y0^2*y2*y3 - y0^2*y3 + y0*y3 + y0) *
224
+ (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y3 + 1)^-1 *
225
+ (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
226
+ [0, 1]: (An equation (0, 1, 0, -1) x + 0 == 0,
227
+ An inequality (1, -1, -1, 0) x - 1 >= 0,
228
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
229
+ An inequality (0, 0, 1, 0) x + 0 >= 0)
230
+ y0 * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
231
+ [2]: (An inequality (-1, 0, 1, 1) x + 1 >= 0,
232
+ An inequality (-1, 1, 1, 0) x + 1 >= 0,
233
+ An inequality (0, 0, 0, 1) x + 0 >= 0,
234
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
235
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
236
+ An inequality (0, 1, 1, -1) x + 1 >= 0,
237
+ An inequality (1, 0, 0, 0) x + 0 >= 0,
238
+ An inequality (1, 1, 0, -1) x + 1 >= 0)
239
+ (y0^2*y1*y2*y3^2 + y0^2*y2^2*y3 + y0*y1^2*y3^2 - y0^2*y2*y3 +
240
+ y0*y1*y2*y3 - y0*y1*y3^2 - 2*y0*y1*y3 - 2*y0*y2*y3 - y0*y2 +
241
+ y0*y3 - y1*y3 + y0 + y3 + 1) *
242
+ (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1 *
243
+ (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
244
+ [0, 2]: (An equation (1, 0, -1, -1) x - 1 == 0,
245
+ An inequality (-1, 1, 1, 0) x + 1 >= 0,
246
+ An inequality (1, 0, -1, 0) x - 1 >= 0,
247
+ An inequality (0, 0, 1, 0) x + 0 >= 0)
248
+ y0 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
249
+ [1, 2]: (An equation (1, -1, -1, 0) x - 1 == 0,
250
+ An inequality (0, -1, 0, 1) x + 0 >= 0,
251
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
252
+ An inequality (1, 0, 0, -1) x + 0 >= 0,
253
+ An inequality (1, -1, 0, 0) x - 1 >= 0)
254
+ (-y0^2*y2*y3 + y0*y3 + y0) *
255
+ (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
256
+ [0, 1, 2]: (An equation (0, 1, 0, -1) x + 0 == 0,
257
+ An equation (1, -1, -1, 0) x - 1 == 0,
258
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
259
+ An inequality (1, -1, 0, 0) x - 1 >= 0)
260
+ y0 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
261
+ [3]: (An inequality (-1, 0, 0, 1) x + 0 >= 0,
262
+ An inequality (0, -1, -1, 1) x - 1 >= 0,
263
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
264
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
265
+ An inequality (1, 0, -1, 0) x + 0 >= 0)
266
+ (-y0*y1*y3^2 - y0*y3^2 + y0*y3 + y3) *
267
+ (-y3 + 1)^-1 * (-y0*y3 + 1)^-1 *
268
+ (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
269
+ [0, 3]: (An equation -1 == 0,)
270
+ 0
271
+ [1, 3]: (An equation (1, 0, 0, -1) x + 0 == 0,
272
+ An inequality (1, -1, -1, 0) x - 1 >= 0,
273
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
274
+ An inequality (0, 0, 1, 0) x + 0 >= 0)
275
+ y0*y3 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
276
+ [0, 1, 3]: (An equation -1 == 0,)
277
+ 0
278
+ [2, 3]: (An equation (0, 1, 1, -1) x + 1 == 0,
279
+ An inequality (1, 0, -1, 0) x + 0 >= 0,
280
+ An inequality (-1, 1, 1, 0) x + 1 >= 0,
281
+ An inequality (0, 0, 1, 0) x + 0 >= 0,
282
+ An inequality (0, 1, 0, 0) x + 0 >= 0)
283
+ (-y0*y1*y3^2 + y0*y3 + y3) *
284
+ (-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
285
+ [0, 2, 3]: (An equation -1 == 0,)
286
+ 0
287
+ [1, 2, 3]: (An equation (1, 0, 0, -1) x + 0 == 0,
288
+ An equation (1, -1, -1, 0) x - 1 == 0,
289
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
290
+ An inequality (1, -1, 0, 0) x - 1 >= 0)
291
+ y0*y3 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
292
+ [0, 1, 2, 3]: (An equation -1 == 0,)
293
+ 0
294
+ [4]: (An inequality (-1, -1, 0, 1) x - 1 >= 0,
295
+ An inequality (-1, 0, 1, 0) x + 0 >= 0,
296
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
297
+ An inequality (1, 0, 0, 0) x + 0 >= 0)
298
+ y3 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
299
+ [0, 4]: (An equation -1 == 0,)
300
+ 0
301
+ [1, 4]: (An equation -1 == 0,)
302
+ 0
303
+ [0, 1, 4]: (An equation -1 == 0,)
304
+ 0
305
+ [2, 4]: (An equation (1, 1, 0, -1) x + 1 == 0,
306
+ An inequality (-1, 0, 1, 0) x + 0 >= 0,
307
+ An inequality (1, 0, 0, 0) x + 0 >= 0,
308
+ An inequality (0, 1, 0, 0) x + 0 >= 0)
309
+ y3 * (-y2 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
310
+ [0, 2, 4]: (An equation -1 == 0,)
311
+ 0
312
+ [1, 2, 4]: (An equation -1 == 0,)
313
+ 0
314
+ [0, 1, 2, 4]: (An equation -1 == 0,)
315
+ 0
316
+ [3, 4]: (An equation (1, 0, -1, 0) x + 0 == 0,
317
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
318
+ An inequality (-1, -1, 0, 1) x - 1 >= 0,
319
+ An inequality (1, 0, 0, 0) x + 0 >= 0)
320
+ y3 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
321
+ [0, 3, 4]: (An equation -1 == 0,)
322
+ 0
323
+ [1, 3, 4]: (An equation -1 == 0,)
324
+ 0
325
+ [0, 1, 3, 4]: (An equation -1 == 0,)
326
+ 0
327
+ [2, 3, 4]: (An equation (1, 1, 0, -1) x + 1 == 0,
328
+ An equation (1, 0, -1, 0) x + 0 == 0,
329
+ An inequality (0, 1, 0, 0) x + 0 >= 0,
330
+ An inequality (1, 0, 0, 0) x + 0 >= 0)
331
+ y3 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
332
+ [0, 2, 3, 4]: (An equation -1 == 0,)
333
+ 0
334
+ [1, 2, 3, 4]: (An equation -1 == 0,)
335
+ 0
336
+ [0, 1, 2, 3, 4]: (An equation -1 == 0,)
337
+ 0
338
+
339
+ sage: # optional - latte_int
340
+ sage: for J in subsets(range(len(P3))):
341
+ ....: if not J:
342
+ ....: continue
343
+ ....: P = intersect([P3[j] for j in J])
344
+ ....: f1 = generating_function_of_integral_points(P, sort_factors=True)
345
+ ....: f2 = generating_function_of_integral_points(P, result_as_tuple=True,
346
+ ....: algorithm="latte")
347
+ ....: print('{}: {}'.format(J, f1.value() == sum(f2)))
348
+ [0]: True
349
+ [1]: True
350
+ [0, 1]: True
351
+ [2]: True
352
+ [0, 2]: True
353
+ [1, 2]: True
354
+ [0, 1, 2]: True
355
+ [3]: True
356
+ [0, 3]: True
357
+ [1, 3]: True
358
+ [0, 1, 3]: True
359
+ [2, 3]: True
360
+ [0, 2, 3]: True
361
+ [1, 2, 3]: True
362
+ [0, 1, 2, 3]: True
363
+ [4]: True
364
+ [0, 4]: True
365
+ [1, 4]: True
366
+ [0, 1, 4]: True
367
+ [2, 4]: True
368
+ [0, 2, 4]: True
369
+ [1, 2, 4]: True
370
+ [0, 1, 2, 4]: True
371
+ [3, 4]: True
372
+ [0, 3, 4]: True
373
+ [1, 3, 4]: True
374
+ [0, 1, 3, 4]: True
375
+ [2, 3, 4]: True
376
+ [0, 2, 3, 4]: True
377
+ [1, 2, 3, 4]: True
378
+ [0, 1, 2, 3, 4]: True
379
+
380
+ ::
381
+
382
+ sage: P = Polyhedron(vertices=[[1], [5]])
383
+ sage: P.generating_function_of_integral_points()
384
+ y0^5 + y0^4 + y0^3 + y0^2 + y0
385
+ sage: P.generating_function_of_integral_points(algorithm="latte", # optional - latte_int
386
+ ....: result_as_tuple=True)
387
+ (y0 * (-y0 + 1)^-1, y0^5 * (1 - y0^-1)^-1)
388
+ sage: P.generating_function_of_integral_points(algorithm="naive")
389
+ y0^5 + y0^4 + y0^3 + y0^2 + y0
390
+
391
+ .. SEEALSO::
392
+
393
+ This function is accessible via
394
+ :meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points`
395
+ as well. More examples can be found there.
396
+
397
+ TESTS::
398
+
399
+ sage: generating_function_of_integral_points(
400
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0),
401
+ ....: (0, 0, 0, 1, 0), (0, 0, 0, 0, 1)]),
402
+ ....: sort_factors=True)
403
+ y0 * (-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y0*y1 + 1)^-1
404
+ sage: generating_function_of_integral_points(
405
+ ....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
406
+ ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0),
407
+ ....: (0, 0, 0, 1, 0)]),
408
+ ....: sort_factors=True)
409
+ (-y0^2*y3 + y0*y3 + y0) *
410
+ (-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1
411
+
412
+ sage: generating_function_of_integral_points(
413
+ ....: Polyhedron(ieqs=[(0, 1, 0, -1, 0, 0), (0, 0, 0, 1, 0, 0)],
414
+ ....: eqns=[(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0),
415
+ ....: (0, 1, -1, 0, 0, 0)]),
416
+ ....: sort_factors=True)
417
+ 1 * (-y0*y1*y3 + 1)^-1 * (-y0*y1*y2*y3*y4 + 1)^-1
418
+
419
+ ::
420
+
421
+ sage: G = generating_function_of_integral_points(P2[0], sort_factors=True)
422
+ sage: S = generating_function_of_integral_points(P2[0], sort_factors=True,
423
+ ....: split=True)
424
+ sage: sum(S) == G.value()
425
+ True
426
+
427
+ sage: G = generating_function_of_integral_points(P2[1], sort_factors=True)
428
+ sage: S = generating_function_of_integral_points(P2[1], sort_factors=True,
429
+ ....: split=True)
430
+ sage: sum(S) == G.value()
431
+ True
432
+
433
+ sage: G = generating_function_of_integral_points(P3[0], sort_factors=True)
434
+ sage: S = generating_function_of_integral_points(P3[0], sort_factors=True,
435
+ ....: split=True)
436
+ sage: sum(S) == G.value()
437
+ True
438
+
439
+ sage: G = generating_function_of_integral_points(P3[1], sort_factors=True)
440
+ sage: S1 = generating_function_of_integral_points(P3[1], sort_factors=True,
441
+ ....: split=True)
442
+ sage: S2 = generating_function_of_integral_points(P3[1], # optional - latte_int
443
+ ....: sort_factors=True,
444
+ ....: algorithm="latte",
445
+ ....: result_as_tuple=True)
446
+ sage: sum(S1) == G.value()
447
+ True
448
+ sage: sum(S2) == G.value() # optional - latte_int
449
+ True
450
+ sage: S1
451
+ (0,
452
+ ...
453
+ 0,
454
+ y0*y3 * (-y0*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
455
+ (-y0^4*y1*y2*y3^2 + y0) * (-y0 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
456
+ 0,
457
+ y0^2*y2 * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1,
458
+ 0,
459
+ 0,
460
+ y0^2*y1*y3^2 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
461
+ (-y0^5*y1^2*y2*y3^3 + y0^2*y1*y3) * (-y0 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
462
+ 0,
463
+ ...
464
+ 0)
465
+ sage: S2 # optional - latte_int
466
+ (y0 * (-y0 + 1)^-1 * (-y3 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1,
467
+ y0*y3 * (1 - y3^-1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1)
468
+ sage: G
469
+ (-y0^2*y2*y3 - y0^2*y3 + y0*y3 + y0) * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1
470
+ * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
471
+
472
+ We show the distinct polyhedra that are used when ``split=True`` and the
473
+ resulting polyhedra that are used in the individual computations::
474
+
475
+ sage: import logging
476
+ sage: logging.basicConfig(level=logging.INFO)
477
+ sage: sum(generating_function_of_integral_points(P2[1], sort_factors=True,
478
+ ....: split=True))
479
+ ...
480
+ INFO:sage.geometry.polyhedron.generating_function:(1/6) split polyhedron by b0 <= b1 <= b2
481
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
482
+ b0 >= 0
483
+ b1 >= b0
484
+ b2 >= b1
485
+ ...
486
+ INFO:sage.geometry.polyhedron.generating_function:(2/6) split polyhedron by b0 <= b2 < b1
487
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
488
+ b2 >= b0
489
+ b1 >= 1 + b2
490
+ b0 >= 0
491
+ ...
492
+ INFO:sage.geometry.polyhedron.generating_function:(3/6) split polyhedron by b1 < b0 <= b2
493
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
494
+ b2 >= b0
495
+ b1 >= 0
496
+ b0 >= 1 + b1
497
+ ...
498
+ INFO:sage.geometry.polyhedron.generating_function:(4/6) split polyhedron by b1 <= b2 < b0
499
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
500
+ 0 == 1
501
+ INFO:sage.geometry.polyhedron.generating_function:(5/6) split polyhedron by b2 < b0 <= b1
502
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
503
+ 0 == 1
504
+ INFO:sage.geometry.polyhedron.generating_function:(6/6) split polyhedron by b2 < b1 < b0
505
+ INFO:sage.geometry.polyhedron.generating_function:using polyhedron
506
+ 0 == 1
507
+ 1/(-y0*y1*y2^2 + y0*y1*y2 + y0*y2^2 - y0*y2 + y1*y2 - y1 - y2 + 1)
508
+ sage: logging.disable()
509
+
510
+ ``"omega"`` can only handle polyhedra with nonnegative coordinates.
511
+ ``"latte"`` can handle more general polyhedra.
512
+ (the following two examples also fail in ``"latte"``)::
513
+
514
+ sage: generating_function_of_integral_points(
515
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0)]),
516
+ ....: sort_factors=True)
517
+ Traceback (most recent call last):
518
+ ...
519
+ NotImplementedError: cannot compute the generating function of
520
+ polyhedra with negative coordinates
521
+ sage: generating_function_of_integral_points(
522
+ ....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
523
+ ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)]),
524
+ ....: sort_factors=True)
525
+ Traceback (most recent call last):
526
+ ...
527
+ NotImplementedError: cannot compute the generating function of
528
+ polyhedra with negative coordinates
529
+
530
+ sage: generating_function_of_integral_points(
531
+ ....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
532
+ ....: sort_factors=True)
533
+ Traceback (most recent call last):
534
+ ...
535
+ NotImplementedError: cannot compute the generating function of
536
+ polyhedra with negative coordinates
537
+ sage: generating_function_of_integral_points( # optional - latte_int
538
+ ....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
539
+ ....: sort_factors=True, algorithm="latte", result_as_tuple=True)
540
+ ((y0^-2) * (-y0 + 1)^-1, y0^5 * (1 - y0^-1)^-1)
541
+ sage: generating_function_of_integral_points(
542
+ ....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
543
+ ....: sort_factors=True, algorithm="naive")
544
+ y0^5 + y0^4 + y0^3 + y0^2 + y0 + 1 + y0^-1 + y0^-2
545
+
546
+ The outputs of different algorithm options might be different.
547
+ But the sum of the output from ``"latte"`` is the same as the output from ``"omega"``::
548
+
549
+ sage: generating_function_of_integral_points(
550
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
551
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
552
+ ....: name='z',
553
+ ....: sort_factors=True)
554
+ (-z0*z1*z2 - z0*z2 + z0 + z2) *
555
+ (-z0 + 1)^-1 * (-z2 + 1)^-1 * (-z0*z1 + 1)^-1 * (-z1*z2 + 1)^-1
556
+ sage: generating_function_of_integral_points(
557
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
558
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
559
+ ....: name='mu',
560
+ ....: sort_factors=True)
561
+ (-mu0*mu1*mu2 - mu0*mu2 + mu0 + mu2) *
562
+ (-mu0 + 1)^-1 * (-mu2 + 1)^-1 * (-mu0*mu1 + 1)^-1 * (-mu1*mu2 + 1)^-1
563
+
564
+ sage: # optional - latte_int
565
+ sage: generating_function_of_integral_points(
566
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
567
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
568
+ ....: name='z',
569
+ ....: sort_factors=True, algorithm="latte", result_as_tuple=True)
570
+ (z0 * (-z0 + 1)^-1 * (-z0*z1 + 1)^-1 * (1 - z0^-1*z2)^-1,
571
+ z2 * (-z2 + 1)^-1 * (-z0*z2^-1 + 1)^-1 * (-z1*z2 + 1)^-1)
572
+ sage: generating_function_of_integral_points(
573
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
574
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
575
+ ....: name='mu',
576
+ ....: sort_factors=True, algorithm="latte", result_as_tuple=True)
577
+ (mu0 * (-mu0 + 1)^-1 * (-mu0*mu1 + 1)^-1 * (1 - mu0^-1*mu2)^-1,
578
+ mu2 * (-mu2 + 1)^-1 * (-mu0*mu2^-1 + 1)^-1 * (-mu1*mu2 + 1)^-1)
579
+
580
+ sage: f1 = generating_function_of_integral_points(
581
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
582
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
583
+ ....: name='z',
584
+ ....: sort_factors=True)
585
+ sage: f2 = generating_function_of_integral_points( # optional - latte_int
586
+ ....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
587
+ ....: (0, 0, 0, 1), (0, 1, 0, 0)]),
588
+ ....: name='z',
589
+ ....: sort_factors=True, algorithm="latte", result_as_tuple=True)
590
+ sage: f1.value() == sum(f2) # optional - latte_int
591
+ True
592
+
593
+ ::
594
+
595
+ sage: generating_function_of_integral_points(P2[0],
596
+ ....: sort_factors=True, names=('a',))
597
+ 1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1
598
+ sage: generating_function_of_integral_points(P2[0],
599
+ ....: sort_factors=True, names=('a', 'b'))
600
+ Traceback (most recent call last):
601
+ ...
602
+ NotImplementedError: exactly one variable name has to be provided
603
+ sage: generating_function_of_integral_points(P2[0],
604
+ ....: sort_factors=True, name='a', names=('a',))
605
+ 1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1
606
+ sage: generating_function_of_integral_points(P2[0],
607
+ ....: sort_factors=True, name='a', names=('b',))
608
+ Traceback (most recent call last):
609
+ ...
610
+ ValueError: keyword argument 'name' cannot be combined with 'names'
611
+
612
+ ::
613
+
614
+ sage: # needs sage.symbolic
615
+ sage: P = Polyhedron(rays=[(1, sqrt(2)), (0, 1)])
616
+ Traceback (most recent call last):
617
+ ...
618
+ ValueError: no default backend for computations with Symbolic Ring
619
+ sage: P = Polyhedron(ieqs=[(RDF(pi), RDF(1))])
620
+ sage: P.generating_function_of_integral_points()
621
+ Traceback (most recent call last):
622
+ ...
623
+ TypeError: base ring Real Double Field of the polyhedron not ZZ or QQ
624
+ """
625
+ import logging
626
+ logger = logging.getLogger(__name__)
627
+
628
+ from sage.combinat.permutation import Permutations
629
+ from sage.geometry.polyhedron.constructor import Polyhedron
630
+ from sage.rings.integer_ring import ZZ
631
+ from sage.rings.rational_field import QQ
632
+ from sage.structure.category_object import normalize_names
633
+
634
+ if result_as_tuple is None:
635
+ result_as_tuple = split
636
+
637
+ if polyhedron.is_empty():
638
+ from sage.structure.factorization import Factorization
639
+ result = Factorization([], unit=0)
640
+ if result_as_tuple:
641
+ return (result,)
642
+ else:
643
+ return result
644
+
645
+ if polyhedron.base_ring() not in (ZZ, QQ):
646
+ raise TypeError('base ring {} of the polyhedron not '
647
+ 'ZZ or QQ'.format(polyhedron.base_ring()))
648
+
649
+ d = polyhedron.ambient_dim()
650
+ nonnegative_orthant = Polyhedron(ieqs=[dd*(0,) + (1,) + (d-dd)*(0,)
651
+ for dd in range(1, d+1)])
652
+ if algorithm == "omega" and polyhedron & nonnegative_orthant != polyhedron:
653
+ raise NotImplementedError('cannot compute the generating function of '
654
+ 'polyhedra with negative coordinates')
655
+
656
+ logger.info('%s', polyhedron)
657
+
658
+ if names is not None:
659
+ names = normalize_names(-1, names)
660
+ if len(names) != 1:
661
+ raise NotImplementedError('exactly one variable name has to be provided')
662
+ if name is not None and name != names[0]:
663
+ raise ValueError("keyword argument 'name' cannot be combined with 'names'")
664
+ name = names[0]
665
+ if name is None:
666
+ name = 'y'
667
+
668
+ if algorithm == "latte":
669
+ from sage.interfaces.latte import count # optional - latte_int
670
+ cddin = polyhedron.cdd_Hrepresentation()
671
+ if result_as_tuple:
672
+ return count(cddin, cdd=True, multivariate_generating_function=True, name=name, **kwds)
673
+ else:
674
+ result = count(cddin, cdd=True, multivariate_generating_function=True, name=name, **kwds)
675
+ if len(result) != 1:
676
+ raise ValueError("cannot unpack result "
677
+ "(set 'result_as_tuple=True')")
678
+ return result[0]
679
+ elif algorithm == "naive":
680
+ if polyhedron.is_compact():
681
+ from sage.rings.integer_ring import ZZ
682
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
683
+ from sage.structure.factorization import Factorization
684
+
685
+ indices = range(polyhedron.ambient_dim())
686
+
687
+ B = LaurentPolynomialRing(ZZ,
688
+ tuple(name + str(k) for k in indices),
689
+ len(indices))
690
+ result = sum(B.monomial(*r) for r in polyhedron.integral_points())
691
+ return result
692
+ else:
693
+ raise ValueError("Polyhedron must be bounded for the naive algorithm")
694
+ elif algorithm != "omega":
695
+ raise ValueError('Algorithm must be omega, latte, or naive.')
696
+
697
+ if split is False:
698
+ result = _generating_function_of_integral_points_(polyhedron, name=name, **kwds)
699
+ if result_as_tuple:
700
+ return result
701
+ else:
702
+ if len(result) != 1:
703
+ raise ValueError("cannot unpack result "
704
+ "(set 'result_as_tuple=True')")
705
+ return result[0]
706
+
707
+ if d <= 1:
708
+ raise ValueError('cannot do splitting with only '
709
+ 'dimension {}'.format(d))
710
+
711
+ parts = None
712
+ if split is True:
713
+
714
+ def polyhedron_from_permutation(pi):
715
+
716
+ def ieq(a, b):
717
+ return ((0 if a < b else -1,) +
718
+ tuple(1 if i == b else (-1 if i == a else 0)
719
+ for i in range(1, d + 1)))
720
+
721
+ def ieq_repr_rhs(a, b):
722
+ return (' <= ' if a < b else ' < ') + 'b{}'.format(b-1)
723
+
724
+ def ieqs_repr_lhs(pi):
725
+ return 'b{}'.format(pi[0]-1)
726
+
727
+ ieqs, repr_rhss = zip(*[(ieq(a, b), ieq_repr_rhs(a, b))
728
+ for a, b in zip(pi[:-1], pi[1:])])
729
+ return Polyhedron(ieqs=ieqs), ieqs_repr_lhs(pi) + ''.join(repr_rhss)
730
+
731
+ split = (polyhedron_from_permutation(pi) for pi in Permutations(d))
732
+ parts = ZZ(d).factorial()
733
+ else:
734
+ if isinstance(split, (list, tuple)):
735
+ parts = len(split)
736
+ split = ((ph, ph.Hrepresentation_str(**Hrepresentation_str_options))
737
+ for ph in split)
738
+
739
+ result = []
740
+ for part, (split_polyhedron, pi_log) in enumerate(split):
741
+ if parts is None:
742
+ parts_log = str(part+1)
743
+ else:
744
+ parts_log = '{}/{}'.format(part+1, parts)
745
+ logger.info('(%s) split polyhedron by %s', parts_log, pi_log)
746
+ result.append(_generating_function_of_integral_points_(
747
+ polyhedron & split_polyhedron, name=name, **kwds))
748
+ if not result_as_tuple:
749
+ raise ValueError("cannot unpack result"
750
+ "(unset 'result_as_tuple=False')")
751
+ return sum(result, ())
752
+
753
+
754
+ def _generating_function_of_integral_points_(
755
+ polyhedron, indices=None, **kwds):
756
+ r"""
757
+ Helper function for :func:`generating_function_of_integral_points` which
758
+ does the mid-level stuff.
759
+
760
+ TESTS::
761
+
762
+ sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points
763
+
764
+ sage: generating_function_of_integral_points( # indirect doctest
765
+ ....: Polyhedron(ieqs=[(0, 1, 0, 0), (0, -1, 1, 0)],
766
+ ....: eqns=[(0, -1, -1, 2)]),
767
+ ....: result_as_tuple=True, sort_factors=True)
768
+ (1 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1,
769
+ y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1)
770
+ """
771
+ import logging
772
+ logger = logging.getLogger(__name__)
773
+
774
+ logger.info('using polyhedron %s',
775
+ polyhedron.Hrepresentation_str(**Hrepresentation_str_options))
776
+
777
+ if polyhedron.is_empty():
778
+ from sage.structure.factorization import Factorization
779
+ return (Factorization([], unit=0),)
780
+
781
+ Hrepr = polyhedron.Hrepresentation()
782
+
783
+ inequalities = tuple(tuple(entry)
784
+ for entry in Hrepr if entry.is_inequality())
785
+ equations = tuple(tuple(entry)
786
+ for entry in Hrepr if entry.is_equation())
787
+ if len(inequalities) + len(equations) != len(Hrepr):
788
+ raise ValueError('cannot handle {}.'.format(polyhedron))
789
+
790
+ if not inequalities:
791
+ raise NotImplementedError('no inequality given')
792
+
793
+ if indices is None:
794
+ indices = range(len(inequalities[0]) - 1)
795
+
796
+ n = len(indices) + 1
797
+ if any(len(e) != n for e in inequalities):
798
+ raise ValueError('not all coefficient vectors of the inequalities '
799
+ 'have the same length')
800
+ if any(len(e) != n for e in equations):
801
+ raise ValueError('not all coefficient vectors of the equations '
802
+ 'have the same length')
803
+
804
+ mods = _TransformMod.generate_mods(equations)
805
+ logger.debug('splitting by moduli %s', mods)
806
+
807
+ return tuple(__generating_function_of_integral_points__(
808
+ indices, inequalities, equations, mod, **kwds) for mod in mods)
809
+
810
+
811
+ def __generating_function_of_integral_points__(
812
+ indices, inequalities, equations, mod,
813
+ name,
814
+ Factorization_sort=False, Factorization_simplify=False,
815
+ sort_factors=False):
816
+ r"""
817
+ Helper function for :func:`generating_function_of_integral_points` which
818
+ does the actual computation of the generating function.
819
+
820
+ TESTS::
821
+
822
+ sage: from sage.geometry.polyhedron.generating_function import __generating_function_of_integral_points__
823
+
824
+ sage: __generating_function_of_integral_points__(
825
+ ....: (0, 2), [(0, 1, 0)], [(1, -1, 2)],
826
+ ....: {0: (2, 1)}, name='y', sort_factors=True)
827
+ y0 * (-y0^2*y2 + 1)^-1
828
+ sage: __generating_function_of_integral_points__(
829
+ ....: srange(3), [(0, 1, 0, 0), (0, 0, 1, 0)], [(1, -1, 0, 2)],
830
+ ....: {0: (2, 1)}, name='y', sort_factors=True)
831
+ y0 * (-y1 + 1)^-1 * (-y0^2*y2 + 1)^-1
832
+ sage: __generating_function_of_integral_points__(
833
+ ....: srange(3), [(0, 1, 0, 0), (0, -1, 1, 0)], [(0, -1, -1, 2)],
834
+ ....: {0: (2, 1), 1: (2, 1)}, name='y', sort_factors=True)
835
+ y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1
836
+ """
837
+ import logging
838
+ logger = logging.getLogger(__name__)
839
+
840
+ from sage.rings.integer_ring import ZZ
841
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
842
+ from sage.structure.factorization import Factorization
843
+
844
+ B = LaurentPolynomialRing(ZZ,
845
+ tuple(name + str(k) for k in indices),
846
+ len(indices))
847
+
848
+ logger.info('preprocessing %s inequalities and %s equations...',
849
+ len(inequalities), len(equations))
850
+
851
+ T_mod = _TransformMod(inequalities, equations, B, mod)
852
+ inequalities = T_mod.inequalities
853
+ equations = T_mod.equations
854
+
855
+ T_equations = _EliminateByEquations(inequalities, equations, B)
856
+ inequalities = T_equations.inequalities
857
+ equations = T_equations.equations
858
+
859
+ T_inequalities = _SplitOffSimpleInequalities(inequalities, equations, B)
860
+ inequalities = T_inequalities.inequalities
861
+ equations = T_inequalities.equations
862
+ assert not equations
863
+
864
+ logger.info('%s inequalities left; using Omega...', len(inequalities))
865
+ numerator, terms = _generating_function_via_Omega_(
866
+ inequalities, B, skip_indices=T_equations.indices)
867
+
868
+ numerator, terms = T_inequalities.apply_rules(numerator, terms)
869
+ numerator, terms = T_equations.apply_rules(numerator, terms)
870
+ numerator, terms = T_mod.apply_rules(numerator, terms)
871
+
872
+ if sort_factors:
873
+ def key(t):
874
+ D = t.monomial_coefficients().popitem()[0]
875
+ return (-sum(abs(d) for d in D), D)
876
+ terms = sorted(terms, key=key, reverse=True)
877
+ return Factorization([(numerator, 1)] +
878
+ list((1-t, -1) for t in terms),
879
+ sort=Factorization_sort,
880
+ simplify=Factorization_simplify)
881
+
882
+
883
+ def _generating_function_via_Omega_(inequalities, B, skip_indices=()):
884
+ r"""
885
+ Compute the generating function of the integral points of the
886
+ polyhedron specified by ``inequalities`` via
887
+ :func:`MacMahon's Omega operator <sage.rings.polynomial.omega.MacMahonOmega>`.
888
+
889
+ INPUT:
890
+
891
+ - ``inequalities`` -- list or other iterable of tuples
892
+ of numbers
893
+
894
+ - ``B`` -- a Laurent polynomial ring
895
+
896
+ - ``skip_indices`` -- list or tuple of indices
897
+
898
+ The variables corresponding to ``skip_indices`` are not handled
899
+ (e.g. because they are determined by an equation).
900
+
901
+ OUTPUT: a pair of
902
+
903
+ - a Laurent polynomial specifying the numerator and
904
+
905
+ - a tuple of Laurent polynomials specifying the denominator;
906
+ each entry `t` corresponds to a factor `1 - t`.
907
+
908
+ EXAMPLES::
909
+
910
+ sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_
911
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
912
+ sage: _generating_function_via_Omega_([(0, -1, -1, 1)], B)
913
+ (1, (y2, y0*y2, y1*y2))
914
+ """
915
+ import logging
916
+ logger = logging.getLogger(__name__)
917
+
918
+ from .representation import repr_pretty
919
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
920
+ from sage.rings.polynomial.omega import _Omega_, _simplify_, partition
921
+
922
+ numerator = B(1)
923
+ terms = B.gens()
924
+ L = B
925
+ mu = 'mu' if not repr(B.gen()).startswith('mu') else 'nu'
926
+ for i, coeffs in enumerate(inequalities):
927
+ L = LaurentPolynomialRing(L, mu + str(i), sparse=True)
928
+ l = L.gen()
929
+ logger.debug('mapping %s --> %s', l, repr_pretty(coeffs, 0))
930
+ it_coeffs = iter(coeffs)
931
+ numerator *= l**next(it_coeffs)
932
+ assert numerator.parent() == L
933
+ terms = tuple(l**c * t for c, t in zip(it_coeffs, terms))
934
+ assert all(y == t for y, t in
935
+ (tuple(zip(B.gens(), terms))[i] for i in skip_indices))
936
+ terms = tuple(t for i, t in enumerate(terms)
937
+ if i not in skip_indices)
938
+
939
+ logger.debug('terms denominator %s', terms)
940
+
941
+ def decode_factor(factor):
942
+ D = factor.monomial_coefficients()
943
+ assert len(D) == 1
944
+ exponent, coefficient = next(iter(D.items()))
945
+ return coefficient, exponent
946
+
947
+ while repr(numerator.parent().gen()).startswith(mu):
948
+ logger.info('applying Omega[%s]...', numerator.parent().gen())
949
+ logger.debug('...on terms denominator %s', terms)
950
+ logger.debug('...(numerator has %s terms)', numerator.number_of_terms())
951
+ logger.debug('...numerator %s', numerator)
952
+
953
+ decoded_factors, other_factors = \
954
+ partition((decode_factor(factor) for factor in terms),
955
+ lambda factor: factor[1] == 0)
956
+ other_factors = tuple(factor[0] for factor in other_factors)
957
+ numerator, factors_denominator = \
958
+ _Omega_(numerator.monomial_coefficients(), tuple(decoded_factors))
959
+ terms = other_factors + factors_denominator
960
+
961
+ return _simplify_(numerator, terms)
962
+
963
+
964
+ class _TransformHrepresentation:
965
+ r"""
966
+ An abstract base class for transformations of the
967
+ Hrepresentation of a polyhedron together with its
968
+ back-substitutions of the corresponding generating function.
969
+
970
+ INPUT:
971
+
972
+ - ``inequalities`` -- list of tuples of numbers
973
+
974
+ - ``equations`` -- list of tuples of numbers
975
+
976
+ - ``B`` -- a Laurent polynomial ring
977
+
978
+ ATTRIBUTES:
979
+
980
+ - ``inequalities``, ``equations`` -- list of tuples
981
+
982
+ Determine the generating function of these inequalities
983
+ and equations instead of the input.
984
+
985
+ - ``factor`` -- a Laurent polynomial
986
+
987
+ The numerator of the generating function has to be multiplied
988
+ with ``factor`` *after* substituting ``rules``.
989
+
990
+ - ``rules`` -- dictionary mapping Laurent polynomial variables to
991
+ Laurent polynomials
992
+
993
+ Substitute ``rules`` into the generating function.
994
+
995
+ The generating function of the input ``inequalities`` and
996
+ ``equations`` is equal to the generating function of the
997
+ attributes ``inequalities`` and ``equations`` in which ``rules``
998
+ were substituted and ``factor`` was multiplied (via
999
+ :meth:`~_TransformHrepresentation.apply_rules`).
1000
+ """
1001
+
1002
+ def __init__(self, inequalities, equations, B):
1003
+ r"""
1004
+ See :class:`_TransformHrepresentation` for details.
1005
+
1006
+ TESTS::
1007
+
1008
+ sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation
1009
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1010
+ sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B)
1011
+ Traceback (most recent call last):
1012
+ ...
1013
+ NotImplementedError
1014
+ """
1015
+ self.inequalities = inequalities
1016
+ self.equations = equations
1017
+ self.B = B
1018
+ self._transform_()
1019
+
1020
+ def _transform_(self):
1021
+ r"""
1022
+ Transform the input given on construction of this object.
1023
+
1024
+ This is called during :meth:`__init__`. Overload this method
1025
+ in derived classes.
1026
+
1027
+ TESTS::
1028
+
1029
+ sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation
1030
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1031
+ sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B)
1032
+ Traceback (most recent call last):
1033
+ ...
1034
+ NotImplementedError
1035
+ """
1036
+ raise NotImplementedError
1037
+
1038
+ def apply_rules(self, numerator, terms):
1039
+ r"""
1040
+ Substitute the generated rules.
1041
+
1042
+ INPUT:
1043
+
1044
+ - ``numerator`` -- a Laurent polynomial
1045
+
1046
+ - ``terms`` -- tuple or other iterable of Laurent polynomials
1047
+
1048
+ The denominator is the product of factors `1 - t` for each
1049
+ `t` in ``terms``.
1050
+
1051
+ OUTPUT:
1052
+
1053
+ A pair of a Laurent polynomial and a tuple of Laurent polynomials
1054
+ representing numerator and denominator as described in the
1055
+ INPUT-section.
1056
+
1057
+ EXAMPLES::
1058
+
1059
+ sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare
1060
+ sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf
1061
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
1062
+ sage: ieqs = [(0, -1, 1, 0)]
1063
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1064
+ ([], 1, {y2: y2, y1: y1, y0: y0*y1})
1065
+ sage: T.apply_rules(*gf(T.inequalities, B))
1066
+ (1, (y0*y1, y1, y2))
1067
+ """
1068
+ return (numerator.subs(self.rules) * self.factor,
1069
+ tuple(t.subs(self.rules) for t in terms))
1070
+
1071
+
1072
+ class _SplitOffSimpleInequalities(_TransformHrepresentation):
1073
+ r"""
1074
+ Split off (simple) inequalities which can be handled better
1075
+ without passing them to Omega.
1076
+
1077
+ INPUT:
1078
+
1079
+ - ``inequalities`` -- list of tuples of numbers
1080
+
1081
+ - ``equations`` -- list of tuples of numbers
1082
+
1083
+ - ``B`` -- a Laurent polynomial ring
1084
+
1085
+ ATTRIBUTES:
1086
+
1087
+ - ``inequalities``, ``equations`` -- list of tuples
1088
+
1089
+ Determine the generating function of these inequalities
1090
+ and equations instead of the input.
1091
+
1092
+ - ``factor`` -- a Laurent polynomial
1093
+
1094
+ The numerator of the generating function has to be multiplied
1095
+ with ``factor`` *after* substituting ``rules``.
1096
+
1097
+ - ``rules`` -- dictionary mapping Laurent polynomial variables to
1098
+ Laurent polynomials
1099
+
1100
+ Substitute ``rules`` into the generating function.
1101
+
1102
+ The generating function of the input ``inequalities`` and
1103
+ ``equations`` is equal to the generating function of the
1104
+ attributes ``inequalities`` and ``equations`` in which ``rules``
1105
+ were substitited and ``factor`` was multiplied (via
1106
+ :meth:`~_TransformHrepresentation.apply_rules`).
1107
+
1108
+ EXAMPLES::
1109
+
1110
+ sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare
1111
+ sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf
1112
+
1113
+ sage: def eq(A, B):
1114
+ ....: return (A[0] == B[0] and
1115
+ ....: sorted(A[1], key=repr) == sorted(B[1], key=repr))
1116
+
1117
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
1118
+
1119
+ sage: ieqs = [(0, -1, 1, 0), (2, -1, -1, 1)]
1120
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1121
+ ([(2, -2, -1, 1)], 1, {y2: y2, y1: y1, y0: y0*y1})
1122
+ sage: T.apply_rules(*gf(T.inequalities, B))
1123
+ (y0*y1^3*y2^3 - y0*y1^3*y2^2 + y0*y1^2*y2^3 - y0*y1^2*y2^2
1124
+ - y0*y1*y2^2 - y1^2*y2 + y0*y1 + y1^2 - y1*y2 + y1 + 1,
1125
+ (y2, y1*y2, y0*y1*y2^2))
1126
+ sage: eq(_, gf(ieqs, B))
1127
+ True
1128
+
1129
+ sage: ieqs = [(-1, -1, 1, 0), (2, -1, -1, 1)]
1130
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1131
+ ([(1, -2, -1, 1)], y1, {y2: y2, y1: y1, y0: y0*y1})
1132
+ sage: T.apply_rules(*gf(T.inequalities, B))
1133
+ (y0*y1^3*y2^3 - y0*y1^3*y2^2 - y0*y1^2*y2^2
1134
+ + y0*y1^2*y2 - y1^2*y2 + y1^2 + y1,
1135
+ (y2, y1*y2, y0*y1*y2^2))
1136
+ sage: eq(_, gf(ieqs, B))
1137
+ True
1138
+
1139
+ sage: ieqs = [(-2, -1, 1, 0), (2, -1, -1, 1)]
1140
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1141
+ ([(0, -2, -1, 1)], y1^2, {y2: y2, y1: y1, y0: y0*y1})
1142
+ sage: T.apply_rules(*gf(T.inequalities, B))
1143
+ (y1^2, (y2, y1*y2, y0*y1*y2^2))
1144
+ sage: eq(_, gf(ieqs, B))
1145
+ True
1146
+
1147
+ sage: ieqs = [(2, -1, 1, 0), (2, -1, -1, 1)]
1148
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1149
+ ([(2, -1, 1, 0), (2, -1, -1, 1)], 1, {y2: y2, y1: y1, y0: y0})
1150
+ sage: eq(T.apply_rules(*gf(T.inequalities, B)), gf(ieqs, B))
1151
+ True
1152
+
1153
+ TESTS::
1154
+
1155
+ sage: # needs sage.symbolic
1156
+ sage: def eq2(A, B):
1157
+ ....: a = SR(repr(A[0])) * prod(1-SR(repr(t)) for t in B[1])
1158
+ ....: b = SR(repr(B[0])) * prod(1-SR(repr(t)) for t in A[1])
1159
+ ....: return bool((a-b).full_simplify() == 0)
1160
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
1161
+ sage: ieqs = [(-2, 1, -1, 0), (-2, -1, 0, 1), (-1, -1, -1, 3)]
1162
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1163
+ ([(9, 2, 1, 3)], y0^2*y2^4, {y2: y2, y1: y0*y1*y2, y0: y0*y2})
1164
+ sage: T.apply_rules(*gf(T.inequalities, B))
1165
+ (y0^2*y2^4, (y2, y0*y2, y0*y1*y2))
1166
+ sage: eq2(_, gf(ieqs, B))
1167
+ True
1168
+
1169
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
1170
+ sage: ieqs = [(-1, 1, -1, 0, 0)]
1171
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1172
+ ([], y0, {y3: y3, y2: y2, y1: y0*y1, y0: y0})
1173
+ sage: T.apply_rules(*gf(T.inequalities, B))
1174
+ (y0, (y0, y0*y1, y2, y3))
1175
+ sage: eq(_, gf(ieqs, B))
1176
+ True
1177
+
1178
+ sage: ieqs = [(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
1179
+ ....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)]
1180
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1181
+ ([(1, 1, 0, 0, -1)], y0, {y3: y3, y2: y2, y1: y0*y1*y3, y0: y0})
1182
+ sage: T.apply_rules(*gf(T.inequalities, B))
1183
+ (-y0^2*y3 + y0*y3 + y0, (y0*y1*y3, y2, y0, y0*y3))
1184
+ sage: eq(_, gf(ieqs, B))
1185
+ True
1186
+
1187
+ sage: ieqs = [(-2, 1, -1, 0, 0)]
1188
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1189
+ ([], y0^2, {y3: y3, y2: y2, y1: y0*y1, y0: y0})
1190
+
1191
+ sage: T.apply_rules(*gf(T.inequalities, B))
1192
+ (y0^2, (y0, y0*y1, y2, y3))
1193
+ sage: eq(_, gf(ieqs, B))
1194
+ True
1195
+
1196
+ sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1),
1197
+ ....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)]
1198
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1199
+ ([(1, 0, -1, 1, 1)],
1200
+ y3^3,
1201
+ {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
1202
+ sage: T.apply_rules(*gf(T.inequalities, B))
1203
+ (-y1*y2*y3^4 - y1*y3^4 + y1*y3^3 + y3^3,
1204
+ (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
1205
+ sage: eq(_, gf(ieqs, B))
1206
+ True
1207
+
1208
+ sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1),
1209
+ ....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)]
1210
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1211
+ ([(1, 0, 1, -1, 1)],
1212
+ y3^3,
1213
+ {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
1214
+ sage: T.apply_rules(*gf(T.inequalities, B))
1215
+ (-y1*y2*y3^4 - y2*y3^4 + y2*y3^3 + y3^3,
1216
+ (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
1217
+ sage: eq(_, gf(ieqs, B))
1218
+ True
1219
+
1220
+ sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1),
1221
+ ....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
1222
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1223
+ ([(1, 0, -1, 1, 1)],
1224
+ y2^3*y3^3,
1225
+ {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
1226
+ sage: T.apply_rules(*gf(T.inequalities, B))
1227
+ (-y1*y2^4*y3^4 - y1*y2^3*y3^4 + y1*y2^3*y3^3 + y2^3*y3^3,
1228
+ (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
1229
+ sage: eq(_, gf(ieqs, B))
1230
+ True
1231
+
1232
+ sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1),
1233
+ ....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
1234
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1235
+ ([(1, 0, 1, -1, 1)],
1236
+ y2^2*y3^3,
1237
+ {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
1238
+ sage: T.apply_rules(*gf(T.inequalities, B))
1239
+ (-y1*y2^3*y3^4 - y2^3*y3^4 + y2^3*y3^3 + y2^2*y3^3,
1240
+ (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
1241
+ sage: eq(_, gf(ieqs, B))
1242
+ True
1243
+
1244
+ sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1),
1245
+ ....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)]
1246
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1247
+ ([(1, 0, -1, 1, 1)],
1248
+ y1^2*y3^3,
1249
+ {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
1250
+ sage: T.apply_rules(*gf(T.inequalities, B))
1251
+ (-y1^3*y2*y3^4 - y1^3*y3^4 + y1^3*y3^3 + y1^2*y3^3,
1252
+ (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
1253
+ sage: eq(_, gf(ieqs, B))
1254
+ True
1255
+
1256
+ sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1),
1257
+ ....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)]
1258
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1259
+ ([(1, 0, 1, -1, 1)],
1260
+ y1^3*y3^3,
1261
+ {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
1262
+ sage: T.apply_rules(*gf(T.inequalities, B))
1263
+ (-y1^4*y2*y3^4 - y1^3*y2*y3^4 + y1^3*y2*y3^3 + y1^3*y3^3,
1264
+ (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
1265
+ sage: eq(_, gf(ieqs, B))
1266
+ True
1267
+
1268
+ sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1),
1269
+ ....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
1270
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1271
+ ([(1, 0, -1, 1, 1)],
1272
+ y1^2*y2^3*y3^3,
1273
+ {y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
1274
+ sage: T.apply_rules(*gf(T.inequalities, B))
1275
+ (-y1^3*y2^4*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^2*y2^3*y3^3,
1276
+ (y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
1277
+ sage: eq(_, gf(ieqs, B))
1278
+ True
1279
+
1280
+ sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1),
1281
+ ....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
1282
+ sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
1283
+ ([(1, 0, 1, -1, 1)],
1284
+ y1^3*y2^2*y3^3,
1285
+ {y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
1286
+ sage: T.apply_rules(*gf(T.inequalities, B))
1287
+ (-y1^4*y2^3*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^3*y2^2*y3^3,
1288
+ (y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
1289
+ sage: eq(_, gf(ieqs, B))
1290
+ True
1291
+ """
1292
+
1293
+ def _transform_(self):
1294
+ r"""
1295
+ Do the actual transformation.
1296
+
1297
+ This is called during initialization.
1298
+ See :class:`_SplitOffSimpleInequalities` for details.
1299
+
1300
+ TESTS::
1301
+
1302
+ sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities
1303
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1304
+ sage: T = _SplitOffSimpleInequalities([(1, 1, 1)], [], B) # indirect doctest
1305
+ """
1306
+ inequalities = self.inequalities
1307
+ B = self.B
1308
+
1309
+ import logging
1310
+ logger = logging.getLogger(__name__)
1311
+
1312
+ from itertools import takewhile
1313
+ from .representation import repr_pretty
1314
+ from sage.graphs.digraph import DiGraph
1315
+ from sage.matrix.constructor import matrix
1316
+ from sage.modules.free_module_element import vector
1317
+ from sage.rings.integer_ring import ZZ
1318
+
1319
+ inequalities_filtered = []
1320
+ chain_links = {}
1321
+ for coeffs in inequalities:
1322
+ dim = len(coeffs)
1323
+ if all(c >= 0 for c in coeffs):
1324
+ logger.debug('skipping %s (all coefficients >= 0)',
1325
+ repr_pretty(coeffs, 0))
1326
+ continue
1327
+ constant = coeffs[0]
1328
+ ones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == 1)
1329
+ mones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == -1)
1330
+ absgetwo = tuple(i+1 for i, c in enumerate(coeffs[1:]) if abs(c) >= 2)
1331
+ if len(ones) == 1 and not mones and not absgetwo:
1332
+ if constant < 0:
1333
+ # This case could be cleverly skipped...
1334
+ inequalities_filtered.append(coeffs)
1335
+ elif len(ones) == 1 and len(mones) == 1 and not absgetwo and constant <= 0:
1336
+ logger.debug('handling %s',
1337
+ repr_pretty(coeffs, 0))
1338
+ chain_links[(mones[0], ones[0])] = constant
1339
+ else:
1340
+ inequalities_filtered.append(coeffs)
1341
+
1342
+ G = DiGraph(chain_links, format='list_of_edges')
1343
+ potential = {}
1344
+ paths = {}
1345
+ D = {}
1346
+ inequalities_extra = []
1347
+ for i in range(dim):
1348
+ D[(i, i)] = 1
1349
+ for v in G.topological_sort():
1350
+ NP = iter(sorted(((n, potential[n] + chain_links[(n, v)])
1351
+ for n in G.neighbor_in_iterator(v)),
1352
+ key=lambda k: (k[1], k[0])))
1353
+ n, p = next(NP, (None, 0))
1354
+ potential[v] = p
1355
+ D[(0, v)] = -p
1356
+ paths[v] = paths.get(n, ()) + (v,)
1357
+ for u in paths[v]:
1358
+ D[(u, v)] = 1
1359
+
1360
+ for n, p in NP:
1361
+ ell = len(tuple(takewhile(lambda u: u[0] == u[1],
1362
+ zip(paths[n], paths[v]))))
1363
+ coeffs = dim*[0]
1364
+ for u in paths[v][ell:]:
1365
+ coeffs[u] = 1
1366
+ for u in paths[n][ell:]:
1367
+ coeffs[u] = -1
1368
+ coeffs[0] = p - potential[v]
1369
+ inequalities_extra.append(tuple(coeffs))
1370
+ T = matrix(ZZ, dim, dim, D)
1371
+
1372
+ self.inequalities = (list(tuple(T*vector(ieq))
1373
+ for ieq in inequalities_filtered)
1374
+ + inequalities_extra)
1375
+
1376
+ rules_pre = ((y, B({tuple(row[1:]): 1}))
1377
+ for y, row in zip((1,) + B.gens(), T.rows()))
1378
+ self.factor = next(rules_pre)[1]
1379
+ self.rules = dict(rules_pre)
1380
+
1381
+
1382
+ class _EliminateByEquations(_TransformHrepresentation):
1383
+ r"""
1384
+ Prepare the substitutions coming from "eliminated" variables
1385
+ in the given equations.
1386
+
1387
+ INPUT:
1388
+
1389
+ - ``inequalities`` -- list of tuples of numbers
1390
+
1391
+ - ``equations`` -- list of tuples of numbers
1392
+
1393
+ - ``B`` -- a Laurent polynomial ring
1394
+
1395
+ ATTRIBUTES:
1396
+
1397
+ - ``inequalities``, ``equations`` -- list of tuples
1398
+
1399
+ Determine the generating function of these inequalities
1400
+ and equations instead of the input.
1401
+
1402
+ - ``factor`` -- a Laurent polynomial
1403
+
1404
+ The numerator of the generating function has to be multiplied
1405
+ with ``factor`` *after* substituting ``rules``.
1406
+
1407
+ - ``rules`` -- dictionary mapping Laurent polynomial variables to
1408
+ Laurent polynomials
1409
+
1410
+ Substitute ``rules`` into the generating function.
1411
+
1412
+ - ``indices`` -- a sorted tuple of integers representing
1413
+ indices of Laurent polynomial ring variables
1414
+
1415
+ These are exactly the "eliminated" variables which we take care of
1416
+ by ``factor`` and ``rules``.
1417
+
1418
+ The generating function of the input ``inequalities`` and
1419
+ ``equations`` is equal to the generating function of the
1420
+ attributes ``inequalities`` and ``equations`` in which ``rules``
1421
+ were substitited and ``factor`` was multiplied (via
1422
+ :meth:`~_TransformHrepresentation.apply_rules`).
1423
+
1424
+ EXAMPLES::
1425
+
1426
+ sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
1427
+
1428
+ sage: def prepare_equations(equations, B):
1429
+ ....: T = _EliminateByEquations([], equations, B)
1430
+ ....: return T.factor, T.rules, T.indices
1431
+
1432
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
1433
+ sage: prepare_equations([(1, 1, 1, -1, 0)], B)
1434
+ (y2, {y1: y1*y2, y0: y0*y2}, (2,))
1435
+ sage: prepare_equations([(0, 1, 0, -1, 0)], B)
1436
+ (1, {y0: y0*y2}, (2,))
1437
+ sage: prepare_equations([(-1, 0, 1, -1, -1), (1, 1, 0, 1, 2)], B)
1438
+ (y2^-1, {y1: y1*y2^2*y3^-1, y0: y0*y2*y3^-1}, (2, 3))
1439
+
1440
+ TESTS::
1441
+
1442
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
1443
+ sage: prepare_equations([(0, 0, 1, 0, -1), (-1, 1, -1, -1, 0)], B)
1444
+ (y2^-1, {y1: y1*y2^-1*y3, y0: y0*y2}, (2, 3))
1445
+
1446
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 5)
1447
+ sage: prepare_equations([(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0),
1448
+ ....: (0, 1, -1, 0, 0, 0)], B)
1449
+ (1, {y2: y2*y4, y0: y0*y1*y3}, (1, 3, 4))
1450
+ """
1451
+
1452
+ def _transform_(self):
1453
+ r"""
1454
+ Do the actual transformation.
1455
+
1456
+ This is called during initialization.
1457
+ See :class:`_EliminateByEquations` for details.
1458
+
1459
+ TESTS::
1460
+
1461
+ sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
1462
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1463
+ sage: T = _EliminateByEquations([], [], B) # indirect doctest
1464
+ """
1465
+ from sage.matrix.constructor import matrix
1466
+ from sage.misc.misc_c import prod
1467
+
1468
+ equations = self.equations
1469
+ B = self.B
1470
+
1471
+ E = matrix(equations)
1472
+ if not E:
1473
+ self.factor = 1
1474
+ self.rules = {}
1475
+ self.indices = ()
1476
+ return
1477
+
1478
+ TE, indices, indicesn = _EliminateByEquations.prepare_equations_transformation(E)
1479
+
1480
+ gens = (1,) + B.gens()
1481
+ z = tuple(gens[i] for i in indices)
1482
+ gens_cols = tuple(zip(gens, TE.columns()))
1483
+ rules_pre = ((y, y * prod(zz**(-c) for zz, c in zip(z, col)))
1484
+ for y, col in (gens_cols[i] for i in indicesn))
1485
+ self.factor = next(rules_pre)[1]
1486
+ self.rules = dict(rules_pre)
1487
+ self.indices = tuple(i-1 for i in indices)
1488
+ self.equations = []
1489
+
1490
+ @staticmethod
1491
+ def prepare_equations_transformation(E):
1492
+ r"""
1493
+ Return a transformation matrix and indices which variables
1494
+ in the equation to "eliminate" and deal with later.
1495
+
1496
+ INPUT:
1497
+
1498
+ - ``E`` -- a matrix whose rows represent equations
1499
+
1500
+ OUTPUT:
1501
+
1502
+ A triple ``(TE, indices, indicesn)`` with the following properties:
1503
+
1504
+ - ``TE`` -- a matrix
1505
+
1506
+ This matrix arises from ``E`` by multiplying a transformation matrix
1507
+ on the left.
1508
+
1509
+ - ``indices`` -- a sorted tuple of integers representing column indices
1510
+
1511
+ The the sub-matrix of ``TE`` with columns ``indices``
1512
+ is the identity matrix.
1513
+
1514
+ - ``indicesn`` -- a sorted tuple of integers representing column indices
1515
+
1516
+ ``indicesn`` contains ``0`` and all indices of the columns of ``E``
1517
+ which are nonzero.
1518
+
1519
+ TESTS::
1520
+
1521
+ sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
1522
+
1523
+ sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, 0, -2)]))
1524
+ ([ 0 -1/2 0 1], (3,), (0, 1))
1525
+ sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, -2, 0), (0, 2, 0, -3)]))
1526
+ (
1527
+ [ 0 -1/2 1 0]
1528
+ [ 0 -2/3 0 1], (2, 3), (0, 1)
1529
+ )
1530
+ """
1531
+ indices_nonzero = tuple(i for i, col in enumerate(E.columns())
1532
+ if i > 0 and not col.is_zero())
1533
+ indices = []
1534
+ r = 0
1535
+ for i in reversed(indices_nonzero):
1536
+ indices.append(i)
1537
+ r1 = E.matrix_from_columns(indices).rank()
1538
+ if r1 > r:
1539
+ r = r1
1540
+ if len(indices) >= E.nrows():
1541
+ break
1542
+ else:
1543
+ indices = indices[:-1]
1544
+ assert len(indices) == E.nrows()
1545
+ indices = tuple(reversed(indices))
1546
+ indicesn = (0,) + tuple(i for i in indices_nonzero if i not in indices)
1547
+ TE = E.matrix_from_columns(indices).inverse() * E
1548
+ return TE, indices, indicesn
1549
+
1550
+
1551
+ class _TransformMod(_TransformHrepresentation):
1552
+ r"""
1553
+ Prepare the substitutions coming from the moduli.
1554
+
1555
+ INPUT:
1556
+
1557
+ - ``inequalities`` -- list of tuples of numbers
1558
+
1559
+ - ``equations`` -- list of tuples of numbers
1560
+
1561
+ - ``B`` -- a Laurent polynomial ring
1562
+
1563
+ - ``mod`` -- dictionary mapping an index ``i`` to ``(m, r)``
1564
+
1565
+ This is one entry of the output tuple of :meth:`generate_mods`.
1566
+
1567
+ ATTRIBUTES:
1568
+
1569
+ - ``inequalities``, ``equations`` -- list of tuples
1570
+
1571
+ Determine the generating function of these inequalities
1572
+ and equations instead of the input.
1573
+
1574
+ - ``factor`` -- a Laurent polynomial
1575
+
1576
+ The numerator of the generating function has to be multiplied
1577
+ with ``factor`` *after* substituting ``rules``.
1578
+
1579
+ - ``rules`` -- dictionary mapping Laurent polynomial variables to
1580
+ Laurent polynomials
1581
+
1582
+ Substitute ``rules`` into the generating function.
1583
+
1584
+ The generating function of the input ``inequalities`` and
1585
+ ``equations`` is equal to the generating function of the
1586
+ attributes ``inequalities`` and ``equations`` in which ``rules``
1587
+ were substitited and ``factor`` was multiplied (via
1588
+ :meth:`~_TransformHrepresentation.apply_rules`).
1589
+
1590
+ EXAMPLES::
1591
+
1592
+ sage: from sage.geometry.polyhedron.generating_function import _TransformMod
1593
+
1594
+ sage: def prepare_mod(mod, B, *vecs):
1595
+ ....: inequalities, equations = vecs
1596
+ ....: T = _TransformMod(inequalities, equations, B, mod)
1597
+ ....: return T.factor, T.rules, T.inequalities, T.equations
1598
+
1599
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
1600
+ sage: prepare_mod({0: (2, 1)}, B, [(1, -1, 0, 2)], [])
1601
+ (y0, {y2: y2, y1: y1, y0: y0^2}, [(0, -2, 0, 2)], [])
1602
+ sage: prepare_mod({0: (2, 1), 1: (2, 1)}, B,
1603
+ ....: [(0, -1, -1, 2)], [(0, -1, 1, 0)])
1604
+ (y0*y1, {y2: y2, y1: y1^2, y0: y0^2},
1605
+ [(-2, -2, -2, 2)], [(0, -2, 2, 0)])
1606
+ """
1607
+
1608
+ def __init__(self, inequalities, equations, B, mod):
1609
+ r"""
1610
+ See :class:`_TransformMod` for details.
1611
+
1612
+ TESTS::
1613
+
1614
+ sage: from sage.geometry.polyhedron.generating_function import _TransformMod
1615
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1616
+ sage: _TransformMod([], [], B, {})
1617
+ <..._TransformMod object at 0x...>
1618
+ """
1619
+ self.mod = mod
1620
+ super().__init__(inequalities, equations, B)
1621
+
1622
+ def _transform_(self):
1623
+ r"""
1624
+ Do the actual transformation.
1625
+
1626
+ This is called during initialization.
1627
+ See :class:`_TransformMod` for details.
1628
+
1629
+ TESTS::
1630
+
1631
+ sage: from sage.geometry.polyhedron.generating_function import _TransformMod
1632
+ sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
1633
+ sage: T = _TransformMod([], [], B, {}) # indirect doctest
1634
+ """
1635
+ from sage.matrix.constructor import matrix
1636
+ from sage.modules.free_module_element import vector
1637
+ from sage.rings.integer_ring import ZZ
1638
+
1639
+ mod = self.mod
1640
+ B = self.B
1641
+
1642
+ if not mod:
1643
+ self.factor = 1
1644
+ self.rules = {}
1645
+
1646
+ n = len(B.gens()) + 1
1647
+
1648
+ D = {(i, i): 1 for i in range(n)}
1649
+ for i, mr in mod.items():
1650
+ D[(i+1, i+1)] = mr[0]
1651
+ D[(i+1, 0)] = mr[1]
1652
+ T = matrix(ZZ, n, n, D)
1653
+
1654
+ rules_pre = ((y, B({tuple(row[1:]): 1}))
1655
+ for y, row in zip((1,) + B.gens(), T.columns()))
1656
+ self.factor = next(rules_pre)[1]
1657
+ self.rules = dict(rules_pre)
1658
+
1659
+ self.inequalities = list(tuple(vector(e)*T) for e in self.inequalities)
1660
+ self.equations = list(tuple(vector(e)*T) for e in self.equations)
1661
+
1662
+ @staticmethod
1663
+ def generate_mods(equations):
1664
+ r"""
1665
+ Extract the moduli and residue classes implied
1666
+ by the equations.
1667
+
1668
+ INPUT:
1669
+
1670
+ - ``equations`` -- list of tuples
1671
+
1672
+ OUTPUT:
1673
+
1674
+ A tuple where each entry represents one possible configuration.
1675
+ Each entry is a dictionary mapping ``i`` to ``(m, r)`` with the following
1676
+ meaning: The ``i``-th coordinate of each element of the polyhedron
1677
+ has to be congruent to ``r`` modulo ``m``.
1678
+
1679
+ TESTS::
1680
+
1681
+ sage: from sage.geometry.polyhedron.generating_function import _TransformMod
1682
+ sage: _TransformMod.generate_mods([(0, 1, 1, -2)])
1683
+ ({0: (2, 0), 1: (2, 0)}, {0: (2, 1), 1: (2, 1)})
1684
+ """
1685
+ from sage.arith.functions import lcm
1686
+ from sage.matrix.constructor import matrix
1687
+ from sage.rings.integer_ring import ZZ
1688
+ from sage.rings.rational_field import QQ
1689
+
1690
+ TE, TEi, TEin = _EliminateByEquations.prepare_equations_transformation(matrix(equations))
1691
+ TEin = TEin[1:]
1692
+ if TE.base_ring() == ZZ:
1693
+ mods = [{}]
1694
+ elif TE.base_ring() == QQ:
1695
+ m = lcm([e.denominator() for e in TE.list()])
1696
+ if m == 1:
1697
+ mods = [{}]
1698
+ else:
1699
+ cols = TE.columns()
1700
+ assert all(cols[j][i] == 1 for i, j in enumerate(TEi))
1701
+ pre_mods = _compositions_mod((tuple(ZZ(cc*m) for cc in cols[i])
1702
+ for i in TEin),
1703
+ m, r=(-cc*m for cc in cols[0]),
1704
+ multidimensional=True)
1705
+ mods = tuple({i-1: (aa.modulus(), ZZ(aa))
1706
+ for i, aa in zip(TEin, a) if aa.modulus() > 1}
1707
+ for a in pre_mods)
1708
+ else:
1709
+ raise TypeError('equations over ZZ or QQ expected, but got '
1710
+ 'equations over {}.'.format(TE.base_ring()))
1711
+
1712
+ return mods
1713
+
1714
+
1715
+ def _compositions_mod(u, m, r=0, multidimensional=False):
1716
+ r"""
1717
+ Return an iterable of all tuples `a` such that `a u^T \equiv r \mod m`.
1718
+
1719
+ INPUT:
1720
+
1721
+ - ``m`` -- the modulus as a positive integer
1722
+
1723
+ - ``multidimensional`` -- boolean (default: ``False``)
1724
+
1725
+ If ``multidimensional=False``:
1726
+
1727
+ - ``u`` -- the coefficients as a tuple
1728
+
1729
+ - ``r`` -- (default: `0`)
1730
+ the remainder as a nonnegative integer
1731
+
1732
+ If ``multidimensional=True``:
1733
+
1734
+ - ``u`` -- the coefficients as a tuple of tuples (read column-wise)
1735
+
1736
+ - ``r`` -- (default: the zero vector)
1737
+ the remainder as a tuple of nonnegative integers
1738
+
1739
+ OUTPUT:
1740
+
1741
+ An iterable of tuples. All these tuples have the same size as ``u``.
1742
+ The elements of the tuples are integers modulo some appropriate
1743
+ modulus; this modulus divides `m` and is possibly different for each element.
1744
+
1745
+ EXAMPLES::
1746
+
1747
+ sage: from sage.geometry.polyhedron.generating_function import _compositions_mod
1748
+ sage: def show_cm(cm):
1749
+ ....: print(', '.join('({})'.format(
1750
+ ....: ', '.join('{}mod{}'.format(aa, aa.modulus())
1751
+ ....: for aa in a))
1752
+ ....: for a in cm))
1753
+
1754
+ sage: list(_compositions_mod([1, 1], 2))
1755
+ [(0, 0), (1, 1)]
1756
+ sage: show_cm(_compositions_mod([1, 1], 2))
1757
+ (0mod2, 0mod2), (1mod2, 1mod2)
1758
+ sage: show_cm(_compositions_mod([1, 2, 3], 6))
1759
+ (0mod6, 0mod3, 0mod2), (1mod6, 1mod3, 1mod2), (2mod6, 2mod3, 0mod2),
1760
+ (3mod6, 0mod3, 1mod2), (4mod6, 1mod3, 0mod2), (5mod6, 2mod3, 1mod2)
1761
+ sage: show_cm(_compositions_mod([2, 2, 2], 6))
1762
+ (0mod3, 0mod3, 0mod3), (0mod3, 1mod3, 2mod3), (0mod3, 2mod3, 1mod3),
1763
+ (1mod3, 0mod3, 2mod3), (1mod3, 1mod3, 1mod3), (1mod3, 2mod3, 0mod3),
1764
+ (2mod3, 0mod3, 1mod3), (2mod3, 1mod3, 0mod3), (2mod3, 2mod3, 2mod3)
1765
+
1766
+ ::
1767
+
1768
+ sage: show_cm(_compositions_mod([(1, 0), (0, 1)], 2,
1769
+ ....: multidimensional=True))
1770
+ (0mod2, 0mod2)
1771
+ sage: show_cm(_compositions_mod([(1, 2), (2, 2), (3, 2)], 6,
1772
+ ....: multidimensional=True))
1773
+ (0mod6, 0mod3, 0mod6), (1mod6, 1mod3, 1mod6), (2mod6, 2mod3, 2mod6),
1774
+ (3mod6, 0mod3, 3mod6), (4mod6, 1mod3, 4mod6), (5mod6, 2mod3, 5mod6)
1775
+
1776
+ TESTS::
1777
+
1778
+ sage: show_cm(_compositions_mod([1, 0], 2))
1779
+ (0mod2, 0mod1)
1780
+ """
1781
+ from sage.arith.functions import lcm
1782
+ from sage.arith.srange import srange
1783
+ from sage.modules.free_module_element import vector
1784
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
1785
+
1786
+ Z = Zmod(m)
1787
+ if not multidimensional:
1788
+ u = tuple(vector([Z(uu)]) for uu in u)
1789
+ r = vector([Z(r)])
1790
+ else:
1791
+ u = tuple(vector(Z(uuu) for uuu in uu) for uu in u)
1792
+ if r == 0:
1793
+ r = vector(Z(0) for _ in range(len(u[0])))
1794
+ else:
1795
+ r = vector(Z(rr) for rr in r)
1796
+
1797
+ def recursively_build_compositions(u, r):
1798
+ if not u:
1799
+ if all(rr == 0 for rr in r):
1800
+ yield ()
1801
+ return
1802
+
1803
+ v = u[0]
1804
+ m = lcm(vv.order() for vv in v)
1805
+ Z = Zmod(m)
1806
+ for j in srange(m):
1807
+ for a in recursively_build_compositions(u[1:], r - j*v):
1808
+ yield (Z(j),) + a
1809
+
1810
+ yield from recursively_build_compositions(u, r)