Repository: sympy/sympy
Pull request: differentiate_finite: set evaluate kwarg default to False (fix gh-12186)
In `differentiate_finite` , the default of the kwarg in `evaluate` is changed from `True` to `False` and hence fixes gh-12186.

"Fortunately", the default value of `evaluate` was undocumented so I think we can skip a deprecation cycle in this case.

@halpernf I would be grateful to hear if you have any feedback on the wording / suggestions for improvement.

Problem statement:
default behavior for product rule in differentiate_finite is incorrect
Hi everybody,

First time user of SymPy. So far I am quite enjoying this and I was able to generate Arakawa brackets of arbitrary order with just a few lines of SymPy. I wanted to report on an issue I came across.

The following expression from the SymPy documentation shows use of the product rule for continuous function applied on a discrete function. This behavior is incorrect, e.g.

``>>> x = symbols('x')``
``>>> f, g = symbols('f g', cls=Function)``
``>>> differentiate_finite(f(x)*g(x))``
``(-f(x - 1/2) + f(x + 1/2))⋅g(x) + (-g(x - 1/2) + g(x + 1/2))⋅f(x)``

A large part of the applied mathematics literature is devoted to designing numerical schemes that preserve a "discrete product rule." Examples are summation-by-parts and mimetic finite differences. A better default behavior in SymPy would be to force ``evaluate=False``, which gives

``>>> differentiate_finite(f(x)*g(x), evaluate=False)``
``-f(x - 1/2)⋅g(x - 1/2) + f(x + 1/2)⋅g(x + 1/2)``

The latter expression is the correct answer.

Hints from issue discussion:
I am +1 for just changing the default to `evaluate=False`, @halpernf would you mind opening a PR changing this and perhaps changing the docstring to include the point you've made in this issue? 
@bjodah I would like to work on this issue, kindly refer to any starting point on this . Thanks

@mohit3011 are you familiar with MFD? I would do this myself if I had experience with MFD, but since I don't, I think it's better that someone who has updates the documentation.
@bjodah  What's the full form of MFD?
@bjodah sorry about my ignorance, by PR you mean pull request?

@bjodah @mohit3011 no need to implement MFD here. Changing the default to `evaluate=False` should be enough for the time being... I'll have some time later on this week. It should be easy enough to do even if I don't know the code that well.

Commit message: Change evaluate kwarg default to False (fixes gh-12186)

Patch to review:
diff --git a/doc/src/tutorial/calculus.rst b/doc/src/tutorial/calculus.rst
index 1c2c3c336561..61ac1077901e 100644
--- a/doc/src/tutorial/calculus.rst
+++ b/doc/src/tutorial/calculus.rst
@@ -323,14 +323,15 @@ the ``differentiate_finite`` function:
 
     >>> f, g = symbols('f g', cls=Function)
     >>> differentiate_finite(f(x)*g(x))
-    (-f(x - 1/2) + f(x + 1/2))⋅g(x) + (-g(x - 1/2) + g(x + 1/2))⋅f(x)
+    -f(x - 1/2)⋅g(x - 1/2) + f(x + 1/2)⋅g(x + 1/2)
 
-If we don't want to expand the intermediate derivative we may pass the
+If we want to expand the intermediate derivative we may pass the
 flag ``evaluate=False``:
 
-    >>> differentiate_finite(f(x)*g(x), evaluate=False)
-    -f(x - 1/2)⋅g(x - 1/2) + f(x + 1/2)⋅g(x + 1/2)
+    >>> differentiate_finite(f(x)*g(x), evaluate=True)
+    (-f(x - 1/2) + f(x + 1/2))⋅g(x) + (-g(x - 1/2) + g(x + 1/2))⋅f(x)
 
+This form however does not respect the product rule.
 If you already have a ``Derivative`` instance, you can use the
 ``as_finite_difference`` method to generate approximations of the
 derivative to arbitrary order:
diff --git a/sympy/calculus/finite_diff.py b/sympy/calculus/finite_diff.py
index d9cfbb1eab16..1647386c20f1 100644
--- a/sympy/calculus/finite_diff.py
+++ b/sympy/calculus/finite_diff.py
@@ -430,8 +430,8 @@ def differentiate_finite(expr, *symbols,
     wrt: Symbol, optional
         see ``Derivative.as_finite_difference``
     evaluate : bool
-        kwarg passed on to ``diff`` (whether or not to
-        evaluate the Derivative intermediately).
+        kwarg passed on to ``diff``, whether or not to
+        evaluate the Derivative intermediately (default: ``False``).
 
 
     Examples
@@ -439,26 +439,33 @@ def differentiate_finite(expr, *symbols,
 
     >>> from sympy import cos, sin, Function, differentiate_finite
     >>> from sympy.abc import x, y, h
-    >>> f = Function('f')
+    >>> f, g = Function('f'), Function('g')
+    >>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h])
+    -f(-h + x)*g(-h + x)/(2*h) + f(h + x)*g(h + x)/(2*h)
+
+    Note that the above form preserves the product rule in discrete form.
+    If we want we can pass ``evaluate=True`` to get another form (which is
+    usually no what we want):
+
+    >>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h], evaluate=True).simplify()
+    -((f(-h + x) - f(h + x))*g(x) + (g(-h + x) - g(h + x))*f(x))/(2*h)
+
+    ``differentiate_finite`` works on any expression:
+
     >>> differentiate_finite(f(x) + sin(x), x, 2)
