Path: blob/master/src/sage/geometry/polyhedron/base_ZZ.py
8817 views
r"""1Base class for polyhedra over `\ZZ`2"""34########################################################################5# Copyright (C) 2011 Volker Braun <[email protected]>6#7# Distributed under the terms of the GNU General Public License (GPL)8#9# http://www.gnu.org/licenses/10########################################################################11121314from sage.rings.all import ZZ, QQ, gcd15from sage.misc.all import cached_method16from sage.matrix.constructor import matrix17from sage.modules.free_module_element import vector18from constructor import Polyhedron19from base import Polyhedron_base20212223#########################################################################24class Polyhedron_ZZ(Polyhedron_base):25"""26Base class for Polyhedra over `\ZZ`2728TESTS::2930sage: p = Polyhedron([(0,0)], base_ring=ZZ); p31A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex32sage: TestSuite(p).run(skip='_test_pickling')33"""34def _is_zero(self, x):35"""36Test whether ``x`` is zero.3738INPUT:3940- ``x`` -- a number in the base ring.4142OUTPUT:4344Boolean.4546EXAMPLES::4748sage: p = Polyhedron([(0,0)], base_ring=ZZ)49sage: p._is_zero(0)50True51sage: p._is_zero(1/100000)52False53"""54return x==05556def _is_nonneg(self, x):57"""58Test whether ``x`` is nonnegative.5960INPUT:6162- ``x`` -- a number in the base ring.6364OUTPUT:6566Boolean.6768EXAMPLES::6970sage: p = Polyhedron([(0,0)], base_ring=ZZ)71sage: p._is_nonneg(1)72True73sage: p._is_nonneg(-1/100000)74False75"""76return x>=07778def _is_positive(self, x):79"""80Test whether ``x`` is positive.8182INPUT:8384- ``x`` -- a number in the base ring.8586OUTPUT:8788Boolean.8990EXAMPLES::9192sage: p = Polyhedron([(0,0)], base_ring=ZZ)93sage: p._is_positive(1)94True95sage: p._is_positive(0)96False97"""98return x>099100_base_ring = ZZ101102def is_lattice_polytope(self):103r"""104Return whether the polyhedron is a lattice polytope.105106OUTPUT:107108``True`` if the polyhedron is compact and has only integral109vertices, ``False`` otherwise.110111EXAMPLES::112113sage: polytopes.cross_polytope(3).is_lattice_polytope()114True115sage: polytopes.regular_polygon(5).is_lattice_polytope()116False117"""118return True119120@cached_method121def polar(self):122"""123Return the polar (dual) polytope.124125The polytope must have the IP-property (see126:meth:`has_IP_property`), that is, the origin must be an127interior point. In particular, it must be full-dimensional.128129OUTPUT:130131The polytope whose vertices are the coefficient vectors of the132inequalities of ``self`` with inhomogeneous term normalized to133unity.134135EXAMPLES::136137sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)138sage: p.polar()139A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices140sage: type(_)141<class 'sage.geometry.polyhedron.backend_ppl.Polyhedra_ZZ_ppl_with_category.element_class'>142sage: p.polar().base_ring()143Integer Ring144"""145if not self.has_IP_property():146raise ValueError('The polytope must have the IP property.')147148vertices = [ ieq.A()/ieq.b() for149ieq in self.inequality_generator() ]150if all( all(v_i in ZZ for v_i in v) for v in vertices):151return Polyhedron(vertices=vertices, base_ring=ZZ)152else:153return Polyhedron(vertices=vertices, base_ring=QQ)154155@cached_method156def is_reflexive(self):157"""158EXAMPLES::159160sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)161sage: p.is_reflexive()162True163"""164return self.polar().is_lattice_polytope()165166@cached_method167def has_IP_property(self):168"""169Test whether the polyhedron has the IP property.170171The IP (interior point) property means that172173* ``self`` is compact (a polytope).174175* ``self`` contains the origin as an interior point.176177This implies that178179* ``self`` is full-dimensional.180181* The dual polyhedron is again a polytope (that is, a compact182polyhedron), though not necessarily a lattice polytope.183184EXAMPLES::185186sage: Polyhedron([(1,1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()187False188sage: Polyhedron([(0,0),(1,0),(0,1)], base_ring=ZZ).has_IP_property()189False190sage: Polyhedron([(-1,-1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()191True192193REFERENCES:194195.. [PALP]196Maximilian Kreuzer, Harald Skarke:197"PALP: A Package for Analyzing Lattice Polytopes198with Applications to Toric Geometry"199Comput.Phys.Commun. 157 (2004) 87-106200:arxiv:`math/0204356`201"""202return self.is_compact() and self.interior_contains(self.ambient_space().zero())203204def fibration_generator(self, dim):205"""206Generate the lattice polytope fibrations.207208For the purposes of this function, a lattice polytope fiber is209a sub-lattice polytope. Projecting the plane spanned by the210subpolytope to a point yields another lattice polytope, the211base of the fibration.212213INPUT:214215- ``dim`` -- integer. The dimension of the lattice polytope216fiber.217218OUTPUT:219220A generator yielding the distinct lattice polytope fibers of221given dimension.222223EXAMPLES::224225sage: P = Polyhedron(toric_varieties.P4_11169().fan().rays(), base_ring=ZZ)226sage: list( P.fibration_generator(2) )227[A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices]228"""229from sage.combinat.combination import Combinations230if not self.is_compact():231raise ValueError('Only polytopes (compact polyhedra) are allowed.')232233nonzero_points = [p for p in self.integral_points() if not p.is_zero()]234origin = [[0]*self.ambient_dim()]235fibers = set()236parent = self.parent()237238for points in Combinations(nonzero_points, dim):239plane = parent.element_class(parent, [origin,[],points], None)240if plane.dim() != dim:241continue242fiber = self.intersection(plane)243if fiber.base_ring() is not ZZ:244continue245fiber_vertices = tuple(sorted(tuple(v) for v in fiber.vertex_generator()))246if fiber_vertices not in fibers:247yield fiber248fibers.update([fiber_vertices])249plane.delete()250251def find_translation(self, translated_polyhedron):252"""253Return the translation vector to ``translated_polyhedron``.254255INPUT:256257- ``translated_polyhedron`` -- a polyhedron.258259OUTPUT:260261A `\ZZ`-vector that translates ``self`` to262``translated_polyhedron``. A ``ValueError`` is raised if263``translated_polyhedron`` is not a translation of ``self``,264this can be used to check that two polyhedra are not265translates of each other.266267EXAMPLES::268269sage: X = polytopes.n_cube(3)270sage: X.find_translation(X + vector([2,3,5]))271(2, 3, 5)272sage: X.find_translation(2*X)273Traceback (most recent call last):274...275ValueError: polyhedron is not a translation of self276"""277no_translation_exception = ValueError('polyhedron is not a translation of self')278if ( set(self.rays()) != set(translated_polyhedron.rays()) or279set(self.lines()) != set(translated_polyhedron.lines()) or280self.n_vertices() != translated_polyhedron.n_vertices() ):281raise no_translation_exception282sorted_vertices = sorted(map(vector, self.vertices()))283sorted_translated_vertices = sorted(map(vector, translated_polyhedron.vertices()))284v = sorted_translated_vertices[0] - sorted_vertices[0]285if any(vertex+v != translated_vertex286for vertex, translated_vertex in zip(sorted_vertices, sorted_translated_vertices)):287raise no_translation_exception288return v289290def _subpoly_parallel_facets(self):291"""292Generator for all lattice sub-polyhedra with parallel facets.293294In a sub-polyhedron `Y\subset X` not all edges of `Y` need to295be parallel to `X`. This method iterates over all296sub-polyhedra where they are parallel, up to an overall297translation of the sub-polyhedron. Degenerate sub-polyhedra of298dimension strictly smaller are included.299300OUTPUT:301302A generator yielding `\ZZ`-polyhedra. By construction, each303facet of the returned polyhedron is parallel to one of the304facets of ``self``.305306EXAMPLES::307308sage: X = Polyhedron(vertices=[(0,0), (0,1), (1,0), (1,1)])309sage: X._subpoly_parallel_facets()310<generator object _subpoly_parallel_facets at 0x...>311sage: for p in X._subpoly_parallel_facets():312... print p.Vrepresentation()313(A vertex at (0, 0),)314(A vertex at (0, -1), A vertex at (0, 0))315(A vertex at (-1, 0), A vertex at (0, 0))316(A vertex at (-1, -1), A vertex at (-1, 0), A vertex at (0, -1), A vertex at (0, 0))317318TESTS::319320sage: X = Polyhedron(vertices=[(0,), (3,)])321sage: [ p.vertices() for p in X._subpoly_parallel_facets() ]322[(A vertex at (0),),323(A vertex at (-1), A vertex at (0)),324(A vertex at (-2), A vertex at (0)),325(A vertex at (-3), A vertex at (0))]326sage: list( Polyhedron(vertices=[[0,0]])._subpoly_parallel_facets() )327[A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex]328sage: list( Polyhedron()._subpoly_parallel_facets() )329[The empty polyhedron in ZZ^0]330"""331if self.dim()>2 or not self.is_compact():332raise NotImplementedError('only implemented for bounded polygons')333from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d334vertices = cyclic_sort_vertices_2d(self.vertices())335n = len(vertices)336if n==1: # single point337yield self338return339edge_vectors = []340for i in range(0,n):341v = vertices[(i+1) % n].vector() - vertices[i].vector()342d = gcd(list(v))343v_prim = (v/d).change_ring(ZZ)344edge_vectors.append([ v_prim*i for i in range(d+1) ])345origin = self.ambient_space().zero()346parent = self.parent()347from sage.combinat.cartesian_product import CartesianProduct348for edges in CartesianProduct(*edge_vectors):349v = []350point = origin351for e in edges:352point += e353v.append(point)354if point!=origin: # does not close up, not a subpolygon355continue356yield parent([v, [], []], None)357358@cached_method359def Minkowski_decompositions(self):360"""361Return all Minkowski sums that add up to the polyhedron.362363OUTPUT:364365A tuple consisting of pairs `(X,Y)` of `\ZZ`-polyhedra that366add up to ``self``. All pairs up to exchange of the summands367are returned, that is, `(Y,X)` is not included if `(X,Y)`368already is.369370EXAMPLES::371372sage: square = Polyhedron(vertices=[(0,0),(1,0),(0,1),(1,1)])373sage: square.Minkowski_decompositions()374((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,375A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),376(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,377A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices))378379Example from http://cgi.di.uoa.gr/~amantzaf/geo/ ::380381sage: Q = Polyhedron(vertices=[(4,0), (6,0), (0,3), (4,3)])382sage: R = Polyhedron(vertices=[(0,0), (5,0), (8,4), (3,2)])383sage: (Q+R).Minkowski_decompositions()384((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,385A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),386(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices,387A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),388(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,389A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),390(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,391A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),392(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,393A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),394(A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,395A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices),396(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,397A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),398(A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,399A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 6 vertices))400401sage: [ len(square.dilation(i).Minkowski_decompositions())402... for i in range(6) ]403[1, 2, 5, 8, 13, 18]404sage: [ ceil((i^2+2*i-1)/2)+1 for i in range(10) ]405[1, 2, 5, 8, 13, 18, 25, 32, 41, 50]406"""407if self.dim()>2 or not self.is_compact():408raise NotImplementedError('only implemented for bounded polygons')409summands = []410def is_known_summand(poly):411for summand in summands:412try:413poly.find_translation(summand)414return True415except ValueError:416pass417decompositions = []418for X in self._subpoly_parallel_facets():419if is_known_summand(X):420continue421Y = self - X422if X+Y != self:423continue424decompositions.append((X,Y))425summands += [X, Y]426return tuple(decompositions)427428429430