Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/triangulation/element.py
4048 views
1
""""
2
A triangulation
3
4
In Sage, the
5
:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`
6
and :class:`Triangulation` satisfy a parent/element relationship. In
7
particular, each triangulation refers back to its point
8
configuration. If you want to triangulate a point configuration, you
9
should construct a point configuration first and then use one of its
10
methods to triangulate it according to your requirements. You should
11
never have to construct a :class:`Triangulation` object directly.
12
13
EXAMPLES:
14
15
First, we select the internal implementation for enumerating
16
triangulations::
17
18
sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
19
20
Here is a simple example of how to triangulate a point configuration::
21
22
sage: p = [[0,-1,-1],[0,0,1],[0,1,0], [1,-1,-1],[1,0,1],[1,1,0]]
23
sage: points = PointConfiguration(p)
24
sage: triang = points.triangulate(); triang
25
(<0,1,2,5>, <0,1,3,5>, <1,3,4,5>)
26
sage: triang.plot(axes=False)
27
28
See :mod:`sage.geometry.triangulation.point_configuration` for more details.
29
"""
30
31
32
########################################################################
33
# Copyright (C) 2010 Volker Braun <[email protected]>
34
#
35
# Distributed under the terms of the GNU General Public License (GPL)
36
#
37
# http://www.gnu.org/licenses/
38
########################################################################
39
40
from sage.structure.element import Element
41
from sage.rings.all import QQ, ZZ
42
from sage.modules.all import vector
43
from sage.misc.cachefunc import cached_method
44
45
46
########################################################################
47
def triangulation_render_2d(triangulation, **kwds):
48
r"""
49
Return a graphical representation of a 2-d triangulation.
50
51
INPUT:
52
53
- ``triangulation`` -- a :class:`Triangulation`.
54
55
- ``**kwds`` -- keywords that are passed on to the graphics primitives.
56
57
OUTPUT:
58
59
A 2-d graphics object.
60
61
EXAMPLES::
62
63
sage: points = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])
64
sage: triang = points.triangulate()
65
sage: triang.plot(axes=False, aspect_ratio=1) # indirect doctest
66
"""
67
from sage.plot.all import point2d, line2d, arrow, polygon2d
68
points = [ point.reduced_affine() for point in triangulation.point_configuration() ]
69
coord = [ [p[0], p[1]] for p in points ]
70
plot_points = sum([ point2d(p,
71
zorder=2, pointsize=10, **kwds)
72
for p in coord ])
73
74
tmp_lines = []
75
for t in triangulation:
76
if len(t)>=2:
77
tmp_lines.append([t[0], t[1]])
78
if len(t)>=3:
79
tmp_lines.append([t[0], t[2]])
80
tmp_lines.append([t[1], t[2]])
81
all_lines = []
82
interior_lines = []
83
for l in tmp_lines:
84
if l not in all_lines:
85
all_lines.append(l)
86
else:
87
interior_lines.append(l)
88
exterior_lines = [ l for l in all_lines if not l in interior_lines ]
89
90
plot_interior_lines = sum([ line2d([ coord[l[0]], coord[l[1]] ],
91
zorder=1, rgbcolor=(0,1,0), **kwds)
92
for l in interior_lines ])
93
plot_exterior_lines = sum([ line2d([ coord[l[0]], coord[l[1]] ],
94
zorder=1, rgbcolor=(0,0,1), **kwds)
95
for l in exterior_lines ])
96
97
plot_triangs = sum([ polygon2d([coord[t[0]], coord[t[1]], coord[t[2]]],
98
zorder=0, rgbcolor=(0.8, 1, 0.8), **kwds)
99
for t in triangulation if len(t)>=3 ])
100
101
return \
102
plot_points + \
103
plot_interior_lines + plot_exterior_lines + \
104
plot_triangs
105
106
107
108
109
def triangulation_render_3d(triangulation, **kwds):
110
r"""
111
Return a graphical representation of a 3-d triangulation.
112
113
INPUT:
114
115
- ``triangulation`` -- a :class:`Triangulation`.
116
117
- ``**kwds`` -- keywords that are passed on to the graphics primitives.
118
119
OUTPUT:
120
121
A 3-d graphics object.
122
123
EXAMPLES::
124
125
sage: p = [[0,-1,-1],[0,0,1],[0,1,0], [1,-1,-1],[1,0,1],[1,1,0]]
126
sage: points = PointConfiguration(p)
127
sage: triang = points.triangulate()
128
sage: triang.plot(axes=False) # indirect doctest
129
"""
130
from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d
131
points = [ point.reduced_affine() for point in triangulation.point_configuration() ]
132
coord = [ [p[0], p[1], p[2] ] for p in points ]
133
plot_points = sum([ point3d(p, size=15,
134
**kwds)
135
for p in coord ])
136
137
tmp_lines = []
138
for t in triangulation:
139
if len(t)>=2:
140
tmp_lines.append([t[0], t[1]])
141
if len(t)>=3:
142
tmp_lines.append([t[0], t[2]])
143
tmp_lines.append([t[1], t[2]])
144
if len(t)>=4:
145
tmp_lines.append([t[0], t[3]])
146
tmp_lines.append([t[1], t[3]])
147
tmp_lines.append([t[2], t[3]])
148
all_lines = []
149
interior_lines = []
150
for l in tmp_lines:
151
if l not in all_lines:
152
all_lines.append(l)
153
else:
154
interior_lines.append(l)
155
exterior_lines = [ l for l in all_lines if not l in interior_lines ]
156
157
from sage.plot.plot3d.texture import Texture
158
line_int = Texture(color='darkblue', ambient=1, diffuse=0)
159
line_ext = Texture(color='green', ambient=1, diffuse=0)
160
triang_int = Texture(opacity=0.3, specular=0, shininess=0, diffuse=0, ambient=1, color='yellow')
161
triang_ext = Texture(opacity=0.6, specular=0, shininess=0, diffuse=0, ambient=1, color='green')
162
163
plot_interior_lines = sum([ line3d([ coord[l[0]], coord[l[1]] ],
164
thickness=2, texture=line_int, **kwds)
165
for l in interior_lines ])
166
plot_exterior_lines = sum([ line3d([ coord[l[0]], coord[l[1]] ],
167
thickness=3, texture=line_ext, **kwds)
168
for l in exterior_lines ])
169
170
tmp_triangs = []
171
for t in triangulation:
172
if len(t)>=3:
173
tmp_triangs.append([t[0], t[1], t[2]])
174
if len(t)>=4:
175
tmp_triangs.append([t[0], t[1], t[3]])
176
tmp_triangs.append([t[0], t[2], t[3]])
177
tmp_triangs.append([t[1], t[2], t[3]])
178
all_triangs = []
179
interior_triangs = []
180
for l in tmp_triangs:
181
if l not in all_triangs:
182
all_triangs.append(l)
183
else:
184
interior_triangs.append(l)
185
exterior_triangs = [ l for l in all_triangs if not l in interior_triangs ]
186
187
plot_interior_triangs = \
188
sum([ polygon3d([coord[t[0]], coord[t[1]], coord[t[2]]],
189
texture = triang_int, **kwds)
190
for t in interior_triangs ])
191
plot_exterior_triangs = \
192
sum([ polygon3d([coord[t[0]], coord[t[1]], coord[t[2]]],
193
texture = triang_ext, **kwds)
194
for t in exterior_triangs ])
195
196
return \
197
plot_points + \
198
plot_interior_lines + plot_exterior_lines + \
199
plot_interior_triangs + plot_exterior_triangs
200
201
202
203
204
205
########################################################################
206
class Triangulation(Element):
207
"""
208
A triangulation of a
209
:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`.
210
211
.. WARNING::
212
213
You should never create :class:`Triangulation` objects
214
manually. See
215
:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.triangulate`
216
and
217
:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.triangulations`
218
to triangulate point configurations.
219
"""
220
221
def __init__(self, triangulation, parent, check=True):
222
"""
223
The constructor of a ``Triangulation`` object. Note that an
224
internal reference to the underlying ``PointConfiguration`` is
225
kept.
226
227
INPUT:
228
229
- ``parent`` -- a
230
:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`
231
232
- ``triangulation`` -- an iterable of integers or iterable of
233
iterables (e.g. a list of lists). In the first case, the
234
integers specify simplices via
235
:meth:`PointConfiguration.simplex_to_int`. In the second
236
case, the point indices of the maximal simplices of the
237
triangulation.
238
239
- ``check`` -- boolean. Whether to perform checks that the
240
triangulation is, indeed, a triangulation of the point
241
configuration.
242
243
NOTE:
244
245
Passing ``check=False`` allows you to create triangulations of
246
subsets of the points of the configuration, see
247
:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.bistellar_flips`.
248
249
EXAMPLES::
250
251
sage: p = [[0,1],[0,0],[1,0]]
252
sage: points = PointConfiguration(p)
253
sage: from sage.geometry.triangulation.point_configuration import Triangulation
254
sage: Triangulation([(0,1,2)], points)
255
(<0,1,2>)
256
sage: Triangulation([1], points)
257
(<0,1,2>)
258
"""
259
Element.__init__(self, parent=parent)
260
self._point_configuration = parent
261
262
try:
263
triangulation = tuple(sorted( tuple(sorted(t)) for t in triangulation))
264
except TypeError:
265
triangulation = tuple( self.point_configuration().int_to_simplex(i)
266
for i in triangulation )
267
assert not check or all( len(t)==self.point_configuration().dim()+1
268
for t in triangulation)
269
self._triangulation = triangulation
270
271
272
def point_configuration(self):
273
"""
274
Returns the point configuration underlying the triangulation.
275
276
EXAMPLES::
277
278
sage: pconfig = PointConfiguration([[0,0],[0,1],[1,0]])
279
sage: pconfig
280
A point configuration in QQ^2 consisting of 3 points. The
281
triangulations of this point configuration are assumed to
282
be connected, not necessarily fine, not necessarily regular.
283
sage: triangulation = pconfig.triangulate()
284
sage: triangulation
285
(<0,1,2>)
286
sage: triangulation.point_configuration()
287
A point configuration in QQ^2 consisting of 3 points. The
288
triangulations of this point configuration are assumed to
289
be connected, not necessarily fine, not necessarily regular.
290
sage: pconfig == triangulation.point_configuration()
291
True
292
"""
293
return self._point_configuration
294
295
296
def __cmp__(self, right):
297
r"""
298
Compare ``self`` and ``right``.
299
300
INPUT:
301
302
- ``right`` -- anything.
303
304
OUTPUT:
305
306
- 0 if ``right`` is the same triangulation as ``self``, 1 or
307
-1 otherwise.
308
309
TESTS::
310
311
sage: pc = PointConfiguration([[0,0],[0,1],[1,0]])
312
sage: t1 = pc.triangulate()
313
sage: from sage.geometry.triangulation.point_configuration import Triangulation
314
sage: t2 = Triangulation([[2,1,0]], pc)
315
sage: t1 is t2
316
False
317
sage: cmp(t1, t2)
318
0
319
sage: t1 == t2 # indirect doctest
320
True
321
sage: abs( cmp(t1, Triangulation(((0,1),(1,2)), pc, check=False) ))
322
1
323
sage: abs( cmp(t2, "not a triangulation") )
324
1
325
"""
326
left = self
327
c = cmp(isinstance(left,Triangulation), isinstance(right,Triangulation))
328
if c: return c
329
c = cmp(left.point_configuration(), right.point_configuration())
330
if c: return c
331
return cmp(left._triangulation, right._triangulation)
332
333
334
def __iter__(self):
335
"""
336
Iterate through the simplices of the triangulation.
337
338
EXAMPLES::
339
340
sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
341
sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])
342
sage: triangulation = pc.triangulate()
343
sage: iter = triangulation.__iter__()
344
sage: iter.next()
345
(1, 3, 4)
346
sage: iter.next()
347
(2, 3, 4)
348
sage: iter.next()
349
Traceback (most recent call last):
350
...
351
StopIteration
352
"""
353
for p in self._triangulation:
354
yield p
355
356
357
def __getitem__(self, i):
358
"""
359
Access the point indices of the i-th simplex of the triangulation.
360
361
INPUT:
362
363
- ``i`` -- integer. The index of a simplex.
364
365
OUTPUT:
366
367
A tuple of integers. The vertex indices of the i-th simplex.
368
369
EXAMPLES::
370
371
sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
372
sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])
373
sage: triangulation = pc.triangulate()
374
sage: triangulation[1]
375
(2, 3, 4)
376
"""
377
return self._triangulation[i]
378
379
380
def __len__(self):
381
"""
382
Returns the length of the triangulation.
383
384
TESTS::
385
386
sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
387
sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])
388
sage: triangulation = pc.triangulations().next()
389
sage: triangulation.__len__()
390
2
391
sage: len(triangulation) # equivalent
392
2
393
"""
394
return len(self._triangulation)
395
396
397
def _repr_(self):
398
r"""
399
Return a string representation.
400
401
TESTS::
402
403
sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
404
sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1],[2,2]])
405
sage: t = pc.triangulations()
406
sage: t.next()._repr_()
407
'(<1,4,5>, <2,4,5>)'
408
"""
409
#s = 'A triangulation'
410
#s += ' in QQ^'+str(self.point_configuration().ambient_dim())
411
#s += ' consisting of '+str(len(self))+' simplices.'
412
s = '('
413
s += ', '.join([ '<'+','.join(map(str,t))+'>' for t in self._triangulation])
414
s += ')'
415
return s
416
417
418
def plot(self, **kwds):
419
r"""
420
Produce a graphical representation of the triangulation.
421
422
EXAMPLES::
423
424
sage: p = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])
425
sage: triangulation = p.triangulate()
426
sage: triangulation
427
(<1,3,4>, <2,3,4>)
428
sage: triangulation.plot(axes=False)
429
"""
430
dim = self.point_configuration().dim()
431
432
if dim == 2:
433
return triangulation_render_2d(self, **kwds)
434
435
if dim == 3:
436
return triangulation_render_3d(self, **kwds)
437
438
raise NotImplementedError, \
439
'Plotting '+str(dim)+'-dimensional triangulations not implemented!'
440
441
442
def gkz_phi(self):
443
r"""
444
Calculate the GKZ phi vector of the triangulation.
445
446
The phi vector is a vector of length equals to the number of
447
points in the point configuration. For a fixed triangulation
448
`T`, the entry corresponding to the `i`-th point `p_i` is
449
450
.. math::
451
452
\phi_T(p_i) = \sum_{t\in T, t\owns p_i} Vol(t)
453
454
that is, the total volume of all simplices containing `p_i`.
455
See also [GKZ]_ page 220 equation 1.4.
456
457
OUTPUT:
458
459
The phi vector of self.
460
461
EXAMPLES::
462
463
sage: p = PointConfiguration([[0,0],[1,0],[2,1],[1,2],[0,1]])
464
sage: p.triangulate().gkz_phi()
465
(3, 1, 5, 2, 4)
466
sage: p.lexicographic_triangulation().gkz_phi()
467
(1, 3, 4, 2, 5)
468
"""
469
vec = vector(ZZ, self.point_configuration().n_points())
470
for simplex in self:
471
vol = self.point_configuration().volume(simplex)
472
for i in simplex:
473
vec[i] = vec[i] + vol
474
return vec
475
476
477
def enumerate_simplices(self):
478
r"""
479
Return the enumerated simplices.
480
481
OUTPUT:
482
483
A tuple of integers that uniquely specifies the triangulation.
484
485
EXAMPLES::
486
487
sage: pc = PointConfiguration(matrix([
488
... [ 0, 0, 0, 0, 0, 2, 4,-1, 1, 1, 0, 0, 1, 0],
489
... [ 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0],
490
... [ 0, 2, 0, 0, 0, 0,-1, 0, 1, 0, 1, 0, 0, 1],
491
... [ 0, 1, 1, 0, 0, 1, 0,-2, 1, 0, 0,-1, 1, 1],
492
... [ 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0]
493
... ]).columns())
494
sage: triangulation = pc.lexicographic_triangulation()
495
sage: triangulation.enumerate_simplices()
496
(1678, 1688, 1769, 1779, 1895, 1905, 2112, 2143, 2234, 2360, 2555, 2580,
497
2610, 2626, 2650, 2652, 2654, 2661, 2663, 2667, 2685, 2755, 2757, 2759,
498
2766, 2768, 2772, 2811, 2881, 2883, 2885, 2892, 2894, 2898)
499
500
You can recreate the triangulation from this list by passing
501
it to the constructor::
502
503
sage: from sage.geometry.triangulation.point_configuration import Triangulation
504
sage: Triangulation([1678, 1688, 1769, 1779, 1895, 1905, 2112, 2143,
505
... 2234, 2360, 2555, 2580, 2610, 2626, 2650, 2652, 2654, 2661, 2663,
506
... 2667, 2685, 2755, 2757, 2759, 2766, 2768, 2772, 2811, 2881, 2883,
507
... 2885, 2892, 2894, 2898], pc)
508
(<1,3,4,7,10,13>, <1,3,4,8,10,13>, <1,3,6,7,10,13>, <1,3,6,8,10,13>,
509
<1,4,6,7,10,13>, <1,4,6,8,10,13>, <2,3,4,6,7,12>, <2,3,4,7,12,13>,
510
<2,3,6,7,12,13>, <2,4,6,7,12,13>, <3,4,5,6,9,12>, <3,4,5,8,9,12>,
511
<3,4,6,7,11,12>, <3,4,6,9,11,12>, <3,4,7,10,11,13>, <3,4,7,11,12,13>,
512
<3,4,8,9,10,12>, <3,4,8,10,12,13>, <3,4,9,10,11,12>, <3,4,10,11,12,13>,
513
<3,5,6,8,9,12>, <3,6,7,10,11,13>, <3,6,7,11,12,13>, <3,6,8,9,10,12>,
514
<3,6,8,10,12,13>, <3,6,9,10,11,12>, <3,6,10,11,12,13>, <4,5,6,8,9,12>,
515
<4,6,7,10,11,13>, <4,6,7,11,12,13>, <4,6,8,9,10,12>, <4,6,8,10,12,13>,
516
<4,6,9,10,11,12>, <4,6,10,11,12,13>)
517
"""
518
pc = self._point_configuration
519
return tuple( pc.simplex_to_int(t) for t in self )
520
521
522
def fan(self, origin=None):
523
r"""
524
Construct the fan of cones over the simplices of the triangulation.
525
526
INPUT:
527
528
- ``origin`` -- ``None`` (default) or coordinates of a
529
point. The common apex of all cones of the fan. If ``None``,
530
the triangulation must be a star triangulation and the
531
distinguished central point is used as the origin.
532
533
OUTPUT:
534
535
A :class:`~sage.geometry.fan.RationalPolyhedralFan`. The
536
coordinates of the points are shifted so that the apex of the
537
fan is the origin of the coordinate system.
538
539
.. note:: If the set of cones over the simplices is not a fan, a
540
suitable exception is raised.
541
542
EXAMPLES::
543
544
sage: pc = PointConfiguration([(0,0), (1,0), (0,1), (-1,-1)], star=0, fine=True)
545
sage: triangulation = pc.triangulate()
546
sage: fan = triangulation.fan(); fan
547
Rational polyhedral fan in 2-d lattice N
548
sage: fan.is_equivalent( toric_varieties.P2().fan() )
549
True
550
551
Toric diagrams (the `\ZZ_5` hyperconifold)::
552
553
sage: vertices=[(0, 1, 0), (0, 3, 1), (0, 2, 3), (0, 0, 2)]
554
sage: interior=[(0, 1, 1), (0, 1, 2), (0, 2, 1), (0, 2, 2)]
555
sage: points = vertices+interior
556
sage: pc = PointConfiguration(points, fine=True)
557
sage: triangulation = pc.triangulate()
558
sage: fan = triangulation.fan( (-1,0,0) )
559
sage: fan
560
Rational polyhedral fan in 3-d lattice N
561
sage: fan.rays()
562
(N(1, 1, 0), N(1, 3, 1), N(1, 2, 3), N(1, 0, 2),
563
N(1, 1, 1), N(1, 1, 2), N(1, 2, 1), N(1, 2, 2))
564
"""
565
from sage.geometry.fan import Fan
566
if origin is None:
567
origin = self.point_configuration().star_center()
568
R = self.base_ring()
569
origin = vector(R, origin)
570
points = self.point_configuration().points()
571
return Fan(self, (vector(R, p) - origin for p in points))
572
573
574
@cached_method
575
def simplicial_complex(self):
576
r"""
577
Return a simplicial complex from a triangulation of the point
578
configuration.
579
580
OUTPUT:
581
582
A :class:`~sage.homology.simplicial_complex.SimplicialComplex`.
583
584
EXAMPLES::
585
586
sage: p = polytopes.cuboctahedron()
587
sage: sc = p.triangulate(engine='internal').simplicial_complex()
588
sage: sc
589
Simplicial complex with 12 vertices and 16 facets
590
591
Any convex set is contractable, so its reduced homology groups vanish::
592
593
sage: sc.homology()
594
{0: 0, 1: 0, 2: 0, 3: 0}
595
"""
596
from sage.homology.simplicial_complex import SimplicialComplex
597
from sage.misc.all import flatten
598
vertex_set = set(flatten(self))
599
return SimplicialComplex(vertex_set = vertex_set,
600
maximal_faces = self)
601
602
603
@cached_method
604
def _boundary_simplex_dictionary(self):
605
"""
606
Return facets and the simplices they bound
607
608
TESTS::
609
610
sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal')
611
sage: triangulation._boundary_simplex_dictionary()
612
{(0, 1): ((0, 1, 3),),
613
(0, 3): ((0, 1, 3), (0, 2, 3)),
614
(1, 3): ((0, 1, 3),),
615
(2, 3): ((0, 2, 3),),
616
(0, 2): ((0, 2, 3),)}
617
618
sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')
619
sage: triangulation._boundary_simplex_dictionary()
620
{(1, 4, 7): ((0, 1, 4, 7), (1, 4, 5, 7)),
621
(1, 3, 7): ((1, 2, 3, 7),),
622
(0, 1, 7): ((0, 1, 2, 7), (0, 1, 4, 7)),
623
(0, 2, 7): ((0, 1, 2, 7), (0, 2, 4, 7)),
624
(0, 1, 4): ((0, 1, 4, 7),),
625
(2, 4, 6): ((2, 4, 6, 7),),
626
(0, 1, 2): ((0, 1, 2, 7),),
627
(1, 2, 7): ((0, 1, 2, 7), (1, 2, 3, 7)),
628
(2, 6, 7): ((2, 4, 6, 7),),
629
(2, 3, 7): ((1, 2, 3, 7),),
630
(1, 4, 5): ((1, 4, 5, 7),),
631
(1, 5, 7): ((1, 4, 5, 7),),
632
(4, 5, 7): ((1, 4, 5, 7),),
633
(0, 4, 7): ((0, 1, 4, 7), (0, 2, 4, 7)),
634
(2, 4, 7): ((0, 2, 4, 7), (2, 4, 6, 7)),
635
(1, 2, 3): ((1, 2, 3, 7),),
636
(4, 6, 7): ((2, 4, 6, 7),),
637
(0, 2, 4): ((0, 2, 4, 7),)}
638
"""
639
result = dict()
640
for simplex in self:
641
for i in range(len(simplex)):
642
facet = simplex[:i] + simplex[i+1:]
643
result[facet] = result.get(facet, tuple()) + (simplex,)
644
return result
645
646
647
@cached_method
648
def boundary(self):
649
"""
650
Return the boundary of the triangulation.
651
652
OUTPUT:
653
654
The outward-facing boundary simplices (of dimension `d-1`) of
655
the `d`-dimensional triangulation as a set. Each boundary is
656
returned by a tuple of point indices.
657
658
EXAMPLES::
659
660
sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')
661
sage: triangulation
662
(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)
663
sage: triangulation.boundary()
664
frozenset([(1, 3, 7), (4, 5, 7), (1, 2, 3), (0, 1, 2), (2, 4, 6), (2, 6, 7),
665
(2, 3, 7), (1, 5, 7), (0, 1, 4), (1, 4, 5), (4, 6, 7), (0, 2, 4)])
666
sage: triangulation.interior_facets()
667
frozenset([(1, 4, 7), (1, 2, 7), (2, 4, 7), (0, 1, 7), (0, 4, 7), (0, 2, 7)])
668
"""
669
return frozenset(facet for facet, bounded_simplices
670
in self._boundary_simplex_dictionary().iteritems()
671
if len(bounded_simplices)==1)
672
673
@cached_method
674
def interior_facets(self):
675
"""
676
Return the interior facets of the triangulation.
677
678
OUTPUT:
679
680
The inward-facing boundary simplices (of dimension `d-1`) of
681
the `d`-dimensional triangulation as a set. Each boundary is
682
returned by a tuple of point indices.
683
684
EXAMPLES::
685
686
sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')
687
sage: triangulation
688
(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)
689
sage: triangulation.boundary()
690
frozenset([(1, 3, 7), (4, 5, 7), (1, 2, 3), (0, 1, 2), (2, 4, 6), (2, 6, 7),
691
(2, 3, 7), (1, 5, 7), (0, 1, 4), (1, 4, 5), (4, 6, 7), (0, 2, 4)])
692
sage: triangulation.interior_facets()
693
frozenset([(1, 4, 7), (1, 2, 7), (2, 4, 7), (0, 1, 7), (0, 4, 7), (0, 2, 7)])
694
"""
695
return frozenset(facet for facet, bounded_simplices
696
in self._boundary_simplex_dictionary().iteritems()
697
if len(bounded_simplices)==2)
698
699
@cached_method
700
def normal_cone(self):
701
r"""
702
Return the (closure of the) normal cone of the triangulation.
703
704
Recall that a regular triangulation is one that equals the
705
"crease lines" of a convex piecewise-linear function. This
706
support function is not unique, for example, you can scale it
707
by a positive constant. The set of all piecewise-linear
708
functions with fixed creases forms an open cone. This cone can
709
be interpreted as the cone of normal vectors at a point of the
710
secondary polytope, which is why we call it normal cone. See
711
[GKZ]_ Section 7.1 for details.
712
713
OUTPUT:
714
715
The closure of the normal cone. The `i`-th entry equals the
716
value of the piecewise-linear function at the `i`-th point of
717
the configuration.
718
719
For an irregular triangulation, the normal cone is empty. In
720
this case, a single point (the origin) is returned.
721
722
EXAMPLES::
723
724
sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal')
725
sage: triangulation
726
(<0,1,3>, <0,2,3>)
727
sage: N = triangulation.normal_cone(); N
728
4-d cone in 4-d lattice
729
sage: N.rays()
730
((-1, 0, 0, 0), (1, 0, 1, 0), (-1, 0, -1, 0), (1, 0, 0, -1),
731
(-1, 0, 0, 1), (1, 1, 0, 0), (-1, -1, 0, 0))
732
sage: N.dual().rays()
733
((-1, 1, 1, -1),)
734
735
TESTS::
736
737
sage: polytopes.n_simplex(2).triangulate().normal_cone()
738
3-d cone in 3-d lattice
739
sage: _.dual().is_trivial()
740
True
741
"""
742
if not self.point_configuration().base_ring().is_subring(QQ):
743
raise NotImplementedError('Only base rings ZZ and QQ are supported')
744
from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron
745
from sage.matrix.constructor import matrix
746
from sage.misc.misc import uniq
747
from sage.rings.arith import lcm
748
pc = self.point_configuration()
749
cs = Constraint_System()
750
for facet in self.interior_facets():
751
s0, s1 = self._boundary_simplex_dictionary()[facet]
752
p = set(s0).difference(facet).pop()
753
q = set(s1).difference(facet).pop()
754
origin = pc.point(p).reduced_affine_vector()
755
base_indices = [ i for i in s0 if i!=p ]
756
base = matrix([ pc.point(i).reduced_affine_vector()-origin for i in base_indices ])
757
sol = base.solve_left( pc.point(q).reduced_affine_vector()-origin )
758
relation = [0]*pc.n_points()
759
relation[p] = sum(sol)-1
760
relation[q] = 1
761
for i, base_i in enumerate(base_indices):
762
relation[base_i] = -sol[i]
763
rel_denom = lcm([QQ(r).denominator() for r in relation])
764
relation = [ ZZ(r*rel_denom) for r in relation ]
765
ex = Linear_Expression(relation,0)
766
cs.insert(ex >= 0)
767
from sage.modules.free_module import FreeModule
768
ambient = FreeModule(ZZ, self.point_configuration().n_points())
769
if cs.empty():
770
cone = C_Polyhedron(ambient.dimension(), 'universe')
771
else:
772
cone = C_Polyhedron(cs)
773
from sage.geometry.cone import _Cone_from_PPL
774
return _Cone_from_PPL(cone, lattice=ambient)
775
776
777
778
779