Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Merge #31732
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Apr 26, 2021
2 parents e10950f + 48b2700 commit b4171c9
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 19 deletions.
3 changes: 3 additions & 0 deletions src/sage/manifolds/point.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ def __init__(self, parent, coords=None, chart=None, name=None,
sage: TestSuite(q).run()
"""
if parent.is_empty():
raise TypeError(f'cannot define a point on the {parent} because it has been declared empty')
Element.__init__(self, parent)
parent._has_defined_points = True
self._manifold = parent.manifold() # a useful shortcut
self._coordinates = {} # dictionary of the point coordinates in various
# charts, with the charts as keys
Expand Down
251 changes: 232 additions & 19 deletions src/sage/manifolds/subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@
# http://www.gnu.org/licenses/
#*****************************************************************************

from collections import defaultdict
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation
from sage.misc.superseded import deprecation
from sage.categories.sets_cat import Sets
from sage.manifolds.family import ManifoldSubsetFiniteFamily
from sage.manifolds.family import ManifoldObjectFiniteFamily, ManifoldSubsetFiniteFamily
from sage.manifolds.point import ManifoldPoint

class ManifoldSubset(UniqueRepresentation, Parent):
Expand Down Expand Up @@ -197,6 +198,7 @@ def __init__(self, manifold, name, latex_name=None, category=None):
self._open_covers = [] # list of open covers of self
self._is_open = False # a priori (may be redefined by subclasses)
self._manifold = manifold # the ambient manifold
self._has_defined_points = False

def _repr_(self):
r"""
Expand Down Expand Up @@ -515,7 +517,7 @@ def is_open(self):
"""
return False

def open_covers(self, trivial=True):
def open_covers(self, trivial=True, supersets=False):
r"""
Generate the open covers of the current subset.
Expand All @@ -534,10 +536,18 @@ def open_covers(self, trivial=True):
A = \bigcup_{U \in F} U.
.. NOTE::
To get the open covers as a family, sorted lexicographically by the
names of the subsets forming the open covers, use the method
:meth:`open_cover_family` instead.
INPUT:
- ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial
open cover of ``self`` by itself
- ``supersets`` -- (default: ``False``) if ``True``, include open covers of
all the supersets; it can also be an iterable of supersets to include
EXAMPLES::
Expand Down Expand Up @@ -565,11 +575,75 @@ def open_covers(self, trivial=True):
Set {A, B, V} of open subsets of the 2-dimensional topological manifold M]
"""
for oc in self._open_covers:
if not trivial:
if len(oc) == 1 and next(iter(oc)) is self:
continue
yield ManifoldSubsetFiniteFamily(oc)
if supersets is False:
supersets = [self]
elif supersets is True:
supersets = self._supersets
for superset in supersets:
for oc in superset._open_covers:
if not trivial:
if any(x in supersets for x in oc):
continue
yield ManifoldSubsetFiniteFamily(oc)

def open_cover_family(self, trivial=True, supersets=False):
r"""
Return the family of open covers of the current subset.
If the current subset, `A` say, is a subset of the manifold `M`, an
*open cover* of `A` is a :class:`ManifoldSubsetFiniteFamily` `F`
of open subsets `U \in F` of `M` such that
.. MATH::
A \subset \bigcup_{U \in F} U.
If `A` is open, we ask that the above inclusion is actually an
identity:
.. MATH::
A = \bigcup_{U \in F} U.
The family is sorted lexicographically by the names of the subsets
forming the open covers.
.. NOTE::
If you only need to iterate over the open covers in arbitrary
order, you can use the generator method :meth:`open_covers`
instead.
INPUT:
- ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial
open cover of ``self`` by itself
- ``supersets`` -- (default: ``False``) if ``True``, include open covers of
all the supersets; it can also be an iterable of supersets to include
EXAMPLES::
sage: M = Manifold(2, 'M', structure='topological')
sage: M.open_cover_family()
Set {{M}} of objects of the 2-dimensional topological manifold M
sage: U = M.open_subset('U')
sage: U.open_cover_family()
Set {{U}} of objects of the 2-dimensional topological manifold M
sage: A = U.open_subset('A')
sage: B = U.open_subset('B')
sage: U.declare_union(A,B)
sage: U.open_cover_family()
Set {{A, B}, {U}} of objects of the 2-dimensional topological manifold M
sage: U.open_cover_family(trivial=False)
Set {{A, B}} of objects of the 2-dimensional topological manifold M
sage: V = M.open_subset('V')
sage: M.declare_union(U,V)
sage: M.open_cover_family()
Set {{A, B, V}, {M}, {U, V}} of objects of the 2-dimensional topological manifold M
"""
return ManifoldObjectFiniteFamily(self.open_covers(
trivial=trivial, supersets=supersets))

def open_supersets(self):
r"""
Expand Down Expand Up @@ -739,16 +813,18 @@ def subset_family(self):
"""
return ManifoldSubsetFiniteFamily(self.subsets())

def subset_digraph(self, loops=False, open_covers=False, lower_bound=None):
def subset_digraph(self, loops=False, open_covers=False, points=False, lower_bound=None):
r"""
Return the digraph whose arcs represent subset relations among the subsets of ``self``.
INPUT:
- ``loops`` -- (default: ``False``) whether to include the trivial containment
of each subset in itself as loops of the digraph
- ``lower_bound`` -- (default: ``None``) only include supersets of this
- ``open_covers`` -- (default: ``False``) whether to include vertices for open covers
- ``points`` -- (default: ``False``) whether to include vertices for declared points;
this can also be an iterable for the points to include
- ``lower_bound`` -- (default: ``None``) only include supersets of this
OUTPUT:
Expand Down Expand Up @@ -830,16 +906,41 @@ def open_cover_vertex(open_cover):
D.add_edges((vertex(S), open_cover_vertex(open_cover))
for open_cover in S.open_covers(trivial=False))

