Path: blob/master/src/sage/geometry/triangulation/element.py
8817 views
""""1A triangulation23In Sage, the4:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`5and :class:`Triangulation` satisfy a parent/element relationship. In6particular, each triangulation refers back to its point7configuration. If you want to triangulate a point configuration, you8should construct a point configuration first and then use one of its9methods to triangulate it according to your requirements. You should10never have to construct a :class:`Triangulation` object directly.1112EXAMPLES:1314First, we select the internal implementation for enumerating15triangulations::1617sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM1819Here is a simple example of how to triangulate a point configuration::2021sage: p = [[0,-1,-1],[0,0,1],[0,1,0], [1,-1,-1],[1,0,1],[1,1,0]]22sage: points = PointConfiguration(p)23sage: triang = points.triangulate(); triang24(<0,1,2,5>, <0,1,3,5>, <1,3,4,5>)25sage: triang.plot(axes=False)2627See :mod:`sage.geometry.triangulation.point_configuration` for more details.28"""293031########################################################################32# Copyright (C) 2010 Volker Braun <[email protected]>33#34# Distributed under the terms of the GNU General Public License (GPL)35#36# http://www.gnu.org/licenses/37########################################################################3839from sage.structure.element import Element40from sage.rings.all import QQ, ZZ41from sage.modules.all import vector42from sage.misc.cachefunc import cached_method43from sage.sets.set import Set44from sage.graphs.graph import Graph454647########################################################################48def triangulation_render_2d(triangulation, **kwds):49r"""50Return a graphical representation of a 2-d triangulation.5152INPUT:5354- ``triangulation`` -- a :class:`Triangulation`.5556- ``**kwds`` -- keywords that are passed on to the graphics primitives.5758OUTPUT:5960A 2-d graphics object.6162EXAMPLES::6364sage: points = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])65sage: triang = points.triangulate()66sage: triang.plot(axes=False, aspect_ratio=1) # indirect doctest67"""68from sage.plot.all import point2d, line2d, arrow, polygon2d69points = [ point.reduced_affine() for point in triangulation.point_configuration() ]70coord = [ [p[0], p[1]] for p in points ]71plot_points = sum([ point2d(p,72zorder=2, pointsize=10, **kwds)73for p in coord ])7475tmp_lines = []76for t in triangulation:77if len(t)>=2:78tmp_lines.append([t[0], t[1]])79if len(t)>=3:80tmp_lines.append([t[0], t[2]])81tmp_lines.append([t[1], t[2]])82all_lines = []83interior_lines = []84for l in tmp_lines:85if l not in all_lines:86all_lines.append(l)87else:88interior_lines.append(l)89exterior_lines = [ l for l in all_lines if not l in interior_lines ]9091plot_interior_lines = sum([ line2d([ coord[l[0]], coord[l[1]] ],92zorder=1, rgbcolor=(0,1,0), **kwds)93for l in interior_lines ])94plot_exterior_lines = sum([ line2d([ coord[l[0]], coord[l[1]] ],95zorder=1, rgbcolor=(0,0,1), **kwds)96for l in exterior_lines ])9798plot_triangs = sum([ polygon2d([coord[t[0]], coord[t[1]], coord[t[2]]],99zorder=0, rgbcolor=(0.8, 1, 0.8), **kwds)100for t in triangulation if len(t)>=3 ])101102return \103plot_points + \104plot_interior_lines + plot_exterior_lines + \105plot_triangs106107108109110def triangulation_render_3d(triangulation, **kwds):111r"""112Return a graphical representation of a 3-d triangulation.113114INPUT:115116- ``triangulation`` -- a :class:`Triangulation`.117118- ``**kwds`` -- keywords that are passed on to the graphics primitives.119120OUTPUT:121122A 3-d graphics object.123124EXAMPLES::125126sage: p = [[0,-1,-1],[0,0,1],[0,1,0], [1,-1,-1],[1,0,1],[1,1,0]]127sage: points = PointConfiguration(p)128sage: triang = points.triangulate()129sage: triang.plot(axes=False) # indirect doctest130"""131from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d132points = [ point.reduced_affine() for point in triangulation.point_configuration() ]133coord = [ [p[0], p[1], p[2] ] for p in points ]134plot_points = sum([ point3d(p, size=15,135**kwds)136for p in coord ])137138tmp_lines = []139for t in triangulation:140if len(t)>=2:141tmp_lines.append([t[0], t[1]])142if len(t)>=3:143tmp_lines.append([t[0], t[2]])144tmp_lines.append([t[1], t[2]])145if len(t)>=4:146tmp_lines.append([t[0], t[3]])147tmp_lines.append([t[1], t[3]])148tmp_lines.append([t[2], t[3]])149all_lines = []150interior_lines = []151for l in tmp_lines:152if l not in all_lines:153all_lines.append(l)154else:155interior_lines.append(l)156exterior_lines = [ l for l in all_lines if not l in interior_lines ]157158from sage.plot.plot3d.texture import Texture159line_int = Texture(color='darkblue', ambient=1, diffuse=0)160line_ext = Texture(color='green', ambient=1, diffuse=0)161triang_int = Texture(opacity=0.3, specular=0, shininess=0, diffuse=0, ambient=1, color='yellow')162triang_ext = Texture(opacity=0.6, specular=0, shininess=0, diffuse=0, ambient=1, color='green')163164plot_interior_lines = sum([ line3d([ coord[l[0]], coord[l[1]] ],165thickness=2, texture=line_int, **kwds)166for l in interior_lines ])167plot_exterior_lines = sum([ line3d([ coord[l[0]], coord[l[1]] ],168thickness=3, texture=line_ext, **kwds)169for l in exterior_lines ])170171tmp_triangs = []172for t in triangulation:173if len(t)>=3:174tmp_triangs.append([t[0], t[1], t[2]])175if len(t)>=4:176tmp_triangs.append([t[0], t[1], t[3]])177tmp_triangs.append([t[0], t[2], t[3]])178tmp_triangs.append([t[1], t[2], t[3]])179all_triangs = []180interior_triangs = []181for l in tmp_triangs:182if l not in all_triangs:183all_triangs.append(l)184else:185interior_triangs.append(l)186exterior_triangs = [ l for l in all_triangs if not l in interior_triangs ]187188plot_interior_triangs = \189sum([ polygon3d([coord[t[0]], coord[t[1]], coord[t[2]]],190texture = triang_int, **kwds)191for t in interior_triangs ])192plot_exterior_triangs = \193sum([ polygon3d([coord[t[0]], coord[t[1]], coord[t[2]]],194texture = triang_ext, **kwds)195for t in exterior_triangs ])196197return \198plot_points + \199plot_interior_lines + plot_exterior_lines + \200plot_interior_triangs + plot_exterior_triangs201202203204205206########################################################################207class Triangulation(Element):208"""209A triangulation of a210:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`.211212.. WARNING::213214You should never create :class:`Triangulation` objects215manually. See216:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.triangulate`217and218:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.triangulations`219to triangulate point configurations.220"""221222def __init__(self, triangulation, parent, check=True):223"""224The constructor of a ``Triangulation`` object. Note that an225internal reference to the underlying ``PointConfiguration`` is226kept.227228INPUT:229230- ``parent`` -- a231:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`232233- ``triangulation`` -- an iterable of integers or iterable of234iterables (e.g. a list of lists). In the first case, the235integers specify simplices via236:meth:`PointConfiguration.simplex_to_int`. In the second237case, the point indices of the maximal simplices of the238triangulation.239240- ``check`` -- boolean. Whether to perform checks that the241triangulation is, indeed, a triangulation of the point242configuration.243244NOTE:245246Passing ``check=False`` allows you to create triangulations of247subsets of the points of the configuration, see248:meth:`~sage.geometry.triangulation.point_configuration.PointConfiguration.bistellar_flips`.249250EXAMPLES::251252sage: p = [[0,1],[0,0],[1,0]]253sage: points = PointConfiguration(p)254sage: from sage.geometry.triangulation.point_configuration import Triangulation255sage: Triangulation([(0,1,2)], points)256(<0,1,2>)257sage: Triangulation([1], points)258(<0,1,2>)259"""260Element.__init__(self, parent=parent)261self._point_configuration = parent262263try:264triangulation = tuple(sorted( tuple(sorted(t)) for t in triangulation))265except TypeError:266triangulation = tuple( self.point_configuration().int_to_simplex(i)267for i in triangulation )268assert not check or all( len(t)==self.point_configuration().dim()+1269for t in triangulation)270self._triangulation = triangulation271272273def point_configuration(self):274"""275Returns the point configuration underlying the triangulation.276277EXAMPLES::278279sage: pconfig = PointConfiguration([[0,0],[0,1],[1,0]])280sage: pconfig281A point configuration in QQ^2 consisting of 3 points. The282triangulations of this point configuration are assumed to283be connected, not necessarily fine, not necessarily regular.284sage: triangulation = pconfig.triangulate()285sage: triangulation286(<0,1,2>)287sage: triangulation.point_configuration()288A point configuration in QQ^2 consisting of 3 points. The289triangulations of this point configuration are assumed to290be connected, not necessarily fine, not necessarily regular.291sage: pconfig == triangulation.point_configuration()292True293"""294return self._point_configuration295296297def __cmp__(self, right):298r"""299Compare ``self`` and ``right``.300301INPUT:302303- ``right`` -- anything.304305OUTPUT:306307- 0 if ``right`` is the same triangulation as ``self``, 1 or308-1 otherwise.309310TESTS::311312sage: pc = PointConfiguration([[0,0],[0,1],[1,0]])313sage: t1 = pc.triangulate()314sage: from sage.geometry.triangulation.point_configuration import Triangulation315sage: t2 = Triangulation([[2,1,0]], pc)316sage: t1 is t2317False318sage: cmp(t1, t2)3190320sage: t1 == t2 # indirect doctest321True322sage: abs( cmp(t1, Triangulation(((0,1),(1,2)), pc, check=False) ))3231324sage: abs( cmp(t2, "not a triangulation") )3251326"""327left = self328c = cmp(isinstance(left,Triangulation), isinstance(right,Triangulation))329if c: return c330c = cmp(left.point_configuration(), right.point_configuration())331if c: return c332return cmp(left._triangulation, right._triangulation)333334335def __iter__(self):336"""337Iterate through the simplices of the triangulation.338339EXAMPLES::340341sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM342sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])343sage: triangulation = pc.triangulate()344sage: iter = triangulation.__iter__()345sage: iter.next()346(1, 3, 4)347sage: iter.next()348(2, 3, 4)349sage: iter.next()350Traceback (most recent call last):351...352StopIteration353"""354for p in self._triangulation:355yield p356357358def __getitem__(self, i):359"""360Access the point indices of the i-th simplex of the triangulation.361362INPUT:363364- ``i`` -- integer. The index of a simplex.365366OUTPUT:367368A tuple of integers. The vertex indices of the i-th simplex.369370EXAMPLES::371372sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM373sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])374sage: triangulation = pc.triangulate()375sage: triangulation[1]376(2, 3, 4)377"""378return self._triangulation[i]379380381def __len__(self):382"""383Returns the length of the triangulation.384385TESTS::386387sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM388sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])389sage: triangulation = pc.triangulations().next()390sage: triangulation.__len__()3912392sage: len(triangulation) # equivalent3932394"""395return len(self._triangulation)396397398def _repr_(self):399r"""400Return a string representation.401402TESTS::403404sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM405sage: pc = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1],[2,2]])406sage: t = pc.triangulations()407sage: t.next()._repr_()408'(<1,4,5>, <2,4,5>)'409"""410#s = 'A triangulation'411#s += ' in QQ^'+str(self.point_configuration().ambient_dim())412#s += ' consisting of '+str(len(self))+' simplices.'413s = '('414s += ', '.join([ '<'+','.join(map(str,t))+'>' for t in self._triangulation])415s += ')'416return s417418419def plot(self, **kwds):420r"""421Produce a graphical representation of the triangulation.422423EXAMPLES::424425sage: p = PointConfiguration([[0,0],[0,1],[1,0],[1,1],[-1,-1]])426sage: triangulation = p.triangulate()427sage: triangulation428(<1,3,4>, <2,3,4>)429sage: triangulation.plot(axes=False)430"""431dim = self.point_configuration().dim()432433if dim == 2:434return triangulation_render_2d(self, **kwds)435436if dim == 3:437return triangulation_render_3d(self, **kwds)438439raise NotImplementedError, \440'Plotting '+str(dim)+'-dimensional triangulations not implemented!'441442443def gkz_phi(self):444r"""445Calculate the GKZ phi vector of the triangulation.446447The phi vector is a vector of length equals to the number of448points in the point configuration. For a fixed triangulation449`T`, the entry corresponding to the `i`-th point `p_i` is450451.. math::452453\phi_T(p_i) = \sum_{t\in T, t\owns p_i} Vol(t)454455that is, the total volume of all simplices containing `p_i`.456See also [GKZ]_ page 220 equation 1.4.457458OUTPUT:459460The phi vector of self.461462EXAMPLES::463464sage: p = PointConfiguration([[0,0],[1,0],[2,1],[1,2],[0,1]])465sage: p.triangulate().gkz_phi()466(3, 1, 5, 2, 4)467sage: p.lexicographic_triangulation().gkz_phi()468(1, 3, 4, 2, 5)469"""470vec = vector(ZZ, self.point_configuration().n_points())471for simplex in self:472vol = self.point_configuration().volume(simplex)473for i in simplex:474vec[i] = vec[i] + vol475return vec476477478def enumerate_simplices(self):479r"""480Return the enumerated simplices.481482OUTPUT:483484A tuple of integers that uniquely specifies the triangulation.485486EXAMPLES::487488sage: pc = PointConfiguration(matrix([489... [ 0, 0, 0, 0, 0, 2, 4,-1, 1, 1, 0, 0, 1, 0],490... [ 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0],491... [ 0, 2, 0, 0, 0, 0,-1, 0, 1, 0, 1, 0, 0, 1],492... [ 0, 1, 1, 0, 0, 1, 0,-2, 1, 0, 0,-1, 1, 1],493... [ 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0]494... ]).columns())495sage: triangulation = pc.lexicographic_triangulation()496sage: triangulation.enumerate_simplices()497(1678, 1688, 1769, 1779, 1895, 1905, 2112, 2143, 2234, 2360, 2555, 2580,4982610, 2626, 2650, 2652, 2654, 2661, 2663, 2667, 2685, 2755, 2757, 2759,4992766, 2768, 2772, 2811, 2881, 2883, 2885, 2892, 2894, 2898)500501You can recreate the triangulation from this list by passing502it to the constructor::503504sage: from sage.geometry.triangulation.point_configuration import Triangulation505sage: Triangulation([1678, 1688, 1769, 1779, 1895, 1905, 2112, 2143,506... 2234, 2360, 2555, 2580, 2610, 2626, 2650, 2652, 2654, 2661, 2663,507... 2667, 2685, 2755, 2757, 2759, 2766, 2768, 2772, 2811, 2881, 2883,508... 2885, 2892, 2894, 2898], pc)509(<1,3,4,7,10,13>, <1,3,4,8,10,13>, <1,3,6,7,10,13>, <1,3,6,8,10,13>,510<1,4,6,7,10,13>, <1,4,6,8,10,13>, <2,3,4,6,7,12>, <2,3,4,7,12,13>,511<2,3,6,7,12,13>, <2,4,6,7,12,13>, <3,4,5,6,9,12>, <3,4,5,8,9,12>,512<3,4,6,7,11,12>, <3,4,6,9,11,12>, <3,4,7,10,11,13>, <3,4,7,11,12,13>,513<3,4,8,9,10,12>, <3,4,8,10,12,13>, <3,4,9,10,11,12>, <3,4,10,11,12,13>,514<3,5,6,8,9,12>, <3,6,7,10,11,13>, <3,6,7,11,12,13>, <3,6,8,9,10,12>,515<3,6,8,10,12,13>, <3,6,9,10,11,12>, <3,6,10,11,12,13>, <4,5,6,8,9,12>,516<4,6,7,10,11,13>, <4,6,7,11,12,13>, <4,6,8,9,10,12>, <4,6,8,10,12,13>,517<4,6,9,10,11,12>, <4,6,10,11,12,13>)518"""519pc = self._point_configuration520return tuple( pc.simplex_to_int(t) for t in self )521522523def fan(self, origin=None):524r"""525Construct the fan of cones over the simplices of the triangulation.526527INPUT:528529- ``origin`` -- ``None`` (default) or coordinates of a530point. The common apex of all cones of the fan. If ``None``,531the triangulation must be a star triangulation and the532distinguished central point is used as the origin.533534OUTPUT:535536A :class:`~sage.geometry.fan.RationalPolyhedralFan`. The537coordinates of the points are shifted so that the apex of the538fan is the origin of the coordinate system.539540.. note:: If the set of cones over the simplices is not a fan, a541suitable exception is raised.542543EXAMPLES::544545sage: pc = PointConfiguration([(0,0), (1,0), (0,1), (-1,-1)], star=0, fine=True)546sage: triangulation = pc.triangulate()547sage: fan = triangulation.fan(); fan548Rational polyhedral fan in 2-d lattice N549sage: fan.is_equivalent( toric_varieties.P2().fan() )550True551552Toric diagrams (the `\ZZ_5` hyperconifold)::553554sage: vertices=[(0, 1, 0), (0, 3, 1), (0, 2, 3), (0, 0, 2)]555sage: interior=[(0, 1, 1), (0, 1, 2), (0, 2, 1), (0, 2, 2)]556sage: points = vertices+interior557sage: pc = PointConfiguration(points, fine=True)558sage: triangulation = pc.triangulate()559sage: fan = triangulation.fan( (-1,0,0) )560sage: fan561Rational polyhedral fan in 3-d lattice N562sage: fan.rays()563N(1, 1, 0),564N(1, 3, 1),565N(1, 2, 3),566N(1, 0, 2),567N(1, 1, 1),568N(1, 1, 2),569N(1, 2, 1),570N(1, 2, 2)571in 3-d lattice N572"""573from sage.geometry.fan import Fan574if origin is None:575origin = self.point_configuration().star_center()576R = self.base_ring()577origin = vector(R, origin)578points = self.point_configuration().points()579return Fan(self, (vector(R, p) - origin for p in points))580581582@cached_method583def simplicial_complex(self):584r"""585Return a simplicial complex from a triangulation of the point586configuration.587588OUTPUT:589590A :class:`~sage.homology.simplicial_complex.SimplicialComplex`.591592EXAMPLES::593594sage: p = polytopes.cuboctahedron()595sage: sc = p.triangulate(engine='internal').simplicial_complex()596sage: sc597Simplicial complex with 12 vertices and 16 facets598599Any convex set is contractable, so its reduced homology groups vanish::600601sage: sc.homology()602{0: 0, 1: 0, 2: 0, 3: 0}603"""604from sage.homology.simplicial_complex import SimplicialComplex605return SimplicialComplex(self)606607608@cached_method609def _boundary_simplex_dictionary(self):610"""611Return facets and the simplices they bound612613TESTS::614615sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal')616sage: triangulation._boundary_simplex_dictionary()617{(0, 1): ((0, 1, 3),),618(0, 3): ((0, 1, 3), (0, 2, 3)),619(1, 3): ((0, 1, 3),),620(2, 3): ((0, 2, 3),),621(0, 2): ((0, 2, 3),)}622623sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')624sage: triangulation._boundary_simplex_dictionary()625{(1, 4, 7): ((0, 1, 4, 7), (1, 4, 5, 7)),626(1, 3, 7): ((1, 2, 3, 7),),627(0, 1, 7): ((0, 1, 2, 7), (0, 1, 4, 7)),628(0, 2, 7): ((0, 1, 2, 7), (0, 2, 4, 7)),629(0, 1, 4): ((0, 1, 4, 7),),630(2, 4, 6): ((2, 4, 6, 7),),631(0, 1, 2): ((0, 1, 2, 7),),632(1, 2, 7): ((0, 1, 2, 7), (1, 2, 3, 7)),633(2, 6, 7): ((2, 4, 6, 7),),634(2, 3, 7): ((1, 2, 3, 7),),635(1, 4, 5): ((1, 4, 5, 7),),636(1, 5, 7): ((1, 4, 5, 7),),637(4, 5, 7): ((1, 4, 5, 7),),638(0, 4, 7): ((0, 1, 4, 7), (0, 2, 4, 7)),639(2, 4, 7): ((0, 2, 4, 7), (2, 4, 6, 7)),640(1, 2, 3): ((1, 2, 3, 7),),641(4, 6, 7): ((2, 4, 6, 7),),642(0, 2, 4): ((0, 2, 4, 7),)}643"""644result = dict()645for simplex in self:646for i in range(len(simplex)):647facet = simplex[:i] + simplex[i+1:]648result[facet] = result.get(facet, tuple()) + (simplex,)649return result650651652@cached_method653def boundary(self):654"""655Return the boundary of the triangulation.656657OUTPUT:658659The outward-facing boundary simplices (of dimension `d-1`) of660the `d`-dimensional triangulation as a set. Each boundary is661returned by a tuple of point indices.662663EXAMPLES::664665sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')666sage: triangulation667(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)668sage: triangulation.boundary()669frozenset([(1, 3, 7), (4, 5, 7), (1, 2, 3), (0, 1, 2), (2, 4, 6), (2, 6, 7),670(2, 3, 7), (1, 5, 7), (0, 1, 4), (1, 4, 5), (4, 6, 7), (0, 2, 4)])671sage: triangulation.interior_facets()672frozenset([(1, 4, 7), (1, 2, 7), (2, 4, 7), (0, 1, 7), (0, 4, 7), (0, 2, 7)])673"""674return frozenset(facet for facet, bounded_simplices675in self._boundary_simplex_dictionary().iteritems()676if len(bounded_simplices)==1)677678@cached_method679def interior_facets(self):680"""681Return the interior facets of the triangulation.682683OUTPUT:684685The inward-facing boundary simplices (of dimension `d-1`) of686the `d`-dimensional triangulation as a set. Each boundary is687returned by a tuple of point indices.688689EXAMPLES::690691sage: triangulation = polytopes.n_cube(3).triangulate(engine='internal')692sage: triangulation693(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)694sage: triangulation.boundary()695frozenset([(1, 3, 7), (4, 5, 7), (1, 2, 3), (0, 1, 2), (2, 4, 6), (2, 6, 7),696(2, 3, 7), (1, 5, 7), (0, 1, 4), (1, 4, 5), (4, 6, 7), (0, 2, 4)])697sage: triangulation.interior_facets()698frozenset([(1, 4, 7), (1, 2, 7), (2, 4, 7), (0, 1, 7), (0, 4, 7), (0, 2, 7)])699"""700return frozenset(facet for facet, bounded_simplices701in self._boundary_simplex_dictionary().iteritems()702if len(bounded_simplices)==2)703704@cached_method705def normal_cone(self):706r"""707Return the (closure of the) normal cone of the triangulation.708709Recall that a regular triangulation is one that equals the710"crease lines" of a convex piecewise-linear function. This711support function is not unique, for example, you can scale it712by a positive constant. The set of all piecewise-linear713functions with fixed creases forms an open cone. This cone can714be interpreted as the cone of normal vectors at a point of the715secondary polytope, which is why we call it normal cone. See716[GKZ]_ Section 7.1 for details.717718OUTPUT:719720The closure of the normal cone. The `i`-th entry equals the721value of the piecewise-linear function at the `i`-th point of722the configuration.723724For an irregular triangulation, the normal cone is empty. In725this case, a single point (the origin) is returned.726727EXAMPLES::728729sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal')730sage: triangulation731(<0,1,3>, <0,2,3>)732sage: N = triangulation.normal_cone(); N7334-d cone in 4-d lattice734sage: N.rays()735(-1, 0, 0, 0),736( 1, 0, 1, 0),737(-1, 0, -1, 0),738( 1, 0, 0, -1),739(-1, 0, 0, 1),740( 1, 1, 0, 0),741(-1, -1, 0, 0)742in Ambient free module of rank 4743over the principal ideal domain Integer Ring744sage: N.dual().rays()745(-1, 1, 1, -1)746in Ambient free module of rank 4747over the principal ideal domain Integer Ring748749TESTS::750751sage: polytopes.n_simplex(2).triangulate().normal_cone()7523-d cone in 3-d lattice753sage: _.dual().is_trivial()754True755"""756if not self.point_configuration().base_ring().is_subring(QQ):757raise NotImplementedError('Only base rings ZZ and QQ are supported')758from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron759from sage.matrix.constructor import matrix760from sage.misc.misc import uniq761from sage.rings.arith import lcm762pc = self.point_configuration()763cs = Constraint_System()764for facet in self.interior_facets():765s0, s1 = self._boundary_simplex_dictionary()[facet]766p = set(s0).difference(facet).pop()767q = set(s1).difference(facet).pop()768origin = pc.point(p).reduced_affine_vector()769base_indices = [ i for i in s0 if i!=p ]770base = matrix([ pc.point(i).reduced_affine_vector()-origin for i in base_indices ])771sol = base.solve_left( pc.point(q).reduced_affine_vector()-origin )772relation = [0]*pc.n_points()773relation[p] = sum(sol)-1774relation[q] = 1775for i, base_i in enumerate(base_indices):776relation[base_i] = -sol[i]777rel_denom = lcm([QQ(r).denominator() for r in relation])778relation = [ ZZ(r*rel_denom) for r in relation ]779ex = Linear_Expression(relation,0)780cs.insert(ex >= 0)781from sage.modules.free_module import FreeModule782ambient = FreeModule(ZZ, self.point_configuration().n_points())783if cs.empty():784cone = C_Polyhedron(ambient.dimension(), 'universe')785else:786cone = C_Polyhedron(cs)787from sage.geometry.cone import _Cone_from_PPL788return _Cone_from_PPL(cone, lattice=ambient)789790def adjacency_graph(self):791"""792Returns a graph showing which simplices are adjacent in the793triangulation794795OUTPUT:796797A graph consisting of vertices referring to the simplices in the798triangulation, and edges showing which simplices are adjacent to each799other.800801.. SEEALSO::802803* To obtain the triangulation's 1-skeleton, use804:meth:`SimplicialComplex.graph` through805``MyTriangulation.simplicial_complex().graph()``.806807AUTHORS:808809* Stephen Farley (2013-08-10): initial version810811EXAMPLES::812813sage: p = PointConfiguration([[1,0,0], [0,1,0], [0,0,1], [-1,0,1],814....: [1,0,-1], [-1,0,0], [0,-1,0], [0,0,-1]])815sage: t = p.triangulate()816sage: t.adjacency_graph()817Graph on 8 vertices818819"""820vertices = map(Set,list(self))821return Graph([vertices,822lambda x,y: len(x-y)==1])823824825826