passagemath-linbox 10.6.32__cp313-cp313-manylinux_2_27_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-linbox might be problematic. Click here for more details.

Files changed (73) hide show
  1. passagemath_linbox-10.6.32.dist-info/METADATA +100 -0
  2. passagemath_linbox-10.6.32.dist-info/RECORD +73 -0
  3. passagemath_linbox-10.6.32.dist-info/WHEEL +6 -0
  4. passagemath_linbox-10.6.32.dist-info/top_level.txt +2 -0
  5. passagemath_linbox.libs/libfflas-d452d784.so.1.0.0 +0 -0
  6. passagemath_linbox.libs/libffpack-32579c9b.so.1.0.0 +0 -0
  7. passagemath_linbox.libs/libflint-66e12231.so.21.0.0 +0 -0
  8. passagemath_linbox.libs/libgd-76eb082b.so.3.0.11 +0 -0
  9. passagemath_linbox.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
  10. passagemath_linbox.libs/libgivaro-fc554fc9.so.9.2.1 +0 -0
  11. passagemath_linbox.libs/libgmp-6e109695.so.10.5.0 +0 -0
  12. passagemath_linbox.libs/libgmpxx-ecb9d6e3.so.4.7.0 +0 -0
  13. passagemath_linbox.libs/libiml-aeb1d147.so.0.1.1 +0 -0
  14. passagemath_linbox.libs/liblinbox-f1d24fc1.so.0.0.0 +0 -0
  15. passagemath_linbox.libs/libm4ri-9da2b874.so.1.0.0 +0 -0
  16. passagemath_linbox.libs/libm4rie-cf8cc058.so.1.0.0 +0 -0
  17. passagemath_linbox.libs/libmpfr-82690d50.so.6.2.1 +0 -0
  18. passagemath_linbox.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  19. passagemath_linbox.libs/libpng16-b4a91cd1.so.16.43.0 +0 -0
  20. passagemath_linbox.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  21. sage/all__sagemath_linbox.py +2 -0
  22. sage/geometry/all__sagemath_linbox.py +1 -0
  23. sage/geometry/integral_points.pxi +1426 -0
  24. sage/geometry/integral_points_integer_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  25. sage/geometry/integral_points_integer_dense.pyx +7 -0
  26. sage/libs/all__sagemath_linbox.py +1 -0
  27. sage/libs/iml.pxd +10 -0
  28. sage/libs/linbox/__init__.py +1 -0
  29. sage/libs/linbox/conversion.pxd +185 -0
  30. sage/libs/linbox/fflas.pxd +189 -0
  31. sage/libs/linbox/givaro.pxd +109 -0
  32. sage/libs/linbox/linbox.pxd +219 -0
  33. sage/libs/linbox/linbox_flint_interface.cpython-313-x86_64-linux-gnu.so +0 -0
  34. sage/libs/linbox/linbox_flint_interface.pxd +18 -0
  35. sage/libs/linbox/linbox_flint_interface.pyx +192 -0
  36. sage/libs/m4ri.pxd +198 -0
  37. sage/libs/m4rie.pxd +204 -0
  38. sage/matrix/all__sagemath_linbox.py +1 -0
  39. sage/matrix/matrix_cyclo_linbox.cpython-313-x86_64-linux-gnu.so +0 -0
  40. sage/matrix/matrix_cyclo_linbox.pyx +361 -0
  41. sage/matrix/matrix_gf2e_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  42. sage/matrix/matrix_gf2e_dense.pxd +15 -0
  43. sage/matrix/matrix_gf2e_dense.pyx +1573 -0
  44. sage/matrix/matrix_integer_iml.cpython-313-x86_64-linux-gnu.so +0 -0
  45. sage/matrix/matrix_integer_iml.pyx +316 -0
  46. sage/matrix/matrix_integer_linbox.cpython-313-x86_64-linux-gnu.so +0 -0
  47. sage/matrix/matrix_integer_linbox.pxd +5 -0
  48. sage/matrix/matrix_integer_linbox.pyx +358 -0
  49. sage/matrix/matrix_integer_sparse_linbox.cpython-313-x86_64-linux-gnu.so +0 -0
  50. sage/matrix/matrix_integer_sparse_linbox.pyx +465 -0
  51. sage/matrix/matrix_mod2_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  52. sage/matrix/matrix_mod2_dense.pxd +14 -0
  53. sage/matrix/matrix_mod2_dense.pyx +2789 -0
  54. sage/matrix/matrix_modn_dense_double.cpython-313-x86_64-linux-gnu.so +0 -0
  55. sage/matrix/matrix_modn_dense_double.pyx +179 -0
  56. sage/matrix/matrix_modn_dense_float.cpython-313-x86_64-linux-gnu.so +0 -0
  57. sage/matrix/matrix_modn_dense_float.pyx +154 -0
  58. sage/matrix/matrix_modn_sparse.cpython-313-x86_64-linux-gnu.so +0 -0
  59. sage/matrix/matrix_modn_sparse.pyx +871 -0
  60. sage/matrix/matrix_rational_linbox.cpython-313-x86_64-linux-gnu.so +0 -0
  61. sage/matrix/matrix_rational_linbox.pyx +36 -0
  62. sage/matrix/misc.cpython-313-x86_64-linux-gnu.so +0 -0
  63. sage/matrix/misc.pyx +418 -0
  64. sage/modules/all__sagemath_linbox.py +1 -0
  65. sage/modules/numpy_util.cpython-313-x86_64-linux-gnu.so +0 -0
  66. sage/modules/numpy_util.pxd +10 -0
  67. sage/modules/numpy_util.pyx +136 -0
  68. sage/modules/vector_mod2_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  69. sage/modules/vector_mod2_dense.pxd +11 -0
  70. sage/modules/vector_mod2_dense.pyx +547 -0
  71. sage/rings/all__sagemath_linbox.py +1 -0
  72. sage/rings/finite_rings/all__sagemath_linbox.py +1 -0
  73. sage/rings/polynomial/all__sagemath_linbox.py +1 -0
