Path: blob/master/src/sage/geometry/polyhedron/base.py
8817 views
r"""1Base class for polyhedra2"""345########################################################################6# Copyright (C) 2008 Marshall Hampton <[email protected]>7# Copyright (C) 2011 Volker Braun <[email protected]>8#9# Distributed under the terms of the GNU General Public License (GPL)10#11# http://www.gnu.org/licenses/12########################################################################131415from sage.structure.element import Element, coerce_binop, is_Vector1617from sage.misc.all import cached_method, prod18from sage.misc.package import is_package_installed1920from sage.rings.all import Integer, QQ, ZZ21from sage.rings.real_double import RDF22from sage.modules.free_module_element import vector23from sage.matrix.constructor import matrix24from sage.functions.other import sqrt, floor, ceil25from sage.misc.prandom import randint2627from sage.graphs.graph import Graph2829from sage.combinat.cartesian_product import CartesianProduct3031from constructor import Polyhedron323334#########################################################################35# Notes if you want to implement your own backend:36#37# * derive from Polyhedron_base38#39# * you must implement _init_from_Vrepresentation and40# _init_from_Vrepresentationa41#42# * You might want to override _init_empty_polyhedron,43# _init_facet_adjacency_matrix, _init_vertex_adjacency_matrix, and44# _make_polyhedron_face.45#46# * You can of course also override any other method for which you47# have a faster implementation.48#########################################################################495051#########################################################################52def is_Polyhedron(X):53"""54Test whether ``X`` is a Polyhedron.5556INPUT:5758- ``X`` -- anything.5960OUTPUT:6162Boolean.6364EXAMPLES::6566sage: p = polytopes.n_cube(2)67sage: from sage.geometry.polyhedron.base import is_Polyhedron68sage: is_Polyhedron(p)69True70sage: is_Polyhedron(123456)71False72"""73return isinstance(X, Polyhedron_base)747576#########################################################################77class Polyhedron_base(Element):78"""79Base class for Polyhedron objects8081INPUT:8283- ``parent`` -- the parent, an instance of84:class:`~sage.geometry.polyhedron.parent.Polyhedra`.8586- ``Vrep`` -- a list ``[vertices, rays, lines]`` or ``None``. The87V-representation of the polyhedron. If ``None``, the polyhedron88is determined by the H-representation.8990- ``Hrep`` -- a list ``[ieqs, eqns]`` or ``None``. The91H-representation of the polyhedron. If ``None``, the polyhedron92is determined by the V-representation.9394Only one of ``Vrep`` or ``Hrep`` can be different from ``None``.9596TESTS::9798sage: p = Polyhedron()99sage: TestSuite(p).run()100"""101102def __init__(self, parent, Vrep, Hrep, **kwds):103"""104Initializes the polyhedron.105106See :class:`Polyhedron_base` for a description of the input107data.108109TESTS::110111sage: p = Polyhedron() # indirect doctests112"""113Element.__init__(self, parent=parent)114if Vrep is not None:115vertices, rays, lines = Vrep116self._init_from_Vrepresentation(vertices, rays, lines, **kwds)117elif Hrep is not None:118ieqs, eqns = Hrep119self._init_from_Hrepresentation(ieqs, eqns, **kwds)120else:121self._init_empty_polyhedron()122123def _sage_input_(self, sib, coerced):124"""125Return Sage command to reconstruct ``self``.126127See :mod:`sage.misc.sage_input` for details.128129EXAMPLES::130131sage: P = Polyhedron([(1,0), (0,1)], rays=[(1,1)])132sage: sage_input(P)133Polyhedron(base_ring=ZZ, rays=[(1, 1)], vertices=[(0, 1), (1, 0)])134"""135kwds = dict()136kwds['base_ring'] = sib(self.base_ring())137if self.n_vertices() > 0:138kwds['vertices'] = [sib(tuple(v)) for v in self.vertices()]139if self.n_rays() > 0:140kwds['rays'] = [sib(tuple(r)) for r in self.rays()]141if self.n_lines() > 0:142kwds['lines'] = [sib(tuple(l)) for l in self.lines()]143return sib.name('Polyhedron')(**kwds)144145def _init_from_Vrepresentation(self, vertices, rays, lines, **kwds):146"""147Construct polyhedron from V-representation data.148149INPUT:150151- ``vertices`` -- list of point. Each point can be specified152as any iterable container of153:meth:`~sage.geometry.polyhedron.base.base_ring` elements.154155- ``rays`` -- list of rays. Each ray can be specified as any156iterable container of157:meth:`~sage.geometry.polyhedron.base.base_ring` elements.158159- ``lines`` -- list of lines. Each line can be specified as160any iterable container of161:meth:`~sage.geometry.polyhedron.base.base_ring` elements.162163EXAMPLES::164165sage: p = Polyhedron()166sage: from sage.geometry.polyhedron.base import Polyhedron_base167sage: Polyhedron_base._init_from_Vrepresentation(p, [], [], [])168Traceback (most recent call last):169...170NotImplementedError: A derived class must implement this method.171"""172raise NotImplementedError('A derived class must implement this method.')173174175def _init_from_Hrepresentation(self, ieqs, eqns, **kwds):176"""177Construct polyhedron from H-representation data.178179INPUT:180181- ``ieqs`` -- list of inequalities. Each line can be specified182as any iterable container of183:meth:`~sage.geometry.polyhedron.base.base_ring` elements.184185- ``eqns`` -- list of equalities. Each line can be specified186as any iterable container of187:meth:`~sage.geometry.polyhedron.base.base_ring` elements.188189EXAMPLES::190191sage: p = Polyhedron()192sage: from sage.geometry.polyhedron.base import Polyhedron_base193sage: Polyhedron_base._init_from_Hrepresentation(p, [], [])194Traceback (most recent call last):195...196NotImplementedError: A derived class must implement this method.197"""198raise NotImplementedError('A derived class must implement this method.')199200def _init_empty_polyhedron(self):201"""202Initializes an empty polyhedron.203204TESTS::205206sage: empty = Polyhedron(); empty207The empty polyhedron in ZZ^0208sage: empty.Vrepresentation()209()210sage: empty.Hrepresentation()211(An equation -1 == 0,)212sage: Polyhedron(vertices = [])213The empty polyhedron in ZZ^0214sage: Polyhedron(vertices = [])._init_empty_polyhedron()215sage: from sage.geometry.polyhedron.parent import Polyhedra216sage: Polyhedra(QQ,7)()217A 0-dimensional polyhedron in QQ^7 defined as the convex hull of 1 vertex218"""219self._Vrepresentation = []220self._Hrepresentation = []221self.parent()._make_Equation(self, [-1] + [0]*self.ambient_dim());222self._Vrepresentation = tuple(self._Vrepresentation)223self._Hrepresentation = tuple(self._Hrepresentation)224225V_matrix = matrix(ZZ, 0, 0, 0)226V_matrix.set_immutable()227self.vertex_adjacency_matrix.set_cache(V_matrix)228229H_matrix = matrix(ZZ, 1, 1, 0)230H_matrix.set_immutable()231self.facet_adjacency_matrix.set_cache(H_matrix)232233def _facet_adjacency_matrix(self):234"""235Compute the facet adjacency matrix in case it has not been236computed during initialization.237238EXAMPLES::239240sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)])241sage: p._facet_adjacency_matrix()242[0 1 1]243[1 0 1]244[1 1 0]245"""246# TODO: This implementation computes the whole face lattice,247# which is much more information than necessary.248M = matrix(ZZ, self.n_Hrepresentation(), self.n_Hrepresentation(), 0)249def set_adjacent(h1,h2):250if h1 is h2:251return252i = h1.index()253j = h2.index()254M[i,j]=1255M[j,i]=1256257face_lattice = self.face_lattice()258for face in face_lattice:259Hrep = face.ambient_Hrepresentation()260if len(Hrep) == 2:261set_adjacent(Hrep[0], Hrep[1])262return M263264def _vertex_adjacency_matrix(self):265"""266Compute the vertex adjacency matrix in case it has not been267computed during initialization.268269EXAMPLES::270271sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)])272sage: p._vertex_adjacency_matrix()273[0 1 1]274[1 0 1]275[1 1 0]276"""277# TODO: This implementation computes the whole face lattice,278# which is much more information than necessary.279M = matrix(ZZ, self.n_Vrepresentation(), self.n_Vrepresentation(), 0)280def set_adjacent(v1,v2):281if v1 is v2:282return283i = v1.index()284j = v2.index()285M[i,j]=1286M[j,i]=1287288face_lattice = self.face_lattice()289for face in face_lattice:290Vrep = face.ambient_Vrepresentation()291if len(Vrep) == 2:292set_adjacent(Vrep[0], Vrep[1])293return M294295def delete(self):296"""297Delete this polyhedron.298299This speeds up creation of new polyhedra by reusing300objects. After recycling a polyhedron object, it is not in a301consistent state any more and neither the polyhedron nor its302H/V-representation objects may be used any more.303304.. seealso:: :meth:`~sage.geometry.polyhedron.parent.Polyhedra_base.recycle`305306EXAMPLES::307308sage: p = Polyhedron([(0,0),(1,0),(0,1)])309sage: p.delete()310311sage: vertices = [(0,0,0,0),(1,0,0,0),(0,1,0,0),(1,1,0,0),(0,0,1,0),(0,0,0,1)]312sage: def loop_polyhedra():313... for i in range(0,100):314... p = Polyhedron(vertices)315316sage: timeit('loop_polyhedra()') # not tested - random3175 loops, best of 3: 79.5 ms per loop318319sage: def loop_polyhedra_with_recycling():320... for i in range(0,100):321... p = Polyhedron(vertices)322... p.delete()323324sage: timeit('loop_polyhedra_with_recycling()') # not tested - random3255 loops, best of 3: 57.3 ms per loop326"""327self.parent().recycle(self)328329def base_extend(self, base_ring, backend=None):330"""331Return a new polyhedron over a larger field.332333INPUT:334335- ``base_ring`` -- the new base ring.336337- ``backend`` -- the new backend, see338:func:`~sage.geometry.polyhedron.constructor.Polyhedron`.339340OUTPUT:341342The same polyhedron, but over a larger base ring.343344EXAMPLES::345346sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ); P347A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray348sage: P.base_extend(QQ)349A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray350sage: P.base_extend(QQ) == P351True352"""353new_parent = self.parent().base_extend(base_ring, backend)354return new_parent(self)355356def __cmp__(self, other):357"""358Compare ``self`` and ``other``.359360INPUT:361362- ``other`` -- anything.363364OUTPUT:365366`-1, 0, +1` depending on how ``self`` and ``other``367compare. If ``other`` is a polyhedron, then the comparison368operator "less or equal than" means "is contained in", and369"less than" means "is strictly contained in".370371EXAMPLES::372373sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)])374sage: Q = Polyhedron(vertices=[(1,0), (0,1)])375sage: cmp(P,Q)3761377sage: cmp(Q,P)378-1379sage: cmp(P,P)3800381sage: abs(cmp(P, 'anything'))3821383384The polytope ``Q`` is contained in ``P``::385386sage: P > Q387True388sage: P < Q389False390sage: P == Q391False392393TESTS::394395sage: abs(cmp(P, 'string'))3961397"""398if not isinstance(other, Polyhedron_base):399return -1400if self._Vrepresentation is None or other._Vrepresentation is None:401return -1 # make sure deleted polyhedra are not used in cache402c = cmp(self.ambient_dim(), other.ambient_dim())403if c != 0: return c404c0 = self._is_subpolyhedron(other)405c1 = other._is_subpolyhedron(self)406if c0 and c1:407return 0408if c0:409return -1410else:411return +1412413@coerce_binop414def _is_subpolyhedron(self, other):415"""416Test whether ``self`` is a (not necessarily strict)417sub-polyhdedron of ``other``.418419INPUT:420421- ``other`` -- a :class:`Polyhedron`.422423OUTPUT:424425Boolean.426427EXAMPLES::428429sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)])430sage: Q = Polyhedron(vertices=[(1,0), (0,1)])431sage: P._is_subpolyhedron(Q)432False433sage: Q._is_subpolyhedron(P)434True435"""436return all( other_H.contains(self_V)437for other_H, self_V in438CartesianProduct(other.Hrepresentation(), self.Vrepresentation()) )439440def plot(self,441point=None, line=None, polygon=None, # None means unspecified by the user442wireframe='blue', fill='green',443**kwds):444"""445Return a graphical representation.446447INPUT:448449- ``point``, ``line``, ``polygon`` -- Parameters to pass to450point (0d), line (1d), and polygon (2d) plot commands.451Allowed values are:452453* A Python dictionary to be passed as keywords to the plot454commands.455456* A string or triple of numbers: The color. This is457equivalent to passing the dictionary ``{'color':...}``.458459* ``False``: Switches off the drawing of the corresponding460graphics object461462- ``wireframe``, ``fill`` -- Similar to ``point``, ``line``,463and ``polygon``, but ``fill`` is used for the graphics464objects in the dimension of the polytope (or of dimension 2465for higher dimensional polytopes) and ``wireframe`` is used466for all lower-dimensional graphics objects467(default: 'green' for fill and 'blue' for wireframe)468469- ``**kwds`` -- optional keyword parameters that are passed to470all graphics objects.471472OUTPUT:473474A (multipart) graphics object.475476EXAMPLES::477478sage: square = polytopes.n_cube(2)479sage: point = Polyhedron([[1,1]])480sage: line = Polyhedron([[1,1],[2,1]])481sage: cube = polytopes.n_cube(3)482sage: hypercube = polytopes.n_cube(4)483484By default, the wireframe is rendered in blue and the fill in green::485486sage: square.plot()487sage: point.plot()488sage: line.plot()489sage: cube.plot()490sage: hypercube.plot()491492Draw the lines in red and nothing else::493494sage: square.plot(point=False, line='red', polygon=False)495sage: point.plot(point=False, line='red', polygon=False)496sage: line.plot(point=False, line='red', polygon=False)497sage: cube.plot(point=False, line='red', polygon=False)498sage: hypercube.plot(point=False, line='red', polygon=False)499500Draw points in red, no lines, and a blue polygon::501502sage: square.plot(point={'color':'red'}, line=False, polygon=(0,0,1))503sage: point.plot(point={'color':'red'}, line=False, polygon=(0,0,1))504sage: line.plot(point={'color':'red'}, line=False, polygon=(0,0,1))505sage: cube.plot(point={'color':'red'}, line=False, polygon=(0,0,1))506sage: hypercube.plot(point={'color':'red'}, line=False, polygon=(0,0,1))507508If we instead use the ``fill`` and ``wireframe`` options, the509coloring depends on the dimension of the object::510511sage: square.plot(fill='green', wireframe='red')512sage: point.plot(fill='green', wireframe='red')513sage: line.plot(fill='green', wireframe='red')514sage: cube.plot(fill='green', wireframe='red')515sage: hypercube.plot(fill='green', wireframe='red')516517TESTS::518519sage: for p in square.plot():520... print p.options()['rgbcolor'], p521blue Point set defined by 4 point(s)522blue Line defined by 2 points523blue Line defined by 2 points524blue Line defined by 2 points525blue Line defined by 2 points526green Polygon defined by 4 points527528sage: for p in line.plot():529... print p.options()['rgbcolor'], p530blue Point set defined by 2 point(s)531green Line defined by 2 points532533sage: for p in point.plot():534... print p.options()['rgbcolor'], p535green Point set defined by 1 point(s)536537Draw the lines in red and nothing else::538539sage: for p in square.plot(point=False, line='red', polygon=False):540... print p.options()['rgbcolor'], p541red Line defined by 2 points542red Line defined by 2 points543red Line defined by 2 points544red Line defined by 2 points545546Draw vertices in red, no lines, and a blue polygon::547548sage: for p in square.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):549... print p.options()['rgbcolor'], p550red Point set defined by 4 point(s)551(0, 0, 1) Polygon defined by 4 points552553sage: for p in line.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):554... print p.options()['rgbcolor'], p555red Point set defined by 2 point(s)556557sage: for p in point.plot(point={'color':'red'}, line=False, polygon=(0,0,1)):558... print p.options()['rgbcolor'], p559red Point set defined by 1 point(s)560561Draw in red without wireframe::562563sage: for p in square.plot(wireframe=False, fill="red"):564... print p.options()['rgbcolor'], p565red Polygon defined by 4 points566567sage: for p in line.plot(wireframe=False, fill="red"):568... print p.options()['rgbcolor'], p569red Line defined by 2 points570571sage: for p in point.plot(wireframe=False, fill="red"):572... print p.options()['rgbcolor'], p573red Point set defined by 1 point(s)574"""575def merge_options(*opts):576merged = dict()577for i in range(len(opts)):578opt = opts[i]579if opt is None:580continue581elif opt is False:582return False583elif isinstance(opt, (basestring, list, tuple)):584merged['color'] = opt585else:586merged.update(opt)587return merged588589d = min(self.dim(), 2)590opts = [wireframe] * d + [fill] + [False] * (2-d)591# The point/line/polygon options take precedence over wireframe/fill592opts = [merge_options(opt1, opt2, kwds)593for opt1, opt2 in zip(opts, [point, line, polygon])]594595from plot import render_2d, render_3d, render_4d596render_method = [ None, None, render_2d, render_3d, render_4d ]597if self.ambient_dim() < len(render_method):598render = render_method[self.ambient_dim()]599if render != None:600return render(self, *opts)601raise NotImplementedError('Plotting of '+str(self.ambient_dim())+602'-dimensional polyhedra not implemented')603604show = plot605606def _repr_(self):607"""608Return a description of the polyhedron.609610EXAMPLES::611612sage: poly_test = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]])613sage: poly_test._repr_()614'A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices'615sage: grammar_test = Polyhedron(vertices = [[1,1,1,1,1,1]])616sage: grammar_test._repr_()617'A 0-dimensional polyhedron in ZZ^6 defined as the convex hull of 1 vertex'618"""619desc = ''620if self.n_vertices()==0:621desc += 'The empty polyhedron'622else:623desc += 'A ' + repr(self.dim()) + '-dimensional polyhedron'624desc += ' in '625if self.base_ring() is QQ: desc += 'QQ'626elif self.base_ring() is ZZ: desc += 'ZZ'627elif self.base_ring() is RDF: desc += 'RDF'628else: assert False629desc += '^' + repr(self.ambient_dim())630631if self.n_vertices()>0:632desc += ' defined as the convex hull of '633desc += repr(self.n_vertices())634if self.n_vertices()==1: desc += ' vertex'635else: desc += ' vertices'636637if self.n_rays()>0:638if self.n_lines()>0: desc += ", "639else: desc += " and "640desc += repr(self.n_rays())641if self.n_rays()==1: desc += ' ray'642else: desc += ' rays'643644if self.n_lines()>0:645if self.n_rays()>0: desc += ", "646else: desc += " and "647desc += repr(self.n_lines())648if self.n_lines()==1: desc +=' line'649else: desc +=' lines'650651return desc652653def cdd_Hrepresentation(self):654"""655Write the inequalities/equations data of the polyhedron in656cdd's H-representation format.657658OUTPUT:659660A string. If you save the output to filename.ine then you can661run the stand-alone cdd via ``cddr+ filename.ine``662663EXAMPLES::664665sage: p = polytopes.n_cube(2)666sage: print p.cdd_Hrepresentation()667H-representation668begin6694 3 rational6701 1 06711 0 16721 -1 06731 0 -1674end675"""676from cdd_file_format import cdd_Hrepresentation677try:678cdd_type = self._cdd_type679except AttributeError:680if self.base_ring() is ZZ or self.base_ring() is QQ:681cdd_type = 'rational'682elif self.base_ring() is RDF:683cdd_type = 'real'684else:685raise TypeError('The base ring must be ZZ, QQ, or RDF')686return cdd_Hrepresentation(cdd_type,687list(self.inequality_generator()),688list(self.equation_generator()) )689690def cdd_Vrepresentation(self):691"""692Write the vertices/rays/lines data of the polyhedron in cdd's693V-representation format.694695OUTPUT:696697A string. If you save the output to filename.ext then you can698run the stand-alone cdd via ``cddr+ filename.ext``699700EXAMPLES::701702sage: q = Polyhedron(vertices = [[1,1],[0,0],[1,0],[0,1]])703sage: print q.cdd_Vrepresentation()704V-representation705begin7064 3 rational7071 0 07081 0 17091 1 07101 1 1711end712"""713from cdd_file_format import cdd_Vrepresentation714try:715cdd_type = self._cdd_type716except AttributeError:717if self.base_ring() is ZZ or self.base_ring() is QQ:718cdd_type = 'rational'719elif self.base_ring() is RDF:720cdd_type = 'real'721else:722raise TypeError('The base ring must be ZZ, QQ, or RDF')723return cdd_Vrepresentation(cdd_type,724list(self.vertex_generator()),725list(self.ray_generator()),726list(self.line_generator()) )727728@cached_method729def n_equations(self):730"""731Return the number of equations. The representation will732always be minimal, so the number of equations is the733codimension of the polyhedron in the ambient space.734735EXAMPLES::736737sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]])738sage: p.n_equations()7391740"""741return len(self.equations())742743@cached_method744def n_inequalities(self):745"""746Return the number of inequalities. The representation will747always be minimal, so the number of inequalities is the748number of facets of the polyhedron in the ambient space.749750EXAMPLES::751752sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]])753sage: p.n_inequalities()7543755756sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in range(6)])757sage: p.n_facets()7588759"""760return len(self.inequalities())761762n_facets = n_inequalities763764@cached_method765def n_vertices(self):766"""767Return the number of vertices. The representation will768always be minimal.769770EXAMPLES::771772sage: p = Polyhedron(vertices = [[1,0],[0,1],[1,1]], rays=[[1,1]])773sage: p.n_vertices()7742775"""776return len(self.vertices())777778@cached_method779def n_rays(self):780"""781Return the number of rays. The representation will782always be minimal.783784EXAMPLES::785786sage: p = Polyhedron(vertices = [[1,0],[0,1]], rays=[[1,1]])787sage: p.n_rays()7881789"""790return len(self.rays())791792@cached_method793def n_lines(self):794"""795Return the number of lines. The representation will796always be minimal.797798EXAMPLES::799800sage: p = Polyhedron(vertices = [[0,0]], rays=[[0,1],[0,-1]])801sage: p.n_lines()8021803"""804return len(self.lines())805806def Hrepresentation(self, index=None):807"""808Return the objects of the H-representaton. Each entry is809either an inequality or a equation.810811INPUT:812813- ``index`` -- either an integer or ``None``.814815OUTPUT:816817The optional argument is an index running from ``0`` to818``self.n_Hrepresentation()-1``. If present, the819H-representation object at the given index will be820returned. Without an argument, returns the list of all821H-representation objects.822823EXAMPLES::824825sage: p = polytopes.n_cube(3)826sage: p.Hrepresentation(0)827An inequality (0, 0, -1) x + 1 >= 0828sage: p.Hrepresentation(0) == p.Hrepresentation() [0]829True830"""831if index is None:832return self._Hrepresentation833else:834return self._Hrepresentation[index]835836837def Hrep_generator(self):838"""839Return an iterator over the objects of the H-representation840(inequalities or equations).841842EXAMPLES::843844sage: p = polytopes.n_cube(3)845sage: p.Hrep_generator().next()846An inequality (0, 0, -1) x + 1 >= 0847"""848for H in self.Hrepresentation():849yield H850851@cached_method852def n_Hrepresentation(self):853"""854Return the number of objects that make up the855H-representation of the polyhedron.856857OUTPUT:858859Integer.860861EXAMPLES::862863sage: p = polytopes.cross_polytope(4)864sage: p.n_Hrepresentation()86516866sage: p.n_Hrepresentation() == p.n_inequalities() + p.n_equations()867True868"""869return len(self.Hrepresentation())870871def Vrepresentation(self, index=None):872"""873Return the objects of the V-representation. Each entry is874either a vertex, a ray, or a line.875876See :mod:`sage.geometry.polyhedron.constructor` for a877definition of vertex/ray/line.878879INPUT:880881- ``index`` -- either an integer or ``None``.882883OUTPUT:884885The optional argument is an index running from ``0`` to886`self.n_Vrepresentation()-1``. If present, the887V-representation object at the given index will be888returned. Without an argument, returns the list of all889V-representation objects.890891EXAMPLES::892893sage: p = polytopes.n_simplex(4)894sage: p.Vrepresentation(0)895A vertex at (-7071/10000, 1633/4000, 7217/25000, 22361/100000)896sage: p.Vrepresentation(0) == p.Vrepresentation() [0]897True898"""899if index is None:900return self._Vrepresentation901else:902return self._Vrepresentation[index]903904@cached_method905def n_Vrepresentation(self):906"""907Return the number of objects that make up the908V-representation of the polyhedron.909910OUTPUT:911912Integer.913914EXAMPLES::915916sage: p = polytopes.n_simplex(4)917sage: p.n_Vrepresentation()9185919sage: p.n_Vrepresentation() == p.n_vertices() + p.n_rays() + p.n_lines()920True921"""922return len(self.Vrepresentation())923924def Vrep_generator(self):925"""926Returns an iterator over the objects of the V-representation927(vertices, rays, and lines).928929EXAMPLES::930931sage: p = polytopes.cyclic_polytope(3,4)932sage: vg = p.Vrep_generator()933sage: vg.next()934A vertex at (0, 0, 0)935sage: vg.next()936A vertex at (1, 1, 1)937"""938for V in self.Vrepresentation():939yield V940941def facial_adjacencies(self):942r"""943Return the list of face indices (i.e. indices of944H-representation objects) and the indices of faces adjacent to945them.946947.. NOTE::948949Instead of working with face indices, it is recommended950that you use the H-representation objects directly (see951example).952953EXAMPLES::954955sage: p = polytopes.permutahedron(4)956sage: p.facial_adjacencies()[0:3]957doctest:...: DeprecationWarning:958This method is deprecated.959Use self.Hrepresentation(i).neighbors() instead.960See http://trac.sagemath.org/11763 for details.961[[0, [1, 2, 5, 10, 12, 13]], [1, [0, 2, 5, 7, 9, 11]], [2, [0, 1, 10, 11]]]962sage: f0 = p.Hrepresentation(0)963sage: f0.index() == 0964True965sage: f0_adjacencies = [f0.index(), [n.index() for n in f0.neighbors()]]966sage: p.facial_adjacencies()[0] == f0_adjacencies967True968"""969from sage.misc.superseded import deprecation970deprecation(11763, 'This method is deprecated. Use self.Hrepresentation(i).neighbors() instead.')971try:972return self._facial_adjacencies973except AttributeError:974self._facial_adjacencies = \975[ [ h.index(),976[n.index() for n in h.neighbors()]977] for h in self.Hrepresentation() ]978return self._facial_adjacencies979980def facial_incidences(self):981"""982Return the face-vertex incidences in the form `[f_i, [v_{i_0}, v_{i_1},\dots ,v_{i_2}]]`.983984.. NOTE::985986Instead of working with face/vertex indices, it is987recommended that you use the988H-representation/V-representation objects directly (see989examples). Or use :meth:`incidence_matrix`.990991OUTPUT:992993The face indices are the indices of the H-representation994objects, and the vertex indices are the indices of the995V-representation objects.996997EXAMPLES::998999sage: p = Polyhedron(vertices = [[5,0,0],[0,5,0],[5,5,0],[0,0,0],[2,2,5]])1000sage: p.facial_incidences()1001doctest:...: DeprecationWarning:1002This method is deprecated. Use self.Hrepresentation(i).incident() instead.1003See http://trac.sagemath.org/11763 for details.1004[[0, [0, 1, 3, 4]],1005[1, [0, 1, 2]],1006[2, [0, 2, 3]],1007[3, [2, 3, 4]],1008[4, [1, 2, 4]]]10091010sage: f0 = p.Hrepresentation(0)1011sage: f0.index() == 01012True1013sage: f0_incidences = [f0.index(), [v.index() for v in f0.incident()]]1014sage: p.facial_incidences()[0] == f0_incidences1015True10161017sage: p.incidence_matrix().column(0)1018(1, 1, 0, 1, 1)1019sage: p.incidence_matrix().column(1)1020(1, 1, 1, 0, 0)1021sage: p.incidence_matrix().column(2)1022(1, 0, 1, 1, 0)1023sage: p.incidence_matrix().column(3)1024(0, 0, 1, 1, 1)1025sage: p.incidence_matrix().column(4)1026(0, 1, 1, 0, 1)1027"""1028from sage.misc.superseded import deprecation1029deprecation(11763, 'This method is deprecated. Use self.Hrepresentation(i).incident() instead.')1030try:1031return self._facial_incidences1032except AttributeError:1033self._facial_incidences = \1034[ [ h.index(),1035[v.index() for v in h.incident()]1036] for h in self.Hrepresentation() ]1037return self._facial_incidences10381039def vertex_adjacencies(self):1040"""1041Return a list of vertex indices and their adjacent vertices.10421043.. NOTE::10441045Instead of working with vertex indices, you can use the1046V-representation objects directly (see examples).10471048Two V-representation objects are adjacent if they generate a1049(1-dimensional) face of the polyhedron. Examples are two1050vertices of a polytope that bound an edge, or a vertex and a1051ray of a polyhedron that generate a bounding half-line of the1052polyhedron. See :meth:`vertex_adjacency_matrix` for a more1053detailed discussion.10541055OUTPUT:10561057The vertex indices are the indices of the V-representation1058objects.10591060EXAMPLES::10611062sage: permuta3 = Polyhedron(vertices = Permutations([1,2,3,4]))1063sage: permuta3.vertex_adjacencies()[0:3]1064doctest:...: DeprecationWarning:1065This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.1066See http://trac.sagemath.org/11763 for details.1067[[0, [1, 2, 6]], [1, [0, 3, 7]], [2, [0, 4, 8]]]1068sage: v0 = permuta3.Vrepresentation(0)1069sage: v0.index() == 01070True1071sage: list( v0.neighbors() )1072[A vertex at (1, 2, 4, 3), A vertex at (1, 3, 2, 4), A vertex at (2, 1, 3, 4)]1073sage: v0_adjacencies = [v0.index(), [v.index() for v in v0.neighbors()]]1074sage: permuta3.vertex_adjacencies()[0] == v0_adjacencies1075True1076"""1077from sage.misc.superseded import deprecation1078deprecation(11763, 'This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.')1079try:1080return self._vertex_adjacencies1081except AttributeError:1082self._vertex_adjacencies = \1083[ [ v.index(),1084[n.index() for n in v.neighbors()]1085] for v in self.Vrepresentation() ]1086return self._vertex_adjacencies10871088def vertex_incidences(self):1089"""1090Return the vertex-face incidences in the form `[v_i, [f_{i_0}, f_{i_1},\dots ,f_{i_2}]]`.10911092.. NOTE::10931094Instead of working with face/vertex indices, you can use1095the H-representation/V-representation objects directly1096(see examples).10971098EXAMPLES::10991100sage: p = polytopes.n_simplex(3)1101sage: p.vertex_incidences()1102doctest:...: DeprecationWarning:1103This method is deprecated. Use self.Vrepresentation(i).incident() instead.1104See http://trac.sagemath.org/11763 for details.1105[[0, [0, 1, 2]], [1, [0, 1, 3]], [2, [0, 2, 3]], [3, [1, 2, 3]]]1106sage: v0 = p.Vrepresentation(0)1107sage: v0.index() == 01108True1109sage: p.vertex_incidences()[0] == [ v0.index(), [h.index() for h in v0.incident()] ]1110True1111"""1112from sage.misc.superseded import deprecation1113deprecation(11763, 'This method is deprecated. Use self.Vrepresentation(i).incident() instead.')1114try:1115return self._vertex_incidences1116except AttributeError:1117self._vertex_incidences = \1118[ [ v.index(),1119[h.index() for h in v.incident()]1120] for v in self.Vrepresentation() ]1121return self._vertex_incidences11221123def inequality_generator(self):1124"""1125Return a generator for the defining inequalities of the1126polyhedron.11271128OUTPUT:11291130A generator of the inequality Hrepresentation objects.11311132EXAMPLES::11331134sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])1135sage: for v in triangle.inequality_generator(): print(v)1136An inequality (1, 1) x - 1 >= 01137An inequality (0, -1) x + 1 >= 01138An inequality (-1, 0) x + 1 >= 01139sage: [ v for v in triangle.inequality_generator() ]1140[An inequality (1, 1) x - 1 >= 0,1141An inequality (0, -1) x + 1 >= 0,1142An inequality (-1, 0) x + 1 >= 0]1143sage: [ [v.A(), v.b()] for v in triangle.inequality_generator() ]1144[[(1, 1), -1], [(0, -1), 1], [(-1, 0), 1]]1145"""1146for H in self.Hrepresentation():1147if H.is_inequality():1148yield H11491150@cached_method1151def inequalities(self):1152"""1153Return all inequalities.11541155OUTPUT:11561157A tuple of inequalities.11581159EXAMPLES::11601161sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]])1162sage: p.inequalities()[0:3]1163(An inequality (1, 0, 0) x + 0 >= 0,1164An inequality (0, 1, 0) x + 0 >= 0,1165An inequality (0, 0, 1) x + 0 >= 0)1166sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))1167sage: ieqs = p3.inequalities()1168sage: ieqs[0]1169An inequality (0, 1, 1, 1) x - 6 >= 01170sage: list(_)1171[-6, 0, 1, 1, 1]1172"""1173return tuple(self.inequality_generator())11741175def inequalities_list(self):1176"""1177Return a list of inequalities as coefficient lists.11781179.. NOTE::11801181It is recommended to use :meth:`inequalities` or1182:meth:`inequality_generator` instead to iterate over the1183list of :class:`Inequality` objects.11841185EXAMPLES::11861187sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]])1188sage: p.inequalities_list()[0:3]1189[[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]1190sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))1191sage: ieqs = p3.inequalities_list()1192sage: ieqs[0]1193[-6, 0, 1, 1, 1]1194sage: ieqs[-1]1195[-3, 0, 1, 0, 1]1196sage: ieqs == [list(x) for x in p3.inequality_generator()]1197True1198"""1199return [list(x) for x in self.inequality_generator()]12001201def ieqs(self):1202"""1203Deprecated. Alias for inequalities()12041205EXAMPLES::12061207sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4]))1208sage: p3.ieqs() == p3.inequalities()1209doctest:...: DeprecationWarning:1210This method is deprecated. Use inequalities() instead.1211See http://trac.sagemath.org/11763 for details.1212True1213"""1214from sage.misc.superseded import deprecation1215deprecation(11763, 'This method is deprecated. Use inequalities() instead.')1216return self.inequalities()12171218def equation_generator(self):1219"""1220Return a generator for the linear equations satisfied by the1221polyhedron.12221223EXAMPLES::12241225sage: p = polytopes.regular_polygon(8,base_ring=RDF)1226sage: p3 = Polyhedron(vertices = [x+[0] for x in p.vertices()], base_ring=RDF)1227sage: p3.equation_generator().next()1228An equation (0.0, 0.0, 1.0) x + 0.0 == 01229"""1230for H in self.Hrepresentation():1231if H.is_equation():1232yield H12331234@cached_method1235def equations(self):1236"""1237Return all linear constraints of the polyhedron.12381239OUTPUT:12401241A tuple of equations.12421243EXAMPLES::12441245sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])1246sage: test_p.equations()1247(An equation (1, 1, 1, 1) x - 10 == 0,)1248"""1249return tuple(self.equation_generator())12501251def equations_list(self):1252"""1253Return the linear constraints of the polyhedron. As with1254inequalities, each constraint is given as [b -a1 -a2 ... an]1255where for variables x1, x2,..., xn, the polyhedron satisfies1256the equation b = a1*x1 + a2*x2 + ... + an*xn.12571258.. NOTE::12591260It is recommended to use :meth:`equations` or1261:meth:`equation_generator()` instead to iterate over the1262list of1263:class:`~sage.geometry.polyhedron.representation.Equation`1264objects.12651266EXAMPLES::12671268sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])1269sage: test_p.equations_list()1270[[-10, 1, 1, 1, 1]]1271"""1272return [list(eq) for eq in self.equation_generator()]12731274def linearities(self):1275"""1276Deprecated. Use equations() instead.1277Returns the linear constraints of the polyhedron. As with1278inequalities, each constraint is given as [b -a1 -a2 ... an]1279where for variables x1, x2,..., xn, the polyhedron satisfies1280the equation b = a1*x1 + a2*x2 + ... + an*xn.12811282EXAMPLES::12831284sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]])1285sage: test_p.linearities()1286doctest:...: DeprecationWarning:1287This method is deprecated. Use equations_list() instead.1288See http://trac.sagemath.org/11763 for details.1289[[-10, 1, 1, 1, 1]]1290sage: test_p.linearities() == test_p.equations_list()1291True1292"""1293from sage.misc.superseded import deprecation1294deprecation(11763, 'This method is deprecated. Use equations_list() instead.')1295return self.equations_list()12961297def vertices_list(self):1298"""1299Return a list of vertices of the polyhedron.13001301.. NOTE::13021303It is recommended to use :meth:`vertex_generator` instead to1304iterate over the list of :class:`Vertex` objects.13051306EXAMPLES::13071308sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])1309sage: triangle.vertices_list()1310[[0, 1], [1, 0], [1, 1]]1311sage: a_simplex = Polyhedron(ieqs = [1312... [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]1313... ], eqns = [[1,-1,-1,-1,-1]])1314sage: a_simplex.vertices_list()1315[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]1316sage: a_simplex.vertices_list() == [list(v) for v in a_simplex.vertex_generator()]1317True1318"""1319return [list(x) for x in self.vertex_generator()]13201321def vertex_generator(self):1322"""1323Return a generator for the vertices of the polyhedron.13241325EXAMPLES::13261327sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])1328sage: for v in triangle.vertex_generator(): print(v)1329A vertex at (0, 1)1330A vertex at (1, 0)1331A vertex at (1, 1)1332sage: v_gen = triangle.vertex_generator()1333sage: v_gen.next() # the first vertex1334A vertex at (0, 1)1335sage: v_gen.next() # the second vertex1336A vertex at (1, 0)1337sage: v_gen.next() # the third vertex1338A vertex at (1, 1)1339sage: try: v_gen.next() # there are only three vertices1340... except StopIteration: print "STOP"1341STOP1342sage: type(v_gen)1343<type 'generator'>1344sage: [ v for v in triangle.vertex_generator() ]1345[A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)]1346"""1347for V in self.Vrepresentation():1348if V.is_vertex():1349yield V13501351@cached_method1352def vertices(self):1353"""1354Return all vertices of the polyhedron.13551356OUTPUT:13571358A tuple of vertices.13591360EXAMPLES::13611362sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])1363sage: triangle.vertices()1364(A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1))1365sage: a_simplex = Polyhedron(ieqs = [1366... [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]1367... ], eqns = [[1,-1,-1,-1,-1]])1368sage: a_simplex.vertices()1369(A vertex at (1, 0, 0, 0), A vertex at (0, 1, 0, 0),1370A vertex at (0, 0, 1, 0), A vertex at (0, 0, 0, 1))1371"""1372return tuple(self.vertex_generator())13731374@cached_method1375def vertices_matrix(self, base_ring=None):1376"""1377Return the coordinates of the vertices as the columns of a matrix.13781379INPUT:13801381- ``base_ring`` -- A ring or ``None`` (default). The base ring1382of the returned matrix. If not specified, the base ring of1383the polyhedron is used.13841385OUTPUT:13861387A matrix over ``base_ring`` whose columns are the coordinates1388of the vertices. A ``TypeError`` is raised if the coordinates1389cannot be converted to ``base_ring``.13901391EXAMPLES::13921393sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]])1394sage: triangle.vertices_matrix()1395[0 1 1]1396[1 0 1]1397sage: (triangle/2).vertices_matrix()1398[ 0 1/2 1/2]1399[1/2 0 1/2]1400sage: (triangle/2).vertices_matrix(ZZ)1401Traceback (most recent call last):1402...1403TypeError: no conversion of this rational to integer1404"""1405if base_ring is None:1406base_ring = self.base_ring()1407m = matrix(base_ring, self.ambient_dim(), self.n_vertices())1408for i,v in enumerate(self.vertices()):1409for j in range(0,self.ambient_dim()):1410m[j,i] = v[j]1411return m14121413def ray_generator(self):1414"""1415Return a generator for the rays of the polyhedron.14161417EXAMPLES::14181419sage: pi = Polyhedron(ieqs = [[1,1,0],[1,0,1]])1420sage: pir = pi.ray_generator()1421sage: [x.vector() for x in pir]1422[(1, 0), (0, 1)]1423"""1424for V in self.Vrepresentation():1425if V.is_ray():1426yield V14271428@cached_method1429def rays(self):1430"""1431Return a list of rays of the polyhedron.14321433OUTPUT:14341435A tuple of rays.14361437EXAMPLES::14381439sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]])1440sage: p.rays()1441(A ray in the direction (1, 0, 0),1442A ray in the direction (0, 1, 0),1443A ray in the direction (0, 0, 1))1444"""1445return tuple(self.ray_generator())14461447def rays_list(self):1448"""1449Return a list of rays as coefficient lists.14501451.. NOTE::14521453It is recommended to use :meth:`rays` or1454:meth:`ray_generator` instead to iterate over the list of1455:class:`Ray` objects.14561457OUTPUT:14581459A list of rays as lists of coordinates.14601461EXAMPLES::14621463sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]])1464sage: p.rays_list()1465[[1, 0, 0], [0, 1, 0], [0, 0, 1]]1466sage: p.rays_list() == [list(r) for r in p.ray_generator()]1467True1468"""1469return [list(x) for x in self.ray_generator()]14701471def line_generator(self):1472"""1473Return a generator for the lines of the polyhedron.14741475EXAMPLES::14761477sage: pr = Polyhedron(rays = [[1,0],[-1,0],[0,1]], vertices = [[-1,-1]])1478sage: pr.line_generator().next().vector()1479(1, 0)1480"""1481for V in self.Vrepresentation():1482if V.is_line():1483yield V14841485@cached_method1486def lines(self):1487"""1488Return all lines of the polyhedron.14891490OUTPUT:14911492A tuple of lines.14931494EXAMPLES::14951496sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]])1497sage: p.lines()1498(A line in the direction (1, 0),)1499"""1500return tuple(self.line_generator())15011502def lines_list(self):1503"""1504Return a list of lines of the polyhedron. The line data is given1505as a list of coordinates rather than as a Hrepresentation object.15061507.. NOTE::15081509It is recommended to use :meth:`line_generator` instead to1510iterate over the list of :class:`Line` objects.15111512EXAMPLES::15131514sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]])1515sage: p.lines_list()1516[[1, 0]]1517sage: p.lines_list() == [list(x) for x in p.line_generator()]1518True1519"""1520return [list(x) for x in self.line_generator()]15211522def bounded_edges(self):1523"""1524Return the bounded edges (excluding rays and lines).15251526OUTPUT:15271528A generator for pairs of vertices, one pair per edge.15291530EXAMPLES::15311532sage: p = Polyhedron(vertices=[[1,0],[0,1]], rays=[[1,0],[0,1]])1533sage: [ e for e in p.bounded_edges() ]1534[(A vertex at (0, 1), A vertex at (1, 0))]1535sage: for e in p.bounded_edges(): print e1536(A vertex at (0, 1), A vertex at (1, 0))1537"""1538obj = self.Vrepresentation()1539for i in range(len(obj)):1540if not obj[i].is_vertex(): continue1541for j in range(i+1,len(obj)):1542if not obj[j].is_vertex(): continue1543if self.vertex_adjacency_matrix()[i,j] == 0: continue1544yield (obj[i], obj[j])15451546def Vrepresentation_space(self):1547r"""1548Return the ambient vector space.15491550OUTPUT:15511552A free module over the base ring of dimension :meth:`ambient_dim`.15531554EXAMPLES::15551556sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])1557sage: poly_test.Vrepresentation_space()1558Ambient free module of rank 4 over the principal ideal domain Integer Ring1559sage: poly_test.ambient_space() is poly_test.Vrepresentation_space()1560True1561"""1562return self.parent().Vrepresentation_space()15631564ambient_space = Vrepresentation_space15651566def Hrepresentation_space(self):1567r"""1568Return the linear space containing the H-representation vectors.15691570OUTPUT:15711572A free module over the base ring of dimension :meth:`ambient_dim` + 1.15731574EXAMPLES::15751576sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])1577sage: poly_test.Hrepresentation_space()1578Ambient free module of rank 5 over the principal ideal domain Integer Ring1579"""1580return self.parent().Hrepresentation_space()15811582def ambient_dim(self):1583r"""1584Return the dimension of the ambient space.15851586EXAMPLES::15871588sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]])1589sage: poly_test.ambient_dim()159041591"""1592return self.parent().ambient_dim()15931594def dim(self):1595"""1596Return the dimension of the polyhedron.15971598OUTPUT:15991600-1 if the polyhedron is empty, otherwise a non-negative integer.16011602EXAMPLES::16031604sage: simplex = Polyhedron(vertices = [[1,0,0,0],[0,0,0,1],[0,1,0,0],[0,0,1,0]])1605sage: simplex.dim()160631607sage: simplex.ambient_dim()1608416091610The empty set is a special case (Trac #12193)::16111612sage: P1=Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]])1613sage: P2=Polyhedron(vertices=[[2,0,0],[0,2,0],[0,0,2]])1614sage: P12 = P1.intersection(P2)1615sage: P121616The empty polyhedron in ZZ^31617sage: P12.dim()1618-11619"""1620if self.n_Vrepresentation() == 0:1621return -1 # the empty set1622else:1623return self.ambient_dim() - self.n_equations()16241625def is_empty(self):1626"""1627Test whether the polyhedron is the empty polyhedron16281629OUTPUT:16301631Boolean.16321633EXAMPLES::16341635sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P1636A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices1637sage: P.is_empty(), P.is_universe()1638(False, False)16391640sage: Q = Polyhedron(vertices=()); Q1641The empty polyhedron in ZZ^01642sage: Q.is_empty(), Q.is_universe()1643(True, False)16441645sage: R = Polyhedron(lines=[(1,0),(0,1)]); R1646A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines1647sage: R.is_empty(), R.is_universe()1648(False, True)1649"""1650return self.n_Vrepresentation() == 016511652def is_universe(self):1653"""1654Test whether the polyhedron is the whole ambient space16551656OUTPUT:16571658Boolean.16591660EXAMPLES::16611662sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P1663A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices1664sage: P.is_empty(), P.is_universe()1665(False, False)16661667sage: Q = Polyhedron(vertices=()); Q1668The empty polyhedron in ZZ^01669sage: Q.is_empty(), Q.is_universe()1670(True, False)16711672sage: R = Polyhedron(lines=[(1,0),(0,1)]); R1673A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines1674sage: R.is_empty(), R.is_universe()1675(False, True)1676"""1677return self.n_Hrepresentation() == 016781679@cached_method1680def vertex_adjacency_matrix(self):1681"""1682Return the binary matrix of vertex adjacencies.16831684EXAMPLES::16851686sage: polytopes.n_simplex(4).vertex_adjacency_matrix()1687[0 1 1 1 1]1688[1 0 1 1 1]1689[1 1 0 1 1]1690[1 1 1 0 1]1691[1 1 1 1 0]16921693The rows and columns of the vertex adjacency matrix correspond1694to the :meth:`Vrepresentation` objects: vertices, rays, and1695lines. The `(i,j)` matrix entry equals `1` if the `i`-th and1696`j`-th V-representation object are adjacent.16971698Two vertices are adjacent if they are the endpoints of an1699edge, that is, a one-dimensional face. For unbounded polyhedra1700this clearly needs to be generalized and we define two1701V-representation objects (see1702:mod:`sage.geometry.polyhedron.constructor`) to be adjacent if1703they together generate a one-face. There are three possible1704combinations:17051706* Two vertices can bound a finite-length edge.17071708* A vertex and a ray can generate a half-infinite edge1709starting at the vertex and with the direction given by the1710ray.17111712* A vertex and a line can generate an infinite edge. The1713position of the vertex on the line is arbitrary in this1714case, only its transverse position matters. The direction of1715the edge is given by the line generator.17161717For example, take the half-plane::17181719sage: half_plane = Polyhedron(ieqs=[(0,1,0)])1720sage: half_plane.Hrepresentation()1721(An inequality (1, 0) x + 0 >= 0,)17221723Its (non-unique) V-representation consists of a vertex, a ray,1724and a line. The only edge is spanned by the vertex and the1725line generator, so they are adjacent::17261727sage: half_plane.Vrepresentation()1728(A line in the direction (0, 1), A ray in the direction (1, 0), A vertex at (0, 0))1729sage: half_plane.vertex_adjacency_matrix()1730[0 0 1]1731[0 0 0]1732[1 0 0]17331734In one dimension higher, that is for a half-space in 31735dimensions, there is no one-dimensional face. Hence nothing is1736adjacent::17371738sage: Polyhedron(ieqs=[(0,1,0,0)]).vertex_adjacency_matrix()1739[0 0 0 0]1740[0 0 0 0]1741[0 0 0 0]1742[0 0 0 0]17431744EXAMPLES:17451746In a bounded polygon, every vertex has precisely two adjacent ones::17471748sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)])1749sage: for v in P.Vrep_generator():1750... print P.adjacency_matrix().row(v.index()), v1751(0, 1, 0, 1) A vertex at (0, 1)1752(1, 0, 1, 0) A vertex at (1, 0)1753(0, 1, 0, 1) A vertex at (3, 0)1754(1, 0, 1, 0) A vertex at (4, 1)17551756If the V-representation of the polygon contains vertices and1757one ray, then each V-representation object is adjacent to two1758V-representation objects::17591760sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)],1761... rays=[(0,1)])1762sage: for v in P.Vrep_generator():1763... print P.adjacency_matrix().row(v.index()), v1764(0, 1, 0, 0, 1) A ray in the direction (0, 1)1765(1, 0, 1, 0, 0) A vertex at (0, 1)1766(0, 1, 0, 1, 0) A vertex at (1, 0)1767(0, 0, 1, 0, 1) A vertex at (3, 0)1768(1, 0, 0, 1, 0) A vertex at (4, 1)17691770If the V-representation of the polygon contains vertices and1771two distinct rays, then each vertex is adjacent to two1772V-representation objects (which can now be vertices or1773rays). The two rays are not adjacent to each other::17741775sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (3, 0), (4, 1)],1776... rays=[(0,1), (1,1)])1777sage: for v in P.Vrep_generator():1778... print P.adjacency_matrix().row(v.index()), v1779(0, 1, 0, 0, 0) A ray in the direction (0, 1)1780(1, 0, 1, 0, 0) A vertex at (0, 1)1781(0, 1, 0, 0, 1) A vertex at (1, 0)1782(0, 0, 0, 0, 1) A ray in the direction (1, 1)1783(0, 0, 1, 1, 0) A vertex at (3, 0)1784"""1785return self._vertex_adjacency_matrix()17861787adjacency_matrix = vertex_adjacency_matrix17881789@cached_method1790def facet_adjacency_matrix(self):1791"""1792Return the adjacency matrix for the facets and hyperplanes.17931794EXAMPLES::17951796sage: polytopes.n_simplex(4).facet_adjacency_matrix()1797[0 1 1 1 1]1798[1 0 1 1 1]1799[1 1 0 1 1]1800[1 1 1 0 1]1801[1 1 1 1 0]1802"""1803return self._facet_adjacency_matrix()18041805@cached_method1806def incidence_matrix(self):1807"""1808Return the incidence matrix.18091810.. NOTE::18111812The columns correspond to inequalities/equations in the1813order :meth:`Hrepresentation`, the rows correspond to1814vertices/rays/lines in the order1815:meth:`Vrepresentation`18161817EXAMPLES::18181819sage: p = polytopes.cuboctahedron()1820sage: p.incidence_matrix()1821[0 0 1 1 0 1 0 0 0 0 1 0 0 0]1822[0 0 0 1 0 0 1 0 1 0 1 0 0 0]1823[0 0 1 1 1 0 0 1 0 0 0 0 0 0]1824[1 0 0 1 1 0 1 0 0 0 0 0 0 0]1825[0 0 0 0 0 1 0 0 1 1 1 0 0 0]1826[0 0 1 0 0 1 0 1 0 0 0 1 0 0]1827[1 0 0 0 0 0 1 0 1 0 0 0 1 0]1828[1 0 0 0 1 0 0 1 0 0 0 0 0 1]1829[0 1 0 0 0 1 0 0 0 1 0 1 0 0]1830[0 1 0 0 0 0 0 0 1 1 0 0 1 0]1831[0 1 0 0 0 0 0 1 0 0 0 1 0 1]1832[1 1 0 0 0 0 0 0 0 0 0 0 1 1]1833sage: v = p.Vrepresentation(0)1834sage: v1835A vertex at (-1/2, -1/2, 0)1836sage: h = p.Hrepresentation(2)1837sage: h1838An inequality (1, 1, -1) x + 1 >= 01839sage: h.eval(v) # evaluation (1, 1, -1) * (-1/2, -1/2, 0) + 1184001841sage: h*v # same as h.eval(v)184201843sage: p.incidence_matrix() [0,2] # this entry is (v,h)184411845sage: h.contains(v)1846True1847sage: p.incidence_matrix() [2,0] # note: not symmetric184801849"""1850incidence_matrix = matrix(ZZ, self.n_Vrepresentation(),1851self.n_Hrepresentation(), 0)1852for V in self.Vrep_generator():1853for H in self.Hrep_generator():1854if self._is_zero(H*V):1855incidence_matrix[V.index(),H.index()] = 11856return incidence_matrix18571858def base_ring(self):1859"""1860Return the base ring.18611862OUTPUT:18631864Either ``QQ`` (exact arithmetic using gmp, default) or ``RDF``1865(double precision floating-point arithmetic)18661867EXAMPLES::18681869sage: triangle = Polyhedron(vertices = [[1,0],[0,1],[1,1]])1870sage: triangle.base_ring() == ZZ1871True1872"""1873return self.parent().base_ring()18741875field = base_ring18761877@cached_method1878def center(self):1879"""1880Return the average of the vertices.18811882See also :meth:`interior_point`.18831884OUTPUT:18851886The center of the polyhedron. All rays and lines are1887ignored. Raises a ``ZeroDivisionError`` for the empty1888polytope.18891890EXAMPLES::18911892sage: p = polytopes.n_cube(3)1893sage: p = p + vector([1,0,0])1894sage: p.center()1895(1, 0, 0)1896"""1897vertex_sum = vector(self.base_ring(), [0]*self.ambient_dim())1898for v in self.vertex_generator():1899vertex_sum += v.vector()1900vertex_sum.set_immutable()1901return vertex_sum / self.n_vertices()19021903@cached_method1904def representative_point(self):1905"""1906Return a "generic" point.19071908See also :meth:`center`.19091910OUTPUT:19111912A point as a coordinate vector. The point is chosen to be1913interior as far as possible. If the polyhedron is not1914full-dimensional, the point is in the relative interior. If1915the polyhedron is zero-dimensional, its single point is1916returned.19171918EXAMPLES::19191920sage: p = Polyhedron(vertices=[(3,2)], rays=[(1,-1)])1921sage: p.representative_point()1922(4, 1)1923sage: p.center()1924(3, 2)19251926sage: Polyhedron(vertices=[(3,2)]).representative_point()1927(3, 2)1928"""1929accumulator = vector(self.base_ring(), [0]*self.ambient_dim())1930for v in self.vertex_generator():1931accumulator += v.vector()1932accumulator /= self.n_vertices()1933for r in self.ray_generator():1934accumulator += r.vector()1935accumulator.set_immutable()1936return accumulator193719381939@cached_method1940def radius_square(self):1941"""1942Return the square of the maximal distance from the1943:meth:`center` to a vertex. All rays and lines are ignored.19441945OUTPUT:19461947The square of the radius, which is in :meth:`field`.19481949EXAMPLES::19501951sage: p = polytopes.permutahedron(4, project = False)1952sage: p.radius_square()195351954"""1955vertices = [ v.vector() - self.center() for v in self.vertex_generator() ]1956return max( v.dot_product(v) for v in vertices )195719581959def radius(self):1960"""1961Return the maximal distance from the center to a vertex. All1962rays and lines are ignored.19631964OUTPUT:19651966The radius for a rational polyhedron is, in general, not1967rational. use :meth:`radius_square` if you need a rational1968distance measure.19691970EXAMPLES::19711972sage: p = polytopes.n_cube(4)1973sage: p.radius()197421975"""1976return sqrt(self.radius_square())19771978def is_compact(self):1979"""1980Test for boundedness of the polytope.19811982EXAMPLES::19831984sage: p = polytopes.icosahedron()1985sage: p.is_compact()1986True1987sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0],[0,0,0,1],[1,-1,0,0]])1988sage: p.is_compact()1989False1990"""1991return self.n_rays()==0 and self.n_lines()==019921993def is_simple(self):1994"""1995Test for simplicity of a polytope.19961997See :wikipedia:`Simple_polytope`19981999EXAMPLES::20002001sage: p = Polyhedron([[0,0,0],[1,0,0],[0,1,0],[0,0,1]])2002sage: p.is_simple()2003True2004sage: p = Polyhedron([[0,0,0],[4,4,0],[4,0,0],[0,4,0],[2,2,2]])2005sage: p.is_simple()2006False20072008"""2009if not self.is_compact(): return False20102011for v in self.vertex_generator():2012adj = [a for a in v.neighbors()]2013if len(adj) != self.dim():2014return False2015return True20162017def is_simplicial(self):2018"""2019Tests if the polytope is simplicial20202021A polytope is simplicial if every facet is a simplex.20222023See :wikipedia:`Simplicial_polytope`20242025EXAMPLES::20262027sage: p = polytopes.n_cube(3)2028sage: p.is_simplicial()2029False2030sage: q = polytopes.n_simplex(5)2031sage: q.is_simplicial()2032True2033sage: p = Polyhedron([[0,0,0],[1,0,0],[0,1,0],[0,0,1]])2034sage: p.is_simplicial()2035True2036sage: q = Polyhedron([[1,1,1],[-1,1,1],[1,-1,1],[-1,-1,1],[1,1,-1]])2037sage: q.is_simplicial()2038False20392040The method is not implemented for unbounded polyhedra::20412042sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)])2043sage: p.is_simplicial()2044Traceback (most recent call last):2045...2046NotImplementedError: This function is implemented for polytopes only.2047"""2048if not(self.is_compact()):2049raise NotImplementedError("This function is implemented for polytopes only.")2050d = self.dim()2051return all(len([vertex for vertex in face.incident()]) == d2052for face in self.Hrepresentation())20532054def hyperplane_arrangement(self):2055"""2056Return the hyperplane arrangement defined by the equations and2057inequalities.20582059OUTPUT:20602061A :class:`hyperplane arrangement2062<sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement>`2063consisting of the hyperplanes defined by the2064:meth:`~sage.geometric.hyperplane_arragement.arrangement.HyperplaneArrangementElement.Hrepresentation`.2065If the polytope is full-dimensional, this is the hyperplane2066arrangement spanned by the facets of the polyhedron.20672068EXAMPLES::20692070sage: p = polytopes.n_cube(2)2071sage: p.hyperplane_arrangement()2072Arrangement <-t0 + 1 | -t1 + 1 | t1 + 1 | t0 + 1>2073"""2074names = tuple('t'+str(i) for i in range(self.ambient_dim()))2075from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements2076field = self.base_ring().fraction_field()2077H = HyperplaneArrangements(field, names)2078return H(self)20792080@cached_method2081def gale_transform(self):2082"""2083Return the Gale transform of a polytope as described in the2084reference below.20852086OUTPUT:20872088A list of vectors, the Gale transform. The dimension is the2089dimension of the affine dependencies of the vertices of the2090polytope.20912092EXAMPLES:20932094This is from the reference, for a triangular prism::20952096sage: p = Polyhedron(vertices = [[0,0],[0,1],[1,0]])2097sage: p2 = p.prism()2098sage: p2.gale_transform()2099[(1, 0), (0, 1), (-1, -1), (-1, 0), (0, -1), (1, 1)]21002101REFERENCES:21022103Lectures in Geometric Combinatorics, R.R.Thomas, 2006, AMS Press.2104"""2105if not self.is_compact(): raise ValueError('Not a polytope.')21062107A = matrix(self.n_vertices(),2108[ [1]+x for x in self.vertex_generator()])2109A = A.transpose()2110A_ker = A.right_kernel()2111return A_ker.basis_matrix().transpose().rows()21122113def triangulate(self, engine='auto', connected=True, fine=False, regular=None, star=None):2114r"""2115Returns a triangulation of the polytope.21162117INPUT:21182119- ``engine`` -- either 'auto' (default), 'internal', or2120'TOPCOM'. The latter two instruct this package to always2121use its own triangulation algorithms or TOPCOM's algorithms,2122respectively. By default ('auto'), TOPCOM is used if it is2123available and internal routines otherwise.21242125The remaining keyword parameters are passed through to the2126:class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`2127constructor:21282129- ``connected`` -- boolean (default: ``True``). Whether the2130triangulations should be connected to the regular2131triangulations via bistellar flips. These are much easier to2132compute than all triangulations.21332134- ``fine`` -- boolean (default: ``False``). Whether the2135triangulations must be fine, that is, make use of all points2136of the configuration.21372138- ``regular`` -- boolean or ``None`` (default:2139``None``). Whether the triangulations must be regular. A2140regular triangulation is one that is induced by a2141piecewise-linear convex support function. In other words,2142the shadows of the faces of a polyhedron in one higher2143dimension.21442145* ``True``: Only regular triangulations.21462147* ``False``: Only non-regular triangulations.21482149* ``None`` (default): Both kinds of triangulation.21502151- ``star`` -- either ``None`` (default) or a point. Whether2152the triangulations must be star. A triangulation is star if2153all maximal simplices contain a common point. The central2154point can be specified by its index (an integer) in the2155given points or by its coordinates (anything iterable.)21562157OUTPUT:21582159A triangulation of the convex hull of the vertices as a2160:class:`~sage.geometry.triangulation.point_configuration.Triangulation`. The2161indices in the triangulation correspond to the2162:meth:`Vrepresentation` objects.21632164EXAMPLES::21652166sage: cube = polytopes.n_cube(3)2167sage: triangulation = cube.triangulate(2168... engine='internal') # to make doctest independent of TOPCOM2169sage: triangulation2170(<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>)2171sage: simplex_indices = triangulation[0]; simplex_indices2172(0, 1, 2, 7)2173sage: simplex_vertices = [ cube.Vrepresentation(i) for i in simplex_indices ]2174sage: simplex_vertices2175[A vertex at (-1, -1, -1), A vertex at (-1, -1, 1),2176A vertex at (-1, 1, -1), A vertex at (1, 1, 1)]2177sage: Polyhedron(simplex_vertices)2178A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices2179"""2180if not self.is_compact():2181raise NotImplementedError('I can only triangulate compact polytopes.')2182from sage.geometry.triangulation.point_configuration import PointConfiguration2183pc = PointConfiguration((v.vector() for v in self.vertex_generator()),2184connected=connected, fine=fine, regular=regular, star=star)2185pc.set_engine(engine)2186return pc.triangulate()21872188def triangulated_facial_incidences(self):2189"""2190Return a list of the form [face_index, [v_i_0,2191v_i_1,...,v_i_{n-1}]] where the face_index refers to the2192original defining inequality. For a given face, the2193collection of triangles formed by each list of v_i should2194triangulate that face.21952196In dimensions greater than 3, this is computed by randomly2197lifting each face up a dimension; this does not always work!2198This should eventually be fixed by using lrs or another2199program that computes triangulations.22002201EXAMPLES:22022203If the figure is already composed of triangles, then all is well::22042205sage: Polyhedron(vertices = [[5,0,0],[0,5,0],[5,5,0],[2,2,5]]2206... ).triangulated_facial_incidences()2207doctest:...: DeprecationWarning:2208This method is deprecated. Use triangulate() instead.2209See http://trac.sagemath.org/11634 for details.2210doctest:...: DeprecationWarning:2211This method is deprecated. Use self.Hrepresentation(i).incident() instead.2212See http://trac.sagemath.org/11763 for details.2213[[0, [0, 1, 2]], [1, [0, 1, 3]], [2, [0, 2, 3]], [3, [1, 2, 3]]]22142215Otherwise some faces get split up to triangles::22162217sage: Polyhedron(vertices = [[2,0,0],[4,1,0],[0,5,0],[5,5,0],2218... [1,1,0],[0,0,1]]).triangulated_facial_incidences()2219doctest:...: DeprecationWarning:2220This method is deprecated. Use triangulate() instead.2221See http://trac.sagemath.org/11634 for details.2222doctest:...: DeprecationWarning:2223This method is deprecated. Use self.Vrepresentation(i).neighbors() instead.2224See http://trac.sagemath.org/11763 for details.2225[[0, [1, 2, 5]], [0, [2, 5, 3]], [0, [5, 3, 4]], [1, [0, 1, 2]],2226[2, [0, 2, 3]], [3, [0, 3, 4]], [4, [0, 4, 5]], [5, [0, 1, 5]]]2227"""2228from sage.misc.superseded import deprecation2229deprecation(11634, 'This method is deprecated. Use triangulate() instead.')2230try:2231return self._triangulated_facial_incidences2232except AttributeError:2233t_fac_incs = []2234for a_face in self.facial_incidences():2235vert_number = len(a_face[1])2236if vert_number == self.dim():2237t_fac_incs.append(a_face)2238elif self.dim() >= 4:2239lifted_verts = []2240for vert_index in a_face[1]:2241lifted_verts.append(self.vertices()[vert_index] +2242[randint(-vert_index,5000+vert_index + vert_number**2)])2243temp_poly = Polyhedron(vertices = lifted_verts)2244for t_face in temp_poly.facial_incidences():2245if len(t_face[1]) != self.dim():2246print 'Failed for face: ' + str(a_face)2247print 'Attempted simplicial face: ' + str(t_face)2248print 'Attempted lifted vertices: ' + str(lifted_verts)2249raise RuntimeError, "triangulation failed"2250normal_fdir = temp_poly.ieqs()[t_face[0]][-1]2251if normal_fdir >= 0:2252t_fac_verts = [temp_poly.vertices()[i] for i in t_face[1]]2253proj_verts = [q[0:self.dim()] for q in t_fac_verts]2254t_fac_incs.append([a_face[0],2255[self.vertices().index(q) for q in proj_verts]])2256else:2257vs = a_face[1][:]2258adj = dict([a[0], filter(lambda p: p in a_face[1], a[1])]2259for a in filter(lambda va: va[0] in a_face[1],2260self.vertex_adjacencies()))2261t = vs[0]2262vs.remove(t)2263ts = adj[t]2264for v in ts:2265vs.remove(v)2266t_fac_incs.append([a_face[0], [t] + ts])2267while vs:2268t = ts[0]2269ts = ts[1:]2270for v in adj[t]:2271if v in vs:2272vs.remove(v)2273ts.append(v)2274t_fac_incs.append([a_face[0], [t] + ts])2275break2276self._triangulated_facial_incidences = t_fac_incs2277return t_fac_incs22782279def simplicial_complex(self):2280"""2281Return a simplicial complex from a triangulation of the polytope.22822283Warning: This first triangulates the polytope using2284``triangulated_facial_incidences``, and this function may fail2285in dimensions greater than 3, although it usually doesn't.22862287OUTPUT:22882289A simplicial complex.22902291EXAMPLES::22922293sage: p = polytopes.cuboctahedron()2294sage: sc = p.simplicial_complex()2295doctest:...: DeprecationWarning:2296This method is deprecated. Use triangulate().simplicial_complex() instead.2297See http://trac.sagemath.org/11634 for details.2298doctest:...: DeprecationWarning:2299This method is deprecated. Use triangulate() instead.2300See http://trac.sagemath.org/11634 for details.2301sage: sc2302Simplicial complex with 12 vertices and 20 facets2303"""2304from sage.misc.superseded import deprecation2305deprecation(11634, 'This method is deprecated. Use triangulate().simplicial_complex() instead.')2306from sage.homology.simplicial_complex import SimplicialComplex2307return SimplicialComplex([x[1] for x in self.triangulated_facial_incidences()])23082309@coerce_binop2310def Minkowski_sum(self, other):2311"""2312Return the Minkowski sum.23132314Minkowski addition of two subsets of a vector space is defined2315as23162317.. math::23182319X \oplus Y =2320\cup_{y\in Y} (X+y) =2321\cup_{x\in X, y\in Y} (x+y)23222323See :meth:`Minkowski_difference` for a partial inverse operation.23242325INPUT:23262327- ``other`` -- a :class:`Polyhedron_base`.23282329OUTPUT:23302331The Minkowski sum of ``self`` and ``other``.23322333EXAMPLES::23342335sage: X = polytopes.n_cube(3)2336sage: Y = Polyhedron(vertices=[(0,0,0), (0,0,1/2), (0,1/2,0), (1/2,0,0)])2337sage: X+Y2338A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 13 vertices23392340sage: four_cube = polytopes.n_cube(4)2341sage: four_simplex = Polyhedron(vertices = [[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])2342sage: four_cube + four_simplex2343A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 36 vertices2344sage: four_cube.Minkowski_sum(four_simplex) == four_cube + four_simplex2345True23462347sage: poly_spam = Polyhedron([[3,4,5,2],[1,0,0,1],[0,0,0,0],[0,4,3,2],[-3,-3,-3,-3]], base_ring=ZZ)2348sage: poly_eggs = Polyhedron([[5,4,5,4],[-4,5,-4,5],[4,-5,4,-5],[0,0,0,0]], base_ring=QQ)2349sage: poly_spam + poly_spam + poly_eggs2350A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 12 vertices2351"""2352new_vertices = []2353for v1 in self.vertex_generator():2354for v2 in other.vertex_generator():2355new_vertices.append(list(v1() + v2()))2356if new_vertices != []:2357new_rays = self.rays() + other.rays()2358new_lines = self.lines() + other.lines()2359return self.parent().element_class(self.parent(), [new_vertices, new_rays, new_lines], None)2360else:2361return self.parent().element_class(self.parent(), None, None)23622363_add_ = Minkowski_sum23642365@coerce_binop2366def Minkowski_difference(self, other):2367"""2368Return the Minkowski difference.23692370Minkowski subtraction can equivalently be defined via2371Minkowski addition (see :meth:`Minkowski_sum`) or as2372set-theoretic intersection via23732374.. math::23752376X \ominus Y =2377(X^c \oplus Y)^c =2378\cap_{y\in Y} (X-y)23792380where superscript-"c" means the complement in the ambient2381vector space. The Minkowski difference of convex sets is2382convex, and the difference of polyhedra is again a2383polyhedron. We only consider the case of polyhedra in the2384following. Note that it is not quite the inverse of2385addition. In fact:23862387* `(X+Y)-Y = X` for any polyhedra `X`, `Y`.23882389* `(X-Y)+Y \subseteq X`23902391* `(X-Y)+Y = X` if and only if Y is a Minkowski summand of X.23922393INPUT:23942395- ``other`` -- a :class:`Polyhedron_base`.23962397OUTPUT:23982399The Minkowski difference of ``self`` and ``other``. Also known2400as Minkowski subtraction of ``other`` from ``self``.24012402EXAMPLES::24032404sage: X = polytopes.n_cube(3)2405sage: Y = Polyhedron(vertices=[(0,0,0), (0,0,1), (0,1,0), (1,0,0)]) / 22406sage: (X+Y)-Y == X2407True2408sage: (X-Y)+Y < X2409True24102411The polyhedra need not be full-dimensional::24122413sage: X2 = Polyhedron(vertices=[(-1,-1,0),(1,-1,0),(-1,1,0),(1,1,0)])2414sage: Y2 = Polyhedron(vertices=[(0,0,0), (0,1,0), (1,0,0)]) / 22415sage: (X2+Y2)-Y2 == X22416True2417sage: (X2-Y2)+Y2 < X22418True24192420Minus sign is really an alias for :meth:`Minkowski_difference`2421::24222423sage: four_cube = polytopes.n_cube(4)2424sage: four_simplex = Polyhedron(vertices = [[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])2425sage: four_cube - four_simplex2426A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices2427sage: four_cube.Minkowski_difference(four_simplex) == four_cube - four_simplex2428True24292430Coercion of the base ring works::24312432sage: poly_spam = Polyhedron([[3,4,5,2],[1,0,0,1],[0,0,0,0],[0,4,3,2],[-3,-3,-3,-3]], base_ring=ZZ)2433sage: poly_eggs = Polyhedron([[5,4,5,4],[-4,5,-4,5],[4,-5,4,-5],[0,0,0,0]], base_ring=QQ) / 1002434sage: poly_spam - poly_eggs2435A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices24362437TESTS::24382439sage: X = polytopes.n_cube(2)2440sage: Y = Polyhedron(vertices=[(1,1)])2441sage: (X-Y).Vrepresentation()2442(A vertex at (0, -2), A vertex at (0, 0), A vertex at (-2, 0), A vertex at (-2, -2))24432444sage: Y = Polyhedron(vertices=[(1,1), (0,0)])2445sage: (X-Y).Vrepresentation()2446(A vertex at (0, -1), A vertex at (0, 0), A vertex at (-1, 0), A vertex at (-1, -1))24472448sage: X = X + Y # now Y is a Minkowski summand of X2449sage: (X+Y)-Y == X2450True2451sage: (X-Y)+Y == X2452True2453"""2454if other.is_empty():2455return self.parent().universe() # empty intersection = everything2456if not other.is_compact():2457raise NotImplementedError('only subtracting compact polyhedra is implemented')2458new_eqns = []2459for eq in self.equations():2460values = [ eq.A() * v.vector() for v in other.vertices() ]2461eq = list(eq)2462eq[0] += min(values) # shift constant term2463new_eqns.append(eq)2464P = self.parent()2465new_ieqs = []2466for ieq in self.inequalities():2467values = [ ieq.A() * v.vector() for v in other.vertices() ]2468ieq = list(ieq)2469ieq[0] += min(values) # shift constant term2470new_ieqs.append(ieq)2471P = self.parent()2472return P.element_class(P, None, [new_ieqs, new_eqns])24732474def __sub__(self, other):2475"""2476Implement minus binary operation24772478Polyhedra are not a ring with respect to dilatation and2479Minkowski sum, for example `X\oplus(-1)*Y \not= X\ominus Y`.24802481INPUT:24822483- ``other`` -- a translation vector or a polyhedron.24842485OUTPUT:24862487Either translation by the negative of the given vector or2488Minkowski subtraction by the given polyhedron.24892490EXAMPLES::24912492sage: X = polytopes.n_cube(2)2493sage: v = vector([1,1])2494sage: (X - v/2).Vrepresentation()2495(A vertex at (-3/2, -3/2), A vertex at (-3/2, 1/2),2496A vertex at (1/2, -3/2), A vertex at (1/2, 1/2))2497sage: (X-v)+v == X2498True24992500sage: Y = Polyhedron(vertices=[(1/2,0),(0,1/2)])2501sage: (X-Y).Vrepresentation()2502(A vertex at (1/2, -1), A vertex at (1/2, 1/2),2503A vertex at (-1, 1/2), A vertex at (-1, -1))2504sage: (X+Y)-Y == X2505True2506"""2507if is_Polyhedron(other):2508return self.Minkowski_difference(other)2509return self + (-other)25102511def is_Minkowski_summand(self, Y):2512"""2513Test whether ``Y`` is a Minkowski summand.25142515See :meth:`Minkowski_sum`.25162517OUTPUT:25182519Boolean. Whether there exists another polyhedron `Z` such that2520``self`` can be written as `Y\oplus Z`.25212522EXAMPLES::25232524sage: A = polytopes.n_cube(2)2525sage: B = Polyhedron(vertices=[(0,1), (1/2,1)])2526sage: C = Polyhedron(vertices=[(1,1)])2527sage: A.is_Minkowski_summand(B)2528True2529sage: A.is_Minkowski_summand(C)2530True2531sage: B.is_Minkowski_summand(C)2532True2533sage: B.is_Minkowski_summand(A)2534False2535sage: C.is_Minkowski_summand(A)2536False2537sage: C.is_Minkowski_summand(B)2538False2539"""2540return self.Minkowski_difference(Y).Minkowski_sum(Y) == self25412542def translation(self, displacement):2543"""2544Return the translated polyhedron.25452546INPUT:25472548- ``displacement`` -- a displacement vector or a list/tuple of2549coordinates that determines a displacement vector.25502551OUTPUT:25522553The translated polyhedron.25542555EXAMPLES::25562557sage: P = Polyhedron([[0,0],[1,0],[0,1]], base_ring=ZZ)2558sage: P.translation([2,1])2559A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices2560sage: P.translation( vector(QQ,[2,1]) )2561A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices2562"""2563displacement = vector(displacement)2564new_vertices = [x.vector()+displacement for x in self.vertex_generator()]2565new_rays = self.rays()2566new_lines = self.lines()2567new_ring = self.parent()._coerce_base_ring(displacement)2568return Polyhedron(vertices=new_vertices, rays=new_rays, lines=new_lines, base_ring=new_ring)25692570@coerce_binop2571def product(self, other):2572"""2573Return the cartesian product.25742575INPUT:25762577- ``other`` -- a :class:`Polyhedron_base`.25782579OUTPUT:25802581The cartesian product of ``self`` and ``other`` with a2582suitable base ring to encompass the two.25832584EXAMPLES::25852586sage: P1 = Polyhedron([[0],[1]], base_ring=ZZ)2587sage: P2 = Polyhedron([[0],[1]], base_ring=QQ)2588sage: P1.product(P2)2589A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices25902591The cartesian product is the product in the semiring of polyhedra::25922593sage: P1 * P12594A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices2595sage: P1 * P22596A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices2597sage: P2 * P22598A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices2599sage: 2 * P12600A 1-dimensional polyhedron in ZZ^1 defined as the convex hull of 2 vertices2601sage: P1 * 2.02602A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices2603"""2604new_vertices = [ list(x)+list(y)2605for x in self.vertex_generator() for y in other.vertex_generator()]2606new_rays = []2607new_rays.extend( [ r+[0]*other.ambient_dim()2608for r in self.ray_generator() ] )2609new_rays.extend( [ [0]*self.ambient_dim()+r2610for r in other.ray_generator() ] )2611new_lines = []2612new_lines.extend( [ l+[0]*other.ambient_dim()2613for l in self.line_generator() ] )2614new_lines.extend( [ [0]*self.ambient_dim()+l2615for l in other.line_generator() ] )2616return Polyhedron(vertices=new_vertices,2617rays=new_rays, lines=new_lines,2618base_ring=self.parent()._coerce_base_ring(other))26192620_mul_ = product26212622def dilation(self, scalar):2623"""2624Return the dilated (uniformly stretched) polyhedron.26252626INPUT:26272628- ``scalar`` -- A scalar, not necessarily in :meth:`base_ring`.26292630OUTPUT:26312632The polyhedron dilated by that scalar, possibly coerced to a2633bigger field.26342635EXAMPLES::26362637sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,6)])2638sage: p.vertex_generator().next()2639A vertex at (2, 4, 8)2640sage: p2 = p.dilation(2)2641sage: p2.vertex_generator().next()2642A vertex at (4, 8, 16)2643sage: p.dilation(2) == p * 22644True26452646TESTS:26472648Dilation of empty polyhedrons works, see :trac:`14987`::26492650sage: p = Polyhedron(ambient_dim=2); p2651The empty polyhedron in ZZ^22652sage: p.dilation(3)2653The empty polyhedron in ZZ^226542655TESTS::26562657sage: p = Polyhedron(vertices=[(1,1)], rays=[(1,0)], lines=[(0,1)])2658sage: (-p).rays()2659(A ray in the direction (-1, 0),)2660sage: (-p).lines()2661(A line in the direction (0, 1),)26622663sage: (0*p).rays()2664()2665sage: (0*p).lines()2666()2667"""2668if scalar > 0:2669new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ]2670new_rays = self.rays()2671new_lines = self.lines()2672elif scalar < 0:2673new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ]2674new_rays = [ list(-r.vector()) for r in self.ray_generator()]2675new_lines = self.lines()2676else:2677new_vertices = [ self.ambient_space().zero() for v in self.vertex_generator() ]2678new_rays = []2679new_lines = []2680return Polyhedron(vertices=new_vertices,2681rays=new_rays, lines=new_lines,2682base_ring=self.parent()._coerce_base_ring(scalar),2683ambient_dim=self.ambient_dim())26842685def _acted_upon_(self, actor, self_on_left):2686"""2687Implement the multiplicative action by scalars or other polyhedra.26882689INPUT:26902691- ``actor`` -- A scalar, not necessarily in :meth:`base_ring`,2692or a :class:`Polyhedron`.26932694OUTPUT:26952696Multiplication by another polyhedron returns the product2697polytope. Multiplication by a scalar returns the polytope2698dilated by that scalar, possibly coerced to the bigger field.26992700EXAMPLES::27012702sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,6)])2703sage: p._acted_upon_(2, True) == p.dilation(2)2704True2705sage: p*2 == p.dilation(2)2706True2707sage: p*p == p.product(p)2708True2709sage: p + vector(ZZ,[1,2,3]) == p.translation([1,2,3])2710True2711"""2712if is_Polyhedron(actor):2713return self.product(actor)2714if is_Vector(actor):2715return self.translation(actor)2716else:2717return self.dilation(actor)27182719def __neg__(self):2720"""2721Negation of a polytope is defined as inverting the coordinates.27222723EXAMPLES::27242725sage: t = polytopes.n_simplex(3,project=False); t.vertices()2726(A vertex at (0, 0, 0, 1), A vertex at (0, 0, 1, 0),2727A vertex at (0, 1, 0, 0), A vertex at (1, 0, 0, 0))2728sage: neg_ = -t2729sage: neg_.vertices()2730(A vertex at (-1, 0, 0, 0), A vertex at (0, -1, 0, 0),2731A vertex at (0, 0, -1, 0), A vertex at (0, 0, 0, -1))27322733TESTS::27342735sage: p = Polyhedron(ieqs=[[1,1,0]])2736sage: p.rays()2737(A ray in the direction (1, 0),)2738sage: pneg = p.__neg__()2739sage: pneg.rays()2740(A ray in the direction (-1, 0),)2741"""2742return self.dilation(-1)27432744def __div__(self, scalar):2745"""2746Divide by a scalar factor.27472748See :meth:`dilation` for details.27492750EXAMPLES::27512752sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in srange(2,4)])2753sage: (p/5).Vrepresentation()2754(A vertex at (2/5, 4/5, 8/5), A vertex at (3/5, 9/5, 27/5))2755"""2756return self.dilation(1/scalar)27572758@coerce_binop2759def convex_hull(self, other):2760"""2761Return the convex hull of the set-theoretic union of the two2762polyhedra.27632764INPUT:27652766- ``other`` -- a :class:`Polyhedron`.27672768OUTPUT:27692770The convex hull.27712772EXAMPLES::27732774sage: a_simplex = polytopes.n_simplex(3)2775sage: verts = a_simplex.vertices()2776sage: verts = [[x[0]*3/5+x[1]*4/5, -x[0]*4/5+x[1]*3/5, x[2]] for x in verts]2777sage: another_simplex = Polyhedron(vertices = verts)2778sage: simplex_union = a_simplex.convex_hull(another_simplex)2779sage: simplex_union.n_vertices()278072781"""2782hull_vertices = self.vertices() + other.vertices()2783hull_rays = self.rays() + other.rays()2784hull_lines = self.lines() + other.lines()2785return self.parent().element_class(self.parent(), [hull_vertices, hull_rays, hull_lines], None)27862787@coerce_binop2788def intersection(self, other):2789"""2790Return the intersection of one polyhedron with another.27912792INPUT:27932794- ``other`` -- a :class:`Polyhedron`.27952796OUTPUT:27972798The intersection.27992800Note that the intersection of two `\ZZ`-polyhedra might not be2801a `\ZZ`-polyhedron. In this case, a `\QQ`-polyhedron is2802returned.28032804EXAMPLES::28052806sage: cube = polytopes.n_cube(3)2807sage: oct = polytopes.cross_polytope(3)2808sage: cube.intersection(oct*2)2809A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 12 vertices28102811As a shorthand, one may use::28122813sage: cube & oct*22814A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 12 vertices28152816The intersection of two `\ZZ`-polyhedra is not necessarily a `\ZZ`-polyhedron::28172818sage: P = Polyhedron([(0,0),(1,1)], base_ring=ZZ)2819sage: P.intersection(P)2820A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices2821sage: Q = Polyhedron([(0,1),(1,0)], base_ring=ZZ)2822sage: P.intersection(Q)2823A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex2824sage: _.Vrepresentation()2825(A vertex at (1/2, 1/2),)2826"""2827new_ieqs = self.inequalities() + other.inequalities()2828new_eqns = self.equations() + other.equations()2829parent = self.parent()2830try:2831return parent.element_class(parent, None, [new_ieqs, new_eqns])2832except TypeError,msg:2833if self.base_ring() is ZZ:2834parent = parent.base_extend(QQ)2835return parent.element_class(parent, None, [new_ieqs, new_eqns])2836else:2837raise TypeError(msg)28382839__and__ = intersection28402841def edge_truncation(self, cut_frac = Integer(1)/3):2842r"""2843Return a new polyhedron formed from two points on each edge2844between two vertices.28452846INPUT:28472848- ``cut_frac`` -- integer. how deeply to cut into the edge.2849Default is `\frac{1}{3}`.28502851OUTPUT:28522853A Polyhedron object, truncated as described above.28542855EXAMPLES::28562857sage: cube = polytopes.n_cube(3)2858sage: trunc_cube = cube.edge_truncation()2859sage: trunc_cube.n_vertices()2860242861sage: trunc_cube.n_inequalities()2862142863"""2864new_vertices = []2865for e in self.bounded_edges():2866new_vertices.append((1-cut_frac)*e[0]() + cut_frac *e[1]())2867new_vertices.append(cut_frac *e[0]() + (1-cut_frac)*e[1]())28682869new_vertices = [list(v) for v in new_vertices]2870new_rays = self.rays()2871new_lines = self.lines()28722873return Polyhedron(vertices=new_vertices, rays=new_rays,2874lines=new_lines,2875base_ring=self.parent()._coerce_base_ring(cut_frac))28762877def _make_polyhedron_face(self, Vindices, Hindices):2878"""2879Construct a face of the polyhedron.28802881INPUT:28822883- ``Vindices`` -- a tuple of integers. The indices of the2884V-represenation objects that span the face.28852886- ``Hindices`` -- a tuple of integers. The indices of the2887H-representation objects that hold as equalities on the2888face.28892890OUTPUT:28912892A new :class:`~sage.geometry.polyhedron.face.PolyhedronFace` instance. It is not checked2893whether the input data actually defines a face.28942895EXAMPLES::28962897sage: square = polytopes.n_cube(2)2898sage: square._make_polyhedron_face((0,2), (1,))2899<0,2>2900"""2901from sage.geometry.polyhedron.face import PolyhedronFace2902return PolyhedronFace(self, Vindices, Hindices)29032904@cached_method2905def face_lattice(self):2906"""2907Return the face-lattice poset.29082909OUTPUT:29102911A :class:`~sage.combinat.posets.posets.FinitePoset`. Elements2912are given as2913:class:`~sage.geometry.polyhedron.face.PolyhedronFace`.29142915In the case of a full-dimensional polytope, the faces are2916pairs (vertices, inequalities) of the spanning vertices and2917corresponding saturated inequalities. In general, a face is2918defined by a pair (V-rep. objects, H-rep. objects). The2919V-representation objects span the face, and the corresponding2920H-representation objects are those inequalities and equations2921that are saturated on the face.29222923The bottom-most element of the face lattice is the "empty2924face". It contains no V-representation object. All2925H-representation objects are incident.29262927The top-most element is the "full face". It is spanned by all2928V-representation objects. The incident H-representation2929objects are all equations and no inequalities.29302931In the case of a full-dimensional polytope, the "empty face"2932and the "full face" are the empty set (no vertices, all2933inequalities) and the full polytope (all vertices, no2934inequalities), respectively.29352936ALGORITHM:29372938For a full-dimensional polytope, the basic algorithm is2939described in2940:func:`~sage.geometry.hasse_diagram.Hasse_diagram_from_incidences`.2941There are three generalizations of [KP2002]_ necessary to deal2942with more general polytopes, corresponding to the extra2943H/V-representation objects:29442945* Lines are removed before calling2946:func:`Hasse_diagram_from_incidences`, and then added back2947to each face V-representation except for the "empty face".29482949* Equations are removed before calling2950:func:`Hasse_diagram_from_incidences`, and then added back2951to each face H-representation.29522953* Rays: Consider the half line as an example. The2954V-representation objects are a point and a ray, which we can2955think of as a point at infinity. However, the point at2956infinity has no inequality associated to it, so there is2957only one H-representation object alltogether. The face2958lattice does not contain the "face at infinity". This means2959that in :func:`Hasse_diagram_from_incidences`, one needs to2960drop faces with V-representations that have no matching2961H-representation. In addition, one needs to ensure that2962every non-empty face contains at least one vertex.29632964EXAMPLES::29652966sage: square = polytopes.n_cube(2)2967sage: square.face_lattice()2968Finite poset containing 10 elements2969sage: list(_)2970[<>, <0>, <1>, <2>, <3>, <0,1>, <0,2>, <2,3>, <1,3>, <0,1,2,3>]2971sage: poset_element = _[6]2972sage: a_face = poset_element2973sage: a_face2974<0,2>2975sage: a_face.dim()297612977sage: set(a_face.ambient_Vrepresentation()) == \2978... set([square.Vrepresentation(0), square.Vrepresentation(2)])2979True2980sage: a_face.ambient_Vrepresentation()2981(A vertex at (-1, -1), A vertex at (1, -1))2982sage: a_face.ambient_Hrepresentation()2983(An inequality (0, 1) x + 1 >= 0,)29842985A more complicated example::29862987sage: c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)])2988sage: c5_10_fl = c5_10.face_lattice()2989sage: [len(x) for x in c5_10_fl.level_sets()]2990[1, 10, 45, 100, 105, 42, 1]29912992Note that if the polyhedron contains lines then there is a2993dimension gap between the empty face and the first non-empty2994face in the face lattice::29952996sage: line = Polyhedron(vertices=[(0,)], lines=[(1,)])2997sage: [ fl.dim() for fl in line.face_lattice() ]2998[-1, 1]29993000TESTS::30013002sage: c5_20 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5]3003... for i in range(1,21)])3004sage: c5_20_fl = c5_20.face_lattice() # long time3005sage: [len(x) for x in c5_20_fl.level_sets()] # long time3006[1, 20, 190, 580, 680, 272, 1]3007sage: polytopes.n_cube(2).face_lattice().plot()3008sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets()3009sage: print level_sets[0], level_sets[-1]3010[<>] [<0,1,2,3>]30113012Various degenerate polyhedra::30133014sage: Polyhedron(vertices=[[0,0,0],[1,0,0],[0,1,0]]).face_lattice().level_sets()3015[[<>], [<0>, <1>, <2>], [<0,1>, <0,2>, <1,2>], [<0,1,2>]]3016sage: Polyhedron(vertices=[(1,0,0),(0,1,0)], rays=[(0,0,1)]).face_lattice().level_sets()3017[[<>], [<1>, <2>], [<0,1>, <0,2>, <1,2>], [<0,1,2>]]3018sage: Polyhedron(rays=[(1,0,0),(0,1,0)], vertices=[(0,0,1)]).face_lattice().level_sets()3019[[<>], [<0>], [<0,1>, <0,2>], [<0,1,2>]]3020sage: Polyhedron(rays=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()3021[[<>], [<0>], [<0,1>, <0,2>], [<0,1,2>]]3022sage: Polyhedron(vertices=[(1,),(0,)]).face_lattice().level_sets()3023[[<>], [<0>, <1>], [<0,1>]]3024sage: Polyhedron(vertices=[(1,0,0),(0,1,0)], lines=[(0,0,1)]).face_lattice().level_sets()3025[[<>], [<0,1>, <0,2>], [<0,1,2>]]3026sage: Polyhedron(lines=[(1,0,0)], vertices=[(0,0,1)]).face_lattice().level_sets()3027[[<>], [<0,1>]]3028sage: Polyhedron(lines=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()3029[[<>], [<0,1,2>]]3030sage: Polyhedron(lines=[(1,0)], rays=[(0,1)], vertices=[(0,0)])\3031... .face_lattice().level_sets()3032[[<>], [<0,1>], [<0,1,2>]]3033sage: Polyhedron(vertices=[(0,)], lines=[(1,)]).face_lattice().level_sets()3034[[<>], [<0,1>]]3035sage: Polyhedron(lines=[(1,0)], vertices=[(0,0)]).face_lattice().level_sets()3036[[<>], [<0,1>]]30373038REFERENCES:30393040.. [KP2002]30413042Volker Kaibel and Marc E. Pfetsch, "Computing the Face3043Lattice of a Polytope from its Vertex-Facet Incidences",3044Computational Geometry: Theory and Applications, Volume304523, Issue 3 (November 2002), 281-290. Available at3046http://portal.acm.org/citation.cfm?id=763203 and free of3047charge at http://arxiv.org/abs/math/01060433048"""3049coatom_to_Hindex = [ h.index() for h in self.inequality_generator() ]3050Hindex_to_coatom = [None] * self.n_Hrepresentation()3051for i in range(0,len(coatom_to_Hindex)):3052Hindex_to_coatom[ coatom_to_Hindex[i] ] = i30533054atom_to_Vindex = [ v.index() for v in self.Vrep_generator() if not v.is_line() ]3055Vindex_to_atom = [None] * self.n_Vrepresentation()3056for i in range(0,len(atom_to_Vindex)):3057Vindex_to_atom[ atom_to_Vindex[i] ] = i30583059atoms_incidences = [ tuple([ Hindex_to_coatom[h.index()]3060for h in v.incident() if h.is_inequality() ])3061for v in self.Vrepresentation() if not v.is_line() ]30623063coatoms_incidences = [ tuple([ Vindex_to_atom[v.index()]3064for v in h.incident() if not v.is_line() ])3065for h in self.Hrepresentation() if h.is_inequality() ]30663067atoms_vertices = [ Vindex_to_atom[v.index()] for v in self.vertex_generator() ]3068equations = [ e.index() for e in self.equation_generator() ]3069lines = [ l.index() for l in self.line_generator() ]30703071def face_constructor(atoms,coatoms):3072if len(atoms)==0:3073Vindices = ()3074else:3075Vindices = tuple(sorted([ atom_to_Vindex[i] for i in atoms ]+lines))3076Hindices = tuple(sorted([ coatom_to_Hindex[i] for i in coatoms ]+equations))3077return self._make_polyhedron_face(Vindices, Hindices)30783079from sage.geometry.hasse_diagram import Hasse_diagram_from_incidences3080return Hasse_diagram_from_incidences\3081(atoms_incidences, coatoms_incidences,3082face_constructor=face_constructor, required_atoms=atoms_vertices)30833084def faces(self, face_dimension):3085"""3086Return the faces of given dimension30873088INPUT:30893090- ``face_dimension`` -- integer. The dimension of the faces3091whose representation will be returned.30923093OUTPUT:30943095A tuple of3096:class:`~sage.geometry.polyhedron.face.PolyhedronFace`. See3097:mod:`~sage.geometry.polyhedron.face` for details. The order3098random but fixed.30993100EXAMPLES:31013102Here we find the vertex and face indices of the eight three-dimensional3103facets of the four-dimensional hypercube::31043105sage: p = polytopes.n_cube(4)3106sage: p.faces(3)3107(<0,1,2,3,4,5,6,7>, <0,1,2,3,8,9,10,11>, <0,1,4,5,8,9,12,13>,3108<0,2,4,6,8,10,12,14>, <2,3,6,7,10,11,14,15>, <8,9,10,11,12,13,14,15>,3109<4,5,6,7,12,13,14,15>, <1,3,5,7,9,11,13,15>)31103111sage: face = p.faces(3)[0]3112sage: face.ambient_Hrepresentation()3113(An inequality (1, 0, 0, 0) x + 1 >= 0,)3114sage: face.vertices()3115(A vertex at (-1, -1, -1, -1), A vertex at (-1, -1, -1, 1),3116A vertex at (-1, -1, 1, -1), A vertex at (-1, -1, 1, 1),3117A vertex at (-1, 1, -1, -1), A vertex at (-1, 1, -1, 1),3118A vertex at (-1, 1, 1, -1), A vertex at (-1, 1, 1, 1))31193120You can use the3121:meth:`~sage.geometry.polyhedron.representation.PolyhedronRepresentation.index`3122method to enumerate vertices and inequalities::31233124sage: def get_idx(rep): return rep.index()3125sage: map(get_idx, face.ambient_Hrepresentation())3126[4]3127sage: map(get_idx, face.ambient_Vrepresentation())3128[0, 1, 2, 3, 4, 5, 6, 7]31293130sage: [ (map(get_idx, face.ambient_Vrepresentation()), map(get_idx, face.ambient_Hrepresentation()))3131... for face in p.faces(3) ]3132[([0, 1, 2, 3, 4, 5, 6, 7], [4]),3133([0, 1, 2, 3, 8, 9, 10, 11], [5]),3134([0, 1, 4, 5, 8, 9, 12, 13], [6]),3135([0, 2, 4, 6, 8, 10, 12, 14], [7]),3136([2, 3, 6, 7, 10, 11, 14, 15], [2]),3137([8, 9, 10, 11, 12, 13, 14, 15], [0]),3138([4, 5, 6, 7, 12, 13, 14, 15], [1]),3139([1, 3, 5, 7, 9, 11, 13, 15], [3])]31403141TESTS::31423143sage: pr = Polyhedron(rays = [[1,0,0],[-1,0,0],[0,1,0]], vertices = [[-1,-1,-1]], lines=[(0,0,1)])3144sage: pr.faces(4)3145()3146sage: pr.faces(3)3147(<0,1,2,3>,)3148sage: pr.faces(2)3149(<0,1,2>,)3150sage: pr.faces(1)3151()3152sage: pr.faces(0)3153()3154sage: pr.faces(-1)3155()3156"""3157fl = self.face_lattice().level_sets()3158codim = self.dim() - face_dimension3159index = len(fl) - 1 - codim3160if index>=len(fl) or index<1:3161return tuple()3162return tuple(fl[index])31633164@cached_method3165def f_vector(self):3166r"""3167Return the f-vector.31683169OUTPUT:31703171Returns a vector whose ``i``-th entry is the number of3172``i``-dimensional faces of the polytope.31733174EXAMPLES::31753176sage: p = Polyhedron(vertices=[[1, 2, 3], [1, 3, 2],3177... [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1], [0, 0, 0]])3178sage: p.f_vector()3179(1, 7, 12, 7, 1)3180"""3181return vector(ZZ,[len(x) for x in self.face_lattice().level_sets()])31823183@cached_method3184def vertex_graph(self):3185"""3186Return a graph in which the vertices correspond to vertices3187of the polyhedron, and edges to edges.31883189EXAMPLES::31903191sage: g3 = polytopes.n_cube(3).vertex_graph()3192sage: len(g3.automorphism_group())3193483194sage: s4 = polytopes.n_simplex(4).vertex_graph()3195sage: s4.is_eulerian()3196True3197"""3198return Graph(self.vertex_adjacency_matrix(), loops=True)31993200graph = vertex_graph32013202def polar(self):3203"""3204Return the polar (dual) polytope.32053206The original vertices are translated so that their barycenter3207is at the origin, and then the vertices are used as the3208coefficients in the polar inequalities.32093210EXAMPLES::32113212sage: p = Polyhedron(vertices = [[0,0,1],[0,1,0],[1,0,0],[0,0,0],[1,1,1]], base_ring=QQ)3213sage: p3214A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 5 vertices3215sage: p.polar()3216A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 6 vertices32173218sage: cube = polytopes.n_cube(3)3219sage: octahedron = polytopes.cross_polytope(3)3220sage: cube_dual = cube.polar()3221sage: octahedron == cube_dual3222True3223"""3224assert self.is_compact(), "Not a polytope."32253226verts = [list(self.center() - v.vector()) for v in self.vertex_generator()]3227base_ring = self.parent()._coerce_base_ring(self.center().parent())3228return Polyhedron(ieqs=[[1] + list(v) for v in verts], base_ring=base_ring)32293230def pyramid(self):3231"""3232Returns a polyhedron that is a pyramid over the original.32333234EXAMPLES::32353236sage: square = polytopes.n_cube(2); square3237A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices3238sage: egyptian_pyramid = square.pyramid(); egyptian_pyramid3239A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 5 vertices3240sage: egyptian_pyramid.n_vertices()324153242sage: for v in egyptian_pyramid.vertex_generator(): print v3243A vertex at (0, -1, -1)3244A vertex at (0, -1, 1)3245A vertex at (0, 1, -1)3246A vertex at (0, 1, 1)3247A vertex at (1, 0, 0)3248"""3249new_verts = \3250[[0] + x for x in self.Vrep_generator()] + \3251[[1] + list(self.center())]3252return Polyhedron(vertices=new_verts)32533254def bipyramid(self):3255"""3256Return a polyhedron that is a bipyramid over the original.32573258EXAMPLES::32593260sage: octahedron = polytopes.cross_polytope(3)3261sage: cross_poly_4d = octahedron.bipyramid()3262sage: cross_poly_4d.n_vertices()326383264sage: q = [list(v) for v in cross_poly_4d.vertex_generator()]3265sage: q3266[[-1, 0, 0, 0],3267[0, -1, 0, 0],3268[0, 0, -1, 0],3269[0, 0, 0, -1],3270[0, 0, 0, 1],3271[0, 0, 1, 0],3272[0, 1, 0, 0],3273[1, 0, 0, 0]]32743275Now check that bipyramids of cross-polytopes are cross-polytopes::32763277sage: q2 = [list(v) for v in polytopes.cross_polytope(4).vertex_generator()]3278sage: [v in q2 for v in q]3279[True, True, True, True, True, True, True, True]3280"""3281new_verts = \3282[[ 0] + list(x) for x in self.vertex_generator()] + \3283[[ 1] + list(self.center())] + \3284[[-1] + list(self.center())]3285new_rays = [[0] + r for r in self.rays()]3286new_lines = [[0] + list(l) for l in self.lines()]3287return Polyhedron(vertices=new_verts, rays=new_rays, lines=new_lines)32883289def prism(self):3290"""3291Return a prism of the original polyhedron.32923293EXAMPLES::32943295sage: square = polytopes.n_cube(2)3296sage: cube = square.prism()3297sage: cube3298A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices3299sage: hypercube = cube.prism()3300sage: hypercube.n_vertices()3301163302"""3303new_verts = []3304new_verts.extend( [ [0] + v for v in self.vertices()] )3305new_verts.extend( [ [1] + v for v in self.vertices()] )3306new_rays = [ [0] + r for r in self.rays()]3307new_lines = [ [0] + l for l in self.lines()]3308return Polyhedron(vertices=new_verts, rays=new_rays, lines=new_lines,3309base_ring=self.base_ring())33103311def projection(self):3312"""3313Return a projection object.33143315EXAMPLES::33163317sage: p = polytopes.n_cube(3)3318sage: proj = p.projection()3319sage: proj3320The projection of a polyhedron into 3 dimensions3321"""3322from plot import Projection3323self.projection = Projection(self)3324return self.projection33253326def render_solid(self, **kwds):3327"""3328Return a solid rendering of a 2- or 3-d polytope.33293330EXAMPLES::33313332sage: p = polytopes.n_cube(3)3333sage: p_solid = p.render_solid(opacity = .7)3334sage: type(p_solid)3335<class 'sage.plot.plot3d.base.Graphics3dGroup'>3336"""3337proj = self.projection()3338if self.ambient_dim()==3:3339return proj.render_solid_3d(**kwds)3340if self.ambient_dim()==2:3341return proj.render_fill_2d(**kwds)3342raise ValueError, "render_solid is only defined for 2 and 3 dimensional polyhedra."33433344def render_wireframe(self, **kwds):3345"""3346For polytopes in 2 or 3 dimensions, return the edges3347as a list of lines.33483349EXAMPLES::33503351sage: p = Polyhedron([[1,2,],[1,1],[0,0]])3352sage: p_wireframe = p.render_wireframe()3353sage: p_wireframe._objects3354[Line defined by 2 points, Line defined by 2 points, Line defined by 2 points]3355"""3356proj = self.projection()3357if self.ambient_dim()==3:3358return proj.render_wireframe_3d(**kwds)3359if self.ambient_dim()==2:3360return proj.render_outline_2d(**kwds)3361raise ValueError, "render_wireframe is only defined for 2 and 3 dimensional polyhedra."33623363def schlegel_projection(self, projection_dir = None, height = 1.1):3364"""3365Returns a projection object whose transformed coordinates are3366a Schlegel projection of the polyhedron.33673368EXAMPLES::33693370sage: p = polytopes.n_cube(3)3371sage: sch_proj = p.schlegel_projection()3372sage: schlegel_edge_indices = sch_proj.lines3373sage: schlegel_edges = [sch_proj.coordinates_of(x) for x in schlegel_edge_indices]3374sage: len([x for x in schlegel_edges if x[0][0] > 0])337543376"""3377proj = self.projection()3378if projection_dir == None:3379vertices = self.vertices()3380facet = self.Hrepresentation(0)3381f0 = [ v.index() for v in facet.incident() ]3382projection_dir = [sum([vertices[f0[i]][j]/len(f0) for i in range(len(f0))])3383for j in range(self.ambient_dim())]3384return proj.schlegel(projection_direction = projection_dir, height = height)33853386def _volume_lrs(self, verbose=False):3387"""3388Computes the volume of a polytope using lrs.33893390OUTPUT:33913392The volume, cast to RDF (although lrs seems to output a3393rational value this must be an approximation in some cases).33943395EXAMPLES::33963397sage: polytopes.n_cube(3)._volume_lrs() #optional - lrs33988.03399sage: (polytopes.n_cube(3)*2)._volume_lrs() #optional - lrs340064.03401sage: polytopes.twenty_four_cell()._volume_lrs() #optional - lrs34022.034033404REFERENCES:34053406David Avis's lrs program.3407"""3408if is_package_installed('lrs') != True:3409print 'You must install the optional lrs package ' \3410'for this function to work'3411raise NotImplementedError34123413from sage.misc.temporary_file import tmp_filename3414from subprocess import Popen, PIPE3415in_str = self.cdd_Vrepresentation()3416in_str += 'volume'3417in_filename = tmp_filename()3418in_file = file(in_filename,'w')3419in_file.write(in_str)3420in_file.close()3421if verbose: print in_str34223423lrs_procs = Popen(['lrs',in_filename],3424stdin = PIPE, stdout=PIPE, stderr=PIPE)3425ans, err = lrs_procs.communicate()3426if verbose:3427print ans3428# FIXME: check err34293430for a_line in ans.splitlines():3431if 'Volume=' in a_line:3432volume = a_line.split('Volume=')[1]3433volume = RDF(QQ(volume))3434return volume34353436raise ValueError("lrs did not return a volume")34373438def lrs_volume(self, verbose=False):3439"""3440Computes the volume of a polytope using lrs.34413442OUTPUT:34433444The volume, cast to RDF (although lrs seems to output a3445rational value this must be an approximation in some cases).34463447EXAMPLES::34483449sage: polytopes.n_cube(3).lrs_volume() #optional - lrs3450doctest:...: DeprecationWarning: use volume(engine='lrs') instead3451See http://trac.sagemath.org/13249 for details.34528.03453sage: (polytopes.n_cube(3)*2).lrs_volume() #optional - lrs345464.03455sage: polytopes.twenty_four_cell().lrs_volume() #optional - lrs34562.034573458REFERENCES:34593460David Avis's lrs program.3461"""3462from sage.misc.superseded import deprecation3463deprecation(13249, "use volume(engine='lrs') instead")3464return self._volume_lrs(verbose=verbose)34653466@cached_method3467def volume(self, engine='auto', **kwds):3468"""3469Return the volume of the polytope.34703471- ``engine`` -- string. The backend to use. Allowed values are:34723473* ``'auto'`` (default): see :meth:`triangulate`.3474* ``'internal'``: see :meth:`triangulate`.3475* ``'TOPCOM'``: see :meth:`triangulate`.3476* ``'lrs'``: use David Avis's lrs program (optional).34773478- ``**kwds`` -- keyword arguments that are passed to the3479triangulation engine.34803481OUTPUT:34823483The volume of the polytope.34843485EXAMPLES::34863487sage: polytopes.n_cube(3).volume()348883489sage: (polytopes.n_cube(3)*2).volume()3490643491sage: polytopes.twenty_four_cell().volume()3492234933494sage: polytopes.regular_polygon(5, base_ring=RDF).volume()34952.37764129...3496sage: P5 = polytopes.regular_polygon(5, base_ring=QQ)3497sage: P5.volume() # rational approximation34983387471714099766473500515673753476175274812279494567801326487870013/14247194172206224265610866402296662239845281422372778033276994354003499sage: _.n()35002.37764129...35013502Volume of the same polytope, using the optional package lrs::35033504sage: P5.volume(engine='lrs') #optional - lrs35052.37764129...3506"""3507if engine=='lrs':3508return self._volume_lrs(**kwds)3509dim = self.dim()3510if dim < self.ambient_dim():3511return self.base_ring().zero()3512triangulation = self.triangulate(engine=engine, **kwds)3513pc = triangulation.point_configuration()3514return sum([ pc.volume(simplex) for simplex in triangulation ]) / ZZ(dim).factorial()35153516def contains(self, point):3517"""3518Test whether the polyhedron contains the given ``point``.35193520See also :meth:`interior_contains` and3521:meth:`relative_interior_contains`.35223523INPUT:35243525- ``point`` -- coordinates of a point (an iterable).35263527OUTPUT:35283529Boolean.35303531EXAMPLES::35323533sage: P = Polyhedron(vertices=[[1,1],[1,-1],[0,0]])3534sage: P.contains( [1,0] )3535True3536sage: P.contains( P.center() ) # true for any convex set3537True35383539As a shorthand, one may use the usual ``in`` operator::35403541sage: P.center() in P3542True3543sage: [-1,-1] in P3544False35453546The point need not have coordinates in the same field as the3547polyhedron::35483549sage: ray = Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ)3550sage: ray.contains([sqrt(2)/3,0]) # irrational coordinates are ok3551True3552sage: a = var('a')3553sage: ray.contains([a,0]) # a might be negative!3554False3555sage: assume(a>0)3556sage: ray.contains([a,0])3557True3558sage: ray.contains(['hello', 'kitty']) # no common ring for coordinates3559False35603561The empty polyhedron needs extra care, see trac #10238::35623563sage: empty = Polyhedron(); empty3564The empty polyhedron in ZZ^03565sage: empty.contains([])3566False3567sage: empty.contains([0]) # not a point in QQ^03568False3569sage: full = Polyhedron(vertices=[()]); full3570A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex3571sage: full.contains([])3572True3573sage: full.contains([0])3574False3575"""3576try:3577p = vector(point)3578except TypeError: # point not iterable or no common ring for elements3579if len(point)>0:3580return False3581else:3582p = vector(self.base_ring(), [])35833584if len(p)!=self.ambient_dim():3585return False35863587for H in self.Hrep_generator():3588if not H.contains(p):3589return False3590return True35913592__contains__ = contains35933594def interior_contains(self, point):3595"""3596Test whether the interior of the polyhedron contains the3597given ``point``.35983599See also :meth:`contains` and3600:meth:`relative_interior_contains`.36013602INPUT:36033604- ``point`` -- coordinates of a point.36053606OUTPUT:36073608``True`` or ``False``.36093610EXAMPLES::36113612sage: P = Polyhedron(vertices=[[0,0],[1,1],[1,-1]])3613sage: P.contains( [1,0] )3614True3615sage: P.interior_contains( [1,0] )3616False36173618If the polyhedron is of strictly smaller dimension than the3619ambient space, its interior is empty::36203621sage: P = Polyhedron(vertices=[[0,1],[0,-1]])3622sage: P.contains( [0,0] )3623True3624sage: P.interior_contains( [0,0] )3625False36263627The empty polyhedron needs extra care, see trac #10238::36283629sage: empty = Polyhedron(); empty3630The empty polyhedron in ZZ^03631sage: empty.interior_contains([])3632False3633"""3634try:3635p = vector(point)3636except TypeError: # point not iterable or no common ring for elements3637if len(point)>0:3638return False3639else:3640p = vector(self.base_ring(), [])36413642if len(p)!=self.ambient_dim():3643return False36443645for H in self.Hrep_generator():3646if not H.interior_contains(p):3647return False3648return True36493650def relative_interior_contains(self, point):3651"""3652Test whether the relative interior of the polyhedron3653contains the given ``point``.36543655See also :meth:`contains` and :meth:`interior_contains`.36563657INPUT:36583659- ``point`` -- coordinates of a point.36603661OUTPUT:36623663``True`` or ``False``.36643665EXAMPLES::36663667sage: P = Polyhedron(vertices=[(1,0), (-1,0)])3668sage: P.contains( (0,0) )3669True3670sage: P.interior_contains( (0,0) )3671False3672sage: P.relative_interior_contains( (0,0) )3673True3674sage: P.relative_interior_contains( (1,0) )3675False36763677The empty polyhedron needs extra care, see trac #10238::36783679sage: empty = Polyhedron(); empty3680The empty polyhedron in ZZ^03681sage: empty.relative_interior_contains([])3682False3683"""3684try:3685p = vector(point)3686except TypeError: # point not iterable or no common ring for elements3687if len(point)>0:3688return False3689else:3690p = vector(self.base_ring(), [])36913692if len(p)!=self.ambient_dim():3693return False36943695for eq in self.equation_generator():3696if not eq.contains(p):3697return False36983699for ine in self.inequality_generator():3700if not ine.interior_contains(p):3701return False37023703return True37043705def is_simplex(self):3706r"""3707Return whether the polyhedron is a simplex.37083709EXAMPLES::37103711sage: Polyhedron([(0,0,0), (1,0,0), (0,1,0)]).is_simplex()3712True3713sage: polytopes.n_simplex(3).is_simplex()3714True3715sage: polytopes.n_cube(3).is_simplex()3716False3717"""3718return self.is_compact() and (self.dim()+1==self.n_vertices())37193720@cached_method3721def is_lattice_polytope(self):3722r"""3723Return whether the polyhedron is a lattice polytope.37243725OUTPUT:37263727``True`` if the polyhedron is compact and has only integral3728vertices, ``False`` otherwise.37293730EXAMPLES::37313732sage: polytopes.cross_polytope(3).is_lattice_polytope()3733True3734sage: polytopes.regular_polygon(5).is_lattice_polytope()3735False3736"""3737if not self.is_compact():3738return False3739if self.base_ring() is ZZ:3740return True3741return all(v.is_integral() for v in self.vertex_generator())37423743@cached_method3744def lattice_polytope(self, envelope=False):3745r"""3746Return an encompassing lattice polytope.37473748INPUT:37493750- ``envelope`` -- boolean (default: ``False``). If the3751polyhedron has non-integral vertices, this option decides3752whether to return a strictly larger lattice polytope or3753raise a ``ValueError``. This option has no effect if the3754polyhedron has already integral vertices.37553756OUTPUT:37573758A :class:`LatticePolytope3759<sage.geometry.lattice_polytope.LatticePolytopeClass>`. If the3760polyhedron is compact and has integral vertices, the lattice3761polytope equals the polyhedron. If the polyhedron is compact3762but has at least one non-integral vertex, a strictly larger3763lattice polytope is returned.37643765If the polyhedron is not compact, a ``NotImplementedError`` is3766raised.37673768If the polyhedron is not integral and ``envelope=False``, a3769``ValueError`` is raised.37703771ALGORITHM:37723773For each non-integral vertex, a bounding box of integral3774points is added and the convex hull of these integral points3775is returned.37763777EXAMPLES:37783779First, a polyhedron with integral vertices::37803781sage: P = Polyhedron( vertices = [(1, 0), (0, 1), (-1, 0), (0, -1)])3782sage: lp = P.lattice_polytope(); lp3783A lattice polytope: 2-dimensional, 4 vertices.3784sage: lp.vertices()3785[-1 0 0 1]3786[ 0 -1 1 0]37873788Here is a polyhedron with non-integral vertices::37893790sage: P = Polyhedron( vertices = [(1/2, 1/2), (0, 1), (-1, 0), (0, -1)])3791sage: lp = P.lattice_polytope()3792Traceback (most recent call last):3793...3794ValueError: Some vertices are not integral. You probably want3795to add the argument "envelope=True" to compute an enveloping3796lattice polytope.3797sage: lp = P.lattice_polytope(True); lp3798A lattice polytope: 2-dimensional, 5 vertices.3799sage: lp.vertices()3800[-1 0 0 1 1]3801[ 0 -1 1 0 1]3802"""3803if not self.is_compact():3804raise NotImplementedError, 'Only compact lattice polytopes are allowed.'38053806try:3807vertices = self.vertices_matrix(ZZ)3808except TypeError:3809if envelope==False:3810raise ValueError, 'Some vertices are not integral. '+\3811'You probably want to add the argument '+\3812'"envelope=True" to compute an enveloping lattice polytope.'3813vertices = []3814for v in self.vertex_generator():3815vbox = [ set([floor(x),ceil(x)]) for x in v ]3816vertices.extend( CartesianProduct(*vbox) )3817vertices = matrix(ZZ, vertices).transpose()38183819# construct the (enveloping) lattice polytope3820from sage.geometry.lattice_polytope import LatticePolytope3821return LatticePolytope(vertices)38223823def _integral_points_PALP(self):3824r"""3825Return the integral points in the polyhedron using PALP.38263827This method is for testing purposes and will eventually be removed.38283829OUTPUT:38303831The list of integral points in the polyhedron. If the3832polyhedron is not compact, a ``ValueError`` is raised.38333834EXAMPLES::38353836sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)])._integral_points_PALP()3837[(-1, -1), (0, 1), (1, 0), (1, 1), (0, 0)]3838sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)]).lattice_polytope(True).points()3839[ 0 -1 -1 0 1 1 0]3840[-1 0 -1 1 0 1 0]3841sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)])._integral_points_PALP()3842[(0, 1), (1, 0), (1, 1), (0, 0)]3843"""3844if not self.is_compact():3845raise ValueError, 'Can only enumerate points in a compact polyhedron.'3846lp = self.lattice_polytope(True)3847# remove cached values to get accurate timings3848try:3849del lp._points3850del lp._npoints3851except AttributeError:3852pass3853if self.is_lattice_polytope():3854return lp.points().columns()3855points = filter(lambda p: self.contains(p),3856lp.points().columns())3857return points38583859@cached_method3860def bounding_box(self, integral=False):3861r"""3862Return the coordinates of a rectangular box containing the non-empty polytope.38633864INPUT:38653866- ``integral`` -- Boolean (default: ``False``). Whether to3867only allow integral coordinates in the bounding box.38683869OUTPUT:38703871A pair of tuples ``(box_min, box_max)`` where ``box_min`` are3872the coordinates of a point bounding the coordinates of the3873polytope from below and ``box_max`` bounds the coordinates3874from above.38753876EXAMPLES::38773878sage: Polyhedron([ (1/3,2/3), (2/3, 1/3) ]).bounding_box()3879((1/3, 1/3), (2/3, 2/3))3880sage: Polyhedron([ (1/3,2/3), (2/3, 1/3) ]).bounding_box(integral=True)3881((0, 0), (1, 1))3882sage: polytopes.buckyball().bounding_box()3883((-1059/1309, -1059/1309, -1059/1309), (1059/1309, 1059/1309, 1059/1309))3884"""3885box_min = []3886box_max = []3887if self.n_vertices==0:3888raise ValueError('Empty polytope is not allowed')3889if not self.is_compact():3890raise ValueError('Only polytopes (compact polyhedra) are allowed.')3891for i in range(0,self.ambient_dim()):3892coords = [ v[i] for v in self.vertex_generator() ]3893max_coord = max(coords)3894min_coord = min(coords)3895if integral:3896box_max.append(ceil(max_coord))3897box_min.append(floor(min_coord))3898else:3899box_max.append(max_coord)3900box_min.append(min_coord)3901return (tuple(box_min), tuple(box_max))39023903def integral_points(self, threshold=100000):3904r"""3905Return the integral points in the polyhedron.39063907Uses either the naive algorithm (iterate over a rectangular3908bounding box) or triangulation + Smith form.39093910INPUT:39113912- ``threshold`` -- integer (default: 100000). Use the naive3913algorith as long as the bounding box is smaller than this.39143915OUTPUT:39163917The list of integral points in the polyhedron. If the3918polyhedron is not compact, a ``ValueError`` is raised.39193920EXAMPLES::39213922sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]).integral_points()3923((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))39243925sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)])3926sage: simplex.integral_points()3927((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))39283929The polyhedron need not be full-dimensional::39303931sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)])3932sage: simplex.integral_points()3933((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))39343935sage: point = Polyhedron([(2,3,7)])3936sage: point.integral_points()3937((2, 3, 7),)39383939sage: empty = Polyhedron()3940sage: empty.integral_points()3941()39423943Here is a simplex where the naive algorithm of running over3944all points in a rectangular bounding box no longer works fast3945enough::39463947sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]3948sage: simplex = Polyhedron(v); simplex3949A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices3950sage: len(simplex.integral_points())39514939523953Finally, the 3-d reflexive polytope number 4078::39543955sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),3956... (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]3957sage: P = Polyhedron(v)3958sage: pts1 = P.integral_points() # Sage's own code3959sage: all(P.contains(p) for p in pts1)3960True3961sage: pts2 = LatticePolytope(v).points().columns() # PALP3962sage: for p in pts1: p.set_immutable()3963sage: for p in pts2: p.set_immutable()3964sage: set(pts1) == set(pts2)3965True39663967sage: timeit('Polyhedron(v).integral_points()') # random output3968sage: timeit('LatticePolytope(v).points()') # random output3969"""3970if not self.is_compact():3971raise ValueError('Can only enumerate points in a compact polyhedron.')3972if self.n_vertices() == 0:3973return tuple()39743975# for small bounding boxes, it is faster to naively iterate over the points of the box3976box_min, box_max = self.bounding_box(integral=True)3977box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max))3978if not self.is_lattice_polytope() or \3979(self.is_simplex() and box_points<1000) or \3980box_points<threshold:3981from sage.geometry.integral_points import rectangular_box_points3982return rectangular_box_points(box_min, box_max, self)39833984# for more complicate polytopes, triangulate & use smith normal form3985from sage.geometry.integral_points import simplex_points3986if self.is_simplex():3987return simplex_points(self.Vrepresentation())3988triangulation = self.triangulate()3989points = set()3990for simplex in triangulation:3991triang_vertices = [ self.Vrepresentation(i) for i in simplex ]3992new_points = simplex_points(triang_vertices)3993for p in new_points:3994p.set_immutable()3995points.update(new_points)3996# assert all(self.contains(p) for p in points) # slow3997return tuple(points)39983999@cached_method4000def combinatorial_automorphism_group(self):4001"""4002Computes the combinatorial automorphism group of the vertex4003graph of the polyhedron.40044005OUTPUT:40064007A4008:class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`4009that is isomorphic to the combinatorial automorphism group is4010returned.40114012Note that in Sage, permutation groups always act on positive4013integers while ``self.Vrepresentation()`` is indexed by4014nonnegative integers. The indexing of the permutation group is4015chosen to be shifted by ``+1``. That is, ``i`` in the4016permutation group corresponds to the V-representation object4017``self.Vrepresentation(i-1)``.40184019EXAMPLES::40204021sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)])4022sage: quadrangle.combinatorial_automorphism_group()4023Permutation Group with generators [(2,3), (1,2)(3,4)]4024sage: quadrangle.restricted_automorphism_group()4025Permutation Group with generators [()]40264027Permutations can only exchange vertices with vertices, rays4028with rays, and lines with lines::40294030sage: P = Polyhedron(vertices=[(1,0,0), (1,1,0)], rays=[(1,0,0)], lines=[(0,0,1)])4031sage: P.combinatorial_automorphism_group()4032Permutation Group with generators [(3,4)]4033"""4034from sage.groups.perm_gps.permgroup import PermutationGroup4035G = Graph()4036for edge in self.vertex_graph().edges():4037i = edge[0]4038j = edge[1]4039G.add_edge(i+1, j+1, (self.Vrepresentation(i).type(), self.Vrepresentation(j).type()) )40404041group = G.automorphism_group(edge_labels=True)4042self._combinatorial_automorphism_group = group4043return group40444045def _affine_coordinates(self, Vrep_object):4046r"""4047Return affine coordinates for a V-representation object.40484049INPUT:40504051- ``v`` -- a V-representation object or any iterable4052containing ``self.ambient_dim()`` coordinates. The4053coordinates must specify a point in the affine plane4054containing the polyhedron, or the output will be invalid. No4055checks on the input are performed.40564057OUTPUT:40584059A ``self.dim()``-dimensional coordinate vector. It contains4060the coordinates of ``v`` in an arbitrary but fixed basis for4061the affine span of the polyhedron.40624063EXAMPLES::40644065sage: P = Polyhedron(rays=[(1,0,0),(0,1,0)])4066sage: P._affine_coordinates( (-1,-2,-3) )4067(-1, -2)4068sage: [ P._affine_coordinates(v) for v in P.Vrep_generator() ]4069[(0, 0), (0, 1), (1, 0)]4070"""4071if '_affine_coordinates_pivots' not in self.__dict__:4072v_list = [ vector(v) for v in self.Vrepresentation() ]4073if len(v_list)>0:4074origin = v_list[0]4075v_list = [ v - origin for v in v_list ]4076coordinates = matrix(v_list)4077self._affine_coordinates_pivots = coordinates.pivots()40784079v = list(Vrep_object)4080if len(v) != self.ambient_dim():4081raise ValueError('Incorrect dimension: '+str(v))40824083return vector(self.base_ring(), [ v[i] for i in self._affine_coordinates_pivots ])40844085@cached_method4086def restricted_automorphism_group(self):4087r"""4088Return the restricted automorphism group.40894090First, let the linear automorphism group be the subgroup of4091the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`4092preserving the `d`-dimensional polyhedron. The Euclidean group4093acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the4094ambient space.40954096The restricted automorphism group is the subgroup of the linear4097automorphism group generated by permutations of the generators4098of the same type. That is, vertices can only be permuted with4099vertices, ray generators with ray generators, and line4100generators with line generators.41014102For example, take the first quadrant41034104.. MATH::41054106Q = \Big\{ (x,y) \Big| x\geq 0,\; y\geq0 \Big\}4107\subset \QQ^241084109Then the linear automorphism group is41104111.. MATH::41124113\mathrm{Aut}(Q) =4114\left\{4115\begin{pmatrix}4116a & 0 \\ 0 & b4117\end{pmatrix}4118,~4119\begin{pmatrix}41200 & c \\ d & 04121\end{pmatrix}4122:~4123a, b, c, d \in \QQ_{>0}4124\right\}4125\subset4126GL(2,\QQ)4127\subset4128E(d)41294130Note that there are no translations that map the quadrant `Q`4131to itself, so the linear automorphism group is contained in4132the subgroup of rotations of the whole Euclidean group. The4133restricted automorphism group is41344135.. MATH::41364137\mathrm{Aut}(Q) =4138\left\{4139\begin{pmatrix}41401 & 0 \\ 0 & 14141\end{pmatrix}4142,~4143\begin{pmatrix}41440 & 1 \\ 1 & 04145\end{pmatrix}4146\right\}4147\simeq \ZZ_241484149OUTPUT:41504151A :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`4152that is isomorphic to the restricted automorphism group is4153returned.41544155Note that in Sage, permutation groups always act on positive4156integers while ``self.Vrepresentation()`` is indexed by4157nonnegative integers. The indexing of the permutation group is4158chosen to be shifted by ``+1``. That is, ``i`` in the4159permutation group corresponds to the V-representation object4160``self.Vrepresentation(i-1)``.41614162REFERENCES:41634164.. [BSS]4165David Bremner, Mathieu Dutour Sikiric, Achill Schuermann:4166Polyhedral representation conversion up to symmetries.4167http://arxiv.org/abs/math/070223941684169EXAMPLES::41704171sage: P = polytopes.cross_polytope(3)4172sage: AutP = P.restricted_automorphism_group(); AutP4173Permutation Group with generators [(3,4), (2,3)(4,5), (2,5), (1,2)(5,6), (1,6)]4174sage: P24 = polytopes.twenty_four_cell()4175sage: AutP24 = P24.restricted_automorphism_group()4176sage: PermutationGroup([4177... '(3,6)(4,7)(10,11)(14,15)(18,21)(19,22)',4178... '(2,3)(7,8)(11,12)(13,14)(17,18)(22,23)',4179... '(2,5)(3,10)(6,11)(8,17)(9,13)(12,16)(14,19)(15,22)(20,23)',4180... '(2,10)(3,5)(6,12)(7,18)(9,14)(11,16)(13,19)(15,23)(20,22)',4181... '(2,11)(3,12)(4,21)(5,6)(9,15)(10,16)(13,22)(14,23)(19,20)',4182... '(1,2)(3,4)(6,7)(8,9)(12,13)(16,17)(18,19)(21,22)(23,24)',4183... '(1,24)(2,13)(3,14)(5,9)(6,15)(10,19)(11,22)(12,23)(16,20)'4184... ]) == AutP244185True41864187Here is the quadrant example mentioned in the beginning::41884189sage: P = Polyhedron(rays=[(1,0),(0,1)])4190sage: P.Vrepresentation()4191(A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0))4192sage: P.restricted_automorphism_group()4193Permutation Group with generators [(2,3)]41944195Also, the polyhedron need not be full-dimensional::41964197sage: P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)])4198sage: P.restricted_automorphism_group()4199Permutation Group with generators [(1,2)]42004201Translations do not change the restricted automorphism4202group. For example, any non-degenerate triangle has the4203dihedral group with 6 elements, `D_6`, as its automorphism4204group::42054206sage: initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])]4207sage: points = initial_points4208sage: Polyhedron(vertices=points).restricted_automorphism_group()4209Permutation Group with generators [(2,3), (1,2)]4210sage: points = [pt - initial_points[0] for pt in initial_points]4211sage: Polyhedron(vertices=points).restricted_automorphism_group()4212Permutation Group with generators [(2,3), (1,2)]4213sage: points = [pt - initial_points[1] for pt in initial_points]4214sage: Polyhedron(vertices=points).restricted_automorphism_group()4215Permutation Group with generators [(2,3), (1,2)]4216sage: points = [pt - 2*initial_points[1] for pt in initial_points]4217sage: Polyhedron(vertices=points).restricted_automorphism_group()4218Permutation Group with generators [(2,3), (1,2)]42194220Floating-point computations are supported with a simple fuzzy4221zero implementation::42224223sage: P = Polyhedron(vertices=[(1.0/3.0,0,0),(0,1.0/3.0,0),(0,0,1.0/3.0)], base_ring=RDF)4224sage: P.restricted_automorphism_group()4225Permutation Group with generators [(2,3), (1,2)]42264227TESTS::42284229sage: p = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])4230sage: p.restricted_automorphism_group()4231Permutation Group with generators [(2,3)]4232"""4233from sage.groups.perm_gps.permgroup import PermutationGroup42344235if self.base_ring() is ZZ or self.base_ring() is QQ:4236def rational_approximation(c):4237return c42384239elif self.base_ring() is RDF:4240c_list = []4241def rational_approximation(c):4242# Implementation detail: Return unique integer if two4243# c-values are the same up to machine precision. But4244# you can think of it as a uniquely-chosen rational4245# approximation.4246for i,x in enumerate(c_list):4247if self._is_zero(x-c):4248return i4249c_list.append(c)4250return len(c_list)-142514252# The algorithm identifies the restricted automorphism group4253# with the automorphism group of a edge-colored graph. The4254# nodes of the graph are the V-representation objects. If all4255# V-representation objects are vertices, the edges are4256# labelled by numbers (to be computed below). Roughly4257# speaking, the edge label is the inner product of the4258# coordinate vectors with some orthogonalization thrown in4259# [BSS].4260def edge_label_compact(i,j,c_ij):4261return c_ij42624263# In the non-compact case we also label the edges by the type4264# of the V-representation object. This ensures that vertices,4265# rays, and lines are only permuted amongst themselves.4266def edge_label_noncompact(i,j,c_ij):4267return (self.Vrepresentation(i).type(), c_ij, self.Vrepresentation(j).type())42684269if self.is_compact():4270edge_label = edge_label_compact4271else:4272edge_label = edge_label_noncompact42734274# good coordinates for the V-representation objects4275v_list = []4276for v in self.Vrepresentation():4277v_coords = list(self._affine_coordinates(v))4278if v.is_vertex():4279v_coords = [1]+v_coords4280else:4281v_coords = [0]+v_coords4282v_list.append(vector(v_coords))42834284# Finally, construct the graph4285Qinv = sum( v.column() * v.row() for v in v_list ).inverse()4286G = Graph()4287for i in range(0,len(v_list)):4288for j in range(i+1,len(v_list)):4289v_i = v_list[i]4290v_j = v_list[j]4291c_ij = rational_approximation( v_i * Qinv * v_j )4292G.add_edge(i+1,j+1, edge_label(i,j,c_ij))42934294group = G.automorphism_group(edge_labels=True)4295self._restricted_automorphism_group = group4296return group4297429842994300430143024303