if points is not False:
subset_to_points = defaultdict(list)
if points is not True:
# Manifolds do not keep track of the points defined on them.
# Use the provided iterator.
def point_vertex(point):
return point

for point in points:
S = point.parent()
subset_to_points[S].append(point)
D.add_edge((point_vertex(point), vertex(S)))

# Add a placeholder vertex under each subset that has a defined
# point that we do not know about.
def anonymous_point_vertex(S):
return f"p{S._name}"

D.add_edges((anonymous_point_vertex(S), vertex(S))
for S in visited
if S.has_defined_points(subsets=False)
and S not in subset_to_points)

return D

def subset_poset(self, open_covers=False, lower_bound=None):
def subset_poset(self, open_covers=False, points=False, lower_bound=None):
r"""
Return the poset of the subsets of ``self``.
INPUT:
- ``lower_bound`` -- (default: ``None``) only include supersets of this
- ``open_covers`` -- (default: ``False``) whether to include vertices for open covers
- ``points`` -- (default: ``False``) whether to include vertices for declared points;
this can also be an iterable for the points to include
- ``lower_bound`` -- (default: ``None``) only include supersets of this
EXAMPLES::
Expand All @@ -866,7 +967,7 @@ def subset_poset(self, open_covers=False, lower_bound=None):
sage: P = M.subset_poset(open_covers=True); P
Finite poset containing 6 elements
sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily
sage: P.upper_covers(ManifoldSubsetFiniteFamily([VW]))
sage: sorted(P.upper_covers(ManifoldSubsetFiniteFamily([VW])), key=str)
[(Set {V} of open subsets of the 3-dimensional differentiable manifold M,
Set {W} of open subsets of the 3-dimensional differentiable manifold M),
Set {M} of open subsets of the 3-dimensional differentiable manifold M]
Expand All @@ -878,7 +979,8 @@ def subset_poset(self, open_covers=False, lower_bound=None):
sage: P.plot(element_labels={element: label(element) for element in P}) # not tested
"""
from sage.combinat.posets.posets import Poset
return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound))
return Poset(self.subset_digraph(open_covers=open_covers, points=points,
lower_bound=lower_bound))

def supersets(self):
r"""
Expand Down Expand Up @@ -928,16 +1030,18 @@ def superset_family(self):
"""
return ManifoldSubsetFiniteFamily(self.supersets())

def superset_digraph(self, loops=False, open_covers=False, upper_bound=None):
def superset_digraph(self, loops=False, open_covers=False, points=False, upper_bound=None):
"""
Return the digraph whose arcs represent subset relations among the supersets of ``self``.
INPUT:
- ``loops`` -- (default: ``False``) whether to include the trivial containment
of each subset in itself as loops of the digraph
- ``upper_bound`` -- (default: ``None``) only include subsets of this
- ``open_covers`` -- (default: ``False``) whether to include vertices for open covers
- ``points`` -- (default: ``False``) whether to include vertices for declared points;
this can also be an iterable for the points to include
- ``upper_bound`` -- (default: ``None``) only include subsets of this
EXAMPLES::
Expand All @@ -950,16 +1054,19 @@ def superset_digraph(self, loops=False, open_covers=False, upper_bound=None):
"""
if upper_bound is None:
upper_bound = self._manifold
return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, lower_bound=self)
return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, points=points,
lower_bound=self)

