Repository: sympy/sympy

Issue:
Add note to sympy.physics.vector/mechanics docs about substitution for derivatives
My students get caught on this, see this gist:

https://gist.github.com/moorepants/aec439a0383ed387f7ebb7693a9927f3

This could be added to the gotchas section of sympy, potentially (if not there) or maybe it is just useful for adding to the sympy.physics.vector or mechanics docs because it is such a common thing to do in those modules.

Discussion:

Patch:
diff --git a/sympy/core/basic.py b/sympy/core/basic.py
index 6866648d4e..4fc124934f 100644
--- a/sympy/core/basic.py
+++ b/sympy/core/basic.py
@@ -889,7 +889,7 @@ def subs(self, *args, **kwargs):
 
         """
         from sympy.core.containers import Dict
-        from sympy.utilities import default_sort_key
+        from sympy.utilities.iterables import sift
         from sympy import Dummy, Symbol
 
         unordered = False
@@ -929,23 +929,10 @@ def subs(self, *args, **kwargs):
 
         if unordered:
             sequence = dict(sequence)
-            if not all(k.is_Atom for k in sequence):
-                d = {}
-                for o, n in sequence.items():
-                    try:
-                        ops = o.count_ops(), len(o.args)
-                    except TypeError:
-                        ops = (0, 0)
-                    d.setdefault(ops, []).append((o, n))
-                newseq = []
-                for k in sorted(d.keys(), reverse=True):
-                    newseq.extend(
-                        sorted([v[0] for v in d[k]], key=default_sort_key))
-                sequence = [(k, sequence[k]) for k in newseq]
-                del newseq, d
-            else:
-                sequence = sorted([(k, v) for (k, v) in sequence.items()],
-                                  key=default_sort_key)
+            atoms, nonatoms = sift(list(sequence),
+                lambda x: x.is_Atom, binary=True)
+            sequence = [(k, sequence[k]) for k in
+                list(reversed(list(ordered(nonatoms)))) + list(ordered(atoms))]
 
         if kwargs.pop('simultaneous', False):  # XXX should this be the default for dict subs?
             reps = {}


Test patch:
diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py
index f345681852..53d428269a 100644
--- a/sympy/core/tests/test_subs.py
+++ b/sympy/core/tests/test_subs.py
@@ -6,8 +6,7 @@
     AccumBounds, Matrix, zeros, ZeroMatrix)
 from sympy.core.basic import _aresame
 from sympy.testing.pytest import XFAIL
-from sympy.abc import a, x, y, z
-
+from sympy.abc import a, x, y, z, t
 
 def test_subs():
     n3 = Rational(3)
@@ -843,3 +842,11 @@ def test_issue_11746():
     assert (1/(x**4)).subs(x**2, 1) == 1
     assert (1/(x**3)).subs(x**4, 1) == x**(-3)
     assert (1/(y**5)).subs(x**5, 1) == y**(-5)
+
+
+def test_issue_17823():
+    from sympy.physics.mechanics import dynamicsymbols
+    q1, q2 = dynamicsymbols('q1, q2')
+    expr = q1.diff().diff()**2*q1 + q1.diff()*q2.diff()
+    reps={q1: a, q1.diff(): a*x*y, q1.diff().diff(): z}
+    assert expr.subs(reps) == a*x*y*Derivative(q2, t) + a*z**2