Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/polyhedron/base_ZZ.py
8817 views
1
r"""
2
Base class for polyhedra over `\ZZ`
3
"""
4
5
########################################################################
6
# Copyright (C) 2011 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
14
15
from sage.rings.all import ZZ, QQ, gcd
16
from sage.misc.all import cached_method
17
from sage.matrix.constructor import matrix
18
from sage.modules.free_module_element import vector
19
from constructor import Polyhedron
20
from base import Polyhedron_base
21
22
23
24
#########################################################################
25
class Polyhedron_ZZ(Polyhedron_base):
26
"""
27
Base class for Polyhedra over `\ZZ`
28
29
TESTS::
30
31
sage: p = Polyhedron([(0,0)], base_ring=ZZ); p
32
A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex
33
sage: TestSuite(p).run(skip='_test_pickling')
34
"""
35
def _is_zero(self, x):
36
"""
37
Test whether ``x`` is zero.
38
39
INPUT:
40
41
- ``x`` -- a number in the base ring.
42
43
OUTPUT:
44
45
Boolean.
46
47
EXAMPLES::
48
49
sage: p = Polyhedron([(0,0)], base_ring=ZZ)
50
sage: p._is_zero(0)
51
True
52
sage: p._is_zero(1/100000)
53
False
54
"""
55
return x==0
56
57
def _is_nonneg(self, x):
58
"""
59
Test whether ``x`` is nonnegative.
60
61
INPUT:
62
63
- ``x`` -- a number in the base ring.
64
65
OUTPUT:
66
67
Boolean.
68
69
EXAMPLES::
70
71
sage: p = Polyhedron([(0,0)], base_ring=ZZ)
72
sage: p._is_nonneg(1)
73
True
74
sage: p._is_nonneg(-1/100000)
75
False
76
"""
77
return x>=0
78
79
def _is_positive(self, x):
80
"""
81
Test whether ``x`` is positive.
82
83
INPUT:
84
85
- ``x`` -- a number in the base ring.
86
87
OUTPUT:
88
89
Boolean.
90
91
EXAMPLES::
92
93
sage: p = Polyhedron([(0,0)], base_ring=ZZ)
94
sage: p._is_positive(1)
95
True
96
sage: p._is_positive(0)
97
False
98
"""
99
return x>0
100
101
_base_ring = ZZ
102
103
def is_lattice_polytope(self):
104
r"""
105
Return whether the polyhedron is a lattice polytope.
106
107
OUTPUT:
108
109
``True`` if the polyhedron is compact and has only integral
110
vertices, ``False`` otherwise.
111
112
EXAMPLES::
113
114
sage: polytopes.cross_polytope(3).is_lattice_polytope()
115
True
116
sage: polytopes.regular_polygon(5).is_lattice_polytope()
117
False
118
"""
119
return True
120
121
@cached_method
122
def polar(self):
123
"""
124
Return the polar (dual) polytope.
125
126
The polytope must have the IP-property (see
127
:meth:`has_IP_property`), that is, the origin must be an
128
interior point. In particular, it must be full-dimensional.
129
130
OUTPUT:
131
132
The polytope whose vertices are the coefficient vectors of the
133
inequalities of ``self`` with inhomogeneous term normalized to
134
unity.
135
136
EXAMPLES::
137
138
sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
139
sage: p.polar()
140
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices
141
sage: type(_)
142
<class 'sage.geometry.polyhedron.backend_ppl.Polyhedra_ZZ_ppl_with_category.element_class'>
143
sage: p.polar().base_ring()
144
Integer Ring
145
"""
146
if not self.has_IP_property():
147
raise ValueError('The polytope must have the IP property.')
148
149
vertices = [ ieq.A()/ieq.b() for
150
ieq in self.inequality_generator() ]
151
if all( all(v_i in ZZ for v_i in v) for v in vertices):
152
return Polyhedron(vertices=vertices, base_ring=ZZ)
153
else:
154
return Polyhedron(vertices=vertices, base_ring=QQ)
155
156
@cached_method
157
def is_reflexive(self):
158
"""
159
EXAMPLES::
160
161
sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
162
sage: p.is_reflexive()
163
True
164
"""
165
return self.polar().is_lattice_polytope()
166
167
@cached_method
168
def has_IP_property(self):
169
"""
170
Test whether the polyhedron has the IP property.
171
172
The IP (interior point) property means that
173
174
* ``self`` is compact (a polytope).
175
176
* ``self`` contains the origin as an interior point.
177
178
This implies that
179
180
* ``self`` is full-dimensional.
181
182
* The dual polyhedron is again a polytope (that is, a compact
183
polyhedron), though not necessarily a lattice polytope.
184
185
EXAMPLES::
186
187
sage: Polyhedron([(1,1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
188
False
189
sage: Polyhedron([(0,0),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
190
False
191
sage: Polyhedron([(-1,-1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
192
True
193
194
REFERENCES:
195
196
.. [PALP]
197
Maximilian Kreuzer, Harald Skarke:
198
"PALP: A Package for Analyzing Lattice Polytopes
199
with Applications to Toric Geometry"
200
Comput.Phys.Commun. 157 (2004) 87-106
201
:arxiv:`math/0204356`
202
"""
203
return self.is_compact() and self.interior_contains(self.ambient_space().zero())
204
205
def fibration_generator(self, dim):
206
"""
207
Generate the lattice polytope fibrations.
208
209
For the purposes of this function, a lattice polytope fiber is
210
a sub-lattice polytope. Projecting the plane spanned by the
211
subpolytope to a point yields another lattice polytope, the
212
base of the fibration.
213
214
INPUT:
215
216
- ``dim`` -- integer. The dimension of the lattice polytope
217
fiber.
218
219
OUTPUT:
220
221
A generator yielding the distinct lattice polytope fibers of
222
given dimension.
223
224
EXAMPLES::
225
226
sage: P = Polyhedron(toric_varieties.P4_11169().fan().rays(), base_ring=ZZ)
227
sage: list( P.fibration_generator(2) )
228
[A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices]
229
"""
230
from sage.combinat.combination import Combinations
231
if not self.is_compact():
232
raise ValueError('Only polytopes (compact polyhedra) are allowed.')
233
234
nonzero_points = [p for p in self.integral_points() if not p.is_zero()]
235
origin = [[0]*self.ambient_dim()]
236
fibers = set()
237
parent = self.parent()
238
239
for points in Combinations(nonzero_points, dim):
240
plane = parent.element_class(parent, [origin,[],points], None)
241
if plane.dim() != dim:
242
continue
243
fiber = self.intersection(plane)
244
if fiber.base_ring() is not ZZ:
245
continue
246
fiber_vertices = tuple(sorted(tuple(v) for v in fiber.vertex_generator()))
247
if fiber_vertices not in fibers:
248
yield fiber
249
fibers.update([fiber_vertices])
250
plane.delete()
251
252
def find_translation(self, translated_polyhedron):
253
"""
254
Return the translation vector to ``translated_polyhedron``.
255
256
INPUT:
257
258
- ``translated_polyhedron`` -- a polyhedron.
259
260
OUTPUT:
261
262
A `\ZZ`-vector that translates ``self`` to
263
``translated_polyhedron``. A ``ValueError`` is raised if
264
``translated_polyhedron`` is not a translation of ``self``,
265
this can be used to check that two polyhedra are not
266
translates of each other.
267
268
EXAMPLES::
269
270
sage: X = polytopes.n_cube(3)
271
sage: X.find_translation(X + vector([2,3,5]))
272
(2, 3, 5)
273
sage: X.find_translation(2*X)
274
Traceback (most recent call last):
275
...
276
ValueError: polyhedron is not a translation of self
277
"""
278
no_translation_exception = ValueError('polyhedron is not a translation of self')
279
if ( set(self.rays()) != set(translated_polyhedron.rays()) or
280
set(self.lines()) != set(translated_polyhedron.lines()) or
281
self.n_vertices() != translated_polyhedron.n_vertices() ):
282
raise no_translation_exception
283
sorted_vertices = sorted(map(vector, self.vertices()))
284
sorted_translated_vertices = sorted(map(vector, translated_polyhedron.vertices()))
285
v = sorted_translated_vertices[0] - sorted_vertices[0]
286
if any(vertex+v != translated_vertex
287
for vertex, translated_vertex in zip(sorted_vertices, sorted_translated_vertices)):
288
raise no_translation_exception
289
return v
290
291
def _subpoly_parallel_facets(self):
292
"""
293
Generator for all lattice sub-polyhedra with parallel facets.
294
295
In a sub-polyhedron `Y\subset X` not all edges of `Y` need to
296
be parallel to `X`. This method iterates over all
297
sub-polyhedra where they are parallel, up to an overall
298
translation of the sub-polyhedron. Degenerate sub-polyhedra of
299
dimension strictly smaller are included.
300
301
OUTPUT:
302
303
A generator yielding `\ZZ`-polyhedra. By construction, each
304
facet of the returned polyhedron is parallel to one of the
305
facets of ``self``.
306
307
EXAMPLES::
308
309
sage: X = Polyhedron(vertices=[(0,0), (0,1), (1,0), (1,1)])
310
sage: X._subpoly_parallel_facets()
311
<generator object _subpoly_parallel_facets at 0x...>
312
sage: for p in X._subpoly_parallel_facets():
313
... print p.Vrepresentation()
314
(A vertex at (0, 0),)
315
(A vertex at (0, -1), A vertex at (0, 0))
316
(A vertex at (-1, 0), A vertex at (0, 0))
317
(A vertex at (-1, -1), A vertex at (-1, 0), A vertex at (0, -1), A vertex at (0, 0))
318
319
TESTS::
320
321
sage: X = Polyhedron(vertices=[(0,), (3,)])
322
sage: [ p.vertices() for p in X._subpoly_parallel_facets() ]
323
[(A vertex at (0),),
324
(A vertex at (-1), A vertex at (0)),
325
(A vertex at (-2), A vertex at (0)),
326
(A vertex at (-3), A vertex at (0))]
327
sage: list( Polyhedron(vertices=[[0,0]])._subpoly_parallel_facets() )
328
[A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex]
329
sage: list( Polyhedron()._subpoly_parallel_facets() )
330
[The empty polyhedron in ZZ^0]
331
"""
332
if self.dim()>2 or not self.is_compact():
333
raise NotImplementedError('only implemented for bounded polygons')
334
from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d
335
vertices = cyclic_sort_vertices_2d(self.vertices())
336
n = len(vertices)
337
if n==1: # single point
338
yield self
339
return
340
edge_vectors = []
341
for i in range(0,n):
342
v = vertices[(i+1) % n].vector() - vertices[i].vector()
343
d = gcd(list(v))
344
v_prim = (v/d).change_ring(ZZ)
345
edge_vectors.append([ v_prim*i for i in range(d+1) ])
346
origin = self.ambient_space().zero()
347
parent = self.parent()
348
from sage.combinat.cartesian_product import CartesianProduct
349
for edges in CartesianProduct(*edge_vectors):
350
v = []
351
point = origin
352
for e in edges:
353
point += e
354
v.append(point)
355
if point!=origin: # does not close up, not a subpolygon
356
continue
357
yield parent([v, [], []], None)
358
359
@cached_method
360
def Minkowski_decompositions(self):
361
"""
362
Return all Minkowski sums that add up to the polyhedron.
363
364
OUTPUT:
365
366
A tuple consisting of pairs `(X,Y)` of `\ZZ`-polyhedra that
367
add up to ``self``. All pairs up to exchange of the summands
368
are returned, that is, `(Y,X)` is not included if `(X,Y)`
369
already is.
370
371
EXAMPLES::
372
373
sage: square = Polyhedron(vertices=[(0,0),(1,0),(0,1),(1,1)])
374
sage: square.Minkowski_decompositions()
375
((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
376
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
377
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
378
A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices))
379
380
Example from http://cgi.di.uoa.gr/~amantzaf/geo/ ::
381
382
sage: Q = Polyhedron(vertices=[(4,0), (6,0), (0,3), (4,3)])
383
sage: R = Polyhedron(vertices=[(0,0), (5,0), (8,4), (3,2)])
384
sage: (Q+R).Minkowski_decompositions()
385
((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
386
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
387
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices,
388
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
389
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
390
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
391
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
392
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
393
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
394
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
395
(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
396
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices),
397
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
398
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
399
(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
400
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 6 vertices))
401
402
sage: [ len(square.dilation(i).Minkowski_decompositions())
403
... for i in range(6) ]
404
[1, 2, 5, 8, 13, 18]
405
sage: [ ceil((i^2+2*i-1)/2)+1 for i in range(10) ]
406
[1, 2, 5, 8, 13, 18, 25, 32, 41, 50]
407
"""
408
if self.dim()>2 or not self.is_compact():
409
raise NotImplementedError('only implemented for bounded polygons')
410
summands = []
411
def is_known_summand(poly):
412
for summand in summands:
413
try:
414
poly.find_translation(summand)
415
return True
416
except ValueError:
417
pass
418
decompositions = []
419
for X in self._subpoly_parallel_facets():
420
if is_known_summand(X):
421
continue
422
Y = self - X
423
if X+Y != self:
424
continue
425
decompositions.append((X,Y))
426
summands += [X, Y]
427
return tuple(decompositions)
428
429
430