I broke my problem down as follows. I am not able to solve the following equation with Python 3.9 in a meaningful way, instead it always stops with the initial_guess for small lambda_ < 1. Is there an alternative algorithm that can handle the error function better? Or can I force fsolve to search until a solution is found?
import numpy as np
from scipy.special import erfcinv, erfc
from scipy.optimize import root, fsolve
def Q(x):
return 0.5*erfc(x/np.sqrt(2))
def Qinvers(x):
return np.sqrt(2)*erfcinv(2*x)
def epseqn(epsilon2):
lambda_ = 0.1
return Q(lambda_*Qinvers(epsilon2))
eps1 = fsolve(epseqn, 1e-2)
print(eps1)
I tried root and fsolve to get a solution. Especially for the gaussian error function I do not find a solution that converges.
root and fsolve can be used to find the roots of a function defined by f(x)=0. Since your outer function, which is basically erfc(x), has no root (it only it approaches the x-axis asymptotically from positive values) the solvers are not able to find one. Real function arguments are assumed like you did.
Before blindly starting with numerical calculations, I would recommend to think about any constraints of your function.
You will find out, that your function is only defined for values between zero and one. If you assume that there is only a single root in this interval, I would recommend to use an interval search method like brentq, see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html#scipy.optimize.brentq and https://en.wikipedia.org/wiki/Brent%27s_method.
However, you could instead think further and/or just plot your function, e.g. using matplotlib
import matplotlib.pyplot as plt
x = np.linspace(0, 1, 1000)
y = epseqn(x)
plt.plot(x, y)
plt.show()
There you will see that the root is at zero, which makes sense when looking at your functions, because the inverse cumulative error function is minus infinity at zero and the regular error function gives you zero at minus infinity (mathematically in the limit sense, but numerically those functions are also defined for such input values). So without any numeric calculation, you can get the root value.
Related
I am trying to find roots of a function in python using fsolve:
import math
import scipy
def f(a):
eq=-2*a**2 - 2*a**2*(math.sin(25*a**(1/4)))**2 - 2*a**2*(math.cos(25*a**(1/4)))**2 - 2*math.exp(-25*a**(1/4))*a**2*math.cos(25*a**(1/4)) - 2*math.exp(25*a**(1/4))*a**2*math.cos(25*a**(1/4))
return eq
print(f(scipy.optimize.fsolve(f,10)))
and it returns the following value:
[1234839.75468454]
That doesn't seem very close to 0 to me... Does it simply lack the computational power to calculate more decimals for the root? If so, what would be a good alternative for fsolve that could also calculate roots, just more accurately?
To better understand what happens, a first step would be to look at the infos of the run, which you can get by setting the full_output argument to True (see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fsolve.html for more details).
When starting with an initial point of 10 as you do, the algorithm claims to converge and do so in less evaluations than the maximum allocated, so it is not a problem of computing power.
Sometimes scipy.integrate.quad wrongly returns near-0 values. This has been addressed in this question, and seems to happen when the integration technique doesn't evaluate the function in the narrow range where it is significantly different than 0. In this and similar questions, the accepted solution was always to use the points parameter to tell scipy where to look. However, for me, this seems to actually make things worse.
Integration of exponential distribution pdf (answer should be just under 1):
from scipy import integrate
import numpy as np
t2=.01
#initial problem: fails when f is large
f=5000000
integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2)
#>>>(3.8816838175855493e-22, 7.717972744727115e-22)
Now, the "fix" makes it fail, even on smaller values of f where the original worked:
f=2000000
integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2)
#>>>(1.00000000000143, 1.6485317987792634e-14)
integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2,points=[t2])
#>>>(1.6117047218907458e-17, 3.2045611390981406e-17)
integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2,points=[t2,t2])
#>>>(1.6117047218907458e-17, 3.2045611390981406e-17)
What's going on here? How can I tell scipy what to do so that this will evaluate for arbitrary values of f?
It's not a generic solution, but I've been able to fix this for the given function by using points=np.geomspace to guide the numerical algorithm towards heavier sampling in the interesting region. I'll leave this open for a little bit to see if anyone finds a generic solution.
Generate random values for t2 and f, then check min and max values for subset that should be very close to 1:
>>> t2s=np.exp((np.random.rand(20)-.5)*10)
>>> fs=np.exp((np.random.rand(20)-.1)*20)
>>> min(integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2,points=t2-np.geomspace(1/f,t2,40))[0] for f in fs for t2 in t2s if f>(1/t2)*10)
0.9999621825009719
>>> max(integrate.quad(lambda t:f*np.exp(-f*(t2-t)),0,t2,points=t2-np.geomspace(1/f,t2,40))[0] for f in fs for t2 in t2s if f>(1/t2)*10)
1.000000288722783
I am using symbolic integration to integrate a combined function of circular function and power function.
from sympy import *
import math
import numpy as np
t = Symbol('t')
integrate(0.000671813*(7/2*(1.22222222+sin(2*math.pi*t-math.pi/2))-6)**0.33516,t)
However, when I finished input, it gives me an odd result:
0.000671813*Integral((3.0*sin(6.28318530717959*t - 1.5707963267949) - 2.33333334)**0.33516, t)
Why does this result contain Integral()? I checked online other functions and there is no Integral() in them.
An unevaluated Integral answer means that SymPy was unable to compute the integral.
Essentially you are trying to integrate a function that looks like
(sin(t) + a)**0.33516
where a is a constant number.
In general such an integration is not possible to express in elementary functions; see, for example, http://www.sosmath.com/calculus/integration/fant/fant.html,
especially the sentence on Chebyshev's theorem.
Here's what I wrote: it's a classical exercise on interpolation, which I already finished and sent. I was wondering if there was another (longer) way...
q is a list of floats (the points of interpolation)
i is the index of the Lagrange polynomial
x is the point where is evaluated:
def l(q,i,x):
poly=1.0
for j,p in enumerate(q):
if j==i:
continue
poly *=(x-p)/(q[i]-p)
return poly
Then there is the function on which I'm working:
def Lambda(q,x):
value=0.0
for j in range(0,len(q)):
value+=abs(l(q,j,x))
return value
Now I can use some routines of python to find it's maxium value in the interval [0,1] and I did.
In python there is a polynomial module, with which I can easily re-define l:
import numpy.polynomial.polynomial as P
def l_poly(q,i):
poly = []
for j,p in enumerate(q):
if j==i:
continue
poly.append(p/(q[i]-p))
return P.polyfromroots(poly)
I'd like to do the same with Lambda so that I can find its maximum using the built in function of the derivative (find its zeros and so on and so forth). The problem is that it is a sum of abs(polynomials). Is there a way to do this? Or to mix the polynomial derivative and the derivative of abs(...)?
NumPy does not support arbitrary symbolic expression. It works only with polynomials, representing a polynomial as an array of coefficients. The absolute value of a polynomial is not a polynomial, so it is not a concept that NumPy has. It's a symbolic expression that can be handled by symbolic manipulation libraries like SymPy.
using the built in function of the derivative (find its zeros and so on and so forth).
There are several problems with this:
As said before, the polyder method of NumPy does not apply to this situation, since abs(polynomial) is not a polynomial.
The derivative of absolute function is undefined at 0.
The minimum or maximum of an expression involving absolute values may be attained where the derivative does not exist, so even if you could find the derivative, and somehow find its roots, you still would not solve the problem.
Looking for zeros of derivative is not a good way to minimize or maximize a function, outside of calculus exercises. Libraries like scipy.optimize implement many efficient numerical methods for this kind of problems.
I'm using fsolve in order to solve a non linear equation. My problem is that, depending on the starting point the solutions change and I am not sure that the ones that I found are the most reasonable.
This is the code
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve, brentq,newton
A = np.arange(0.05,0.95,0.01)
PHI = np.deg2rad(np.arange(0,90,1))
def f(b):
return np.angle((1+3*a**4-3*a**2)+(a**4-a**6)*(np.exp(2j*b)+2*np.exp(-1j*b))+(a**2-2*a**4+a**6)*(np.exp(-2j*b)+2*np.exp(1j*b)))-Phi
B = np.zeros((len(A),len(PHI)))
for i in range(len(A)):
for j in range(len(PHI)):
a = A[i]
Phi = PHI[j]
b = fsolve(f, 1)
B[i,j]= b
I fixed x0 = 1 because it seems to give the more reasonable values. But sometimes, I think the method doesn't converge and the resulting values are too big.
What can I do to find the best solution?
Many thanks!
The eternal issue with turning non-linear solvers loose is having a really good understanding of your function, your initial guess, the solver itself, and the problem you are trying to address.
I note that there are many (a,Phi) combinations where your function does not have real roots. You should do some math, directed by the actual problem you are trying to solve, and determine where the function should have roots. Not knowing the actual problem, I can't do that for you.
Also, as noted on a (since deleted) answer, this is cyclical on b, so using a bounded solver (such as scipy.optimize.minimize using method='L-BFGS-B' might help to keep things under control. Note that to find roots with a minimizer you use the square of your function. If the found minimum is not close to zero (for you to define based on the problem), the real minima might be a complex conjugate pair.
Good luck.