@@ -0,0 +1,1426 @@
1
+ # sage_setup: distribution = sagemath-linbox
2
+ # sage.doctest: needs sage.geometry.polyhedron
3
+ r"""
4
+ Cython helper methods to compute integral points in polyhedra.
5
+ """
6
+
7
+ # ***************************************************************************
8
+ # Copyright (C) 2010 Volker Braun <vbraun.name@gmail.com>
9
+ #
10
+ # This program is free software: you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation, either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ # https://www.gnu.org/licenses/
15
+ # ***************************************************************************
16
+
17
+ from cysignals.signals cimport sig_check
18
+ import copy
19
+ import itertools
20
+
21
+ from sage.matrix.constructor import matrix, column_matrix, vector, diagonal_matrix
22
+ from sage.rings.integer_ring import ZZ
23
+ from sage.rings.integer cimport Integer
24
+ from sage.arith.misc import gcd
25
+ from sage.arith.functions import lcm
26
+ from sage.misc.misc_c import prod
27
+ from sage.modules.free_module import FreeModule
28
+
29
+ ##############################################################################
30
+ # The basic idea to enumerate the lattice points in the parallelotope
31
+ # is to use the Smith normal form of the ray coordinate matrix to
32
+ # bring the lattice into a "nice" basis. Here is the straightforward
33
+ # implementation. Note that you do not need to reduce to the
34
+ # full-dimensional case, the Smith normal form takes care of that for
35
+ # you.
36
+ #
37
+ ## def parallelotope_points(spanning_points, lattice):
38
+ ## # compute points in the open parallelotope, see [BK2001]
39
+ ## R = matrix(spanning_points).transpose()
40
+ ## D,U,V = R.smith_form()
41
+ ## e = D.diagonal() # the elementary divisors
42
+ ## d = prod(e) # the determinant
43
+ ## u = U.inverse().columns() # generators for gp(semigroup)
44
+ ##
45
+ ## # "inverse" of the ray matrix as far as possible over ZZ
46
+ ## # R*Rinv == diagonal_matrix([d]*D.ncols() + [0]*(D.nrows()-D.ncols()))
47
+ ## # If R is full rank, this is Rinv = matrix(ZZ, R.inverse() * d)
48
+ ## Dinv = D.transpose()
49
+ ## for i in range(D.ncols()):
50
+ ## Dinv[i,i] = d/D[i,i]
51
+ ## Rinv = V * Dinv * U
52
+ ##
53
+ ## gens = []
54
+ ## for b in CartesianProduct(*[ range(i) for i in e ]):
55
+ ## # this is our generator modulo the lattice spanned by the rays
56
+ ## gen_mod_rays = sum( b_i*u_i for b_i, u_i in zip(b,u) )
57
+ ## q_times_d = Rinv * gen_mod_rays
58
+ ## q_times_d = vector(ZZ,[ q_i % d for q_i in q_times_d ])
59
+ ## gen = lattice(R*q_times_d / d)
60
+ ## gen.set_immutable()
61
+ ## gens.append(gen)
62
+ ## assert(len(gens) == d)
63
+ ## return tuple(gens)
64
+ #
65
+ # The problem with the naive implementation is that it is slow:
66
+ #
67
+ # 1. You can simplify some of the matrix multiplications
68
+ #
69
+ # 2. The inner loop keeps creating new matrices and vectors, which
70
+ # is slow. It needs to recycle objects. Instead of creating a new
71
+ # lattice point again and again, change the entries of an
72
+ # existing lattice point and then copy it!
73
+
74
+
75
+ cpdef tuple parallelotope_points(spanning_points, lattice):
76
+ r"""
77
+ Return integral points in the parallelotope starting at the origin
78
+ and spanned by the ``spanning_points``.
79
+
80
+ See :meth:`~ConvexRationalPolyhedralCone.semigroup_generators` for a description of the
81
+ algorithm.
82
+
83
+ INPUT:
84
+
85
+ - ``spanning_points`` -- a non-empty list of linearly independent
86
+ rays (`\ZZ`-vectors or :class:`toric lattice
87
+ <sage.geometry.toric_lattice.ToricLatticeFactory>` elements),
88
+ not necessarily primitive lattice points.
89
+
90
+ OUTPUT:
91
+
92
+ The tuple of all lattice points in the half-open parallelotope
93
+ spanned by the rays `r_i`,
94
+
95
+ .. MATH::
96
+
97
+ \mathop{par}(\{r_i\}) =
98
+ \sum_{0\leq a_i < 1} a_i r_i
99
+
100
+ By half-open parallelotope, we mean that the
101
+ points in the facets not meeting the origin are omitted.
102
+
103
+ EXAMPLES:
104
+
105
+ Note how the points on the outward-facing factes are omitted::
106
+
107
+ sage: from sage.geometry.integral_points import parallelotope_points
108
+ sage: rays = list(map(vector, [(2,0), (0,2)]))
109
+ sage: parallelotope_points(rays, ZZ^2)
110
+ ((0, 0), (0, 1), (1, 0), (1, 1))
111
+
112
+ The rays can also be toric lattice points::
113
+
114
+ sage: rays = list(map(ToricLattice(2), [(2,0), (0,2)]))
115
+ sage: parallelotope_points(rays, ToricLattice(2))
116
+ (N(0, 0), N(0, 1), N(1, 0), N(1, 1))
117
+
118
+ A non-smooth cone::
119
+
120
+ sage: c = Cone([ (1,0), (1,2) ])
121
+ sage: parallelotope_points(c.rays(), c.lattice())
122
+ (N(0, 0), N(1, 1))
123
+
124
+ A :exc:`ValueError` is raised if the ``spanning_points`` are not
125
+ linearly independent::
126
+
127
+ sage: rays = list(map(ToricLattice(2), [(1,1)]*2))
128
+ sage: parallelotope_points(rays, ToricLattice(2))
129
+ Traceback (most recent call last):
130
+ ...
131
+ ValueError: The spanning points are not linearly independent!
132
+
133
+ TESTS::
134
+
135
+ sage: rays = list(map(vector,[(-3, -2, -3, -2), (-2, -1, -8, 5), (1, 9, -7, -4), (-3, -1, -2, 2)]))
136
+ sage: len(parallelotope_points(rays, ZZ^4))
137
+ 967
138
+ """
139
+ cdef MatrixClass VDinv
140
+ cdef MatrixClass R = matrix(spanning_points).transpose()
141
+ e, d, VDinv = ray_matrix_normal_form(R)
142
+ cdef tuple points = loop_over_parallelotope_points(e, d, VDinv, R, lattice)
143
+ for p in points:
144
+ p.set_immutable()
145
+ return points
146
+
147
+
148
+ cpdef tuple ray_matrix_normal_form(R):
149
+ r"""
150
+ Compute the Smith normal form of the ray matrix for
151
+ :func:`parallelotope_points`.
152
+
153
+ INPUT:
154
+
155
+ - ``R`` -- `\ZZ`-matrix whose columns are the rays spanning the
156
+ parallelotope
157
+
158
+ OUTPUT: a tuple containing ``e``, ``d``, and ``VDinv``
159
+
160
+ EXAMPLES::
161
+
162
+ sage: from sage.geometry.integral_points import ray_matrix_normal_form
163
+ sage: R = column_matrix(ZZ,[3,3,3])
164
+ sage: ray_matrix_normal_form(R)
165
+ ([3], 3, [1])
166
+ """
167
+ D,U,V = R.smith_form()
168
+ e = D.diagonal() # the elementary divisors
169
+ cdef Integer d = prod(e) # the determinant
170
+ if d == ZZ.zero():
171
+ raise ValueError('The spanning points are not linearly independent!')
172
+ cdef int i
173
+ Dinv = diagonal_matrix(ZZ, [ d // e[i] for i in range(D.ncols()) ])
174
+ VDinv = V * Dinv
175
+ return (e, d, VDinv)
176
+
177
+
178
+ # The optimized version avoids constructing new matrices, vectors, and lattice points
179
+ cpdef tuple loop_over_parallelotope_points(e, d, MatrixClass VDinv,
180
+ MatrixClass R, lattice,
181
+ A=None, b=None):
182
+ r"""
183
+ The inner loop of :func:`parallelotope_points`.
184
+
185
+ INPUT:
186
+
187
+ See :meth:`parallelotope_points` for ``e``, ``d``, ``VDinv``, ``R``, ``lattice``.
188
+
189
+ - ``A``, ``b`` -- either both ``None`` or a vector and number. If
190
+ present, only the parallelotope points satisfying `A x \leq b`
191
+ are returned.
192
+
193
+ OUTPUT:
194
+
195
+ The points of the half-open parallelotope as a tuple of lattice
196
+ points.
197
+
198
+ EXAMPLES::
199
+
200
+ sage: e = [3]
201
+ sage: d = prod(e)
202
+ sage: VDinv = matrix(ZZ, [[1]])
203
+ sage: R = column_matrix(ZZ,[3,3,3])
204
+ sage: lattice = ZZ^3
205
+ sage: from sage.geometry.integral_points import loop_over_parallelotope_points
206
+ sage: loop_over_parallelotope_points(e, d, VDinv, R, lattice)
207
+ ((0, 0, 0), (1, 1, 1), (2, 2, 2))
208
+
209
+ sage: A = vector(ZZ, [1,0,0])
210
+ sage: b = 1
211
+ sage: loop_over_parallelotope_points(e, d, VDinv, R, lattice, A, b)
212
+ ((0, 0, 0), (1, 1, 1))
213
+ """
214
+ cdef int i, j
215
+ cdef int dim = VDinv.nrows()
216
+ cdef int ambient_dim = R.nrows()
217
+ s = ZZ.zero() # summation variable
218
+ cdef list gens = []
219
+ gen = lattice(ZZ.zero())
220
+ cdef VectorClass q_times_d = vector(ZZ, dim)
221
+ for base in itertools.product(*[ range(i) for i in e ]):
222
+ for i in range(dim):
223
+ s = ZZ.zero()
224
+ for j in range(dim):
225
+ s += VDinv.get_unsafe(i,j) * base[j]
226
+ q_times_d.set_unsafe(i, s % d)
227
+ for i in range(ambient_dim):
228
+ s = ZZ.zero()
229
+ for j in range(dim):
230
+ s += R.get_unsafe(i,j) * q_times_d.get_unsafe(j)
231
+ gen[i] = s / d
232
+ if A is not None:
233
+ s = ZZ.zero()
234
+ for i in range(ambient_dim):
235
+ s += A[i] * gen[i]
236
+ if s > b:
237
+ continue
238
+ gens.append(copy.copy(gen))
239
+ if A is None:
240
+ assert(len(gens) == d)
241
+ return tuple(gens)
242
+
243
+
244
+ ##############################################################################
245
+ cpdef tuple simplex_points(vertices):
246
+ r"""
247
+ Return the integral points in a lattice simplex.
248
+
249
+ INPUT:
250
+
251
+ - ``vertices`` -- an iterable of integer coordinate vectors. The
252
+ indices of vertices that span the simplex under
253
+ consideration.
254
+
255
+ OUTPUT:
256
+
257
+ A tuple containing the integral point coordinates as `\ZZ`-vectors.
258
+
259
+ EXAMPLES::
260
+
261
+ sage: from sage.geometry.integral_points import simplex_points
262
+ sage: simplex_points([(1,2,3), (2,3,7), (-2,-3,-11)])
263
+ ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
264
+
265
+ The simplex need not be full-dimensional::
266
+
267
+ sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)])
268
+ sage: simplex_points(simplex.Vrepresentation())
269
+ ((2, 3, 7, 5), (0, 0, -2, 5), (-2, -3, -11, 5), (1, 2, 3, 5))
270
+
271
+ sage: simplex_points([(2,3,7)])
272
+ ((2, 3, 7),)
273
+
274
+ TESTS::
275
+
276
+ sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
277
+ sage: simplex = Polyhedron(v); simplex
278
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
279
+ sage: pts = simplex_points(simplex.Vrepresentation())
280
+ sage: len(pts)
281
+ 49
282
+ sage: for p in pts: p.set_immutable()
283
+ sage: len(set(pts))
284
+ 49
285
+
286
+ sage: all(simplex.contains(p) for p in pts)
287
+ True
288
+
289
+ sage: v = [(4,-1,-1,-1), (-1,4,-1,-1), (-1,-1,4,-1), (-1,-1,-1,4), (-1,-1,-1,-1)]
290
+ sage: P4mirror = Polyhedron(v); P4mirror
291
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
292
+ sage: len(simplex_points(P4mirror.Vrepresentation()))
293
+ 126
294
+
295
+ sage: vertices = list(map(vector, [(1,2,3), (2,3,7), (-2,-3,-11)]))
296
+ sage: for v in vertices: v.set_immutable()
297
+ sage: simplex_points(vertices)
298
+ ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
299
+ """
300
+ cdef list rays = [vector(ZZ, list(v)) for v in vertices]
301
+ if not rays:
302
+ return ()
303
+ cdef VectorClass origin = rays.pop()
304
+ origin.set_immutable()
305
+ if not rays:
306
+ return (origin,)
307
+ translate_points(rays, origin)
308
+
309
+ # Find equation Ax<=b that cuts out simplex from parallelotope
310
+ cdef MatrixClass Rt = matrix(ZZ, rays)
311
+ cdef MatrixClass R = Rt.transpose()
312
+ cdef int nrays = len(rays)
313
+ cdef Integer b
314
+ M = FreeModule(ZZ, nrays)
315
+ if R.is_square():
316
+ b = abs(R.det())
317
+ A = R.solve_left(M([b]*nrays))
318
+ else:
319
+ RtR = Rt * R
320
+ b = abs(RtR.det())
321
+ A = RtR.solve_left(M([b]*nrays)) * Rt
322
+
323
+ e, d, VDinv = ray_matrix_normal_form(R)
324
+ lattice = origin.parent()
325
+ points = loop_over_parallelotope_points(e, d, VDinv, R, lattice, A, b) + tuple(rays)
326
+ translate_points(points, -origin)
327
+ return points
328
+
329
+
330
+ cdef translate_points(v_list, VectorClass delta):
331
+ r"""
332
+ Add ``delta`` to each vector in ``v_list``.
333
+ """
334
+ cdef int dim = delta.degree()
335
+ cdef int i
336
+ for v in v_list:
337
+ for i in range(dim):
338
+ v[i] -= delta.get_unsafe(i)
339
+
340
+
341
+ ##############################################################################
342
+ # For points with "small" coordinates (that is, fitting into a small
343
+ # rectangular bounding box) it is faster to naively enumerate the
344
+ # points. This saves the overhead of triangulating the polytope etc.
345
+
346
+ cpdef rectangular_box_points(list box_min, list box_max,
347
+ polyhedron=None, count_only=False,
348
+ return_saturated=False):
349
+ r"""
350
+ Return the integral points in the lattice bounding box that are
351
+ also contained in the given polyhedron.
352
+
353
+ INPUT:
354
+
355
+ - ``box_min`` -- list of integers; the minimal value for each
356
+ coordinate of the rectangular bounding box
357
+
358
+ - ``box_max`` -- list of integers; the maximal value for each
359
+ coordinate of the rectangular bounding box
360
+
361
+ - ``polyhedron`` -- a
362
+ :class:`~sage.geometry.polyhedron.base.Polyhedron_base`, a PPL
363
+ :class:`~ppl.polyhedron.C_Polyhedron`, or ``None`` (default)
364
+
365
+ - ``count_only`` -- boolean (default: ``False``); whether to
366
+ return only the total number of vertices, and not their
367
+ coordinates. Enabling this option speeds up the
368
+ enumeration. Cannot be combined with the ``return_saturated``
369
+ option.
370
+
371
+ - ``return_saturated`` -- boolean (default: ``False``); whether to
372
+ also return which inequalities are saturated for each point of
373
+ the polyhedron. Enabling this slows down the enumeration. Cannot
374
+ be combined with the ``count_only`` option.
375
+
376
+ OUTPUT:
377
+
378
+ By default, this function returns a tuple containing the integral
379
+ points of the rectangular box spanned by ``box_min`` and ``box_max``
380
+ and that lie inside the ``polyhedron``. For sufficiently large
381
+ bounding boxes, this are all integral points of the polyhedron.
382
+
383
+ If no polyhedron is specified, all integral points of the
384
+ rectangular box are returned.
385
+
386
+ If ``count_only`` is specified, only the total number (an integer)
387
+ of found lattice points is returned.
388
+
389
+ If ``return_saturated`` is enabled, then for each integral point a
390
+ pair ``(point, Hrep)`` is returned where ``point`` is the point
391
+ and ``Hrep`` is the set of indices of the H-representation objects
392
+ that are saturated at the point.
393
+
394
+ ALGORITHM:
395
+
396
+ This function implements the naive algorithm towards counting
397
+ integral points. Given min and max of vertex coordinates, it
398
+ iterates over all points in the bounding box and checks whether
399
+ they lie in the polyhedron. The following optimizations are
400
+ implemented:
401
+
402
+ * Cython: Use machine integers and optimizing C/C++ compiler
403
+ where possible, arbitrary precision integers where necessary.
404
+ Bounds checking, no compile time limits.
405
+
406
+ * Unwind inner loop (and next-to-inner loop):
407
+
408
+ .. MATH::
409
+
410
+ Ax \leq b
411
+ \quad \Leftrightarrow \quad
412
+ a_1 x_1 ~\leq~ b - \sum_{i=2}^d a_i x_i
413
+
414
+ so we only have to evaluate `a_1 * x_1` in the inner loop.
415
+
416
+ * Coordinates are permuted to make the longest box edge the
417
+ inner loop. The inner loop is optimized to run very fast, so
418
+ its best to do as much work as possible there.
419
+
420
+ * Continuously reorder inequalities and test the most
421
+ restrictive inequalities first.
422
+
423
+ * Use convexity and only find first and last allowed point in
424
+ the inner loop. The points in-between must be points of the
425
+ polyhedron, too.
426
+
427
+ EXAMPLES::
428
+
429
+ sage: from sage.geometry.integral_points import rectangular_box_points
430
+ sage: rectangular_box_points([0,0,0],[1,2,3])
431
+ ((0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3),
432
+ (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3),
433
+ (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 2, 3),
434
+ (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3),
435
+ (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 1, 3),
436
+ (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 2, 3))
437
+
438
+ sage: from sage.geometry.integral_points import rectangular_box_points
439
+ sage: rectangular_box_points([0,0,0],[1,2,3], count_only=True)
440
+ 24
441
+
442
+ sage: cell24 = polytopes.twenty_four_cell()
443
+ sage: rectangular_box_points([-1]*4, [1]*4, cell24)
444
+ ((-1, 0, 0, 0), (0, -1, 0, 0), (0, 0, -1, 0), (0, 0, 0, -1),
445
+ (0, 0, 0, 0),
446
+ (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (1, 0, 0, 0))
447
+ sage: d = 3
448
+ sage: dilated_cell24 = d*cell24
449
+ sage: len( rectangular_box_points([-d]*4, [d]*4, dilated_cell24) )
450
+ 305
451
+
452
+ sage: d = 6
453
+ sage: dilated_cell24 = d*cell24
454
+ sage: len( rectangular_box_points([-d]*4, [d]*4, dilated_cell24) )
455
+ 3625
456
+
457
+ sage: rectangular_box_points([-d]*4, [d]*4, dilated_cell24, count_only=True)
458
+ 3625
459
+
460
+ sage: polytope = Polyhedron([(-4,-3,-2,-1),(3,1,1,1),(1,2,1,1),(1,1,3,0),(1,3,2,4)])
461
+ sage: pts = rectangular_box_points([-4]*4, [4]*4, polytope); pts
462
+ ((-4, -3, -2, -1), (-1, 0, 0, 1), (0, 1, 1, 1), (1, 1, 1, 1), (1, 1, 3, 0),
463
+ (1, 2, 1, 1), (1, 2, 2, 2), (1, 3, 2, 4), (2, 1, 1, 1), (3, 1, 1, 1))
464
+ sage: all(polytope.contains(p) for p in pts)
465
+ True
466
+
467
+ sage: set(map(tuple,pts)) == \
468
+ ....: set([(-4,-3,-2,-1),(3,1,1,1),(1,2,1,1),(1,1,3,0),(1,3,2,4),
469
+ ....: (0,1,1,1),(1,2,2,2),(-1,0,0,1),(1,1,1,1),(2,1,1,1)]) # computed with PALP
470
+ True
471
+
472
+ Long ints and non-integral polyhedra are explicitly allowed::
473
+
474
+ sage: polytope = Polyhedron([[1], [10*pi.n()]], base_ring=RDF) # needs sage.symbolic
475
+ sage: len(rectangular_box_points([-100], [100], polytope)) # needs sage.symbolic
476
+ 31
477
+
478
+ sage: halfplane = Polyhedron(ieqs=[(-1,1,0)])
479
+ sage: rectangular_box_points([0,-1+10^50], [0,1+10^50])
480
+ ((0, 99999999999999999999999999999999999999999999999999),
481
+ (0, 100000000000000000000000000000000000000000000000000),
482
+ (0, 100000000000000000000000000000000000000000000000001))
483
+ sage: len( rectangular_box_points([0,-100+10^50], [1,100+10^50], halfplane) )
484
+ 201
485
+
486
+ Using a PPL polyhedron::
487
+
488
+ sage: # needs pplpy
489
+ sage: from ppl import Variable, Generator_System, C_Polyhedron, point
490
+ sage: gs = Generator_System()
491
+ sage: x = Variable(0); y = Variable(1); z = Variable(2)
492
+ sage: gs.insert(point(0*x + 1*y + 0*z))
493
+ sage: gs.insert(point(0*x + 1*y + 3*z))
494
+ sage: gs.insert(point(3*x + 1*y + 0*z))
495
+ sage: gs.insert(point(3*x + 1*y + 3*z))
496
+ sage: poly = C_Polyhedron(gs)
497
+ sage: rectangular_box_points([0]*3, [3]*3, poly)
498
+ ((0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 1, 3),
499
+ (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3), (3, 1, 0), (3, 1, 1), (3, 1, 2), (3, 1, 3))
500
+
501
+ Optionally, return the information about the saturated inequalities as well::
502
+
503
+ sage: cube = polytopes.cube()
504
+ sage: cube.Hrepresentation(0)
505
+ An inequality (-1, 0, 0) x + 1 >= 0
506
+ sage: cube.Hrepresentation(1)
507
+ An inequality (0, -1, 0) x + 1 >= 0
508
+ sage: cube.Hrepresentation(2)
509
+ An inequality (0, 0, -1) x + 1 >= 0
510
+ sage: rectangular_box_points([0]*3, [1]*3, cube, return_saturated=True)
511
+ (((0, 0, 0), frozenset()),
512
+ ((0, 0, 1), frozenset({2})),
513
+ ((0, 1, 0), frozenset({1})),
514
+ ((0, 1, 1), frozenset({1, 2})),
515
+ ((1, 0, 0), frozenset({0})),
516
+ ((1, 0, 1), frozenset({0, 2})),
517
+ ((1, 1, 0), frozenset({0, 1})),
518
+ ((1, 1, 1), frozenset({0, 1, 2})))
519
+
520
+ TESTS:
521
+
522
+ Check that this can be interrupted, see :issue:`20781`::
523
+
524
+ sage: ieqs = [(-1, -1, -1, -1, -1, -1, -1, -1, -1),
525
+ ....: (0, -1, 0, 0, 0, 0, 0, 0, 0),
526
+ ....: (0, -1, 0, 2, -1, 0, 0, 0, 0),
527
+ ....: (0, 0, -1, -1, 2, -1, 0, 0, 0),
528
+ ....: (0, 2, 0, -1, 0, 0, 0, 0, 0),
529
+ ....: (0, 0, 0, 0, 0, 0, 0, -1, 2),
530
+ ....: (1, 0, 2, 0, -1, 0, 0, 0, 0),
531
+ ....: (0, 0, 0, 0, -1, 2, -1, 0, 0),
532
+ ....: (0, 0, 0, 0, 0, 0, 0, 0, -1),
533
+ ....: (0, 0, 0, 0, 0, -1, 2, -1, 0),
534
+ ....: (0, 0, 0, 0, 0, 0, -1, 2, -1)]
535
+ sage: P = Polyhedron(ieqs=ieqs)
536
+ sage: from sage.doctest.util import ensure_interruptible_after
537
+ sage: with ensure_interruptible_after(0.5): P.integral_points()
538
+ """
539
+ assert len(box_min) == len(box_max)
540
+ assert not (count_only and return_saturated)
541
+ cdef int d = len(box_min)
542
+ cdef int i, j
543
+ cdef list diameter = sorted([(box_max[i]-box_min[i], i) for i in range(d)],
544
+ reverse=True)
545
+ cdef list diameter_value = [x[0] for x in diameter]
546
+ cdef list diameter_index = [x[1] for x in diameter]
547
+
548
+ # Construct the inverse permutation
549
+ cdef list orig_perm = list(range(len(diameter_index)))
550
+ for i, j in enumerate(diameter_index):
551
+ orig_perm[j] = i
552
+
553
+ box_min = perm_action(diameter_index, box_min)
554
+ box_max = perm_action(diameter_index, box_max)
555
+ cdef InequalityCollection inequalities = InequalityCollection(polyhedron, diameter_index, box_min, box_max)
556
+
557
+ if count_only:
558
+ return loop_over_rectangular_box_points(box_min, box_max, inequalities, d, count_only)
559
+
560
+ cdef list points = []
561
+ cdef VectorClass v = vector(ZZ, d)
562
+ if not return_saturated:
563
+ for p in loop_over_rectangular_box_points(box_min, box_max, inequalities, d, count_only):
564
+ # v = vector(ZZ, perm_action(orig_perm, p)) # too slow
565
+ for i in range(d):
566
+ v.set_unsafe(i, Integer(p[orig_perm[i]]))
567
+ v_copy = copy.copy(v)
568
+ v_copy.set_immutable()
569
+ points.append(v_copy)
570
+ else:
571
+ for p, saturated in loop_over_rectangular_box_points_saturated(box_min, box_max,
572
+ inequalities, d):
573
+ for i in range(d):
574
+ v.set_unsafe(i, Integer(p[orig_perm[i]]))
575
+ v_copy = copy.copy(v)
576
+ v_copy.set_immutable()
577
+ points.append( (v_copy, saturated) )
578
+
579
+ return tuple(points)
580
+
581
+ cdef list perm_action(list p, list lst):
582
+ """
583
+ Return the action of a permutation ``p`` of `(0, ..., n-1)`
584
+ on a list of length `n`.
585
+ """
586
+ return [lst[i] for i in p]
587
+
588
+ cdef loop_over_rectangular_box_points(list box_min, list box_max,
589
+ InequalityCollection inequalities,
590
+ int d, bint count_only):
591
+ """
592
+ The inner loop of :func:`rectangular_box_points`.
593
+
594
+ INPUT:
595
+
596
+ - ``box_min``, ``box_max`` -- the bounding box
597
+
598
+ - ``inequalities`` -- a :class:`InequalityCollection` containing
599
+ the inequalities defining the polyhedron
600
+
601
+ - ``d`` -- the ambient space dimension
602
+
603
+ - ``count_only`` -- whether to only return the total number of
604
+ lattice points
605
+
606
+ OUTPUT: the integral points in the bounding box satisfying all
607
+ inequalities
608
+ """
609
+ cdef int inc
610
+ cdef Integer i_min, i_max
611
+ if count_only:
612
+ points = 0
613
+ else:
614
+ points = []
615
+ cdef list p = list(box_min)
616
+ inequalities.prepare_next_to_inner_loop(p)
617
+ while True:
618
+ sig_check()
619
+ inequalities.prepare_inner_loop(p)
620
+ i_min = box_min[0]
621
+ i_max = box_max[0]
622
+ # Find the lower bound for the allowed region
623
+ while i_min <= i_max:
624
+ if inequalities.are_satisfied(i_min):
625
+ break
626
+ i_min += 1
627
+ # Find the upper bound for the allowed region
628
+ while i_min <= i_max:
629
+ if inequalities.are_satisfied(i_max):
630
+ break
631
+ i_max -= 1
632
+ # The points i_min .. i_max are contained in the polyhedron
633
+ if count_only:
634
+ if i_max >= i_min:
635
+ points += i_max - i_min + 1
636
+ else:
637
+ i = i_min
638
+ while i <= i_max:
639
+ p[0] = i
640
+ points.append(tuple(p))
641
+ i += 1
642
+ # finally increment the other entries in p to move on to next inner loop
643
+ inc = 1
644
+ if d == 1:
645
+ return points
646
+ while True:
647
+ if p[inc] == box_max[inc]:
648
+ p[inc] = box_min[inc]
649
+ inc += 1
650
+ if inc == d:
651
+ return points
652
+ else:
653
+ p[inc] += 1
654
+ break
655
+ if inc > 1:
656
+ inequalities.prepare_next_to_inner_loop(p)
657
+
658
+
659
+ cdef loop_over_rectangular_box_points_saturated(list box_min, list box_max,
660
+ InequalityCollection inequalities,
661
+ int d):
662
+ """
663
+ The analog of :func:`rectangular_box_points` except that it keeps
664
+ track of which inequalities are saturated.
665
+
666
+ INPUT:
667
+
668
+ See :func:`rectangular_box_points`.
669
+
670
+ OUTPUT:
671
+
672
+ The integral points in the bounding box satisfying all
673
+ inequalities, each point being returned by a coordinate vector and
674
+ a set of H-representation object indices.
675
+ """
676
+ cdef int inc
677
+ cdef list points = []
678
+ cdef list p = list(box_min)
679
+ inequalities.prepare_next_to_inner_loop(p)
680
+ while True:
681
+ inequalities.prepare_inner_loop(p)
682
+ i_min = box_min[0]
683
+ i_max = box_max[0]
684
+ # Find the lower bound for the allowed region
685
+ while i_min <= i_max:
686
+ if inequalities.are_satisfied(i_min):
687
+ break
688
+ i_min += 1
689
+ # Find the upper bound for the allowed region
690
+ while i_min <= i_max:
691
+ if inequalities.are_satisfied(i_max):
692
+ break
693
+ i_max -= 1
694
+ # The points i_min .. i_max are contained in the polyhedron
695
+ i = i_min
696
+ while i <= i_max:
697
+ p[0] = i
698
+ saturated = inequalities.satisfied_as_equalities(i)
699
+ points.append( (tuple(p), saturated) )
700
+ i += 1
701
+ # finally increment the other entries in p to move on to next inner loop
702
+ inc = 1
703
+ if d == 1:
704
+ return points
705
+ while True:
706
+ if p[inc] == box_max[inc]:
707
+ p[inc] = box_min[inc]
708
+ inc += 1
709
+ if inc == d:
710
+ return points
711
+ else:
712
+ p[inc] += 1
713
+ break
714
+ if inc > 1:
715
+ inequalities.prepare_next_to_inner_loop(p)
716
+
717
+
718
+ cdef class Inequality_generic:
719
+ r"""
720
+ An inequality whose coefficients are arbitrary Python/Sage objects
721
+
722
+ INPUT:
723
+
724
+ - ``A`` -- list of coefficients
725
+
726
+ - ``b`` -- element
727
+
728
+ OUTPUT: inequality `A x + b \geq 0`
729
+
730
+ EXAMPLES::
731
+
732
+ sage: from sage.geometry.integral_points import Inequality_generic
733
+ sage: Inequality_generic([2 * pi, sqrt(3), 7/2], -5.5) # needs sage.symbolic
734
+ generic: (2*pi, sqrt(3), 7/2) x + -5.50000000000000 >= 0
735
+ """
736
+
737
+ cdef list A
738
+ cdef object b
739
+ cdef object coeff
740
+ cdef object cache
741
+ # The index of the inequality in the polyhedron H-representation
742
+ cdef int index
743
+
744
+ def __cinit__(self, list A, b, int index=-1):
745
+ """
746
+ The Cython constructor.
747
+
748
+ INPUT:
749
+
750
+ See :class:`Inequality_generic`.
751
+
752
+ EXAMPLES::
753
+
754
+ sage: from sage.geometry.integral_points import Inequality_generic
755
+ sage: Inequality_generic([2 * pi, sqrt(3), 7/2], -5.5) # needs sage.symbolic
756
+ generic: (2*pi, sqrt(3), 7/2) x + -5.50000000000000 >= 0
757
+ """
758
+ self.A = A
759
+ self.b = b
760
+ self.coeff = 0
761
+ self.cache = 0
762
+ self.index = int(index)
763
+
764
+ def __repr__(self):
765
+ """
766
+ Return a string representation.
767
+
768
+ OUTPUT: string
769
+
770
+ EXAMPLES::
771
+
772
+ sage: from sage.geometry.integral_points import Inequality_generic
773
+ sage: Inequality_generic([2,3,7], -5).__repr__()
774
+ 'generic: (2, 3, 7) x + -5 >= 0'
775
+ """
776
+ s = 'generic: ('
777
+ s += ', '.join(str(self.A[i]) for i in range(len(self.A)))
778
+ s += ') x + ' + str(self.b) + ' >= 0'
779
+ return s
780
+
781
+ cdef prepare_next_to_inner_loop(self, p):
782
+ """
783
+ In :class:`Inequality_int` this method is used to peel of the
784
+ next-to-inner loop.
785
+
786
+ See :meth:`InequalityCollection.prepare_inner_loop` for more details.
787
+ """
788
+ pass
789
+
790
+ cdef prepare_inner_loop(self, p):
791
+ """
792
+ Peel off the inner loop.
793
+
794
+ See :meth:`InequalityCollection.prepare_inner_loop` for more details.
795
+ """
796
+ cdef int j
797
+ self.coeff = self.A[0]
798
+ self.cache = self.b
799
+ for j in range(1, len(self.A)):
800
+ self.cache += self.A[j] * p[j]
801
+
802
+ cdef bint is_not_satisfied(self, inner_loop_variable) except -1:
803
+ r"""
804
+ Test the inequality, using the cached value from :meth:`prepare_inner_loop`
805
+
806
+ OUTPUT: boolean; whether the inequality is not satisfied
807
+ """
808
+ return inner_loop_variable * self.coeff + self.cache < 0
809
+
810
+ cdef bint is_equality(Inequality_generic self, int inner_loop_variable) except -1:
811
+ r"""
812
+ Test the inequality, using the cached value from :meth:`prepare_inner_loop`
813
+
814
+ OUTPUT:
815
+
816
+ boolean. Given the inequality `Ax + b \geq 0`, this method
817
+ returns whether the equality `Ax + b = 0` is satisfied.
818
+ """
819
+ return inner_loop_variable * self.coeff + self.cache == 0
820
+
821
+
822
+ # if dim>20 then we always use the generic inequalities (Inequality_generic)
823
+ DEF INEQ_INT_MAX_DIM = 20
824
+
825
+ cdef class Inequality_int:
826
+ r"""
827
+ Fast version of inequality in the case that all coefficients fit
828
+ into machine ints.
829
+
830
+ INPUT:
831
+
832
+ - ``A`` -- list of integers
833
+
834
+ - ``b`` -- integer
835
+
836
+ - ``max_abs_coordinates`` -- the maximum of the coordinates that
837
+ one wants to evaluate the coordinates on; used for overflow
838
+ checking
839
+
840
+ OUTPUT:
841
+
842
+ Inequality `A x + b \geq 0`. A :exc:`OverflowError` is raised if a
843
+ machine integer is not long enough to hold the results. A
844
+ :exc:`ValueError` is raised if some of the input is not integral.
845
+
846
+ EXAMPLES::
847
+
848
+ sage: from sage.geometry.integral_points import Inequality_int
849
+ sage: Inequality_int([2,3,7], -5, [10]*3)
850
+ integer: (2, 3, 7) x + -5 >= 0
851
+
852
+ sage: Inequality_int([1]*21, -5, [10]*21)
853
+ Traceback (most recent call last):
854
+ ...
855
+ OverflowError: Dimension limit exceeded.
856
+
857
+ sage: Inequality_int([2,3/2,7], -5, [10]*3)
858
+ Traceback (most recent call last):
859
+ ...
860
+ ValueError: Not integral.
861
+
862
+ sage: Inequality_int([2,3,7], -5.2, [10]*3)
863
+ Traceback (most recent call last):
864
+ ...
865
+ ValueError: Not integral.
866
+
867
+ sage: Inequality_int([2,3,7], -5*10^50, [10]*3) # actual error message can differ between 32 and 64 bit
868
+ Traceback (most recent call last):
869
+ ...
870
+ OverflowError: ...
871
+
872
+ TESTS:
873
+
874
+ Check that :issue:`21993` is fixed::
875
+
876
+ sage: Inequality_int([18560500, -89466500], 108027, [178933, 37121])
877
+ Traceback (most recent call last):
878
+ ...
879
+ OverflowError: ...
880
+ """
881
+ cdef int A[INEQ_INT_MAX_DIM]
882
+ cdef int b
883
+ cdef int dim
884
+ # the innermost coefficient
885
+ cdef int coeff
886
+ cdef int cache
887
+ # the next-to-innermost coefficient
888
+ cdef int coeff_next
889
+ cdef int cache_next
890
+ # The index of the inequality in the polyhedron H-representation
891
+ cdef int index
892
+
893
+ cdef int _to_int(self, x) except? -999:
894
+ if x not in ZZ:
895
+ raise ValueError('Not integral.')
896
+ return int(x) # raises OverflowError in Cython if necessary
897
+
898
+ def __cinit__(self, list A, b, list max_abs_coordinates, int index=-1):
899
+ """
900
+ The Cython constructor.
901
+
902
+ See :class:`Inequality_int` for input.
903
+
904
+ EXAMPLES::
905
+
906
+ sage: from sage.geometry.integral_points import Inequality_int
907
+ sage: Inequality_int([2,3,7], -5, [10]*3)
908
+ integer: (2, 3, 7) x + -5 >= 0
909
+ """
910
+ cdef int i
911
+ self.dim = self._to_int(len(A))
912
+ self.index = int(index)
913
+ if self.dim < 1 or self.dim > INEQ_INT_MAX_DIM:
914
+ raise OverflowError('Dimension limit exceeded.')
915
+ for i in range(self.dim):
916
+ self.A[i] = self._to_int(A[i])
917
+ self.b = self._to_int(b)
918
+ self.coeff = self.A[0]
919
+ if self.dim > 0:
920
+ self.coeff_next = self.A[1]
921
+ # finally, make sure that there cannot be any overflow during the enumeration
922
+ self._to_int(abs(ZZ(b)) + sum( abs(ZZ(A[i])) * ZZ(max_abs_coordinates[i])
923
+ for i in range(self.dim) ))
924
+
925
+ def __repr__(self):
926
+ """
927
+ Return a string representation.
928
+
929
+ OUTPUT: string
930
+
931
+ EXAMPLES::
932
+
933
+ sage: from sage.geometry.integral_points import Inequality_int
934
+ sage: Inequality_int([2,3,7], -5, [10]*3).__repr__()
935
+ 'integer: (2, 3, 7) x + -5 >= 0'
936
+ """
937
+ s = 'integer: ('
938
+ s += ', '.join(str(self.A[i]) for i in range(self.dim))
939
+ s += ') x + ' + str(self.b) + ' >= 0'
940
+ return s
941
+
942
+ cdef prepare_next_to_inner_loop(Inequality_int self, p):
943
+ """
944
+ Peel off the next-to-inner loop.
945
+
946
+ See :meth:`InequalityCollection.prepare_inner_loop` for more details.
947
+ """
948
+ cdef int j
949
+ self.cache_next = self.b
950
+ for j in range(2, self.dim):
951
+ self.cache_next += self.A[j] * p[j]
952
+
953
+ cdef prepare_inner_loop(Inequality_int self, p):
954
+ """
955
+ Peel off the inner loop.
956
+
957
+ See :meth:`InequalityCollection.prepare_inner_loop` for more details.
958
+ """
959
+ cdef int j
960
+ if self.dim > 1:
961
+ self.cache = self.cache_next + self.coeff_next * p[1]
962
+ else:
963
+ self.cache = self.cache_next
964
+
965
+ cdef bint is_not_satisfied(Inequality_int self, int inner_loop_variable) noexcept:
966
+ return inner_loop_variable * self.coeff + self.cache < 0
967
+
968
+ cdef bint is_equality(Inequality_int self, int inner_loop_variable) noexcept:
969
+ return inner_loop_variable * self.coeff + self.cache == 0
970
+
971
+
972
+ cdef class InequalityCollection:
973
+ """
974
+ A collection of inequalities.
975
+
976
+ INPUT:
977
+
978
+ - ``polyhedron`` -- a polyhedron defining the inequalities
979
+
980
+ - ``permutation`` -- list; a 0-based permutation of the coordinates
981
+ Will be used to permute the coordinates of the inequality
982
+
983
+ - ``box_min``, ``box_max`` -- the (not permuted) minimal and maximal
984
+ coordinates of the bounding box; used for bounds checking
985
+
986
+ EXAMPLES::
987
+
988
+ sage: from sage.geometry.integral_points import InequalityCollection
989
+ sage: P_QQ = Polyhedron(identity_matrix(3).columns() + [(-2, -1,-1)], base_ring=QQ)
990
+ sage: ieq = InequalityCollection(P_QQ, [0,1,2], [0]*3,[1]*3); ieq
991
+ The collection of inequalities
992
+ integer: (3, -2, -2) x + 2 >= 0
993
+ integer: (-1, 4, -1) x + 1 >= 0
994
+ integer: (-1, -1, 4) x + 1 >= 0
995
+ integer: (-1, -1, -1) x + 1 >= 0
996
+
997
+ sage: P_RR = Polyhedron(identity_matrix(2).columns() + [(-2.7, -1)], base_ring=RDF)
998
+ sage: InequalityCollection(P_RR, [0,1], [0]*2, [1]*2)
999
+ The collection of inequalities
1000
+ integer: (-1, -1) x + 1 >= 0
1001
+ generic: (-1.0, 3.7) x + 1.0 >= 0
1002
+ generic: (1.0, -1.35) x + 1.35 >= 0
1003
+
1004
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1005
+ sage: InequalityCollection(line, [0,1], [0]*2, [1]*2 )
1006
+ The collection of inequalities
1007
+ integer: (3, 7) x + 2 >= 0
1008
+ integer: (-3, -7) x + -2 >= 0
1009
+
1010
+ TESTS::
1011
+
1012
+ sage: TestSuite(ieq).run(skip='_test_pickling')
1013
+ """
1014
+ cdef list ineqs_int
1015
+ cdef list ineqs_generic
1016
+
1017
+ def __repr__(self):
1018
+ r"""
1019
+ Return a string representation.
1020
+
1021
+ OUTPUT: string
1022
+
1023
+ EXAMPLES::
1024
+
1025
+ sage: from sage.geometry.integral_points import InequalityCollection
1026
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1027
+ sage: InequalityCollection(line, [0,1], [0]*2, [1]*2 ).__repr__()
1028
+ 'The collection of inequalities\ninteger: (3, 7) x + 2 >= 0\ninteger: (-3, -7) x + -2 >= 0'
1029
+ """
1030
+ s = 'The collection of inequalities\n'
1031
+ for ineq in self.ineqs_int:
1032
+ s += str(<Inequality_int>ineq) + '\n'
1033
+ for ineq in self.ineqs_generic:
1034
+ s += str(<Inequality_generic>ineq) + '\n'
1035
+ return s.strip()
1036
+
1037
+ cpdef tuple _make_A_b(self, Hrep_obj, list permutation):
1038
+ r"""
1039
+ Return the coefficients and constant of the H-representation
1040
+ object.
1041
+
1042
+ INPUT:
1043
+
1044
+ - ``Hrep_obj`` -- a H-representation object of the polyhedron
1045
+ - ``permutation`` -- the permutation of the coordinates to
1046
+ apply to ``A``
1047
+
1048
+ OUTPUT:
1049
+
1050
+ A pair ``(A,b)``.
1051
+
1052
+ EXAMPLES::
1053
+
1054
+ sage: from sage.geometry.integral_points import InequalityCollection
1055
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1056
+ sage: ieq = InequalityCollection(line, [0,1], [0]*2, [1]*2 )
1057
+ sage: ieq._make_A_b(line.Hrepresentation(0), [0,1])
1058
+ ([3, 7], 2)
1059
+ sage: ieq._make_A_b(line.Hrepresentation(0), [1,0])
1060
+ ([7, 3], 2)
1061
+ """
1062
+ cdef list v = list(Hrep_obj)
1063
+ cdef list A = perm_action(permutation, v[1:])
1064
+ b = v[0]
1065
+ try:
1066
+ x = lcm([a.denominator() for a in A] + [b.denominator()])
1067
+ A = [a * x for a in A]
1068
+ b = b * x
1069
+ except AttributeError:
1070
+ pass
1071
+ return (A, b)
1072
+
1073
+ def __cinit__(self, polyhedron, list permutation, box_min, box_max):
1074
+ """
1075
+ The Cython constructor.
1076
+
1077
+ See the class documentation for the description of the arguments.
1078
+
1079
+ EXAMPLES::
1080
+
1081
+ sage: from sage.geometry.integral_points import InequalityCollection
1082
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1083
+ sage: InequalityCollection(line, [0,1], [0]*2, [1]*2 )
1084
+ The collection of inequalities
1085
+ integer: (3, 7) x + 2 >= 0
1086
+ integer: (-3, -7) x + -2 >= 0
1087
+ """
1088
+ cdef list max_abs_coordinates = [max(abs(c_min), abs(c_max))
1089
+ for c_min, c_max in zip(box_min, box_max)]
1090
+ max_abs_coordinates = perm_action(permutation, max_abs_coordinates)
1091
+ self.ineqs_int = []
1092
+ self.ineqs_generic = []
1093
+ if polyhedron is None:
1094
+ return
1095
+
1096
+ try:
1097
+ # polyhedron is a PPL C_Polyhedron class?
1098
+ self._cinit_from_PPL(max_abs_coordinates, permutation, polyhedron)
1099
+ except AttributeError:
1100
+ try:
1101
+ # polyhedron is a Polyhedron class?
1102
+ self._cinit_from_Polyhedron(max_abs_coordinates, permutation, polyhedron)
1103
+ except AttributeError:
1104
+ raise TypeError('Cannot extract Hrepresentation data from polyhedron.')
1105
+
1106
+ cdef _cinit_from_PPL(self, list max_abs_coordinates, list permutation,
1107
+ polyhedron):
1108
+ """
1109
+ Initialize the inequalities from a PPL C_Polyhedron.
1110
+
1111
+ See __cinit__ for a description of the arguments.
1112
+
1113
+ EXAMPLES::
1114
+
1115
+ sage: # needs pplpy
1116
+ sage: from ppl import Variable, Generator_System, C_Polyhedron, point
1117
+ sage: gs = Generator_System()
1118
+ sage: x = Variable(0); y = Variable(1); z = Variable(2)
1119
+ sage: gs.insert(point(0*x + 0*y + 1*z))
1120
+ sage: gs.insert(point(0*x + 3*y + 1*z))
1121
+ sage: gs.insert(point(3*x + 0*y + 1*z))
1122
+ sage: gs.insert(point(3*x + 3*y + 1*z))
1123
+ sage: poly = C_Polyhedron(gs)
1124
+ sage: from sage.geometry.integral_points import InequalityCollection
1125
+ sage: InequalityCollection(poly, [0,2,1], [0]*3, [3]*3 )
1126
+ The collection of inequalities
1127
+ integer: (0, 1, 0) x + -1 >= 0
1128
+ integer: (0, -1, 0) x + 1 >= 0
1129
+ integer: (1, 0, 0) x + 0 >= 0
1130
+ integer: (0, 0, 1) x + 0 >= 0
1131
+ integer: (-1, 0, 0) x + 3 >= 0
1132
+ integer: (0, 0, -1) x + 3 >= 0
1133
+ """
1134
+ cdef list A
1135
+ cdef int index
1136
+ for index,c in enumerate(polyhedron.minimized_constraints()):
1137
+ A = perm_action(permutation, [Integer(mpz) for mpz in c.coefficients()])
1138
+ b = Integer(c.inhomogeneous_term())
1139
+ try:
1140
+ H = Inequality_int(A, b, max_abs_coordinates, index)
1141
+ self.ineqs_int.append(H)
1142
+ except (OverflowError, ValueError):
1143
+ H = Inequality_generic(A, b, index)
1144
+ self.ineqs_generic.append(H)
1145
+ if c.is_equality():
1146
+ A = [ -a for a in A ]
1147
+ b = -b
1148
+ try:
1149
+ H = Inequality_int(A, b, max_abs_coordinates, index)
1150
+ self.ineqs_int.append(H)
1151
+ except (OverflowError, ValueError):
1152
+ H = Inequality_generic(A, b, index)
1153
+ self.ineqs_generic.append(H)
1154
+
1155
+ cdef _cinit_from_Polyhedron(self, list max_abs_coordinates,
1156
+ list permutation, polyhedron):
1157
+ """
1158
+ Initialize the inequalities from a Sage Polyhedron.
1159
+
1160
+ See __cinit__ for a description of the arguments.
1161
+
1162
+ EXAMPLES::
1163
+
1164
+ sage: from sage.geometry.integral_points import InequalityCollection
1165
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1166
+ sage: InequalityCollection(line, [0,1], [0]*2, [1]*2 )
1167
+ The collection of inequalities
1168
+ integer: (3, 7) x + 2 >= 0
1169
+ integer: (-3, -7) x + -2 >= 0
1170
+
1171
+ TESTS:
1172
+
1173
+ Check that :issue:`21037` is fixed::
1174
+
1175
+ sage: P = Polyhedron(vertices=((0, 0), (17,3)))
1176
+ sage: P += 1/1000*polytopes.regular_polygon(5) # needs sage.rings.number_field
1177
+ sage: P.integral_points() # needs sage.rings.number_field
1178
+ ((0, 0), (17, 3))
1179
+ """
1180
+ cdef list A
1181
+ for Hrep_obj in polyhedron.inequality_generator():
1182
+ A, b = self._make_A_b(Hrep_obj, permutation)
1183
+ try:
1184
+ H = Inequality_int(A, b, max_abs_coordinates, Hrep_obj.index())
1185
+ self.ineqs_int.append(H)
1186
+ except (OverflowError, ValueError, TypeError):
1187
+ H = Inequality_generic(A, b, Hrep_obj.index())
1188
+ self.ineqs_generic.append(H)
1189
+ for Hrep_obj in polyhedron.equation_generator():
1190
+ A, b = self._make_A_b(Hrep_obj, permutation)
1191
+ # add inequality
1192
+ try:
1193
+ H = Inequality_int(A, b, max_abs_coordinates, Hrep_obj.index())
1194
+ self.ineqs_int.append(H)
1195
+ except (OverflowError, ValueError, TypeError):
1196
+ H = Inequality_generic(A, b, Hrep_obj.index())
1197
+ self.ineqs_generic.append(H)
1198
+ # add sign-reversed inequality
1199
+ A = [ -a for a in A ]
1200
+ b = -b
1201
+ try:
1202
+ H = Inequality_int(A, b, max_abs_coordinates, Hrep_obj.index())
1203
+ self.ineqs_int.append(H)
1204
+ except (OverflowError, ValueError, TypeError):
1205
+ H = Inequality_generic(A, b, Hrep_obj.index())
1206
+ self.ineqs_generic.append(H)
1207
+
1208
+ cpdef prepare_next_to_inner_loop(self, p):
1209
+ r"""
1210
+ Peel off the next-to-inner loop.
1211
+
1212
+ In the next-to-inner loop of :func:`rectangular_box_points`,
1213
+ we have to repeatedly evaluate `A x-A_0 x_0+b`. To speed up
1214
+ computation, we pre-evaluate
1215
+
1216
+ .. MATH::
1217
+
1218
+ c = b + \sum_{i=2} A_i x_i
1219
+
1220
+ and only compute `A x-A_0 x_0+b = A_1 x_1 +c \geq 0` in the
1221
+ next-to-inner loop.
1222
+
1223
+ INPUT:
1224
+
1225
+ - ``p`` -- the point coordinates. Only ``p[2:]`` coordinates
1226
+ are potentially used by this method
1227
+
1228
+ EXAMPLES::
1229
+
1230
+ sage: from sage.geometry.integral_points import InequalityCollection, print_cache
1231
+ sage: P = Polyhedron(ieqs=[(2,3,7,11)])
1232
+ sage: ieq = InequalityCollection(P, [0,1,2], [0]*3,[1]*3); ieq
1233
+ The collection of inequalities
1234
+ integer: (3, 7, 11) x + 2 >= 0
1235
+ sage: ieq.prepare_next_to_inner_loop([2,1,3])
1236
+ sage: ieq.prepare_inner_loop([2,1,3])
1237
+ sage: print_cache(ieq)
1238
+ Cached inner loop: 3 * x_0 + 42 >= 0
1239
+ Cached next-to-inner loop: 3 * x_0 + 7 * x_1 + 35 >= 0
1240
+ """
1241
+ for ineq in self.ineqs_int:
1242
+ (<Inequality_int>ineq).prepare_next_to_inner_loop(p)
1243
+ for ineq in self.ineqs_generic:
1244
+ (<Inequality_generic>ineq).prepare_next_to_inner_loop(p)
1245
+
1246
+ cpdef prepare_inner_loop(self, p):
1247
+ r"""
1248
+ Peel off the inner loop.
1249
+
1250
+ In the inner loop of :func:`rectangular_box_points`, we have
1251
+ to repeatedly evaluate `A x+b\geq 0`. To speed up computation, we pre-evaluate
1252
+
1253
+ .. MATH::
1254
+
1255
+ c = A x - A_0 x_0 +b = b + \sum_{i=1} A_i x_i
1256
+
1257
+ and only test `A_0 x_0 +c \geq 0` in the inner loop.
1258
+
1259
+ You must call :meth:`prepare_next_to_inner_loop` before
1260
+ calling this method.
1261
+
1262
+ INPUT:
1263
+
1264
+ - ``p`` -- the coordinates of the point to loop over. Only the
1265
+ ``p[1:]`` entries are used
1266
+
1267
+ EXAMPLES::
1268
+
1269
+ sage: from sage.geometry.integral_points import InequalityCollection, print_cache
1270
+ sage: P = Polyhedron(ieqs=[(2,3,7,11)])
1271
+ sage: ieq = InequalityCollection(P, [0,1,2], [0]*3,[1]*3); ieq
1272
+ The collection of inequalities
1273
+ integer: (3, 7, 11) x + 2 >= 0
1274
+ sage: ieq.prepare_next_to_inner_loop([2,1,3])
1275
+ sage: ieq.prepare_inner_loop([2,1,3])
1276
+ sage: print_cache(ieq)
1277
+ Cached inner loop: 3 * x_0 + 42 >= 0
1278
+ Cached next-to-inner loop: 3 * x_0 + 7 * x_1 + 35 >= 0
1279
+ """
1280
+ for ineq in self.ineqs_int:
1281
+ (<Inequality_int>ineq).prepare_inner_loop(p)
1282
+ for ineq in self.ineqs_generic:
1283
+ (<Inequality_generic>ineq).prepare_inner_loop(p)
1284
+
1285
+ cpdef swap_ineq_to_front(self, int i):
1286
+ r"""
1287
+ Swap the ``i``-th entry of the list to the front of the list of inequalities.
1288
+
1289
+ INPUT:
1290
+
1291
+ - ``i`` -- integer; the :class:`Inequality_int` to swap to the
1292
+ beginning of the list of integral inequalities
1293
+
1294
+ EXAMPLES::
1295
+
1296
+ sage: from sage.geometry.integral_points import InequalityCollection
1297
+ sage: P_QQ = Polyhedron(identity_matrix(3).columns() + [(-2, -1,-1)], base_ring=QQ)
1298
+ sage: iec = InequalityCollection(P_QQ, [0,1,2], [0]*3,[1]*3)
1299
+ sage: iec
1300
+ The collection of inequalities
1301
+ integer: (3, -2, -2) x + 2 >= 0
1302
+ integer: (-1, 4, -1) x + 1 >= 0
1303
+ integer: (-1, -1, 4) x + 1 >= 0
1304
+ integer: (-1, -1, -1) x + 1 >= 0
1305
+ sage: iec.swap_ineq_to_front(3)
1306
+ sage: iec
1307
+ The collection of inequalities
1308
+ integer: (-1, -1, -1) x + 1 >= 0
1309
+ integer: (3, -2, -2) x + 2 >= 0
1310
+ integer: (-1, 4, -1) x + 1 >= 0
1311
+ integer: (-1, -1, 4) x + 1 >= 0
1312
+ """
1313
+ i_th_entry = self.ineqs_int[i]
1314
+ cdef int j
1315
+ for j in range(i-1,-1,-1):
1316
+ self.ineqs_int[j+1] = self.ineqs_int[j]
1317
+ self.ineqs_int[0] = i_th_entry
1318
+
1319
+ cpdef bint are_satisfied(self, inner_loop_variable) except -1:
1320
+ r"""
1321
+ Return whether all inequalities are satisfied.
1322
+
1323
+ You must call :meth:`prepare_inner_loop` before calling this
1324
+ method.
1325
+
1326
+ INPUT:
1327
+
1328
+ - ``inner_loop_variable`` -- integer; the 0th coordinate of
1329
+ the lattice point
1330
+
1331
+ OUTPUT: boolean; whether the lattice point is in the polyhedron
1332
+
1333
+ EXAMPLES::
1334
+
1335
+ sage: from sage.geometry.integral_points import InequalityCollection
1336
+ sage: line = Polyhedron(eqns=[(2,3,7)])
1337
+ sage: ieq = InequalityCollection(line, [0,1], [0]*2, [1]*2 )
1338
+ sage: ieq.prepare_next_to_inner_loop([3,4])
1339
+ sage: ieq.prepare_inner_loop([3,4])
1340
+ sage: ieq.are_satisfied(3)
1341
+ False
1342
+ """
1343
+ cdef int i
1344
+ for i in range(len(self.ineqs_int)):
1345
+ sig_check()
1346
+ ineq = self.ineqs_int[i]
1347
+ if (<Inequality_int>ineq).is_not_satisfied(inner_loop_variable):
1348
+ if i > 0:
1349
+ self.swap_ineq_to_front(i)
1350
+ return False
1351
+ for i in range(len(self.ineqs_generic)):
1352
+ sig_check()
1353
+ ineq = self.ineqs_generic[i]
1354
+ if (<Inequality_generic>ineq).is_not_satisfied(inner_loop_variable):
1355
+ return False
1356
+ return True
1357
+
1358
+ cpdef frozenset satisfied_as_equalities(self, inner_loop_variable):
1359
+ """
1360
+ Return the inequalities (by their index) that are satisfied as
1361
+ equalities.
1362
+
1363
+ INPUT:
1364
+
1365
+ - ``inner_loop_variable`` -- integer; the 0th coordinate of
1366
+ the lattice point
1367
+
1368
+ OUTPUT:
1369
+
1370
+ A set of integers in ascending order. Each integer is the
1371
+ index of a H-representation object of the polyhedron (either a
1372
+ inequality or an equation).
1373
+
1374
+ EXAMPLES::
1375
+
1376
+ sage: from sage.geometry.integral_points import InequalityCollection
1377
+ sage: quadrant = Polyhedron(rays=[(1,0), (0,1)])
1378
+ sage: ieqs = InequalityCollection(quadrant, [0,1], [-1]*2, [1]*2 )
1379
+ sage: ieqs.prepare_next_to_inner_loop([-1,0])
1380
+ sage: ieqs.prepare_inner_loop([-1,0])
1381
+ sage: ieqs.satisfied_as_equalities(-1)
1382
+ frozenset({1})
1383
+ sage: ieqs.satisfied_as_equalities(0)
1384
+ frozenset({0, 1})
1385
+ sage: ieqs.satisfied_as_equalities(1)
1386
+ frozenset({1})
1387
+ """
1388
+ cdef int i
1389
+ cdef list result = []
1390
+ for i in range(len(self.ineqs_int)):
1391
+ sig_check()
1392
+ ineq = self.ineqs_int[i]
1393
+ if (<Inequality_int>ineq).is_equality(inner_loop_variable):
1394
+ result.append( (<Inequality_int>ineq).index )
1395
+ for i in range(len(self.ineqs_generic)):
1396
+ sig_check()
1397
+ ineq = self.ineqs_generic[i]
1398
+ if (<Inequality_generic>ineq).is_equality(inner_loop_variable):
1399
+ result.append( (<Inequality_generic>ineq).index )
1400
+ return frozenset(result)
1401
+
1402
+
1403
+ cpdef print_cache(InequalityCollection inequality_collection):
1404
+ r"""
1405
+ Print the cached values in :class:`Inequality_int` (for
1406
+ debugging/doctesting only).
1407
+
1408
+ EXAMPLES::
1409
+
1410
+ sage: from sage.geometry.integral_points import InequalityCollection, print_cache
1411
+ sage: P = Polyhedron(ieqs=[(2,3,7)])
1412
+ sage: ieq = InequalityCollection(P, [0,1], [0]*2,[1]*2); ieq
1413
+ The collection of inequalities
1414
+ integer: (3, 7) x + 2 >= 0
1415
+ sage: ieq.prepare_next_to_inner_loop([3,5])
1416
+ sage: ieq.prepare_inner_loop([3,5])
1417
+ sage: print_cache(ieq)
1418
+ Cached inner loop: 3 * x_0 + 37 >= 0
1419
+ Cached next-to-inner loop: 3 * x_0 + 7 * x_1 + 2 >= 0
1420
+ """
1421
+ cdef Inequality_int ieq = <Inequality_int>(inequality_collection.ineqs_int[0])
1422
+ print('Cached inner loop: ' +
1423
+ str(ieq.coeff) + ' * x_0 + ' + str(ieq.cache) + ' >= 0')
1424
+ print('Cached next-to-inner loop: ' +
1425
+ str(ieq.coeff) + ' * x_0 + ' +
1426
+ str(ieq.coeff_next) + ' * x_1 + ' + str(ieq.cache_next) + ' >= 0')