5

I would like to compute derivative of y=Abs(0.5-0.5*sqrt(1-4*x)) in 0.1, using python. This is my code:

x=Symbol('x')
y=Abs(0.5-0.5*sqrt(1-4*x))
deriv=y.diff(x)
d=lambdify(x,deriv,'numpy')
print d(0.1)

This is what I get:

Traceback (most recent call last):
  File "/home/milossimic/g4/s1/.../optimize.py", line 100, in <module>
    print d(0.1)
  File "<string>", line 1, in <lambda>
NameError: global name 'Derivative' is not defined

I'm a newbie to sympy and numpy, so I guess I'm using the wrong method to determine derivative.

EDIT: I printed deriv and this is what I got:

enter image description here

After reading this http://docs.sympy.org/dev/modules/functions/elementary.html, I've tried fdiff():

x=Symbol('x')
y=Abs(0.5-0.5*sqrt(1-4*x))
deriv=y.fdiff()
d=lambdify(x,deriv,'numpy')
print d(0)

But after experimenting with other values to compute derivative, I figured out that the result is -1, 0 or 1 because deriv is actually sign(-0.5*sqrt(-4*x + 1) + 0.5).

What should I do?

Both numpy and sympy are imported:

from sympy import *
import numpy as np

If I try to find derivative of a function that is not under Abs, there are no problems.

Milos
  • 518
  • 7
  • 22
  • Just as a side note: At `x=0` your function is not differentiable. I don't know how simpy handles this though. Have you tried other `x` values? –  Dec 28 '13 at 15:56
  • I did, but it is always the same: global name 'Derivative' is not defined, no matter what x I choose. : ) – Milos Dec 28 '13 at 16:34
  • I checked your initial code with my setup, I get the error `NameError: global name 'Abs' is not defined`, but only if I choose `'numpy'` as module. Without or with `('numpy','sympy')` it works. However you will still get a division error at `x=0`. –  Dec 28 '13 at 16:39
  • It's always the error `NameError: global name 'Derivative' is not defined`. – Milos Dec 28 '13 at 16:49
  • The `NameError: global name 'Abs' is not defined` is due to an bug in my old version of sympy and probably not connected with your issue. –  Dec 28 '13 at 16:50
  • The second method is obviously wrong, the only thing `fdiff` did is replace `Abs` with `sign` as the documentation you posted points out if you click on `source`. At `x=0` there is no derivative. –  Dec 28 '13 at 17:13
  • Yes, there isn't derivative at x=0. I'll edit the question now, but the problem will remain the same, in terms of error returned. – Milos Dec 28 '13 at 17:20

2 Answers2

6

This is more a mathematical problem than anything else.

>>> import sympy
>>> x = sympy.symbols('x')
>>> def f(x):
...  return abs(x)
... 
>>> dx = f(x).diff(x)
>>> dx
(re(x)*Derivative(re(x), x) + im(x)*Derivative(im(x), x))/Abs(x)

Notice there is a real part and an imaginary part. abs(x) is differentiable at every real x, but zero. However, there are issues when it comes to complex values (which I can't explain since I don't know complex differentiation). I guess sympy doesn't have an implementation for that, and thus returns Derivative(f) instead of the actual derivative of f.

If you're working only with real numbers, then just use x = sympy.symbols('x', real=True):

>>> import sympy
>>> x = sympy.symbols('x', real=True)
>>> def f(x):
...  return abs(0.5-0.5*(1-4*x)**0.5)
... 
>>> dx = f(x).diff(x)
>>> dx
(1.0*(-0.5*re((-4*x + 1)**0.5) + 0.5)*re((-4*x + 1)**(-0.5)) - 0.5*im((-4*x + 1)**(-0.5))*im((-4*x + 1)**0.5))/Abs(-0.5*(-4*x + 1)**0.5 + 0.5)
>>> dx_ = sympy.lambdify(x, dx)
>>> dx_(0.1)
1.2909944487358056
Alex
  • 1,416
  • 4
  • 16
  • 42
  • Which version of python/sympy are you using? I get `re(x)/Abs(x)` as result for `dx` in your first code block with python 2.7 and sympy 0.7.1.rc1, which is probably why I couldn't reproduce the issue. –  Dec 28 '13 at 23:39
  • @Nabla that SymPy version is quite old. 0.7.4.1 gives what Alex got. – asmeurer Dec 29 '13 at 04:26
  • Another thing to note, if you plan to enter values such that the square roots become complex, you'll need to input a numpy array with complex dtype, or else you will get a TypeError because numpy's sqrt refuses to work with negative real numbers. – asmeurer Dec 29 '13 at 04:46
  • This solution is so much better and easier. Thank you! – Oleh Prypin May 29 '14 at 16:50
  • I think we should reopen this one, I have issues if lambdify is to work on a NumPy array. I always get global name Abs or MyAbs is not defined. I post a new question – Jens Munk Dec 22 '14 at 19:40
0

You probably just want the derivative of Abs to be sign. SymPy does do this, but only if it can deduce that the argument to the absolute value is real, which it can't in this case (even if x is real).

You can make your own custom version of Abs that always uses sign pretty easily by subclassing and overriding the _eval_derivative method:

class MyAbs(Abs):
    def _eval_derivative(self, x):
        return Derivative(self.args[0], x, evaluate=True)*sign(conjugate(self.args[0]))

.

In [110]: x = Symbol('x')

In [111]: y = MyAbs(0.5-0.5*sqrt(1-4*x))

In [112]: deriv = y.diff(x)

In [113]: print(deriv)
1.0*sign(-0.5*conjugate(sqrt(-4*x + 1)) + 0.5)/sqrt(-4*x + 1)

In [114]: lambdify(x, deriv, 'numpy')(0.1)
Out[114]: 1.29099444874
asmeurer
  • 86,894
  • 26
  • 169
  • 240