2

I've written a simple code that takes derivatives, however it fails to take derivative of trigonometry.

from math import sin, radians

def sinus(x):
    return sin(radians(x))

def derivative(func , x):
    h = 0.0000000001
    return (func(x+h)-func(x))/h

def f(x):
    return 2*x**2+x


print(derivative(f, 5))
print(derivative(sinus, 60))

Since the derivative of sine is cosine and cosine(60) is 0.5 the output should be

21.000...
0.5

but rather than that it outputs

21.0000195011
0.00872746319658

Am I doing something wrong or it just because of math.sin() function?

ForceBru
  • 43,482
  • 10
  • 63
  • 98
ihsancemil
  • 432
  • 5
  • 16

4 Answers4

4

As for the slight difference in the derivative of f(), that comes from your formula not being exact. That general difference quotient gives the exact answer only for an infinitely small value of h. The computer clearly cannot handle an infinitely small value, so you used a "regular" but somewhat small value of h and you got an approximate answer. If you use a very small value of h you then get other problems due to the computer working only in finite precision. That accuracy is about as good as you can get with double-precision floating point numbers. There are more complicated formulas that give better answers: do a search for "numeric derivative."

Rory Daulton
  • 21,934
  • 6
  • 42
  • 50
  • 'since it is already radians your conversion does nothing' -> well, it _does_ convert the given number to radians resulting in garbage being passed to `math.sin` – ForceBru May 27 '16 at 14:26
  • This second part of this answer is wrong... the reason that the sine derivative doesn't work is because `h` is being converted to radians in the sine function, but not in the denominator. – Jared Goguen May 27 '16 at 14:38
1

Often in numerical computation, it is not good to perform a computational task as you would on paper. Primarily because you have to be concerned with floating point errors. As such, a better implementation of a derivative is found by the following:

def derivative(func, x, h = None):
    if h is None:
        # Note the hard coded value found here is the square root of the
        # floating point precision, which can be found from the function
        # call np.sqrt(np.finfo(float).eps).
        h = 1.49011611938477e-08
    xph = x + h
    dx = xph - x
    return (func(xph) - func(x)) / dx

By looking at this, you may object and say that dx = h since xph - x = x + h - x = h, however if you actually do the calculation on a computer, you'll find out that this isn't true, due to rounding. Note also that it is important to pick a good value for h to get the best result possible. Right now the default is set to the square root of the machine precision for a float in python.

zephyr
  • 2,182
  • 3
  • 29
  • 51
  • good point. also mathematically `f’(x) ≈ (f(x+h) – f(x-h)) / 2h` has better properties. see http://www.johndcook.com/blog/2015/11/21/numerical-differentiation/ – Jacob May 27 '16 at 14:34
  • @Jacob Another good implementation. It should be simple enough to change the above code for that example. – zephyr May 27 '16 at 14:59
  • This doesn't change the result produced by OP, nor does it explain their issue. – Jared Goguen May 27 '16 at 15:03
1

No, you are not wrong on the code.

'd/dx sin(radians(x))' is 1/180 π cos((π x)/180)

http://www.wolframalpha.com/input/?i=d%2Fdx+sin(radians(x))

and that at 60 is ~ 0.00872746319658

Jacob
  • 1,423
  • 16
  • 29
1

The sine derivative is not working as expected because sinus converts the +h part into radians, while the denominator leaves it in degrees. You'll notice that the following function calculates the derivative correctly.

# treat h as degrees
def derivative(func, x):
    h = 0.0000000001
    return (func(x + h) - func(x)) / radians(h)

derivative(sinus, 60) # 0.5000468070196941

Or, alternatively, the value of h could be converted into degrees before passing it into sinus.

# treat h as radians
def derivative(func, x):
    h = 0.0000000001
    return (func(x + degrees(h)) - func(x)) / h

derivative(sinus, 60) # 0.5000000413701855

Notice that the latter function produces a more precise value because a radian value of 0.00...01 is smaller than its corresponding degree value. However, both of these approaches will not work for functions that don't expect an argument in degrees.

The only way to address this issue is to specify whether the input is in degrees, and even this may not work for more complicated trigonometric equations (since powers of π/180 pop up when differentiating the equations when expressed in degrees).

def derivative(func, x, trans=lambda x: x):
    h = 0.0000000001
    return (func(x + trans(h)) - func(x)) / h

derivative(sinus, 60, degrees) # 0.5000000413701855
Jared Goguen
  • 8,772
  • 2
  • 18
  • 36