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