I am experiencing precision issue with fsolve.
import numpy as np
from scipy.optimize import fsolve
ens=np.arange(0,50,1)
def f(x):
return x*(np.sin(x)/np.cos(x))-1000
s=[]
roots=fsolve(f,ens)
roots=np.around(roots, decimals=3 , out=None)
a = roots[roots >= 0]
g = np.unique(a)
g=g[:5]
s.append(g)
print(s)
result :
[array([10.842, 11.006, 15.165, 21.116, 22.382])]
The result should be : [1.569,4.708,7.846,10.985,14.123]
My code miss the first three solutions, and the others are not accurate. Do you know how could I improve the precision of my results ?
1 Answer 1
You can use scipy.optimize.newton() with first and second derivative of the function f to get a more accurate result. You can do it by hand, or use derivative-calculator.net or wolframalpha. Pass both the first derivative fprime and the second derivative, fprime2 to newton(). If you do this, Halley's method will be used instead of a simple Newton-Raphson, which is more accurate (see the description below fprime2 in newton() docs).
def f(x):
return x*(np.sin(x)/np.cos(x))-1000
def fprime(x):
# first derivative of f
# do this by hand or use derivative-calculator.net or wolframalpha
return np.tan(x)+ x*(1/np.cos(x))**2
def fprime2(x):
# second derivative of f
# do this by hand or use derivative-calculator.net or wolframalpha
return 2*1/(np.cos(x)**2)*(x*np.tan(x)+1)
res = newton(f,x0=[1,4,7,10,14],fprime=fprime, fprime2=fprime2)
res = np.around(res, decimals=3)
res will be:
array([ 1.569, 4.708, 7.846, 10.985, 14.123])
The x0 argument in newton() above are the list of so-called initial guesses of where the roots are. As your function have infinitely many roots (see plot below), passing some of these helps to get the roots you actually care about.
This is how f looks like (well, f/1000 to make features visible):