Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/polyhedron/library.py
4072 views
1
"""
2
Library of commonly used, famous, or interesting polytopes
3
"""
4
5
########################################################################
6
# Copyright (C) 2008 Marshall Hampton <[email protected]>
7
# Copyright (C) 2011 Volker Braun <[email protected]>
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# http://www.gnu.org/licenses/
12
########################################################################
13
14
15
from sage.rings.all import Integer, QQ, ZZ, RDF
16
from sage.matrix.constructor import matrix
17
from sage.modules.free_module_element import vector
18
from sage.combinat.combinat import permutations
19
from sage.groups.perm_gps.permgroup_named import AlternatingGroup
20
from sage.misc.functional import norm
21
from sage.functions.other import sqrt, floor, ceil
22
from sage.functions.trig import sin, cos
23
from sage.misc.decorators import rename_keyword
24
25
from constructor import Polyhedron
26
27
28
29
#########################################################################
30
class Polytopes():
31
"""
32
A class of constructors for commonly used, famous, or interesting
33
polytopes.
34
35
TESTS::
36
37
sage: TestSuite(polytopes).run(skip='_test_pickling')
38
"""
39
40
@staticmethod
41
def orthonormal_1(dim_n=5):
42
"""
43
A matrix of rational approximations to orthonormal vectors to
44
``(1,...,1)``.
45
46
INPUT:
47
48
- ``dim_n`` - the dimension of the vectors
49
50
OUTPUT:
51
52
A matrix over ``QQ`` whose rows are close to an orthonormal
53
basis to the subspace normal to ``(1,...,1)``.
54
55
EXAMPLES::
56
57
sage: from sage.geometry.polyhedron.library import Polytopes
58
sage: m = Polytopes.orthonormal_1(5)
59
sage: m
60
[ 70711/100000 -7071/10000 0 0 0]
61
[ 1633/4000 1633/4000 -81649/100000 0 0]
62
[ 7217/25000 7217/25000 7217/25000 -43301/50000 0]
63
[ 22361/100000 22361/100000 22361/100000 22361/100000 -44721/50000]
64
"""
65
pb = []
66
for i in range(0,dim_n-1):
67
pb.append([1.0/(i+1)]*(i+1) + [-1] + [0]*(dim_n-i-2))
68
m = matrix(RDF,pb)
69
new_m = []
70
for i in range(0,dim_n-1):
71
new_m.append([RDF(100000*q/norm(m[i])).ceil()/100000 for q in m[i]])
72
return matrix(QQ,new_m)
73
74
@staticmethod
75
def project_1(fpoint):
76
"""
77
Take a ndim-dimensional point and projects it onto the plane
78
perpendicular to (1,1,...,1).
79
80
INPUT:
81
82
- ``fpoint`` - a list of ndim numbers
83
84
EXAMPLES::
85
86
sage: from sage.geometry.polyhedron.library import Polytopes
87
sage: Polytopes.project_1([1,1,1,1,2])
88
[1/100000, 1/100000, 1/50000, -559/625]
89
"""
90
dim_n = len(fpoint)
91
p_basis = [list(q) for q in Polytopes.orthonormal_1(dim_n)]
92
out_v = []
93
for v in p_basis:
94
out_v.append(sum([fpoint[ind]*v[ind] for ind in range(dim_n)]))
95
return out_v
96
97
@staticmethod
98
def _pfunc(i,j,perm):
99
"""
100
An internal utility function for constructing the Birkhoff polytopes.
101
102
EXAMPLES::
103
104
sage: from sage.geometry.polyhedron.library import Polytopes
105
sage: Polytopes._pfunc(1,2,permutations(3)[0])
106
0
107
"""
108
if perm[i-1] == j:
109
return 1
110
else:
111
return 0
112
113
114
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
115
def regular_polygon(self, n, base_ring=QQ):
116
"""
117
Return a regular polygon with n vertices. Over the rational
118
field the vertices may not be exact.
119
120
INPUT:
121
122
- ``n`` -- a positive integer, the number of vertices.
123
124
- ``field`` -- either ``QQ`` or ``RDF``.
125
126
EXAMPLES::
127
128
sage: octagon = polytopes.regular_polygon(8)
129
sage: len(octagon.vertices())
130
8
131
"""
132
npi = 3.14159265359
133
verts = []
134
for i in range(n):
135
t = 2*npi*i/n
136
verts.append([sin(t),cos(t)])
137
verts = [[base_ring(RDF(x)) for x in y] for y in verts]
138
return Polyhedron(vertices=verts, base_ring=base_ring)
139
140
141
def Birkhoff_polytope(self, n):
142
"""
143
Return the Birkhoff polytope with n! vertices. Each vertex
144
is a (flattened) n by n permutation matrix.
145
146
INPUT:
147
148
- ``n`` -- a positive integer giving the size of the permutation matrices.
149
150
EXAMPLES::
151
152
sage: b3 = polytopes.Birkhoff_polytope(3)
153
sage: b3.n_vertices()
154
6
155
"""
156
perms = permutations(range(1,n+1))
157
verts = []
158
for p in perms:
159
verts += [ [Polytopes._pfunc(i,j,p) for j in range(1,n+1)
160
for i in range(1,n+1) ] ]
161
return Polyhedron(vertices=verts)
162
163
164
def n_simplex(self, dim_n=3, project = True):
165
"""
166
Return a rational approximation to a regular simplex in
167
dimension ``dim_n``.
168
169
INPUT:
170
171
- ``dim_n`` -- The dimension of the cross-polytope, a positive
172
integer.
173
174
- ``project`` -- Optional argument, whether to project
175
orthogonally. Default is True.
176
177
OUTPUT:
178
179
A Polyhedron object of the ``dim_n``-dimensional simplex.
180
181
EXAMPLES::
182
183
sage: s5 = polytopes.n_simplex(5)
184
sage: s5.dim()
185
5
186
"""
187
verts = permutations([0 for i in range(dim_n)] + [1])
188
if project: verts = [Polytopes.project_1(x) for x in verts]
189
return Polyhedron(vertices=verts)
190
191
192
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
193
def icosahedron(self, base_ring=QQ):
194
"""
195
Return an icosahedron with edge length 1.
196
197
INPUT:
198
199
- ``base_ring`` -- Either ``QQ`` or ``RDF``.
200
201
OUTPUT:
202
203
A Polyhedron object of a floating point or rational
204
approximation to the regular 3d icosahedron.
205
206
If ``base_ring=QQ``, a rational approximation is used and the
207
points are not exactly the vertices of the icosahedron. The
208
icosahedron's coordinates contain the golden ratio, so there
209
is no exact representation possible.
210
211
EXAMPLES::
212
213
sage: ico = polytopes.icosahedron()
214
sage: sum(sum( ico.vertex_adjacency_matrix() ))/2
215
30
216
"""
217
if base_ring == QQ:
218
g = QQ(1618033)/1000000 # Golden ratio approximation
219
r12 = QQ(1)/2
220
elif base_ring == RDF:
221
g = RDF( (1 + sqrt(5))/2 )
222
r12 = RDF( QQ(1)/2 )
223
else:
224
raise ValueError, "field must be QQ or RDF."
225
verts = [i([0,r12,g/2]) for i in AlternatingGroup(3)]
226
verts = verts + [i([0,r12,-g/2]) for i in AlternatingGroup(3)]
227
verts = verts + [i([0,-r12,g/2]) for i in AlternatingGroup(3)]
228
verts = verts + [i([0,-r12,-g/2]) for i in AlternatingGroup(3)]
229
return Polyhedron(vertices=verts, base_ring=base_ring)
230
231
232
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
233
def dodecahedron(self, base_ring=QQ):
234
"""
235
Return a dodecahedron.
236
237
INPUT:
238
239
- ``base_ring`` -- Either ``QQ`` (in which case a rational
240
approximation to the golden ratio is used) or ``RDF``.
241
242
EXAMPLES::
243
244
sage: d12 = polytopes.dodecahedron()
245
sage: d12.n_inequalities()
246
12
247
"""
248
return self.icosahedron(base_ring=base_ring).polar()
249
250
251
def small_rhombicuboctahedron(self):
252
"""
253
Return an Archimedean solid with 24 vertices and 26 faces.
254
255
EXAMPLES::
256
257
sage: sr = polytopes.small_rhombicuboctahedron()
258
sage: sr.n_vertices()
259
24
260
sage: sr.n_inequalities()
261
26
262
"""
263
verts = [ [-3/2, -1/2, -1/2], [-3/2, -1/2, 1/2], [-3/2, 1/2, -1/2],
264
[-3/2, 1/2, 1/2], [-1/2, -3/2, -1/2], [-1/2, -3/2, 1/2],
265
[-1/2, -1/2, -3/2], [-1/2,-1/2, 3/2], [-1/2, 1/2, -3/2],
266
[-1/2, 1/2, 3/2], [-1/2, 3/2, -1/2], [-1/2, 3/2, 1/2],
267
[1/2, -3/2, -1/2], [1/2, -3/2, 1/2], [1/2, -1/2,-3/2],
268
[1/2, -1/2, 3/2], [1/2, 1/2, -3/2], [1/2, 1/2, 3/2],
269
[1/2, 3/2,-1/2], [1/2, 3/2, 1/2], [3/2, -1/2, -1/2],
270
[3/2, -1/2, 1/2], [3/2, 1/2,-1/2], [3/2, 1/2, 1/2] ]
271
return Polyhedron(vertices=verts)
272
273
274
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
275
def great_rhombicuboctahedron(self, base_ring=QQ):
276
"""
277
Return an Archimedean solid with 48 vertices and 26 faces.
278
279
EXAMPLES::
280
281
sage: gr = polytopes.great_rhombicuboctahedron()
282
sage: gr.n_vertices()
283
48
284
sage: gr.n_inequalities()
285
26
286
"""
287
v1 = QQ(131739771357/54568400000)
288
v2 = QQ(104455571357/27284200000)
289
verts = [ [1, v1, v2],
290
[1, v2, v1],
291
[v1, 1, v2],
292
[v1, v2, 1],
293
[v2, 1, v1],
294
[v2, v1, 1] ]
295
verts = verts + [[x[0],x[1],-x[2]] for x in verts]
296
verts = verts + [[x[0],-x[1],x[2]] for x in verts]
297
verts = verts + [[-x[0],x[1],x[2]] for x in verts]
298
if base_ring!=QQ:
299
verts = [base_ring(v) for v in verts]
300
return Polyhedron(vertices=verts, base_ring=base_ring)
301
302
303
def rhombic_dodecahedron(self):
304
"""
305
This face-regular, vertex-uniform polytope is dual to the
306
cuboctahedron. It has 14 vertices and 12 faces.
307
308
EXAMPLES::
309
310
sage: rd = polytopes.rhombic_dodecahedron()
311
sage: rd.n_vertices()
312
14
313
sage: rd.n_inequalities()
314
12
315
"""
316
v = [ [1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1], [-1, 1, 1],
317
[-1, 1, -1], [-1, -1, 1], [-1, -1, -1], [0, 0, 2], [0, 2, 0],
318
[2, 0, 0], [0, 0, -2], [0, -2, 0], [-2, 0, 0] ]
319
return Polyhedron(vertices=v)
320
321
322
def cuboctahedron(self):
323
"""
324
An Archimedean solid with 12 vertices and 14 faces. Dual to
325
the rhombic dodecahedron.
326
327
EXAMPLES::
328
329
sage: co = polytopes.cuboctahedron()
330
sage: co.n_vertices()
331
12
332
sage: co.n_inequalities()
333
14
334
"""
335
one = Integer(1)
336
v = [ [0, -one/2, -one/2], [0, one/2, -one/2], [one/2, -one/2, 0],
337
[one/2, one/2, 0], [one/2, 0, one/2], [one/2, 0, -one/2],
338
[0, one/2, one/2], [0, -one/2, one/2], [-one/2, 0, one/2],
339
[-one/2, one/2, 0], [-one/2, 0, -one/2], [-one/2, -one/2, 0] ]
340
return Polyhedron(vertices=v)
341
342
343
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
344
def buckyball(self, base_ring=QQ):
345
"""
346
Also known as the truncated icosahedron, an Archimedean solid.
347
It has 32 faces and 60 vertices. Rational coordinates are not
348
exact.
349
350
EXAMPLES::
351
352
sage: bb = polytopes.buckyball()
353
sage: bb.n_vertices()
354
60
355
sage: bb.n_inequalities() # number of facets
356
32
357
sage: bb.base_ring()
358
Rational Field
359
"""
360
# Note: QQ would give some incorrecty subdivided facets
361
p = self.icosahedron(base_ring=RDF).edge_truncation()
362
if base_ring==RDF:
363
return p
364
# Converting with low precision to save time.
365
new_ieqs = [[int(1000*x)/QQ(1000) for x in y] for y in p.inequalities()]
366
return Polyhedron(ieqs=new_ieqs)
367
368
369
def pentakis_dodecahedron(self):
370
"""
371
This face-regular, vertex-uniform polytope is dual to the
372
truncated icosahedron. It has 60 faces and 32 vertices.
373
374
EXAMPLES::
375
376
sage: pd = polytopes.pentakis_dodecahedron()
377
sage: pd.n_vertices()
378
32
379
sage: pd.n_inequalities() # number of facets
380
60
381
"""
382
return self.buckyball().polar()
383
384
385
def twenty_four_cell(self):
386
"""
387
Return the standard 24-cell polytope.
388
389
OUTPUT:
390
391
A Polyhedron object of the 4-dimensional 24-cell, a regular
392
polytope. The coordinates of this polytope are exact.
393
394
EXAMPLES::
395
396
sage: p24 = polytopes.twenty_four_cell()
397
sage: v = p24.vertex_generator().next()
398
sage: for adj in v.neighbors(): print adj
399
A vertex at (-1/2, -1/2, -1/2, 1/2)
400
A vertex at (-1/2, -1/2, 1/2, -1/2)
401
A vertex at (-1, 0, 0, 0)
402
A vertex at (-1/2, 1/2, -1/2, -1/2)
403
A vertex at (0, -1, 0, 0)
404
A vertex at (0, 0, -1, 0)
405
A vertex at (0, 0, 0, -1)
406
A vertex at (1/2, -1/2, -1/2, -1/2)
407
"""
408
verts = []
409
q12 = QQ(1)/2
410
base = [q12,q12,q12,q12]
411
for i in range(2):
412
for j in range(2):
413
for k in range(2):
414
for l in range(2):
415
verts.append([x for x in base])
416
base[3] = base[3]*(-1)
417
base[2] = base[2]*(-1)
418
base[1] = base[1]*(-1)
419
base[0] = base[0]*(-1)
420
verts = verts + permutations([0,0,0,1])
421
verts = verts + permutations([0,0,0,-1])
422
return Polyhedron(vertices=verts)
423
424
425
def six_hundred_cell(self):
426
"""
427
Return the standard 600-cell polytope.
428
429
OUTPUT:
430
431
A Polyhedron object of the 4-dimensional 600-cell, a regular
432
polytope. In many ways this is an analogue of the
433
icosahedron. The coordinates of this polytope are rational
434
approximations of the true coordinates of the 600-cell, some
435
of which involve the (irrational) golden ratio.
436
437
EXAMPLES::
438
439
sage: p600 = polytopes.six_hundred_cell() # not tested - very long time
440
sage: len(list(p600.bounded_edges())) # not tested - very long time
441
120
442
"""
443
verts = []
444
q12 = QQ(1)/2
445
base = [q12,q12,q12,q12]
446
for i in range(2):
447
for j in range(2):
448
for k in range(2):
449
for l in range(2):
450
verts.append([x for x in base])
451
base[3] = base[3]*(-1)
452
base[2] = base[2]*(-1)
453
base[1] = base[1]*(-1)
454
base[0] = base[0]*(-1)
455
for x in permutations([0,0,0,1]):
456
verts.append(x)
457
for x in permutations([0,0,0,-1]):
458
verts.append(x)
459
g = QQ(1618033)/1000000 # Golden ratio approximation
460
verts = verts + [i([q12,g/2,1/(g*2),0]) for i in AlternatingGroup(4)]
461
verts = verts + [i([q12,g/2,-1/(g*2),0]) for i in AlternatingGroup(4)]
462
verts = verts + [i([q12,-g/2,1/(g*2),0]) for i in AlternatingGroup(4)]
463
verts = verts + [i([q12,-g/2,-1/(g*2),0]) for i in AlternatingGroup(4)]
464
verts = verts + [i([-q12,g/2,1/(g*2),0]) for i in AlternatingGroup(4)]
465
verts = verts + [i([-q12,g/2,-1/(g*2),0]) for i in AlternatingGroup(4)]
466
verts = verts + [i([-q12,-g/2,1/(g*2),0]) for i in AlternatingGroup(4)]
467
verts = verts + [i([-q12,-g/2,-1/(g*2),0]) for i in AlternatingGroup(4)]
468
return Polyhedron(vertices=verts)
469
470
471
@rename_keyword(deprecated='Sage version 4.7.2', field='base_ring')
472
def cyclic_polytope(self, dim_n, points_n, base_ring=QQ):
473
"""
474
Return a cyclic polytope.
475
476
INPUT:
477
478
- ``dim_n`` -- positive integer. the dimension of the polytope.
479
480
- ``points_n`` -- positive integer. the number of vertices.
481
482
- ``base_ring`` -- either ``QQ`` (default) or ``RDF``.
483
484
OUTPUT:
485
486
A cyclic polytope of dim_n with points_n vertices on the
487
moment curve ``(t,t^2,...,t^n)``, as Polyhedron object.
488
489
EXAMPLES::
490
491
sage: c = polytopes.cyclic_polytope(4,10)
492
sage: c.n_inequalities()
493
35
494
"""
495
verts = [[t**i for i in range(1,dim_n+1)] for t in range(points_n)]
496
return Polyhedron(vertices=verts, base_ring=base_ring)
497
498
499
def hypersimplex(self, dim_n, k, project = True):
500
"""
501
The hypersimplex in dimension dim_n with d choose k vertices,
502
projected into (dim_n - 1) dimensions.
503
504
INPUT:
505
506
- ``n`` -- the numbers ``(1,...,n)`` are permuted
507
508
- ``project`` -- If ``False``, the polyhedron is left in
509
dimension ``n``.
510
511
OUTPUT:
512
513
A Polyhedron object representing the hypersimplex.
514
515
EXAMPLES::
516
517
sage: h_4_2 = polytopes.hypersimplex(4,2) # combinatorially equivalent to octahedron
518
sage: h_4_2.n_vertices()
519
6
520
sage: h_4_2.n_inequalities()
521
8
522
"""
523
vert0 = [0]*(dim_n-k) + [1]*k
524
verts = permutations(vert0)
525
if project:
526
verts = [Polytopes.project_1(x) for x in verts]
527
return Polyhedron(vertices=verts)
528
529
530
def permutahedron(self, n, project = True):
531
"""
532
The standard permutahedron of (1,...,n) projected into n-1
533
dimensions.
534
535
INPUT:
536
537
- ``n`` -- the numbers ``(1,...,n)`` are permuted
538
539
- ``project`` -- If ``False`` the polyhedron is left in dimension ``n``.
540
541
OUTPUT:
542
543
A Polyhedron object representing the permutahedron.
544
545
EXAMPLES::
546
547
sage: perm4 = polytopes.permutahedron(4)
548
sage: perm4
549
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices
550
sage: polytopes.permutahedron(5).show() # long time
551
"""
552
verts = range(1,n+1)
553
verts = permutations(verts)
554
if project:
555
verts = [Polytopes.project_1(x) for x in verts]
556
p = Polyhedron(vertices=verts)
557
return p
558
559
560
def n_cube(self, dim_n):
561
"""
562
Return a cube in the given dimension
563
564
INPUT:
565
566
- ``dim_n`` -- integer. The dimension of the cube.
567
568
OUTPUT:
569
570
A Polyhedron object of the ``dim_n``-dimensional cube, with
571
exact coordinates.
572
573
EXAMPLES::
574
575
sage: four_cube = polytopes.n_cube(4)
576
sage: four_cube.is_simple()
577
True
578
"""
579
if dim_n == 1:
580
return Polyhedron(vertices = [[1],[-1]])
581
582
pre_cube = polytopes.n_cube(dim_n-1)
583
vertices = [];
584
for pre_v in pre_cube.vertex_generator():
585
vertices.append( [ 1] + [v for v in pre_v] );
586
vertices.append( [-1] + [v for v in pre_v] );
587
return Polyhedron(vertices = vertices)
588
589
590
def cross_polytope(self, dim_n):
591
"""
592
Return a cross-polytope in dimension ``dim_n``. These are
593
the generalization of the octahedron.
594
595
INPUT:
596
597
- ``dim_n`` -- integer. The dimension of the cross-polytope.
598
599
OUTPUT:
600
601
A Polyhedron object of the ``dim_n``-dimensional cross-polytope,
602
with exact coordinates.
603
604
EXAMPLES::
605
606
sage: four_cross = polytopes.cross_polytope(4)
607
sage: four_cross.is_simple()
608
False
609
sage: four_cross.n_vertices()
610
8
611
"""
612
verts = permutations([0 for i in range(dim_n-1)] + [1])
613
verts = verts + permutations([0 for i in range(dim_n-1)] + [-1])
614
return Polyhedron(vertices=verts)
615
616
617
def parallelotope(self, generators):
618
r"""
619
Return the parallelotope spanned by the generators.
620
621
INPUT:
622
623
- ``generators`` -- an iterable of anything convertible to vector
624
(for example, a list of vectors) such that the vectors all
625
have the same dimension.
626
627
OUTPUT:
628
629
The parallelotope. This is the multi-dimensional
630
generalization of a parallelogram (2 generators) and a
631
parallelepiped (3 generators).
632
633
EXAMPLES::
634
635
sage: polytopes.parallelotope([ (1,0), (0,1) ])
636
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
637
sage: polytopes.parallelotope([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]])
638
A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 16 vertices
639
"""
640
try:
641
generators = [ vector(QQ,v) for v in generators ]
642
base_ring = QQ
643
except TypeError:
644
generators = [ vector(RDF,v) for v in generators ]
645
base_ring = RDF
646
647
from sage.combinat.combination import Combinations
648
par = [ 0*generators[0] ]
649
par += [ sum(c) for c in Combinations(generators) if c!=[] ]
650
return Polyhedron(vertices=par, base_ring=base_ring)
651
652
653
654
polytopes = Polytopes()
655
656