diff --git a/src/sage/geometry/abc.pyx b/src/sage/geometry/abc.pyx index 199db3ec6eb..4db85b7ace8 100644 --- a/src/sage/geometry/abc.pyx +++ b/src/sage/geometry/abc.pyx @@ -72,7 +72,7 @@ class Polyhedron: By design, there is a unique direct subclass:: sage: sage.geometry.abc.Polyhedron.__subclasses__() # optional - sage.geometry.polyhedron - [] + [] sage: len(sage.geometry.abc.Polyhedron.__subclasses__()) <= 1 True diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d78d1357c5..6b5bf1b5ee0 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,11 +51,10 @@ from sage.arith.misc import integer_ceil as ceil from sage.misc.lazy_import import lazy_import lazy_import('sage.groups.matrix_gps.finitely_generated', 'MatrixGroup') -from sage.geometry.convex_set import ConvexSet_closed, AffineHullProjectionData +from sage.geometry.convex_set import AffineHullProjectionData from .constructor import Polyhedron -from .base0 import Polyhedron_base0 -from sage.geometry.relative_interior import RelativeInterior +from .base1 import Polyhedron_base1 from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -101,7 +100,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Polyhedron_base0, ConvexSet_closed): +class Polyhedron_base(Polyhedron_base1): """ Base class for Polyhedron objects @@ -185,7 +184,7 @@ def _init_empty_polyhedron(self): sage: Polyhedron().facet_adjacency_matrix() [0] """ - Polyhedron_base0._init_empty_polyhedron(self) + Polyhedron_base1._init_empty_polyhedron(self) V_matrix = matrix(ZZ, 0, 0, 0) V_matrix.set_immutable() @@ -195,32 +194,6 @@ def _init_empty_polyhedron(self): H_matrix.set_immutable() self.facet_adjacency_matrix.set_cache(H_matrix) - def __hash__(self): - r""" - TESTS:: - - sage: K. = QuadraticField(2) - sage: p = Polyhedron(vertices=[(0,1,a),(3,a,5)], - ....: rays=[(a,2,3), (0,0,1)], - ....: base_ring=K) - sage: q = Polyhedron(vertices=[(3,a,5),(0,1,a)], - ....: rays=[(0,0,1), (a,2,3)], - ....: base_ring=K) - sage: hash(p) == hash(q) - True - """ - # TODO: find something better *but* fast - return hash((self.dim(), - self.ambient_dim(), - self.n_Hrepresentation(), - self.n_Vrepresentation(), - self.n_equations(), - self.n_facets(), - self.n_inequalities(), - self.n_lines(), - self.n_rays(), - self.n_vertices())) - def _sage_input_(self, sib, coerced): """ Return Sage command to reconstruct ``self``. @@ -907,59 +880,6 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color, facet_color, opacity, vertex_color, axis) - def _repr_(self): - """ - Return a description of the polyhedron. - - EXAMPLES:: - - sage: poly_test = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]]) - sage: poly_test._repr_() - 'A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices' - sage: grammar_test = Polyhedron(vertices = [[1,1,1,1,1,1]]) - sage: grammar_test._repr_() - 'A 0-dimensional polyhedron in ZZ^6 defined as the convex hull of 1 vertex' - """ - desc = '' - if self.n_vertices() == 0: - desc += 'The empty polyhedron' - else: - desc += 'A ' + repr(self.dim()) + '-dimensional polyhedron' - desc += ' in ' - desc += self.parent()._repr_ambient_module() - - if self.n_vertices() > 0: - desc += ' defined as the convex hull of ' - desc += repr(self.n_vertices()) - if self.n_vertices() == 1: - desc += ' vertex' - else: - desc += ' vertices' - - if self.n_rays() > 0: - if self.n_lines() > 0: - desc += ", " - else: - desc += " and " - desc += repr(self.n_rays()) - if self.n_rays() == 1: - desc += ' ray' - else: - desc += ' rays' - - if self.n_lines() > 0: - if self.n_rays() > 0: - desc += ", " - else: - desc += " and " - desc += repr(self.n_lines()) - if self.n_lines() == 1: - desc += ' line' - else: - desc += ' lines' - - return desc - def _rich_repr_(self, display_manager, **kwds): r""" Rich Output Magic Method @@ -1317,91 +1237,6 @@ def vertices_matrix(self, base_ring=None): m.set_immutable() return m - def an_affine_basis(self): - """ - Return points in ``self`` that are a basis for the affine span of the polytope. - - This implementation of the method :meth:`ConvexSet_base.an_affine_basis` - for polytopes guarantees the following: - - - All points are vertices. - - - The basis is obtained by considering a maximal chain of faces - in the face lattice and picking for each cover relation - one vertex that is in the difference. Thus this method - is independent of the concrete realization of the polytope. - - EXAMPLES:: - - sage: P = polytopes.cube() - sage: P.an_affine_basis() - [A vertex at (-1, -1, -1), - A vertex at (1, -1, -1), - A vertex at (1, -1, 1), - A vertex at (1, 1, -1)] - - sage: P = polytopes.permutahedron(5) - sage: P.an_affine_basis() - [A vertex at (1, 2, 3, 5, 4), - A vertex at (2, 1, 3, 5, 4), - A vertex at (1, 3, 2, 5, 4), - A vertex at (4, 1, 3, 5, 2), - A vertex at (4, 2, 5, 3, 1)] - - The method is not implemented for unbounded polyhedra:: - - sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) - sage: p.an_affine_basis() - Traceback (most recent call last): - ... - NotImplementedError: this function is not implemented for unbounded polyhedra - """ - if not self.is_compact(): - raise NotImplementedError("this function is not implemented for unbounded polyhedra") - - chain = self.a_maximal_chain()[1:] # we exclude the empty face - chain_indices = [face.ambient_V_indices() for face in chain] - basis_indices = [] - - # We use in the following that elements in ``chain_indices`` are sorted lists - # of V-indices. - # Thus for each two faces we can easily find the first vertex that differs. - for dim, face in enumerate(chain_indices): - if dim == 0: - # Append the vertex. - basis_indices.append(face[0]) - continue - - prev_face = chain_indices[dim-1] - for i in range(len(prev_face)): - if prev_face[i] != face[i]: - # We found a vertex that ``face`` has, but its facet does not. - basis_indices.append(face[i]) - break - else: # no break - # ``prev_face`` contains all the same vertices as ``face`` until now. - # But ``face`` is guaranteed to contain one more vertex (at least). - basis_indices.append(face[len(prev_face)]) - - return [self.Vrepresentation()[i] for i in basis_indices] - - def _test_an_affine_basis(self, tester=None, **options): - """ - Run tests on the method :meth:`.an_affine_basis` - - TESTS:: - - sage: polytopes.cross_polytope(3)._test_an_affine_basis() - """ - if tester is None: - tester = self._tester(**options) - if self.is_compact(): - b = self.an_affine_basis() - m = matrix([1] + list(v) for v in b) - tester.assertEqual(m.rank(), self.dim() + 1) - for v in b: - tester.assertIn(v, self.vertices()) - def bounded_edges(self): """ Return the bounded edges (excluding rays and lines). @@ -1429,171 +1264,6 @@ def bounded_edges(self): continue yield (obj[i], obj[j]) - def ambient_dim(self): - r""" - Return the dimension of the ambient space. - - EXAMPLES:: - - sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) - sage: poly_test.ambient_dim() - 4 - """ - return self.parent().ambient_dim() - - def dim(self): - """ - Return the dimension of the polyhedron. - - OUTPUT: - - -1 if the polyhedron is empty, otherwise a non-negative integer. - - EXAMPLES:: - - sage: simplex = Polyhedron(vertices = [[1,0,0,0],[0,0,0,1],[0,1,0,0],[0,0,1,0]]) - sage: simplex.dim() - 3 - sage: simplex.ambient_dim() - 4 - - The empty set is a special case (:trac:`12193`):: - - sage: P1=Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]) - sage: P2=Polyhedron(vertices=[[2,0,0],[0,2,0],[0,0,2]]) - sage: P12 = P1.intersection(P2) - sage: P12 - The empty polyhedron in ZZ^3 - sage: P12.dim() - -1 - """ - if self.n_Vrepresentation() == 0: - return -1 # the empty set - else: - return self.ambient_dim() - self.n_equations() - - dimension = dim - - def Vrepresentation_space(self): - r""" - Return the ambient free module. - - OUTPUT: - - A free module over the base ring of dimension :meth:`ambient_dim`. - - EXAMPLES:: - - sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) - sage: poly_test.Vrepresentation_space() - Ambient free module of rank 4 over the principal ideal domain Integer Ring - sage: poly_test.ambient_space() is poly_test.Vrepresentation_space() - True - """ - return self.parent().Vrepresentation_space() - - ambient_space = Vrepresentation_space - - def ambient_vector_space(self, base_field=None): - r""" - Return the ambient vector space. - - It is the ambient free module (:meth:`Vrepresentation_space`) tensored - with a field. - - INPUT: - - - ``base_field`` -- (default: the fraction field of the base ring) a field. - - EXAMPLES:: - - sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) - sage: poly_test.ambient_vector_space() - Vector space of dimension 4 over Rational Field - sage: poly_test.ambient_vector_space() is poly_test.ambient() - True - - sage: poly_test.ambient_vector_space(AA) - Vector space of dimension 4 over Algebraic Real Field - sage: poly_test.ambient_vector_space(RR) - Vector space of dimension 4 over Real Field with 53 bits of precision - sage: poly_test.ambient_vector_space(SR) - Vector space of dimension 4 over Symbolic Ring - """ - return self.Vrepresentation_space().vector_space(base_field=base_field) - - ambient = ambient_vector_space - - def Hrepresentation_space(self): - r""" - Return the linear space containing the H-representation vectors. - - OUTPUT: - - A free module over the base ring of dimension :meth:`ambient_dim` + 1. - - EXAMPLES:: - - sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) - sage: poly_test.Hrepresentation_space() - Ambient free module of rank 5 over the principal ideal domain Integer Ring - """ - return self.parent().Hrepresentation_space() - - def is_empty(self): - """ - Test whether the polyhedron is the empty polyhedron - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P - A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: P.is_empty(), P.is_universe() - (False, False) - - sage: Q = Polyhedron(vertices=()); Q - The empty polyhedron in ZZ^0 - sage: Q.is_empty(), Q.is_universe() - (True, False) - - sage: R = Polyhedron(lines=[(1,0),(0,1)]); R - A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines - sage: R.is_empty(), R.is_universe() - (False, True) - """ - return self.n_Vrepresentation() == 0 - - def is_universe(self): - """ - Test whether the polyhedron is the whole ambient space - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P - A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: P.is_empty(), P.is_universe() - (False, False) - - sage: Q = Polyhedron(vertices=()); Q - The empty polyhedron in ZZ^0 - sage: Q.is_empty(), Q.is_universe() - (True, False) - - sage: R = Polyhedron(lines=[(1,0),(0,1)]); R - A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines - sage: R.is_empty(), R.is_universe() - (False, True) - """ - return self.n_Hrepresentation() == 0 - @cached_method def vertex_adjacency_matrix(self): """ @@ -2175,74 +1845,6 @@ def centroid(self, engine='auto', **kwds): pass return centroid - @cached_method - def representative_point(self): - """ - Return a "generic" point. - - .. SEEALSO:: - - :meth:`center`. - - OUTPUT: - - A point as a coordinate vector. The point is chosen to be - interior if possible. If the polyhedron is not - full-dimensional, the point is in the relative interior. If - the polyhedron is zero-dimensional, its single point is - returned. - - EXAMPLES:: - - sage: p = Polyhedron(vertices=[(3,2)], rays=[(1,-1)]) - sage: p.representative_point() - (4, 1) - sage: p.center() - (3, 2) - - sage: Polyhedron(vertices=[(3,2)]).representative_point() - (3, 2) - """ - accumulator = vector(self.base_ring(), [0]*self.ambient_dim()) - for v in self.vertex_generator(): - accumulator += v.vector() - accumulator /= self.n_vertices() - for r in self.ray_generator(): - accumulator += r.vector() - accumulator.set_immutable() - return accumulator - - def _some_elements_(self): - r""" - Generate some points of ``self``. - - If ``self`` is empty, no points are generated; no exception will be raised. - - EXAMPLES:: - - sage: P = polytopes.simplex() - sage: P.an_element() # indirect doctest - (1/4, 1/4, 1/4, 1/4) - sage: P.some_elements() # indirect doctest - [(1/4, 1/4, 1/4, 1/4), - (0, 0, 0, 1), - (0, 0, 1/2, 1/2), - (0, 1/2, 1/4, 1/4), - (1/2, 1/4, 1/8, 1/8)] - """ - if self.is_empty(): - return - yield self.representative_point() - vertex_iter = iter(self.vertex_generator()) - try: - p = next(vertex_iter).vector() - yield vector(p, immutable=True) - for i in range(4): - p = (p + next(vertex_iter).vector()) / 2 - yield vector(p, immutable=True) - except StopIteration: - pass - def a_maximal_chain(self): r""" Return a maximal chain of the face lattice in increasing order. @@ -7945,323 +7547,6 @@ def _integrate_latte_(self, polynomial, **kwds): polynomial, cdd=True, **kwds) - def contains(self, point): - """ - Test whether the polyhedron contains the given ``point``. - - .. SEEALSO:: - - :meth:`interior_contains`, :meth:`relative_interior_contains`. - - INPUT: - - - ``point`` -- coordinates of a point (an iterable) - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[[1,1],[1,-1],[0,0]]) - sage: P.contains( [1,0] ) - True - sage: P.contains( P.center() ) # true for any convex set - True - - As a shorthand, one may use the usual ``in`` operator:: - - sage: P.center() in P - True - sage: [-1,-1] in P - False - - The point need not have coordinates in the same field as the - polyhedron:: - - sage: ray = Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ) - sage: ray.contains([sqrt(2)/3,0]) # irrational coordinates are ok - True - sage: a = var('a') - sage: ray.contains([a,0]) # a might be negative! - False - sage: assume(a>0) - sage: ray.contains([a,0]) - True - sage: ray.contains(['hello', 'kitty']) # no common ring for coordinates - False - - The empty polyhedron needs extra care, see :trac:`10238`:: - - sage: empty = Polyhedron(); empty - The empty polyhedron in ZZ^0 - sage: empty.contains([]) - False - sage: empty.contains([0]) # not a point in QQ^0 - False - sage: full = Polyhedron(vertices=[()]); full - A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex - sage: full.contains([]) - True - sage: full.contains([0]) - False - - TESTS: - - Passing non-iterable objects does not cause an exception, see :trac:`32013`:: - - sage: None in Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ) - False - """ - try: - p = vector(point) - except TypeError: # point not iterable or no common ring for elements - try: - l = len(point) - except TypeError: - return False - if l > 0: - return False - else: - p = vector(self.base_ring(), []) - - if len(p) != self.ambient_dim(): - return False - - for H in self.Hrep_generator(): - if not H.contains(p): - return False - return True - - __contains__ = contains - - @cached_method - def interior(self): - """ - The interior of ``self``. - - OUTPUT: - - - either an empty polyhedron or an instance of - :class:`~sage.geometry.relative_interior.RelativeInterior` - - EXAMPLES: - - If the polyhedron is full-dimensional, the result is the - same as that of :meth:`relative_interior`:: - - sage: P_full = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) - sage: P_full.interior() - Relative interior of - a 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices - - If the polyhedron is of strictly smaller dimension than the - ambient space, its interior is empty:: - - sage: P_lower = Polyhedron(vertices=[[0,1], [0,-1]]) - sage: P_lower.interior() - The empty polyhedron in ZZ^2 - - TESTS:: - - sage: Empty = Polyhedron(ambient_dim=2); Empty - The empty polyhedron in ZZ^2 - sage: Empty.interior() is Empty - True - """ - if self.is_open(): - return self - if not self.is_full_dimensional(): - return self.parent().element_class(self.parent(), None, None) - return self.relative_interior() - - def interior_contains(self, point): - """ - Test whether the interior of the polyhedron contains the - given ``point``. - - .. SEEALSO:: - - :meth:`contains`, :meth:`relative_interior_contains`. - - INPUT: - - - ``point`` -- coordinates of a point - - OUTPUT: - - ``True`` or ``False``. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) - sage: P.contains( [1,0] ) - True - sage: P.interior_contains( [1,0] ) - False - - If the polyhedron is of strictly smaller dimension than the - ambient space, its interior is empty:: - - sage: P = Polyhedron(vertices=[[0,1],[0,-1]]) - sage: P.contains( [0,0] ) - True - sage: P.interior_contains( [0,0] ) - False - - The empty polyhedron needs extra care, see :trac:`10238`:: - - sage: empty = Polyhedron(); empty - The empty polyhedron in ZZ^0 - sage: empty.interior_contains([]) - False - """ - try: - p = vector(point) - except TypeError: # point not iterable or no common ring for elements - try: - l = len(point) - except TypeError: - return False - if l > 0: - return False - else: - p = vector(self.base_ring(), []) - - if len(p) != self.ambient_dim(): - return False - - for H in self.Hrep_generator(): - if not H.interior_contains(p): - return False - return True - - def is_relatively_open(self): - r""" - Return whether ``self`` is relatively open. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (-1,0)]); P - A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices - sage: P.is_relatively_open() - False - - sage: P0 = Polyhedron(vertices=[[1, 2]]); P0 - A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex - sage: P0.is_relatively_open() - True - - sage: Empty = Polyhedron(ambient_dim=2); Empty - The empty polyhedron in ZZ^2 - sage: Empty.is_relatively_open() - True - - sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]); Line - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line - sage: Line.is_relatively_open() - True - - """ - return not self.inequalities() - - @cached_method - def relative_interior(self): - """ - Return the relative interior of ``self``. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) - sage: ri_P = P.relative_interior(); ri_P - Relative interior of - a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices - sage: (0, 0) in ri_P - True - sage: (1, 0) in ri_P - False - - sage: P0 = Polyhedron(vertices=[[1, 2]]) - sage: P0.relative_interior() is P0 - True - - sage: Empty = Polyhedron(ambient_dim=2) - sage: Empty.relative_interior() is Empty - True - - sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]) - sage: Line.relative_interior() is Line - True - """ - if self.is_relatively_open(): - return self - return RelativeInterior(self) - - def relative_interior_contains(self, point): - """ - Test whether the relative interior of the polyhedron - contains the given ``point``. - - .. SEEALSO:: - - :meth:`contains`, :meth:`interior_contains`. - - INPUT: - - - ``point`` -- coordinates of a point - - OUTPUT: - - ``True`` or ``False`` - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) - sage: P.contains( (0,0) ) - True - sage: P.interior_contains( (0,0) ) - False - sage: P.relative_interior_contains( (0,0) ) - True - sage: P.relative_interior_contains( (1,0) ) - False - - The empty polyhedron needs extra care, see :trac:`10238`:: - - sage: empty = Polyhedron(); empty - The empty polyhedron in ZZ^0 - sage: empty.relative_interior_contains([]) - False - """ - try: - p = vector(point) - except TypeError: # point not iterable or no common ring for elements - try: - l = len(point) - except TypeError: - return False - if l > 0: - return False - else: - p = vector(self.base_ring(), []) - - if len(p) != self.ambient_dim(): - return False - - for eq in self.equation_generator(): - if not eq.contains(p): - return False - - for ine in self.inequality_generator(): - if not ine.interior_contains(p): - return False - - return True - def is_simplex(self): r""" Return whether the polyhedron is a simplex. diff --git a/src/sage/geometry/polyhedron/base1.py b/src/sage/geometry/polyhedron/base1.py new file mode 100644 index 00000000000..d580cb8924e --- /dev/null +++ b/src/sage/geometry/polyhedron/base1.py @@ -0,0 +1,802 @@ +r""" +Base class for polyhedra, part 1 + +Define methods that exist for convex sets, +but not constructions such as dilation or product. +""" + +# **************************************************************************** +# Copyright (C) 2008-2012 Marshall Hampton +# Copyright (C) 2011-2015 Volker Braun +# Copyright (C) 2012-2018 Frederic Chapoton +# Copyright (C) 2013 Andrey Novoseltsev +# Copyright (C) 2014-2017 Moritz Firsching +# Copyright (C) 2014-2019 Thierry Monteil +# Copyright (C) 2015 Nathann Cohen +# Copyright (C) 2015-2017 Jeroen Demeyer +# Copyright (C) 2015-2017 Vincent Delecroix +# Copyright (C) 2015-2018 Dima Pasechnik +# Copyright (C) 2015-2020 Jean-Philippe Labbe +# Copyright (C) 2015-2021 Matthias Koeppe +# Copyright (C) 2016-2019 Daniel Krenn +# Copyright (C) 2017 Marcelo Forets +# Copyright (C) 2017-2018 Mark Bell +# Copyright (C) 2019 Julian Ritter +# Copyright (C) 2019-2020 Laith Rastanawi +# Copyright (C) 2019-2020 Sophia Elia +# Copyright (C) 2019-2021 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.modules.free_module_element import vector +from .base0 import Polyhedron_base0 +from sage.geometry.convex_set import ConvexSet_closed +from sage.geometry.relative_interior import RelativeInterior + + +class Polyhedron_base1(Polyhedron_base0, ConvexSet_closed): + """ + Convex set methods for polyhedra, + but not constructions such as dilation or product. + + See :class:`sage.geometry.polyhedron.base.Polyhedron_base`. + + TESTS:: + + sage: from sage.geometry.polyhedron.base1 import Polyhedron_base1 + sage: P = polytopes.cube() + sage: Q = polytopes.cube() + sage: Polyhedron_base1.__hash__(P) == Polyhedron_base1.__hash__(Q) + True + sage: Polyhedron_base1.__repr__(P) + 'A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices' + sage: Polyhedron_base1.is_empty(P) + False + sage: Polyhedron_base1.is_universe(P) + False + sage: Polyhedron_base1.dim(P) + 3 + sage: Polyhedron_base1.ambient_vector_space(P) + Vector space of dimension 3 over Rational Field + sage: Polyhedron_base1.ambient_dim(P) + 3 + sage: Polyhedron_base1.an_affine_basis(P) + [A vertex at (-1, -1, -1), + A vertex at (1, -1, -1), + A vertex at (1, -1, 1), + A vertex at (1, 1, -1)] + sage: list(Polyhedron_base1._some_elements_(P)) + [(0, 0, 0), + (1, -1, -1), + (1, 0, -1), + (1, 1/2, 0), + (1, -1/4, 1/2), + (0, -5/8, 3/4)] + sage: Polyhedron_base1.contains(P, vector([1, 1, 1])) + True + sage: Polyhedron_base1.interior_contains(P, vector([1, 1, 1])) + False + sage: Polyhedron_base1.is_relatively_open(P) + False + sage: Polyhedron_base1.relative_interior.f(P) == Polyhedron_base1.interior.f(P) + True + """ + + def __hash__(self): + r""" + TESTS:: + + sage: K. = QuadraticField(2) + sage: p = Polyhedron(vertices=[(0,1,a),(3,a,5)], + ....: rays=[(a,2,3), (0,0,1)], + ....: base_ring=K) + sage: q = Polyhedron(vertices=[(3,a,5),(0,1,a)], + ....: rays=[(0,0,1), (a,2,3)], + ....: base_ring=K) + sage: hash(p) == hash(q) + True + """ + # TODO: find something better *but* fast + return hash((self.dim(), + self.ambient_dim(), + self.n_Hrepresentation(), + self.n_Vrepresentation(), + self.n_equations(), + self.n_facets(), + self.n_inequalities(), + self.n_lines(), + self.n_rays(), + self.n_vertices())) + + def _repr_(self): + """ + Return a description of the polyhedron. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]]) + sage: poly_test._repr_() + 'A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices' + sage: grammar_test = Polyhedron(vertices = [[1,1,1,1,1,1]]) + sage: grammar_test._repr_() + 'A 0-dimensional polyhedron in ZZ^6 defined as the convex hull of 1 vertex' + """ + desc = '' + if self.n_vertices() == 0: + desc += 'The empty polyhedron' + else: + desc += 'A ' + repr(self.dim()) + '-dimensional polyhedron' + desc += ' in ' + desc += self.parent()._repr_ambient_module() + + if self.n_vertices() > 0: + desc += ' defined as the convex hull of ' + desc += repr(self.n_vertices()) + if self.n_vertices() == 1: + desc += ' vertex' + else: + desc += ' vertices' + + if self.n_rays() > 0: + if self.n_lines() > 0: + desc += ", " + else: + desc += " and " + desc += repr(self.n_rays()) + if self.n_rays() == 1: + desc += ' ray' + else: + desc += ' rays' + + if self.n_lines() > 0: + if self.n_rays() > 0: + desc += ", " + else: + desc += " and " + desc += repr(self.n_lines()) + if self.n_lines() == 1: + desc += ' line' + else: + desc += ' lines' + + return desc + + def is_empty(self): + """ + Test whether the polyhedron is the empty polyhedron + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P + A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices + sage: P.is_empty(), P.is_universe() + (False, False) + + sage: Q = Polyhedron(vertices=()); Q + The empty polyhedron in ZZ^0 + sage: Q.is_empty(), Q.is_universe() + (True, False) + + sage: R = Polyhedron(lines=[(1,0),(0,1)]); R + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: R.is_empty(), R.is_universe() + (False, True) + """ + return self.n_Vrepresentation() == 0 + + def is_universe(self): + """ + Test whether the polyhedron is the whole ambient space + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]); P + A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices + sage: P.is_empty(), P.is_universe() + (False, False) + + sage: Q = Polyhedron(vertices=()); Q + The empty polyhedron in ZZ^0 + sage: Q.is_empty(), Q.is_universe() + (True, False) + + sage: R = Polyhedron(lines=[(1,0),(0,1)]); R + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: R.is_empty(), R.is_universe() + (False, True) + """ + return self.n_Hrepresentation() == 0 + + def dim(self): + """ + Return the dimension of the polyhedron. + + OUTPUT: + + -1 if the polyhedron is empty, otherwise a non-negative integer. + + EXAMPLES:: + + sage: simplex = Polyhedron(vertices = [[1,0,0,0],[0,0,0,1],[0,1,0,0],[0,0,1,0]]) + sage: simplex.dim() + 3 + sage: simplex.ambient_dim() + 4 + + The empty set is a special case (:trac:`12193`):: + + sage: P1=Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]) + sage: P2=Polyhedron(vertices=[[2,0,0],[0,2,0],[0,0,2]]) + sage: P12 = P1.intersection(P2) + sage: P12 + The empty polyhedron in ZZ^3 + sage: P12.dim() + -1 + """ + if self.n_Vrepresentation() == 0: + return -1 # the empty set + else: + return self.ambient_dim() - self.n_equations() + + dimension = dim + + def Vrepresentation_space(self): + r""" + Return the ambient free module. + + OUTPUT: + + A free module over the base ring of dimension :meth:`ambient_dim`. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.Vrepresentation_space() + Ambient free module of rank 4 over the principal ideal domain Integer Ring + sage: poly_test.ambient_space() is poly_test.Vrepresentation_space() + True + """ + return self.parent().Vrepresentation_space() + + def Hrepresentation_space(self): + r""" + Return the linear space containing the H-representation vectors. + + OUTPUT: + + A free module over the base ring of dimension :meth:`ambient_dim` + 1. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.Hrepresentation_space() + Ambient free module of rank 5 over the principal ideal domain Integer Ring + """ + return self.parent().Hrepresentation_space() + + ambient_space = Vrepresentation_space + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module (:meth:`Vrepresentation_space`) tensored + with a field. + + INPUT: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.ambient_vector_space() + Vector space of dimension 4 over Rational Field + sage: poly_test.ambient_vector_space() is poly_test.ambient() + True + + sage: poly_test.ambient_vector_space(AA) + Vector space of dimension 4 over Algebraic Real Field + sage: poly_test.ambient_vector_space(RR) + Vector space of dimension 4 over Real Field with 53 bits of precision + sage: poly_test.ambient_vector_space(SR) + Vector space of dimension 4 over Symbolic Ring + """ + return self.Vrepresentation_space().vector_space(base_field=base_field) + + ambient = ambient_vector_space + + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.ambient_dim() + 4 + """ + return self.parent().ambient_dim() + + def an_affine_basis(self): + """ + Return points in ``self`` that are a basis for the affine span of the polytope. + + This implementation of the method :meth:`ConvexSet_base.an_affine_basis` + for polytopes guarantees the following: + + - All points are vertices. + + - The basis is obtained by considering a maximal chain of faces + in the face lattice and picking for each cover relation + one vertex that is in the difference. Thus this method + is independent of the concrete realization of the polytope. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: P.an_affine_basis() + [A vertex at (-1, -1, -1), + A vertex at (1, -1, -1), + A vertex at (1, -1, 1), + A vertex at (1, 1, -1)] + + sage: P = polytopes.permutahedron(5) + sage: P.an_affine_basis() + [A vertex at (1, 2, 3, 5, 4), + A vertex at (2, 1, 3, 5, 4), + A vertex at (1, 3, 2, 5, 4), + A vertex at (4, 1, 3, 5, 2), + A vertex at (4, 2, 5, 3, 1)] + + The method is not implemented for unbounded polyhedra:: + + sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) + sage: p.an_affine_basis() + Traceback (most recent call last): + ... + NotImplementedError: this function is not implemented for unbounded polyhedra + """ + if not self.is_compact(): + raise NotImplementedError("this function is not implemented for unbounded polyhedra") + + chain = self.a_maximal_chain()[1:] # we exclude the empty face + chain_indices = [face.ambient_V_indices() for face in chain] + basis_indices = [] + + # We use in the following that elements in ``chain_indices`` are sorted lists + # of V-indices. + # Thus for each two faces we can easily find the first vertex that differs. + for dim, face in enumerate(chain_indices): + if dim == 0: + # Append the vertex. + basis_indices.append(face[0]) + continue + + prev_face = chain_indices[dim-1] + for i in range(len(prev_face)): + if prev_face[i] != face[i]: + # We found a vertex that ``face`` has, but its facet does not. + basis_indices.append(face[i]) + break + else: # no break + # ``prev_face`` contains all the same vertices as ``face`` until now. + # But ``face`` is guaranteed to contain one more vertex (at least). + basis_indices.append(face[len(prev_face)]) + + return [self.Vrepresentation()[i] for i in basis_indices] + + @abstract_method + def a_maximal_chain(self): + r""" + Return a maximal chain of the face lattice in increasing order. + + Subclasses must provide an implementation of this method. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.base1 import Polyhedron_base1 + sage: P = polytopes.cube() + sage: Polyhedron_base1.a_maximal_chain + + """ + + @cached_method + def representative_point(self): + """ + Return a "generic" point. + + .. SEEALSO:: + + :meth:`center`. + + OUTPUT: + + A point as a coordinate vector. The point is chosen to be + interior if possible. If the polyhedron is not + full-dimensional, the point is in the relative interior. If + the polyhedron is zero-dimensional, its single point is + returned. + + EXAMPLES:: + + sage: p = Polyhedron(vertices=[(3,2)], rays=[(1,-1)]) + sage: p.representative_point() + (4, 1) + sage: p.center() + (3, 2) + + sage: Polyhedron(vertices=[(3,2)]).representative_point() + (3, 2) + """ + accumulator = vector(self.base_ring(), [0]*self.ambient_dim()) + for v in self.vertex_generator(): + accumulator += v.vector() + accumulator /= self.n_vertices() + for r in self.ray_generator(): + accumulator += r.vector() + accumulator.set_immutable() + return accumulator + + def _some_elements_(self): + r""" + Generate some points of ``self``. + + If ``self`` is empty, no points are generated; no exception will be raised. + + EXAMPLES:: + + sage: P = polytopes.simplex() + sage: P.an_element() # indirect doctest + (1/4, 1/4, 1/4, 1/4) + sage: P.some_elements() # indirect doctest + [(1/4, 1/4, 1/4, 1/4), + (0, 0, 0, 1), + (0, 0, 1/2, 1/2), + (0, 1/2, 1/4, 1/4), + (1/2, 1/4, 1/8, 1/8)] + """ + if self.is_empty(): + return + yield self.representative_point() + vertex_iter = iter(self.vertex_generator()) + try: + p = next(vertex_iter).vector() + yield vector(p, immutable=True) + for i in range(4): + p = (p + next(vertex_iter).vector()) / 2 + yield vector(p, immutable=True) + except StopIteration: + pass + + def contains(self, point): + """ + Test whether the polyhedron contains the given ``point``. + + .. SEEALSO:: + + :meth:`interior_contains`, :meth:`relative_interior_contains`. + + INPUT: + + - ``point`` -- coordinates of a point (an iterable) + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[[1,1],[1,-1],[0,0]]) + sage: P.contains( [1,0] ) + True + sage: P.contains( P.center() ) # true for any convex set + True + + As a shorthand, one may use the usual ``in`` operator:: + + sage: P.center() in P + True + sage: [-1,-1] in P + False + + The point need not have coordinates in the same field as the + polyhedron:: + + sage: ray = Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ) + sage: ray.contains([sqrt(2)/3,0]) # irrational coordinates are ok + True + sage: a = var('a') + sage: ray.contains([a,0]) # a might be negative! + False + sage: assume(a>0) + sage: ray.contains([a,0]) + True + sage: ray.contains(['hello', 'kitty']) # no common ring for coordinates + False + + The empty polyhedron needs extra care, see :trac:`10238`:: + + sage: empty = Polyhedron(); empty + The empty polyhedron in ZZ^0 + sage: empty.contains([]) + False + sage: empty.contains([0]) # not a point in QQ^0 + False + sage: full = Polyhedron(vertices=[()]); full + A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex + sage: full.contains([]) + True + sage: full.contains([0]) + False + + TESTS: + + Passing non-iterable objects does not cause an exception, see :trac:`32013`:: + + sage: None in Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ) + False + """ + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + try: + l = len(point) + except TypeError: + return False + if l > 0: + return False + else: + p = vector(self.base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + for H in self.Hrep_generator(): + if not H.contains(p): + return False + return True + + __contains__ = contains + + @cached_method + def interior(self): + """ + The interior of ``self``. + + OUTPUT: + + - either an empty polyhedron or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior` + + EXAMPLES: + + If the polyhedron is full-dimensional, the result is the + same as that of :meth:`relative_interior`:: + + sage: P_full = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) + sage: P_full.interior() + Relative interior of + a 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices + + If the polyhedron is of strictly smaller dimension than the + ambient space, its interior is empty:: + + sage: P_lower = Polyhedron(vertices=[[0,1], [0,-1]]) + sage: P_lower.interior() + The empty polyhedron in ZZ^2 + + TESTS:: + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.interior() is Empty + True + """ + if self.is_open(): + return self + if not self.is_full_dimensional(): + return self.parent().element_class(self.parent(), None, None) + return self.relative_interior() + + def interior_contains(self, point): + """ + Test whether the interior of the polyhedron contains the + given ``point``. + + .. SEEALSO:: + + :meth:`contains`, :meth:`relative_interior_contains`. + + INPUT: + + - ``point`` -- coordinates of a point + + OUTPUT: + + ``True`` or ``False``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) + sage: P.contains( [1,0] ) + True + sage: P.interior_contains( [1,0] ) + False + + If the polyhedron is of strictly smaller dimension than the + ambient space, its interior is empty:: + + sage: P = Polyhedron(vertices=[[0,1],[0,-1]]) + sage: P.contains( [0,0] ) + True + sage: P.interior_contains( [0,0] ) + False + + The empty polyhedron needs extra care, see :trac:`10238`:: + + sage: empty = Polyhedron(); empty + The empty polyhedron in ZZ^0 + sage: empty.interior_contains([]) + False + """ + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + try: + l = len(point) + except TypeError: + return False + if l > 0: + return False + else: + p = vector(self.base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + for H in self.Hrep_generator(): + if not H.interior_contains(p): + return False + return True + + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]); P + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: P.is_relatively_open() + False + + sage: P0 = Polyhedron(vertices=[[1, 2]]); P0 + A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P0.is_relatively_open() + True + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.is_relatively_open() + True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]); Line + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: Line.is_relatively_open() + True + + """ + return not self.inequalities() + + @cached_method + def relative_interior(self): + """ + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) + sage: ri_P = P.relative_interior(); ri_P + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: (0, 0) in ri_P + True + sage: (1, 0) in ri_P + False + + sage: P0 = Polyhedron(vertices=[[1, 2]]) + sage: P0.relative_interior() is P0 + True + + sage: Empty = Polyhedron(ambient_dim=2) + sage: Empty.relative_interior() is Empty + True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]) + sage: Line.relative_interior() is Line + True + """ + if self.is_relatively_open(): + return self + return RelativeInterior(self) + + def relative_interior_contains(self, point): + """ + Test whether the relative interior of the polyhedron + contains the given ``point``. + + .. SEEALSO:: + + :meth:`contains`, :meth:`interior_contains`. + + INPUT: + + - ``point`` -- coordinates of a point + + OUTPUT: + + ``True`` or ``False`` + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) + sage: P.contains( (0,0) ) + True + sage: P.interior_contains( (0,0) ) + False + sage: P.relative_interior_contains( (0,0) ) + True + sage: P.relative_interior_contains( (1,0) ) + False + + The empty polyhedron needs extra care, see :trac:`10238`:: + + sage: empty = Polyhedron(); empty + The empty polyhedron in ZZ^0 + sage: empty.relative_interior_contains([]) + False + """ + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + try: + l = len(point) + except TypeError: + return False + if l > 0: + return False + else: + p = vector(self.base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + for eq in self.equation_generator(): + if not eq.contains(p): + return False + + for ine in self.inequality_generator(): + if not ine.interior_contains(p): + return False + + return True