-    -2*f(x) + f(x - 1) + f(x + 1) - sin(x)
-    >>> differentiate_finite(f(x) + sin(x), x, 2, evaluate=False)
     -2*f(x) + f(x - 1) + f(x + 1) - 2*sin(x) + sin(x - 1) + sin(x + 1)
+    >>> differentiate_finite(f(x) + sin(x), x, 2, evaluate=True)
+    -2*f(x) + f(x - 1) + f(x + 1) - sin(x)
     >>> differentiate_finite(f(x, y), x, y)
     f(x - 1/2, y - 1/2) - f(x - 1/2, y + 1/2) - f(x + 1/2, y - 1/2) + \
 f(x + 1/2, y + 1/2)
-    >>> g = Function('g')
-    >>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h]).simplify()
-    -((f(-h + x) - f(h + x))*g(x) + (g(-h + x) - g(h + x))*f(x))/(2*h)
-    >>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h], evaluate=False)
-    -f(-h + x)*g(-h + x)/(2*h) + f(h + x)*g(h + x)/(2*h)
 
     """
     # Key-word only arguments only available in Python 3
     points = kwargs.pop('points', 1)
     x0 = kwargs.pop('x0', None)
     wrt = kwargs.pop('wrt', None)
-    evaluate = kwargs.pop('evaluate', True)
+    evaluate = kwargs.pop('evaluate', False)
     if kwargs != {}:
         raise ValueError("Unknown kwargs: %s" % kwargs)
 
diff --git a/sympy/calculus/tests/test_finite_diff.py b/sympy/calculus/tests/test_finite_diff.py
index a75b92927fef..b70ea5fa152f 100644
--- a/sympy/calculus/tests/test_finite_diff.py
+++ b/sympy/calculus/tests/test_finite_diff.py
@@ -114,18 +114,17 @@ def test_as_finite_diff():
 def test_differentiate_finite():
     x, y = symbols('x y')
     f = Function('f')
-    res0 = differentiate_finite(f(x, y) + exp(42), x, y)
+    res0 = differentiate_finite(f(x, y) + exp(42), x, y, evaluate=True)
     xm, xp, ym, yp = [v + sign*S(1)/2 for v, sign in product([x, y], [-1, 1])]
     ref0 = f(xm, ym) + f(xp, yp) - f(xm, yp) - f(xp, ym)
     assert (res0 - ref0).simplify() == 0
 
     g = Function('g')
-    res1 = differentiate_finite(f(x)*g(x) + 42, x)
+    res1 = differentiate_finite(f(x)*g(x) + 42, x, evaluate=True)
     ref1 = (-f(x - S(1)/2) + f(x + S(1)/2))*g(x) + \
            (-g(x - S(1)/2) + g(x + S(1)/2))*f(x)
     assert (res1 - ref1).simplify() == 0
 
-    res2 = differentiate_finite(f(x) + x**3 + 42, x, points=[x-1, x+1],
-                                evaluate=False)
+    res2 = differentiate_finite(f(x) + x**3 + 42, x, points=[x-1, x+1])
     ref2 = (f(x + 1) + (x + 1)**3 - f(x - 1) - (x - 1)**3)/2
     assert (res2 - ref2).simplify() == 0


Code review comments:
diff_hunk: @@ -430,35 +430,42 @@ def differentiate_finite(expr, *symbols,
     wrt: Symbol, optional
         see ``Derivative.as_finite_difference``
     evaluate : bool
-        kwarg passed on to ``diff`` (whether or not to
-        evaluate the Derivative intermediately).
+        kwarg passed on to ``diff``, whether or not to
+        evaluate the Derivative intermediately (default: ``False``).
 
 
     Examples
     ========
 
     >>> from sympy import cos, sin, Function, differentiate_finite
     >>> from sympy.abc import x, y, h
-    >>> f = Function('f')
+    >>> f, g = Function('f'), Function('g')
+    >>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h])
+    -f(-h + x)*g(-h + x)/(2*h) + f(h + x)*g(h + x)/(2*h)
+
+    Note that the above form preserves the product rule in discrete form.
+    If we want we can pass ``evaluate=True`` to get another form (which is
+    usually no what we want):
path: sympy/calculus/finite_diff.py
text: @user1:
Typo