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