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