sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.33k stars 453 forks source link

`limit` fails for piecewise expressions. #35883

Open EmmanuelCharpentier opened 1 year ago

EmmanuelCharpentier commented 1 year ago

Is there an existing issue for this?

Did you read the documentation and troubleshoot guide?

Environment

- **OS**: Debian testing as of 2023-07-02
- **Sage Version**: 10.1.beta5

Found thanks to [this `ask.sagemath.org` question](https://ask.sagemath.org/question/69653/simple-piecewise-limit/).

Steps To Reproduce

sage: reset()
sage: x=var("x", domain="real")
sage: f(x)=piecewise([((-oo, -2), 3*x+1), ((-2, oo), 3*x+1), ([-2, -2], 2)], var=x)
sage: f(-2)
2

Expected Behavior

-5

Actual Behavior

sage: f(x).limit(x=-2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [200], line 1
----> 1 f(x).limit(x=-Integer(2))

File /usr/local/sage-10/src/sage/symbolic/expression.pyx:13332, in sage.symbolic.expression.Expression.limit()
  13330     """
  13331     from sage.calculus.calculus import limit
> 13332     return limit(self, *args, **kwds)
  13333 
  13334 def laplace(self, t, s):

File /usr/local/sage-10/src/sage/calculus/calculus.py:1429, in limit(ex, dir, taylor, algorithm, **argv)
   1427 if algorithm == 'maxima':
   1428     if dir is None:
-> 1429         l = maxima.sr_limit(ex, v, a)
   1430     elif dir in dir_plus:
   1431         l = maxima.sr_limit(ex, v, a, 'plus')

File /usr/local/sage-10/src/sage/interfaces/maxima_lib.py:1006, in MaximaLib.sr_limit(self, expr, v, a, dir)
   1004     elif dir == "minus":
   1005         L.append(max_minus)
-> 1006     return max_to_sr(maxima_eval(([max_limit], L)))
   1007 except RuntimeError as error:
   1008     s = str(error)

File /usr/local/sage-10/src/sage/interfaces/maxima_lib.py:1728, in max_to_sr(expr)
   1724     op = max_to_pynac_table[op_max_str]
   1725 else:
   1726     # This could be unsafe if the conversion to SR
   1727     # changes the structure of expr
-> 1728     sage_expr = SR(maxima(expr))
   1729     op = sage_expr.operator()
   1730 if op in sage_op_dict:

File /usr/local/sage-10/src/sage/structure/parent.pyx:901, in sage.structure.parent.Parent.__call__()
    899 if mor is not None:
    900     if no_extra_args:
--> 901         return mor._call_(x)
    902     else:
    903         return mor._call_with_args(x, args, kwds)

File /usr/local/sage-10/src/sage/structure/coerce_maps.pyx:163, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_()
    161             print(type(C), C)
    162             print(type(C._element_constructor), C._element_constructor)
--> 163         raise
    164 
    165 cpdef Element _call_with_args(self, x, args=(), kwds={}):

File /usr/local/sage-10/src/sage/structure/coerce_maps.pyx:158, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_()
    156 cdef Parent C = self._codomain
    157 try:
--> 158     return C._element_constructor(x)
    159 except Exception:
    160     if print_warnings:

File /usr/local/sage-10/src/sage/symbolic/ring.pyx:376, in sage.symbolic.ring.SymbolicRing._element_constructor_()
    374         TypeError: Malformed expression: λ + * !!!  1
    375     """
--> 376     return new_Expression(self, x)
    377 
    378 def _force_pyobject(self, x, bint force=False, bint recursive=True):

File /usr/local/sage-10/src/sage/symbolic/expression.pyx:13773, in sage.symbolic.expression.new_Expression()
  13771     return new_Expression_from_GEx(parent, (<Expression>x)._gobj)
  13772 if hasattr(x, '_symbolic_'):
> 13773     return x._symbolic_(parent)
  13774 elif isinstance(x, str):
  13775     try:

File /usr/local/sage-10/src/sage/interfaces/maxima_abstract.py:1280, in MaximaAbstractElement._symbolic_(self, R)
   1257 def _symbolic_(self, R):
   1258     """
   1259     Return a symbolic expression equivalent to this Maxima object.
   1260 
   (...)
   1278         sqrt(2)
   1279     """
-> 1280     return R(self._sage_())

File /usr/local/sage-10/src/sage/interfaces/maxima_abstract.py:1254, in MaximaAbstractElement._sage_(self)
   1199 """
   1200 Attempt to make a native Sage object out of this Maxima object.
   1201 This is useful for automatic coercions in addition to other
   (...)
   1251     (True, False)
   1252 """
   1253 import sage.calculus.calculus as calculus
-> 1254 return calculus.symbolic_expression_from_maxima_string(self.name(),
   1255         maxima=self.parent())

File /usr/local/sage-10/src/sage/calculus/calculus.py:2403, in symbolic_expression_from_maxima_string(x, equals_sub, maxima)
   2401     SRM_parser._variable_constructor().set_names(var_syms)
   2402     SRM_parser._callable_constructor().set_names(function_syms)
-> 2403     return SRM_parser.parse_sequence(s)
   2404 except SyntaxError:
   2405     raise TypeError("unable to make sense of Maxima expression '%s' in Sage" % s)

File /usr/local/sage-10/src/sage/misc/parser.pyx:571, in sage.misc.parser.Parser.parse_sequence()
    569     return expr
    570 
--> 571 cpdef parse_sequence(self, s):
    572     """
    573     Parse a (possibly nested) set of lists and tuples.

File /usr/local/sage-10/src/sage/misc/parser.pyx:587, in sage.misc.parser.Parser.parse_sequence()
    585 """
    586 cdef Tokenizer tokens = Tokenizer(s)
--> 587 all = self.p_sequence(tokens)
    588 if tokens.next() != EOS:
    589     self.parse_error(tokens)

File /usr/local/sage-10/src/sage/misc/parser.pyx:660, in sage.misc.parser.Parser.p_sequence()
    658     return all
    659 else:
--> 660     obj = self.p_eqn(tokens)
    661 PyList_Append(all, obj)
    662 token = tokens.next()

File /usr/local/sage-10/src/sage/misc/parser.pyx:750, in sage.misc.parser.Parser.p_eqn()
    748     a != b
    749 """
--> 750 lhs = self.p_expr(tokens)
    751 cdef int op = tokens.next()
    752 if op == c'=':

File /usr/local/sage-10/src/sage/misc/parser.pyx:790, in sage.misc.parser.Parser.p_expr()
    788 # Note: this is left-recursive, so we can't just recurse
    789 cdef int op
--> 790 operand1 = self.p_term(tokens)
    791 op = tokens.next()
    792 while op == c'+' or op == c'-':

File /usr/local/sage-10/src/sage/misc/parser.pyx:824, in sage.misc.parser.Parser.p_term()
    822 # Note: this is left-recursive, so we can't just recurse
    823 cdef int op
--> 824 operand1 = self.p_factor(tokens)
    825 op = tokens.next()
    826 if op == NAME and self.implicit_multiplication:

File /usr/local/sage-10/src/sage/misc/parser.pyx:867, in sage.misc.parser.Parser.p_factor()
    865         else:
    866             tokens.backtrack()
--> 867             return self.p_power(tokens)
    868 
    869 # power ::=  (atom | atom!) ^ factor | atom | atom!

File /usr/local/sage-10/src/sage/misc/parser.pyx:895, in sage.misc.parser.Parser.p_power()
    893 
    894         """
--> 895         operand1 = self.p_atom(tokens)
    896         cdef int token = tokens.next()
    897         if token == c'^':

File /usr/local/sage-10/src/sage/misc/parser.pyx:950, in sage.misc.parser.Parser.p_atom()
    948 if token == c'(':
    949     func = self.callable_constructor(name)
--> 950     args, kwds = self.p_args(tokens)
    951     token = tokens.next()
    952     if token != c')':

File /usr/local/sage-10/src/sage/misc/parser.pyx:987, in sage.misc.parser.Parser.p_args()
    985 cdef int token = c','
    986 while token == c',':
--> 987     arg = self.p_arg(tokens)
    988     if isinstance(arg, tuple):
    989         name, value = arg

File /usr/local/sage-10/src/sage/misc/parser.pyx:1037, in sage.misc.parser.Parser.p_arg()
   1035     else:
   1036         tokens.backtrack()
-> 1037         return self.p_expr(tokens)
   1038 
   1039 cdef parse_error(self, Tokenizer tokens, msg="Malformed expression"):

File /usr/local/sage-10/src/sage/misc/parser.pyx:790, in sage.misc.parser.Parser.p_expr()
    788 # Note: this is left-recursive, so we can't just recurse
    789 cdef int op
--> 790 operand1 = self.p_term(tokens)
    791 op = tokens.next()
    792 while op == c'+' or op == c'-':

File /usr/local/sage-10/src/sage/misc/parser.pyx:824, in sage.misc.parser.Parser.p_term()
    822 # Note: this is left-recursive, so we can't just recurse
    823 cdef int op
--> 824 operand1 = self.p_factor(tokens)
    825 op = tokens.next()
    826 if op == NAME and self.implicit_multiplication:

File /usr/local/sage-10/src/sage/misc/parser.pyx:867, in sage.misc.parser.Parser.p_factor()
    865         else:
    866             tokens.backtrack()
--> 867             return self.p_power(tokens)
    868 
    869 # power ::=  (atom | atom!) ^ factor | atom | atom!

File /usr/local/sage-10/src/sage/misc/parser.pyx:895, in sage.misc.parser.Parser.p_power()
    893 
    894         """
--> 895         operand1 = self.p_atom(tokens)
    896         cdef int token = tokens.next()
    897         if token == c'^':

File /usr/local/sage-10/src/sage/misc/parser.pyx:954, in sage.misc.parser.Parser.p_atom()
    952     if token != c')':
    953         self.parse_error(tokens, "Bad function call")
--> 954     return func(*args, **kwds)
    955 else:
    956     tokens.backtrack()

TypeError: PiecewiseFunction.__call__() takes 2 positional arguments but 3 were given

Additional Information

BTW,

sage: f(x).limit(x=-2, algorithm="sympy")
2
sage: f(x).limit(x=-2, algorithm="giac")
-5
sage: %%mathematica
....: f[x_]:=Piecewise[{{3*x+1, x<-2}, {3*x+1, x>-2}, {2,x==-2}}]
....: f[-2]
....: Limit[f[x], x->-2]
....: 
....: 
        2
Out[7]= -5

Not a pretty sight...

vamshipy commented 7 months ago

i am sorry if this is stupid but what if we change the algorithm parameter in the limit definition to "giac" instead of "maxima" will that fix this or just create more problems..