From e9c670c00a492f8f50f3e21e55895a0d3c1d55f2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 5 Jun 2021 19:05:53 -0700 Subject: [PATCH 01/49] sage.geometry.polyhedron.relint, Polyhedron_base.relative_interior, Polyhedron_base.interior: New --- src/sage/geometry/polyhedron/base.py | 54 ++++++++++++++++++++++++++ src/sage/geometry/polyhedron/relint.py | 36 +++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/sage/geometry/polyhedron/relint.py diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..8e36e1dda63 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,6 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron +from .relint import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8394,6 +8395,38 @@ def contains(self, point): __contains__ = contains + @cached_method + def interior(self): + """ + The interior of ``self``. + + OUTPUT: + + - either an empty polyhedron or an instance of + :class:`~sage.geometry.polyhedra.relint.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 + + """ + 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 @@ -8451,6 +8484,27 @@ def interior_contains(self, point): return False return True + @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 + + """ + if self.is_empty() or self.is_universe(): + return self + return RelativeInterior(self) + def relative_interior_contains(self, point): """ Test whether the relative interior of the polyhedron diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/polyhedron/relint.py new file mode 100644 index 00000000000..6075ba970ac --- /dev/null +++ b/src/sage/geometry/polyhedron/relint.py @@ -0,0 +1,36 @@ +r""" +Relative interiors of polyhedra +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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.structure.sage_object import SageObject + +class RelativeInterior(SageObject): + + """ + The relative interior of a polyhedron + """ + + def __init__(self, polyhedron): + self._polyhedron = polyhedron + + def __contains__(self, point): + return self._polyhedron.relative_interior_contains(point) + + def closure(self): + return self._polyhedron + + def _repr_(self): + repr_P = repr(self._polyhedron) + if repr_P.startswith('A '): + repr_P = 'a ' + repr_P[2:] + return 'Relative interior of ' + repr_P From 9df21042f5cb76869b14d54c501bda4c7ef2cbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:07 -0700 Subject: [PATCH 02/49] sage.geometry.relative_interior: Move here from sage.geometry.polyhedron.relint --- src/sage/geometry/polyhedron/base.py | 4 ++-- .../relint.py => relative_interior.py} | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) rename src/sage/geometry/{polyhedron/relint.py => relative_interior.py} (65%) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e36e1dda63..1175d21925f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,7 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron -from .relint import RelativeInterior +from sage.geometry.relative_interior import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8403,7 +8403,7 @@ def interior(self): OUTPUT: - either an empty polyhedron or an instance of - :class:`~sage.geometry.polyhedra.relint.RelativeInterior` + :class:`~sage.geometry.relative_interior.RelativeInterior` EXAMPLES: diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/relative_interior.py similarity index 65% rename from src/sage/geometry/polyhedron/relint.py rename to src/sage/geometry/relative_interior.py index 6075ba970ac..3c20f90d1dd 100644 --- a/src/sage/geometry/polyhedron/relint.py +++ b/src/sage/geometry/relative_interior.py @@ -1,5 +1,5 @@ r""" -Relative interiors of polyhedra +Relative Interiors of Polyhedra and Cones """ # **************************************************************************** @@ -17,7 +17,7 @@ class RelativeInterior(SageObject): """ - The relative interior of a polyhedron + The relative interior of a polyhedron or cone """ def __init__(self, polyhedron): @@ -30,6 +30,18 @@ def closure(self): return self._polyhedron def _repr_(self): + """ + Return a description of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]]) + sage: P.relative_interior()._repr_() + 'Relative interior of a 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices' + sage: P.rename('A') + sage: P.relative_interior()._repr_() + 'Relative interior of A' + """ repr_P = repr(self._polyhedron) if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] From b8bfe200f6e698dec855394af9adaaa63c282abd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:57 -0700 Subject: [PATCH 03/49] ConvexRationalPolyhedralCone: Add methods interior, relative_interior --- src/sage/geometry/cone.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..e7813040521 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -205,6 +205,7 @@ from sage.geometry.toric_lattice import (ToricLattice, is_ToricLattice, is_ToricLatticeQuotient) from sage.geometry.toric_plotter import ToricPlotter, label_list +from sage.geometry.relative_interior import RelativeInterior from sage.graphs.digraph import DiGraph from sage.matrix.all import column_matrix, matrix, MatrixSpace from sage.misc.all import cached_method, flatten, latex @@ -1711,6 +1712,14 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + def interior(self): + r""" + Return the interior of ``self``. + """ + if self.is_solid(): + return self.relative_interior() + return Polyhedron(ambient_dim=self.lattice_dim()) + def relative_interior_contains(self, *args): r""" Check if a given point is contained in the relative interior of ``self``. @@ -1752,6 +1761,14 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + def relative_interior(self): + r""" + Return the relative interior of ``self``. + """ + if self.is_full_space(): + return self + return RelativeInterior(self) + def cartesian_product(self, other, lattice=None): r""" Return the Cartesian product of ``self`` with ``other``. From 6869673199fb80af0a09caf039873e27d50d2741 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:45:02 -0700 Subject: [PATCH 04/49] relative_interior: Fix for dimension 0 --- src/sage/geometry/cone.py | 4 +++- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index e7813040521..a4e11c5a9d3 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1712,6 +1712,7 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + @cached_method def interior(self): r""" Return the interior of ``self``. @@ -1761,11 +1762,12 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + @cached_method def relative_interior(self): r""" Return the relative interior of ``self``. """ - if self.is_full_space(): + if self.is_trivial() or self.is_full_space(): return self return RelativeInterior(self) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1175d21925f..985a9f5c6c5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8500,8 +8500,15 @@ def relative_interior(self): 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 """ - if self.is_empty() or self.is_universe(): + if self.dim() <= 0 or self.is_universe(): return self return RelativeInterior(self) From 021d073e758d37f0abdb2466e3dc59e3383ed05b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 12:40:46 -0700 Subject: [PATCH 05/49] RelativeInterior: Add documentation, tests, comparison methods, method relative_interior --- src/sage/geometry/cone.py | 15 ++- src/sage/geometry/relative_interior.py | 127 ++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index a4e11c5a9d3..29007febb11 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -181,9 +181,18 @@ """ # **************************************************************************** -# Copyright (C) 2010 Volker Braun -# Copyright (C) 2012 Andrey Novoseltsev -# Copyright (C) 2010 William Stein +# Copyright (C) 2010-2014 Volker Braun +# Copyright (C) 2010-2018 Andrey Novoseltsev +# Copyright (C) 2010 William Stein +# Copyright (C) 2012 Christian Stump +# Copyright (C) 2014-2018 Frédéric Chapoton +# Copyright (C) 2014 Peter Bruin +# Copyright (C) 2015-2017 Jori Mäntysalo +# Copyright (C) 2015-2020 Michael Orlitzky +# Copyright (C) 2016-2020 John H. Palmieri +# Copyright (C) 2018 David Coudert +# Copyright (C) 2019-2020 Jonathan Kliem +# Copyright (C) 2020-2021 Matthias Koeppe # # 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 diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3c20f90d1dd..d715b9f708d 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -16,21 +16,96 @@ class RelativeInterior(SageObject): - """ + r""" The relative interior of a polyhedron or cone + + This class should not be used directly. Use methods + :meth:`~sage.geometry.polyhedron.Polyhedron_base.relative_interior`, + :meth:`~sage.geometry.polyhedron.Polyhedron_base.interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.relative_interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.interior` instead. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.relative_interior() + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: octant.relative_interior() + Relative interior of 3-d cone in 3-d lattice N + """ def __init__(self, polyhedron): + r""" + Initialize ``self``. + + INPUT: + + - ``polyhedron`` - an instance of :class:`Polyhedron_base` or + :class:`ConvexRationalPolyhedralCone`. + + TESTS:: + + sage: P = Polyhedron() + sage: from sage.geometry.relative_interior import RelativeInterior + sage: TestSuite(RelativeInterior(P)).run() + + """ self._polyhedron = polyhedron def __contains__(self, point): + r""" + Return whether ``self`` contains ``point``. + + EXAMPLES:: + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: (1, 1, 1) in ri_octant + True + sage: (1, 0, 0) in ri_octant + False + """ return self._polyhedron.relative_interior_contains(point) + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + As ``self`` is already relatively open, this method just returns ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.relative_interior() is ri_segment + True + """ + return self + def closure(self): + r""" + Return the topological closure of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.closure() is segment + True + + """ return self._polyhedron def _repr_(self): - """ + r""" Return a description of ``self``. EXAMPLES:: @@ -46,3 +121,51 @@ def _repr_(self): if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] return 'Relative interior of ' + repr_P + + def __eq__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment == ri_segment2 + True + + """ + return self._polyhedron == other._polyhedron + + def __ne__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + TESTS:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment != ri_segment2 + False + + """ + return self._polyhedron != other._polyhedron From 8f38e0475edfd5913bf25ede90162b04597db64c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 13:02:44 -0700 Subject: [PATCH 06/49] ConvexRationalPolyhedralCone.interior, relative_interior: Add doctests --- src/sage/geometry/cone.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 29007febb11..6d2a49eec66 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1725,6 +1725,34 @@ def interior_contains(self, *args): def interior(self): r""" Return the interior of ``self``. + + OUTPUT: + + - either ``self``, an empty polyhedron, or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.interior() + The empty polyhedron in ZZ^3 + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.interior() + The empty polyhedron in ZZ^2 + + sage: K = cones.nonnegative_orthant(2); K + 2-d cone in 2-d lattice N + sage: K.interior() + Relative interior of 2-d cone in 2-d lattice N + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.interior() is K2 + True + """ if self.is_solid(): return self.relative_interior() @@ -1775,6 +1803,29 @@ def relative_interior_contains(self, *args): def relative_interior(self): r""" Return the relative interior of ``self``. + + OUTPUT: + + - either ``self`` or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.relative_interior() + Relative interior of 2-d cone in 3-d lattice N + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.relative_interior() is origin + True + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.relative_interior() is K2 + True + """ if self.is_trivial() or self.is_full_space(): return self From 669a161dfb16a5e725183df86b30619a452f3e6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 15:36:10 -0700 Subject: [PATCH 07/49] ConvexSet_{base,closed,relatively_open}: New; make Polyhedron_base, LatticePolytopeClass, ConvexRationalPolyhedralCone subclasses --- src/sage/geometry/cone.py | 3 +- src/sage/geometry/convex_set.py | 157 ++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 5 +- src/sage/geometry/polyhedron/base.py | 3 +- 4 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/sage/geometry/convex_set.py diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..243314c173c 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -213,6 +213,7 @@ from sage.structure.all import SageObject, parent from sage.structure.richcmp import richcmp_method, richcmp from sage.geometry.integral_points import parallelotope_points +from sage.geometry.convex_set import ConvexSet_closed from sage.misc.lazy_import import lazy_import from sage.features import PythonModule @@ -1375,7 +1376,7 @@ def classify_cone_2d(ray0, ray1, check=True): # and ``ambient_ray_indices`` keyword parameters. See ``intersection`` method # for an example why this is needed. @richcmp_method -class ConvexRationalPolyhedralCone(IntegralRayCollection, Container): +class ConvexRationalPolyhedralCone(IntegralRayCollection, Container, ConvexSet_closed): r""" Create a convex rational polyhedral cone. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py new file mode 100644 index 00000000000..ed8a72ca225 --- /dev/null +++ b/src/sage/geometry/convex_set.py @@ -0,0 +1,157 @@ +r""" +Convex Sets +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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.structure.sage_object import SageObject +from sage.misc.abstract_method import abstract_method + +class ConvexSet_base(SageObject): + + """ + Abstract base class for convex sets. + """ + + @abstract_method + def is_empty(self): + r""" + Test whether ``self`` is the empty set + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_universe(self): + r""" + Test whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_full_dimensional(self): + r""" + Return whether ``self`` is full dimensional. + + OUTPUT: + + Boolean. Whether the polyhedron is not contained in any strict + affine subspace. + + """ + + @abstract_method + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + if self.is_open(): + return True + raise NotImplementedError + + @abstract_method + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + + def closure(self): + r""" + Return the topological closure of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + def interior(self): + r""" + Return the topological interior of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + +class ConvexSet_closed(ConvexSet_base): + + r""" + Abstract base class for closed convex sets. + """ + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + """ + return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() + + +class ConvexSet_relatively_open(ConvexSet_base): + + r""" + Abstract base class for relatively open sets. + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index cc5d4303897..0456f5cc14b 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,6 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp +from sage.geometry.convex_set import ConvexSet_closed from copy import copy from collections.abc import Hashable @@ -464,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(SageObject, Hashable): +class LatticePolytopeClass(ConvexSet_closed, Hashable): r""" Create a lattice polytope. @@ -517,6 +518,8 @@ def __init__(self, points=None, compute_vertices=None, sage: LatticePolytope([(1,2,3), (4,5,6)]) # indirect test 1-d lattice polytope in 3-d lattice M + sage: TestSuite(_).run() + """ if ambient is None: self._ambient = self diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..5cfb0278aef 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,6 +51,7 @@ 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 from .constructor import Polyhedron from sage.categories.sets_cat import EmptySetError @@ -98,7 +99,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Element): +class Polyhedron_base(Element, ConvexSet_closed): """ Base class for Polyhedron objects From e67f75377b140110ccd28fc9fa54b6e090b077cd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 20:33:52 -0700 Subject: [PATCH 08/49] ConvexRationalPolyhedralCone: Add method is_empty and aliases is_full_dimensional, is_universe --- src/sage/geometry/cone.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 243314c173c..8838d58b654 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3191,6 +3191,14 @@ def is_smooth(self): return False return self.rays().matrix().elementary_divisors() == [1] * self.nrays() + def is_empty(self): + """ + Return whether ``self`` is the empty set. + + Because a cone always contains the origin, this method returns ``False``. + """ + return False + def is_trivial(self): """ Checks if the cone has no rays. @@ -4448,6 +4456,8 @@ def is_solid(self): A cone is said to be solid if it has nonempty interior. That is, if its extreme rays span the entire ambient space. + An alias is :meth:`is_full_dimensional`. + OUTPUT: ``True`` if this cone is solid, and ``False`` otherwise. @@ -4487,6 +4497,8 @@ def is_solid(self): """ return (self.dim() == self.lattice_dim()) + is_full_dimensional = is_solid + def is_proper(self): r""" Check if this cone is proper. @@ -4537,6 +4549,8 @@ def is_full_space(self): r""" Check if this cone is equal to its ambient vector space. + An alias is :meth:`is_universe`. + OUTPUT: ``True`` if this cone equals its entire ambient vector @@ -4574,6 +4588,8 @@ def is_full_space(self): """ return self.linear_subspace() == self.lattice().vector_space() + is_universe = is_full_space + def lineality(self): r""" Return the lineality of this cone. From 770fdcd9125c8bb78dd45573613e6489220664a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:52:29 -0700 Subject: [PATCH 09/49] LatticePolytopeClass, IntegralRayCollection: Make ambient_dim an alias for lattice_dim --- src/sage/geometry/cone.py | 4 ++++ src/sage/geometry/convex_set.py | 5 +++++ src/sage/geometry/lattice_polytope.py | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8838d58b654..07df0fc575e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1010,6 +1010,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -1024,6 +1026,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def nrays(self): r""" Return the number of rays of ``self``. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index ed8a72ca225..197aecac9be 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -42,6 +42,11 @@ def is_universe(self): """ @abstract_method + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + """ + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 0456f5cc14b..10866bc8c31 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2599,6 +2599,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -2613,6 +2615,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. From b2ac6396c075f8ad4d71a379de367fd9cbaa3561 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:22 -0700 Subject: [PATCH 10/49] ConvexSet_base.dim: New --- src/sage/geometry/convex_set.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 197aecac9be..56189f2518c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -21,7 +21,6 @@ class ConvexSet_base(SageObject): Abstract base class for convex sets. """ - @abstract_method def is_empty(self): r""" Test whether ``self`` is the empty set @@ -30,8 +29,8 @@ def is_empty(self): Boolean. """ + return self.dim() < 0 - @abstract_method def is_universe(self): r""" Test whether ``self`` is the whole ambient space @@ -40,6 +39,15 @@ def is_universe(self): Boolean. """ + if not self.is_full_dimensional(): + return False + raise NotImplementedError + + @abstract_method + def dim(self): + r""" + Return the dimension of ``self``. + """ @abstract_method def ambient_dim(self): @@ -55,8 +63,8 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. - """ + return self.dim() == self.ambient_dim() @abstract_method def is_open(self): @@ -105,7 +113,7 @@ def interior(self): r""" Return the topological interior of ``self``. """ - if self.is_closed(): + if self.is_open(): return self raise NotImplementedError From ccc84213906ff6bf34d2e5c6b50d146a2901ec9c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:52 -0700 Subject: [PATCH 11/49] ConvexSet_base.is_compact, ConvexSet_compact: New --- src/sage/geometry/convex_set.py | 43 +++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 4 +-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 56189f2518c..37ef89ae39e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -101,6 +101,21 @@ def is_closed(self): """ + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + if not self.is_closed(): + return False + if self.dimension() < 1: + return True + raise NotImplementedError + def closure(self): r""" Return the topological closure of ``self``. @@ -152,6 +167,34 @@ def is_open(self): return self.is_empty() or self.is_universe() +class ConvexSet_compact(ConvexSet_closed): + + r""" + Abstract base class for compact convex sets. + """ + + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + return self.ambient_dim() == 0 and not self.is_empty() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + return True + + class ConvexSet_relatively_open(ConvexSet_base): r""" diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 10866bc8c31..a063657e30a 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,7 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp -from sage.geometry.convex_set import ConvexSet_closed +from sage.geometry.convex_set import ConvexSet_compact from copy import copy from collections.abc import Hashable @@ -465,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(ConvexSet_closed, Hashable): +class LatticePolytopeClass(ConvexSet_compact, Hashable): r""" Create a lattice polytope. From 5f9c8526d013234f9caf33c50b9117812fbaad55 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 11:55:20 -0700 Subject: [PATCH 12/49] RelativeInterior.interior: New --- src/sage/geometry/relative_interior.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index d715b9f708d..65fd898ffd1 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -71,6 +71,27 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def interior(self): + r""" + Return the interior of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.interior() + The empty polyhedron in ZZ^2 + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: ri_octant.interior() is ri_octant + True + """ + return self._polyhedron.interior() + def relative_interior(self): r""" Return the relative interior of ``self``. From 5c089ec4926664d426e5d46735cb8c1a9b1d1017 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:01:39 -0700 Subject: [PATCH 13/49] RelativeInterior.__eq__, __ne__: Handle comparisons with objects of other types --- src/sage/geometry/relative_interior.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 65fd898ffd1..73e65d4361e 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -149,7 +149,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object EXAMPLES:: @@ -164,7 +164,15 @@ def __eq__(self, other): sage: ri_segment == ri_segment2 True + TESTS:: + + sage: empty = Polyhedron(ambient_dim=2) + sage: ri_segment == empty + False + """ + if type(self) != type(other): + return False return self._polyhedron == other._polyhedron def __ne__(self, other): @@ -173,7 +181,7 @@ def __ne__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object TESTS:: @@ -189,4 +197,4 @@ def __ne__(self, other): False """ - return self._polyhedron != other._polyhedron + return not (self == other) From 86ce301c02936f4b56f6ab821520e8542bd5fbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:38:05 -0700 Subject: [PATCH 14/49] Polyhedron_base.is_relatively_open: New; fix relative_interior for affine subspaces --- src/sage/geometry/polyhedron/base.py | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 985a9f5c6c5..b49ab2a8d38 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8484,6 +8484,39 @@ def interior_contains(self, point): 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): """ @@ -8507,8 +8540,12 @@ def relative_interior(self): 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.dim() <= 0 or self.is_universe(): + if self.is_relatively_open(): return self return RelativeInterior(self) From 4bac2fe2db4519478d6e5633e86b29098d9aa0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:53:33 -0700 Subject: [PATCH 15/49] RelativeInterior: Subclass ConvexSet_relatively_open, add missing methods --- src/sage/geometry/convex_set.py | 13 +++++- src/sage/geometry/relative_interior.py | 59 +++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 37ef89ae39e..5a531dd9c44 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -203,7 +203,7 @@ class ConvexSet_relatively_open(ConvexSet_base): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. OUTPUT: @@ -211,3 +211,14 @@ def is_relatively_open(self): """ return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_full_dimensional() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..c41012835eb 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -12,9 +12,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.sage_object import SageObject +from sage.geometry.convex_set import ConvexSet_relatively_open -class RelativeInterior(SageObject): +class RelativeInterior(ConvexSet_relatively_open): r""" The relative interior of a polyhedron or cone @@ -71,6 +71,40 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.ambient_dim() + 2 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_dim() + 2 + + """ + return self._polyhedron.ambient_dim() + + def dim(self): + r""" + Return the dimension of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.dim() + 1 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.dim() + 1 + + """ + return self._polyhedron.dim() + def interior(self): r""" Return the interior of ``self``. @@ -125,6 +159,27 @@ def closure(self): """ return self._polyhedron + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_closed() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_relatively_open() + return False + def _repr_(self): r""" Return a description of ``self``. From 216cb811d16bdf1bedfb2d7e04339ed4387f7212 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:01:25 -0700 Subject: [PATCH 16/49] ConvexRationalPolyhedralCone.is_relatively_open: New, fix ConvexRationalPolyhedralCone.relative_interior for linear subspaces --- src/sage/geometry/cone.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 6d2a49eec66..46b7d88b2e9 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1821,13 +1821,18 @@ def relative_interior(self): sage: origin.relative_interior() is origin True + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.relative_interior() is K1 + True + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True """ - if self.is_trivial() or self.is_full_space(): + if self.is_relatively_open(): return self return RelativeInterior(self) @@ -4724,6 +4729,29 @@ def lineality(self): """ return self.linear_subspace().dimension() + def is_relatively_open(self): + + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.is_relatively_open() + False + + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.is_relatively_open() + True + + """ + return self.lineality() == self.dim() + @cached_method def discrete_complementarity_set(self): r""" From 44cde1e3dbea9086e75f235c0f61164278e4e451 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:24:42 -0700 Subject: [PATCH 17/49] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/relative_interior --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 3d027326933..88dbc812caf 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -107,6 +107,7 @@ Miscellaneous sage/geometry/linear_expression sage/geometry/newton_polygon + sage/geometry/relative_interior sage/geometry/ribbon_graph sage/geometry/pseudolines sage/geometry/voronoi_diagram From fa4c2d238a8f16a61f02e7cfa89ba3f17d8f4980 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 15:07:47 -0700 Subject: [PATCH 18/49] Whitespace fixes --- src/sage/geometry/cone.py | 3 --- src/sage/geometry/relative_interior.py | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 46b7d88b2e9..8e3e73d75dd 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1830,7 +1830,6 @@ def relative_interior(self): 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True - """ if self.is_relatively_open(): return self @@ -4730,7 +4729,6 @@ def lineality(self): return self.linear_subspace().dimension() def is_relatively_open(self): - r""" Return whether ``self`` is relatively open. @@ -4748,7 +4746,6 @@ def is_relatively_open(self): 1-d cone in 2-d lattice N sage: K1.is_relatively_open() True - """ return self.lineality() == self.dim() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..0850ea4860c 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -14,8 +14,8 @@ from sage.structure.sage_object import SageObject -class RelativeInterior(SageObject): +class RelativeInterior(SageObject): r""" The relative interior of a polyhedron or cone @@ -34,7 +34,6 @@ class RelativeInterior(SageObject): sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) sage: octant.relative_interior() Relative interior of 3-d cone in 3-d lattice N - """ def __init__(self, polyhedron): @@ -51,7 +50,6 @@ def __init__(self, polyhedron): sage: P = Polyhedron() sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() - """ self._polyhedron = polyhedron @@ -121,7 +119,6 @@ def closure(self): a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.closure() is segment True - """ return self._polyhedron @@ -169,7 +166,6 @@ def __eq__(self, other): sage: empty = Polyhedron(ambient_dim=2) sage: ri_segment == empty False - """ if type(self) != type(other): return False @@ -195,6 +191,5 @@ def __ne__(self, other): a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices sage: ri_segment != ri_segment2 False - """ return not (self == other) From dede9de1a8e01114b289149bccb51c466223d027 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 16:47:05 -0700 Subject: [PATCH 19/49] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/convex_set --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 88dbc812caf..6975fa144a7 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -105,6 +105,7 @@ Miscellaneous .. toctree:: :maxdepth: 1 + sage/geometry/convex_set sage/geometry/linear_expression sage/geometry/newton_polygon sage/geometry/relative_interior From 007e6fda9fb5dfccf109b06d15f8799f7f03ac5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 13:33:20 -0700 Subject: [PATCH 20/49] ConvexRationalPolyhedralCone.is_empty: Add doctest --- src/sage/geometry/cone.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index dc43db1a6a0..aa1acf765ac 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3283,6 +3283,13 @@ def is_empty(self): Return whether ``self`` is the empty set. Because a cone always contains the origin, this method returns ``False``. + + EXAMPLES:: + + sage: trivial_cone = cones.trivial(3) + sage: trivial_cone.is_empty() + False + """ return False From 73e39ce9c9121c34da6d3ced36b36262ae4e9026 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:43:40 -0700 Subject: [PATCH 21/49] ConvexRationalPolyhedralCone.is_compact: Define --- src/sage/geometry/cone.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index aa1acf765ac..8e42648aef5 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3311,6 +3311,8 @@ def is_trivial(self): """ return self.nrays() == 0 + is_compact = is_trivial + def is_strictly_convex(self): r""" Check if ``self`` is strictly convex. From 92f0610e33b15cba97247b787bbcc0a9593d9d34 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:45:44 -0700 Subject: [PATCH 22/49] ConvexSet_open: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 5a531dd9c44..f77498b4fb1 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -196,9 +196,8 @@ def is_compact(self): class ConvexSet_relatively_open(ConvexSet_base): - r""" - Abstract base class for relatively open sets. + Abstract base class for relatively open convex sets. """ def is_relatively_open(self): @@ -222,3 +221,31 @@ def is_open(self): """ return self.is_full_dimensional() + + +class ConvexSet_open(ConvexSet_relatively_open): + r""" + Abstract base class for open convex sets. + """ + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() From 03a31efc560cffebe2cf270ae7d460302307ac95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:48:50 -0700 Subject: [PATCH 23/49] Polyhedron_base.is_full_dimensional: Merge into ConvexSet_base.is_full_dimensional --- src/sage/geometry/convex_set.py | 11 +++++++++++ src/sage/geometry/polyhedron/base.py | 18 ------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f77498b4fb1..cd8dd1d9911 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,17 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.is_full_dimensional() + False + + sage: polytopes.hypercube(3).is_full_dimensional() + True + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() + False """ return self.dim() == self.ambient_dim() diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 03ee54f8077..8e242132313 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -9814,24 +9814,6 @@ def edge_label(i, j, c_ij): else: return MatrixGroup(matrices) - def is_full_dimensional(self): - """ - Return whether the polyhedron is full dimensional. - - OUTPUT: - - Boolean. Whether the polyhedron is not contained in any strict - affine subspace. - - EXAMPLES:: - - sage: polytopes.hypercube(3).is_full_dimensional() - True - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() - False - """ - return self.dim() == self.ambient_dim() - def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): r""" Return whether the polyhedron is combinatorially isomorphic to another polyhedron. From a71507e11c33cbc2c61600b76098a814e6132f1d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:49:54 -0700 Subject: [PATCH 24/49] ConvexSet_base: Add some doctests --- src/sage/geometry/convex_set.py | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index cd8dd1d9911..53caea0b12b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -16,7 +16,6 @@ from sage.misc.abstract_method import abstract_method class ConvexSet_base(SageObject): - """ Abstract base class for convex sets. """ @@ -28,6 +27,14 @@ def is_empty(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p + -1-d lattice polytope in 3-d lattice M + sage: p.is_empty() + True + """ return self.dim() < 0 @@ -90,12 +97,24 @@ def is_open(self): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. + + The default implementation of this method only knows that open + sets are also relatively open. OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_open(self): + ....: return True + sage: ExampleSet().is_relatively_open() + True + """ if self.is_open(): return True @@ -123,13 +142,20 @@ def is_compact(self): """ if not self.is_closed(): return False - if self.dimension() < 1: + if self.dim() < 1: return True raise NotImplementedError def closure(self): r""" Return the topological closure of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: C = ConvexSet_closed() + sage: C.closure() is C + True """ if self.is_closed(): return self @@ -138,6 +164,13 @@ def closure(self): def interior(self): r""" Return the topological interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: C = ConvexSet_open() + sage: C.interior() is C + True """ if self.is_open(): return self @@ -151,7 +184,6 @@ def affine_hull(self): class ConvexSet_closed(ConvexSet_base): - r""" Abstract base class for closed convex sets. """ @@ -179,7 +211,6 @@ def is_open(self): class ConvexSet_compact(ConvexSet_closed): - r""" Abstract base class for compact convex sets. """ From 3a831826846c776c95a51133e1b4e216d248acc3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:23 -0700 Subject: [PATCH 25/49] ConvexSet_base.relative_interior: New --- src/sage/geometry/convex_set.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 53caea0b12b..114018dc629 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -176,6 +176,21 @@ def interior(self): return self raise NotImplementedError + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_relatively_open + sage: C = ConvexSet_relatively_open() + sage: C.relative_interior() is C + True + """ + if self.is_relatively_open(): + return self + raise NotImplementedError + @abstract_method(optional=True) def affine_hull(self): r""" From 6a0baac5f679d90a1acd3696f3613350af5af9f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:51 -0700 Subject: [PATCH 26/49] ConvexSet_base._test_convex_set: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 114018dc629..8c5a0019c16 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -197,6 +197,37 @@ def affine_hull(self): Return the affine hull of ``self``. """ + def _test_convex_set(self, tester=None, **options): + """ + Run some tests on the methods of :class:`ConvexSet_base`. + """ + if tester is None: + tester = self._tester(**options) + dim = self.dim() + tester.assertTrue(dim <= self.ambient_dim()) + if self.is_empty(): + tester.assertTrue(dim == -1) + if self.is_universe(): + tester.assertTrue(self.is_full_dimensional()) + cl_self = self.closure() + try: + int_self = self.interior() + except NotImplementedError: + int_self = None + try: + relint_self = self.relative_interior() + except NotImplementedError: + relint_self = None + if self.is_full_dimensional(): + tester.assertTrue(int_self == relint_self) + if self.is_relatively_open(): + tester.assertTrue(self == relint_self) + if self.is_open(): + tester.assertTrue(self == int_self) + if self.is_closed(): + tester.assertTrue(self == cl_self) + if self.is_compact(): + tester.assertTrue(self.is_closed()) class ConvexSet_closed(ConvexSet_base): r""" From 0495bb05c37f55257b892c6790234d9fb5dd77f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 16:34:03 -0700 Subject: [PATCH 27/49] RelativeInterior: Fix up doctest --- src/sage/geometry/relative_interior.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index e0dfc9ce6b9..795ef408692 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -47,7 +47,7 @@ def __init__(self, polyhedron): TESTS:: - sage: P = Polyhedron() + sage: P = Polyhedron([[1, 2], [3, 4]]) sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() """ From 9a7ce3a53d63ace6ed56f4addc92f61b85549b32 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 17:22:27 -0700 Subject: [PATCH 28/49] src/sage/geometry/convex_set.py: More examples and tests --- src/sage/geometry/convex_set.py | 141 ++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8c5a0019c16..9ea892c99b9 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -22,7 +22,7 @@ class ConvexSet_base(SageObject): def is_empty(self): r""" - Test whether ``self`` is the empty set + Test whether ``self`` is the empty set. OUTPUT: @@ -34,13 +34,12 @@ def is_empty(self): -1-d lattice polytope in 3-d lattice M sage: p.is_empty() True - """ return self.dim() < 0 def is_universe(self): r""" - Test whether ``self`` is the whole ambient space + Test whether ``self`` is the whole ambient space. OUTPUT: @@ -84,23 +83,39 @@ def is_full_dimensional(self): """ return self.dim() == self.ambient_dim() - @abstract_method def is_open(self): r""" Return whether ``self`` is open. + The default implementation of this method only knows that the + empty set and the ambient space are open. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_empty(self): + ....: return False + ....: def is_universe(self): + ....: return True + sage: ExampleSet().is_open() + True """ + if self.is_empty() or self.is_universe(): + return True + raise NotImplementedError def is_relatively_open(self): r""" Return whether ``self`` is relatively open. The default implementation of this method only knows that open - sets are also relatively open. + sets are also relatively open, and in addition singletons are + relatively open. OUTPUT: @@ -114,31 +129,55 @@ def is_relatively_open(self): ....: return True sage: ExampleSet().is_relatively_open() True - """ if self.is_open(): return True + if self.dim() == 0: + return True raise NotImplementedError - @abstract_method def is_closed(self): r""" Return whether ``self`` is closed. + The default implementation of this method only knows that the + empty set, a singleton set, and the ambient space are closed. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_closed() + True """ + if self.is_empty() or self.dim() == 0 or self.is_universe(): + return True + raise NotImplementedError def is_compact(self): r""" Return whether ``self`` is compact. + The default implementation of this method only knows that a + non-closed set cannot be compact, and that the empty set and + a singleton set are compact. + OUTPUT: Boolean. + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_compact() + True """ if not self.is_closed(): return False @@ -200,6 +239,32 @@ def affine_hull(self): def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class FaultyConvexSet(ConvexSet_open): + ....: def is_universe(self): + ....: return True + ....: def dim(self): + ....: return 42 + ....: def ambient_dim(self): + ....: return 91 + sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + + sage: class BiggerOnTheInside(ConvexSet_open): + ....: def dim(self): + ....: return 100000 + ....: def ambient_dim(self): + ....: return 3 + sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + """ if tester is None: tester = self._tester(**options) @@ -241,6 +306,12 @@ def is_closed(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_closed() + True """ return True @@ -252,6 +323,15 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_open() + False + + sage: zerocube = polytopes.hypercube(0) + sage: zerocube.is_open() + True """ return self.is_empty() or self.is_universe() @@ -268,6 +348,16 @@ def is_universe(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_universe() + False + sage: point0 = LatticePolytope([[]]); point0 + 0-d reflexive polytope in 0-d lattice M + sage: point0.is_universe() + True """ return self.ambient_dim() == 0 and not self.is_empty() @@ -279,9 +369,16 @@ def is_compact(self): Boolean. + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_compact() + True """ return True + is_relatively_open = ConvexSet_closed.is_open + class ConvexSet_relatively_open(ConvexSet_base): r""" @@ -296,6 +393,12 @@ def is_relatively_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_relatively_open() + True """ return True @@ -307,8 +410,14 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_open() + False """ - return self.is_full_dimensional() + return self.is_empty() or self.is_full_dimensional() class ConvexSet_open(ConvexSet_relatively_open): @@ -324,6 +433,12 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: b = ConvexSet_open() + sage: b.is_open() + True """ return True @@ -335,5 +450,15 @@ def is_closed(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class OpenBall(ConvexSet_open): + ....: def dim(self): + ....: return 3 + ....: def is_universe(self): + ....: return False + sage: OpenBall().is_closed() + False """ return self.is_empty() or self.is_universe() From e2b0ef7390426c7d8f6f7f0a1382b326d5c9bc6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 18:39:17 -0700 Subject: [PATCH 29/49] ConvexSet_base._test_convex_set: Fix doctest output --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 9ea892c99b9..4372d90a1cd 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -251,7 +251,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 91 sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -261,7 +261,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set From 45c840a98ea222b30847b6ae8411d52f7cd778ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:41:58 -0700 Subject: [PATCH 30/49] ConvexSet_base.codim, codimension: New --- src/sage/geometry/cone.py | 3 +++ src/sage/geometry/convex_set.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8e42648aef5..eab23498e85 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1225,8 +1225,11 @@ def codim(self): sage: K.codim() == K.dual().lineality() True """ + # same as ConvexSet_base.codim; the main point is the much more detailed + # docstring. return (self.lattice_dim() - self.dim()) + codimension = codim def span(self, base_ring=None): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 4372d90a1cd..dab6e61b25e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -61,6 +61,21 @@ def ambient_dim(self): Return the dimension of the ambient space. """ + def codimension(self): + r""" + Return the codimension of ``self``. + + An alias is :meth:`codim`. + + EXAMPLES:: + + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + 2 + """ + return self.ambient_dim() - self.dim() + + codim = codimension + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. @@ -269,7 +284,10 @@ def _test_convex_set(self, tester=None, **options): if tester is None: tester = self._tester(**options) dim = self.dim() + codim = self.codim() tester.assertTrue(dim <= self.ambient_dim()) + if dim >= 0: + tester.assertTrue(dim + codim == self.ambient_dim()) if self.is_empty(): tester.assertTrue(dim == -1) if self.is_universe(): From 17467c498978ca9f165b0619b30d5ab2fc84272b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:43:20 -0700 Subject: [PATCH 31/49] ConvexSet_base: Make dimension, ambient_dimension aliases for dim, ambient_dim --- src/sage/geometry/convex_set.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index dab6e61b25e..d2ae244dd3c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -55,12 +55,46 @@ def dim(self): Return the dimension of ``self``. """ + def dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 42 + sage: ExampleSet().dimension() + 42 + """ + return self.dim() + @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient space. """ + def ambient_dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`ambient_dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_dim(self): + ....: return 91 + sage: ExampleSet().ambient_dimension() + 91 + """ + return self.ambient_dim() + def codimension(self): r""" Return the codimension of ``self``. From fa5dc6eb2c696c0094261c76b72a16cb0bc92846 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:47:03 -0700 Subject: [PATCH 32/49] ConvexSet_base.cartesian_product: New --- src/sage/geometry/convex_set.py | 29 ++++++++++++++++++++++------ src/sage/geometry/polyhedron/base.py | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d2ae244dd3c..465e975c15c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -279,12 +279,6 @@ def relative_interior(self): return self raise NotImplementedError - @abstract_method(optional=True) - def affine_hull(self): - r""" - Return the affine hull of ``self``. - """ - def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. @@ -346,6 +340,29 @@ def _test_convex_set(self, tester=None, **options): if self.is_compact(): tester.assertTrue(self.is_closed()) + # Optional methods + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + @abstract_method(optional=True) + def cartesian_product(self, other): + """ + Return the Cartesian product. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The Cartesian product of ``self`` and ``other``. + """ + + class ConvexSet_closed(ConvexSet_base): r""" Abstract base class for closed convex sets. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e242132313..1d17d27df09 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4817,6 +4817,8 @@ def product(self, other): _mul_ = product + cartesian_product = product + def _test_product(self, tester=None, **options): """ Run tests on the method :meth:`.product`. From f4bdffda473c608289d6b8c90a3687484d24f3be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:58:07 -0700 Subject: [PATCH 33/49] ConvexSet_base.contains, intersection: New --- src/sage/geometry/convex_set.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 465e975c15c..6827b0a10d8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -362,6 +362,30 @@ def cartesian_product(self, other): The Cartesian product of ``self`` and ``other``. """ + @abstract_method(optional=True) + def contains(self, point): + """ + Test whether ``self`` contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + """ + + @abstract_method(optional=True) + def intersection(self, other): + r""" + Return the intersection of ``self`` and ``other``. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The intersection. + """ + class ConvexSet_closed(ConvexSet_base): r""" From ee00642b382baf7b5ee7ed3b42e5bf6dcaa9fb64 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:19 -0700 Subject: [PATCH 34/49] ConvexSet_base.ambient: New; clarify ambient_dim, ambient_dimension, codimension --- src/sage/geometry/convex_set.py | 16 +++++++++++++--- src/sage/geometry/polyhedron/base.py | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..f34bcb78493 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,15 +72,21 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient(self): + r""" + Return the ambient convex set or space. + """ + @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. @@ -97,7 +103,7 @@ def ambient_dimension(self): def codimension(self): r""" - Return the codimension of ``self``. + Return the codimension of ``self`` in `self.ambient()``. An alias is :meth:`codim`. @@ -287,6 +293,8 @@ def _test_convex_set(self, tester=None, **options): sage: from sage.geometry.convex_set import ConvexSet_open sage: class FaultyConvexSet(ConvexSet_open): + ....: def ambient(self): + ....: return QQ^55 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -301,6 +309,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient(self): + ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..0f9d65c4594 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2478,7 +2478,7 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient_space = Vrepresentation_space + ambient = ambient_space = Vrepresentation_space def Hrepresentation_space(self): r""" From 2c756d43f92db60d12be732c515fee2a0928ea99 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:41 -0700 Subject: [PATCH 35/49] PolyhedronFace: Make it a subclass of ConvexSet_closed --- src/sage/geometry/polyhedron/face.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 7a00f46ab19..12baa5425b8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -78,12 +78,12 @@ from sage.misc.all import cached_method from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix - +from sage.geometry.convex_set import ConvexSet_closed ######################################################################### @richcmp_method -class PolyhedronFace(SageObject): +class PolyhedronFace(ConvexSet_closed): r""" A face of a polyhedron. @@ -121,6 +121,11 @@ class PolyhedronFace(SageObject): (An inequality (1, 1, 1) x + 1 >= 0,) sage: face.ambient_Vrepresentation() (A vertex at (-1, 0, 0), A vertex at (0, -1, 0), A vertex at (0, 0, -1)) + + TESTS:: + + sage: TestSuite(face).run() + """ def __init__(self, polyhedron, V_indices, H_indices): @@ -147,6 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 + sage: TestSuite(_).run() """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -649,6 +655,8 @@ def polyhedron(self): """ return self._polyhedron + ambient = polyhedron + @cached_method def as_polyhedron(self): """ From 8d77b3e67c27e24ccae8e529eddb4beece0e8a30 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:47:17 -0700 Subject: [PATCH 36/49] PolyhedronFace.is_relatively_open, is_compact: New, add doctests --- src/sage/geometry/polyhedron/face.py | 61 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 12baa5425b8..31f0de091e1 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -124,7 +124,7 @@ class PolyhedronFace(ConvexSet_closed): TESTS:: - sage: TestSuite(face).run() + sage: TestSuite(face).run(skip='_test_pickling') """ @@ -152,7 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 - sage: TestSuite(_).run() + sage: TestSuite(_).run(skip='_test_pickling') """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -186,6 +186,10 @@ def vertex_generator(self): A vertex at (1, 1) sage: type(face.vertex_generator()) <... 'generator'> + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_vertex(): @@ -206,6 +210,10 @@ def vertices(self): sage: face = triangle.faces(1)[2] sage: face.vertices() (A vertex at (0, 1), A vertex at (1, 0)) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return tuple(self.vertex_generator()) @@ -224,6 +232,10 @@ def n_vertices(self): sage: face = Q.faces(2)[0] sage: face.n_vertices() 3 + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return len(self.vertices()) @@ -237,6 +249,10 @@ def ray_generator(self): sage: face = pi.faces(1)[1] sage: next(face.ray_generator()) A ray in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_ray(): @@ -288,6 +304,10 @@ def line_generator(self): sage: face = pr.faces(1)[0] sage: next(face.line_generator()) A line in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_line(): @@ -657,6 +677,43 @@ def polyhedron(self): ambient = polyhedron + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_relatively_open() + True + """ + return self.as_polyhedron().is_relatively_open() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_compact() + False + """ + return not any(V.is_ray() or V.is_line() + for V in self.ambient_Vrepresentation()) + @cached_method def as_polyhedron(self): """ From 6ba5d9c2bd5dca867d632b9f569407ac5f758e6f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:23:51 -0700 Subject: [PATCH 37/49] ConvexSet_base.ambient_vector_space: New --- src/sage/geometry/cone.py | 20 ++++++++++++++++ src/sage/geometry/convex_set.py | 10 ++++++++ src/sage/geometry/lattice_polytope.py | 20 ++++++++++++++++ src/sage/geometry/polyhedron/base.py | 34 +++++++++++++++++++++++++-- src/sage/geometry/polyhedron/face.py | 23 ++++++++++++++++++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index eab23498e85..fd2074676c7 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -981,6 +981,26 @@ def lattice(self): """ return self._lattice + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: c.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + @cached_method def dual_lattice(self): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f34bcb78493..fbb297d4ae7 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,6 +72,12 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + """ + @abstract_method def ambient(self): r""" @@ -295,6 +301,8 @@ def _test_convex_set(self, tester=None, **options): sage: class FaultyConvexSet(ConvexSet_open): ....: def ambient(self): ....: return QQ^55 + ....: def ambient_vector_space(self, base_field=None): + ....: return QQ^16 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -309,6 +317,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient_vector_space(self): + ....: return QQ^3 ....: def ambient(self): ....: return QQ^3 ....: def ambient_dim(self): diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index a063657e30a..bde2f0a6be7 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2617,6 +2617,26 @@ def lattice_dim(self): ambient_dim = lattice_dim + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: p = LatticePolytope([(1,0)]) + sage: p.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: p.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0f9d65c4594..67208a3b5d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2462,7 +2462,7 @@ def bounded_edges(self): def Vrepresentation_space(self): r""" - Return the ambient vector space. + Return the ambient free module. OUTPUT: @@ -2478,7 +2478,37 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient = ambient_space = 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""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 31f0de091e1..af317977e5a 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -677,6 +677,29 @@ def polyhedron(self): ambient = polyhedron + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module of the containing polyhedron tensored + with a field. + + INPUT:: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: line.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.polyhedron().ambient_vector_space(base_field=base_field) + def is_relatively_open(self): r""" Return whether ``self`` is relatively open. From e66448ea0b3de1286595ef4af19fe95c5d9ef18e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:24:30 -0700 Subject: [PATCH 38/49] PolyhedronFace.contains, ConvexSet_base._test_contains: New --- src/sage/geometry/convex_set.py | 16 +++++++++-- src/sage/geometry/polyhedron/face.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index fbb297d4ae7..518ed2bc51f 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -309,7 +309,7 @@ def _test_convex_set(self, tester=None, **options): ....: return 42 ....: def ambient_dim(self): ....: return 91 - sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + sage: TestSuite(FaultyConvexSet()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -323,7 +323,7 @@ def _test_convex_set(self, tester=None, **options): ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 - sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + sage: TestSuite(BiggerOnTheInside()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -392,6 +392,18 @@ def contains(self, point): - ``point`` -- a point or its coordinates """ + def _test_contains(self, tester=None, **options): + """ + Test the ``contains`` method. + """ + if tester is None: + tester = self._tester(**options) + space = self.ambient_vector_space() + point = space.an_element() + coords = space.coordinates(point) + if self.contains != NotImplemented: + tester.assertEqual(self.contains(point), self.contains(coords)) + @abstract_method(optional=True) def intersection(self, other): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index af317977e5a..56305d035e8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -764,6 +764,49 @@ def as_polyhedron(self): Vrep = (self.vertices(), self.rays(), self.lines()) return P.__class__(parent, Vrep, None) + def contains(self, point): + """ + Test whether the polyhedron contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.contains([0, 1]) + True + + As a shorthand, one may use the usual ``in`` operator:: + + sage: [5, 7] in line + False + """ + # preprocess in the same way as Polyhedron_base.contains + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + if len(point) > 0: + return False + else: + p = vector(self.polyhedron().base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + if not self.polyhedron().contains(p): + return False + + for H in self.ambient_Hrepresentation(): + if H.eval(p) != 0: + return False + return True + + __contains__ = contains + @cached_method def normal_cone(self, direction='outer'): """ From 1f7826d757f80ba7bae0aa64fb34c57206b2ad8f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:42:08 -0700 Subject: [PATCH 39/49] ConvexSet_base._test_contains: Expand and add doctests --- src/sage/geometry/convex_set.py | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 518ed2bc51f..56ff0d75bc4 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -395,14 +395,61 @@ def contains(self, point): def _test_contains(self, tester=None, **options): """ Test the ``contains`` method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: class FaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: ambient = ambient_vector_space + ....: def contains(self, point): + ....: if isinstance(point, (tuple, list)): + ....: return all(x in ZZ for x in point) + ....: return point.parent() == ZZ^2 + sage: FaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: False != True + + sage: class AlsoFaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: def ambient(self): + ....: return ZZ^2 + ....: def contains(self, point): + ....: return point in ZZ^2 + sage: AlsoFaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: True != False """ if tester is None: tester = self._tester(**options) + ambient = self.ambient() space = self.ambient_vector_space() - point = space.an_element() - coords = space.coordinates(point) + try: + ambient_point = ambient.an_element() + except (AttributeError, NotImplementedError): + ambient_point = None + space_point = space.an_element() + else: + space_point = space(ambient_point) + space_coords = space.coordinates(space_point) if self.contains != NotImplemented: - tester.assertEqual(self.contains(point), self.contains(coords)) + contains_space_point = self.contains(space_point) + if ambient_point is not None: + tester.assertEqual(contains_space_point, self.contains(ambient_point)) + tester.assertEqual(contains_space_point, self.contains(space_coords)) + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + from sage.symbolic.ring import SR + symbolic_space = self.ambient_vector_space(SR) + symbolic_space_point = symbolic_space(space_point) + # Only test that it can accept SR vectors without error. + self.contains(symbolic_space_point) @abstract_method(optional=True) def intersection(self, other): From 142fb462e22a0598eb56c5b61f6b7b602fcbbaac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:45:28 -0700 Subject: [PATCH 40/49] ConvexSet_base.codimension: Add doctest for alias 'codim' --- src/sage/geometry/convex_set.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..7945296471d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -99,11 +99,15 @@ def codimension(self): r""" Return the codimension of ``self``. - An alias is :meth:`codim`. - EXAMPLES:: - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + sage: P = Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]) + sage: P.codimension() + 2 + + An alias is :meth:`codim`:: + + sage: P.codim() 2 """ return self.ambient_dim() - self.dim() From 30ebaf5a657040cb787bea0f5c2540ca08115284 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:49:31 -0700 Subject: [PATCH 41/49] ConvexSet_base.ambient_dim, ambient_dimension: Fix doc --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 7945296471d..b7793476cf8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -75,12 +75,12 @@ def dimension(self): @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. From fcf2a32a768097f9248cda456624784cc9f199c6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 14:30:04 +0200 Subject: [PATCH 42/49] fix coverage --- src/sage/geometry/convex_set.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index b7793476cf8..8ef66c68285 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -44,6 +44,15 @@ def is_universe(self): OUTPUT: Boolean. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.is_universe() + Traceback (most recent call last): + ... + NotImplementedError: """ if not self.is_full_dimensional(): return False @@ -53,6 +62,15 @@ def is_universe(self): def dim(self): r""" Return the dimension of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def dimension(self): @@ -76,6 +94,15 @@ def dimension(self): def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def ambient_dimension(self): @@ -350,6 +377,15 @@ def _test_convex_set(self, tester=None, **options): def affine_hull(self): r""" Return the affine hull of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.affine_hull() + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -364,6 +400,15 @@ def cartesian_product(self, other): OUTPUT: The Cartesian product of ``self`` and ``other``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.cartesian_product(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -374,6 +419,15 @@ def contains(self, point): INPUT: - ``point`` -- a point or its coordinates + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.contains(vector([0, 0])) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -388,6 +442,15 @@ def intersection(self, other): OUTPUT: The intersection. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.intersection(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ From 6bef52ba121da086a07e2fe2b6e5f9b7ff3cbb51 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:20:46 -0700 Subject: [PATCH 43/49] ConvexSet_base._test_convex_set: Run the testsuite of relint --- src/sage/geometry/convex_set.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8ef66c68285..f55a4cd17b5 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -370,6 +370,12 @@ def _test_convex_set(self, tester=None, **options): tester.assertTrue(self == cl_self) if self.is_compact(): tester.assertTrue(self.is_closed()) + from sage.misc.sage_unittest import TestSuite + if relint_self is not None and relint_self is not self: + tester.info("\n Running the test suite of self.relative_interior()") + TestSuite(relint_self).run(verbose=tester._verbose, + prefix=tester._prefix + " ") + tester.info(tester._prefix + " ", newline=False) # Optional methods From 6ab5677085b94f2d7453a4b619e16806cff3b233 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 12:44:58 -0700 Subject: [PATCH 44/49] RelativeInterior.is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 795ef408692..3fd2f6c6b54 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -79,7 +79,8 @@ def ambient_dim(self): sage: segment.ambient_dim() 2 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient_dim() 2 """ @@ -95,7 +96,8 @@ def dim(self): sage: segment.dim() 1 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.dim() 1 """ @@ -154,6 +156,28 @@ def closure(self): """ return self._polyhedron + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_universe() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_universe() + return False + def is_closed(self): r""" Return whether ``self`` is closed. From c085d30d1c10ceacd9980520d0f3c7e2a78d531c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:33:31 -0700 Subject: [PATCH 45/49] Polyhedron_base.interior: Handle the empty polyhedron correctly --- src/sage/geometry/polyhedron/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..50274eced76 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8425,7 +8425,15 @@ def interior(self): 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() From 686d0afbeba9f5d33131ecbe20a907c20635faa5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:39:53 -0700 Subject: [PATCH 46/49] Polyhedron_base.product: Add doctest for alias 'cartesian_product' --- src/sage/geometry/polyhedron/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 50274eced76..fe4253fe3bc 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4767,6 +4767,11 @@ def product(self, other): sage: P1 * 2.0 A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices + An alias is :meth:`cartesian_product`:: + + sage: P1.cartesian_product(P2) == P1.product(P2) + True + TESTS: Check that :trac:`15253` is fixed:: From 7323b10d6c628c32adfeb62025879f909f707c61 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 14 Jun 2021 15:50:57 -0700 Subject: [PATCH 47/49] ConvexSet_base._test_contains: Only test extension to AA for exact base rings --- src/sage/geometry/convex_set.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8e672a94c68..d256ae1671b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -505,10 +505,11 @@ def _test_contains(self, tester=None, **options): if ambient_point is not None: tester.assertEqual(contains_space_point, self.contains(ambient_point)) tester.assertEqual(contains_space_point, self.contains(space_coords)) - from sage.rings.qqbar import AA - ext_space = self.ambient_vector_space(AA) - ext_space_point = ext_space(space_point) - tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + if space.base_ring().is_exact(): + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) from sage.symbolic.ring import SR symbolic_space = self.ambient_vector_space(SR) symbolic_space_point = symbolic_space(space_point) From 94e68582fde0f5bb8b2c57e5f7aa56dbaa5e825a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:21:21 -0700 Subject: [PATCH 48/49] RelativeInterior.ambient, ambient_vector_space, is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3fd2f6c6b54..c714faabcb9 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -69,6 +69,34 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient(self): + r""" + Return the ambient convex set or space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient() + """ + return self._polyhedron.ambient() + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_vector_space() + """ + return self._polyhedron.ambient_vector_space(base_field=base_field) + def ambient_dim(self): r""" Return the dimension of the ambient space. From 0c9bc945a59ffbf38c59d3679036bf4c90a516fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 16 Jun 2021 09:16:27 -0700 Subject: [PATCH 49/49] ConvexSet_base: Add default implementations of ambient, ambient_dim; add doctests --- src/sage/geometry/convex_set.py | 45 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d256ae1671b..c6dc7967e4d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,8 @@ def dim(self): r""" Return the dimension of ``self``. + Subclasses must provide an implementation of this method. + TESTS:: sage: from sage.geometry.convex_set import ConvexSet_base @@ -94,28 +96,55 @@ def dimension(self): def ambient_vector_space(self, base_field=None): r""" Return the ambient vector space. + + Subclasses must provide an implementation of this method. + + The default implementations of :meth:`ambient`, :meth:`ambient_dim`, + :meth:`ambient_dimension` use this method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_vector_space() + Traceback (most recent call last): + ... + NotImplementedError: """ - @abstract_method def ambient(self): r""" Return the ambient convex set or space. + + The default implementation delegates to :meth:`ambient_vector_space`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_vector_space(self, base_field=None): + ....: return (base_field or QQ)^2001 + sage: ExampleSet().ambient_dim() + 2001 """ + return self.ambient_vector_space() - @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. - TESTS:: + The default implementation obtains it from :meth:`ambient`. + + EXAMPLES:: sage: from sage.geometry.convex_set import ConvexSet_base - sage: C = ConvexSet_base() - sage: C.ambient_dim() - Traceback (most recent call last): - ... - NotImplementedError: + sage: class ExampleSet(ConvexSet_base): + ....: def ambient(self): + ....: return QQ^7 + sage: ExampleSet().ambient_dim() + 7 """ + return self.ambient().dimension() def ambient_dimension(self): r"""