Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/polyhedron/ppl_lattice_polygon.py
8817 views
1
"""
2
Fast Lattice Polygons using PPL.
3
4
See :mod:`ppl_lattice_polytope` for the implementation of
5
arbitrary-dimensional lattice polytopes. This module is about the
6
specialization to 2 dimensions. To be more precise, the
7
:class:`LatticePolygon_PPL_class` is used if the ambient space is of
8
dimension 2 or less. These all allow you to cyclically order (see
9
:meth:`LatticePolygon_PPL_class.ordered_vertices`) the vertices, which
10
is in general not possible in higher dimensions.
11
"""
12
13
########################################################################
14
# Copyright (C) 2012 Volker Braun <[email protected]>
15
#
16
# Distributed under the terms of the GNU General Public License (GPL)
17
#
18
# http://www.gnu.org/licenses/
19
########################################################################
20
21
from sage.rings.integer_ring import ZZ
22
from sage.misc.all import cached_method, cached_function
23
from sage.modules.all import (vector, zero_vector)
24
from sage.matrix.constructor import (matrix, zero_matrix, block_matrix)
25
from sage.libs.ppl import (C_Polyhedron, Generator_System_iterator,
26
Poly_Con_Relation)
27
from sage.geometry.polyhedron.lattice_euclidean_group_element import (
28
LatticeEuclideanGroupElement)
29
from sage.geometry.polyhedron.ppl_lattice_polytope import (
30
LatticePolytope_PPL, LatticePolytope_PPL_class)
31
32
33
########################################################################
34
class LatticePolygon_PPL_class(LatticePolytope_PPL_class):
35
"""
36
A lattice polygon
37
38
This includes 2-dimensional polytopes as well as degenerate (0 and
39
1-dimensional) lattice polygons. Any polytope in 2d is a polygon.
40
"""
41
42
@cached_method
43
def ordered_vertices(self):
44
"""
45
Return the vertices of a lattice polygon in cyclic order.
46
47
OUTPUT:
48
49
A tuple of vertices ordered along the perimeter of the
50
polygon. The first point is arbitrary.
51
52
EXAMPLES::
53
54
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
55
sage: square = LatticePolytope_PPL((0,0), (1,1), (0,1), (1,0))
56
sage: square.vertices()
57
((0, 0), (0, 1), (1, 0), (1, 1))
58
sage: square.ordered_vertices()
59
((0, 0), (1, 0), (1, 1), (0, 1))
60
"""
61
neighbors = dict()
62
if self.affine_dimension() < 2:
63
return self.vertices()
64
for c in self.minimized_constraints():
65
v1, v2 = self.vertices_saturating(c)
66
neighbors[v1] = [v2] + neighbors.get(v1, [])
67
neighbors[v2] = [v1] + neighbors.get(v2, [])
68
v_prev = self.vertices()[0]
69
v_curr = neighbors[v_prev][0]
70
result = [v_prev, v_curr]
71
while len(result) < self.n_vertices():
72
v1, v2 = neighbors[v_curr]
73
if v1 == v_prev:
74
v_next = v2
75
else:
76
v_next = v1
77
result.append(v_next)
78
v_prev = v_curr
79
v_curr = v_next
80
return tuple(result)
81
82
def _find_isomorphism_degenerate(self, polytope):
83
"""
84
Helper to pick an isomorphism of degenerate polygons
85
86
INPUT:
87
88
- ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The
89
polytope to compare with.
90
91
EXAMPLES::
92
93
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron
94
sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty'))
95
sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty'))
96
sage: iso = L1.find_isomorphism(L2) # indirect doctest
97
sage: iso(L1) == L2
98
True
99
sage: iso = L1._find_isomorphism_degenerate(L2)
100
sage: iso(L1) == L2
101
True
102
103
sage: L1 = LatticePolytope_PPL((-1,4))
104
sage: L2 = LatticePolytope_PPL((2,1,5))
105
sage: iso = L1.find_isomorphism(L2)
106
sage: iso(L1) == L2
107
True
108
109
sage: L1 = LatticePolytope_PPL((-1,), (3,))
110
sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
111
sage: iso = L1.find_isomorphism(L2)
112
sage: iso(L1) == L2
113
True
114
115
sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1))
116
sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
117
sage: iso = L1.find_isomorphism(L2)
118
sage: iso(L1) == L2
119
True
120
121
sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
122
sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
123
sage: iso = L1.find_isomorphism(L2)
124
sage: iso(L1) == L2
125
True
126
127
sage: L1 = LatticePolytope_PPL((-1,2), (3,2))
128
sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
129
sage: L1.find_isomorphism(L2)
130
Traceback (most recent call last):
131
...
132
LatticePolytopesNotIsomorphicError: different number of integral points
133
134
sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
135
sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5))
136
sage: L1.find_isomorphism(L2)
137
Traceback (most recent call last):
138
...
139
LatticePolytopesNotIsomorphicError: different number of integral points
140
"""
141
from sage.geometry.polyhedron.lattice_euclidean_group_element import \
142
LatticePolytopesNotIsomorphicError
143
polytope_vertices = polytope.vertices()
144
self_vertices = self.ordered_vertices()
145
# handle degenerate cases
146
if self.n_vertices() == 0:
147
A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension())
148
b = zero_vector(ZZ, polytope.space_dimension())
149
return LatticeEuclideanGroupElement(A, b)
150
if self.n_vertices() == 1:
151
A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension())
152
b = polytope_vertices[0]
153
return LatticeEuclideanGroupElement(A, b)
154
if self.n_vertices() == 2:
155
self_origin = self_vertices[0]
156
self_ray = self_vertices[1] - self_origin
157
polytope_origin = polytope_vertices[0]
158
polytope_ray = polytope_vertices[1] - polytope_origin
159
Ds, Us, Vs = self_ray.column().smith_form()
160
Dp, Up, Vp = polytope_ray.column().smith_form()
161
assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1
162
assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1
163
A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows())
164
A[0, 0] = 1
165
A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0])
166
b = polytope_origin - A*self_origin
167
try:
168
A = matrix(ZZ, A)
169
b = vector(ZZ, b)
170
except TypeError:
171
raise LatticePolytopesNotIsomorphicError('different lattice')
172
hom = LatticeEuclideanGroupElement(A, b)
173
if hom(self) == polytope:
174
return hom
175
raise LatticePolytopesNotIsomorphicError('different polygons')
176
177
def _find_cyclic_isomorphism_matching_edge(self, polytope,
178
polytope_origin, p_ray_left,
179
p_ray_right):
180
"""
181
Helper to find an isomorphism of polygons
182
183
INPUT:
184
185
- ``polytope`` -- the lattice polytope to compare to.
186
187
- ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope``
188
189
- ``p_ray_left`` - vector. the vector from ``polytope_origin``
190
to one of its neighboring vertices.
191
192
- ``p_ray_right`` - vector. the vector from
193
``polytope_origin`` to the other neighboring vertices.
194
195
OUTPUT:
196
197
The element of the lattice Euclidean group that maps ``self``
198
to ``polytope`` with given origin and left/right neighboring
199
vertex. A
200
:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`
201
is raised if no such isomorphism exists.
202
203
EXAMPLES::
204
205
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
206
sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))
207
sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))
208
sage: v0, v1, v2 = L2.vertices()
209
sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0)
210
The map A*x+b with A=
211
[ 0 1]
212
[-1 -1]
213
[ 1 3]
214
b =
215
(0, 1, 0)
216
"""
217
from sage.geometry.polyhedron.lattice_euclidean_group_element import \
218
LatticePolytopesNotIsomorphicError
219
polytope_matrix = block_matrix(1, 2, [p_ray_left.column(),
220
p_ray_right.column()])
221
self_vertices = self.ordered_vertices()
222
for i in range(len(self_vertices)):
223
# three consecutive vertices
224
v_left = self_vertices[(i+0) % len(self_vertices)]
225
v_origin = self_vertices[(i+1) % len(self_vertices)]
226
v_right = self_vertices[(i+2) % len(self_vertices)]
227
r_left = v_left-v_origin
228
r_right = v_right-v_origin
229
self_matrix = block_matrix(1, 2, [r_left.column(),
230
r_right.column()])
231
A = self_matrix.solve_left(polytope_matrix)
232
b = polytope_origin - A*v_origin
233
try:
234
A = matrix(ZZ, A)
235
b = vector(ZZ, b)
236
except TypeError:
237
continue
238
if A.elementary_divisors()[0:2] != [1, 1]:
239
continue
240
hom = LatticeEuclideanGroupElement(A, b)
241
if hom(self) == polytope:
242
return hom
243
raise LatticePolytopesNotIsomorphicError('different polygons')
244
245
def find_isomorphism(self, polytope):
246
"""
247
Return a lattice isomorphism with ``polytope``.
248
249
INPUT:
250
251
- ``polytope`` -- a polytope, potentially higher-dimensional.
252
253
OUTPUT:
254
255
A
256
:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticeEuclideanGroupElement`. It
257
is not necessarily invertible if the affine dimension of
258
``self`` or ``polytope`` is not two. A
259
:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`
260
is raised if no such isomorphism exists.
261
262
EXAMPLES::
263
264
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
265
sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))
266
sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))
267
sage: iso = L1.find_isomorphism(L2)
268
sage: iso(L1) == L2
269
True
270
271
sage: L1 = LatticePolytope_PPL((0, 1), (3, 0), (0, 3), (1, 0))
272
sage: L2 = LatticePolytope_PPL((0,0,2,1),(0,1,2,0),(2,0,0,3),(2,3,0,0))
273
sage: iso = L1.find_isomorphism(L2)
274
sage: iso(L1) == L2
275
True
276
277
The following polygons are isomorphic over `\QQ`, but not as
278
lattice polytopes::
279
280
sage: L1 = LatticePolytope_PPL((1,0),(0,1),(-1,-1))
281
sage: L2 = LatticePolytope_PPL((0, 0), (0, 1), (1, 0))
282
sage: L1.find_isomorphism(L2)
283
Traceback (most recent call last):
284
...
285
LatticePolytopesNotIsomorphicError: different number of integral points
286
sage: L2.find_isomorphism(L1)
287
Traceback (most recent call last):
288
...
289
LatticePolytopesNotIsomorphicError: different number of integral points
290
"""
291
from sage.geometry.polyhedron.lattice_euclidean_group_element import \
292
LatticePolytopesNotIsomorphicError
293
if polytope.affine_dimension() != self.affine_dimension():
294
raise LatticePolytopesNotIsomorphicError('different dimension')
295
polytope_vertices = polytope.vertices()
296
if len(polytope_vertices) != self.n_vertices():
297
raise LatticePolytopesNotIsomorphicError('different number of vertices')
298
self_vertices = self.ordered_vertices()
299
if len(polytope.integral_points()) != len(self.integral_points()):
300
raise LatticePolytopesNotIsomorphicError('different number of integral points')
301
302
if len(self_vertices) < 3:
303
return self._find_isomorphism_degenerate(polytope)
304
305
polytope_origin = polytope_vertices[0]
306
origin_P = C_Polyhedron(Generator_System_iterator(
307
polytope.minimized_generators()).next())
308
309
neighbors = []
310
for c in polytope.minimized_constraints():
311
if not c.is_inequality():
312
continue
313
if origin_P.relation_with(c).implies(Poly_Con_Relation.saturates()):
314
for i, g in enumerate(polytope.minimized_generators()):
315
if i == 0:
316
continue
317
g = C_Polyhedron(g)
318
if g.relation_with(c).implies(Poly_Con_Relation.saturates()):
319
neighbors.append(polytope_vertices[i])
320
break
321
322
p_ray_left = neighbors[0] - polytope_origin
323
p_ray_right = neighbors[1] - polytope_origin
324
try:
325
return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin,
326
p_ray_left, p_ray_right)
327
except LatticePolytopesNotIsomorphicError:
328
pass
329
try:
330
return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin,
331
p_ray_right, p_ray_left)
332
except LatticePolytopesNotIsomorphicError:
333
pass
334
raise LatticePolytopesNotIsomorphicError('different polygons')
335
336
def is_isomorphic(self, polytope):
337
"""
338
Test if ``self`` and ``polytope`` are isomorphic.
339
340
INPUT:
341
342
- ``polytope`` -- a lattice polytope.
343
344
OUTPUT:
345
346
Boolean.
347
348
EXAMPLES::
349
350
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
351
sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))
352
sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))
353
sage: L1.is_isomorphic(L2)
354
True
355
"""
356
from sage.geometry.polyhedron.lattice_euclidean_group_element import \
357
LatticePolytopesNotIsomorphicError
358
try:
359
self.find_isomorphism(polytope)
360
return True
361
except LatticePolytopesNotIsomorphicError:
362
return False
363
364
def sub_polytopes(self):
365
"""
366
Returns a list of all lattice sub-polygons up to isomorphsm.
367
368
OUTPUT:
369
370
All non-empty sub-lattice polytopes up to isomorphism. This
371
includes ``self`` as improper sub-polytope, but excludes the
372
empty polytope. Isomorphic sub-polytopes that can be embedded
373
in different places are only returned once.
374
375
EXAMPLES::
376
377
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
378
sage: P1xP1 = LatticePolytope_PPL((1,0), (0,1), (-1,0), (0,-1))
379
sage: P1xP1.sub_polytopes()
380
(A 2-dimensional lattice polytope in ZZ^2 with 4 vertices,
381
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,
382
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,
383
A 1-dimensional lattice polytope in ZZ^2 with 2 vertices,
384
A 1-dimensional lattice polytope in ZZ^2 with 2 vertices,
385
A 0-dimensional lattice polytope in ZZ^2 with 1 vertex)
386
"""
387
subpolytopes = [self]
388
todo = list(subpolytopes)
389
while todo:
390
polytope = todo.pop()
391
for p in polytope.sub_polytope_generator():
392
if p.is_empty():
393
continue
394
if any(p.is_isomorphic(q) for q in subpolytopes):
395
continue
396
subpolytopes.append(p)
397
todo.append(p)
398
return tuple(subpolytopes)
399
400
def plot(self):
401
"""
402
Plot the lattice polygon
403
404
OUTPUT:
405
406
A graphics object.
407
408
EXAMPLES::
409
410
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
411
sage: P = LatticePolytope_PPL((1,0), (0,1), (0,0), (2,2))
412
sage: P.plot() # not tested
413
sage: LatticePolytope_PPL([0], [1]).plot()
414
sage: LatticePolytope_PPL([0]).plot()
415
"""
416
from sage.plot.point import point2d
417
from sage.plot.polygon import polygon2d
418
vertices = self.ordered_vertices()
419
points = self.integral_points()
420
if self.space_dimension() == 1:
421
vertices = [vector(ZZ, (v[0], 0)) for v in vertices]
422
points = [vector(ZZ, (p[0], 0)) for p in points]
423
point_plot = sum(point2d(p, pointsize=100, color='red') for p in points)
424
polygon_plot = polygon2d(vertices, alpha=0.2, color='green',
425
zorder=-1, thickness=2)
426
return polygon_plot + point_plot
427
428
429
########################################################################
430
#
431
# Reflexive lattice polygons and their subpolygons
432
#
433
########################################################################
434
435
@cached_function
436
def polar_P2_polytope():
437
"""
438
The polar of the `P^2` polytope
439
440
EXAMPLES::
441
442
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P2_polytope
443
sage: polar_P2_polytope()
444
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
445
sage: _.vertices()
446
((0, 0), (0, 3), (3, 0))
447
"""
448
return LatticePolytope_PPL((0, 0), (3, 0), (0, 3))
449
450
451
@cached_function
452
def polar_P1xP1_polytope():
453
"""
454
The polar of the `P^1 \times P^1` polytope
455
456
EXAMPLES::
457
458
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P1xP1_polytope
459
sage: polar_P1xP1_polytope()
460
A 2-dimensional lattice polytope in ZZ^2 with 4 vertices
461
sage: _.vertices()
462
((0, 0), (0, 2), (2, 0), (2, 2))
463
"""
464
return LatticePolytope_PPL((0, 0), (2, 0), (0, 2), (2, 2))
465
466
467
@cached_function
468
def polar_P2_112_polytope():
469
"""
470
The polar of the `P^2[1,1,2]` polytope
471
472
EXAMPLES::
473
474
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P2_112_polytope
475
sage: polar_P2_112_polytope()
476
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
477
sage: _.vertices()
478
((0, 0), (0, 2), (4, 0))
479
"""
480
return LatticePolytope_PPL((0, 0), (4, 0), (0, 2))
481
482
483
@cached_function
484
def subpolygons_of_polar_P2():
485
"""
486
The lattice sub-polygons of the polar `P^2` polytope
487
488
OUTPUT:
489
490
A tuple of lattice polytopes.
491
492
EXAMPLES::
493
494
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P2
495
sage: len(subpolygons_of_polar_P2())
496
27
497
"""
498
return polar_P2_polytope().sub_polytopes()
499
500
501
@cached_function
502
def subpolygons_of_polar_P2_112():
503
"""
504
The lattice sub-polygons of the polar `P^2[1,1,2]` polytope
505
506
OUTPUT:
507
508
A tuple of lattice polytopes.
509
510
EXAMPLES::
511
512
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P2_112
513
sage: len(subpolygons_of_polar_P2_112())
514
28
515
"""
516
return polar_P2_112_polytope().sub_polytopes()
517
518
519
@cached_function
520
def subpolygons_of_polar_P1xP1():
521
"""
522
The lattice sub-polygons of the polar `P^1 \times P^1` polytope
523
524
OUTPUT:
525
526
A tuple of lattice polytopes.
527
528
EXAMPLES::
529
530
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P1xP1
531
sage: len(subpolygons_of_polar_P1xP1())
532
20
533
"""
534
return polar_P1xP1_polytope().sub_polytopes()
535
536
537
@cached_function
538
def sub_reflexive_polygons():
539
"""
540
Return all lattice sub-polygons of reflexive polygons.
541
542
OUTPUT:
543
544
A tuple of all lattice sub-polygons. Each sub-polygon is returned
545
as a pair sub-polygon, containing reflexive polygon.
546
547
EXAMPLES::
548
549
sage: from sage.geometry.polyhedron.ppl_lattice_polygon import sub_reflexive_polygons
550
sage: l = sub_reflexive_polygons(); l[5]
551
(A 2-dimensional lattice polytope in ZZ^2 with 6 vertices,
552
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices)
553
sage: len(l)
554
33
555
"""
556
result = []
557
558
def add_result(subpolygon, ambient):
559
if not any(subpolygon.is_isomorphic(p[0]) for p in result):
560
result.append((subpolygon, ambient))
561
for p in subpolygons_of_polar_P2():
562
add_result(p, polar_P2_polytope())
563
for p in subpolygons_of_polar_P2_112():
564
add_result(p, polar_P2_112_polytope())
565
for p in subpolygons_of_polar_P1xP1():
566
add_result(p, polar_P1xP1_polytope())
567
return tuple(result)
568
569