From 82ea84cfad2c81080f51bfa03255dcf1362c4488 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 19 Oct 2021 23:44:28 -0700 Subject: [PATCH 01/18] src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py: Use integer division instead of floor(.../...) --- .../endPN_automorphism_group.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index e5ef59acf59..f820d893503 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -39,7 +39,7 @@ from sage.sets.set import Set from sage.combinat.permutation import Arrangements from sage.parallel.use_fork import p_iter_fork -from sage.functions.other import floor + def automorphism_group_QQ_fixedpoints(rational_function, return_functions=False, iso_type=False): r""" @@ -2213,8 +2213,8 @@ def find_conjugations_arrangement(tuples): # and check linear independence in parallel if len(all_subsets) > num_cpus: for i in range(num_cpus): - start = floor(len(all_subsets)*i/num_cpus) - end = floor(len(all_subsets)*(i+1)/num_cpus) + start = (len(all_subsets) * i) // num_cpus + end = (len(all_subsets) * (i+1)) // num_cpus tuples = all_subsets[start:end] parallel_data.append(([tuples], {})) @@ -2242,8 +2242,8 @@ def find_conjugations_arrangement(tuples): all_arrangements += list(product(*subset_arrangements)) parallel_data = [] for i in range(num_cpus): - start = floor(len(all_arrangements)*i/num_cpus) - end = floor(len(all_arrangements)*(i+1)/num_cpus) + start = (len(all_arrangements) * i) // num_cpus + end = (len(all_arrangements) * (i+1)) // num_cpus tuples = all_arrangements[start:end] parallel_data.append(([tuples], {})) X = p_iter_fork(num_cpus) @@ -2350,8 +2350,8 @@ def find_conjugations_arrangement(tuples): # and check linear independence in parallel if len(all_subsets) > num_cpus: for i in range(num_cpus): - start = floor(len(all_subsets)*i/num_cpus) - end = floor(len(all_subsets)*(i+1)/num_cpus) + start = (len(all_subsets) * i) // num_cpus + end = (len(all_subsets) * (i+1)) // num_cpus tuples = all_subsets[start:end] parallel_data.append(([tuples], {})) @@ -2380,8 +2380,8 @@ def find_conjugations_arrangement(tuples): all_arrangements += list(product(*subset_arrangements)) parallel_data = [] for i in range(num_cpus): - start = floor(len(all_arrangements)*i/num_cpus) - end = floor(len(all_arrangements)*(i+1)/num_cpus) + start = (len(all_arrangements) * i) // num_cpus + end = (len(all_arrangements) * (i+1)) // num_cpus tuples = all_arrangements[start:end] parallel_data.append(([tuples], {})) X = p_iter_fork(num_cpus) From 1b876e41ec68201bf1d1b60461e9ff8a3780b004 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Oct 2021 18:58:02 -0700 Subject: [PATCH 02/18] sage.arith.misc.integer_trun: New, move here from sage.combinat.crystals --- src/sage/arith/misc.py | 18 +++++++++++++++++ src/sage/combinat/crystals/tensor_product.py | 21 +------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 9a2aa40819d..273055ba387 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -5031,6 +5031,24 @@ def integer_floor(x): raise NotImplementedError("computation of floor of %s not implemented"%x) +def integer_trunc(i): + """ + Truncate to the integer closer to zero + + EXAMPLES:: + + sage: from sage.arith.misc import integer_trunc as trunc + sage: trunc(-3/2), trunc(-1), trunc(-1/2), trunc(0), trunc(1/2), trunc(1), trunc(3/2) + (-1, -1, 0, 0, 0, 1, 1) + sage: isinstance(trunc(3/2), Integer) + True + """ + if i >= 0: + return integer_floor(i) + else: + return integer_ceil(i) + + def two_squares(n): """ Write the integer `n` as a sum of two integer squares if possible; diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 9df369573f8..4b8ee8480d8 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -52,27 +52,8 @@ from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model from sage.rings.semirings.non_negative_integer_semiring import NN +from sage.arith.misc import integer_trunc as trunc -############################################################################## -# Until trunc gets implemented in sage.function.other - -from sage.functions.other import floor, ceil -def trunc(i): - """ - Truncates to the integer closer to zero - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import trunc - sage: trunc(-3/2), trunc(-1), trunc(-1/2), trunc(0), trunc(1/2), trunc(1), trunc(3/2) - (-1, -1, 0, 0, 0, 1, 1) - sage: isinstance(trunc(3/2), Integer) - True - """ - if i>= 0: - return floor(i) - else: - return ceil(i) ############################################################################## # Support classes From 8305ac34dd41816e8482a656e67756a345f30bb4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 4 Oct 2021 15:12:22 -0700 Subject: [PATCH 03/18] sage.geometry.polyhedron: Remove use of sage.functions --- src/sage/geometry/polyhedron/backend_normaliz.py | 2 +- src/sage/geometry/polyhedron/base.py | 13 ++++++------- .../polyhedron/combinatorial_polyhedron/base.pyx | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index d1f30e2d31f..58af1b3059c 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1538,7 +1538,7 @@ def _volume_normaliz(self, measure='euclidean'): from sage.rings.infinity import infinity return infinity - from sage.functions.other import factorial + from sage.arith.misc import factorial return self._volume_normaliz('induced_lattice') / factorial(self.dim()) else: diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c07d3ccfe3f..81f8e630e2a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -40,7 +40,7 @@ from sage.misc.misc_c import prod from sage.misc.randstate import current_randstate from sage.misc.superseded import deprecated_function_alias - +y from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA from sage.rings.rational_field import QQ @@ -48,10 +48,9 @@ from sage.modules.free_module_element import vector from sage.modules.vector_space_morphism import linear_transformation from sage.matrix.constructor import matrix -from sage.functions.other import floor, ceil -from sage.misc.functional import sqrt -from sage.groups.matrix_gps.finitely_generated import MatrixGroup -from sage.graphs.graph import Graph +from sage.arith.misc import integer_floor as floor +from sage.arith.misc import integer_ceil as ceil +lazy_import('sage.groups.matrix_gps.finitely_generated', 'MatrixGroup') from sage.geometry.convex_set import ConvexSet_closed, AffineHullProjectionData from .constructor import Polyhedron @@ -3418,7 +3417,7 @@ def radius(self): sage: p.radius() 2 """ - return sqrt(self.radius_square()) + return self.radius_square().sqrt() def is_inscribed(self, certificate=False): """ @@ -8999,7 +8998,7 @@ def integrate(self, function, measure='ambient', **kwds): Adet = AA.coerce(Adet) except TypeError: pass - return I / sqrt(Adet) + return I / Adet.sqrt() else: raise ValueError('unknown measure "{}"'.format(measure)) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 524933a01a8..c63b8a943bd 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1854,7 +1854,7 @@ cdef class CombinatorialPolyhedron(SageObject): if self.is_simplex(): return self.dim() + 1 else: - from sage.functions.other import binomial + from sage.arith.misc import binomial k = 1 while self.f_vector()[k+1] == binomial(self.n_vertices(), k + 1): k += 1 @@ -1904,7 +1904,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C.is_neighborly(k=2) True """ - from sage.functions.other import binomial + from sage.arith.misc import binomial if k is None: k = self.dim() // 2 return all(self.f_vector()[i+1] == binomial(self.n_vertices(), i + 1) From 00f5615f14bf34dc82b377a2c6b7cd4a32fc0e3b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Oct 2021 19:08:02 -0700 Subject: [PATCH 04/18] src/sage/graphs/hyperbolicity.pyx: Remove unused import --- src/sage/graphs/hyperbolicity.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 1f479774b7e..44e33930181 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -158,7 +158,6 @@ from sage.graphs.distances_all_pairs cimport c_distances_all_pairs from sage.arith.all import binomial from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR -from sage.functions.other import floor from sage.data_structures.bitset import Bitset from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph From 27b44b9df1de6b0ebbe83eadde4ceba60a096c45 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Oct 2021 22:19:17 -0700 Subject: [PATCH 05/18] src/sage/modular/pollack_stevens/dist.pyx: Remove unused import --- src/sage/modular/pollack_stevens/dist.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 65942454937..b9165153621 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -39,7 +39,6 @@ from sage.matrix.matrix cimport Matrix from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import matrix from sage.misc.prandom import random -from sage.functions.other import floor from sage.structure.element cimport RingElement, Element import operator from sage.rings.padics.padic_generic import pAdicGeneric From 7f1d021ee896adb823e20a34367333a2031b943e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Oct 2021 22:19:36 -0700 Subject: [PATCH 06/18] src/sage/quadratic_forms/ternary.pyx: Remove unused import --- src/sage/quadratic_forms/ternary.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/quadratic_forms/ternary.pyx b/src/sage/quadratic_forms/ternary.pyx index 63b59b82525..80405d61158 100644 --- a/src/sage/quadratic_forms/ternary.pyx +++ b/src/sage/quadratic_forms/ternary.pyx @@ -19,7 +19,6 @@ from sage.arith.all import inverse_mod, xgcd, gcd from sage.quadratic_forms.extras import extend_to_primitive from sage.rings.finite_rings.integer_mod import mod from sage.misc.prandom import randint -from sage.functions.other import ceil, floor def red_mfact(a,b): From d5cfe2a94a61a066cf0fbab4eb1cedd7a62e8886 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Oct 2021 22:20:19 -0700 Subject: [PATCH 07/18] src/sage/combinat/crystals/tensor_product_element.pyx: Remove unused import --- src/sage/combinat/crystals/tensor_product_element.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx index ed3e688ee56..8a6f7043491 100644 --- a/src/sage/combinat/crystals/tensor_product_element.pyx +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -34,7 +34,6 @@ from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from sage.structure.parent cimport Parent from sage.misc.cachefunc import cached_method, cached_in_parent_method -from sage.functions.other import ceil from sage.combinat.tableau import Tableau from sage.rings.integer_ring import ZZ From d825aa8bdcd7c6ef1ae2d9254940921457448829 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Oct 2021 19:10:35 -0700 Subject: [PATCH 08/18] src/sage/combinat/crystals/littelmann_path.py: Use integer_floor instead of sage.functions.other.floor --- src/sage/combinat/crystals/littelmann_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 4efbef4a0c3..04cddcf7278 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -40,7 +40,7 @@ from sage.rings.integer import Integer from sage.rings.rational_field import QQ from sage.combinat.root_system.root_system import RootSystem -from sage.functions.other import floor +from sage.arith.misc import integer_floor as floor from sage.misc.latex import latex From 9c35213ad90d917f46d5875616c3bdc7320da411 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Oct 2021 19:11:01 -0700 Subject: [PATCH 09/18] src/sage/combinat/diagram_algebras.py: Use integer_floor, integer_ceil instead of importing from sage.functions --- src/sage/combinat/diagram_algebras.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 17a9f85bc5f..ea5303bf1bc 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -43,7 +43,8 @@ from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.functions.other import floor, ceil +from sage.arith.misc import integer_floor as floor +from sage.arith.misc import integer_ceil as ceil import itertools From 57551d338a8bc8d105fbdc5cff8bc6e8b42d8608 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Oct 2021 19:11:16 -0700 Subject: [PATCH 10/18] src/sage/graphs/generic_graph.py: Use integer_floor instead of sage.functions.other.floor --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 33f06c06dda..1a1ff3f292d 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -9272,7 +9272,7 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only 0 """ from sage.graphs.digraph import DiGraph - from sage.functions.other import floor + from sage.arith.misc import integer_floor as floor # Whether we should consider the edges labeled if use_edge_labels: From 9a48bd91c5be689844600d7f9f0c3baabbfb0e84 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 25 Oct 2021 18:22:59 +0200 Subject: [PATCH 11/18] outsource initialization of polyhedron into base0.py --- src/sage/geometry/polyhedron/base.py | 1315 ++----------------------- src/sage/geometry/polyhedron/base0.py | 1282 ++++++++++++++++++++++++ 2 files changed, 1344 insertions(+), 1253 deletions(-) create mode 100644 src/sage/geometry/polyhedron/base0.py diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e2e6772defb..78abf572793 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -32,7 +32,7 @@ import itertools -from sage.structure.element import Element, coerce_binop, is_Vector, is_Matrix +from sage.structure.element import coerce_binop, is_Vector, is_Matrix from sage.structure.richcmp import rich_to_bool, op_NE from sage.cpython.string import bytes_to_str @@ -54,6 +54,7 @@ from sage.geometry.convex_set import ConvexSet_closed, AffineHullProjectionData from .constructor import Polyhedron +from .base0 import Polyhedron_base0 from sage.geometry.relative_interior import RelativeInterior from sage.categories.sets_cat import EmptySetError @@ -100,7 +101,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Element, ConvexSet_closed): +class Polyhedron_base(Polyhedron_base0, ConvexSet_closed): """ Base class for Polyhedron objects @@ -173,115 +174,26 @@ class Polyhedron_base(Element, ConvexSet_closed): sage: TestSuite(P).run() """ - def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, mutable=False, **kwds): + def _init_empty_polyhedron(self): """ - Initializes the polyhedron. - - See :class:`Polyhedron_base` for a description of the input - data. + Initializes an empty polyhedron. TESTS:: - sage: p = Polyhedron() # indirect doctests - - sage: from sage.geometry.polyhedron.backend_field import Polyhedron_field - sage: from sage.geometry.polyhedron.parent import Polyhedra_field - sage: parent = Polyhedra_field(AA, 1, 'field') - sage: Vrep = [[[0], [1/2], [1]], [], []] - sage: Hrep = [[[0, 1], [1, -1]], []] - sage: p = Polyhedron_field(parent, Vrep, Hrep, - ....: Vrep_minimal=False, Hrep_minimal=True) - Traceback (most recent call last): - ... - ValueError: if both Vrep and Hrep are provided, they must be minimal... - - Illustration of ``pref_rep``. - Note that ``ppl`` doesn't support precomputed data:: - - sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl - sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_ppl - sage: parent = Polyhedra_QQ_ppl(QQ, 1, 'ppl') - sage: p = Polyhedron_QQ_ppl(parent, Vrep, 'nonsense', - ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') - sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, - ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep') - sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, - ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrepresentation') - Traceback (most recent call last): - ... - ValueError: ``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')`` + sage: Polyhedron().vertex_adjacency_matrix() # indirect doctest + [] + sage: Polyhedron().facet_adjacency_matrix() + [0] + """ + Polyhedron_base0._init_empty_polyhedron(self) - If the backend supports precomputed data, ``pref_rep`` is ignored:: + V_matrix = matrix(ZZ, 0, 0, 0) + V_matrix.set_immutable() + self.vertex_adjacency_matrix.set_cache(V_matrix) - sage: p = Polyhedron_field(parent, Vrep, 'nonsense', - ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') - Traceback (most recent call last): - ... - TypeError: ..._init_Hrepresentation() takes 3 positional arguments but 9 were given - - The empty polyhedron is detected when the Vrepresentation is given with generator; - see :trac:`29899`:: - - sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd - sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_cdd - sage: parent = Polyhedra_QQ_cdd(QQ, 0, 'cdd') - sage: p = Polyhedron_QQ_cdd(parent, [iter([]), iter([]), iter([])], None) - """ - Element.__init__(self, parent=parent) - if Vrep is not None and Hrep is not None: - if not (Vrep_minimal is True and Hrep_minimal is True): - raise ValueError("if both Vrep and Hrep are provided, they must be minimal" - " and Vrep_minimal and Hrep_minimal must both be True") - if hasattr(self, "_init_from_Vrepresentation_and_Hrepresentation"): - self._init_from_Vrepresentation_and_Hrepresentation(Vrep, Hrep) - return - else: - if pref_rep is None: - # Initialize from Hrepresentation if this seems simpler. - Vrep = [tuple(Vrep[0]), tuple(Vrep[1]), Vrep[2]] - Hrep = [tuple(Hrep[0]), Hrep[1]] - if len(Hrep[0]) < len(Vrep[0]) + len(Vrep[1]): - pref_rep = 'Hrep' - else: - pref_rep = 'Vrep' - if pref_rep == 'Vrep': - Hrep = None - elif pref_rep == 'Hrep': - Vrep = None - else: - raise ValueError("``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')``") - if Vrep is not None: - vertices, rays, lines = Vrep - - # We build tuples out of generators now to detect the empty polyhedron. - - # The damage is limited: - # The backend will have to obtain all elements from the generator anyway. - # The generators are mainly for saving time with initializing from - # Vrepresentation and Hrepresentation. - # If we dispose of one of them (see above), it is wasteful to have generated it. - - # E.g. the dilate will be set up with new Vrepresentation and Hrepresentation - # regardless of the backend along with the argument ``pref_rep``. - # As we only use generators, there is no penalty to this approach - # (and the method ``dilation`` does not have to distinguish by backend). - - if not isinstance(vertices, (tuple, list)): - vertices = tuple(vertices) - if not isinstance(rays, (tuple, list)): - rays = tuple(rays) - if not isinstance(lines, (tuple, list)): - lines = tuple(lines) - - if vertices or rays or lines: - self._init_from_Vrepresentation(vertices, rays, lines, **kwds) - else: - self._init_empty_polyhedron() - elif Hrep is not None: - ieqs, eqns = Hrep - self._init_from_Hrepresentation(ieqs, eqns, **kwds) - else: - self._init_empty_polyhedron() + H_matrix = matrix(ZZ, 1, 1, 0) + H_matrix.set_immutable() + self.facet_adjacency_matrix.set_cache(H_matrix) def __hash__(self): r""" @@ -342,93 +254,6 @@ def _sage_input_(self, sib, coerced): kwds['lines'] = [sib(tuple(l)) for l in self.lines()] return sib.name('Polyhedron')(**kwds) - def _init_from_Vrepresentation(self, vertices, rays, lines, **kwds): - """ - Construct polyhedron from V-representation data. - - INPUT: - - - ``vertices`` -- list of point. Each point can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - - - ``rays`` -- list of rays. Each ray can be specified as any - iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - - - ``lines`` -- list of lines. Each line can be specified as - any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - - EXAMPLES:: - - sage: p = Polyhedron() - sage: from sage.geometry.polyhedron.base import Polyhedron_base - sage: Polyhedron_base._init_from_Vrepresentation(p, [], [], []) - Traceback (most recent call last): - ... - NotImplementedError: a derived class must implement this method - """ - raise NotImplementedError('a derived class must implement this method') - - def _init_from_Hrepresentation(self, ieqs, eqns, **kwds): - """ - Construct polyhedron from H-representation data. - - INPUT: - - - ``ieqs`` -- list of inequalities. Each line can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - - - ``eqns`` -- list of equalities. Each line can be specified - as any iterable container of - :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - - EXAMPLES:: - - sage: p = Polyhedron() - sage: from sage.geometry.polyhedron.base import Polyhedron_base - sage: Polyhedron_base._init_from_Hrepresentation(p, [], []) - Traceback (most recent call last): - ... - NotImplementedError: a derived class must implement this method - """ - raise NotImplementedError('a derived class must implement this method') - - def _init_empty_polyhedron(self): - """ - Initializes an empty polyhedron. - - TESTS:: - - sage: empty = Polyhedron(); empty - The empty polyhedron in ZZ^0 - sage: empty.Vrepresentation() - () - sage: empty.Hrepresentation() - (An equation -1 == 0,) - sage: Polyhedron(vertices = []) - The empty polyhedron in ZZ^0 - sage: Polyhedron(vertices = [])._init_empty_polyhedron() - sage: from sage.geometry.polyhedron.parent import Polyhedra - sage: Polyhedra(QQ,7)() - A 0-dimensional polyhedron in QQ^7 defined as the convex hull of 1 vertex - """ - self._Vrepresentation = [] - self._Hrepresentation = [] - self.parent()._make_Equation(self, [-1] + [0] * self.ambient_dim()) - self._Vrepresentation = tuple(self._Vrepresentation) - self._Hrepresentation = tuple(self._Hrepresentation) - - V_matrix = matrix(ZZ, 0, 0, 0) - V_matrix.set_immutable() - self.vertex_adjacency_matrix.set_cache(V_matrix) - - H_matrix = matrix(ZZ, 1, 1, 0) - H_matrix.set_immutable() - self.facet_adjacency_matrix.set_cache(H_matrix) - def _facet_adjacency_matrix(self): """ Compute the facet adjacency matrix in case it has not been @@ -532,126 +357,6 @@ def _test_basic_properties(self, tester=None, **options): if self.n_inequalities() < 40: tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations(), ambient_dim=self.ambient_dim())) - def base_extend(self, base_ring, backend=None): - """ - Return a new polyhedron over a larger base ring. - - This method can also be used to change the backend. - - INPUT: - - - ``base_ring`` -- the new base ring - - - ``backend`` -- the new backend, see - :func:`~sage.geometry.polyhedron.constructor.Polyhedron`. - If ``None`` (the default), attempt to keep the same backend. - Otherwise, use the same defaulting behavior - as described there. - - OUTPUT: - - The same polyhedron, but over a larger base ring and possibly with a changed backend. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ); P - A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray - sage: P.base_extend(QQ) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray - sage: P.base_extend(QQ) == P - True - - TESTS: - - Test that :trac:`22575` is fixed:: - - sage: Q = P.base_extend(ZZ, backend='field') - sage: Q.backend() - 'field' - - """ - new_parent = self.parent().base_extend(base_ring, backend) - return new_parent(self, copy=True) - - def change_ring(self, base_ring, backend=None): - """ - Return the polyhedron obtained by coercing the entries of the - vertices/lines/rays of this polyhedron into the given ring. - - This method can also be used to change the backend. - - INPUT: - - - ``base_ring`` -- the new base ring - - - ``backend`` -- the new backend or ``None`` (default), see - :func:`~sage.geometry.polyhedron.constructor.Polyhedron`. - If ``None`` (the default), attempt to keep the same backend. - Otherwise, use the same defaulting behavior - as described there. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=QQ); P - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray - sage: P.change_ring(ZZ) - A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray - sage: P.change_ring(ZZ) == P - True - - sage: P = Polyhedron(vertices=[(-1.3,0), (0,2.3)], base_ring=RDF); P.vertices() - (A vertex at (-1.3, 0.0), A vertex at (0.0, 2.3)) - sage: P.change_ring(QQ).vertices() - (A vertex at (-13/10, 0), A vertex at (0, 23/10)) - sage: P == P.change_ring(QQ) - True - sage: P.change_ring(ZZ) - Traceback (most recent call last): - ... - TypeError: cannot change the base ring to the Integer Ring - - sage: P = polytopes.regular_polygon(3); P # optional - sage.rings.number_field - A 2-dimensional polyhedron in AA^2 defined as the convex hull of 3 vertices - sage: P.vertices() # optional - sage.rings.number_field - (A vertex at (0.?e-16, 1.000000000000000?), - A vertex at (0.866025403784439?, -0.500000000000000?), - A vertex at (-0.866025403784439?, -0.500000000000000?)) - sage: P.change_ring(QQ) # optional - sage.rings.number_field - Traceback (most recent call last): - ... - TypeError: cannot change the base ring to the Rational Field - - .. WARNING:: - - The base ring ``RDF`` should be used with care. As it is - not an exact ring, certain computations may break or - silently produce wrong results, for example changing the - base ring from an exact ring into ``RDF`` may cause a - loss of data:: - - sage: P = Polyhedron([[2/3,0],[6666666666666667/10^16,0]], base_ring=AA); P # optional - sage.rings.number_field - A 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices - sage: Q = P.change_ring(RDF); Q # optional - sage.rings.number_field - A 0-dimensional polyhedron in RDF^2 defined as the convex hull of 1 vertex - sage: P.n_vertices() == Q.n_vertices() # optional - sage.rings.number_field - False - """ - from sage.categories.rings import Rings - - if base_ring not in Rings(): - raise ValueError("invalid base ring") - - try: - vertices = [[base_ring(x) for x in vertex] for vertex in self.vertices_list()] - rays = [[base_ring(x) for x in ray] for ray in self.rays_list()] - lines = [[base_ring(x) for x in line] for line in self.lines_list()] - - except (TypeError, ValueError): - raise TypeError("cannot change the base ring to the {0}".format(base_ring)) - - new_parent = self.parent().change_ring(base_ring, backend) - return new_parent([vertices, rays, lines], None) - def _richcmp_(self, other, op): """ Compare ``self`` and ``other``. @@ -741,30 +446,6 @@ def _is_subpolyhedron(self, other): for other_H in other.Hrepresentation() for self_V in self.Vrepresentation()) - def is_mutable(self): - r""" - Return True if the polyhedron is mutable, i.e. it can be modified in place. - - EXAMPLES:: - - sage: p = polytopes.cube(backend='field') - sage: p.is_mutable() - False - """ - return False - - def is_immutable(self): - r""" - Return True if the polyhedron is immutable, i.e. it cannot be modified in place. - - EXAMPLES:: - - sage: p = polytopes.cube(backend='field') - sage: p.is_immutable() - True - """ - return True - @cached_method def vertex_facet_graph(self, labels=True): r""" @@ -1226,59 +907,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 @@ -1456,105 +1084,6 @@ def write_cdd_Vrepresentation(self, filename): with open(filename, 'w') as f: f.write(self.cdd_Vrepresentation()) - @cached_method - def n_equations(self): - """ - Return the number of equations. The representation will - always be minimal, so the number of equations is the - codimension of the polyhedron in the ambient space. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]]) - sage: p.n_equations() - 1 - """ - return len(self.equations()) - - @cached_method - def n_inequalities(self): - """ - Return the number of inequalities. The representation will - always be minimal, so the number of inequalities is the - number of facets of the polyhedron in the ambient space. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]]) - sage: p.n_inequalities() - 3 - - sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in range(6)]) - sage: p.n_facets() - 8 - """ - return len(self.inequalities()) - - n_facets = n_inequalities - - @cached_method - def n_vertices(self): - """ - Return the number of vertices. The representation will - always be minimal. - - .. WARNING:: - - If the polyhedron has lines, return the number of vertices in - the ``Vrepresentation``. As the represented polyhedron has - no 0-dimensional faces (i.e. vertices), ``n_vertices`` corresponds - to the number of `k`-faces, where `k` is the number of lines:: - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) - sage: P.n_vertices() - 1 - sage: P.faces(0) - () - sage: P.f_vector() - (1, 0, 1, 1) - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0],[0,1,1]]) - sage: P.n_vertices() - 1 - sage: P.f_vector() - (1, 0, 0, 1, 1) - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[1,0],[0,1],[1,1]], rays=[[1,1]]) - sage: p.n_vertices() - 2 - """ - return len(self.vertices()) - - @cached_method - def n_rays(self): - """ - Return the number of rays. The representation will - always be minimal. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[1,0],[0,1]], rays=[[1,1]]) - sage: p.n_rays() - 1 - """ - return len(self.rays()) - - @cached_method - def n_lines(self): - """ - Return the number of lines. The representation will - always be minimal. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[0,0]], rays=[[0,1],[0,-1]]) - sage: p.n_lines() - 1 - """ - return len(self.lines()) - def to_linear_program(self, solver=None, return_variable=False, base_ring=None): r""" Return a linear optimization problem over the polyhedron in the form of @@ -1674,573 +1203,66 @@ def to_linear_program(self, solver=None, return_variable=False, base_ring=None): else: return p - def Hrepresentation(self, index=None): + @cached_method + def vertices_matrix(self, base_ring=None): """ - Return the objects of the H-representation. Each entry is - either an inequality or a equation. + Return the coordinates of the vertices as the columns of a matrix. INPUT: - - ``index`` -- either an integer or ``None`` + - ``base_ring`` -- A ring or ``None`` (default). The base ring + of the returned matrix. If not specified, the base ring of + the polyhedron is used. OUTPUT: - The optional argument is an index running from ``0`` to - ``self.n_Hrepresentation()-1``. If present, the - H-representation object at the given index will be - returned. Without an argument, returns the list of all - H-representation objects. - - EXAMPLES:: - - sage: p = polytopes.hypercube(3, backend='field') - sage: p.Hrepresentation(0) - An inequality (-1, 0, 0) x + 1 >= 0 - sage: p.Hrepresentation(0) == p.Hrepresentation()[0] - True - """ - if index is None: - return self._Hrepresentation - else: - return self._Hrepresentation[index] + A matrix over ``base_ring`` whose columns are the coordinates + of the vertices. A ``TypeError`` is raised if the coordinates + cannot be converted to ``base_ring``. - def Hrepresentation_str(self, separator='\n', latex=False, style='>=', align=None, **kwds): - r""" - Return a human-readable string representation of the Hrepresentation of this - polyhedron. + .. WARNING:: - INPUT: + If the polyhedron has lines, return the coordinates of the vertices + of the ``Vrepresentation``. However, the represented polyhedron + has no 0-dimensional faces (i.e. vertices):: - - ``separator`` -- a string. Default is ``"\n"``. + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) + sage: P.vertices_matrix() + [0] + [0] + [0] + sage: P.faces(0) + () - - ``latex`` -- a boolean. Default is ``False``. + EXAMPLES:: - - ``style`` -- either ``"positive"`` (making all coefficients positive) - or ``"<="``, or ``">="``. Default is ``">="``. + sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) + sage: triangle.vertices_matrix() + [0 1 1] + [1 0 1] + sage: (triangle/2).vertices_matrix() + [ 0 1/2 1/2] + [1/2 0 1/2] + sage: (triangle/2).vertices_matrix(ZZ) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer - - ``align`` -- a boolean or ``None''. Default is ``None`` in which case - ``align`` is ``True`` if ``separator`` is the newline character. - If set, then the lines of the output string are aligned - by the comparison symbol by padding blanks. + TESTS: - Keyword parameters of - :meth:`~sage.geometry.polyhedron.representation.Hrepresentation.repr_pretty` - are passed on: + Check that :trac:`28828` is fixed:: - - ``prefix`` -- a string - - - ``indices`` -- a tuple or other iterable - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: P = polytopes.permutahedron(3) - sage: print(P.Hrepresentation_str()) - x0 + x1 + x2 == 6 - x0 + x1 >= 3 - -x0 - x1 >= -5 - x1 >= 1 - -x0 >= -3 - x0 >= 1 - -x1 >= -3 - - sage: print(P.Hrepresentation_str(style='<=')) - -x0 - x1 - x2 == -6 - -x0 - x1 <= -3 - x0 + x1 <= 5 - -x1 <= -1 - x0 <= 3 - -x0 <= -1 - x1 <= 3 - - sage: print(P.Hrepresentation_str(style='positive')) - x0 + x1 + x2 == 6 - x0 + x1 >= 3 - 5 >= x0 + x1 - x1 >= 1 - 3 >= x0 - x0 >= 1 - 3 >= x1 - - sage: print(P.Hrepresentation_str(latex=True)) - \begin{array}{rcl} - x_{0} + x_{1} + x_{2} & = & 6 \\ - x_{0} + x_{1} & \geq & 3 \\ - -x_{0} - x_{1} & \geq & -5 \\ - x_{1} & \geq & 1 \\ - -x_{0} & \geq & -3 \\ - x_{0} & \geq & 1 \\ - -x_{1} & \geq & -3 - \end{array} - - sage: print(P.Hrepresentation_str(align=False)) - x0 + x1 + x2 == 6 - x0 + x1 >= 3 - -x0 - x1 >= -5 - x1 >= 1 - -x0 >= -3 - x0 >= 1 - -x1 >= -3 - - sage: c = polytopes.cube() - sage: c.Hrepresentation_str(separator=', ', style='positive') - '1 >= x0, 1 >= x1, 1 >= x2, 1 + x0 >= 0, 1 + x2 >= 0, 1 + x1 >= 0' - """ - pretty_hs = [h.repr_pretty(split=True, latex=latex, style=style, **kwds) for h in self.Hrepresentation()] - shift = any(pretty_h[2].startswith('-') for pretty_h in pretty_hs) - - if align is None: - align = separator == "\n" - if align: - lengths = [(len(s[0]), len(s[1]), len(s[2])) for s in pretty_hs] - from operator import itemgetter - length_left = max(lengths, key=itemgetter(0))[0] - length_middle = max(lengths, key=itemgetter(1))[1] - length_right = max(lengths, key=itemgetter(2))[2] - if shift: - length_right += 1 - if latex: - h_line = "{:>" + "{}".format(length_left) + "} & {:" + \ - "{}".format(length_middle) + "} & {:" + \ - "{}".format(length_right) + "}\\\\" - else: - h_line = "{:>" + "{}".format(length_left) \ - + "} {:" + "{}".format(length_middle) \ - + "} {:" + "{}".format(length_right) + "}" - elif latex: - h_line = "{} & {} & {}\\\\" - else: - h_line = "{} {} {}" - - def pad_non_minus(s): - if align and shift and not s.startswith('-'): - return ' ' + s - else: - return s - h_list = [h_line.format(pretty_h[0], pretty_h[1], pad_non_minus(pretty_h[2])) - for pretty_h in pretty_hs] - pretty_print = separator.join(h_list) - - if not latex: - return pretty_print - else: - # below we remove the 2 unnecessary backslashes at the end of pretty_print - return "\\begin{array}{rcl}\n" + pretty_print[:-2] + "\n\\end{array}" - - def Hrep_generator(self): - """ - Return an iterator over the objects of the H-representation - (inequalities or equations). - - EXAMPLES:: - - sage: p = polytopes.hypercube(3) - sage: next(p.Hrep_generator()) - An inequality (-1, 0, 0) x + 1 >= 0 - """ - for H in self.Hrepresentation(): - yield H - - @cached_method - def n_Hrepresentation(self): - """ - Return the number of objects that make up the - H-representation of the polyhedron. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: p = polytopes.cross_polytope(4) - sage: p.n_Hrepresentation() - 16 - sage: p.n_Hrepresentation() == p.n_inequalities() + p.n_equations() - True - """ - return len(self.Hrepresentation()) - - def Vrepresentation(self, index=None): - """ - Return the objects of the V-representation. Each entry is - either a vertex, a ray, or a line. - - See :mod:`sage.geometry.polyhedron.constructor` for a - definition of vertex/ray/line. - - INPUT: - - - ``index`` -- either an integer or ``None`` - - OUTPUT: - - The optional argument is an index running from ``0`` to - ``self.n_Vrepresentation()-1``. If present, the - V-representation object at the given index will be - returned. Without an argument, returns the list of all - V-representation objects. - - EXAMPLES:: - - sage: p = polytopes.simplex(4, project=True) - sage: p.Vrepresentation(0) - A vertex at (0.7071067812, 0.4082482905, 0.2886751346, 0.2236067977) - sage: p.Vrepresentation(0) == p.Vrepresentation() [0] - True - """ - if index is None: - return self._Vrepresentation - else: - return self._Vrepresentation[index] - - @cached_method - def n_Vrepresentation(self): - """ - Return the number of objects that make up the - V-representation of the polyhedron. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: p = polytopes.simplex(4) - sage: p.n_Vrepresentation() - 5 - sage: p.n_Vrepresentation() == p.n_vertices() + p.n_rays() + p.n_lines() - True - """ - return len(self.Vrepresentation()) - - def Vrep_generator(self): - """ - Return an iterator over the objects of the V-representation - (vertices, rays, and lines). - - EXAMPLES:: - - sage: p = polytopes.cyclic_polytope(3,4) - sage: vg = p.Vrep_generator() - sage: next(vg) - A vertex at (0, 0, 0) - sage: next(vg) - A vertex at (1, 1, 1) - """ - for V in self.Vrepresentation(): - yield V - - def inequality_generator(self): - """ - Return a generator for the defining inequalities of the - polyhedron. - - OUTPUT: - - A generator of the inequality Hrepresentation objects. - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) - sage: for v in triangle.inequality_generator(): print(v) - An inequality (1, 1) x - 1 >= 0 - An inequality (0, -1) x + 1 >= 0 - An inequality (-1, 0) x + 1 >= 0 - sage: [ v for v in triangle.inequality_generator() ] - [An inequality (1, 1) x - 1 >= 0, - An inequality (0, -1) x + 1 >= 0, - An inequality (-1, 0) x + 1 >= 0] - sage: [ [v.A(), v.b()] for v in triangle.inequality_generator() ] - [[(1, 1), -1], [(0, -1), 1], [(-1, 0), 1]] - """ - for H in self.Hrepresentation(): - if H.is_inequality(): - yield H - - @cached_method - def inequalities(self): - """ - Return all inequalities. - - OUTPUT: - - A tuple of inequalities. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]]) - sage: p.inequalities()[0:3] - (An inequality (1, 0, 0) x + 0 >= 0, - An inequality (0, 1, 0) x + 0 >= 0, - An inequality (0, 0, 1) x + 0 >= 0) - sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4])) - sage: ieqs = p3.inequalities() - sage: ieqs[0] - An inequality (0, 1, 1, 1) x - 6 >= 0 - sage: list(_) - [-6, 0, 1, 1, 1] - """ - return tuple(self.inequality_generator()) - - def inequalities_list(self): - """ - Return a list of inequalities as coefficient lists. - - .. NOTE:: - - It is recommended to use :meth:`inequalities` or - :meth:`inequality_generator` instead to iterate over the - list of :class:`Inequality` objects. - - EXAMPLES:: - - sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]]) - sage: p.inequalities_list()[0:3] - [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] - sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4])) - sage: ieqs = p3.inequalities_list() - sage: ieqs[0] - [-6, 0, 1, 1, 1] - sage: ieqs[-1] - [-3, 0, 1, 0, 1] - sage: ieqs == [list(x) for x in p3.inequality_generator()] - True - """ - return [list(x) for x in self.inequality_generator()] - - def equation_generator(self): - """ - Return a generator for the linear equations satisfied by the - polyhedron. - - EXAMPLES:: - - sage: p = polytopes.regular_polygon(8,base_ring=RDF) - sage: p3 = Polyhedron(vertices = [x+[0] for x in p.vertices()], base_ring=RDF) - sage: next(p3.equation_generator()) - An equation (0.0, 0.0, 1.0) x + 0.0 == 0 - """ - for H in self.Hrepresentation(): - if H.is_equation(): - yield H - - @cached_method - def equations(self): - """ - Return all linear constraints of the polyhedron. - - OUTPUT: - - A tuple of equations. - - EXAMPLES:: - - sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]]) - sage: test_p.equations() - (An equation (1, 1, 1, 1) x - 10 == 0,) - """ - return tuple(self.equation_generator()) - - def equations_list(self): - """ - Return the linear constraints of the polyhedron. As with - inequalities, each constraint is given as [b -a1 -a2 ... an] - where for variables x1, x2,..., xn, the polyhedron satisfies - the equation b = a1*x1 + a2*x2 + ... + an*xn. - - .. NOTE:: - - It is recommended to use :meth:`equations` or - :meth:`equation_generator()` instead to iterate over the - list of - :class:`~sage.geometry.polyhedron.representation.Equation` - objects. - - EXAMPLES:: - - sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]]) - sage: test_p.equations_list() - [[-10, 1, 1, 1, 1]] - """ - return [list(eq) for eq in self.equation_generator()] - - def vertices_list(self): - """ - Return a list of vertices of the polyhedron. - - .. NOTE:: - - It is recommended to use :meth:`vertex_generator` instead to - iterate over the list of :class:`Vertex` objects. - - .. WARNING:: - - If the polyhedron has lines, return the vertices - of the ``Vrepresentation``. However, the represented polyhedron - has no 0-dimensional faces (i.e. vertices):: - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) - sage: P.vertices_list() - [[0, 0, 0]] - sage: P.faces(0) - () - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) - sage: triangle.vertices_list() - [[0, 1], [1, 0], [1, 1]] - sage: a_simplex = Polyhedron(ieqs = [ - ....: [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1] - ....: ], eqns = [[1,-1,-1,-1,-1]]) - sage: a_simplex.vertices_list() - [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] - sage: a_simplex.vertices_list() == [list(v) for v in a_simplex.vertex_generator()] - True - """ - return [list(x) for x in self.vertex_generator()] - - def vertex_generator(self): - """ - Return a generator for the vertices of the polyhedron. - - .. WARNING:: - - If the polyhedron has lines, return a generator for the vertices - of the ``Vrepresentation``. However, the represented polyhedron - has no 0-dimensional faces (i.e. vertices):: - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) - sage: list(P.vertex_generator()) - [A vertex at (0, 0, 0)] - sage: P.faces(0) - () - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) - sage: for v in triangle.vertex_generator(): print(v) - A vertex at (0, 1) - A vertex at (1, 0) - A vertex at (1, 1) - sage: v_gen = triangle.vertex_generator() - sage: next(v_gen) # the first vertex - A vertex at (0, 1) - sage: next(v_gen) # the second vertex - A vertex at (1, 0) - sage: next(v_gen) # the third vertex - A vertex at (1, 1) - sage: try: next(v_gen) # there are only three vertices - ....: except StopIteration: print("STOP") - STOP - sage: type(v_gen) - <... 'generator'> - sage: [ v for v in triangle.vertex_generator() ] - [A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)] - """ - for V in self.Vrepresentation(): - if V.is_vertex(): - yield V - - @cached_method - def vertices(self): - """ - Return all vertices of the polyhedron. - - OUTPUT: - - A tuple of vertices. - - .. WARNING:: - - If the polyhedron has lines, return the vertices - of the ``Vrepresentation``. However, the represented polyhedron - has no 0-dimensional faces (i.e. vertices):: - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) - sage: P.vertices() - (A vertex at (0, 0, 0),) - sage: P.faces(0) - () - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) - sage: triangle.vertices() - (A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)) - sage: a_simplex = Polyhedron(ieqs = [ - ....: [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1] - ....: ], eqns = [[1,-1,-1,-1,-1]]) - sage: a_simplex.vertices() - (A vertex at (1, 0, 0, 0), A vertex at (0, 1, 0, 0), - A vertex at (0, 0, 1, 0), A vertex at (0, 0, 0, 1)) - """ - return tuple(self.vertex_generator()) - - @cached_method - def vertices_matrix(self, base_ring=None): - """ - Return the coordinates of the vertices as the columns of a matrix. - - INPUT: - - - ``base_ring`` -- A ring or ``None`` (default). The base ring - of the returned matrix. If not specified, the base ring of - the polyhedron is used. - - OUTPUT: - - A matrix over ``base_ring`` whose columns are the coordinates - of the vertices. A ``TypeError`` is raised if the coordinates - cannot be converted to ``base_ring``. - - .. WARNING:: - - If the polyhedron has lines, return the coordinates of the vertices - of the ``Vrepresentation``. However, the represented polyhedron - has no 0-dimensional faces (i.e. vertices):: - - sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) - sage: P.vertices_matrix() - [0] - [0] - [0] - sage: P.faces(0) - () - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) - sage: triangle.vertices_matrix() - [0 1 1] - [1 0 1] - sage: (triangle/2).vertices_matrix() - [ 0 1/2 1/2] - [1/2 0 1/2] - sage: (triangle/2).vertices_matrix(ZZ) - Traceback (most recent call last): - ... - TypeError: no conversion of this rational to integer - - TESTS: - - Check that :trac:`28828` is fixed:: - - sage: P.vertices_matrix().is_immutable() - True - """ - if base_ring is None: - base_ring = self.base_ring() - m = matrix(base_ring, self.ambient_dim(), self.n_vertices()) - for i, v in enumerate(self.vertices()): - for j in range(self.ambient_dim()): - m[j, i] = v[j] - m.set_immutable() - return m + sage: P.vertices_matrix().is_immutable() + True + """ + if base_ring is None: + base_ring = self.base_ring() + m = matrix(base_ring, self.ambient_dim(), self.n_vertices()) + for i, v in enumerate(self.vertices()): + for j in range(self.ambient_dim()): + m[j, i] = v[j] + m.set_immutable() + return m def an_affine_basis(self): """ @@ -2327,115 +1349,6 @@ def _test_an_affine_basis(self, tester=None, **options): for v in b: tester.assertIn(v, self.vertices()) - def ray_generator(self): - """ - Return a generator for the rays of the polyhedron. - - EXAMPLES:: - - sage: pi = Polyhedron(ieqs = [[1,1,0],[1,0,1]]) - sage: pir = pi.ray_generator() - sage: [x.vector() for x in pir] - [(1, 0), (0, 1)] - """ - for V in self.Vrepresentation(): - if V.is_ray(): - yield V - - @cached_method - def rays(self): - """ - Return a list of rays of the polyhedron. - - OUTPUT: - - A tuple of rays. - - EXAMPLES:: - - sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]]) - sage: p.rays() - (A ray in the direction (1, 0, 0), - A ray in the direction (0, 1, 0), - A ray in the direction (0, 0, 1)) - """ - return tuple(self.ray_generator()) - - def rays_list(self): - """ - Return a list of rays as coefficient lists. - - .. NOTE:: - - It is recommended to use :meth:`rays` or - :meth:`ray_generator` instead to iterate over the list of - :class:`Ray` objects. - - OUTPUT: - - A list of rays as lists of coordinates. - - EXAMPLES:: - - sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]]) - sage: p.rays_list() - [[1, 0, 0], [0, 1, 0], [0, 0, 1]] - sage: p.rays_list() == [list(r) for r in p.ray_generator()] - True - """ - return [list(x) for x in self.ray_generator()] - - def line_generator(self): - """ - Return a generator for the lines of the polyhedron. - - EXAMPLES:: - - sage: pr = Polyhedron(rays = [[1,0],[-1,0],[0,1]], vertices = [[-1,-1]]) - sage: next(pr.line_generator()).vector() - (1, 0) - """ - for V in self.Vrepresentation(): - if V.is_line(): - yield V - - @cached_method - def lines(self): - """ - Return all lines of the polyhedron. - - OUTPUT: - - A tuple of lines. - - EXAMPLES:: - - sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]]) - sage: p.lines() - (A line in the direction (1, 0),) - """ - return tuple(self.line_generator()) - - def lines_list(self): - """ - Return a list of lines of the polyhedron. The line data is given - as a list of coordinates rather than as a Hrepresentation object. - - .. NOTE:: - - It is recommended to use :meth:`line_generator` instead to - iterate over the list of :class:`Line` objects. - - EXAMPLES:: - - sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]]) - sage: p.lines_list() - [[1, 0]] - sage: p.lines_list() == [list(x) for x in p.line_generator()] - True - """ - return [list(x) for x in self.line_generator()] - def bounded_edges(self): """ Return the bounded edges (excluding rays and lines). @@ -2529,51 +1442,6 @@ def Hrepresentation_space(self): """ return self.parent().Hrepresentation_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 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 is_empty(self): """ Test whether the polyhedron is the empty polyhedron @@ -3086,65 +1954,6 @@ def slack_matrix(self): slack_matrix.set_immutable() return slack_matrix - def base_ring(self): - """ - Return the base ring. - - OUTPUT: - - The ring over which the polyhedron is defined. Must be a - sub-ring of the reals to define a polyhedron, in particular - comparison must be defined. Popular choices are - - * ``ZZ`` (the ring of integers, lattice polytope), - - * ``QQ`` (exact arithmetic using gmp), - - * ``RDF`` (double precision floating-point arithmetic), or - - * ``AA`` (real algebraic field). - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices = [[1,0],[0,1],[1,1]]) - sage: triangle.base_ring() == ZZ - True - """ - return self.parent().base_ring() - - def backend(self): - """ - Return the backend used. - - OUTPUT: - - The name of the backend used for computations. It will be one of - the following backends: - - * ``ppl`` the Parma Polyhedra Library - - * ``cdd`` CDD - - * ``normaliz`` normaliz - - * ``polymake`` polymake - - * ``field`` a generic Sage implementation - - EXAMPLES:: - - sage: triangle = Polyhedron(vertices = [[1, 0], [0, 1], [1, 1]]) - sage: triangle.backend() - 'ppl' - sage: D = polytopes.dodecahedron() - sage: D.backend() - 'field' - sage: P = Polyhedron([[1.23]]) - sage: P.backend() - 'cdd' - """ - return self.parent().backend() - @cached_method def center(self): """ diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py new file mode 100644 index 00000000000..fe66d0dcd74 --- /dev/null +++ b/src/sage/geometry/polyhedron/base0.py @@ -0,0 +1,1282 @@ +r""" +Base class for polyhedra, part 0 + +Initialization and access to Vrepresentation and Hrepresentation. +""" + +# **************************************************************************** +# 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.cachefunc import cached_method +from sage.structure.element import Element + +class Polyhedron_base0(Element): + """ + Initialization and basic access for polyhedra. + + See :class:`sage.geometry.polyhedron.base.Polyhedron_base`. + + TESTS:: + + sage: from sage.geometry.polyhedron.base0 import Polyhedron_base0 + sage: P = Polyhedron(rays=[[1, 0, 0]], lines=[[0, 1, 0]]) + sage: Polyhedron_base0.__repr__(P) + 'A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line' + sage: Polyhedron_base0.Vrepresentation(P) + (A line in the direction (0, 1, 0), + A vertex at (0, 0, 0), + A ray in the direction (1, 0, 0)) + sage: Polyhedron_base0.vertices.f(P) + (A vertex at (0, 0, 0),) + sage: Polyhedron_base0.rays.f(P) + (A ray in the direction (1, 0, 0),) + sage: Polyhedron_base0.lines.f(P) + (A line in the direction (0, 1, 0),) + sage: Polyhedron_base0.Hrepresentation(P) + (An equation (0, 0, 1) x + 0 == 0, An inequality (1, 0, 0) x + 0 >= 0) + sage: Polyhedron_base0.inequalities.f(P) + (An inequality (1, 0, 0) x + 0 >= 0,) + sage: Polyhedron_base0.equations.f(P) + (An equation (0, 0, 1) x + 0 == 0,) + sage: Polyhedron_base0.base_ring(P) + Integer Ring + sage: Polyhedron_base0.backend(P) + 'ppl' + sage: Polyhedron_base0.dim(P) + 2 + sage: Polyhedron_base0.ambient_dim(P) + 3 + sage: Polyhedron_base0.change_ring(P, ZZ, backend='field').backend() + 'field' + sage: Polyhedron_base0.base_extend(P, QQ) + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line + """ + def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, mutable=False, **kwds): + """ + Initializes the polyhedron. + + See :class:`sage.geometry.polyhedron.base.Polyhedron_base` for a description of the input + data. + + TESTS:: + + sage: p = Polyhedron() # indirect doctests + + sage: from sage.geometry.polyhedron.backend_field import Polyhedron_field + sage: from sage.geometry.polyhedron.parent import Polyhedra_field + sage: parent = Polyhedra_field(AA, 1, 'field') + sage: Vrep = [[[0], [1/2], [1]], [], []] + sage: Hrep = [[[0, 1], [1, -1]], []] + sage: p = Polyhedron_field(parent, Vrep, Hrep, + ....: Vrep_minimal=False, Hrep_minimal=True) + Traceback (most recent call last): + ... + ValueError: if both Vrep and Hrep are provided, they must be minimal... + + Illustration of ``pref_rep``. + Note that ``ppl`` doesn't support precomputed data:: + + sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl + sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_ppl + sage: parent = Polyhedra_QQ_ppl(QQ, 1, 'ppl') + sage: p = Polyhedron_QQ_ppl(parent, Vrep, 'nonsense', + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') + sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep') + sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrepresentation') + Traceback (most recent call last): + ... + ValueError: ``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')`` + + If the backend supports precomputed data, ``pref_rep`` is ignored:: + + sage: p = Polyhedron_field(parent, Vrep, 'nonsense', + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') + Traceback (most recent call last): + ... + TypeError: ..._init_Hrepresentation() takes 3 positional arguments but 9 were given + + The empty polyhedron is detected when the Vrepresentation is given with generator; + see :trac:`29899`:: + + sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd + sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_cdd + sage: parent = Polyhedra_QQ_cdd(QQ, 0, 'cdd') + sage: p = Polyhedron_QQ_cdd(parent, [iter([]), iter([]), iter([])], None) + """ + Element.__init__(self, parent=parent) + if Vrep is not None and Hrep is not None: + if not (Vrep_minimal is True and Hrep_minimal is True): + raise ValueError("if both Vrep and Hrep are provided, they must be minimal" + " and Vrep_minimal and Hrep_minimal must both be True") + if hasattr(self, "_init_from_Vrepresentation_and_Hrepresentation"): + self._init_from_Vrepresentation_and_Hrepresentation(Vrep, Hrep) + return + else: + if pref_rep is None: + # Initialize from Hrepresentation if this seems simpler. + Vrep = [tuple(Vrep[0]), tuple(Vrep[1]), Vrep[2]] + Hrep = [tuple(Hrep[0]), Hrep[1]] + if len(Hrep[0]) < len(Vrep[0]) + len(Vrep[1]): + pref_rep = 'Hrep' + else: + pref_rep = 'Vrep' + if pref_rep == 'Vrep': + Hrep = None + elif pref_rep == 'Hrep': + Vrep = None + else: + raise ValueError("``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')``") + if Vrep is not None: + vertices, rays, lines = Vrep + + # We build tuples out of generators now to detect the empty polyhedron. + + # The damage is limited: + # The backend will have to obtain all elements from the generator anyway. + # The generators are mainly for saving time with initializing from + # Vrepresentation and Hrepresentation. + # If we dispose of one of them (see above), it is wasteful to have generated it. + + # E.g. the dilate will be set up with new Vrepresentation and Hrepresentation + # regardless of the backend along with the argument ``pref_rep``. + # As we only use generators, there is no penalty to this approach + # (and the method ``dilation`` does not have to distinguish by backend). + + if not isinstance(vertices, (tuple, list)): + vertices = tuple(vertices) + if not isinstance(rays, (tuple, list)): + rays = tuple(rays) + if not isinstance(lines, (tuple, list)): + lines = tuple(lines) + + if vertices or rays or lines: + self._init_from_Vrepresentation(vertices, rays, lines, **kwds) + else: + self._init_empty_polyhedron() + elif Hrep is not None: + ieqs, eqns = Hrep + self._init_from_Hrepresentation(ieqs, eqns, **kwds) + else: + self._init_empty_polyhedron() + + def _init_from_Vrepresentation(self, vertices, rays, lines, **kwds): + """ + Construct polyhedron from V-representation data. + + INPUT: + + - ``vertices`` -- list of point. Each point can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``rays`` -- list of rays. Each ray can be specified as any + iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``lines`` -- list of lines. Each line can be specified as + any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + EXAMPLES:: + + sage: p = Polyhedron() + sage: from sage.geometry.polyhedron.base import Polyhedron_base + sage: Polyhedron_base._init_from_Vrepresentation(p, [], [], []) + Traceback (most recent call last): + ... + NotImplementedError: a derived class must implement this method + """ + raise NotImplementedError('a derived class must implement this method') + + def _init_from_Hrepresentation(self, ieqs, eqns, **kwds): + """ + Construct polyhedron from H-representation data. + + INPUT: + + - ``ieqs`` -- list of inequalities. Each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + - ``eqns`` -- list of equalities. Each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements. + + EXAMPLES:: + + sage: p = Polyhedron() + sage: from sage.geometry.polyhedron.base import Polyhedron_base + sage: Polyhedron_base._init_from_Hrepresentation(p, [], []) + Traceback (most recent call last): + ... + NotImplementedError: a derived class must implement this method + """ + raise NotImplementedError('a derived class must implement this method') + + def _init_empty_polyhedron(self): + """ + Initializes an empty polyhedron. + + TESTS:: + + sage: empty = Polyhedron(); empty + The empty polyhedron in ZZ^0 + sage: empty.Vrepresentation() + () + sage: empty.Hrepresentation() + (An equation -1 == 0,) + sage: Polyhedron(vertices = []) + The empty polyhedron in ZZ^0 + sage: Polyhedron(vertices = [])._init_empty_polyhedron() + sage: from sage.geometry.polyhedron.parent import Polyhedra + sage: Polyhedra(QQ,7)() + A 0-dimensional polyhedron in QQ^7 defined as the convex hull of 1 vertex + """ + self._Vrepresentation = [] + self._Hrepresentation = [] + self.parent()._make_Equation(self, [-1] + [0] * self.ambient_dim()) + self._Vrepresentation = tuple(self._Vrepresentation) + self._Hrepresentation = tuple(self._Hrepresentation) + + def base_extend(self, base_ring, backend=None): + """ + Return a new polyhedron over a larger base ring. + + This method can also be used to change the backend. + + INPUT: + + - ``base_ring`` -- the new base ring + + - ``backend`` -- the new backend, see + :func:`~sage.geometry.polyhedron.constructor.Polyhedron`. + If ``None`` (the default), attempt to keep the same backend. + Otherwise, use the same defaulting behavior + as described there. + + OUTPUT: + + The same polyhedron, but over a larger base ring and possibly with a changed backend. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ); P + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray + sage: P.base_extend(QQ) + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray + sage: P.base_extend(QQ) == P + True + + TESTS: + + Test that :trac:`22575` is fixed:: + + sage: Q = P.base_extend(ZZ, backend='field') + sage: Q.backend() + 'field' + + """ + new_parent = self.parent().base_extend(base_ring, backend) + return new_parent(self, copy=True) + + def change_ring(self, base_ring, backend=None): + """ + Return the polyhedron obtained by coercing the entries of the + vertices/lines/rays of this polyhedron into the given ring. + + This method can also be used to change the backend. + + INPUT: + + - ``base_ring`` -- the new base ring + + - ``backend`` -- the new backend or ``None`` (default), see + :func:`~sage.geometry.polyhedron.constructor.Polyhedron`. + If ``None`` (the default), attempt to keep the same backend. + Otherwise, use the same defaulting behavior + as described there. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=QQ); P + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 1 ray + sage: P.change_ring(ZZ) + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices and 1 ray + sage: P.change_ring(ZZ) == P + True + + sage: P = Polyhedron(vertices=[(-1.3,0), (0,2.3)], base_ring=RDF); P.vertices() + (A vertex at (-1.3, 0.0), A vertex at (0.0, 2.3)) + sage: P.change_ring(QQ).vertices() + (A vertex at (-13/10, 0), A vertex at (0, 23/10)) + sage: P == P.change_ring(QQ) + True + sage: P.change_ring(ZZ) + Traceback (most recent call last): + ... + TypeError: cannot change the base ring to the Integer Ring + + sage: P = polytopes.regular_polygon(3); P # optional - sage.rings.number_field + A 2-dimensional polyhedron in AA^2 defined as the convex hull of 3 vertices + sage: P.vertices() # optional - sage.rings.number_field + (A vertex at (0.?e-16, 1.000000000000000?), + A vertex at (0.866025403784439?, -0.500000000000000?), + A vertex at (-0.866025403784439?, -0.500000000000000?)) + sage: P.change_ring(QQ) # optional - sage.rings.number_field + Traceback (most recent call last): + ... + TypeError: cannot change the base ring to the Rational Field + + .. WARNING:: + + The base ring ``RDF`` should be used with care. As it is + not an exact ring, certain computations may break or + silently produce wrong results, for example changing the + base ring from an exact ring into ``RDF`` may cause a + loss of data:: + + sage: P = Polyhedron([[2/3,0],[6666666666666667/10^16,0]], base_ring=AA); P # optional - sage.rings.number_field + A 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: Q = P.change_ring(RDF); Q # optional - sage.rings.number_field + A 0-dimensional polyhedron in RDF^2 defined as the convex hull of 1 vertex + sage: P.n_vertices() == Q.n_vertices() # optional - sage.rings.number_field + False + """ + from sage.categories.rings import Rings + + if base_ring not in Rings(): + raise ValueError("invalid base ring") + + try: + vertices = [[base_ring(x) for x in vertex] for vertex in self.vertices_list()] + rays = [[base_ring(x) for x in ray] for ray in self.rays_list()] + lines = [[base_ring(x) for x in line] for line in self.lines_list()] + + except (TypeError, ValueError): + raise TypeError("cannot change the base ring to the {0}".format(base_ring)) + + new_parent = self.parent().change_ring(base_ring, backend) + return new_parent([vertices, rays, lines], None) + + def is_mutable(self): + r""" + Return True if the polyhedron is mutable, i.e. it can be modified in place. + + EXAMPLES:: + + sage: p = polytopes.cube(backend='field') + sage: p.is_mutable() + False + """ + return False + + def is_immutable(self): + r""" + Return True if the polyhedron is immutable, i.e. it cannot be modified in place. + + EXAMPLES:: + + sage: p = polytopes.cube(backend='field') + sage: p.is_immutable() + True + """ + return True + + 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 + + @cached_method + def n_equations(self): + """ + Return the number of equations. The representation will + always be minimal, so the number of equations is the + codimension of the polyhedron in the ambient space. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]]) + sage: p.n_equations() + 1 + """ + return len(self.equations()) + + @cached_method + def n_inequalities(self): + """ + Return the number of inequalities. The representation will + always be minimal, so the number of inequalities is the + number of facets of the polyhedron in the ambient space. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[1,0,0],[0,1,0],[0,0,1]]) + sage: p.n_inequalities() + 3 + + sage: p = Polyhedron(vertices = [[t,t^2,t^3] for t in range(6)]) + sage: p.n_facets() + 8 + """ + return len(self.inequalities()) + + n_facets = n_inequalities + + @cached_method + def n_vertices(self): + """ + Return the number of vertices. The representation will + always be minimal. + + .. WARNING:: + + If the polyhedron has lines, return the number of vertices in + the ``Vrepresentation``. As the represented polyhedron has + no 0-dimensional faces (i.e. vertices), ``n_vertices`` corresponds + to the number of `k`-faces, where `k` is the number of lines:: + + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) + sage: P.n_vertices() + 1 + sage: P.faces(0) + () + sage: P.f_vector() + (1, 0, 1, 1) + + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0],[0,1,1]]) + sage: P.n_vertices() + 1 + sage: P.f_vector() + (1, 0, 0, 1, 1) + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[1,0],[0,1],[1,1]], rays=[[1,1]]) + sage: p.n_vertices() + 2 + """ + return len(self.vertices()) + + @cached_method + def n_rays(self): + """ + Return the number of rays. The representation will + always be minimal. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[1,0],[0,1]], rays=[[1,1]]) + sage: p.n_rays() + 1 + """ + return len(self.rays()) + + @cached_method + def n_lines(self): + """ + Return the number of lines. The representation will + always be minimal. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[0,0]], rays=[[0,1],[0,-1]]) + sage: p.n_lines() + 1 + """ + return len(self.lines()) + + def Hrepresentation(self, index=None): + """ + Return the objects of the H-representation. Each entry is + either an inequality or a equation. + + INPUT: + + - ``index`` -- either an integer or ``None`` + + OUTPUT: + + The optional argument is an index running from ``0`` to + ``self.n_Hrepresentation()-1``. If present, the + H-representation object at the given index will be + returned. Without an argument, returns the list of all + H-representation objects. + + EXAMPLES:: + + sage: p = polytopes.hypercube(3, backend='field') + sage: p.Hrepresentation(0) + An inequality (-1, 0, 0) x + 1 >= 0 + sage: p.Hrepresentation(0) == p.Hrepresentation()[0] + True + """ + if index is None: + return self._Hrepresentation + else: + return self._Hrepresentation[index] + + def Hrepresentation_str(self, separator='\n', latex=False, style='>=', align=None, **kwds): + r""" + Return a human-readable string representation of the Hrepresentation of this + polyhedron. + + INPUT: + + - ``separator`` -- a string. Default is ``"\n"``. + + - ``latex`` -- a boolean. Default is ``False``. + + - ``style`` -- either ``"positive"`` (making all coefficients positive) + or ``"<="``, or ``">="``. Default is ``">="``. + + - ``align`` -- a boolean or ``None''. Default is ``None`` in which case + ``align`` is ``True`` if ``separator`` is the newline character. + If set, then the lines of the output string are aligned + by the comparison symbol by padding blanks. + + Keyword parameters of + :meth:`~sage.geometry.polyhedron.representation.Hrepresentation.repr_pretty` + are passed on: + + - ``prefix`` -- a string + + - ``indices`` -- a tuple or other iterable + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(3) + sage: print(P.Hrepresentation_str()) + x0 + x1 + x2 == 6 + x0 + x1 >= 3 + -x0 - x1 >= -5 + x1 >= 1 + -x0 >= -3 + x0 >= 1 + -x1 >= -3 + + sage: print(P.Hrepresentation_str(style='<=')) + -x0 - x1 - x2 == -6 + -x0 - x1 <= -3 + x0 + x1 <= 5 + -x1 <= -1 + x0 <= 3 + -x0 <= -1 + x1 <= 3 + + sage: print(P.Hrepresentation_str(style='positive')) + x0 + x1 + x2 == 6 + x0 + x1 >= 3 + 5 >= x0 + x1 + x1 >= 1 + 3 >= x0 + x0 >= 1 + 3 >= x1 + + sage: print(P.Hrepresentation_str(latex=True)) + \begin{array}{rcl} + x_{0} + x_{1} + x_{2} & = & 6 \\ + x_{0} + x_{1} & \geq & 3 \\ + -x_{0} - x_{1} & \geq & -5 \\ + x_{1} & \geq & 1 \\ + -x_{0} & \geq & -3 \\ + x_{0} & \geq & 1 \\ + -x_{1} & \geq & -3 + \end{array} + + sage: print(P.Hrepresentation_str(align=False)) + x0 + x1 + x2 == 6 + x0 + x1 >= 3 + -x0 - x1 >= -5 + x1 >= 1 + -x0 >= -3 + x0 >= 1 + -x1 >= -3 + + sage: c = polytopes.cube() + sage: c.Hrepresentation_str(separator=', ', style='positive') + '1 >= x0, 1 >= x1, 1 >= x2, 1 + x0 >= 0, 1 + x2 >= 0, 1 + x1 >= 0' + """ + pretty_hs = [h.repr_pretty(split=True, latex=latex, style=style, **kwds) for h in self.Hrepresentation()] + shift = any(pretty_h[2].startswith('-') for pretty_h in pretty_hs) + + if align is None: + align = separator == "\n" + if align: + lengths = [(len(s[0]), len(s[1]), len(s[2])) for s in pretty_hs] + from operator import itemgetter + length_left = max(lengths, key=itemgetter(0))[0] + length_middle = max(lengths, key=itemgetter(1))[1] + length_right = max(lengths, key=itemgetter(2))[2] + if shift: + length_right += 1 + if latex: + h_line = "{:>" + "{}".format(length_left) + "} & {:" + \ + "{}".format(length_middle) + "} & {:" + \ + "{}".format(length_right) + "}\\\\" + else: + h_line = "{:>" + "{}".format(length_left) \ + + "} {:" + "{}".format(length_middle) \ + + "} {:" + "{}".format(length_right) + "}" + elif latex: + h_line = "{} & {} & {}\\\\" + else: + h_line = "{} {} {}" + + def pad_non_minus(s): + if align and shift and not s.startswith('-'): + return ' ' + s + else: + return s + h_list = [h_line.format(pretty_h[0], pretty_h[1], pad_non_minus(pretty_h[2])) + for pretty_h in pretty_hs] + pretty_print = separator.join(h_list) + + if not latex: + return pretty_print + else: + # below we remove the 2 unnecessary backslashes at the end of pretty_print + return "\\begin{array}{rcl}\n" + pretty_print[:-2] + "\n\\end{array}" + + def Hrep_generator(self): + """ + Return an iterator over the objects of the H-representation + (inequalities or equations). + + EXAMPLES:: + + sage: p = polytopes.hypercube(3) + sage: next(p.Hrep_generator()) + An inequality (-1, 0, 0) x + 1 >= 0 + """ + for H in self.Hrepresentation(): + yield H + + @cached_method + def n_Hrepresentation(self): + """ + Return the number of objects that make up the + H-representation of the polyhedron. + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: p = polytopes.cross_polytope(4) + sage: p.n_Hrepresentation() + 16 + sage: p.n_Hrepresentation() == p.n_inequalities() + p.n_equations() + True + """ + return len(self.Hrepresentation()) + + def Vrepresentation(self, index=None): + """ + Return the objects of the V-representation. Each entry is + either a vertex, a ray, or a line. + + See :mod:`sage.geometry.polyhedron.constructor` for a + definition of vertex/ray/line. + + INPUT: + + - ``index`` -- either an integer or ``None`` + + OUTPUT: + + The optional argument is an index running from ``0`` to + ``self.n_Vrepresentation()-1``. If present, the + V-representation object at the given index will be + returned. Without an argument, returns the list of all + V-representation objects. + + EXAMPLES:: + + sage: p = polytopes.simplex(4, project=True) + sage: p.Vrepresentation(0) + A vertex at (0.7071067812, 0.4082482905, 0.2886751346, 0.2236067977) + sage: p.Vrepresentation(0) == p.Vrepresentation() [0] + True + """ + if index is None: + return self._Vrepresentation + else: + return self._Vrepresentation[index] + + @cached_method + def n_Vrepresentation(self): + """ + Return the number of objects that make up the + V-representation of the polyhedron. + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: p = polytopes.simplex(4) + sage: p.n_Vrepresentation() + 5 + sage: p.n_Vrepresentation() == p.n_vertices() + p.n_rays() + p.n_lines() + True + """ + return len(self.Vrepresentation()) + + def Vrep_generator(self): + """ + Return an iterator over the objects of the V-representation + (vertices, rays, and lines). + + EXAMPLES:: + + sage: p = polytopes.cyclic_polytope(3,4) + sage: vg = p.Vrep_generator() + sage: next(vg) + A vertex at (0, 0, 0) + sage: next(vg) + A vertex at (1, 1, 1) + """ + for V in self.Vrepresentation(): + yield V + + def inequality_generator(self): + """ + Return a generator for the defining inequalities of the + polyhedron. + + OUTPUT: + + A generator of the inequality Hrepresentation objects. + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) + sage: for v in triangle.inequality_generator(): print(v) + An inequality (1, 1) x - 1 >= 0 + An inequality (0, -1) x + 1 >= 0 + An inequality (-1, 0) x + 1 >= 0 + sage: [ v for v in triangle.inequality_generator() ] + [An inequality (1, 1) x - 1 >= 0, + An inequality (0, -1) x + 1 >= 0, + An inequality (-1, 0) x + 1 >= 0] + sage: [ [v.A(), v.b()] for v in triangle.inequality_generator() ] + [[(1, 1), -1], [(0, -1), 1], [(-1, 0), 1]] + """ + for H in self.Hrepresentation(): + if H.is_inequality(): + yield H + + @cached_method + def inequalities(self): + """ + Return all inequalities. + + OUTPUT: + + A tuple of inequalities. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]]) + sage: p.inequalities()[0:3] + (An inequality (1, 0, 0) x + 0 >= 0, + An inequality (0, 1, 0) x + 0 >= 0, + An inequality (0, 0, 1) x + 0 >= 0) + sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4])) + sage: ieqs = p3.inequalities() + sage: ieqs[0] + An inequality (0, 1, 1, 1) x - 6 >= 0 + sage: list(_) + [-6, 0, 1, 1, 1] + """ + return tuple(self.inequality_generator()) + + def inequalities_list(self): + """ + Return a list of inequalities as coefficient lists. + + .. NOTE:: + + It is recommended to use :meth:`inequalities` or + :meth:`inequality_generator` instead to iterate over the + list of :class:`Inequality` objects. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[1,0,0],[2,2,2]]) + sage: p.inequalities_list()[0:3] + [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] + sage: p3 = Polyhedron(vertices = Permutations([1,2,3,4])) + sage: ieqs = p3.inequalities_list() + sage: ieqs[0] + [-6, 0, 1, 1, 1] + sage: ieqs[-1] + [-3, 0, 1, 0, 1] + sage: ieqs == [list(x) for x in p3.inequality_generator()] + True + """ + return [list(x) for x in self.inequality_generator()] + + def equation_generator(self): + """ + Return a generator for the linear equations satisfied by the + polyhedron. + + EXAMPLES:: + + sage: p = polytopes.regular_polygon(8,base_ring=RDF) + sage: p3 = Polyhedron(vertices = [x+[0] for x in p.vertices()], base_ring=RDF) + sage: next(p3.equation_generator()) + An equation (0.0, 0.0, 1.0) x + 0.0 == 0 + """ + for H in self.Hrepresentation(): + if H.is_equation(): + yield H + + @cached_method + def equations(self): + """ + Return all linear constraints of the polyhedron. + + OUTPUT: + + A tuple of equations. + + EXAMPLES:: + + sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]]) + sage: test_p.equations() + (An equation (1, 1, 1, 1) x - 10 == 0,) + """ + return tuple(self.equation_generator()) + + def equations_list(self): + """ + Return the linear constraints of the polyhedron. As with + inequalities, each constraint is given as [b -a1 -a2 ... an] + where for variables x1, x2,..., xn, the polyhedron satisfies + the equation b = a1*x1 + a2*x2 + ... + an*xn. + + .. NOTE:: + + It is recommended to use :meth:`equations` or + :meth:`equation_generator()` instead to iterate over the + list of + :class:`~sage.geometry.polyhedron.representation.Equation` + objects. + + EXAMPLES:: + + sage: test_p = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1],[3,4,1,2]]) + sage: test_p.equations_list() + [[-10, 1, 1, 1, 1]] + """ + return [list(eq) for eq in self.equation_generator()] + + def vertices_list(self): + """ + Return a list of vertices of the polyhedron. + + .. NOTE:: + + It is recommended to use :meth:`vertex_generator` instead to + iterate over the list of :class:`Vertex` objects. + + .. WARNING:: + + If the polyhedron has lines, return the vertices + of the ``Vrepresentation``. However, the represented polyhedron + has no 0-dimensional faces (i.e. vertices):: + + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) + sage: P.vertices_list() + [[0, 0, 0]] + sage: P.faces(0) + () + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) + sage: triangle.vertices_list() + [[0, 1], [1, 0], [1, 1]] + sage: a_simplex = Polyhedron(ieqs = [ + ....: [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1] + ....: ], eqns = [[1,-1,-1,-1,-1]]) + sage: a_simplex.vertices_list() + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] + sage: a_simplex.vertices_list() == [list(v) for v in a_simplex.vertex_generator()] + True + """ + return [list(x) for x in self.vertex_generator()] + + def vertex_generator(self): + """ + Return a generator for the vertices of the polyhedron. + + .. WARNING:: + + If the polyhedron has lines, return a generator for the vertices + of the ``Vrepresentation``. However, the represented polyhedron + has no 0-dimensional faces (i.e. vertices):: + + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) + sage: list(P.vertex_generator()) + [A vertex at (0, 0, 0)] + sage: P.faces(0) + () + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) + sage: for v in triangle.vertex_generator(): print(v) + A vertex at (0, 1) + A vertex at (1, 0) + A vertex at (1, 1) + sage: v_gen = triangle.vertex_generator() + sage: next(v_gen) # the first vertex + A vertex at (0, 1) + sage: next(v_gen) # the second vertex + A vertex at (1, 0) + sage: next(v_gen) # the third vertex + A vertex at (1, 1) + sage: try: next(v_gen) # there are only three vertices + ....: except StopIteration: print("STOP") + STOP + sage: type(v_gen) + <... 'generator'> + sage: [ v for v in triangle.vertex_generator() ] + [A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)] + """ + for V in self.Vrepresentation(): + if V.is_vertex(): + yield V + + @cached_method + def vertices(self): + """ + Return all vertices of the polyhedron. + + OUTPUT: + + A tuple of vertices. + + .. WARNING:: + + If the polyhedron has lines, return the vertices + of the ``Vrepresentation``. However, the represented polyhedron + has no 0-dimensional faces (i.e. vertices):: + + sage: P = Polyhedron(rays=[[1,0,0]],lines=[[0,1,0]]) + sage: P.vertices() + (A vertex at (0, 0, 0),) + sage: P.faces(0) + () + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[1,1]]) + sage: triangle.vertices() + (A vertex at (0, 1), A vertex at (1, 0), A vertex at (1, 1)) + sage: a_simplex = Polyhedron(ieqs = [ + ....: [0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1] + ....: ], eqns = [[1,-1,-1,-1,-1]]) + sage: a_simplex.vertices() + (A vertex at (1, 0, 0, 0), A vertex at (0, 1, 0, 0), + A vertex at (0, 0, 1, 0), A vertex at (0, 0, 0, 1)) + """ + return tuple(self.vertex_generator()) + + def ray_generator(self): + """ + Return a generator for the rays of the polyhedron. + + EXAMPLES:: + + sage: pi = Polyhedron(ieqs = [[1,1,0],[1,0,1]]) + sage: pir = pi.ray_generator() + sage: [x.vector() for x in pir] + [(1, 0), (0, 1)] + """ + for V in self.Vrepresentation(): + if V.is_ray(): + yield V + + @cached_method + def rays(self): + """ + Return a list of rays of the polyhedron. + + OUTPUT: + + A tuple of rays. + + EXAMPLES:: + + sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]]) + sage: p.rays() + (A ray in the direction (1, 0, 0), + A ray in the direction (0, 1, 0), + A ray in the direction (0, 0, 1)) + """ + return tuple(self.ray_generator()) + + def rays_list(self): + """ + Return a list of rays as coefficient lists. + + .. NOTE:: + + It is recommended to use :meth:`rays` or + :meth:`ray_generator` instead to iterate over the list of + :class:`Ray` objects. + + OUTPUT: + + A list of rays as lists of coordinates. + + EXAMPLES:: + + sage: p = Polyhedron(ieqs = [[0,0,0,1],[0,0,1,0],[1,1,0,0]]) + sage: p.rays_list() + [[1, 0, 0], [0, 1, 0], [0, 0, 1]] + sage: p.rays_list() == [list(r) for r in p.ray_generator()] + True + """ + return [list(x) for x in self.ray_generator()] + + def line_generator(self): + """ + Return a generator for the lines of the polyhedron. + + EXAMPLES:: + + sage: pr = Polyhedron(rays = [[1,0],[-1,0],[0,1]], vertices = [[-1,-1]]) + sage: next(pr.line_generator()).vector() + (1, 0) + """ + for V in self.Vrepresentation(): + if V.is_line(): + yield V + + @cached_method + def lines(self): + """ + Return all lines of the polyhedron. + + OUTPUT: + + A tuple of lines. + + EXAMPLES:: + + sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]]) + sage: p.lines() + (A line in the direction (1, 0),) + """ + return tuple(self.line_generator()) + + def lines_list(self): + """ + Return a list of lines of the polyhedron. The line data is given + as a list of coordinates rather than as a Hrepresentation object. + + .. NOTE:: + + It is recommended to use :meth:`line_generator` instead to + iterate over the list of :class:`Line` objects. + + EXAMPLES:: + + sage: p = Polyhedron(rays = [[1,0],[-1,0],[0,1],[1,1]], vertices = [[-2,-2],[2,3]]) + sage: p.lines_list() + [[1, 0]] + sage: p.lines_list() == [list(x) for x in p.line_generator()] + True + """ + return [list(x) for x in self.line_generator()] + + 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 base_ring(self): + """ + Return the base ring. + + OUTPUT: + + The ring over which the polyhedron is defined. Must be a + sub-ring of the reals to define a polyhedron, in particular + comparison must be defined. Popular choices are + + * ``ZZ`` (the ring of integers, lattice polytope), + + * ``QQ`` (exact arithmetic using gmp), + + * ``RDF`` (double precision floating-point arithmetic), or + + * ``AA`` (real algebraic field). + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices = [[1,0],[0,1],[1,1]]) + sage: triangle.base_ring() == ZZ + True + """ + return self.parent().base_ring() + + def backend(self): + """ + Return the backend used. + + OUTPUT: + + The name of the backend used for computations. It will be one of + the following backends: + + * ``ppl`` the Parma Polyhedra Library + + * ``cdd`` CDD + + * ``normaliz`` normaliz + + * ``polymake`` polymake + + * ``field`` a generic Sage implementation + + EXAMPLES:: + + sage: triangle = Polyhedron(vertices = [[1, 0], [0, 1], [1, 1]]) + sage: triangle.backend() + 'ppl' + sage: D = polytopes.dodecahedron() + sage: D.backend() + 'field' + sage: P = Polyhedron([[1.23]]) + sage: P.backend() + 'cdd' + """ + return self.parent().backend() From 6dfadb810949aa2b2beea63b8fffd29396da0807 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 25 Oct 2021 21:37:54 +0200 Subject: [PATCH 12/18] leave dim and ambient_dim for convex set --- src/sage/geometry/polyhedron/base.py | 98 ++++++++++++++++++++++++ src/sage/geometry/polyhedron/base0.py | 104 -------------------------- 2 files changed, 98 insertions(+), 104 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 78abf572793..4e618af342b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -907,6 +907,59 @@ 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 @@ -1376,6 +1429,51 @@ 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. diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py index fe66d0dcd74..21c22c4f4aa 100644 --- a/src/sage/geometry/polyhedron/base0.py +++ b/src/sage/geometry/polyhedron/base0.py @@ -45,8 +45,6 @@ class Polyhedron_base0(Element): sage: from sage.geometry.polyhedron.base0 import Polyhedron_base0 sage: P = Polyhedron(rays=[[1, 0, 0]], lines=[[0, 1, 0]]) - sage: Polyhedron_base0.__repr__(P) - 'A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line' sage: Polyhedron_base0.Vrepresentation(P) (A line in the direction (0, 1, 0), A vertex at (0, 0, 0), @@ -67,10 +65,6 @@ class Polyhedron_base0(Element): Integer Ring sage: Polyhedron_base0.backend(P) 'ppl' - sage: Polyhedron_base0.dim(P) - 2 - sage: Polyhedron_base0.ambient_dim(P) - 3 sage: Polyhedron_base0.change_ring(P, ZZ, backend='field').backend() 'field' sage: Polyhedron_base0.base_extend(P, QQ) @@ -409,59 +403,6 @@ def is_immutable(self): """ return True - 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 - @cached_method def n_equations(self): """ @@ -1177,51 +1118,6 @@ def lines_list(self): """ return [list(x) for x in self.line_generator()] - 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 base_ring(self): """ Return the base ring. From bed7308f0d57dc8e4d1c052121217a07156fc888 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 25 Oct 2021 14:21:08 -0700 Subject: [PATCH 13/18] src/sage/geometry/polyhedron/base.py: Remove stray character --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 81f8e630e2a..7de1c996072 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -40,7 +40,7 @@ from sage.misc.misc_c import prod from sage.misc.randstate import current_randstate from sage.misc.superseded import deprecated_function_alias -y + from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA from sage.rings.rational_field import QQ From 078cc56af53928090a86d4dbd00633565a3b4676 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 25 Oct 2021 16:17:55 -0700 Subject: [PATCH 14/18] src/sage/geometry/polyhedron/base.py: Remove unused import --- src/sage/geometry/polyhedron/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4e618af342b..e3d4346bc7a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -39,7 +39,6 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.misc.randstate import current_randstate -from sage.misc.superseded import deprecated_function_alias from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA From 33a2e7bc53d3eb3f23e80e6a8208b4e1608b82c0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 25 Oct 2021 18:45:20 -0700 Subject: [PATCH 15/18] src/sage/geometry/polyhedron/base.py: Fix up imports --- src/sage/geometry/polyhedron/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7de1c996072..085c117a087 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -50,6 +50,7 @@ from sage.matrix.constructor import matrix from sage.arith.misc import integer_floor as floor 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 @@ -10537,6 +10538,7 @@ def edge_label(i, j, c_ij): Qplus = sum(v.column() * v.row() for v in V).pseudoinverse() # Construct the graph. + from sage.graphs.graph import Graph G = Graph() for i in range(len(V)): for j in range(i+1, len(V)): From f0121c596134a37152a3c9b42d05e2d8cc25e6c3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 26 Oct 2021 16:28:02 +0200 Subject: [PATCH 16/18] outsource convex set methods to base1.py --- src/sage/geometry/polyhedron/base.py | 723 +---------------------- src/sage/geometry/polyhedron/base1.py | 802 ++++++++++++++++++++++++++ 2 files changed, 806 insertions(+), 719 deletions(-) create mode 100644 src/sage/geometry/polyhedron/base1.py diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e3d4346bc7a..c3d760ab1cc 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -50,11 +50,10 @@ from sage.functions.other import sqrt, floor, ceil from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.graphs.graph import Graph -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 ######################################################################### @@ -100,7 +99,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Polyhedron_base0, ConvexSet_closed): +class Polyhedron_base(Polyhedron_base1): """ Base class for Polyhedron objects @@ -184,7 +183,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() @@ -194,32 +193,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``. @@ -906,59 +879,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 @@ -1316,91 +1236,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). @@ -1428,171 +1263,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): """ @@ -2174,74 +1844,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 as far as 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. @@ -7956,323 +7558,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..84968dbe01d --- /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, AffineHullProjectionData +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 as far as 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 From 315748c8236d22ee4948fb262bde747a22a7b725 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Oct 2021 11:26:44 -0700 Subject: [PATCH 17/18] src/sage/geometry/abc.pyx: Update unique direct subclass test for ABC Polyhedron --- src/sage/geometry/abc.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/abc.pyx b/src/sage/geometry/abc.pyx index 199db3ec6eb..ada0cac6a14 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 From 2bb33dca58544d1f8b41ff102907b8e46f3040ed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Oct 2021 11:38:44 -0700 Subject: [PATCH 18/18] src/sage/geometry/abc.pyx: Update unique direct subclass test for ABC Polyhedron (fixup) --- src/sage/geometry/abc.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/abc.pyx b/src/sage/geometry/abc.pyx index ada0cac6a14..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