python - Sympy: Define custom derivative on symbol - Stack Overflow

admin2025-04-30  2

I'm trying to create a custom symbol in sympy that behaves like r = √(x2 + y2 + z2) -- specifically, ∂r/∂x = x/r:

from sympy import Symbol, symbols

x, y, z = symbols("x y z")

class _r(Symbol):
    def __new__(self):
        r = super().__new__(self, "r")
        return r

    def diff(self, var):
        assert var in [x, y, z]
        return 1 / self * var

r = _r()

The first derivative works as intended:

>>> r.diff(x)
x/r

The second derivative should return -x2/r3 + 1/r, but sympy doesn't differentiate the r symbol again:

>>> r.diff(x).diff(x)
1/r

I inspected the intermediate result and found the following:

>>> d = r.diff(x)
>>> type(d)
sympy.core.mul.Mul
>>> d.free_symbols
{r, x}
>>> for s in d.free_symbols:
>>>     print(s, type(s))
r <class '__main__._r'>
x <class 'sympy.core.symbol.Symbol'>

So it does still recognize r as my custom object, but somehow that gets lost when calling .diff of the resulting Mul object.

How do I get this to work? Am I even on the right track by subclassing Symbol?

I'm trying to create a custom symbol in sympy that behaves like r = √(x2 + y2 + z2) -- specifically, ∂r/∂x = x/r:

from sympy import Symbol, symbols

x, y, z = symbols("x y z")

class _r(Symbol):
    def __new__(self):
        r = super().__new__(self, "r")
        return r

    def diff(self, var):
        assert var in [x, y, z]
        return 1 / self * var

r = _r()

The first derivative works as intended:

>>> r.diff(x)
x/r

The second derivative should return -x2/r3 + 1/r, but sympy doesn't differentiate the r symbol again:

>>> r.diff(x).diff(x)
1/r

I inspected the intermediate result and found the following:

>>> d = r.diff(x)
>>> type(d)
sympy.core.mul.Mul
>>> d.free_symbols
{r, x}
>>> for s in d.free_symbols:
>>>     print(s, type(s))
r <class '__main__._r'>
x <class 'sympy.core.symbol.Symbol'>

So it does still recognize r as my custom object, but somehow that gets lost when calling .diff of the resulting Mul object.

How do I get this to work? Am I even on the right track by subclassing Symbol?

Share Improve this question edited Jan 4 at 22:11 Antimon asked Jan 4 at 21:52 AntimonAntimon 7525 silver badges12 bronze badges 8
  • One of the challenges is that newly created objects (by the sympy operators and diff) are again just of type sympy.Symbol. I myself have derived a class from pint.Quantity, but they provide a registry to register the class that is created. I wonder if sympyoffers a similar mechanism. – Dr. V Commented Jan 4 at 22:02
  • @Dr.V Thanks for your input. I inspected the intermediate results and it seems that sympy still recognizes my custom symbol after the first diff call. I've updated my post. – Antimon Commented Jan 4 at 22:13
  • Good. The part I didn't understand yet (and I hoped it wasn't part of the problem) is how sympy can know that r is a function of x, y an z, and what that function is. – Dr. V Commented Jan 4 at 22:24
  • 1 Don't subclass Symbol and don't override .diff. See the guide for making custom functions: docs.sympy.org/latest/guides/custom-functions.html – Oscar Benjamin Commented Jan 4 at 23:00
  • 1 The second diff(x) is a standard sympy diff(x) and it takes a derivative of x/r, which is 1/r. To check, change diff() in the _r class definition to rdiff(). Runing r.rdiff(x).rdiff(x) will result in an error AttributeError: 'Mul' object has no attribute 'rdiff'. However, r.rdiff(x).diff(x) will result in 1/r. – Man made of meat Commented Jan 5 at 7:09
 |  Show 3 more comments

1 Answer 1

Reset to default 0

You can probably have your display/representation cake and eat it too if you switch to using a pair of symbols and rely on .subs() to switch between them

R = symbols("R")              # your special symbol
r = sqrt(x**2 + y**2 + z**2)  # logical equivalence
>>> r.diff(x)
x/sqrt(x**2 + y**2 + z**2)
>>> r.diff(x).diff(x)
-x**2/(x**2 + y**2 + z**2)**(3/2) + 1/sqrt(x**2 + y**2 + z**2)
>>> r.diff(x).diff(x).subs(r, R)
1/R - x**2/R**3
转载请注明原文地址:http://www.anycun.com/QandA/1746026218a91524.html