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