Repository: sympy/sympy
Pull request: Fix Subs._eval_derivative function for higher order derivative
Fixes #12005 , one of the test cases(only one) taken from diofant.
If this fix has any similarity with the code of skirpichev that is probably a coincidence. I have only used the test case from his PR.
<!-- If this pull request fixes an issue please indicate which issue by typing "Fixes #NNNN" below.>


Problem statement:
Subs._eval_derivative doubles derivatives
This issue appears in the computation of higher derivatives with respect to functions.
```
>>> x = symbols('x')
>>> f = symbols('f', cls=Function)
>>> e = Subs(Derivative(f(x), x), (x,), (x,))
>>> e._eval_derivative(x)
2*Derivative(f(x), x, x)
```
Apparently, the derivative is computed in both of the two parts of the returned sum:
```
        return self.func(self.expr.diff(s), self.variables, self.point).doit() \
            + Add(*[ Subs(point.diff(s) * self.expr.diff(arg),
                    self.variables, self.point).doit() for arg,
                    point in zip(self.variables, self.point) ])
```

Hints from issue discussion:
This is the reason for the error encountered in #11799.

Commit message: Fix Subs._eval_derivative function for higher order derivative

Fixes #12005, one of the test cases(only one) taken from diofant

Patch to review:
diff --git a/sympy/core/function.py b/sympy/core/function.py
index 2d33d96b94b3..262690a5aa92 100644
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -1738,10 +1738,9 @@ def _eval_subs(self, old, new):
     def _eval_derivative(self, s):
         if s not in self.free_symbols:
             return S.Zero
-        return self.func(self.expr.diff(s), self.variables, self.point).doit() \
-            + Add(*[ Subs(point.diff(s) * self.expr.diff(arg),
-                    self.variables, self.point).doit() for arg,
-                    point in zip(self.variables, self.point) ])
+        return Add(*[point.diff(s)*self.func(self.expr.diff(var), self.variables, self.point).doit() + \
+                self.func(self.expr.diff(s) if s != var else S.Zero, self.variables, self.point).doit() \
+                for var, point in zip(self.variables, self.point)])
 
     def _eval_nseries(self, x, n, logx):
         if x in self.point:
diff --git a/sympy/core/tests/test_function.py b/sympy/core/tests/test_function.py
index d1fdd97ef814..c91d37768804 100644
--- a/sympy/core/tests/test_function.py
+++ b/sympy/core/tests/test_function.py
@@ -811,3 +811,13 @@ def test_issue_11159():
     expr0 = expr1 * expr1
     expr1 = expr0.subs(expr1,expr0)
     assert expr0 == expr1
+
+
+def test_issue_12005():
+    e1 = Subs(Derivative(f(x), x), (x,), (x,))
+    assert e1.diff(x) == Derivative(f(x), x, x)
+
+    e2 = Subs(Derivative(f(x), x), (x,), (x**2 + 1,))
+    assert e2.diff(x) == 2*x*Subs(Derivative(f(x), x, x), (x,), (x**2 + 1,))
+
+    assert f(g(x)).diff(g(x), g(x)) == Subs(Derivative(f(y), y, y), (y,), (g(x),))


Code review comments:
diff_hunk: @@ -1738,10 +1738,9 @@ def _eval_subs(self, old, new):
     def _eval_derivative(self, s):
         if s not in self.free_symbols:
             return S.Zero
-        return self.func(self.expr.diff(s), self.variables, self.point).doit() \
-            + Add(*[ Subs(point.diff(s) * self.expr.diff(arg),
-                    self.variables, self.point).doit() for arg,
-                    point in zip(self.variables, self.point) ])
+        return Add(*[point.diff(s)*self.func(self.expr.diff(var), self.variables, self.point).doit() + \
+                self.func(self.expr.diff(s) if s != var else S.Zero, self.variables, self.point).doit() \
path: sympy/core/function.py
text: @user1:
It looks like this term may occur several times.
```
In [42]: e = Subs(Derivative(f(x), x), (y, z), (y, z))

In [43]: print(e.diff(x))
2*Derivative(f(x), x, x)

In [45]: e = Subs(Derivative(f(x), x), (y, z, t), (y, z, t))

In [46]: print(e.diff(x))
3*Derivative(f(x), x, x)
```
The backslashes are probably not needed.