Path: blob/master/src/sage/geometry/polyhedron/ppl_lattice_polygon.py
8817 views
"""1Fast Lattice Polygons using PPL.23See :mod:`ppl_lattice_polytope` for the implementation of4arbitrary-dimensional lattice polytopes. This module is about the5specialization to 2 dimensions. To be more precise, the6:class:`LatticePolygon_PPL_class` is used if the ambient space is of7dimension 2 or less. These all allow you to cyclically order (see8:meth:`LatticePolygon_PPL_class.ordered_vertices`) the vertices, which9is in general not possible in higher dimensions.10"""1112########################################################################13# Copyright (C) 2012 Volker Braun <[email protected]>14#15# Distributed under the terms of the GNU General Public License (GPL)16#17# http://www.gnu.org/licenses/18########################################################################1920from sage.rings.integer_ring import ZZ21from sage.misc.all import cached_method, cached_function22from sage.modules.all import (vector, zero_vector)23from sage.matrix.constructor import (matrix, zero_matrix, block_matrix)24from sage.libs.ppl import (C_Polyhedron, Generator_System_iterator,25Poly_Con_Relation)26from sage.geometry.polyhedron.lattice_euclidean_group_element import (27LatticeEuclideanGroupElement)28from sage.geometry.polyhedron.ppl_lattice_polytope import (29LatticePolytope_PPL, LatticePolytope_PPL_class)303132########################################################################33class LatticePolygon_PPL_class(LatticePolytope_PPL_class):34"""35A lattice polygon3637This includes 2-dimensional polytopes as well as degenerate (0 and381-dimensional) lattice polygons. Any polytope in 2d is a polygon.39"""4041@cached_method42def ordered_vertices(self):43"""44Return the vertices of a lattice polygon in cyclic order.4546OUTPUT:4748A tuple of vertices ordered along the perimeter of the49polygon. The first point is arbitrary.5051EXAMPLES::5253sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL54sage: square = LatticePolytope_PPL((0,0), (1,1), (0,1), (1,0))55sage: square.vertices()56((0, 0), (0, 1), (1, 0), (1, 1))57sage: square.ordered_vertices()58((0, 0), (1, 0), (1, 1), (0, 1))59"""60neighbors = dict()61if self.affine_dimension() < 2:62return self.vertices()63for c in self.minimized_constraints():64v1, v2 = self.vertices_saturating(c)65neighbors[v1] = [v2] + neighbors.get(v1, [])66neighbors[v2] = [v1] + neighbors.get(v2, [])67v_prev = self.vertices()[0]68v_curr = neighbors[v_prev][0]69result = [v_prev, v_curr]70while len(result) < self.n_vertices():71v1, v2 = neighbors[v_curr]72if v1 == v_prev:73v_next = v274else:75v_next = v176result.append(v_next)77v_prev = v_curr78v_curr = v_next79return tuple(result)8081def _find_isomorphism_degenerate(self, polytope):82"""83Helper to pick an isomorphism of degenerate polygons8485INPUT:8687- ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The88polytope to compare with.8990EXAMPLES::9192sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron93sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty'))94sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty'))95sage: iso = L1.find_isomorphism(L2) # indirect doctest96sage: iso(L1) == L297True98sage: iso = L1._find_isomorphism_degenerate(L2)99sage: iso(L1) == L2100True101102sage: L1 = LatticePolytope_PPL((-1,4))103sage: L2 = LatticePolytope_PPL((2,1,5))104sage: iso = L1.find_isomorphism(L2)105sage: iso(L1) == L2106True107108sage: L1 = LatticePolytope_PPL((-1,), (3,))109sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))110sage: iso = L1.find_isomorphism(L2)111sage: iso(L1) == L2112True113114sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1))115sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))116sage: iso = L1.find_isomorphism(L2)117sage: iso(L1) == L2118True119120sage: L1 = LatticePolytope_PPL((-1,2), (3,1))121sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))122sage: iso = L1.find_isomorphism(L2)123sage: iso(L1) == L2124True125126sage: L1 = LatticePolytope_PPL((-1,2), (3,2))127sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))128sage: L1.find_isomorphism(L2)129Traceback (most recent call last):130...131LatticePolytopesNotIsomorphicError: different number of integral points132133sage: L1 = LatticePolytope_PPL((-1,2), (3,1))134sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5))135sage: L1.find_isomorphism(L2)136Traceback (most recent call last):137...138LatticePolytopesNotIsomorphicError: different number of integral points139"""140from sage.geometry.polyhedron.lattice_euclidean_group_element import \141LatticePolytopesNotIsomorphicError142polytope_vertices = polytope.vertices()143self_vertices = self.ordered_vertices()144# handle degenerate cases145if self.n_vertices() == 0:146A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension())147b = zero_vector(ZZ, polytope.space_dimension())148return LatticeEuclideanGroupElement(A, b)149if self.n_vertices() == 1:150A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension())151b = polytope_vertices[0]152return LatticeEuclideanGroupElement(A, b)153if self.n_vertices() == 2:154self_origin = self_vertices[0]155self_ray = self_vertices[1] - self_origin156polytope_origin = polytope_vertices[0]157polytope_ray = polytope_vertices[1] - polytope_origin158Ds, Us, Vs = self_ray.column().smith_form()159Dp, Up, Vp = polytope_ray.column().smith_form()160assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1161assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1162A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows())163A[0, 0] = 1164A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0])165b = polytope_origin - A*self_origin166try:167A = matrix(ZZ, A)168b = vector(ZZ, b)169except TypeError:170raise LatticePolytopesNotIsomorphicError('different lattice')171hom = LatticeEuclideanGroupElement(A, b)172if hom(self) == polytope:173return hom174raise LatticePolytopesNotIsomorphicError('different polygons')175176def _find_cyclic_isomorphism_matching_edge(self, polytope,177polytope_origin, p_ray_left,178p_ray_right):179"""180Helper to find an isomorphism of polygons181182INPUT:183184- ``polytope`` -- the lattice polytope to compare to.185186- ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope``187188- ``p_ray_left`` - vector. the vector from ``polytope_origin``189to one of its neighboring vertices.190191- ``p_ray_right`` - vector. the vector from192``polytope_origin`` to the other neighboring vertices.193194OUTPUT:195196The element of the lattice Euclidean group that maps ``self``197to ``polytope`` with given origin and left/right neighboring198vertex. A199:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`200is raised if no such isomorphism exists.201202EXAMPLES::203204sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL205sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))206sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))207sage: v0, v1, v2 = L2.vertices()208sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0)209The map A*x+b with A=210[ 0 1]211[-1 -1]212[ 1 3]213b =214(0, 1, 0)215"""216from sage.geometry.polyhedron.lattice_euclidean_group_element import \217LatticePolytopesNotIsomorphicError218polytope_matrix = block_matrix(1, 2, [p_ray_left.column(),219p_ray_right.column()])220self_vertices = self.ordered_vertices()221for i in range(len(self_vertices)):222# three consecutive vertices223v_left = self_vertices[(i+0) % len(self_vertices)]224v_origin = self_vertices[(i+1) % len(self_vertices)]225v_right = self_vertices[(i+2) % len(self_vertices)]226r_left = v_left-v_origin227r_right = v_right-v_origin228self_matrix = block_matrix(1, 2, [r_left.column(),229r_right.column()])230A = self_matrix.solve_left(polytope_matrix)231b = polytope_origin - A*v_origin232try:233A = matrix(ZZ, A)234b = vector(ZZ, b)235except TypeError:236continue237if A.elementary_divisors()[0:2] != [1, 1]:238continue239hom = LatticeEuclideanGroupElement(A, b)240if hom(self) == polytope:241return hom242raise LatticePolytopesNotIsomorphicError('different polygons')243244def find_isomorphism(self, polytope):245"""246Return a lattice isomorphism with ``polytope``.247248INPUT:249250- ``polytope`` -- a polytope, potentially higher-dimensional.251252OUTPUT:253254A255:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticeEuclideanGroupElement`. It256is not necessarily invertible if the affine dimension of257``self`` or ``polytope`` is not two. A258:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`259is raised if no such isomorphism exists.260261EXAMPLES::262263sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL264sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))265sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))266sage: iso = L1.find_isomorphism(L2)267sage: iso(L1) == L2268True269270sage: L1 = LatticePolytope_PPL((0, 1), (3, 0), (0, 3), (1, 0))271sage: L2 = LatticePolytope_PPL((0,0,2,1),(0,1,2,0),(2,0,0,3),(2,3,0,0))272sage: iso = L1.find_isomorphism(L2)273sage: iso(L1) == L2274True275276The following polygons are isomorphic over `\QQ`, but not as277lattice polytopes::278279sage: L1 = LatticePolytope_PPL((1,0),(0,1),(-1,-1))280sage: L2 = LatticePolytope_PPL((0, 0), (0, 1), (1, 0))281sage: L1.find_isomorphism(L2)282Traceback (most recent call last):283...284LatticePolytopesNotIsomorphicError: different number of integral points285sage: L2.find_isomorphism(L1)286Traceback (most recent call last):287...288LatticePolytopesNotIsomorphicError: different number of integral points289"""290from sage.geometry.polyhedron.lattice_euclidean_group_element import \291LatticePolytopesNotIsomorphicError292if polytope.affine_dimension() != self.affine_dimension():293raise LatticePolytopesNotIsomorphicError('different dimension')294polytope_vertices = polytope.vertices()295if len(polytope_vertices) != self.n_vertices():296raise LatticePolytopesNotIsomorphicError('different number of vertices')297self_vertices = self.ordered_vertices()298if len(polytope.integral_points()) != len(self.integral_points()):299raise LatticePolytopesNotIsomorphicError('different number of integral points')300301if len(self_vertices) < 3:302return self._find_isomorphism_degenerate(polytope)303304polytope_origin = polytope_vertices[0]305origin_P = C_Polyhedron(Generator_System_iterator(306polytope.minimized_generators()).next())307308neighbors = []309for c in polytope.minimized_constraints():310if not c.is_inequality():311continue312if origin_P.relation_with(c).implies(Poly_Con_Relation.saturates()):313for i, g in enumerate(polytope.minimized_generators()):314if i == 0:315continue316g = C_Polyhedron(g)317if g.relation_with(c).implies(Poly_Con_Relation.saturates()):318neighbors.append(polytope_vertices[i])319break320321p_ray_left = neighbors[0] - polytope_origin322p_ray_right = neighbors[1] - polytope_origin323try:324return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin,325p_ray_left, p_ray_right)326except LatticePolytopesNotIsomorphicError:327pass328try:329return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin,330p_ray_right, p_ray_left)331except LatticePolytopesNotIsomorphicError:332pass333raise LatticePolytopesNotIsomorphicError('different polygons')334335def is_isomorphic(self, polytope):336"""337Test if ``self`` and ``polytope`` are isomorphic.338339INPUT:340341- ``polytope`` -- a lattice polytope.342343OUTPUT:344345Boolean.346347EXAMPLES::348349sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL350sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))351sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))352sage: L1.is_isomorphic(L2)353True354"""355from sage.geometry.polyhedron.lattice_euclidean_group_element import \356LatticePolytopesNotIsomorphicError357try:358self.find_isomorphism(polytope)359return True360except LatticePolytopesNotIsomorphicError:361return False362363def sub_polytopes(self):364"""365Returns a list of all lattice sub-polygons up to isomorphsm.366367OUTPUT:368369All non-empty sub-lattice polytopes up to isomorphism. This370includes ``self`` as improper sub-polytope, but excludes the371empty polytope. Isomorphic sub-polytopes that can be embedded372in different places are only returned once.373374EXAMPLES::375376sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL377sage: P1xP1 = LatticePolytope_PPL((1,0), (0,1), (-1,0), (0,-1))378sage: P1xP1.sub_polytopes()379(A 2-dimensional lattice polytope in ZZ^2 with 4 vertices,380A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,381A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,382A 1-dimensional lattice polytope in ZZ^2 with 2 vertices,383A 1-dimensional lattice polytope in ZZ^2 with 2 vertices,384A 0-dimensional lattice polytope in ZZ^2 with 1 vertex)385"""386subpolytopes = [self]387todo = list(subpolytopes)388while todo:389polytope = todo.pop()390for p in polytope.sub_polytope_generator():391if p.is_empty():392continue393if any(p.is_isomorphic(q) for q in subpolytopes):394continue395subpolytopes.append(p)396todo.append(p)397return tuple(subpolytopes)398399def plot(self):400"""401Plot the lattice polygon402403OUTPUT:404405A graphics object.406407EXAMPLES::408409sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL410sage: P = LatticePolytope_PPL((1,0), (0,1), (0,0), (2,2))411sage: P.plot() # not tested412sage: LatticePolytope_PPL([0], [1]).plot()413sage: LatticePolytope_PPL([0]).plot()414"""415from sage.plot.point import point2d416from sage.plot.polygon import polygon2d417vertices = self.ordered_vertices()418points = self.integral_points()419if self.space_dimension() == 1:420vertices = [vector(ZZ, (v[0], 0)) for v in vertices]421points = [vector(ZZ, (p[0], 0)) for p in points]422point_plot = sum(point2d(p, pointsize=100, color='red') for p in points)423polygon_plot = polygon2d(vertices, alpha=0.2, color='green',424zorder=-1, thickness=2)425return polygon_plot + point_plot426427428########################################################################429#430# Reflexive lattice polygons and their subpolygons431#432########################################################################433434@cached_function435def polar_P2_polytope():436"""437The polar of the `P^2` polytope438439EXAMPLES::440441sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P2_polytope442sage: polar_P2_polytope()443A 2-dimensional lattice polytope in ZZ^2 with 3 vertices444sage: _.vertices()445((0, 0), (0, 3), (3, 0))446"""447return LatticePolytope_PPL((0, 0), (3, 0), (0, 3))448449450@cached_function451def polar_P1xP1_polytope():452"""453The polar of the `P^1 \times P^1` polytope454455EXAMPLES::456457sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P1xP1_polytope458sage: polar_P1xP1_polytope()459A 2-dimensional lattice polytope in ZZ^2 with 4 vertices460sage: _.vertices()461((0, 0), (0, 2), (2, 0), (2, 2))462"""463return LatticePolytope_PPL((0, 0), (2, 0), (0, 2), (2, 2))464465466@cached_function467def polar_P2_112_polytope():468"""469The polar of the `P^2[1,1,2]` polytope470471EXAMPLES::472473sage: from sage.geometry.polyhedron.ppl_lattice_polygon import polar_P2_112_polytope474sage: polar_P2_112_polytope()475A 2-dimensional lattice polytope in ZZ^2 with 3 vertices476sage: _.vertices()477((0, 0), (0, 2), (4, 0))478"""479return LatticePolytope_PPL((0, 0), (4, 0), (0, 2))480481482@cached_function483def subpolygons_of_polar_P2():484"""485The lattice sub-polygons of the polar `P^2` polytope486487OUTPUT:488489A tuple of lattice polytopes.490491EXAMPLES::492493sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P2494sage: len(subpolygons_of_polar_P2())49527496"""497return polar_P2_polytope().sub_polytopes()498499500@cached_function501def subpolygons_of_polar_P2_112():502"""503The lattice sub-polygons of the polar `P^2[1,1,2]` polytope504505OUTPUT:506507A tuple of lattice polytopes.508509EXAMPLES::510511sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P2_112512sage: len(subpolygons_of_polar_P2_112())51328514"""515return polar_P2_112_polytope().sub_polytopes()516517518@cached_function519def subpolygons_of_polar_P1xP1():520"""521The lattice sub-polygons of the polar `P^1 \times P^1` polytope522523OUTPUT:524525A tuple of lattice polytopes.526527EXAMPLES::528529sage: from sage.geometry.polyhedron.ppl_lattice_polygon import subpolygons_of_polar_P1xP1530sage: len(subpolygons_of_polar_P1xP1())53120532"""533return polar_P1xP1_polytope().sub_polytopes()534535536@cached_function537def sub_reflexive_polygons():538"""539Return all lattice sub-polygons of reflexive polygons.540541OUTPUT:542543A tuple of all lattice sub-polygons. Each sub-polygon is returned544as a pair sub-polygon, containing reflexive polygon.545546EXAMPLES::547548sage: from sage.geometry.polyhedron.ppl_lattice_polygon import sub_reflexive_polygons549sage: l = sub_reflexive_polygons(); l[5]550(A 2-dimensional lattice polytope in ZZ^2 with 6 vertices,551A 2-dimensional lattice polytope in ZZ^2 with 3 vertices)552sage: len(l)55333554"""555result = []556557def add_result(subpolygon, ambient):558if not any(subpolygon.is_isomorphic(p[0]) for p in result):559result.append((subpolygon, ambient))560for p in subpolygons_of_polar_P2():561add_result(p, polar_P2_polytope())562for p in subpolygons_of_polar_P2_112():563add_result(p, polar_P2_112_polytope())564for p in subpolygons_of_polar_P1xP1():565add_result(p, polar_P1xP1_polytope())566return tuple(result)567568569