Repository: sympy/sympy

Issue:
Incorrect subsitution of partial derivatives by .subs() 
With sympy 0.7.6, the subs() method does a wrong substitution:

    from sympy import *
    init_printing()

    x,y,z=symbols('x,y,z')
    f=Function('f')

    expr = diff(f(x,y),x,x,y)
    pprint(expr)

    e2 = expr.subs(diff(f(x,y),y,2), z)
    pprint(e2)

The call to subs() method incorrectly substitutes a `df/dy` with `z`, while we asked to substitute `d2f/dy2` instead !


Discussion:

Patch:
diff --git a/sympy/core/function.py b/sympy/core/function.py
index 94eb5128ff..e1ae71929a 100644
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -57,7 +57,7 @@
 import mpmath.libmp as mlib
 
 import inspect
-
+import collections
 
 def _coeff_isneg(a):
     """Return True if the leading Number is negative.
@@ -1330,31 +1330,18 @@ def _eval_subs(self, old, new):
         # equivalent to self or if old is a subderivative of self.
         if old.is_Derivative and old.expr == self.args[0]:
             # Check if canonnical order of variables is equal.
-            old_vars = Derivative._sort_variables(old.variables)
-            self_vars = Derivative._sort_variables(self.args[1:])
+            old_vars = collections.Counter(old.variables)
+            self_vars = collections.Counter(self.variables)
             if old_vars == self_vars:
                 return new
 
-            # Check if olf is a subderivative of self.
-            if len(old_vars) < len(self_vars):
-                self_vars_front = []
-                match = True
-                while old_vars and self_vars and match:
-                    if old_vars[0] == self_vars[0]:
-                        old_vars.pop(0)
-                        self_vars.pop(0)
-                    else:
-                        # If self_v does not match old_v, we need to check if
-                        # the types are the same (symbol vs non-symbol). If
-                        # they are, we can continue checking self_vars for a
-                        # match.
-                        if old_vars[0].is_Symbol != self_vars[0].is_Symbol:
-                            match = False
-                        else:
-                            self_vars_front.append(self_vars.pop(0))
-                if match:
-                    variables = self_vars_front + self_vars
-                    return Derivative(new, *variables)
+            # collections.Counter doesn't have __le__
+            def _subset(a, b):
+                return all(a[i] <= b[i] for i in a)
+
+            if _subset(old_vars, self_vars):
+                return Derivative(new, *(self_vars - old_vars).elements())
+
         return Derivative(*(x._subs(old, new) for x in self.args))
 
     def _eval_lseries(self, x, logx):


Test patch:
diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py
index c50890e0a3..f2133a36c3 100644
--- a/sympy/core/tests/test_subs.py
+++ b/sympy/core/tests/test_subs.py
@@ -431,17 +431,27 @@ def test_derivative_subs():
 
 def test_derivative_subs2():
     x, y, z = symbols('x y z')
-    f, g = symbols('f g', cls=Function)
+    f_func, g_func = symbols('f g', cls=Function)
+    f, g = f_func(x, y, z), g_func(x, y, z)
     assert Derivative(f, x, y).subs(Derivative(f, x, y), g) == g
     assert Derivative(f, y, x).subs(Derivative(f, x, y), g) == g
     assert Derivative(f, x, y).subs(Derivative(f, x), g) == Derivative(g, y)
     assert Derivative(f, x, y).subs(Derivative(f, y), g) == Derivative(g, x)
-    assert (Derivative(f(x, y, z), x, y, z).subs(
-                Derivative(f(x, y, z), x, z), g) == Derivative(g, y))
-    assert (Derivative(f(x, y, z), x, y, z).subs(
-                Derivative(f(x, y, z), z, y), g) == Derivative(g, x))
-    assert (Derivative(f(x, y, z), x, y, z).subs(
-                Derivative(f(x, y, z), z, y, x), g) == g)
+    assert (Derivative(f, x, y, z).subs(
+                Derivative(f, x, z), g) == Derivative(g, y))
+    assert (Derivative(f, x, y, z).subs(
+                Derivative(f, z, y), g) == Derivative(g, x))
+    assert (Derivative(f, x, y, z).subs(
+                Derivative(f, z, y, x), g) == g)
+
+    # Issue 9135
+    assert (Derivative(f, x, x, y).subs(
+                Derivative(f, y, y), g) == Derivative(f, x, x, y))
+    assert (Derivative(f, x, y, y, z).subs(
+                Derivative(f, x, y, y, y), g) == Derivative(f, x, y, y, z))
+
+    assert Derivative(f, x, y).subs(Derivative(f_func(x), x, y), g) == Derivative(f, x, y)
+
 
 def test_derivative_subs3():
     x = Symbol('x')