def superset_poset(self, open_covers=False, upper_bound=None):
def superset_poset(self, open_covers=False, points=False, upper_bound=None):
r"""
Return the poset of the supersets of ``self``.
INPUT:
- ``upper_bound`` -- (default: ``None``) only include subsets of this
- ``open_covers`` -- (default: ``False``) whether to include vertices for open covers
- ``points`` -- (default: ``False``) whether to include vertices for declared points;
this can also be an iterable for the points to include
- ``upper_bound`` -- (default: ``None``) only include subsets of this
EXAMPLES::
Expand All @@ -973,7 +1080,8 @@ def superset_poset(self, open_covers=False, upper_bound=None):
"""
if upper_bound is None:
upper_bound = self._manifold
return upper_bound.subset_poset(open_covers=open_covers, lower_bound=self)
return upper_bound.subset_poset(open_covers=open_covers, points=points,
lower_bound=self)

def get_subset(self, name):
r"""
Expand Down Expand Up @@ -1097,6 +1205,111 @@ def declare_union(self, dom1, dom2):
oc.append(s)
self._open_covers.append(oc)

def declare_empty(self):
r"""
Declare that ``self`` is the empty set.
EXAMPLES::
sage: M = Manifold(2, 'M', structure='topological')
sage: A = M.subset('A', is_open=True)
sage: AA = A.subset('AA')
sage: A
Open subset A of the 2-dimensional topological manifold M
sage: A.declare_empty()
sage: A.is_empty()
True
Empty sets do not allow to define points on them::
sage: A.point()
Traceback (most recent call last):
...
TypeError: cannot define a point on the
Open subset A of the 2-dimensional topological manifold M
because it has been declared empty
Emptiness transfers to subsets::
sage: AA.is_empty()
True
sage: AA.point()
Traceback (most recent call last):
...
TypeError: cannot define a point on the
Subset AA of the 2-dimensional topological manifold M
because it has been declared empty
sage: AD = A.subset('AD')
sage: AD.is_empty()
True
If points have already been defined on ``self`` (or its subsets),
it is an error to declare it to be empty::
sage: B = M.subset('B')
sage: b = B.point(name='b'); b
Point b on the 2-dimensional topological manifold M
sage: B.declare_empty()
Traceback (most recent call last):
...
TypeError: cannot be empty because it has defined points
Emptiness is recorded as empty open covers::
sage: P = M.subset_poset(open_covers=True, points=[b])
sage: def label(element):
....: if isinstance(element, str):
....: return element
....: try:
....: return element._name
....: except AttributeError:
....: return '[' + ', '.join(sorted(x._name for x in element)) + ']'
sage: P.plot(element_labels={element: label(element) for element in P})
Graphics object consisting of 14 graphics primitives
.. PLOT::
def label(element):
if isinstance(element, str):
return element
try:
return element._name
except AttributeError:
return '[' + ', '.join(sorted(x._name for x in element)) + ']'
M = Manifold(2, 'M', structure='topological')
A = M.subset('A', is_open=True)
AA = A.subset('AA')
A.declare_empty()
AD = A.subset('AD')
B = M.subset('B')
b = B.point(name='b')
P = M.subset_poset(open_covers=True, points=[b])
g1 = P.plot(element_labels={element: label(element) for element in P})
sphinx_plot(graphics_array([g1]), figsize=(8, 3))
"""
if self.has_defined_points():
raise TypeError('cannot be empty because it has defined points')
for subset in self.subsets():
if not subset.is_empty():
subset._open_covers.append([])

def is_empty(self):
if self._has_defined_points:
return False
return any(not cover
for cover in self.open_covers(trivial=False, supersets=True))

def declare_nonempty(self):
if not self.has_defined_points():
self._has_defined_points = True

def has_defined_points(self, subsets=True):
if subsets:
return any(subset._has_defined_points for subset in self.subsets())
else:
return self._has_defined_points

def point(self, coords=None, chart=None, name=None, latex_name=None):
r"""
Define a point in ``self``.
Expand Down

0 comments on commit b4171c9

Please sign in to comment.