From 4b09685667a8dddec24847dd14067abbe2d50076 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 29 May 2021 15:01:21 -0700 Subject: [PATCH 1/7] RealSet: Put it in a suitable subcategory of TopologicalSpaces() --- src/sage/sets/real_set.py | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..b27e39fd25b 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -88,7 +88,7 @@ class RealSet. from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.sets_cat import Sets +from sage.categories.topological_spaces import TopologicalSpaces from sage.rings.all import ZZ from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity @@ -885,8 +885,37 @@ def __init__(self, *intervals): (0, 1) + (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set (0, 1) + [3, 4] + + Real sets belong to a subcategory of topological spaces:: + + sage: RealSet().category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet.point(1).category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet([1, 2]).category() + Join of Category of infinite sets and Category of compact topological spaces and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet((1, 2), (3, 4)).category() + Join of Category of infinite sets and Category of subobjects of sets and Category of topological spaces + """ - Parent.__init__(self, category = Sets()) + category = TopologicalSpaces() + if len(intervals) <= 1: + category = category.Connected() + if all(i.is_point() for i in intervals): + category = category.Subobjects().Finite() + else: + # Have at least one non-degenerate interval + category = category.Infinite() + inf = intervals[0].lower() + sup = intervals[-1].upper() + if not (len(intervals) == 1 and inf is minus_infinity and sup is infinity): + category = category.Subobjects() # subobject of real line + if inf is not minus_infinity and sup is not infinity: + # Bounded + if all(i.lower_closed() and i.upper_closed() + for i in intervals): + category = category.Compact() + Parent.__init__(self, category=category) self._intervals = intervals def __richcmp__(self, other, op): @@ -1017,6 +1046,59 @@ def get_interval(self, i): __getitem__ = get_interval + # ParentMethods of Subobjects + + @staticmethod + def ambient(): + """ + Construct the real line + + EXAMPLES:: + + sage: s = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)) + sage: s.ambient() + (-oo, +oo) + """ + return RealSet(minus_infinity, infinity) + + def lift(self, x): + """ + Lift ``x`` to the ambient space for ``self``. + + This version of the method just returns ``x``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.lift(1) + 1 + """ + return x + + def retract(self, x): + """ + Retract ``x`` to ``self``. + + It raises an error if ``x`` does not lie in the set ``self``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.retract(1) + 1 + sage: s.retract(2) + Traceback (most recent call last): + ... + ValueError: 2 is not an element of (0, 2) + """ + if x not in self: + raise ValueError(f'{x} is not an element of {self}') + return x + + # + @staticmethod def normalize(intervals): """ From 5b8ceca516427f34634ab0eeb17a53a70d62f0bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:01:36 -0700 Subject: [PATCH 2/7] InternalRealInterval, RealSet: Add _latex_ methods --- src/sage/sets/real_set.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..7aedf03abce 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -415,6 +415,17 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition + def _latex_(self): + from sage.misc.latex import latex + if self.is_point(): + return r'\{' + latex(self.lower()) + r'\}' + s = '[' if self._lower_closed else '(' + s += latex(self.lower()) + s += ', ' + s += latex(self.upper()) + s += ']' if self._upper_closed else ')' + return s + def closure(self): """ Return the closure @@ -1095,6 +1106,21 @@ def _repr_(self): return ' + '.join(map(repr, self._intervals)) # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + def _latex_(self): + """ + EXAMPLES:: + + sage: from cutgeneratingfunctionology.spam.real_set import RealSet + sage: latex(RealSet(0, 1)) + ( 0 , 1 ) + sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) + ( 0 , 1 ) \cup [ 2 , +\infty ) + """ + from sage.misc.latex import latex + if self.n_components() == 0: + return r'\emptyset' + else: + return r' \cup '.join(map(latex, self._intervals)) def _sympy_condition_(self, variable): """ From 69ca8543ccd948f9c01143dda49f574710efe0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:24:46 -0700 Subject: [PATCH 3/7] RealSet._repr_: Use unicode cup sign instead of + --- src/sage/sets/real_set.py | 84 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 7aedf03abce..170b762a5af 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -10,7 +10,7 @@ sage: RealSet(0,1) (0, 1) sage: RealSet((0,1), [2,3]) - (0, 1) + [2, 3] + (0, 1) ∪ [2, 3] sage: RealSet(-oo, oo) (-oo, +oo) @@ -42,7 +42,7 @@ Relations containing symbols and numeric values or constants:: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -753,12 +753,12 @@ def __classcall__(cls, *args): EXAMPLES:: sage: R = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)); R - (0, 1] + [2, 3) + (0, 1] ∪ [2, 3) :: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -893,9 +893,9 @@ def __init__(self, *intervals): sage: RealSet(i) # interval (0, 1) sage: RealSet(i, (3,4)) # tuple of two numbers = open set - (0, 1) + (3, 4) + (0, 1) ∪ (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set - (0, 1) + [3, 4] + (0, 1) ∪ [3, 4] """ Parent.__init__(self, category = Sets()) self._intervals = intervals @@ -1058,9 +1058,9 @@ def normalize(intervals): sage: RealSet((0, 1), [1, 2], (2, 3)) (0, 3) sage: RealSet((0, 1), (1, 2), (2, 3)) - (0, 1) + (1, 2) + (2, 3) + (0, 1) ∪ (1, 2) ∪ (2, 3) sage: RealSet([0, 1], [2, 3]) - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: RealSet((0, 2), (1, 3)) (0, 3) sage: RealSet(0,0) @@ -1088,7 +1088,7 @@ def normalize(intervals): def _repr_(self): """ - Return a string representation + Return a string representation of ``self``. OUTPUT: @@ -1102,9 +1102,7 @@ def _repr_(self): if self.n_components() == 0: return '{}' else: - # Switch to u'\u222A' (cup sign) with Python 3 - return ' + '.join(map(repr, self._intervals)) - # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): """ @@ -1437,26 +1435,26 @@ def intersection(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.intersection(s2) (1, 2) sage: s1 & s2 # syntactic sugar (1, 2) sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([0, 1], [2, 3]); s2 - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: s3 = RealSet([1, 2]); s3 [1, 2] sage: s1.intersection(s2) - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s1.intersection(s3) {} sage: s2.intersection(s3) - {1} + {2} + {1} ∪ {2} """ other = RealSet(*other) # TODO: this can be done in linear time since the intervals are already sorted @@ -1479,12 +1477,12 @@ def inf(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.inf() 0 sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.inf() -Infinity """ @@ -1503,12 +1501,12 @@ def sup(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.sup() +Infinity sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.sup() 3 """ @@ -1527,17 +1525,17 @@ def complement(self): EXAMPLES:: sage: RealSet(0,1).complement() - (-oo, 0] + [1, +oo) + (-oo, 0] ∪ [1, +oo) sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.complement() - (-oo, 0] + [2, 10) + (-oo, 0] ∪ [2, 10) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.complement() - (-10, 1] + [3, +oo) + (-10, 1] ∪ [3, +oo) """ n = self.n_components() if n == 0: @@ -1575,19 +1573,19 @@ def difference(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.difference(s2) - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s1 - s2 # syntactic sugar - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s2.difference(s1) - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s2 - s1 # syntactic sugar - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s1.difference(1,11) - (0, 1] + [11, +oo) + (0, 1] ∪ [11, +oo) """ other = RealSet(*other) return self.intersection(other.complement()) @@ -1609,7 +1607,7 @@ def contains(self, x): EXAMPLES:: sage: s = RealSet(0,2) + RealSet.unbounded_above_closed(10); s - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s.contains(1) True sage: s.contains(0) @@ -1703,7 +1701,7 @@ def is_disjoint_from(self, *other): EXAMPLES:: sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([1, 2]); s2 [1, 2] sage: s1.is_disjoint_from(s2) @@ -1808,15 +1806,15 @@ def __mul__(self, right): EXAMPLES:: sage: A = RealSet([0, 1/2], (2, infinity)); A - [0, 1/2] + (2, +oo) + [0, 1/2] ∪ (2, +oo) sage: 2 * A - [0, 1] + (4, +oo) + [0, 1] ∪ (4, +oo) sage: A * 100 - [0, 50] + (200, +oo) + [0, 50] ∪ (200, +oo) sage: 1.5 * A - [0.000000000000000, 0.750000000000000] + (3.00000000000000, +oo) + [0.000000000000000, 0.750000000000000] ∪ (3.00000000000000, +oo) sage: (-2) * A - (-oo, -4) + [-1, 0] + (-oo, -4) ∪ [-1, 0] """ if not isinstance(right, RealSet): return RealSet(*[e * right for e in self]) @@ -1832,8 +1830,8 @@ def __rmul__(self, other): TESTS:: sage: A = RealSet([0, 1/2], RealSet.unbounded_above_closed(2)); A - [0, 1/2] + [2, +oo) + [0, 1/2] ∪ [2, +oo) sage: pi * A - [0, 1/2*pi] + [2*pi, +oo) + [0, 1/2*pi] ∪ [2*pi, +oo) """ return self * other From dbdfc068f2d2ac39ae8171ea8737b2b1a535e021 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:26:42 -0700 Subject: [PATCH 4/7] InternalRealInterval, RealSet: Remove extra whitespace in latex, add documentation --- src/sage/sets/real_set.py | 47 ++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 170b762a5af..9106db527d4 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -382,6 +382,31 @@ def _repr_(self): s += ']' if self._upper_closed else ')' return s + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: RealSet.open_closed(1/2, pi)._latex_() + '(\\frac{1}{2}, \\pi]' + sage: (RealSet.point(sqrt(2)))._latex_() + '\\{\\sqrt{2}\\}' + """ + from sage.misc.latex import latex + if self.is_point(): + # Converting to str avoids the extra whitespace + # that LatexExpr add on concenation. We do not need + # the whitespace because we are wrapping it in + # non-letter characters. + return r'\{' + str(latex(self.lower())) + r'\}' + s = '[' if self._lower_closed else '(' + s += str(latex(self.lower())) + s += ', ' + s += str(latex(self.upper())) + s += ']' if self._upper_closed else ')' + return s + def _sympy_condition_(self, variable): """ Convert to a sympy conditional expression. @@ -415,17 +440,6 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition - def _latex_(self): - from sage.misc.latex import latex - if self.is_point(): - return r'\{' + latex(self.lower()) + r'\}' - s = '[' if self._lower_closed else '(' - s += latex(self.lower()) - s += ', ' - s += latex(self.upper()) - s += ']' if self._upper_closed else ')' - return s - def closure(self): """ Return the closure @@ -1105,20 +1119,21 @@ def _repr_(self): return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): - """ + r""" + Return a latex representation of ``self``. + EXAMPLES:: - sage: from cutgeneratingfunctionology.spam.real_set import RealSet sage: latex(RealSet(0, 1)) - ( 0 , 1 ) + (0, 1) sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) - ( 0 , 1 ) \cup [ 2 , +\infty ) + (0, 1) \cup [2, +\infty) """ from sage.misc.latex import latex if self.n_components() == 0: return r'\emptyset' else: - return r' \cup '.join(map(latex, self._intervals)) + return r' \cup '.join(latex(i) for i in self._intervals) def _sympy_condition_(self, variable): """ From 7f563381617c2b016a8b849f83d3387ccc25d621 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:09:43 -0700 Subject: [PATCH 5/7] PiecewiseFunction: Adjust doctests for changed RealSet repr --- src/sage/functions/piecewise.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..a0276824a3a 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -527,7 +527,7 @@ def restriction(self, parameters, variable, restricted_domain): sage: f = piecewise([((-oo, oo), x)]); f piecewise(x|-->x on (-oo, +oo); x) sage: f.restriction([[-1,1], [3,3]]) - piecewise(x|-->x on [-1, 1] + {3}; x) + piecewise(x|-->x on [-1, 1] ∪ {3}; x) """ restricted_domain = RealSet(*restricted_domain) new_param = [] @@ -559,7 +559,7 @@ def extension(self, parameters, variable, extension, extension_domain=None): ValueError: point 3 is not in the domain sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 @@ -583,7 +583,7 @@ def unextend_zero(self, parameters, variable): sage: f = piecewise([((-1,1), x)]); f piecewise(x|-->x on (-1, 1); x) sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 sage: h = g.unextend_zero() @@ -652,7 +652,7 @@ def piecewise_add(self, parameters, variable, other): sage: f = piecewise([([0,1], 1), ((2,3), x)]) sage: g = piecewise([((1/2, 2), x)]) sage: f.piecewise_add(g).unextend_zero() - piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) + (2, 3); x) + piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) ∪ (2, 3); x) """ points = ([minus_infinity] + sorted(set(self.end_points() + other.end_points())) + From 46eed0eeb3fc4e9509b7c55df967455ae49984d0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:25:17 -0700 Subject: [PATCH 6/7] RealSet.ambient: Change to a normal method --- src/sage/sets/real_set.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index b27e39fd25b..5d39fbe0029 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1048,10 +1048,9 @@ def get_interval(self, i): # ParentMethods of Subobjects - @staticmethod - def ambient(): + def ambient(self): """ - Construct the real line + Return the ambient space (the real line). EXAMPLES:: From 8abdc8b16f9762041c1c60bcb4672fefea12f4b4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:22:24 -0700 Subject: [PATCH 7/7] src/sage/functions/piecewise.py: Add coding header --- src/sage/functions/piecewise.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index a0276824a3a..4428543bf6b 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Piecewise-defined Functions