Open b7a46a19-303e-4ecf-84fd-1c9426f587e4 opened 3 years ago
Attachment: bug Derivative of the symbolic sum of function of two variables.ipynb.gz
Attachment: bug Derivative of the symbolic sum of function of two variables.2.ipynb.gz
Confirming the bug reported here:
sage: var("x,a,b")
(x, a, b)
sage: S=sum(f(x),x,a,b)
sage: S
sum(f(x), x, a, b)
sage: S.diff(x)
diff(f(x), x)*D[0](sum)(f(x), x, a, b) + D[1](sum)(f(x), x, a, b)
Clearly, sage has no knowledge on how to differentiate sums (it just considers the sum as an unknown function in four variables). The first step would be to throw an error, because things like 'sum(f(x+y),x,a,b+y).diff(y)' can probably not be handled.
In most cases, the most useful answer would probably be to differentiate term-wise (provided the differentiating variable does not occur in the bounds), which would be the correct answer for formal sums and absolutely convergent ones.
My investigation of this issue shown that giac, maxima and sympy solve this example in true way.
Let's try giac:
sage: sum_str = "sum(F(sageVARx,sageVARy), sageVARx, sageVARa, sageVARb)"
sage: from sage.interfaces.giac import giac
sage: result_giac = giac(sum_str)
sage: result_giac
sum(F(sageVARx,sageVARy),sageVARx,sageVARa,sageVARb)
sage: y = var('y')
sage: result_giac.diff(y)
sum((diff(F,1))(sageVARx,sageVARy),sageVARx,sageVARa,sageVARb)
Let's try sympy:
sage: x,y = var("x,y")
sage: a,b = var("a,b")
sage: F = function("F")(x, y)
sage: expression,v,a,b = [expr._sympy_() for expr in (F, x, a, b)]
sage: from sympy import summation
sage: from sage.interfaces.sympy import sympy_init
sage: sympy_init()
sage: result_sympy = summation(expression, (v, a, b)).diff(y)
sage: result_sympy
Sum(Derivative(F(x, y), y), (x, a, b))
and an least maxima:
sage: s = maxima('sum(F(x,y),x,a,b)')
sage: s
'sum(F(x,y),x,a,b)
sage: s.diff('y')
'sum('diff(F(x,y),y,1),x,a,b)
sage: s.diff('y').sage()
sum(diff(F(x, y), y), x, a, b)
All OK, but just
s.sage().diff(var('y'))
diff(F(x, y), y)*D[0](sum)(F(x, y), x, a, b)
gives wrong answer.
I have understood what part of sage code gives this wrong result. This is function def dummy_diff(*args):
from src/sage/calculus/calculus.py
which is called from
def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima):
Considering that
def symbolic_sum(expression, v, a, b, algorithm='maxima', hold=False):
if algorithm == 'maxima':
return maxima.sr_sum(expression,v,a,b)
I have provided example which reproduces the issue:
sage: from sage.misc.parser import Parser, LookupNameMaker
....: from sage.calculus.calculus import _find_var, _find_func
....: from sage.libs.pynac.pynac import symbol_table
....: from sage.calculus.calculus import _is_function
....:
....: parser_make_Mvar = LookupNameMaker({}, fallback=lambda x: _find_var(x, interface='maxima'))
....: parser_make_function = LookupNameMaker({}, fallback=_find_func)
....: SRM_parser = Parser(make_int = lambda x: SR(Integer(x)),
....: make_float = lambda x: SR(RealDoubleElement(x)),
....: make_var = parser_make_Mvar,
....: make_function = parser_make_function)
....:
....: var_syms = {k: v for k, v in symbol_table.get('maxima', {}).items()
....: if not _is_function(v)}
....: function_syms = {k: v for k, v in symbol_table.get('maxima', {}).items()
....: if _is_function(v)}
....:
....: #from sage.calculus.calculus import dummy_diff
....: def dummy_diff(*args):
....: """
....: This function is called when 'diff' appears in a Maxima string.
....:
....: EXAMPLES::
....:
....: sage: from sage.calculus.calculus import dummy_diff
....: sage: x,y = var('x,y')
....: sage: dummy_diff(sin(x*y), x, SR(2), y, SR(1))
....: -x*y^2*cos(x*y) - 2*y*sin(x*y)
....:
....: Here the function is used implicitly::
....:
....: sage: a = var('a')
....: sage: f = function('cr')(a)
....: sage: g = f.diff(a); g
....: diff(cr(a), a)
....: """
....: f = args[0]
....: from sage.cpython.debug import type_debug, shortrepr
....: args = list(args[1:])
....: for i in range(1, len(args), 2):
....: args[i] = Integer(args[i])
....: print ("f", f)
....: print ("*args", *args)
....: res = f.diff(*args)
....: print("res", res)
....:
....: return res
....:
....: function_syms['diff'] = dummy_diff
sage:
sage: s = "diff(sr_sum(F(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,a,b),_SAGE_VAR_y,1)"
....:
....: SRM_parser._variable_constructor().set_names(var_syms)
....: SRM_parser._callable_constructor().set_names(function_syms)
....: parser_output = SRM_parser.parse_sequence(s)
....: print("parser_output", parser_output)
f sr_sum(F(x, y), x, a, b)
*args y 1
res diff(F(x, y), y)*D[0](sr_sum)(F(x, y), x, a, b)
parser_output diff(F(x, y), y)*D[0](sr_sum)(F(x, y), x, a, b)
Inside dummy_diff(*args)
type of f
is just type(f) = <class 'sage.symbolic.expression.Expression'>
and may be you are right that sage thinks that 'sr_sum' an unknown function in four variables.
You may have found which code in sage is involved in producing the wrong answer, but that code is not "at fault". It's just that it doesn't know how to differentiate sums, because it hasn't been told how to.
If one defines:
def sum_deriv(self,*args,**kwds):
return self(*[[args[0].diff(kwds['diff_param'])]+list(args[1:])])
mysum=function('mysum',nargs=4,tderivative_func=sum_deriv)
one can just do:
sage: var('x,y,a,b')
(x, y, a, b)
sage: function('f')
f
sage: mysum(f(x,y),x,a,b).diff(y)
mysum(diff(f(x, y), y), x, a, b)
This approach is a little simple minded, of course (apart from the mathematical problem that term-wise differentiation is not always justified): it would need to check that the differentiation variable is indeed independent of the summation. With the current code, we get nonsense such as:
sage: mysum(f(x,y),x,a,b).diff(x)
mysum(diff(f(x, y), x), x, a, b)
sage: mysum(f(x,y),x,a,b).diff(a)
mysum(0, x, a, b)
However, it does show which hook on sage.functions.other.Function_sum'
would need to be populated.
Please see my commit which fixes the issue
New commits:
1130e20 | Trac #32161: adding "_tderivative_" method for Function_sum |
Commit: 1130e20
What does your code presently do with sum(f(x,b),x,a,b).diff(b)
? It should probably check that the differentiation variable is independent from the bounds as well and perhaps throw an error if it's not.
I'm also not so sure that 0 is the best answer if a derivative with respect to the summation variable is requested. Sure, it's not a free variable, so an external reference to it can be interpreted as a "different" variable, but it' more likely a typo.
I have confirmed with sympy output
sage: x,y = var("x,y")
....: a,b = var("a,b")
sage: F = function("F")(x, y)
sage: expression,v,a,b = [expr._sympy_() for expr in (F, x, a, b)]
....: from sympy import summation
....: from sage.interfaces.sympy import sympy_init
....: sympy_init()
sage: summation(expression, (v, a, b)).diff(y)
Sum(Derivative(F(x, y), y), (x, a, b))
sage: summation(expression, (v, a, b)).diff(v)
0
sage: summation(expression, (v, a, b)).diff(a)
Derivative(Sum(F(x, y), (x, a, b)), a)
sage: summation(expression, (v, a, b)).diff(b)
Derivative(Sum(F(x, y), (x, a, b)), b)
and tried the following
from sage.calculus.functional import derivative
if diff_param == a:
ans = derivative(symbolic_sum(f, x, a, b, hold=True), a)
if diff_param == b:
ans = derivative(symbolic_sum(f, x, a, b, hold=True), b)
The problem is that with this test
sage: s.diff(a)
diff(sum(f, x, a, b), a)
sage: s.diff(b)
diff(sum(f, x, a, b), b)
I have received exception caused by infinitly recursive call-stack.
Replying to @daju1:
The problem is that with this test
sage: s.diff(a) diff(sum(f, x, a, b), a) sage: s.diff(b) diff(sum(f, x, a, b), b)
I have received exception caused by infinitly recursive call-stack.
Yes, I guess the hold isn't strong enough here. You'll probably have to construct the derivative from lower-level primitives (basically, have a hold on the "derivative")
The problem is that in the current lingo, diff(sum(f, x, a, b), a)
should basically return (D[2]sum)(f,x,a,b)
. The operator in question is easily constructed by
sage.symbolic.operators.FDerivativeOperator(sage.functions.other.symbolic_sum,[2])
, but I think as soon as it's evaluated at (f,x,a,b)
it will try to expand the operator again, leading to an infinite recursion. I don't know if we have precedent of this: functions that do have some rules for expanding their derivatives, but still need inert forms to linger too. It would be worth asking sage symbolic computation experts (particularly the pynac crowd?) what means we have to support this.
For now I'd say raising an error would be fine; I don't think anything useful would be possible with a derivative with respect to a summation bound.
Branch pushed to git repo; I updated commit sha1. New commits:
60f89e6 | Raise exception when trying to take a derivative of symbilic sum with respect to a summation bound |
Branch pushed to git repo; I updated commit sha1. New commits:
f99433d | Merge tag '9.4.rc2' into t/32161/derivative_of_the_symbolic_sum_of_function_of_two_variables |
Branch pushed to git repo; I updated commit sha1. New commits:
6afad65 | Trac #32161: testable examples extended |
Author: Alexey Drozdov
I think we should also be a bit careful if one of the summation limits is oo
.
sage: n,x = var('n,x')
sage: F = function('F')(x)
sage: sum(F(x=x), x, 1, oo)
sum(F(x), x, 1, +Infinity)
sage: sum(x^(n*x), x, 1, oo)
sum(x^(n*x), x, 1, +Infinity)
Limits may not commute. While I think such a useful case for this is unlikely, we might want to be a little more on the cautious side here.
Stalled in needs_review
or needs_info
; likely won't make it into Sage 9.5.
This issue came up again in:
https://groups.google.com/g/sage-devel/c/Ksil-Qg9SlM/m/ZWUwKxdVAAAJ
perhaps merging a more modest solution first that just addresses the simple cases is worthwhile?
Ahem...
I'm not sure that "derive a sum with respect to (one of) is bounds" has any meaning... set
is a dscrete operation ; the summation variable takes integer values only. Forgetting this for a moment, try plotting points([(u, sum(x, x, 0, u)) for u in (0, 1/3..4)])
to convince yourselves. You can even plot(lambda u:sum(x, x, 0, u), (0, 4))
...
In other words, sum(x, x, a, b) == -1/2*a^2 + 1/2*b^2 + 1/2*a + 1/2*b
holds if and only if a
and b
are integers. Differentiating the right-hand side of this equality wrt a
and b
has a meaning ; differentiating the left hand has not.
The fact that the closed expression -1/2*a^2 + 1/2*b^2 + 1/2*a + 1/2*b
does not convey this condition is a design insufficiency of Sage (and Giac, Sympy and Mathematica ; Fricas (Axiom) may be a horse of an entirely different color...)
In consequence, we can check if the differentiation variable occurs in the expression of bounds, and safely raise some "Nonsense exception" (to be carefully choosen...) if so.
HTH,
Replying to @EmmanuelCharpentier:
I'm not sure that "derive a sum with respect to (one of) is bounds" has any meaning...
set
is a dscrete operation ; the summation variable takes integer values only. Forgetting this for a moment, try plottingpoints([(u, sum(x, x, 0, u)) for u in (0, 1/3..4)])
to convince yourselves. You can evenplot(lambda u:sum(x, x, 0, u), (0, 4))
...
I don't think many interesting things will be done with it but something like
F(x)=sum((x+k)^2,k,0,floor(sqrt(x))
makes perfect sense, and F(x)
is a piecewise differentiable function. So I think the restraint the people considering the implementation here have shown discarding differentiation with respect to variables that occur in the bounds is somewhat warranted.
I also think we've seen it gets pretty hairy quickly, so for the sake of at least being able to deal with more straightforward cases (and avoiding the outright wrong results we're getting now!) we SHOULD probably just cast an error if the summation variable or any of the bounds contain the differentiation variable.
Let's try to differentiate the symbolic sum of function of two variables.
But expected something like
or
CC: @nbruin @slel @mkoeppe @EmmanuelCharpentier
Component: symbolics
Author: Alexey Drozdov
Branch/Commit: u/gh-daju1/derivative_of_the_symbolic_sum_of_function_of_two_variables @
6afad65
Issue created by migration from https://trac.sagemath.org/ticket/32161