diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..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 @@ -527,7 +528,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 +560,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 +584,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 +653,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())) + diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..f9dd8610491 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) @@ -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 @@ -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. @@ -742,12 +767,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) @@ -882,11 +907,40 @@ 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] + + 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 +1071,58 @@ def get_interval(self, i): __getitem__ = get_interval + # ParentMethods of Subobjects + + def ambient(self): + """ + Return the ambient space (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): """ @@ -1047,9 +1153,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) @@ -1077,7 +1183,7 @@ def normalize(intervals): def _repr_(self): """ - Return a string representation + Return a string representation of ``self``. OUTPUT: @@ -1091,10 +1197,24 @@ 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): + r""" + Return a latex representation of ``self``. + EXAMPLES:: + + 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(latex(i) for i in self._intervals) def _sympy_condition_(self, variable): """ @@ -1411,26 +1531,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 @@ -1453,12 +1573,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 """ @@ -1477,12 +1597,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 """ @@ -1501,17 +1621,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: @@ -1549,19 +1669,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()) @@ -1583,7 +1703,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) @@ -1677,7 +1797,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) @@ -1782,15 +1902,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]) @@ -1806,8 +1926,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