Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/polyhedron/base.py
8817 views
1
r"""
2
Base class for polyhedra
3
"""
4
5
6
########################################################################
7
# Copyright (C) 2008 Marshall Hampton <[email protected]>
8
# Copyright (C) 2011 Volker Braun <[email protected]>
9
#
10
# Distributed under the terms of the GNU General Public License (GPL)
11
#
12
# http://www.gnu.org/licenses/
13
########################################################################
14
15
16
from sage.structure.element import Element, coerce_binop, is_Vector
17
18
from sage.misc.all import cached_method, prod
19
from sage.misc.package import is_package_installed
20
21
from sage.rings.all import Integer, QQ, ZZ
22
from sage.rings.real_double import RDF
23
from sage.modules.free_module_element import vector
24
from sage.matrix.constructor import matrix
25
from sage.functions.other import sqrt, floor, ceil
26
from sage.misc.prandom import randint
27
28
from sage.graphs.graph import Graph
29
30
from sage.combinat.cartesian_product import CartesianProduct
31
32
from constructor import Polyhedron
33
34
35
#########################################################################
36
# Notes if you want to implement your own backend:
37
#
38
# * derive from Polyhedron_base
39
#
40
# * you must implement _init_from_Vrepresentation and
41
# _init_from_Vrepresentationa
42
#
43
# * You might want to override _init_empty_polyhedron,
44
# _init_facet_adjacency_matrix, _init_vertex_adjacency_matrix, and
45
# _make_polyhedron_face.
46
#
47
# * You can of course also override any other method for which you
48
# have a faster implementation.
49
#########################################################################
50
51
52
#########################################################################
53
def is_Polyhedron(X):
54
"""
55
Test whether ``X`` is a Polyhedron.
56
57
INPUT:
58
59
- ``X`` -- anything.
60
61
OUTPUT:
62
63
Boolean.
64
65
EXAMPLES::
66
67
sage: p = polytopes.n_cube(2)
68
sage: from sage.geometry.polyhedron.base import is_Polyhedron
69
sage: is_Polyhedron(p)
70
True
71
sage: is_Polyhedron(123456)
72
False
73
"""
74
return isinstance(X, Polyhedron_base)
75
76
77
#########################################################################
78
class Polyhedron_base(Element):
79
"""
80
Base class for Polyhedron objects
81
82
INPUT:
83
84
- ``parent`` -- the parent, an instance of
85
:class:`~sage.geometry.polyhedron.parent.Polyhedra`.
86
87
- ``Vrep`` -- a list ``[vertices, rays, lines]`` or ``None``. The
88
V-representation of the polyhedron. If ``None``, the polyhedron
89
is determined by the H-representation.
90
91
- ``Hrep`` -- a list ``[ieqs, eqns]`` or ``None``. The
92
H-representation of the polyhedron. If ``None``, the polyhedron
93
is determined by the V-representation.
94
95
Only one of ``Vrep`` or ``Hrep`` can be different from ``None``.
96
97
TESTS::
98
99
sage: p = Polyhedron()
100
sage: TestSuite(p).run()
101
"""
102
103
def __init__(self, parent, Vrep, Hrep, **kwds):
104
"""
105
Initializes the polyhedron.
106
107
See :class:`Polyhedron_base` for a description of the input
108
data.
109
110
TESTS::
111
112
sage: p = Polyhedron() # indirect doctests
113
"""
114
Element.__init__(self, parent=parent)
115
if Vrep is not None:
116
vertices, rays, lines = Vrep
117
self._init_from_Vrepresentation(vertices, rays, lines, **kwds)
118
elif Hrep is not None:
119
ieqs, eqns = Hrep
120
self._init_from_Hrepresentation(ieqs, eqns, **kwds)
121
else:
122
self._init_empty_polyhedron()
123
124
def _sage_input_(self, sib, coerced):
125
"""
126
Return Sage command to reconstruct ``self``.
127
128
See :mod:`sage.misc.sage_input` for details.
129
130
EXAMPLES::
131
132
sage: P = Polyhedron([(1,0), (0,1)], rays=[(1,1)])
133
sage: sage_input(P)
134
Polyhedron(base_ring=ZZ, rays=[(1, 1)], vertices=[(0, 1), (1, 0)])
135
"""
136
kwds = dict()
137
kwds['base_ring'] = sib(self.base_ring())
138
if self.n_vertices() > 0:
139
kwds['vertices'] = [sib(tuple(v)) for v in self.vertices()]
140
if self.n_rays() > 0:
141
kwds['rays'] = [sib(tuple(r)) for r in self.rays()]
142
if self.n_lines() > 0:
143
kwds['lines'] = [sib(tuple(l)) for l in self.lines()]
144
return sib.name('Polyhedron')(**kwds)
145
146
def _init_from_Vrepresentation(self, vertices, rays, lines, **kwds):
147
"""
148
Construct polyhedron from V-representation data.
149
150
INPUT:
151
152
- ``vertices`` -- list of point. Each point can be specified
153
as any iterable container of
154
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
155
156
- ``rays`` -- list of rays. Each ray can be specified as any
157
iterable container of
158
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
159
160
- ``lines`` -- list of lines. Each line can be specified as
161
any iterable container of
162
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
163
164
EXAMPLES::
165
166
sage: p = Polyhedron()
167
sage: from sage.geometry.polyhedron.base import Polyhedron_base
168
sage: Polyhedron_base._init_from_Vrepresentation(p, [], [], [])
169
Traceback (most recent call last):
170
...
171
NotImplementedError: A derived class must implement this method.
172
"""
173
raise NotImplementedError('A derived class must implement this method.')
174
175
176
def _init_from_Hrepresentation(self, ieqs, eqns, **kwds):
177
"""
178
Construct polyhedron from H-representation data.
179
180
INPUT:
181
182
- ``ieqs`` -- list of inequalities. Each line can be specified
183
as any iterable container of
184
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
185
186
- ``eqns`` -- list of equalities. Each line can be specified
187
as any iterable container of
188
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
189
190
EXAMPLES::
191
192
sage: p = Polyhedron()
193
sage: from sage.geometry.polyhedron.base import Polyhedron_base
194
sage: Polyhedron_base._init_from_Hrepresentation(p, [], [])
195
Traceback (most recent call last):
196
...
197
NotImplementedError: A derived class must implement this method.
198
"""
199
raise NotImplementedError('A derived class must implement this method.')
200
201
def _init_empty_polyhedron(self):
202
"""
203
Initializes an empty polyhedron.
204
205
TESTS::
206
207
sage: empty = Polyhedron(); empty
208
The empty polyhedron in ZZ^0
209
sage: empty.Vrepresentation()
210
()
211
sage: empty.Hrepresentation()
212
(An equation -1 == 0,)
213
sage: Polyhedron(vertices = [])
214
The empty polyhedron in ZZ^0
215
sage: Polyhedron(vertices = [])._init_empty_polyhedron()
216
sage: from sage.geometry.polyhedron.parent import Polyhedra
217
sage: Polyhedra(QQ,7)()
218
A 0-dimensional polyhedron in QQ^7 defined as the convex hull of 1 vertex
219
"""
220
self._Vrepresentation = []
221
self._Hrepresentation = []
222
self.parent()._make_Equation(self, [-1] + [0]*self.ambient_dim());
223
self._Vrepresentation = tuple(self._Vrepresentation)
224
self._Hrepresentation = tuple(self._Hrepresentation)
225
226
V_matrix = matrix(ZZ, 0, 0, 0)
227
V_matrix.set_immutable()
228
self.vertex_adjacency_matrix.set_cache(V_matrix)
229
230
H_matrix = matrix(ZZ, 1, 1, 0)
231
H_matrix.set_immutable()
232
self.facet_adjacency_matrix.set_cache(H_matrix)
233
234
def _facet_adjacency_matrix(self):
235
"""
236
Compute the facet adjacency matrix in case it has not been
237
computed during initialization.
238
239
EXAMPLES::
240
241
sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)])
242
sage: p._facet_adjacency_matrix()
243
[0 1 1]
244
[1 0 1]
245
[1 1 0]
246
"""
247
# TODO: This implementation computes the whole face lattice,
248
# which is much more information than necessary.
249
M = matrix(ZZ, self.n_Hrepresentation(), self.n_Hrepresentation(), 0)
250
def set_adjacent(h1,h2):
251
if h1 is h2:
252
return
253
i = h1.index()
254
j = h2.index()
255
M[i,j]=1
256
M[j,i]=1
257
258
face_lattice = self.face_lattice()
259
for face in face_lattice:
260
Hrep = face.ambient_Hrepresentation()
261
if len(Hrep) == 2:
262
set_adjacent(Hrep[0], Hrep[1])
263
return M
264
265
def _vertex_adjacency_matrix(self):
266
"""
267
Compute the vertex adjacency matrix in case it has not been
268
computed during initialization.
269
270
EXAMPLES::
271
272
sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)])
273
sage: p._vertex_adjacency_matrix()
274
[0 1 1]
275
[1 0 1]
276
[1 1 0]
277
"""
278
# TODO: This implementation computes the whole face lattice,
279
# which is much more information than necessary.
280
M = matrix(ZZ, self.n_Vrepresentation(), self.n_Vrepresentation(), 0)
281
def set_adjacent(v1,v2):
282
if v1 is v2:
283
return
284
i = v1.index()
285
j = v2.index()
286
M[i,j]=1
287
M[j,i]=1
288
289
face_lattice = self.face_lattice()
290
for face in face_lattice:
291
Vrep = face.ambient_Vrepresentation()
292
if len(Vrep) == 2:
293
set_adjacent(Vrep[0], Vrep[1])
294
return M
295
296
def delete(self):
297
"""
298
Delete this polyhedron.
299
300
This speeds up creation of new polyhedra by reusing
301
objects. After recycling a polyhedron object, it is not in a
302
consistent state any more and neither the polyhedron nor its
303
H/V-representation objects may be used any more.
304
305
.. seealso:: :meth:`~sage.geometry.polyhedron.parent.Polyhedra_base.recycle`
306
307
EXAMPLES::
308
309
sage: p = Polyhedron([(0,0),(1,0),(0,1)])
310
sage: p.delete()
311
312
sage: vertices = [(0,0,0,0),(1,0,0,0),(0,1,0,0),(1,1,0,0),(0,0,1,0),(0,0,0,1)]
313
sage: def loop_polyhedra():
314
... for i in range(0,100):
315
... p = Polyhedron(vertices)
316
317
sage: timeit('loop_polyhedra()') # not tested - random
318
5 loops, best of 3: 79.5 ms per loop
319
320
sage: def loop_polyhedra_with_recycling():
321
... for i in range(0,100):
322
... p = Polyhedron(vertices)
323
... p.delete()
324
325
sage: timeit('loop_polyhedra_with_recycling()') # not tested - random
326
5 loops, best of 3: 57.3 ms per loop
327
"""
328
self.parent().recycle(self)
329
330
def base_extend(self, base_ring, backend=None):
331
"""
332
Return a new polyhedron over a larger field.
333
334
INPUT:
335
336
- ``base_ring`` -- the new base ring.
337
338
- ``backend`` -- the new backend, see
339
:func:`~sage.geometry.polyhedron.constructor.Polyhedron`.
340
341
OUTPUT:
342
343
The same polyhedron, but over a larger base ring.
344
345
EXAMPLES::
346
347
sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ); P
348
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray
349
sage: P.base_extend(QQ)
350
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray
351
sage: P.base_extend(QQ) == P
352
True
353
"""
354
new_parent = self.parent().base_extend(base_ring, backend)
355
return new_parent(self)
356
357
def __cmp__(self, other):
358
"""
359
Compare ``self`` and ``other``.
360
361
INPUT:
362
363
- ``other`` -- anything.
364
365
OUTPUT:
366
367
`-1, 0, +1` depending on how ``self`` and ``other``
368
compare. If ``other`` is a polyhedron, then the comparison
369
operator "less or equal than" means "is contained in", and
370
"less than" means "is strictly contained in".
371
372
EXAMPLES::
373
374
sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)])
375
sage: Q = Polyhedron(vertices=[(1,0), (0,1)])
376
sage: cmp(P,Q)
377
1
378
sage: cmp(Q,P)
379
-1
380
sage: cmp(P,P)
381
0
382
sage: abs(cmp(P, 'anything'))
383
1
384
385
The polytope ``Q`` is contained in ``P``::
386
387
sage: P > Q
388
True
389
sage: P < Q
390
False
391
sage: P == Q
392
False
393
394
TESTS::
395
396
sage: abs(cmp(P, 'string'))
397
1
398
"""
399
if not isinstance(other, Polyhedron_base):
400
return -1
401
if self._Vrepresentation is None or other._Vrepresentation is None:
402
return -1 # make sure deleted polyhedra are not used in cache
403
c = cmp(self.ambient_dim(), other.ambient_dim())
404
if c != 0: return c
405
c0 = self._is_subpolyhedron(other)
406
c1 = other._is_subpolyhedron(self)
407
if c0 and c1:
408
return 0
409
if c0:
410
return -1
411
else:
412
return +1
413
414
@coerce_binop
415
def _is_subpolyhedron(self, other):
416
"""
417
Test whether ``self`` is a (not necessarily strict)
418
sub-polyhdedron of ``other``.
419
420
INPUT:
421
422
- ``other`` -- a :class:`Polyhedron`.
423
424
OUTPUT:
425
426
Boolean.
427
428
EXAMPLES::
429
430
sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)])
431
sage: Q = Polyhedron(vertices=[(1,0), (0,1)])
432
sage: P._is_subpolyhedron(Q)
433
False
434
sage: Q._is_subpolyhedron(P)
435
True
436
"""
437
return all( other_H.contains(self_V)
438
for other_H, self_V in
439
CartesianProduct(other.Hrepresentation(), self.Vrepresentation()) )
440
441
def plot(self,
442
point=None, line=None, polygon=None, # None means unspecified by the user
443
wireframe='blue', fill='green',
444
**kwds):
445
"""
446
Return a graphical representation.
447
448
INPUT:
449
450
- ``point``, ``line``, ``polygon`` -- Parameters to pass to
451
point (0d), line (1d), and polygon (2d) plot commands.
452
Allowed values are:
453
454
* A Python dictionary to be passed as keywords to the plot
455
commands.
456
457
* A string or triple of numbers: The color. This is
458
equivalent to passing the dictionary ``{'color':...}``.
459
460
* ``False``: Switches off the drawing of the corresponding
461
graphics object
462
463
- ``wireframe``, ``fill`` -- Similar to ``point``, ``line``,
464
and ``polygon``, but ``fill`` is used for the graphics
465
objects in the dimension of the polytope (or of dimension 2
466
for higher dimensional polytopes) and ``wireframe`` is used
467
for all lower-dimensional graphics objects
468
(default: 'green' for fill and 'blue' for wireframe)
469
470
- ``**kwds`` -- optional keyword parameters that are passed to
471
all graphics objects.
472
473
OUTPUT:
474
475
A (multipart) graphics object.
476
477
EXAMPLES::
478
479
sage: square = polytopes.n_cube(2)
480
sage: point = Polyhedron([[1,1]])
481
sage: line = Polyhedron([[1,1],[2,1]])
482
sage: cube = polytopes.n_cube(3)
483
sage: hypercube = polytopes.n_cube(4)
484
485
By default, the wireframe is rendered in blue and the fill in green::
486
487
sage: square.plot()
488
sage: point.plot()
489
sage: line.plot()
490
sage: cube.plot()
491
sage: hypercube.plot()
492
493
Draw the lines in red and nothing else::
494
495
sage: square.plot(point=False, line='red', polygon=False)
496
sage: point.plot(point=False, line='red', polygon=False)
497
sage: line.plot(point=False, line='red', polygon=False)
498
sage: cube.plot(point=False, line='red', polygon=False)
499
sage: hypercube.plot(point=False, line='red', polygon=False)
500
501
Draw points in red, no lines, and a blue polygon::
502
503
sage: square.plot(point={'color':'red'}, line=False, polygon=(0,0,1))
504
sage: point.plot(point={'color':'red'}, line=False, polygon=(0,0,1))
505
sage: line.plot(point={'color':'red'}, line=False, polygon=(0,0,1))
506
sage: cube.plot(point={'color':'red'}, line=False, polygon=(0,0,1))
507
sage: hypercube.plot(point={'color':'red'}, line=False, polygon=(0,0,1))
508
509
If we instead use the ``fill`` and ``wireframe`` options, the
510
coloring depends on the dimension of the object::
511
512
sage: square.plot(fill='green', wireframe='red')
513
sage: point.plot(fill='green', wireframe='red')
514
sage: line.plot(fill='green', wireframe='red')
515
sage: cube.plot(fill='green', wireframe='red')
516
sage: hypercube.plot(fill='green', wireframe='red')
517
518
TESTS::
519
520
sage: for p in square.plot():
521
... print p.options()['rgbcolor'], p
522
blue Point set defined by 4 point(s)
523
blue Line defined by 2 points
524
blue Line defined by 2 points
525
blue Line defined by 2 points
526
blue Line defined by 2 points
527
green Polygon defined by 4 points
528
529
sage: for p in line.plot():
530
... print p.options()['rgbcolor'], p
531
blue Point set defined by 2 point(s)
532
green Line defined by 2 points
533
534
sage: for p in point.plot():
535
... print p.options()['rgbcolor'], p
536
green Point set defined by 1 point(s)
537
538
Draw the lines in red and nothing else::
539
540
sage: for p in square.plot(point=False, line='red', polygon=False):
541
... print p.options()['rgbcolor'], p
542
red Line defined by 2 points
543
red Line defined by 2 points
544
red Line defined by 2 points
545
red Line defined by 2 points
546
547
Draw vertices in red, no lines, and a blue polygon::
548
549
sage: for p in square.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):
550
... print p.options()['rgbcolor'], p
551
red Point set defined by 4 point(s)
552
(0, 0, 1) Polygon defined by 4 points
553
554
sage: for p in line.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):
555
... print p.options()['rgbcolor'], p
556
red Point set defined by 2 point(s)
557
558
sage: for p in point.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):
559
... print p.options()['rgbcolor'], p
560
red Point set defined by 1 point(s)
561
562
Draw in red without wireframe::
563
564
sage: for p in square.plot(wireframe=False, fill="red"):
565
... print p.options()['rgbcolor'], p
566
red Polygon defined by 4 points
567
568
sage: for p in line.plot(wireframe=False, fill="red"):
569
... print p.options()['rgbcolor'], p
570
red Line defined by 2 points
571
572
sage: for p in point.plot(wireframe=False, fill="red"):
573
... print p.options()['rgbcolor'], p
574
red Point set defined by 1 point(s)
575
"""
576
def merge_options(*opts):
577
merged = dict()
578
for i in range(len(opts)):
579
opt = opts[i]
580
if opt is None:
581
continue
582
elif opt is False:
583
return False
584
elif isinstance(opt, (basestring, list, tuple)):
585
merged['color'] = opt
586
else:
587
merged.update(opt)
588
return merged
589
590
d = min(self.dim(), 2)
591
opts = [wireframe] * d + [fill] + [False] * (2-d)
592
# The point/line/polygon options take precedence over wireframe/fill
593
opts = [merge_options(opt1, opt2, kwds)
594
for opt1, opt2 in zip(opts, [point, line, polygon])]
595
596
from plot import render_2d, render_3d, render_4d
597
render_method = [ None, None, render_2d, render_3d, render_4d ]
598
if self.ambient_dim() < len(render_method):
599
render = render_method[self.ambient_dim()]
600
if render != None:
601
return render(self, *opts)
602
raise NotImplementedError('Plotting of '+str(self.ambient_dim())+
603
'-dimensional polyhedra not implemented')
604
605
show = plot
606
607
def _repr_(self):
608
"""
609
Return a description of the polyhedron.
610
611
EXAMPLES::
612
613
sage: poly_test = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]])
614
sage: poly_test._repr_()
615
'A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices'
616
sage: grammar_test = Polyhedron(vertices = [[1,1,1,1,1,1]])
617
sage: grammar_test._repr_()
618
'A 0-dimensional polyhedron in ZZ^6 defined as the convex hull of 1 vertex'
619
"""
620
desc = ''
621
if self.n_vertices()==0:
622
desc += 'The empty polyhedron'
623
else:
624
desc += 'A ' + repr(self.dim()) + '-dimensional polyhedron'
625
desc += ' in '
626
if self.base_ring() is QQ: desc += 'QQ'
627
elif self.base_ring() is ZZ: desc += 'ZZ'
628
elif self.base_ring() is RDF: desc += 'RDF'
629
else: assert False
630
desc += '^' + repr(self.ambient_dim())
631
632
if self.n_vertices()>0:
633
desc += ' defined as the convex hull of '
634
desc += repr(self.n_vertices())
635
if self.n_vertices()==1: desc += ' vertex'
636
else: desc += ' vertices'
637
638
if self.n_rays()>0:
639
if self.n_lines()>0: desc += ", "
640
else: desc += " and "
641
desc += repr(self.n_rays())
642
if self.n_rays()==1: desc += ' ray'
643
else: desc += ' rays'
644
645
if self.n_lines()>0:
646
if self.n_rays()>0: desc += ", "
647
else: desc += " and "
648
desc += repr(self.n_lines())
649
if self.n_lines()==1: desc +=' line'
650
else: desc +=' lines'
651
652
return desc
653
654
def cdd_Hrepresentation(self):
655
"""
656
Write the inequalities/equations data of the polyhedron in
657
cdd's H-representation format.
658
659
OUTPUT:
660
661
A string. If you save the output to filename.ine then you can
662
run the stand-alone cdd via ``cddr+ filename.ine``
663
664
EXAMPLES::
665
666
sage: p = polytopes.n_cube(2)
667
sage: print p.cdd_Hrepresentation()
668
H-representation
669
begin
670
4 3 rational
671
1 1 0
672
1 0 1
673
1 -1 0
674
1 0 -1
675
end
676
"""
677
from cdd_file_format import cdd_Hrepresentation
678
try:
679
cdd_type = self._cdd_type
680
except AttributeError:
681
if self.base_ring() is ZZ or self.base_ring() is QQ:
682
cdd_type = 'rational'
683
elif self.base_ring() is RDF:
684
cdd_type = 'real'
685
else:
686
raise TypeError('The base ring must be ZZ, QQ, or RDF')
687
return cdd_Hrepresentation(cdd_type,
688
list(self.inequality_generator()),
689
list(self.equation_generator()) )
690
691
def cdd_Vrepresentation(self):
692
"""
693
Write the vertices/rays/lines data of the polyhedron in cdd's
694
V-representation format.
695
696
OUTPUT:
697
698
A string. If you save the output to filename.ext then you can
699
run the stand-alone cdd via ``cddr+ filename.ext``
700
701
EXAMPLES::
702
703
sage: q = Polyhedron(vertices = [[1,1],[0,0],[1,0],[0,1]])
704
sage: print q.cdd_Vrepresentation()
705
V-representation
706
begin
707
4 3 rational
708
1 0 0
709
1 0 1
710
1 1 0
711
1 1 1
712
end
713
"""
714
from cdd_file_format import cdd_Vrepresentation
715
try:
716
cdd_type = self._cdd_type
717
except AttributeError:
718
if self.base_ring() is ZZ or self.base_ring() is QQ:
719
cdd_type = 'rational'
720
elif self.base_ring() is RDF:
721
cdd_type = 'real'
722
else:
723
raise TypeError('The base ring must be ZZ, QQ, or RDF')
724
return cdd_Vrepresentation(cdd_type,
725
list(self.vertex_generator()),
726
list(self.ray_generator()),
727
list(self.line_generator()) )
728
729
@cached_method
730
def n_equations(self):
731
"""
732
Return the number of equations. The representation will
733
always be minimal, so the number of equations is the
734
codimension of the polyhedron in the ambient space.
735
736
EXAMPLES::
737
738
sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]])
739
sage: p.n_equations()
740
1
741
"""
742
return len(self.equations())
743
744
@cached_method
745
def n_inequalities(self):
746
"""
747
Return the number of inequalities. The representation will
748
always be minimal, so the number of inequalities is the
749
number of facets of the polyhedron in the ambient space.
750
751
EXAMPLES::
752
753
sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]])
754
sage: p.n_inequalities()
755
3
756
757
sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in range(6)])
758
sage: p.n_facets()
759
8
760
"""
761
return len(self.inequalities())
762
763
n_facets = n_inequalities
764
765
@cached_method
766
def n_vertices(self):
767
"""
768
Return the number of vertices. The representation will
769
always be minimal.
770
771
EXAMPLES::
772
773
sage: p = Polyhedron(vertices = [[1,0],[0,1],[1,1]], rays=[[1,1]])
774
sage: p.n_vertices()
775
2
776
"""
777
return len(self.vertices())
778
779
@cached_method
780
def n_rays(self):
781
"""
782
Return the number of rays. The representation will
783
always be minimal.
784
785
EXAMPLES::
786
787
sage: p = Polyhedron(vertices = [[1,0],[0,1]], rays=[[1,1]])
788
sage: p.n_rays()
789
1
790
"""
791
return len(self.rays())
792
793
@cached_method
794
def n_lines(self):
795
"""
796
Return the number of lines. The representation will
797
always be minimal.
798
799
EXAMPLES::
800
801
sage: p = Polyhedron(vertices = [[0,0]], rays=[[0,1],[0,-1]])
802
sage: p.n_lines()
803
1
804
"""
805
return len(self.lines())
806
807
def Hrepresentation(self, index=None):
808
"""
809
Return the objects of the H-representaton. Each entry is
810
either an inequality or a equation.
811
812
INPUT:
813
814
- ``index`` -- either an integer or ``None``.
815
816
OUTPUT:
817
818
The optional argument is an index running from ``0`` to
819
``self.n_Hrepresentation()-1``. If present, the
820
H-representation object at the given index will be
821
returned. Without an argument, returns the list of all
822
H-representation objects.
823
824
EXAMPLES::
825
826
sage: p = polytopes.n_cube(3)
827
sage: p.Hrepresentation(0)
828
An inequality (0, 0, -1) x + 1 >= 0
829
sage: p.Hrepresentation(0) == p.Hrepresentation() [0]
830
True
831
"""
832
if index is None:
833
return self._Hrepresentation
834
else:
835
return self._Hrepresentation[index]
836
837
838
def Hrep_generator(self):
839
"""
840
Return an iterator over the objects of the H-representation
841
(inequalities or equations).
842
843
EXAMPLES::
844
845
sage: p = polytopes.n_cube(3)
846
sage: p.Hrep_generator().next()
847
An inequality (0, 0, -1) x + 1 >= 0
848
"""
849
for H in self.Hrepresentation():
850
yield H
851
852
@cached_method
853
def n_Hrepresentation(self):
854
"""
855
Return the number of objects that make up the
856
H-representation of the polyhedron.
857
858
OUTPUT:
859
860
Integer.
861
862
EXAMPLES::
863
864
sage: p = polytopes.cross_polytope(4)
865
sage: p.n_Hrepresentation()
866
16
867
sage: p.n_Hrepresentation() == p.n_inequalities() + p.n_equations()
868
True
869
"""
870
return len(self.Hrepresentation())
871
872
def Vrepresentation(self, index=None):
873
"""
874
Return the objects of the V-representation. Each entry is
875
either a vertex, a ray, or a line.
876
877
See :mod:`sage.geometry.polyhedron.constructor` for a
878
definition of vertex/ray/line.
879
880
INPUT:
881
882
- ``index`` -- either an integer or ``None``.
883
884
OUTPUT:
885
886
The optional argument is an index running from ``0`` to
887
`self.n_Vrepresentation()-1``. If present, the
888
V-representation object at the given index will be
889
returned. Without an argument, returns the list of all
890
V-representation objects.
891
892
EXAMPLES::
893
894
sage: p = polytopes.n_simplex(4)
895
sage: p.Vrepresentation(0)
896
A vertex at (-7071/10000, 1633/4000, 7217/25000, 22361/100000)
897
sage: p.Vrepresentation(0) == p.Vrepresentation() [0]
898
True
899
"""
900
if index is None:
901
return self._Vrepresentation
902
else:
903
return self._Vrepresentation[index]
904
905
@cached_method
906
def n_Vrepresentation(self):
907
"""
908
Return the number of objects that make up the
909
V-representation of the polyhedron.
910
911
OUTPUT:
912
913
Integer.
914
915
EXAMPLES::
916
917
sage: p = polytopes.n_simplex(4)
918
sage: p.n_Vrepresentation()
919
5
920
sage: p.n_Vrepresentation() == p.n_vertices() + p.n_rays() + p.n_lines()
921
True
922
"""
923
return len(self.Vrepresentation())
924
925
def Vrep_generator(self):
926
"""
927
Returns an iterator over the objects of the V-representation
928
(vertices, rays, and lines).
929
930
EXAMPLES::
931
932
sage: p = polytopes.cyclic_polytope(3,4)
933
sage: vg = p.Vrep_generator()
934
sage: vg.next()
935
A vertex at (0, 0, 0)
936
sage: vg.next()
937
A vertex at (1, 1, 1)
938
"""
939
for V in self.Vrepresentation():
940
yield V
941
942
def facial_adjacencies(self):
943
r"""
944
Return the list of face indices (i.e. indices of
945
H-representation objects) and the indices of faces adjacent to
946
them.
947
948
.. NOTE::
949
950
Instead of working with face indices, it is recommended
951
that you use the H-representation objects directly (see
952
example).
953
954
EXAMPLES::
955
956
sage: p = polytopes.permutahedron(4)
957
sage: p.facial_adjacencies()[0:3]
958
doctest:...: DeprecationWarning:
959
This method is deprecated.
960
Use self.Hrepresentation(i).neighbors() instead.
961
See http://trac.sagemath.org/11763 for details.
962
[[0, [1, 2, 5, 10, 12, 13]], [1, [0, 2, 5, 7, 9, 11]], [2, [0, 1, 10, 11]]]
963
sage: f0 = p.Hrepresentation(0)
964
sage: f0.index() == 0
965
True
966
sage: f0_adjacencies = [f0.index(), [n.index() for n in f0.neighbors()]]
967
sage: p.facial_adjacencies()[0] == f0_adjacencies
968
True
969
"""
970
from sage.misc.superseded import deprecation
971
deprecation(11763, 'This method is deprecated. Use self.Hrepresentation(i).neighbors() instead.')
972
try:
973
return self._facial_adjacencies
974
except AttributeError:
975
self._facial_adjacencies = \
976
[ [ h.index(),
977
[n.index() for n in h.neighbors()]
978
] for h in self.Hrepresentation() ]
979
return self._facial_adjacencies
980
981
def facial_incidences(self):
982
"""
983
Return the face-vertex incidences in the form `[f_i, [v_{i_0}, v_{i_1},\dots ,v_{i_2}]]`.
984
985
.. NOTE::
986
987
Instead of working with face/vertex indices, it is
988
recommended that you use the
989
H-representation/V-representation objects directly (see
990
examples). Or use :meth:`incidence_matrix`.
991
992
OUTPUT:
993
994
The face indices are the indices of the H-representation
995
objects, and the vertex indices are the indices of the
996
V-representation objects.
997
998
EXAMPLES::
999
1000
sage: p = Polyhedron(vertices = [[5,0,0],[0,5,0],[5,5,0],[0,0,0],[2,2,5]])
1001
sage: p.facial_incidences()
1002
doctest:...: DeprecationWarning:
1003
This method is deprecated. Use self.Hrepresentation(i).incident() instead.
1004
See http://trac.sagemath.org/11763 for details.
1005
[[0, [0, 1, 3, 4]],
1006
[1, [0, 1, 2]],
1007
[2, [0, 2, 3]],
1008
[3, [2, 3, 4]],
1009
[4, [1, 2, 4]]]
1010
1011
sage: f0 = p.Hrepresentation(0)
1012
sage: f0.index() == 0
1013
True
1014
sage: f0_incidences = [f0.index(), [v.index() for v in f0.incident()]]
1015
sage: p.facial_incidences()[0] == f0_incidences
1016
True
1017
1018
sage: p.incidence_matrix().column(0)
1019
(1, 1, 0, 1, 1)
1020
sage: p.incidence_matrix().column(1)
1021
(1, 1, 1, 0, 0)
1022
sage: p.incidence_matrix().column(2)
1023
(1, 0, 1, 1, 0)
1024
sage: p.incidence_matrix().column(3)
1025
(0, 0, 1, 1, 1)
1026
sage: p.incidence_matrix().column(4)
1027
(0, 1, 1, 0, 1)
1028
"""
1029
from sage.misc.superseded import deprecation
1030
deprecation(11763, 'This method is deprecated. Use self.Hrepresentation(i).incident() instead.')
1031
try:
1032
return self._facial_incidences
1033
except AttributeError:
1034
self._facial_incidences = \
1035
[ [ h.index(),
1036
[v.index() for v in h.incident()]
1037
] for h in self.Hrepresentation() ]
1038
return self._facial_incidences
1039
1040
def vertex_adjacencies(self):
1041
"""
1042
Return a list of vertex indices and their adjacent vertices.
1043
1044
.. NOTE::
1045
1046
Instead of working with vertex indices, you can use the
1047
V-representation objects directly (see examples).
1048
1049
Two V-representation objects are adjacent if they generate a
1050
(1-dimensional) face of the polyhedron. Examples are two
1051
vertices of a polytope that bound an edge, or a vertex and a
1052
ray of a polyhedron that generate a bounding half-line of the
1053
polyhedron. See :meth:`vertex_adjacency_matrix` for a more
1054
detailed discussion.
1055
1056
OUTPUT:
1057
1058
The vertex indices are the indices of the V-representation
1059
objects.
1060
1061
EXAMPLES::
1062
1063
sage: permuta3 = Polyhedron(vertices = Permutations([1,2,3,4]))
1064
sage: permuta3.vertex_adjacencies()[0:3]
1065
doctest:...: DeprecationWarning:
1066
This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.
1067
See http://trac.sagemath.org/11763 for details.
1068
[[0, [1, 2, 6]], [1, [0, 3, 7]], [2, [0, 4, 8]]]
1069
sage: v0 = permuta3.Vrepresentation(0)
1070
sage: v0.index() == 0
1071
True
1072
sage: list( v0.neighbors() )
1073
[A vertex at (1, 2, 4, 3), A vertex at (1, 3, 2, 4), A vertex at (2, 1, 3, 4)]
1074
sage: v0_adjacencies = [v0.index(), [v.index() for v in v0.neighbors()]]
1075
sage: permuta3.vertex_adjacencies()[0] == v0_adjacencies
1076
True
1077
"""
1078
from sage.misc.superseded import deprecation
1079
deprecation(11763, 'This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.')
1080
try:
1081
return self._vertex_adjacencies
1082
except AttributeError:
1083
self._vertex_adjacencies = \
1084
[ [ v.index(),
1085
[n.index() for n in v.neighbors()]
1086
] for v in self.Vrepresentation() ]
1087
return self._vertex_adjacencies
1088
1089
def vertex_incidences(self):
1090
"""
1091
Return the vertex-face incidences in the form `[v_i, [f_{i_0}, f_{i_1},\dots ,f_{i_2}]]`.
1092
1093
.. NOTE::
1094
1095
Instead of working with face/vertex indices, you can use
1096
the H-representation/V-representation objects directly
1097
(see examples).
1098
1099
EXAMPLES::
1100
1101
sage: p = polytopes.n_simplex(3)
1102
sage: p.vertex_incidences()
1103
doctest:...: DeprecationWarning:
1104
This method is deprecated. Use self.Vrepresentation(i).incident() instead.
1105
See http://trac.sagemath.org/11763 for details.
1106
[[0, [0, 1, 2]], [1, [0, 1, 3]], [2, [0, 2, 3]], [3, [1, 2, 3]]]
1107
sage: v0 = p.Vrepresentation(0)
1108
sage: v0.index() == 0
1109
True
1110
sage: p.vertex_incidences()[0] == [ v0.index(), [h.index() for h in v0.incident()] ]
1111
True
1112
"""
1113
from sage.misc.superseded import deprecation
1114
deprecation(11763, 'This method is deprecated. Use self.Vrepresentation(i).incident() instead.')
1115
try:
1116
return self._vertex_incidences
1117
except AttributeError:
1118
self._vertex_incidences = \
1119
[ [ v.index(),
1120
[h.index() for h in v.incident()]
1121
] for v in self.Vrepresentation() ]
1122
return self._vertex_incidences
1123
1124
def inequality_generator(self):
1125
"""
1126
Return a generator for the defining inequalities of the
1127
polyhedron.
1128
1129
OUTPUT:
1130
1131
A generator of the inequality Hrepresentation objects.
1132
1133
EXAMPLES::
1134
1135
sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])
1136
sage: for v in triangle.inequality_generator(): print(v)
1137
An inequality (1, 1) x - 1 >= 0
1138
An inequality (0, -1) x + 1 >= 0
1139
An inequality (-1, 0) x + 1 >= 0
1140
sage: [ v for v in triangle.inequality_generator() ]
1141
[An inequality (1, 1) x - 1 >= 0,
1142
An inequality (0, -1) x + 1 >= 0,
1143
An inequality (-1, 0) x + 1 >= 0]
1144
sage: [ [v.A(), v.b()] for v in triangle.inequality_generator() ]
1145
[[(1, 1), -1], [(0, -1), 1], [(-1, 0), 1]]
1146
"""
1147
for H in self.Hrepresentation():
1148
if H.is_inequality():
1149
yield H
1150
1151
@cached_method
1152
def inequalities(self):
1153
"""
1154
Return all inequalities.
1155
1156
OUTPUT:
1157
1158
A tuple of inequalities.
1159
1160
EXAMPLES::
1161
1162
sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]])
1163
sage: p.inequalities()[0:3]
1164
(An inequality (1, 0, 0) x + 0 >= 0,
1165
An inequality (0, 1, 0) x + 0 >= 0,
1166
An inequality (0, 0, 1) x + 0 >= 0)
1167
sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))
1168
sage: ieqs = p3.inequalities()
1169
sage: ieqs[0]
1170
An inequality (0, 1, 1, 1) x - 6 >= 0
1171
sage: list(_)
1172
[-6, 0, 1, 1, 1]
1173
"""
1174
return tuple(self.inequality_generator())
1175
1176
def inequalities_list(self):
1177
"""
1178
Return a list of inequalities as coefficient lists.
1179
1180
.. NOTE::
1181
1182
It is recommended to use :meth:`inequalities` or
1183
:meth:`inequality_generator` instead to iterate over the
1184
list of :class:`Inequality` objects.
1185
1186
EXAMPLES::
1187
1188
sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]])
1189
sage: p.inequalities_list()[0:3]
1190
[[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
1191
sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))
1192
sage: ieqs = p3.inequalities_list()
1193
sage: ieqs[0]
1194
[-6, 0, 1, 1, 1]
1195
sage: ieqs[-1]
1196
[-3, 0, 1, 0, 1]
1197
sage: ieqs == [list(x) for x in p3.inequality_generator()]
1198
True
1199
"""
1200
return [list(x) for x in self.inequality_generator()]
1201
1202
def ieqs(self):
1203
"""
1204
Deprecated. Alias for inequalities()
1205
1206
EXAMPLES::
1207
1208
sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))
1209
sage: p3.ieqs() == p3.inequalities()
1210
doctest:...: DeprecationWarning:
1211
This method is deprecated. Use inequalities() instead.
1212
See http://trac.sagemath.org/11763 for details.
1213
True
1214
"""
1215
from sage.misc.superseded import deprecation
1216
deprecation(11763, 'This method is deprecated. Use inequalities() instead.')
1217
return self.inequalities()
1218
1219
def equation_generator(self):
1220
"""
1221
Return a generator for the linear equations satisfied by the
1222
polyhedron.
1223
1224
EXAMPLES::
1225
1226
sage: p = polytopes.regular_polygon(8,base_ring=RDF)
1227
sage: p3 = Polyhedron(vertices = [x+[0] for x in p.vertices()], base_ring=RDF)
1228
sage: p3.equation_generator().next()
1229
An equation (0.0, 0.0, 1.0) x + 0.0 == 0
1230
"""
1231
for H in self.Hrepresentation():
1232
if H.is_equation():
1233
yield H
1234
1235
@cached_method
1236
def equations(self):
1237
"""
1238
Return all linear constraints of the polyhedron.
1239
1240
OUTPUT:
1241
1242
A tuple of equations.
1243
1244
EXAMPLES::
1245
1246
sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])
1247
sage: test_p.equations()
1248
(An equation (1, 1, 1, 1) x - 10 == 0,)
1249
"""
1250
return tuple(self.equation_generator())
1251
1252
def equations_list(self):
1253
"""
1254
Return the linear constraints of the polyhedron. As with
1255
inequalities, each constraint is given as [b -a1 -a2 ... an]
1256
where for variables x1, x2,..., xn, the polyhedron satisfies
1257
the equation b = a1*x1 + a2*x2 + ... + an*xn.
1258
1259
.. NOTE::
1260
1261
It is recommended to use :meth:`equations` or
1262
:meth:`equation_generator()` instead to iterate over the
1263
list of
1264
:class:`~sage.geometry.polyhedron.representation.Equation`
1265
objects.
1266
1267
EXAMPLES::
1268
1269
sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])
1270
sage: test_p.equations_list()
1271
[[-10, 1, 1, 1, 1]]
1272
"""
1273
return [list(eq) for eq in self.equation_generator()]
1274
1275
def linearities(self):
1276
"""
1277
Deprecated. Use equations() instead.
1278
Returns the linear constraints of the polyhedron. As with
1279
inequalities, each constraint is given as [b -a1 -a2 ... an]
1280
where for variables x1, x2,..., xn, the polyhedron satisfies
1281
the equation b = a1*x1 + a2*x2 + ... + an*xn.
1282
1283
EXAMPLES::
1284
1285
sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])
1286
sage: test_p.linearities()
1287
doctest:...: DeprecationWarning:
1288
This method is deprecated. Use equations_list() instead.
1289
See http://trac.sagemath.org/11763 for details.
1290
[[-10, 1, 1, 1, 1]]
1291
sage: test_p.linearities() == test_p.equations_list()
1292
True
1293
"""
1294
from sage.misc.superseded import deprecation
1295
deprecation(11763, 'This method is deprecated. Use equations_list() instead.')
1296
return self.equations_list()
1297
1298
def vertices_list(self):
1299
"""
1300
Return a list of vertices of the polyhedron.
1301
1302
.. NOTE::
1303
1304
It is recommended to use :meth:`vertex_generator` instead to
1305
iterate over the list of :class:`Vertex` objects.
1306
1307
EXAMPLES::
1308
1309
sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])
1310
sage: triangle.vertices_list()
1311
[[0, 1], [1, 0], [1, 1]]
1312
sage: a_simplex = Polyhedron(ieqs = [
1313
... [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]
1314
... ], eqns = [[1,-1,-1,-1,-1]])
1315
sage: a_simplex.vertices_list()
1316
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
1317
sage: a_simplex.vertices_list() == [list(v) for v in a_simplex.vertex_generator()]
1318
True
1319
"""
1320
return [list(x) for x in self.vertex_generator()]
1321
1322
def vertex_generator(self):
1323
"""
1324
Return a generator for the vertices of the polyhedron.
1325
1326
EXAMPLES::
1327
1328
sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])
1329
sage: for v in triangle.vertex_generator(): print(v)
1330
A vertex at (0, 1)
1331
A vertex at (1, 0)
1332
A vertex at (1, 1)
1333
sage: v_gen = triangle.vertex_generator()
1334
sage: v_gen.next() # the first vertex
1335
A vertex at (0, 1)
1336
sage: v_gen.next() # the second vertex
1337
A vertex at (1, 0)
1338
sage: v_gen.next() # the third vertex
1339
A vertex at (1, 1)
1340
sage: try: v_gen.next() # there are only three vertices
1341
... except StopIteration: print "STOP"
1342
STOP
1343
sage: type(v_gen)
1344
<type 'generator'>
1345
sage: [ v for v in triangle.vertex_generator() ]
1346
[A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)]
1347
"""
1348
for V in self.Vrepresentation():
1349
if V.is_vertex():
1350
yield V
1351
1352
@cached_method
1353
def vertices(self):
1354
"""
1355
Return all vertices of the polyhedron.
1356
1357
OUTPUT:
1358
1359
A tuple of vertices.
1360
1361
EXAMPLES::
1362
1363
sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])
1364
sage: triangle.vertices()
1365
(A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1))
1366
sage: a_simplex = Polyhedron(ieqs = [
1367
... [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]
1368
... ], eqns = [[1,-1,-1,-1,-1]])
1369
sage: a_simplex.vertices()
1370
(A vertex at (1, 0, 0, 0), A vertex at (0, 1, 0, 0),
1371
A vertex at (0, 0, 1, 0), A vertex at (0, 0, 0, 1))
1372
"""
1373
return tuple(self.vertex_generator())
1374
1375
@cached_method
1376
def vertices_matrix(self, base_ring=None):
1377
"""
1378
Return the coordinates of the vertices as the columns of a matrix.
1379
1380
INPUT:
1381
1382
- ``base_ring`` -- A ring or ``None`` (default). The base ring
1383
of the returned matrix. If not specified, the base ring of
1384
the polyhedron is used.
1385
1386
OUTPUT:
1387
1388
A matrix over ``base_ring`` whose columns are the coordinates
1389
of the vertices. A ``TypeError`` is raised if the coordinates
1390
cannot be converted to ``base_ring``.
1391
1392
EXAMPLES::
1393
1394
sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])
1395
sage: triangle.vertices_matrix()
1396
[0 1 1]
1397
[1 0 1]
1398
sage: (triangle/2).vertices_matrix()
1399
[ 0 1/2 1/2]
1400
[1/2 0 1/2]
1401
sage: (triangle/2).vertices_matrix(ZZ)
1402
Traceback (most recent call last):
1403
...
1404
TypeError: no conversion of this rational to integer
1405
"""
1406
if base_ring is None:
1407
base_ring = self.base_ring()
1408
m = matrix(base_ring, self.ambient_dim(), self.n_vertices())
1409
for i,v in enumerate(self.vertices()):
1410
for j in range(0,self.ambient_dim()):
1411
m[j,i] = v[j]
1412
return m
1413
1414
def ray_generator(self):
1415
"""
1416
Return a generator for the rays of the polyhedron.
1417
1418
EXAMPLES::
1419
1420
sage: pi = Polyhedron(ieqs = [[1,1,0],[1,0,1]])
1421
sage: pir = pi.ray_generator()
1422
sage: [x.vector() for x in pir]
1423
[(1, 0), (0, 1)]
1424
"""
1425
for V in self.Vrepresentation():
1426
if V.is_ray():
1427
yield V
1428
1429
@cached_method
1430
def rays(self):
1431
"""
1432
Return a list of rays of the polyhedron.
1433
1434
OUTPUT:
1435
1436
A tuple of rays.
1437
1438
EXAMPLES::
1439
1440
sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]])
1441
sage: p.rays()
1442
(A ray in the direction (1, 0, 0),
1443
A ray in the direction (0, 1, 0),
1444
A ray in the direction (0, 0, 1))
1445
"""
1446
return tuple(self.ray_generator())
1447
1448
def rays_list(self):
1449
"""
1450
Return a list of rays as coefficient lists.
1451
1452
.. NOTE::
1453
1454
It is recommended to use :meth:`rays` or
1455
:meth:`ray_generator` instead to iterate over the list of
1456
:class:`Ray` objects.
1457
1458
OUTPUT:
1459
1460
A list of rays as lists of coordinates.
1461
1462
EXAMPLES::
1463
1464
sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]])
1465
sage: p.rays_list()
1466
[[1, 0, 0], [0, 1, 0], [0, 0, 1]]
1467
sage: p.rays_list() == [list(r) for r in p.ray_generator()]
1468
True
1469
"""
1470
return [list(x) for x in self.ray_generator()]
1471
1472
def line_generator(self):
1473
"""
1474
Return a generator for the lines of the polyhedron.
1475
1476
EXAMPLES::
1477
1478
sage: pr = Polyhedron(rays = [[1,0],[-1,0],[0,1]], vertices = [[-1,-1]])
1479
sage: pr.line_generator().next().vector()
1480
(1, 0)
1481
"""
1482
for V in self.Vrepresentation():
1483
if V.is_line():
1484
yield V
1485
1486
@cached_method
1487
def lines(self):
1488
"""
1489
Return all lines of the polyhedron.
1490
1491
OUTPUT:
1492
1493
A tuple of lines.
1494
1495
EXAMPLES::
1496
1497
sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]])
1498
sage: p.lines()
1499
(A line in the direction (1, 0),)
1500
"""
1501
return tuple(self.line_generator())
1502
1503
def lines_list(self):
1504
"""
1505
Return a list of lines of the polyhedron. The line data is given
1506
as a list of coordinates rather than as a Hrepresentation object.
1507
1508
.. NOTE::
1509
1510
It is recommended to use :meth:`line_generator` instead to
1511
iterate over the list of :class:`Line` objects.
1512
1513
EXAMPLES::
1514
1515
sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]])
1516
sage: p.lines_list()
1517
[[1, 0]]
1518
sage: p.lines_list() == [list(x) for x in p.line_generator()]
1519
True
1520
"""
1521
return [list(x) for x in self.line_generator()]
1522
1523
def bounded_edges(self):
1524
"""
1525
Return the bounded edges (excluding rays and lines).
1526
1527
OUTPUT:
1528
1529
A generator for pairs of vertices, one pair per edge.
1530
1531
EXAMPLES::
1532
1533
sage: p = Polyhedron(vertices=[[1,0],[0,1]], rays=[[1,0],[0,1]])
1534
sage: [ e for e in p.bounded_edges() ]
1535
[(A vertex at (0, 1), A vertex at (1, 0))]
1536
sage: for e in p.bounded_edges(): print e
1537
(A vertex at (0, 1), A vertex at (1, 0))
1538
"""
1539
obj = self.Vrepresentation()
1540
for i in range(len(obj)):
1541
if not obj[i].is_vertex(): continue
1542
for j in range(i+1,len(obj)):
1543
if not obj[j].is_vertex(): continue
1544
if self.vertex_adjacency_matrix()[i,j] == 0: continue
1545
yield (obj[i], obj[j])
1546
1547
def Vrepresentation_space(self):
1548
r"""
1549
Return the ambient vector space.
1550
1551
OUTPUT:
1552
1553
A free module over the base ring of dimension :meth:`ambient_dim`.
1554
1555
EXAMPLES::
1556
1557
sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])
1558
sage: poly_test.Vrepresentation_space()
1559
Ambient free module of rank 4 over the principal ideal domain Integer Ring
1560
sage: poly_test.ambient_space() is poly_test.Vrepresentation_space()
1561
True
1562
"""
1563
return self.parent().Vrepresentation_space()
1564
1565
ambient_space = Vrepresentation_space
1566
1567
def Hrepresentation_space(self):
1568
r"""
1569
Return the linear space containing the H-representation vectors.
1570
1571
OUTPUT:
1572
1573
A free module over the base ring of dimension :meth:`ambient_dim` + 1.
1574
1575
EXAMPLES::
1576
1577
sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])
1578
sage: poly_test.Hrepresentation_space()
1579
Ambient free module of rank 5 over the principal ideal domain Integer Ring
1580
"""
1581
return self.parent().Hrepresentation_space()
1582
1583
def ambient_dim(self):
1584
r"""
1585
Return the dimension of the ambient space.
1586
1587
EXAMPLES::
1588
1589
sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])
1590
sage: poly_test.ambient_dim()
1591
4
1592
"""
1593
return self.parent().ambient_dim()
1594
1595
def dim(self):
1596
"""
1597
Return the dimension of the polyhedron.
1598
1599
OUTPUT:
1600
1601
-1 if the polyhedron is empty, otherwise a non-negative integer.
1602
1603
EXAMPLES::
1604
1605
sage: simplex = Polyhedron(vertices = [[1,0,0,0],[0,0,0,1],[0,1,0,0],[0,0,1,0]])
1606
sage: simplex.dim()
1607
3
1608
sage: simplex.ambient_dim()
1609
4
1610
1611
The empty set is a special case (Trac #12193)::
1612
1613
sage: P1=Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]])
1614
sage: P2=Polyhedron(vertices=[[2,0,0],[0,2,0],[0,0,2]])
1615
sage: P12 = P1.intersection(P2)
1616
sage: P12
1617
The empty polyhedron in ZZ^3
1618
sage: P12.dim()
1619
-1
1620
"""
1621
if self.n_Vrepresentation() == 0:
1622
return -1 # the empty set
1623
else:
1624
return self.ambient_dim() - self.n_equations()
1625
1626
def is_empty(self):
1627
"""
1628
Test whether the polyhedron is the empty polyhedron
1629
1630
OUTPUT:
1631
1632
Boolean.
1633
1634
EXAMPLES::
1635
1636
sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P
1637
A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices
1638
sage: P.is_empty(), P.is_universe()
1639
(False, False)
1640
1641
sage: Q = Polyhedron(vertices=()); Q
1642
The empty polyhedron in ZZ^0
1643
sage: Q.is_empty(), Q.is_universe()
1644
(True, False)
1645
1646
sage: R = Polyhedron(lines=[(1,0),(0,1)]); R
1647
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines
1648
sage: R.is_empty(), R.is_universe()
1649
(False, True)
1650
"""
1651
return self.n_Vrepresentation() == 0
1652
1653
def is_universe(self):
1654
"""
1655
Test whether the polyhedron is the whole ambient space
1656
1657
OUTPUT:
1658
1659
Boolean.
1660
1661
EXAMPLES::
1662
1663
sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P
1664
A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices
1665
sage: P.is_empty(), P.is_universe()
1666
(False, False)
1667
1668
sage: Q = Polyhedron(vertices=()); Q
1669
The empty polyhedron in ZZ^0
1670
sage: Q.is_empty(), Q.is_universe()
1671
(True, False)
1672
1673
sage: R = Polyhedron(lines=[(1,0),(0,1)]); R
1674
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines
1675
sage: R.is_empty(), R.is_universe()
1676
(False, True)
1677
"""
1678
return self.n_Hrepresentation() == 0
1679
1680
@cached_method
1681
def vertex_adjacency_matrix(self):
1682
"""
1683
Return the binary matrix of vertex adjacencies.
1684
1685
EXAMPLES::
1686
1687
sage: polytopes.n_simplex(4).vertex_adjacency_matrix()
1688
[0 1 1 1 1]
1689
[1 0 1 1 1]
1690
[1 1 0 1 1]
1691
[1 1 1 0 1]
1692
[1 1 1 1 0]
1693
1694
The rows and columns of the vertex adjacency matrix correspond
1695
to the :meth:`Vrepresentation` objects: vertices, rays, and
1696
lines. The `(i,j)` matrix entry equals `1` if the `i`-th and
1697
`j`-th V-representation object are adjacent.
1698
1699
Two vertices are adjacent if they are the endpoints of an
1700
edge, that is, a one-dimensional face. For unbounded polyhedra
1701
this clearly needs to be generalized and we define two
1702
V-representation objects (see
1703
:mod:`sage.geometry.polyhedron.constructor`) to be adjacent if
1704
they together generate a one-face. There are three possible
1705
combinations:
1706
1707
* Two vertices can bound a finite-length edge.
1708
1709
* A vertex and a ray can generate a half-infinite edge
1710
starting at the vertex and with the direction given by the
1711
ray.
1712
1713
* A vertex and a line can generate an infinite edge. The
1714
position of the vertex on the line is arbitrary in this
1715
case, only its transverse position matters. The direction of
1716
the edge is given by the line generator.
1717
1718
For example, take the half-plane::
1719
1720
sage: half_plane = Polyhedron(ieqs=[(0,1,0)])
1721
sage: half_plane.Hrepresentation()
1722
(An inequality (1, 0) x + 0 >= 0,)
1723
1724
Its (non-unique) V-representation consists of a vertex, a ray,
1725
and a line. The only edge is spanned by the vertex and the
1726
line generator, so they are adjacent::
1727
1728
sage: half_plane.Vrepresentation()
1729
(A line in the direction (0, 1), A ray in the direction (1, 0), A vertex at (0, 0))
1730
sage: half_plane.vertex_adjacency_matrix()
1731
[0 0 1]
1732
[0 0 0]
1733
[1 0 0]
1734
1735
In one dimension higher, that is for a half-space in 3
1736
dimensions, there is no one-dimensional face. Hence nothing is
1737
adjacent::
1738
1739
sage: Polyhedron(ieqs=[(0,1,0,0)]).vertex_adjacency_matrix()
1740
[0 0 0 0]
1741
[0 0 0 0]
1742
[0 0 0 0]
1743
[0 0 0 0]
1744
1745
EXAMPLES:
1746
1747
In a bounded polygon, every vertex has precisely two adjacent ones::
1748
1749
sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)])
1750
sage: for v in P.Vrep_generator():
1751
... print P.adjacency_matrix().row(v.index()), v
1752
(0, 1, 0, 1) A vertex at (0, 1)
1753
(1, 0, 1, 0) A vertex at (1, 0)
1754
(0, 1, 0, 1) A vertex at (3, 0)
1755
(1, 0, 1, 0) A vertex at (4, 1)
1756
1757
If the V-representation of the polygon contains vertices and
1758
one ray, then each V-representation object is adjacent to two
1759
V-representation objects::
1760
1761
sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)],
1762
... rays=[(0,1)])
1763
sage: for v in P.Vrep_generator():
1764
... print P.adjacency_matrix().row(v.index()), v
1765
(0, 1, 0, 0, 1) A ray in the direction (0, 1)
1766
(1, 0, 1, 0, 0) A vertex at (0, 1)
1767
(0, 1, 0, 1, 0) A vertex at (1, 0)
1768
(0, 0, 1, 0, 1) A vertex at (3, 0)
1769
(1, 0, 0, 1, 0) A vertex at (4, 1)
1770
1771
If the V-representation of the polygon contains vertices and
1772
two distinct rays, then each vertex is adjacent to two
1773
V-representation objects (which can now be vertices or
1774
rays). The two rays are not adjacent to each other::
1775
1776
sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)],
1777
... rays=[(0,1), (1,1)])
1778
sage: for v in P.Vrep_generator():
1779
... print P.adjacency_matrix().row(v.index()), v
1780
(0, 1, 0, 0, 0) A ray in the direction (0, 1)
1781
(1, 0, 1, 0, 0) A vertex at (0, 1)
1782
(0, 1, 0, 0, 1) A vertex at (1, 0)
1783
(0, 0, 0, 0, 1) A ray in the direction (1, 1)
1784
(0, 0, 1, 1, 0) A vertex at (3, 0)
1785
"""
1786
return self._vertex_adjacency_matrix()
1787
1788
adjacency_matrix = vertex_adjacency_matrix
1789
1790
@cached_method
1791
def facet_adjacency_matrix(self):
1792
"""
1793
Return the adjacency matrix for the facets and hyperplanes.
1794
1795
EXAMPLES::
1796
1797
sage: polytopes.n_simplex(4).facet_adjacency_matrix()
1798
[0 1 1 1 1]
1799
[1 0 1 1 1]
1800
[1 1 0 1 1]
1801
[1 1 1 0 1]
1802
[1 1 1 1 0]
1803
"""
1804
return self._facet_adjacency_matrix()
1805
1806
@cached_method
1807
def incidence_matrix(self):
1808
"""
1809
Return the incidence matrix.
1810
1811
.. NOTE::
1812
1813
The columns correspond to inequalities/equations in the
1814
order :meth:`Hrepresentation`, the rows correspond to
1815
vertices/rays/lines in the order
1816
:meth:`Vrepresentation`
1817
1818
EXAMPLES::
1819
1820
sage: p = polytopes.cuboctahedron()
1821
sage: p.incidence_matrix()
1822
[0 0 1 1 0 1 0 0 0 0 1 0 0 0]
1823
[0 0 0 1 0 0 1 0 1 0 1 0 0 0]
1824
[0 0 1 1 1 0 0 1 0 0 0 0 0 0]
1825
[1 0 0 1 1 0 1 0 0 0 0 0 0 0]
1826
[0 0 0 0 0 1 0 0 1 1 1 0 0 0]
1827
[0 0 1 0 0 1 0 1 0 0 0 1 0 0]
1828
[1 0 0 0 0 0 1 0 1 0 0 0 1 0]
1829
[1 0 0 0 1 0 0 1 0 0 0 0 0 1]
1830
[0 1 0 0 0 1 0 0 0 1 0 1 0 0]
1831
[0 1 0 0 0 0 0 0 1 1 0 0 1 0]
1832
[0 1 0 0 0 0 0 1 0 0 0 1 0 1]
1833
[1 1 0 0 0 0 0 0 0 0 0 0 1 1]
1834
sage: v = p.Vrepresentation(0)
1835
sage: v
1836
A vertex at (-1/2, -1/2, 0)
1837
sage: h = p.Hrepresentation(2)
1838
sage: h
1839
An inequality (1, 1, -1) x + 1 >= 0
1840
sage: h.eval(v) # evaluation (1, 1, -1) * (-1/2, -1/2, 0) + 1
1841
0
1842
sage: h*v # same as h.eval(v)
1843
0
1844
sage: p.incidence_matrix() [0,2] # this entry is (v,h)
1845
1
1846
sage: h.contains(v)
1847
True
1848
sage: p.incidence_matrix() [2,0] # note: not symmetric
1849
0
1850
"""
1851
incidence_matrix = matrix(ZZ, self.n_Vrepresentation(),
1852
self.n_Hrepresentation(), 0)
1853
for V in self.Vrep_generator():
1854
for H in self.Hrep_generator():
1855
if self._is_zero(H*V):
1856
incidence_matrix[V.index(),H.index()] = 1
1857
return incidence_matrix
1858
1859
def base_ring(self):
1860
"""
1861
Return the base ring.
1862
1863
OUTPUT:
1864
1865
Either ``QQ`` (exact arithmetic using gmp, default) or ``RDF``
1866
(double precision floating-point arithmetic)
1867
1868
EXAMPLES::
1869
1870
sage: triangle = Polyhedron(vertices = [[1,0],[0,1],[1,1]])
1871
sage: triangle.base_ring() == ZZ
1872
True
1873
"""
1874
return self.parent().base_ring()
1875
1876
field = base_ring
1877
1878
@cached_method
1879
def center(self):
1880
"""
1881
Return the average of the vertices.
1882
1883
See also :meth:`interior_point`.
1884
1885
OUTPUT:
1886
1887
The center of the polyhedron. All rays and lines are
1888
ignored. Raises a ``ZeroDivisionError`` for the empty
1889
polytope.
1890
1891
EXAMPLES::
1892
1893
sage: p = polytopes.n_cube(3)
1894
sage: p = p + vector([1,0,0])
1895
sage: p.center()
1896
(1, 0, 0)
1897
"""
1898
vertex_sum = vector(self.base_ring(), [0]*self.ambient_dim())
1899
for v in self.vertex_generator():
1900
vertex_sum += v.vector()
1901
vertex_sum.set_immutable()
1902
return vertex_sum / self.n_vertices()
1903
1904
@cached_method
1905
def representative_point(self):
1906
"""
1907
Return a "generic" point.
1908
1909
See also :meth:`center`.
1910
1911
OUTPUT:
1912
1913
A point as a coordinate vector. The point is chosen to be
1914
interior as far as possible. If the polyhedron is not
1915
full-dimensional, the point is in the relative interior. If
1916
the polyhedron is zero-dimensional, its single point is
1917
returned.
1918
1919
EXAMPLES::
1920
1921
sage: p = Polyhedron(vertices=[(3,2)], rays=[(1,-1)])
1922
sage: p.representative_point()
1923
(4, 1)
1924
sage: p.center()
1925
(3, 2)
1926
1927
sage: Polyhedron(vertices=[(3,2)]).representative_point()
1928
(3, 2)
1929
"""
1930
accumulator = vector(self.base_ring(), [0]*self.ambient_dim())
1931
for v in self.vertex_generator():
1932
accumulator += v.vector()
1933
accumulator /= self.n_vertices()
1934
for r in self.ray_generator():
1935
accumulator += r.vector()
1936
accumulator.set_immutable()
1937
return accumulator
1938
1939
1940
@cached_method
1941
def radius_square(self):
1942
"""
1943
Return the square of the maximal distance from the
1944
:meth:`center` to a vertex. All rays and lines are ignored.
1945
1946
OUTPUT:
1947
1948
The square of the radius, which is in :meth:`field`.
1949
1950
EXAMPLES::
1951
1952
sage: p = polytopes.permutahedron(4, project = False)
1953
sage: p.radius_square()
1954
5
1955
"""
1956
vertices = [ v.vector() - self.center() for v in self.vertex_generator() ]
1957
return max( v.dot_product(v) for v in vertices )
1958
1959
1960
def radius(self):
1961
"""
1962
Return the maximal distance from the center to a vertex. All
1963
rays and lines are ignored.
1964
1965
OUTPUT:
1966
1967
The radius for a rational polyhedron is, in general, not
1968
rational. use :meth:`radius_square` if you need a rational
1969
distance measure.
1970
1971
EXAMPLES::
1972
1973
sage: p = polytopes.n_cube(4)
1974
sage: p.radius()
1975
2
1976
"""
1977
return sqrt(self.radius_square())
1978
1979
def is_compact(self):
1980
"""
1981
Test for boundedness of the polytope.
1982
1983
EXAMPLES::
1984
1985
sage: p = polytopes.icosahedron()
1986
sage: p.is_compact()
1987
True
1988
sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0],[0,0,0,1],[1,-1,0,0]])
1989
sage: p.is_compact()
1990
False
1991
"""
1992
return self.n_rays()==0 and self.n_lines()==0
1993
1994
def is_simple(self):
1995
"""
1996
Test for simplicity of a polytope.
1997
1998
See :wikipedia:`Simple_polytope`
1999
2000
EXAMPLES::
2001
2002
sage: p = Polyhedron([[0,0,0],[1,0,0],[0,1,0],[0,0,1]])
2003
sage: p.is_simple()
2004
True
2005
sage: p = Polyhedron([[0,0,0],[4,4,0],[4,0,0],[0,4,0],[2,2,2]])
2006
sage: p.is_simple()
2007
False
2008
2009
"""
2010
if not self.is_compact(): return False
2011
2012
for v in self.vertex_generator():
2013
adj = [a for a in v.neighbors()]
2014
if len(adj) != self.dim():
2015
return False
2016
return True
2017
2018
def is_simplicial(self):
2019
"""
2020
Tests if the polytope is simplicial
2021
2022
A polytope is simplicial if every facet is a simplex.
2023
2024
See :wikipedia:`Simplicial_polytope`
2025
2026
EXAMPLES::
2027
2028
sage: p = polytopes.n_cube(3)
2029
sage: p.is_simplicial()
2030
False
2031
sage: q = polytopes.n_simplex(5)
2032
sage: q.is_simplicial()
2033
True
2034
sage: p = Polyhedron([[0,0,0],[1,0,0],[0,1,0],[0,0,1]])
2035
sage: p.is_simplicial()
2036
True
2037
sage: q = Polyhedron([[1,1,1],[-1,1,1],[1,-1,1],[-1,-1,1],[1,1,-1]])
2038
sage: q.is_simplicial()
2039
False
2040
2041
The method is not implemented for unbounded polyhedra::
2042
2043
sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)])
2044
sage: p.is_simplicial()
2045
Traceback (most recent call last):
2046
...
2047
NotImplementedError: This function is implemented for polytopes only.
2048
"""
2049
if not(self.is_compact()):
2050
raise NotImplementedError("This function is implemented for polytopes only.")
2051
d = self.dim()
2052
return all(len([vertex for vertex in face.incident()]) == d
2053
for face in self.Hrepresentation())
2054
2055
def hyperplane_arrangement(self):
2056
"""
2057
Return the hyperplane arrangement defined by the equations and
2058
inequalities.
2059
2060
OUTPUT:
2061
2062
A :class:`hyperplane arrangement
2063
<sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement>`
2064
consisting of the hyperplanes defined by the
2065
:meth:`~sage.geometric.hyperplane_arragement.arrangement.HyperplaneArrangementElement.Hrepresentation`.
2066
If the polytope is full-dimensional, this is the hyperplane
2067
arrangement spanned by the facets of the polyhedron.
2068
2069
EXAMPLES::
2070
2071
sage: p = polytopes.n_cube(2)
2072
sage: p.hyperplane_arrangement()
2073
Arrangement <-t0 + 1 | -t1 + 1 | t1 + 1 | t0 + 1>
2074
"""
2075
names = tuple('t'+str(i) for i in range(self.ambient_dim()))
2076
from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements
2077
field = self.base_ring().fraction_field()
2078
H = HyperplaneArrangements(field, names)
2079
return H(self)
2080
2081
@cached_method
2082
def gale_transform(self):
2083
"""
2084
Return the Gale transform of a polytope as described in the
2085
reference below.
2086
2087
OUTPUT:
2088
2089
A list of vectors, the Gale transform. The dimension is the
2090
dimension of the affine dependencies of the vertices of the
2091
polytope.
2092
2093
EXAMPLES:
2094
2095
This is from the reference, for a triangular prism::
2096
2097
sage: p = Polyhedron(vertices = [[0,0],[0,1],[1,0]])
2098
sage: p2 = p.prism()
2099
sage: p2.gale_transform()
2100
[(1, 0), (0, 1), (-1, -1), (-1, 0), (0, -1), (1, 1)]
2101
2102
REFERENCES:
2103
2104
Lectures in Geometric Combinatorics, R.R.Thomas, 2006, AMS Press.
2105
"""
2106
if not self.is_compact(): raise ValueError('Not a polytope.')
2107
2108
A = matrix(self.n_vertices(),
2109
[ [1]+x for x in self.vertex_generator()])
2110
A = A.transpose()
2111
A_ker = A.right_kernel()
2112
return A_ker.basis_matrix().transpose().rows()
2113
2114
def triangulate(self, engine='auto', connected=True, fine=False, regular=None, star=None):
2115
r"""
2116
Returns a triangulation of the polytope.
2117
2118
INPUT:
2119
2120
- ``engine`` -- either 'auto' (default), 'internal', or
2121
'TOPCOM'. The latter two instruct this package to always
2122
use its own triangulation algorithms or TOPCOM's algorithms,
2123
respectively. By default ('auto'), TOPCOM is used if it is
2124
available and internal routines otherwise.
2125
2126
The remaining keyword parameters are passed through to the
2127
:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`
2128
constructor:
2129
2130
- ``connected`` -- boolean (default: ``True``). Whether the
2131
triangulations should be connected to the regular
2132
triangulations via bistellar flips. These are much easier to
2133
compute than all triangulations.
2134
2135
- ``fine`` -- boolean (default: ``False``). Whether the
2136
triangulations must be fine, that is, make use of all points
2137
of the configuration.
2138
2139
- ``regular`` -- boolean or ``None`` (default:
2140
``None``). Whether the triangulations must be regular. A
2141
regular triangulation is one that is induced by a
2142
piecewise-linear convex support function. In other words,
2143
the shadows of the faces of a polyhedron in one higher
2144
dimension.
2145
2146
* ``True``: Only regular triangulations.
2147
2148
* ``False``: Only non-regular triangulations.
2149
2150
* ``None`` (default): Both kinds of triangulation.
2151
2152
- ``star`` -- either ``None`` (default) or a point. Whether
2153
the triangulations must be star. A triangulation is star if
2154
all maximal simplices contain a common point. The central
2155
point can be specified by its index (an integer) in the
2156
given points or by its coordinates (anything iterable.)
2157
2158
OUTPUT:
2159
2160
A triangulation of the convex hull of the vertices as a
2161
:class:`~sage.geometry.triangulation.point_configuration.Triangulation`. The
2162
indices in the triangulation correspond to the
2163
:meth:`Vrepresentation` objects.
2164
2165
EXAMPLES::
2166
2167
sage: cube = polytopes.n_cube(3)
2168
sage: triangulation = cube.triangulate(
2169
... engine='internal') # to make doctest independent of TOPCOM
2170
sage: triangulation
2171
(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)
2172
sage: simplex_indices = triangulation[0]; simplex_indices
2173
(0, 1, 2, 7)
2174
sage: simplex_vertices = [ cube.Vrepresentation(i) for i in simplex_indices ]
2175
sage: simplex_vertices
2176
[A vertex at (-1, -1, -1), A vertex at (-1, -1, 1),
2177
A vertex at (-1, 1, -1), A vertex at (1, 1, 1)]
2178
sage: Polyhedron(simplex_vertices)
2179
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices
2180
"""
2181
if not self.is_compact():
2182
raise NotImplementedError('I can only triangulate compact polytopes.')
2183
from sage.geometry.triangulation.point_configuration import PointConfiguration
2184
pc = PointConfiguration((v.vector() for v in self.vertex_generator()),
2185
connected=connected, fine=fine, regular=regular, star=star)
2186
pc.set_engine(engine)
2187
return pc.triangulate()
2188
2189
def triangulated_facial_incidences(self):
2190
"""
2191
Return a list of the form [face_index, [v_i_0,
2192
v_i_1,...,v_i_{n-1}]] where the face_index refers to the
2193
original defining inequality. For a given face, the
2194
collection of triangles formed by each list of v_i should
2195
triangulate that face.
2196
2197
In dimensions greater than 3, this is computed by randomly
2198
lifting each face up a dimension; this does not always work!
2199
This should eventually be fixed by using lrs or another
2200
program that computes triangulations.
2201
2202
EXAMPLES:
2203
2204
If the figure is already composed of triangles, then all is well::
2205
2206
sage: Polyhedron(vertices = [[5,0,0],[0,5,0],[5,5,0],[2,2,5]]
2207
... ).triangulated_facial_incidences()
2208
doctest:...: DeprecationWarning:
2209
This method is deprecated. Use triangulate() instead.
2210
See http://trac.sagemath.org/11634 for details.
2211
doctest:...: DeprecationWarning:
2212
This method is deprecated. Use self.Hrepresentation(i).incident() instead.
2213
See http://trac.sagemath.org/11763 for details.
2214
[[0, [0, 1, 2]], [1, [0, 1, 3]], [2, [0, 2, 3]], [3, [1, 2, 3]]]
2215
2216
Otherwise some faces get split up to triangles::
2217
2218
sage: Polyhedron(vertices = [[2,0,0],[4,1,0],[0,5,0],[5,5,0],
2219
... [1,1,0],[0,0,1]]).triangulated_facial_incidences()
2220
doctest:...: DeprecationWarning:
2221
This method is deprecated. Use triangulate() instead.
2222
See http://trac.sagemath.org/11634 for details.
2223
doctest:...: DeprecationWarning:
2224
This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.
2225
See http://trac.sagemath.org/11763 for details.
2226
[[0, [1, 2, 5]], [0, [2, 5, 3]], [0, [5, 3, 4]], [1, [0, 1, 2]],
2227
[2, [0, 2, 3]], [3, [0, 3, 4]], [4, [0, 4, 5]], [5, [0, 1, 5]]]
2228
"""
2229
from sage.misc.superseded import deprecation
2230
deprecation(11634, 'This method is deprecated. Use triangulate() instead.')
2231
try:
2232
return self._triangulated_facial_incidences
2233
except AttributeError:
2234
t_fac_incs = []
2235
for a_face in self.facial_incidences():
2236
vert_number = len(a_face[1])
2237
if vert_number == self.dim():
2238
t_fac_incs.append(a_face)
2239
elif self.dim() >= 4:
2240
lifted_verts = []
2241
for vert_index in a_face[1]:
2242
lifted_verts.append(self.vertices()[vert_index] +
2243
[randint(-vert_index,5000+vert_index + vert_number**2)])
2244
temp_poly = Polyhedron(vertices = lifted_verts)
2245
for t_face in temp_poly.facial_incidences():
2246
if len(t_face[1]) != self.dim():
2247
print 'Failed for face: ' + str(a_face)
2248
print 'Attempted simplicial face: ' + str(t_face)
2249
print 'Attempted lifted vertices: ' + str(lifted_verts)
2250
raise RuntimeError, "triangulation failed"
2251
normal_fdir = temp_poly.ieqs()[t_face[0]][-1]
2252
if normal_fdir >= 0:
2253
t_fac_verts = [temp_poly.vertices()[i] for i in t_face[1]]
2254
proj_verts = [q[0:self.dim()] for q in t_fac_verts]
2255
t_fac_incs.append([a_face[0],
2256
[self.vertices().index(q) for q in proj_verts]])
2257
else:
2258
vs = a_face[1][:]
2259
adj = dict([a[0], filter(lambda p: p in a_face[1], a[1])]
2260
for a in filter(lambda va: va[0] in a_face[1],
2261
self.vertex_adjacencies()))
2262
t = vs[0]
2263
vs.remove(t)
2264
ts = adj[t]
2265
for v in ts:
2266
vs.remove(v)
2267
t_fac_incs.append([a_face[0], [t] + ts])
2268
while vs:
2269
t = ts[0]
2270
ts = ts[1:]
2271
for v in adj[t]:
2272
if v in vs:
2273
vs.remove(v)
2274
ts.append(v)
2275
t_fac_incs.append([a_face[0], [t] + ts])
2276
break
2277
self._triangulated_facial_incidences = t_fac_incs
2278
return t_fac_incs
2279
2280
def simplicial_complex(self):
2281
"""
2282
Return a simplicial complex from a triangulation of the polytope.
2283
2284
Warning: This first triangulates the polytope using
2285
``triangulated_facial_incidences``, and this function may fail
2286
in dimensions greater than 3, although it usually doesn't.
2287
2288
OUTPUT:
2289
2290
A simplicial complex.
2291
2292
EXAMPLES::
2293
2294
sage: p = polytopes.cuboctahedron()
2295
sage: sc = p.simplicial_complex()
2296
doctest:...: DeprecationWarning:
2297
This method is deprecated. Use triangulate().simplicial_complex() instead.
2298
See http://trac.sagemath.org/11634 for details.
2299
doctest:...: DeprecationWarning:
2300
This method is deprecated. Use triangulate() instead.
2301
See http://trac.sagemath.org/11634 for details.
2302
sage: sc
2303
Simplicial complex with 12 vertices and 20 facets
2304
"""
2305
from sage.misc.superseded import deprecation
2306
deprecation(11634, 'This method is deprecated. Use triangulate().simplicial_complex() instead.')
2307
from sage.homology.simplicial_complex import SimplicialComplex
2308
return SimplicialComplex([x[1] for x in self.triangulated_facial_incidences()])
2309
2310
@coerce_binop
2311
def Minkowski_sum(self, other):
2312
"""
2313
Return the Minkowski sum.
2314
2315
Minkowski addition of two subsets of a vector space is defined
2316
as
2317
2318
.. math::
2319
2320
X \oplus Y =
2321
\cup_{y\in Y} (X+y) =
2322
\cup_{x\in X, y\in Y} (x+y)
2323
2324
See :meth:`Minkowski_difference` for a partial inverse operation.
2325
2326
INPUT:
2327
2328
- ``other`` -- a :class:`Polyhedron_base`.
2329
2330
OUTPUT:
2331
2332
The Minkowski sum of ``self`` and ``other``.
2333
2334
EXAMPLES::
2335
2336
sage: X = polytopes.n_cube(3)
2337
sage: Y = Polyhedron(vertices=[(0,0,0), (0,0,1/2), (0,1/2,0), (1/2,0,0)])
2338
sage: X+Y
2339
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 13 vertices
2340
2341
sage: four_cube = polytopes.n_cube(4)
2342
sage: four_simplex = Polyhedron(vertices = [[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])
2343
sage: four_cube + four_simplex
2344
A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 36 vertices
2345
sage: four_cube.Minkowski_sum(four_simplex) == four_cube + four_simplex
2346
True
2347
2348
sage: poly_spam = Polyhedron([[3,4,5,2],[1,0,0,1],[0,0,0,0],[0,4,3,2],[-3,-3,-3,-3]], base_ring=ZZ)
2349
sage: poly_eggs = Polyhedron([[5,4,5,4],[-4,5,-4,5],[4,-5,4,-5],[0,0,0,0]], base_ring=QQ)
2350
sage: poly_spam + poly_spam + poly_eggs
2351
A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 12 vertices
2352
"""
2353
new_vertices = []
2354
for v1 in self.vertex_generator():
2355
for v2 in other.vertex_generator():
2356
new_vertices.append(list(v1() + v2()))
2357
if new_vertices != []:
2358
new_rays = self.rays() + other.rays()
2359
new_lines = self.lines() + other.lines()
2360
return self.parent().element_class(self.parent(), [new_vertices, new_rays, new_lines], None)
2361
else:
2362
return self.parent().element_class(self.parent(), None, None)
2363
2364
_add_ = Minkowski_sum
2365
2366
@coerce_binop
2367
def Minkowski_difference(self, other):
2368
"""
2369
Return the Minkowski difference.
2370
2371
Minkowski subtraction can equivalently be defined via
2372
Minkowski addition (see :meth:`Minkowski_sum`) or as
2373
set-theoretic intersection via
2374
2375
.. math::
2376
2377
X \ominus Y =
2378
(X^c \oplus Y)^c =
2379
\cap_{y\in Y} (X-y)
2380
2381
where superscript-"c" means the complement in the ambient
2382
vector space. The Minkowski difference of convex sets is
2383
convex, and the difference of polyhedra is again a
2384
polyhedron. We only consider the case of polyhedra in the
2385
following. Note that it is not quite the inverse of
2386
addition. In fact:
2387
2388
* `(X+Y)-Y = X` for any polyhedra `X`, `Y`.
2389
2390
* `(X-Y)+Y \subseteq X`
2391
2392
* `(X-Y)+Y = X` if and only if Y is a Minkowski summand of X.
2393
2394
INPUT:
2395
2396
- ``other`` -- a :class:`Polyhedron_base`.
2397
2398
OUTPUT:
2399
2400
The Minkowski difference of ``self`` and ``other``. Also known
2401
as Minkowski subtraction of ``other`` from ``self``.
2402
2403
EXAMPLES::
2404
2405
sage: X = polytopes.n_cube(3)
2406
sage: Y = Polyhedron(vertices=[(0,0,0), (0,0,1), (0,1,0), (1,0,0)]) / 2
2407
sage: (X+Y)-Y == X
2408
True
2409
sage: (X-Y)+Y < X
2410
True
2411
2412
The polyhedra need not be full-dimensional::
2413
2414
sage: X2 = Polyhedron(vertices=[(-1,-1,0),(1,-1,0),(-1,1,0),(1,1,0)])
2415
sage: Y2 = Polyhedron(vertices=[(0,0,0), (0,1,0), (1,0,0)]) / 2
2416
sage: (X2+Y2)-Y2 == X2
2417
True
2418
sage: (X2-Y2)+Y2 < X2
2419
True
2420
2421
Minus sign is really an alias for :meth:`Minkowski_difference`
2422
::
2423
2424
sage: four_cube = polytopes.n_cube(4)
2425
sage: four_simplex = Polyhedron(vertices = [[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])
2426
sage: four_cube - four_simplex
2427
A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices
2428
sage: four_cube.Minkowski_difference(four_simplex) == four_cube - four_simplex
2429
True
2430
2431
Coercion of the base ring works::
2432
2433
sage: poly_spam = Polyhedron([[3,4,5,2],[1,0,0,1],[0,0,0,0],[0,4,3,2],[-3,-3,-3,-3]], base_ring=ZZ)
2434
sage: poly_eggs = Polyhedron([[5,4,5,4],[-4,5,-4,5],[4,-5,4,-5],[0,0,0,0]], base_ring=QQ) / 100
2435
sage: poly_spam - poly_eggs
2436
A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices
2437
2438
TESTS::
2439
2440
sage: X = polytopes.n_cube(2)
2441
sage: Y = Polyhedron(vertices=[(1,1)])
2442
sage: (X-Y).Vrepresentation()
2443
(A vertex at (0, -2), A vertex at (0, 0), A vertex at (-2, 0), A vertex at (-2, -2))
2444
2445
sage: Y = Polyhedron(vertices=[(1,1), (0,0)])
2446
sage: (X-Y).Vrepresentation()
2447
(A vertex at (0, -1), A vertex at (0, 0), A vertex at (-1, 0), A vertex at (-1, -1))
2448
2449
sage: X = X + Y # now Y is a Minkowski summand of X
2450
sage: (X+Y)-Y == X
2451
True
2452
sage: (X-Y)+Y == X
2453
True
2454
"""
2455
if other.is_empty():
2456
return self.parent().universe() # empty intersection = everything
2457
if not other.is_compact():
2458
raise NotImplementedError('only subtracting compact polyhedra is implemented')
2459
new_eqns = []
2460
for eq in self.equations():
2461
values = [ eq.A() * v.vector() for v in other.vertices() ]
2462
eq = list(eq)
2463
eq[0] += min(values) # shift constant term
2464
new_eqns.append(eq)
2465
P = self.parent()
2466
new_ieqs = []
2467
for ieq in self.inequalities():
2468
values = [ ieq.A() * v.vector() for v in other.vertices() ]
2469
ieq = list(ieq)
2470
ieq[0] += min(values) # shift constant term
2471
new_ieqs.append(ieq)
2472
P = self.parent()
2473
return P.element_class(P, None, [new_ieqs, new_eqns])
2474
2475
def __sub__(self, other):
2476
"""
2477
Implement minus binary operation
2478
2479
Polyhedra are not a ring with respect to dilatation and
2480
Minkowski sum, for example `X\oplus(-1)*Y \not= X\ominus Y`.
2481
2482
INPUT:
2483
2484
- ``other`` -- a translation vector or a polyhedron.
2485
2486
OUTPUT:
2487
2488
Either translation by the negative of the given vector or
2489
Minkowski subtraction by the given polyhedron.
2490
2491
EXAMPLES::
2492
2493
sage: X = polytopes.n_cube(2)
2494
sage: v = vector([1,1])
2495
sage: (X - v/2).Vrepresentation()
2496
(A vertex at (-3/2, -3/2), A vertex at (-3/2, 1/2),
2497
A vertex at (1/2, -3/2), A vertex at (1/2, 1/2))
2498
sage: (X-v)+v == X
2499
True
2500
2501
sage: Y = Polyhedron(vertices=[(1/2,0),(0,1/2)])
2502
sage: (X-Y).Vrepresentation()
2503
(A vertex at (1/2, -1), A vertex at (1/2, 1/2),
2504
A vertex at (-1, 1/2), A vertex at (-1, -1))
2505
sage: (X+Y)-Y == X
2506
True
2507
"""
2508
if is_Polyhedron(other):
2509
return self.Minkowski_difference(other)
2510
return self + (-other)
2511
2512
def is_Minkowski_summand(self, Y):
2513
"""
2514
Test whether ``Y`` is a Minkowski summand.
2515
2516
See :meth:`Minkowski_sum`.
2517
2518
OUTPUT:
2519
2520
Boolean. Whether there exists another polyhedron `Z` such that
2521
``self`` can be written as `Y\oplus Z`.
2522
2523
EXAMPLES::
2524
2525
sage: A = polytopes.n_cube(2)
2526
sage: B = Polyhedron(vertices=[(0,1), (1/2,1)])
2527
sage: C = Polyhedron(vertices=[(1,1)])
2528
sage: A.is_Minkowski_summand(B)
2529
True
2530
sage: A.is_Minkowski_summand(C)
2531
True
2532
sage: B.is_Minkowski_summand(C)
2533
True
2534
sage: B.is_Minkowski_summand(A)
2535
False
2536
sage: C.is_Minkowski_summand(A)
2537
False
2538
sage: C.is_Minkowski_summand(B)
2539
False
2540
"""
2541
return self.Minkowski_difference(Y).Minkowski_sum(Y) == self
2542
2543
def translation(self, displacement):
2544
"""
2545
Return the translated polyhedron.
2546
2547
INPUT:
2548
2549
- ``displacement`` -- a displacement vector or a list/tuple of
2550
coordinates that determines a displacement vector.
2551
2552
OUTPUT:
2553
2554
The translated polyhedron.
2555
2556
EXAMPLES::
2557
2558
sage: P = Polyhedron([[0,0],[1,0],[0,1]], base_ring=ZZ)
2559
sage: P.translation([2,1])
2560
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices
2561
sage: P.translation( vector(QQ,[2,1]) )
2562
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices
2563
"""
2564
displacement = vector(displacement)
2565
new_vertices = [x.vector()+displacement for x in self.vertex_generator()]
2566
new_rays = self.rays()
2567
new_lines = self.lines()
2568
new_ring = self.parent()._coerce_base_ring(displacement)
2569
return Polyhedron(vertices=new_vertices, rays=new_rays, lines=new_lines, base_ring=new_ring)
2570
2571
@coerce_binop
2572
def product(self, other):
2573
"""
2574
Return the cartesian product.
2575
2576
INPUT:
2577
2578
- ``other`` -- a :class:`Polyhedron_base`.
2579
2580
OUTPUT:
2581
2582
The cartesian product of ``self`` and ``other`` with a
2583
suitable base ring to encompass the two.
2584
2585
EXAMPLES::
2586
2587
sage: P1 = Polyhedron([[0],[1]], base_ring=ZZ)
2588
sage: P2 = Polyhedron([[0],[1]], base_ring=QQ)
2589
sage: P1.product(P2)
2590
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
2591
2592
The cartesian product is the product in the semiring of polyhedra::
2593
2594
sage: P1 * P1
2595
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
2596
sage: P1 * P2
2597
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
2598
sage: P2 * P2
2599
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
2600
sage: 2 * P1
2601
A 1-dimensional polyhedron in ZZ^1 defined as the convex hull of 2 vertices
2602
sage: P1 * 2.0
2603
A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices
2604
"""
2605
new_vertices = [ list(x)+list(y)
2606
for x in self.vertex_generator() for y in other.vertex_generator()]
2607
new_rays = []
2608
new_rays.extend( [ r+[0]*other.ambient_dim()
2609
for r in self.ray_generator() ] )
2610
new_rays.extend( [ [0]*self.ambient_dim()+r
2611
for r in other.ray_generator() ] )
2612
new_lines = []
2613
new_lines.extend( [ l+[0]*other.ambient_dim()
2614
for l in self.line_generator() ] )
2615
new_lines.extend( [ [0]*self.ambient_dim()+l
2616
for l in other.line_generator() ] )
2617
return Polyhedron(vertices=new_vertices,
2618
rays=new_rays, lines=new_lines,
2619
base_ring=self.parent()._coerce_base_ring(other))
2620
2621
_mul_ = product
2622
2623
def dilation(self, scalar):
2624
"""
2625
Return the dilated (uniformly stretched) polyhedron.
2626
2627
INPUT:
2628
2629
- ``scalar`` -- A scalar, not necessarily in :meth:`base_ring`.
2630
2631
OUTPUT:
2632
2633
The polyhedron dilated by that scalar, possibly coerced to a
2634
bigger field.
2635
2636
EXAMPLES::
2637
2638
sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,6)])
2639
sage: p.vertex_generator().next()
2640
A vertex at (2, 4, 8)
2641
sage: p2 = p.dilation(2)
2642
sage: p2.vertex_generator().next()
2643
A vertex at (4, 8, 16)
2644
sage: p.dilation(2) == p * 2
2645
True
2646
2647
TESTS:
2648
2649
Dilation of empty polyhedrons works, see :trac:`14987`::
2650
2651
sage: p = Polyhedron(ambient_dim=2); p
2652
The empty polyhedron in ZZ^2
2653
sage: p.dilation(3)
2654
The empty polyhedron in ZZ^2
2655
2656
TESTS::
2657
2658
sage: p = Polyhedron(vertices=[(1,1)], rays=[(1,0)], lines=[(0,1)])
2659
sage: (-p).rays()
2660
(A ray in the direction (-1, 0),)
2661
sage: (-p).lines()
2662
(A line in the direction (0, 1),)
2663
2664
sage: (0*p).rays()
2665
()
2666
sage: (0*p).lines()
2667
()
2668
"""
2669
if scalar > 0:
2670
new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ]
2671
new_rays = self.rays()
2672
new_lines = self.lines()
2673
elif scalar < 0:
2674
new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ]
2675
new_rays = [ list(-r.vector()) for r in self.ray_generator()]
2676
new_lines = self.lines()
2677
else:
2678
new_vertices = [ self.ambient_space().zero() for v in self.vertex_generator() ]
2679
new_rays = []
2680
new_lines = []
2681
return Polyhedron(vertices=new_vertices,
2682
rays=new_rays, lines=new_lines,
2683
base_ring=self.parent()._coerce_base_ring(scalar),
2684
ambient_dim=self.ambient_dim())
2685
2686
def _acted_upon_(self, actor, self_on_left):
2687
"""
2688
Implement the multiplicative action by scalars or other polyhedra.
2689
2690
INPUT:
2691
2692
- ``actor`` -- A scalar, not necessarily in :meth:`base_ring`,
2693
or a :class:`Polyhedron`.
2694
2695
OUTPUT:
2696
2697
Multiplication by another polyhedron returns the product
2698
polytope. Multiplication by a scalar returns the polytope
2699
dilated by that scalar, possibly coerced to the bigger field.
2700
2701
EXAMPLES::
2702
2703
sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,6)])
2704
sage: p._acted_upon_(2, True) == p.dilation(2)
2705
True
2706
sage: p*2 == p.dilation(2)
2707
True
2708
sage: p*p == p.product(p)
2709
True
2710
sage: p + vector(ZZ,[1,2,3]) == p.translation([1,2,3])
2711
True
2712
"""
2713
if is_Polyhedron(actor):
2714
return self.product(actor)
2715
if is_Vector(actor):
2716
return self.translation(actor)
2717
else:
2718
return self.dilation(actor)
2719
2720
def __neg__(self):
2721
"""
2722
Negation of a polytope is defined as inverting the coordinates.
2723
2724
EXAMPLES::
2725
2726
sage: t = polytopes.n_simplex(3,project=False); t.vertices()
2727
(A vertex at (0, 0, 0, 1), A vertex at (0, 0, 1, 0),
2728
A vertex at (0, 1, 0, 0), A vertex at (1, 0, 0, 0))
2729
sage: neg_ = -t
2730
sage: neg_.vertices()
2731
(A vertex at (-1, 0, 0, 0), A vertex at (0, -1, 0, 0),
2732
A vertex at (0, 0, -1, 0), A vertex at (0, 0, 0, -1))
2733
2734
TESTS::
2735
2736
sage: p = Polyhedron(ieqs=[[1,1,0]])
2737
sage: p.rays()
2738
(A ray in the direction (1, 0),)
2739
sage: pneg = p.__neg__()
2740
sage: pneg.rays()
2741
(A ray in the direction (-1, 0),)
2742
"""
2743
return self.dilation(-1)
2744
2745
def __div__(self, scalar):
2746
"""
2747
Divide by a scalar factor.
2748
2749
See :meth:`dilation` for details.
2750
2751
EXAMPLES::
2752
2753
sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,4)])
2754
sage: (p/5).Vrepresentation()
2755
(A vertex at (2/5, 4/5, 8/5), A vertex at (3/5, 9/5, 27/5))
2756
"""
2757
return self.dilation(1/scalar)
2758
2759
@coerce_binop
2760
def convex_hull(self, other):
2761
"""
2762
Return the convex hull of the set-theoretic union of the two
2763
polyhedra.
2764
2765
INPUT:
2766
2767
- ``other`` -- a :class:`Polyhedron`.
2768
2769
OUTPUT:
2770
2771
The convex hull.
2772
2773
EXAMPLES::
2774
2775
sage: a_simplex = polytopes.n_simplex(3)
2776
sage: verts = a_simplex.vertices()
2777
sage: verts = [[x[0]*3/5+x[1]*4/5, -x[0]*4/5+x[1]*3/5, x[2]] for x in verts]
2778
sage: another_simplex = Polyhedron(vertices = verts)
2779
sage: simplex_union = a_simplex.convex_hull(another_simplex)
2780
sage: simplex_union.n_vertices()
2781
7
2782
"""
2783
hull_vertices = self.vertices() + other.vertices()
2784
hull_rays = self.rays() + other.rays()
2785
hull_lines = self.lines() + other.lines()
2786
return self.parent().element_class(self.parent(), [hull_vertices, hull_rays, hull_lines], None)
2787
2788
@coerce_binop
2789
def intersection(self, other):
2790
"""
2791
Return the intersection of one polyhedron with another.
2792
2793
INPUT:
2794
2795
- ``other`` -- a :class:`Polyhedron`.
2796
2797
OUTPUT:
2798
2799
The intersection.
2800
2801
Note that the intersection of two `\ZZ`-polyhedra might not be
2802
a `\ZZ`-polyhedron. In this case, a `\QQ`-polyhedron is
2803
returned.
2804
2805
EXAMPLES::
2806
2807
sage: cube = polytopes.n_cube(3)
2808
sage: oct = polytopes.cross_polytope(3)
2809
sage: cube.intersection(oct*2)
2810
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 12 vertices
2811
2812
As a shorthand, one may use::
2813
2814
sage: cube & oct*2
2815
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 12 vertices
2816
2817
The intersection of two `\ZZ`-polyhedra is not necessarily a `\ZZ`-polyhedron::
2818
2819
sage: P = Polyhedron([(0,0),(1,1)], base_ring=ZZ)
2820
sage: P.intersection(P)
2821
A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices
2822
sage: Q = Polyhedron([(0,1),(1,0)], base_ring=ZZ)
2823
sage: P.intersection(Q)
2824
A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex
2825
sage: _.Vrepresentation()
2826
(A vertex at (1/2, 1/2),)
2827
"""
2828
new_ieqs = self.inequalities() + other.inequalities()
2829
new_eqns = self.equations() + other.equations()
2830
parent = self.parent()
2831
try:
2832
return parent.element_class(parent, None, [new_ieqs, new_eqns])
2833
except TypeError,msg:
2834
if self.base_ring() is ZZ:
2835
parent = parent.base_extend(QQ)
2836
return parent.element_class(parent, None, [new_ieqs, new_eqns])
2837
else:
2838
raise TypeError(msg)
2839
2840
__and__ = intersection
2841
2842
def edge_truncation(self, cut_frac = Integer(1)/3):
2843
r"""
2844
Return a new polyhedron formed from two points on each edge
2845
between two vertices.
2846
2847
INPUT:
2848
2849
- ``cut_frac`` -- integer. how deeply to cut into the edge.
2850
Default is `\frac{1}{3}`.
2851
2852
OUTPUT:
2853
2854
A Polyhedron object, truncated as described above.
2855
2856
EXAMPLES::
2857
2858
sage: cube = polytopes.n_cube(3)
2859
sage: trunc_cube = cube.edge_truncation()
2860
sage: trunc_cube.n_vertices()
2861
24
2862
sage: trunc_cube.n_inequalities()
2863
14
2864
"""
2865
new_vertices = []
2866
for e in self.bounded_edges():
2867
new_vertices.append((1-cut_frac)*e[0]() + cut_frac *e[1]())
2868
new_vertices.append(cut_frac *e[0]() + (1-cut_frac)*e[1]())
2869
2870
new_vertices = [list(v) for v in new_vertices]
2871
new_rays = self.rays()
2872
new_lines = self.lines()
2873
2874
return Polyhedron(vertices=new_vertices, rays=new_rays,
2875
lines=new_lines,
2876
base_ring=self.parent()._coerce_base_ring(cut_frac))
2877
2878
def _make_polyhedron_face(self, Vindices, Hindices):
2879
"""
2880
Construct a face of the polyhedron.
2881
2882
INPUT:
2883
2884
- ``Vindices`` -- a tuple of integers. The indices of the
2885
V-represenation objects that span the face.
2886
2887
- ``Hindices`` -- a tuple of integers. The indices of the
2888
H-representation objects that hold as equalities on the
2889
face.
2890
2891
OUTPUT:
2892
2893
A new :class:`~sage.geometry.polyhedron.face.PolyhedronFace` instance. It is not checked
2894
whether the input data actually defines a face.
2895
2896
EXAMPLES::
2897
2898
sage: square = polytopes.n_cube(2)
2899
sage: square._make_polyhedron_face((0,2), (1,))
2900
<0,2>
2901
"""
2902
from sage.geometry.polyhedron.face import PolyhedronFace
2903
return PolyhedronFace(self, Vindices, Hindices)
2904
2905
@cached_method
2906
def face_lattice(self):
2907
"""
2908
Return the face-lattice poset.
2909
2910
OUTPUT:
2911
2912
A :class:`~sage.combinat.posets.posets.FinitePoset`. Elements
2913
are given as
2914
:class:`~sage.geometry.polyhedron.face.PolyhedronFace`.
2915
2916
In the case of a full-dimensional polytope, the faces are
2917
pairs (vertices, inequalities) of the spanning vertices and
2918
corresponding saturated inequalities. In general, a face is
2919
defined by a pair (V-rep. objects, H-rep. objects). The
2920
V-representation objects span the face, and the corresponding
2921
H-representation objects are those inequalities and equations
2922
that are saturated on the face.
2923
2924
The bottom-most element of the face lattice is the "empty
2925
face". It contains no V-representation object. All
2926
H-representation objects are incident.
2927
2928
The top-most element is the "full face". It is spanned by all
2929
V-representation objects. The incident H-representation
2930
objects are all equations and no inequalities.
2931
2932
In the case of a full-dimensional polytope, the "empty face"
2933
and the "full face" are the empty set (no vertices, all
2934
inequalities) and the full polytope (all vertices, no
2935
inequalities), respectively.
2936
2937
ALGORITHM:
2938
2939
For a full-dimensional polytope, the basic algorithm is
2940
described in
2941
:func:`~sage.geometry.hasse_diagram.Hasse_diagram_from_incidences`.
2942
There are three generalizations of [KP2002]_ necessary to deal
2943
with more general polytopes, corresponding to the extra
2944
H/V-representation objects:
2945
2946
* Lines are removed before calling
2947
:func:`Hasse_diagram_from_incidences`, and then added back
2948
to each face V-representation except for the "empty face".
2949
2950
* Equations are removed before calling
2951
:func:`Hasse_diagram_from_incidences`, and then added back
2952
to each face H-representation.
2953
2954
* Rays: Consider the half line as an example. The
2955
V-representation objects are a point and a ray, which we can
2956
think of as a point at infinity. However, the point at
2957
infinity has no inequality associated to it, so there is
2958
only one H-representation object alltogether. The face
2959
lattice does not contain the "face at infinity". This means
2960
that in :func:`Hasse_diagram_from_incidences`, one needs to
2961
drop faces with V-representations that have no matching
2962
H-representation. In addition, one needs to ensure that
2963
every non-empty face contains at least one vertex.
2964
2965
EXAMPLES::
2966
2967
sage: square = polytopes.n_cube(2)
2968
sage: square.face_lattice()
2969
Finite poset containing 10 elements
2970
sage: list(_)
2971
[<>, <0>, <1>, <2>, <3>, <0,1>, <0,2>, <2,3>, <1,3>, <0,1,2,3>]
2972
sage: poset_element = _[6]
2973
sage: a_face = poset_element
2974
sage: a_face
2975
<0,2>
2976
sage: a_face.dim()
2977
1
2978
sage: set(a_face.ambient_Vrepresentation()) == \
2979
... set([square.Vrepresentation(0), square.Vrepresentation(2)])
2980
True
2981
sage: a_face.ambient_Vrepresentation()
2982
(A vertex at (-1, -1), A vertex at (1, -1))
2983
sage: a_face.ambient_Hrepresentation()
2984
(An inequality (0, 1) x + 1 >= 0,)
2985
2986
A more complicated example::
2987
2988
sage: c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)])
2989
sage: c5_10_fl = c5_10.face_lattice()
2990
sage: [len(x) for x in c5_10_fl.level_sets()]
2991
[1, 10, 45, 100, 105, 42, 1]
2992
2993
Note that if the polyhedron contains lines then there is a
2994
dimension gap between the empty face and the first non-empty
2995
face in the face lattice::
2996
2997
sage: line = Polyhedron(vertices=[(0,)], lines=[(1,)])
2998
sage: [ fl.dim() for fl in line.face_lattice() ]
2999
[-1, 1]
3000
3001
TESTS::
3002
3003
sage: c5_20 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5]
3004
... for i in range(1,21)])
3005
sage: c5_20_fl = c5_20.face_lattice() # long time
3006
sage: [len(x) for x in c5_20_fl.level_sets()] # long time
3007
[1, 20, 190, 580, 680, 272, 1]
3008
sage: polytopes.n_cube(2).face_lattice().plot()
3009
sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets()
3010
sage: print level_sets[0], level_sets[-1]
3011
[<>] [<0,1,2,3>]
3012
3013
Various degenerate polyhedra::
3014
3015
sage: Polyhedron(vertices=[[0,0,0],[1,0,0],[0,1,0]]).face_lattice().level_sets()
3016
[[<>], [<0>, <1>, <2>], [<0,1>, <0,2>, <1,2>], [<0,1,2>]]
3017
sage: Polyhedron(vertices=[(1,0,0),(0,1,0)], rays=[(0,0,1)]).face_lattice().level_sets()
3018
[[<>], [<1>, <2>], [<0,1>, <0,2>, <1,2>], [<0,1,2>]]
3019
sage: Polyhedron(rays=[(1,0,0),(0,1,0)], vertices=[(0,0,1)]).face_lattice().level_sets()
3020
[[<>], [<0>], [<0,1>, <0,2>], [<0,1,2>]]
3021
sage: Polyhedron(rays=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()
3022
[[<>], [<0>], [<0,1>, <0,2>], [<0,1,2>]]
3023
sage: Polyhedron(vertices=[(1,),(0,)]).face_lattice().level_sets()
3024
[[<>], [<0>, <1>], [<0,1>]]
3025
sage: Polyhedron(vertices=[(1,0,0),(0,1,0)], lines=[(0,0,1)]).face_lattice().level_sets()
3026
[[<>], [<0,1>, <0,2>], [<0,1,2>]]
3027
sage: Polyhedron(lines=[(1,0,0)], vertices=[(0,0,1)]).face_lattice().level_sets()
3028
[[<>], [<0,1>]]
3029
sage: Polyhedron(lines=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()
3030
[[<>], [<0,1,2>]]
3031
sage: Polyhedron(lines=[(1,0)], rays=[(0,1)], vertices=[(0,0)])\
3032
... .face_lattice().level_sets()
3033
[[<>], [<0,1>], [<0,1,2>]]
3034
sage: Polyhedron(vertices=[(0,)], lines=[(1,)]).face_lattice().level_sets()
3035
[[<>], [<0,1>]]
3036
sage: Polyhedron(lines=[(1,0)], vertices=[(0,0)]).face_lattice().level_sets()
3037
[[<>], [<0,1>]]
3038
3039
REFERENCES:
3040
3041
.. [KP2002]
3042
3043
Volker Kaibel and Marc E. Pfetsch, "Computing the Face
3044
Lattice of a Polytope from its Vertex-Facet Incidences",
3045
Computational Geometry: Theory and Applications, Volume
3046
23, Issue 3 (November 2002), 281-290. Available at
3047
http://portal.acm.org/citation.cfm?id=763203 and free of
3048
charge at http://arxiv.org/abs/math/0106043
3049
"""
3050
coatom_to_Hindex = [ h.index() for h in self.inequality_generator() ]
3051
Hindex_to_coatom = [None] * self.n_Hrepresentation()
3052
for i in range(0,len(coatom_to_Hindex)):
3053
Hindex_to_coatom[ coatom_to_Hindex[i] ] = i
3054
3055
atom_to_Vindex = [ v.index() for v in self.Vrep_generator() if not v.is_line() ]
3056
Vindex_to_atom = [None] * self.n_Vrepresentation()
3057
for i in range(0,len(atom_to_Vindex)):
3058
Vindex_to_atom[ atom_to_Vindex[i] ] = i
3059
3060
atoms_incidences = [ tuple([ Hindex_to_coatom[h.index()]
3061
for h in v.incident() if h.is_inequality() ])
3062
for v in self.Vrepresentation() if not v.is_line() ]
3063
3064
coatoms_incidences = [ tuple([ Vindex_to_atom[v.index()]
3065
for v in h.incident() if not v.is_line() ])
3066
for h in self.Hrepresentation() if h.is_inequality() ]
3067
3068
atoms_vertices = [ Vindex_to_atom[v.index()] for v in self.vertex_generator() ]
3069
equations = [ e.index() for e in self.equation_generator() ]
3070
lines = [ l.index() for l in self.line_generator() ]
3071
3072
def face_constructor(atoms,coatoms):
3073
if len(atoms)==0:
3074
Vindices = ()
3075
else:
3076
Vindices = tuple(sorted([ atom_to_Vindex[i] for i in atoms ]+lines))
3077
Hindices = tuple(sorted([ coatom_to_Hindex[i] for i in coatoms ]+equations))
3078
return self._make_polyhedron_face(Vindices, Hindices)
3079
3080
from sage.geometry.hasse_diagram import Hasse_diagram_from_incidences
3081
return Hasse_diagram_from_incidences\
3082
(atoms_incidences, coatoms_incidences,
3083
face_constructor=face_constructor, required_atoms=atoms_vertices)
3084
3085
def faces(self, face_dimension):
3086
"""
3087
Return the faces of given dimension
3088
3089
INPUT:
3090
3091
- ``face_dimension`` -- integer. The dimension of the faces
3092
whose representation will be returned.
3093
3094
OUTPUT:
3095
3096
A tuple of
3097
:class:`~sage.geometry.polyhedron.face.PolyhedronFace`. See
3098
:mod:`~sage.geometry.polyhedron.face` for details. The order
3099
random but fixed.
3100
3101
EXAMPLES:
3102
3103
Here we find the vertex and face indices of the eight three-dimensional
3104
facets of the four-dimensional hypercube::
3105
3106
sage: p = polytopes.n_cube(4)
3107
sage: p.faces(3)
3108
(<0,1,2,3,4,5,6,7>, <0,1,2,3,8,9,10,11>, <0,1,4,5,8,9,12,13>,
3109
<0,2,4,6,8,10,12,14>, <2,3,6,7,10,11,14,15>, <8,9,10,11,12,13,14,15>,
3110
<4,5,6,7,12,13,14,15>, <1,3,5,7,9,11,13,15>)
3111
3112
sage: face = p.faces(3)[0]
3113
sage: face.ambient_Hrepresentation()
3114
(An inequality (1, 0, 0, 0) x + 1 >= 0,)
3115
sage: face.vertices()
3116
(A vertex at (-1, -1, -1, -1), A vertex at (-1, -1, -1, 1),
3117
A vertex at (-1, -1, 1, -1), A vertex at (-1, -1, 1, 1),
3118
A vertex at (-1, 1, -1, -1), A vertex at (-1, 1, -1, 1),
3119
A vertex at (-1, 1, 1, -1), A vertex at (-1, 1, 1, 1))
3120
3121
You can use the
3122
:meth:`~sage.geometry.polyhedron.representation.PolyhedronRepresentation.index`
3123
method to enumerate vertices and inequalities::
3124
3125
sage: def get_idx(rep): return rep.index()
3126
sage: map(get_idx, face.ambient_Hrepresentation())
3127
[4]
3128
sage: map(get_idx, face.ambient_Vrepresentation())
3129
[0, 1, 2, 3, 4, 5, 6, 7]
3130
3131
sage: [ (map(get_idx, face.ambient_Vrepresentation()), map(get_idx, face.ambient_Hrepresentation()))
3132
... for face in p.faces(3) ]
3133
[([0, 1, 2, 3, 4, 5, 6, 7], [4]),
3134
([0, 1, 2, 3, 8, 9, 10, 11], [5]),
3135
([0, 1, 4, 5, 8, 9, 12, 13], [6]),
3136
([0, 2, 4, 6, 8, 10, 12, 14], [7]),
3137
([2, 3, 6, 7, 10, 11, 14, 15], [2]),
3138
([8, 9, 10, 11, 12, 13, 14, 15], [0]),
3139
([4, 5, 6, 7, 12, 13, 14, 15], [1]),
3140
([1, 3, 5, 7, 9, 11, 13, 15], [3])]
3141
3142
TESTS::
3143
3144
sage: pr = Polyhedron(rays = [[1,0,0],[-1,0,0],[0,1,0]], vertices = [[-1,-1,-1]], lines=[(0,0,1)])
3145
sage: pr.faces(4)
3146
()
3147
sage: pr.faces(3)
3148
(<0,1,2,3>,)
3149
sage: pr.faces(2)
3150
(<0,1,2>,)
3151
sage: pr.faces(1)
3152
()
3153
sage: pr.faces(0)
3154
()
3155
sage: pr.faces(-1)
3156
()
3157
"""
3158
fl = self.face_lattice().level_sets()
3159
codim = self.dim() - face_dimension
3160
index = len(fl) - 1 - codim
3161
if index>=len(fl) or index<1:
3162
return tuple()
3163
return tuple(fl[index])
3164
3165
@cached_method
3166
def f_vector(self):
3167
r"""
3168
Return the f-vector.
3169
3170
OUTPUT:
3171
3172
Returns a vector whose ``i``-th entry is the number of
3173
``i``-dimensional faces of the polytope.
3174
3175
EXAMPLES::
3176
3177
sage: p = Polyhedron(vertices=[[1, 2, 3], [1, 3, 2],
3178
... [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1], [0, 0, 0]])
3179
sage: p.f_vector()
3180
(1, 7, 12, 7, 1)
3181
"""
3182
return vector(ZZ,[len(x) for x in self.face_lattice().level_sets()])
3183
3184
@cached_method
3185
def vertex_graph(self):
3186
"""
3187
Return a graph in which the vertices correspond to vertices
3188
of the polyhedron, and edges to edges.
3189
3190
EXAMPLES::
3191
3192
sage: g3 = polytopes.n_cube(3).vertex_graph()
3193
sage: len(g3.automorphism_group())
3194
48
3195
sage: s4 = polytopes.n_simplex(4).vertex_graph()
3196
sage: s4.is_eulerian()
3197
True
3198
"""
3199
return Graph(self.vertex_adjacency_matrix(), loops=True)
3200
3201
graph = vertex_graph
3202
3203
def polar(self):
3204
"""
3205
Return the polar (dual) polytope.
3206
3207
The original vertices are translated so that their barycenter
3208
is at the origin, and then the vertices are used as the
3209
coefficients in the polar inequalities.
3210
3211
EXAMPLES::
3212
3213
sage: p = Polyhedron(vertices = [[0,0,1],[0,1,0],[1,0,0],[0,0,0],[1,1,1]], base_ring=QQ)
3214
sage: p
3215
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 5 vertices
3216
sage: p.polar()
3217
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 6 vertices
3218
3219
sage: cube = polytopes.n_cube(3)
3220
sage: octahedron = polytopes.cross_polytope(3)
3221
sage: cube_dual = cube.polar()
3222
sage: octahedron == cube_dual
3223
True
3224
"""
3225
assert self.is_compact(), "Not a polytope."
3226
3227
verts = [list(self.center() - v.vector()) for v in self.vertex_generator()]
3228
base_ring = self.parent()._coerce_base_ring(self.center().parent())
3229
return Polyhedron(ieqs=[[1] + list(v) for v in verts], base_ring=base_ring)
3230
3231
def pyramid(self):
3232
"""
3233
Returns a polyhedron that is a pyramid over the original.
3234
3235
EXAMPLES::
3236
3237
sage: square = polytopes.n_cube(2); square
3238
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
3239
sage: egyptian_pyramid = square.pyramid(); egyptian_pyramid
3240
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 5 vertices
3241
sage: egyptian_pyramid.n_vertices()
3242
5
3243
sage: for v in egyptian_pyramid.vertex_generator(): print v
3244
A vertex at (0, -1, -1)
3245
A vertex at (0, -1, 1)
3246
A vertex at (0, 1, -1)
3247
A vertex at (0, 1, 1)
3248
A vertex at (1, 0, 0)
3249
"""
3250
new_verts = \
3251
[[0] + x for x in self.Vrep_generator()] + \
3252
[[1] + list(self.center())]
3253
return Polyhedron(vertices=new_verts)
3254
3255
def bipyramid(self):
3256
"""
3257
Return a polyhedron that is a bipyramid over the original.
3258
3259
EXAMPLES::
3260
3261
sage: octahedron = polytopes.cross_polytope(3)
3262
sage: cross_poly_4d = octahedron.bipyramid()
3263
sage: cross_poly_4d.n_vertices()
3264
8
3265
sage: q = [list(v) for v in cross_poly_4d.vertex_generator()]
3266
sage: q
3267
[[-1, 0, 0, 0],
3268
[0, -1, 0, 0],
3269
[0, 0, -1, 0],
3270
[0, 0, 0, -1],
3271
[0, 0, 0, 1],
3272
[0, 0, 1, 0],
3273
[0, 1, 0, 0],
3274
[1, 0, 0, 0]]
3275
3276
Now check that bipyramids of cross-polytopes are cross-polytopes::
3277
3278
sage: q2 = [list(v) for v in polytopes.cross_polytope(4).vertex_generator()]
3279
sage: [v in q2 for v in q]
3280
[True, True, True, True, True, True, True, True]
3281
"""
3282
new_verts = \
3283
[[ 0] + list(x) for x in self.vertex_generator()] + \
3284
[[ 1] + list(self.center())] + \
3285
[[-1] + list(self.center())]
3286
new_rays = [[0] + r for r in self.rays()]
3287
new_lines = [[0] + list(l) for l in self.lines()]
3288
return Polyhedron(vertices=new_verts, rays=new_rays, lines=new_lines)
3289
3290
def prism(self):
3291
"""
3292
Return a prism of the original polyhedron.
3293
3294
EXAMPLES::
3295
3296
sage: square = polytopes.n_cube(2)
3297
sage: cube = square.prism()
3298
sage: cube
3299
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
3300
sage: hypercube = cube.prism()
3301
sage: hypercube.n_vertices()
3302
16
3303
"""
3304
new_verts = []
3305
new_verts.extend( [ [0] + v for v in self.vertices()] )
3306
new_verts.extend( [ [1] + v for v in self.vertices()] )
3307
new_rays = [ [0] + r for r in self.rays()]
3308
new_lines = [ [0] + l for l in self.lines()]
3309
return Polyhedron(vertices=new_verts, rays=new_rays, lines=new_lines,
3310
base_ring=self.base_ring())
3311
3312
def projection(self):
3313
"""
3314
Return a projection object.
3315
3316
EXAMPLES::
3317
3318
sage: p = polytopes.n_cube(3)
3319
sage: proj = p.projection()
3320
sage: proj
3321
The projection of a polyhedron into 3 dimensions
3322
"""
3323
from plot import Projection
3324
self.projection = Projection(self)
3325
return self.projection
3326
3327
def render_solid(self, **kwds):
3328
"""
3329
Return a solid rendering of a 2- or 3-d polytope.
3330
3331
EXAMPLES::
3332
3333
sage: p = polytopes.n_cube(3)
3334
sage: p_solid = p.render_solid(opacity = .7)
3335
sage: type(p_solid)
3336
<class 'sage.plot.plot3d.base.Graphics3dGroup'>
3337
"""
3338
proj = self.projection()
3339
if self.ambient_dim()==3:
3340
return proj.render_solid_3d(**kwds)
3341
if self.ambient_dim()==2:
3342
return proj.render_fill_2d(**kwds)
3343
raise ValueError, "render_solid is only defined for 2 and 3 dimensional polyhedra."
3344
3345
def render_wireframe(self, **kwds):
3346
"""
3347
For polytopes in 2 or 3 dimensions, return the edges
3348
as a list of lines.
3349
3350
EXAMPLES::
3351
3352
sage: p = Polyhedron([[1,2,],[1,1],[0,0]])
3353
sage: p_wireframe = p.render_wireframe()
3354
sage: p_wireframe._objects
3355
[Line defined by 2 points, Line defined by 2 points, Line defined by 2 points]
3356
"""
3357
proj = self.projection()
3358
if self.ambient_dim()==3:
3359
return proj.render_wireframe_3d(**kwds)
3360
if self.ambient_dim()==2:
3361
return proj.render_outline_2d(**kwds)
3362
raise ValueError, "render_wireframe is only defined for 2 and 3 dimensional polyhedra."
3363
3364
def schlegel_projection(self, projection_dir = None, height = 1.1):
3365
"""
3366
Returns a projection object whose transformed coordinates are
3367
a Schlegel projection of the polyhedron.
3368
3369
EXAMPLES::
3370
3371
sage: p = polytopes.n_cube(3)
3372
sage: sch_proj = p.schlegel_projection()
3373
sage: schlegel_edge_indices = sch_proj.lines
3374
sage: schlegel_edges = [sch_proj.coordinates_of(x) for x in schlegel_edge_indices]
3375
sage: len([x for x in schlegel_edges if x[0][0] > 0])
3376
4
3377
"""
3378
proj = self.projection()
3379
if projection_dir == None:
3380
vertices = self.vertices()
3381
facet = self.Hrepresentation(0)
3382
f0 = [ v.index() for v in facet.incident() ]
3383
projection_dir = [sum([vertices[f0[i]][j]/len(f0) for i in range(len(f0))])
3384
for j in range(self.ambient_dim())]
3385
return proj.schlegel(projection_direction = projection_dir, height = height)
3386
3387
def _volume_lrs(self, verbose=False):
3388
"""
3389
Computes the volume of a polytope using lrs.
3390
3391
OUTPUT:
3392
3393
The volume, cast to RDF (although lrs seems to output a
3394
rational value this must be an approximation in some cases).
3395
3396
EXAMPLES::
3397
3398
sage: polytopes.n_cube(3)._volume_lrs() #optional - lrs
3399
8.0
3400
sage: (polytopes.n_cube(3)*2)._volume_lrs() #optional - lrs
3401
64.0
3402
sage: polytopes.twenty_four_cell()._volume_lrs() #optional - lrs
3403
2.0
3404
3405
REFERENCES:
3406
3407
David Avis's lrs program.
3408
"""
3409
if is_package_installed('lrs') != True:
3410
print 'You must install the optional lrs package ' \
3411
'for this function to work'
3412
raise NotImplementedError
3413
3414
from sage.misc.temporary_file import tmp_filename
3415
from subprocess import Popen, PIPE
3416
in_str = self.cdd_Vrepresentation()
3417
in_str += 'volume'
3418
in_filename = tmp_filename()
3419
in_file = file(in_filename,'w')
3420
in_file.write(in_str)
3421
in_file.close()
3422
if verbose: print in_str
3423
3424
lrs_procs = Popen(['lrs',in_filename],
3425
stdin = PIPE, stdout=PIPE, stderr=PIPE)
3426
ans, err = lrs_procs.communicate()
3427
if verbose:
3428
print ans
3429
# FIXME: check err
3430
3431
for a_line in ans.splitlines():
3432
if 'Volume=' in a_line:
3433
volume = a_line.split('Volume=')[1]
3434
volume = RDF(QQ(volume))
3435
return volume
3436
3437
raise ValueError("lrs did not return a volume")
3438
3439
def lrs_volume(self, verbose=False):
3440
"""
3441
Computes the volume of a polytope using lrs.
3442
3443
OUTPUT:
3444
3445
The volume, cast to RDF (although lrs seems to output a
3446
rational value this must be an approximation in some cases).
3447
3448
EXAMPLES::
3449
3450
sage: polytopes.n_cube(3).lrs_volume() #optional - lrs
3451
doctest:...: DeprecationWarning: use volume(engine='lrs') instead
3452
See http://trac.sagemath.org/13249 for details.
3453
8.0
3454
sage: (polytopes.n_cube(3)*2).lrs_volume() #optional - lrs
3455
64.0
3456
sage: polytopes.twenty_four_cell().lrs_volume() #optional - lrs
3457
2.0
3458
3459
REFERENCES:
3460
3461
David Avis's lrs program.
3462
"""
3463
from sage.misc.superseded import deprecation
3464
deprecation(13249, "use volume(engine='lrs') instead")
3465
return self._volume_lrs(verbose=verbose)
3466
3467
@cached_method
3468
def volume(self, engine='auto', **kwds):
3469
"""
3470
Return the volume of the polytope.
3471
3472
- ``engine`` -- string. The backend to use. Allowed values are:
3473
3474
* ``'auto'`` (default): see :meth:`triangulate`.
3475
* ``'internal'``: see :meth:`triangulate`.
3476
* ``'TOPCOM'``: see :meth:`triangulate`.
3477
* ``'lrs'``: use David Avis's lrs program (optional).
3478
3479
- ``**kwds`` -- keyword arguments that are passed to the
3480
triangulation engine.
3481
3482
OUTPUT:
3483
3484
The volume of the polytope.
3485
3486
EXAMPLES::
3487
3488
sage: polytopes.n_cube(3).volume()
3489
8
3490
sage: (polytopes.n_cube(3)*2).volume()
3491
64
3492
sage: polytopes.twenty_four_cell().volume()
3493
2
3494
3495
sage: polytopes.regular_polygon(5, base_ring=RDF).volume()
3496
2.37764129...
3497
sage: P5 = polytopes.regular_polygon(5, base_ring=QQ)
3498
sage: P5.volume() # rational approximation
3499
3387471714099766473500515673753476175274812279494567801326487870013/1424719417220622426561086640229666223984528142237277803327699435400
3500
sage: _.n()
3501
2.37764129...
3502
3503
Volume of the same polytope, using the optional package lrs::
3504
3505
sage: P5.volume(engine='lrs') #optional - lrs
3506
2.37764129...
3507
"""
3508
if engine=='lrs':
3509
return self._volume_lrs(**kwds)
3510
dim = self.dim()
3511
if dim < self.ambient_dim():
3512
return self.base_ring().zero()
3513
triangulation = self.triangulate(engine=engine, **kwds)
3514
pc = triangulation.point_configuration()
3515
return sum([ pc.volume(simplex) for simplex in triangulation ]) / ZZ(dim).factorial()
3516
3517
def contains(self, point):
3518
"""
3519
Test whether the polyhedron contains the given ``point``.
3520
3521
See also :meth:`interior_contains` and
3522
:meth:`relative_interior_contains`.
3523
3524
INPUT:
3525
3526
- ``point`` -- coordinates of a point (an iterable).
3527
3528
OUTPUT:
3529
3530
Boolean.
3531
3532
EXAMPLES::
3533
3534
sage: P = Polyhedron(vertices=[[1,1],[1,-1],[0,0]])
3535
sage: P.contains( [1,0] )
3536
True
3537
sage: P.contains( P.center() ) # true for any convex set
3538
True
3539
3540
As a shorthand, one may use the usual ``in`` operator::
3541
3542
sage: P.center() in P
3543
True
3544
sage: [-1,-1] in P
3545
False
3546
3547
The point need not have coordinates in the same field as the
3548
polyhedron::
3549
3550
sage: ray = Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ)
3551
sage: ray.contains([sqrt(2)/3,0]) # irrational coordinates are ok
3552
True
3553
sage: a = var('a')
3554
sage: ray.contains([a,0]) # a might be negative!
3555
False
3556
sage: assume(a>0)
3557
sage: ray.contains([a,0])
3558
True
3559
sage: ray.contains(['hello', 'kitty']) # no common ring for coordinates
3560
False
3561
3562
The empty polyhedron needs extra care, see trac #10238::
3563
3564
sage: empty = Polyhedron(); empty
3565
The empty polyhedron in ZZ^0
3566
sage: empty.contains([])
3567
False
3568
sage: empty.contains([0]) # not a point in QQ^0
3569
False
3570
sage: full = Polyhedron(vertices=[()]); full
3571
A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex
3572
sage: full.contains([])
3573
True
3574
sage: full.contains([0])
3575
False
3576
"""
3577
try:
3578
p = vector(point)
3579
except TypeError: # point not iterable or no common ring for elements
3580
if len(point)>0:
3581
return False
3582
else:
3583
p = vector(self.base_ring(), [])
3584
3585
if len(p)!=self.ambient_dim():
3586
return False
3587
3588
for H in self.Hrep_generator():
3589
if not H.contains(p):
3590
return False
3591
return True
3592
3593
__contains__ = contains
3594
3595
def interior_contains(self, point):
3596
"""
3597
Test whether the interior of the polyhedron contains the
3598
given ``point``.
3599
3600
See also :meth:`contains` and
3601
:meth:`relative_interior_contains`.
3602
3603
INPUT:
3604
3605
- ``point`` -- coordinates of a point.
3606
3607
OUTPUT:
3608
3609
``True`` or ``False``.
3610
3611
EXAMPLES::
3612
3613
sage: P = Polyhedron(vertices=[[0,0],[1,1],[1,-1]])
3614
sage: P.contains( [1,0] )
3615
True
3616
sage: P.interior_contains( [1,0] )
3617
False
3618
3619
If the polyhedron is of strictly smaller dimension than the
3620
ambient space, its interior is empty::
3621
3622
sage: P = Polyhedron(vertices=[[0,1],[0,-1]])
3623
sage: P.contains( [0,0] )
3624
True
3625
sage: P.interior_contains( [0,0] )
3626
False
3627
3628
The empty polyhedron needs extra care, see trac #10238::
3629
3630
sage: empty = Polyhedron(); empty
3631
The empty polyhedron in ZZ^0
3632
sage: empty.interior_contains([])
3633
False
3634
"""
3635
try:
3636
p = vector(point)
3637
except TypeError: # point not iterable or no common ring for elements
3638
if len(point)>0:
3639
return False
3640
else:
3641
p = vector(self.base_ring(), [])
3642
3643
if len(p)!=self.ambient_dim():
3644
return False
3645
3646
for H in self.Hrep_generator():
3647
if not H.interior_contains(p):
3648
return False
3649
return True
3650
3651
def relative_interior_contains(self, point):
3652
"""
3653
Test whether the relative interior of the polyhedron
3654
contains the given ``point``.
3655
3656
See also :meth:`contains` and :meth:`interior_contains`.
3657
3658
INPUT:
3659
3660
- ``point`` -- coordinates of a point.
3661
3662
OUTPUT:
3663
3664
``True`` or ``False``.
3665
3666
EXAMPLES::
3667
3668
sage: P = Polyhedron(vertices=[(1,0), (-1,0)])
3669
sage: P.contains( (0,0) )
3670
True
3671
sage: P.interior_contains( (0,0) )
3672
False
3673
sage: P.relative_interior_contains( (0,0) )
3674
True
3675
sage: P.relative_interior_contains( (1,0) )
3676
False
3677
3678
The empty polyhedron needs extra care, see trac #10238::
3679
3680
sage: empty = Polyhedron(); empty
3681
The empty polyhedron in ZZ^0
3682
sage: empty.relative_interior_contains([])
3683
False
3684
"""
3685
try:
3686
p = vector(point)
3687
except TypeError: # point not iterable or no common ring for elements
3688
if len(point)>0:
3689
return False
3690
else:
3691
p = vector(self.base_ring(), [])
3692
3693
if len(p)!=self.ambient_dim():
3694
return False
3695
3696
for eq in self.equation_generator():
3697
if not eq.contains(p):
3698
return False
3699
3700
for ine in self.inequality_generator():
3701
if not ine.interior_contains(p):
3702
return False
3703
3704
return True
3705
3706
def is_simplex(self):
3707
r"""
3708
Return whether the polyhedron is a simplex.
3709
3710
EXAMPLES::
3711
3712
sage: Polyhedron([(0,0,0), (1,0,0), (0,1,0)]).is_simplex()
3713
True
3714
sage: polytopes.n_simplex(3).is_simplex()
3715
True
3716
sage: polytopes.n_cube(3).is_simplex()
3717
False
3718
"""
3719
return self.is_compact() and (self.dim()+1==self.n_vertices())
3720
3721
@cached_method
3722
def is_lattice_polytope(self):
3723
r"""
3724
Return whether the polyhedron is a lattice polytope.
3725
3726
OUTPUT:
3727
3728
``True`` if the polyhedron is compact and has only integral
3729
vertices, ``False`` otherwise.
3730
3731
EXAMPLES::
3732
3733
sage: polytopes.cross_polytope(3).is_lattice_polytope()
3734
True
3735
sage: polytopes.regular_polygon(5).is_lattice_polytope()
3736
False
3737
"""
3738
if not self.is_compact():
3739
return False
3740
if self.base_ring() is ZZ:
3741
return True
3742
return all(v.is_integral() for v in self.vertex_generator())
3743
3744
@cached_method
3745
def lattice_polytope(self, envelope=False):
3746
r"""
3747
Return an encompassing lattice polytope.
3748
3749
INPUT:
3750
3751
- ``envelope`` -- boolean (default: ``False``). If the
3752
polyhedron has non-integral vertices, this option decides
3753
whether to return a strictly larger lattice polytope or
3754
raise a ``ValueError``. This option has no effect if the
3755
polyhedron has already integral vertices.
3756
3757
OUTPUT:
3758
3759
A :class:`LatticePolytope
3760
<sage.geometry.lattice_polytope.LatticePolytopeClass>`. If the
3761
polyhedron is compact and has integral vertices, the lattice
3762
polytope equals the polyhedron. If the polyhedron is compact
3763
but has at least one non-integral vertex, a strictly larger
3764
lattice polytope is returned.
3765
3766
If the polyhedron is not compact, a ``NotImplementedError`` is
3767
raised.
3768
3769
If the polyhedron is not integral and ``envelope=False``, a
3770
``ValueError`` is raised.
3771
3772
ALGORITHM:
3773
3774
For each non-integral vertex, a bounding box of integral
3775
points is added and the convex hull of these integral points
3776
is returned.
3777
3778
EXAMPLES:
3779
3780
First, a polyhedron with integral vertices::
3781
3782
sage: P = Polyhedron( vertices = [(1, 0), (0, 1), (-1, 0), (0, -1)])
3783
sage: lp = P.lattice_polytope(); lp
3784
A lattice polytope: 2-dimensional, 4 vertices.
3785
sage: lp.vertices()
3786
[-1 0 0 1]
3787
[ 0 -1 1 0]
3788
3789
Here is a polyhedron with non-integral vertices::
3790
3791
sage: P = Polyhedron( vertices = [(1/2, 1/2), (0, 1), (-1, 0), (0, -1)])
3792
sage: lp = P.lattice_polytope()
3793
Traceback (most recent call last):
3794
...
3795
ValueError: Some vertices are not integral. You probably want
3796
to add the argument "envelope=True" to compute an enveloping
3797
lattice polytope.
3798
sage: lp = P.lattice_polytope(True); lp
3799
A lattice polytope: 2-dimensional, 5 vertices.
3800
sage: lp.vertices()
3801
[-1 0 0 1 1]
3802
[ 0 -1 1 0 1]
3803
"""
3804
if not self.is_compact():
3805
raise NotImplementedError, 'Only compact lattice polytopes are allowed.'
3806
3807
try:
3808
vertices = self.vertices_matrix(ZZ)
3809
except TypeError:
3810
if envelope==False:
3811
raise ValueError, 'Some vertices are not integral. '+\
3812
'You probably want to add the argument '+\
3813
'"envelope=True" to compute an enveloping lattice polytope.'
3814
vertices = []
3815
for v in self.vertex_generator():
3816
vbox = [ set([floor(x),ceil(x)]) for x in v ]
3817
vertices.extend( CartesianProduct(*vbox) )
3818
vertices = matrix(ZZ, vertices).transpose()
3819
3820
# construct the (enveloping) lattice polytope
3821
from sage.geometry.lattice_polytope import LatticePolytope
3822
return LatticePolytope(vertices)
3823
3824
def _integral_points_PALP(self):
3825
r"""
3826
Return the integral points in the polyhedron using PALP.
3827
3828
This method is for testing purposes and will eventually be removed.
3829
3830
OUTPUT:
3831
3832
The list of integral points in the polyhedron. If the
3833
polyhedron is not compact, a ``ValueError`` is raised.
3834
3835
EXAMPLES::
3836
3837
sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)])._integral_points_PALP()
3838
[(-1, -1), (0, 1), (1, 0), (1, 1), (0, 0)]
3839
sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)]).lattice_polytope(True).points()
3840
[ 0 -1 -1 0 1 1 0]
3841
[-1 0 -1 1 0 1 0]
3842
sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)])._integral_points_PALP()
3843
[(0, 1), (1, 0), (1, 1), (0, 0)]
3844
"""
3845
if not self.is_compact():
3846
raise ValueError, 'Can only enumerate points in a compact polyhedron.'
3847
lp = self.lattice_polytope(True)
3848
# remove cached values to get accurate timings
3849
try:
3850
del lp._points
3851
del lp._npoints
3852
except AttributeError:
3853
pass
3854
if self.is_lattice_polytope():
3855
return lp.points().columns()
3856
points = filter(lambda p: self.contains(p),
3857
lp.points().columns())
3858
return points
3859
3860
@cached_method
3861
def bounding_box(self, integral=False):
3862
r"""
3863
Return the coordinates of a rectangular box containing the non-empty polytope.
3864
3865
INPUT:
3866
3867
- ``integral`` -- Boolean (default: ``False``). Whether to
3868
only allow integral coordinates in the bounding box.
3869
3870
OUTPUT:
3871
3872
A pair of tuples ``(box_min, box_max)`` where ``box_min`` are
3873
the coordinates of a point bounding the coordinates of the
3874
polytope from below and ``box_max`` bounds the coordinates
3875
from above.
3876
3877
EXAMPLES::
3878
3879
sage: Polyhedron([ (1/3,2/3), (2/3, 1/3) ]).bounding_box()
3880
((1/3, 1/3), (2/3, 2/3))
3881
sage: Polyhedron([ (1/3,2/3), (2/3, 1/3) ]).bounding_box(integral=True)
3882
((0, 0), (1, 1))
3883
sage: polytopes.buckyball().bounding_box()
3884
((-1059/1309, -1059/1309, -1059/1309), (1059/1309, 1059/1309, 1059/1309))
3885
"""
3886
box_min = []
3887
box_max = []
3888
if self.n_vertices==0:
3889
raise ValueError('Empty polytope is not allowed')
3890
if not self.is_compact():
3891
raise ValueError('Only polytopes (compact polyhedra) are allowed.')
3892
for i in range(0,self.ambient_dim()):
3893
coords = [ v[i] for v in self.vertex_generator() ]
3894
max_coord = max(coords)
3895
min_coord = min(coords)
3896
if integral:
3897
box_max.append(ceil(max_coord))
3898
box_min.append(floor(min_coord))
3899
else:
3900
box_max.append(max_coord)
3901
box_min.append(min_coord)
3902
return (tuple(box_min), tuple(box_max))
3903
3904
def integral_points(self, threshold=100000):
3905
r"""
3906
Return the integral points in the polyhedron.
3907
3908
Uses either the naive algorithm (iterate over a rectangular
3909
bounding box) or triangulation + Smith form.
3910
3911
INPUT:
3912
3913
- ``threshold`` -- integer (default: 100000). Use the naive
3914
algorith as long as the bounding box is smaller than this.
3915
3916
OUTPUT:
3917
3918
The list of integral points in the polyhedron. If the
3919
polyhedron is not compact, a ``ValueError`` is raised.
3920
3921
EXAMPLES::
3922
3923
sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]).integral_points()
3924
((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))
3925
3926
sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)])
3927
sage: simplex.integral_points()
3928
((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
3929
3930
The polyhedron need not be full-dimensional::
3931
3932
sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)])
3933
sage: simplex.integral_points()
3934
((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))
3935
3936
sage: point = Polyhedron([(2,3,7)])
3937
sage: point.integral_points()
3938
((2, 3, 7),)
3939
3940
sage: empty = Polyhedron()
3941
sage: empty.integral_points()
3942
()
3943
3944
Here is a simplex where the naive algorithm of running over
3945
all points in a rectangular bounding box no longer works fast
3946
enough::
3947
3948
sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
3949
sage: simplex = Polyhedron(v); simplex
3950
A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
3951
sage: len(simplex.integral_points())
3952
49
3953
3954
Finally, the 3-d reflexive polytope number 4078::
3955
3956
sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
3957
... (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
3958
sage: P = Polyhedron(v)
3959
sage: pts1 = P.integral_points() # Sage's own code
3960
sage: all(P.contains(p) for p in pts1)
3961
True
3962
sage: pts2 = LatticePolytope(v).points().columns() # PALP
3963
sage: for p in pts1: p.set_immutable()
3964
sage: for p in pts2: p.set_immutable()
3965
sage: set(pts1) == set(pts2)
3966
True
3967
3968
sage: timeit('Polyhedron(v).integral_points()') # random output
3969
sage: timeit('LatticePolytope(v).points()') # random output
3970
"""
3971
if not self.is_compact():
3972
raise ValueError('Can only enumerate points in a compact polyhedron.')
3973
if self.n_vertices() == 0:
3974
return tuple()
3975
3976
# for small bounding boxes, it is faster to naively iterate over the points of the box
3977
box_min, box_max = self.bounding_box(integral=True)
3978
box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max))
3979
if not self.is_lattice_polytope() or \
3980
(self.is_simplex() and box_points<1000) or \
3981
box_points<threshold:
3982
from sage.geometry.integral_points import rectangular_box_points
3983
return rectangular_box_points(box_min, box_max, self)
3984
3985
# for more complicate polytopes, triangulate & use smith normal form
3986
from sage.geometry.integral_points import simplex_points
3987
if self.is_simplex():
3988
return simplex_points(self.Vrepresentation())
3989
triangulation = self.triangulate()
3990
points = set()
3991
for simplex in triangulation:
3992
triang_vertices = [ self.Vrepresentation(i) for i in simplex ]
3993
new_points = simplex_points(triang_vertices)
3994
for p in new_points:
3995
p.set_immutable()
3996
points.update(new_points)
3997
# assert all(self.contains(p) for p in points) # slow
3998
return tuple(points)
3999
4000
@cached_method
4001
def combinatorial_automorphism_group(self):
4002
"""
4003
Computes the combinatorial automorphism group of the vertex
4004
graph of the polyhedron.
4005
4006
OUTPUT:
4007
4008
A
4009
:class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
4010
that is isomorphic to the combinatorial automorphism group is
4011
returned.
4012
4013
Note that in Sage, permutation groups always act on positive
4014
integers while ``self.Vrepresentation()`` is indexed by
4015
nonnegative integers. The indexing of the permutation group is
4016
chosen to be shifted by ``+1``. That is, ``i`` in the
4017
permutation group corresponds to the V-representation object
4018
``self.Vrepresentation(i-1)``.
4019
4020
EXAMPLES::
4021
4022
sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)])
4023
sage: quadrangle.combinatorial_automorphism_group()
4024
Permutation Group with generators [(2,3), (1,2)(3,4)]
4025
sage: quadrangle.restricted_automorphism_group()
4026
Permutation Group with generators [()]
4027
4028
Permutations can only exchange vertices with vertices, rays
4029
with rays, and lines with lines::
4030
4031
sage: P = Polyhedron(vertices=[(1,0,0), (1,1,0)], rays=[(1,0,0)], lines=[(0,0,1)])
4032
sage: P.combinatorial_automorphism_group()
4033
Permutation Group with generators [(3,4)]
4034
"""
4035
from sage.groups.perm_gps.permgroup import PermutationGroup
4036
G = Graph()
4037
for edge in self.vertex_graph().edges():
4038
i = edge[0]
4039
j = edge[1]
4040
G.add_edge(i+1, j+1, (self.Vrepresentation(i).type(), self.Vrepresentation(j).type()) )
4041
4042
group = G.automorphism_group(edge_labels=True)
4043
self._combinatorial_automorphism_group = group
4044
return group
4045
4046
def _affine_coordinates(self, Vrep_object):
4047
r"""
4048
Return affine coordinates for a V-representation object.
4049
4050
INPUT:
4051
4052
- ``v`` -- a V-representation object or any iterable
4053
containing ``self.ambient_dim()`` coordinates. The
4054
coordinates must specify a point in the affine plane
4055
containing the polyhedron, or the output will be invalid. No
4056
checks on the input are performed.
4057
4058
OUTPUT:
4059
4060
A ``self.dim()``-dimensional coordinate vector. It contains
4061
the coordinates of ``v`` in an arbitrary but fixed basis for
4062
the affine span of the polyhedron.
4063
4064
EXAMPLES::
4065
4066
sage: P = Polyhedron(rays=[(1,0,0),(0,1,0)])
4067
sage: P._affine_coordinates( (-1,-2,-3) )
4068
(-1, -2)
4069
sage: [ P._affine_coordinates(v) for v in P.Vrep_generator() ]
4070
[(0, 0), (0, 1), (1, 0)]
4071
"""
4072
if '_affine_coordinates_pivots' not in self.__dict__:
4073
v_list = [ vector(v) for v in self.Vrepresentation() ]
4074
if len(v_list)>0:
4075
origin = v_list[0]
4076
v_list = [ v - origin for v in v_list ]
4077
coordinates = matrix(v_list)
4078
self._affine_coordinates_pivots = coordinates.pivots()
4079
4080
v = list(Vrep_object)
4081
if len(v) != self.ambient_dim():
4082
raise ValueError('Incorrect dimension: '+str(v))
4083
4084
return vector(self.base_ring(), [ v[i] for i in self._affine_coordinates_pivots ])
4085
4086
@cached_method
4087
def restricted_automorphism_group(self):
4088
r"""
4089
Return the restricted automorphism group.
4090
4091
First, let the linear automorphism group be the subgroup of
4092
the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`
4093
preserving the `d`-dimensional polyhedron. The Euclidean group
4094
acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
4095
ambient space.
4096
4097
The restricted automorphism group is the subgroup of the linear
4098
automorphism group generated by permutations of the generators
4099
of the same type. That is, vertices can only be permuted with
4100
vertices, ray generators with ray generators, and line
4101
generators with line generators.
4102
4103
For example, take the first quadrant
4104
4105
.. MATH::
4106
4107
Q = \Big\{ (x,y) \Big| x\geq 0,\; y\geq0 \Big\}
4108
\subset \QQ^2
4109
4110
Then the linear automorphism group is
4111
4112
.. MATH::
4113
4114
\mathrm{Aut}(Q) =
4115
\left\{
4116
\begin{pmatrix}
4117
a & 0 \\ 0 & b
4118
\end{pmatrix}
4119
,~
4120
\begin{pmatrix}
4121
0 & c \\ d & 0
4122
\end{pmatrix}
4123
:~
4124
a, b, c, d \in \QQ_{>0}
4125
\right\}
4126
\subset
4127
GL(2,\QQ)
4128
\subset
4129
E(d)
4130
4131
Note that there are no translations that map the quadrant `Q`
4132
to itself, so the linear automorphism group is contained in
4133
the subgroup of rotations of the whole Euclidean group. The
4134
restricted automorphism group is
4135
4136
.. MATH::
4137
4138
\mathrm{Aut}(Q) =
4139
\left\{
4140
\begin{pmatrix}
4141
1 & 0 \\ 0 & 1
4142
\end{pmatrix}
4143
,~
4144
\begin{pmatrix}
4145
0 & 1 \\ 1 & 0
4146
\end{pmatrix}
4147
\right\}
4148
\simeq \ZZ_2
4149
4150
OUTPUT:
4151
4152
A :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
4153
that is isomorphic to the restricted automorphism group is
4154
returned.
4155
4156
Note that in Sage, permutation groups always act on positive
4157
integers while ``self.Vrepresentation()`` is indexed by
4158
nonnegative integers. The indexing of the permutation group is
4159
chosen to be shifted by ``+1``. That is, ``i`` in the
4160
permutation group corresponds to the V-representation object
4161
``self.Vrepresentation(i-1)``.
4162
4163
REFERENCES:
4164
4165
.. [BSS]
4166
David Bremner, Mathieu Dutour Sikiric, Achill Schuermann:
4167
Polyhedral representation conversion up to symmetries.
4168
http://arxiv.org/abs/math/0702239
4169
4170
EXAMPLES::
4171
4172
sage: P = polytopes.cross_polytope(3)
4173
sage: AutP = P.restricted_automorphism_group(); AutP
4174
Permutation Group with generators [(3,4), (2,3)(4,5), (2,5), (1,2)(5,6), (1,6)]
4175
sage: P24 = polytopes.twenty_four_cell()
4176
sage: AutP24 = P24.restricted_automorphism_group()
4177
sage: PermutationGroup([
4178
... '(3,6)(4,7)(10,11)(14,15)(18,21)(19,22)',
4179
... '(2,3)(7,8)(11,12)(13,14)(17,18)(22,23)',
4180
... '(2,5)(3,10)(6,11)(8,17)(9,13)(12,16)(14,19)(15,22)(20,23)',
4181
... '(2,10)(3,5)(6,12)(7,18)(9,14)(11,16)(13,19)(15,23)(20,22)',
4182
... '(2,11)(3,12)(4,21)(5,6)(9,15)(10,16)(13,22)(14,23)(19,20)',
4183
... '(1,2)(3,4)(6,7)(8,9)(12,13)(16,17)(18,19)(21,22)(23,24)',
4184
... '(1,24)(2,13)(3,14)(5,9)(6,15)(10,19)(11,22)(12,23)(16,20)'
4185
... ]) == AutP24
4186
True
4187
4188
Here is the quadrant example mentioned in the beginning::
4189
4190
sage: P = Polyhedron(rays=[(1,0),(0,1)])
4191
sage: P.Vrepresentation()
4192
(A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0))
4193
sage: P.restricted_automorphism_group()
4194
Permutation Group with generators [(2,3)]
4195
4196
Also, the polyhedron need not be full-dimensional::
4197
4198
sage: P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)])
4199
sage: P.restricted_automorphism_group()
4200
Permutation Group with generators [(1,2)]
4201
4202
Translations do not change the restricted automorphism
4203
group. For example, any non-degenerate triangle has the
4204
dihedral group with 6 elements, `D_6`, as its automorphism
4205
group::
4206
4207
sage: initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])]
4208
sage: points = initial_points
4209
sage: Polyhedron(vertices=points).restricted_automorphism_group()
4210
Permutation Group with generators [(2,3), (1,2)]
4211
sage: points = [pt - initial_points[0] for pt in initial_points]
4212
sage: Polyhedron(vertices=points).restricted_automorphism_group()
4213
Permutation Group with generators [(2,3), (1,2)]
4214
sage: points = [pt - initial_points[1] for pt in initial_points]
4215
sage: Polyhedron(vertices=points).restricted_automorphism_group()
4216
Permutation Group with generators [(2,3), (1,2)]
4217
sage: points = [pt - 2*initial_points[1] for pt in initial_points]
4218
sage: Polyhedron(vertices=points).restricted_automorphism_group()
4219
Permutation Group with generators [(2,3), (1,2)]
4220
4221
Floating-point computations are supported with a simple fuzzy
4222
zero implementation::
4223
4224
sage: P = Polyhedron(vertices=[(1.0/3.0,0,0),(0,1.0/3.0,0),(0,0,1.0/3.0)], base_ring=RDF)
4225
sage: P.restricted_automorphism_group()
4226
Permutation Group with generators [(2,3), (1,2)]
4227
4228
TESTS::
4229
4230
sage: p = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])
4231
sage: p.restricted_automorphism_group()
4232
Permutation Group with generators [(2,3)]
4233
"""
4234
from sage.groups.perm_gps.permgroup import PermutationGroup
4235
4236
if self.base_ring() is ZZ or self.base_ring() is QQ:
4237
def rational_approximation(c):
4238
return c
4239
4240
elif self.base_ring() is RDF:
4241
c_list = []
4242
def rational_approximation(c):
4243
# Implementation detail: Return unique integer if two
4244
# c-values are the same up to machine precision. But
4245
# you can think of it as a uniquely-chosen rational
4246
# approximation.
4247
for i,x in enumerate(c_list):
4248
if self._is_zero(x-c):
4249
return i
4250
c_list.append(c)
4251
return len(c_list)-1
4252
4253
# The algorithm identifies the restricted automorphism group
4254
# with the automorphism group of a edge-colored graph. The
4255
# nodes of the graph are the V-representation objects. If all
4256
# V-representation objects are vertices, the edges are
4257
# labelled by numbers (to be computed below). Roughly
4258
# speaking, the edge label is the inner product of the
4259
# coordinate vectors with some orthogonalization thrown in
4260
# [BSS].
4261
def edge_label_compact(i,j,c_ij):
4262
return c_ij
4263
4264
# In the non-compact case we also label the edges by the type
4265
# of the V-representation object. This ensures that vertices,
4266
# rays, and lines are only permuted amongst themselves.
4267
def edge_label_noncompact(i,j,c_ij):
4268
return (self.Vrepresentation(i).type(), c_ij, self.Vrepresentation(j).type())
4269
4270
if self.is_compact():
4271
edge_label = edge_label_compact
4272
else:
4273
edge_label = edge_label_noncompact
4274
4275
# good coordinates for the V-representation objects
4276
v_list = []
4277
for v in self.Vrepresentation():
4278
v_coords = list(self._affine_coordinates(v))
4279
if v.is_vertex():
4280
v_coords = [1]+v_coords
4281
else:
4282
v_coords = [0]+v_coords
4283
v_list.append(vector(v_coords))
4284
4285
# Finally, construct the graph
4286
Qinv = sum( v.column() * v.row() for v in v_list ).inverse()
4287
G = Graph()
4288
for i in range(0,len(v_list)):
4289
for j in range(i+1,len(v_list)):
4290
v_i = v_list[i]
4291
v_j = v_list[j]
4292
c_ij = rational_approximation( v_i * Qinv * v_j )
4293
G.add_edge(i+1,j+1, edge_label(i,j,c_ij))
4294
4295
group = G.automorphism_group(edge_labels=True)
4296
self._restricted_automorphism_group = group
4297
return group
4298
4299
4300
4301
4302
4303