I have a problem performing a double definite integral in a function which depend of 2 variables (q,r) and has one extra integral in it.
The function I want to weight with a gaussian function is:
F(q,r)=f(q,r)+int_{0,r}(h(q,r')dr')
And in must be integrated again to be weighted with the gaussian:
I(q)=int_{0,inf}(F(q,r)^2*g(r)dr)
The gaussian g(r) is centered in the coordinate R.
The main problem as you can observe is that I am mixing arrays with scalars. Using the same method that it is used for the gaussian (np.ogrid and sum over the axis) could be a solution, but I don't know how to implement it.
import numpy as np
from scipy.integrate import quad
import math as m
R=53.
R0=40.
delta=50.
c=2.
qm, rm = np.ogrid[0.0005:2.0:0.0005, 20:100:500j]
#normalized gauss function
#g(r)
def gauss_grid(r,Rmin,pd):
def gauss(r,Rmin,pd):
sigma=1.5
return (1/sigma)*np.exp(-((r-Rmin)**2)/(2*sigma**2))
gauss_grid = gauss(r,Rmin,pd)
#normalization of gaussian
gauss_grid /= np.sum(gauss_grid)
return gauss_grid
#spherical function
#f(q,r)
def form(q,R):
return (4/3)*m.pi*3*(np.sin(q*R)-q*R*np.cos(q*R))/(q**3)
#FINAL function
#I(q)
def helfand():
def F(q,R):
#integral (0,R) of h(q,r)
def integral(q,Rmax):
#h(q,r)
def integrand(r,q):
return np.sin(q*r)*(r**2)/(q*r*(1+np.exp(c*(R0-r))))
return quad(integrand, 0, Rmax, args=(q))[0]
return (form(q,R)+delta*integral(q,R))**2
FF_hel=F(qm,rm)
FF_hel *= gauss_grid(rm,R,pd)
I=FF_hel.sum(axis=1)
return I,qm.ravel()
helfand()
*UPDATE****
I tried with the scipy.integrate library (with quad) and I cannot make it done. It is like it doesn't pass the right argument (q) to the next function. Here a very simplified version of what I'm trying:
import numpy as np
from scipy.integrate import quad
import matplotlib.pyplot as plt
R=53.
R0=41.
pd=15.
sigma=1.5
def I(q):
#(function with integral inside) squared
def FF(q,r):
def integral_f(q,r):
def f(r1,q):
return np.sin(q*r1)
return quad(f,0,r,args=(q))[0]
def h(q,r):
return (r*np.cos(q*r))
return (h(q,r)+integral_f(q,r))**2
#gaussian function normalized
def g(r,R0):
def gauss(r,R0):
return (1/sigma)*np.exp(-((r-R0)**2)/(2*sigma**2))
return gauss(r,R0)/(quad(gauss,0,np.inf,args=(R0))[0])
#main function to be integrated with gaussian
def function(r,q):
return FF(q,r)*g(r,R)
return quad(function,0,np.inf,args=(q))[0]
q=np.arange(0.001,1.,0.001)
plt.plot(q,I(q))
The error says:
Supplied function does not return a valid float.
I'd create a simple 2D rectangular mesh of points that spanned the limits of integration points. Then I'd prefer Gaussian quadrature over each element to evaluate the integral. It would mean calling the function, weighted or not, at each integration point, multiplying by the quadrature weight, and summing.
It's similar to 2D quadrilaterial finite elements and evaluating the stiffness matrix by numerical integration.
There are 2D quadrature methods in SciPy. I'd use that before writing my own.
http://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quadrature.html
I think you can compute this with two single integrals.
If you write out the double integral you get two parts:
int_{0,inf}(f(q,r)*g(r)dr)+ int_(0,inf)( int_{0,r}(h(q,r')dr')*g(r)dr)
We can exchange the order of integration in the second to get
int_(0,inf)( int_{r',inf}(g(r)dr) * h(q,r')dr')
The inner integral can be expressed in terms of the complementary
error function.
Related
I have a function represented as a narray i. e. y = f(x), where y and x are two narrays.
I am searching for a method that find the roots of f(x).
Reading the scipy documentation, I was able to find just methods that works on user defined functions, like scipy.optimize.root_scalar. I thought about using scipy.interpolate.interp1d to get an interpolated version of my function to be used in scipy.optimize.root_scalar, but I'm not sure it can work and it seems pretty complicated.
Is it there some other function that I can use instead?
You have to interpolate a function defined by numpy arrays as all the solvers require a function that can return a value for any input x, not just those in your array. But this is not complicated, here is an example
from scipy import optimize
from scipy import interpolate
# our xs and ys
xs = np.array([0,2,5])
ys = np.array([-3,-1,2])
# interpolated function
f = interpolate.interp1d(xs, ys)
sol = optimize.root_scalar(f, bracket = [xs[0],xs[-1]])
print(f'root is {sol.root}')
# check
f0 = f(sol.root)
print(f'value of function at the root: f({sol.root})={f0}')
output:
root is 3.0
value of function at the root: f(3.0)=0.0
You may also want to interpolate with higher-degree polynomials for higher accuracy of your root-finding, eg How to perform cubic spline interpolation in python?
How can I calculate mixed partial derivatives a function: ? I understand there is a SymPy way of doing this, but I would prefer numeric computation with the standard and established numeric techniques for calculating partial derivatives of any order (Abramowitz, Stegun Ch 25). scipy.misc.derivative can do it for single variable but not for the mixed ones.
import numpy as np
def f(x, y):
return (np.sin(x) + np.tanh(y))*x*y
I am trying to find the coefficients of a finite series, $f(x) = \sum_n a_nx^n$. To get the $m$th coefficient, we can take the $m$th derivative evaluated at zero. Therefore, the $m$th coefficient is
$$
a_n = \frac{1}{2\pi i } \oint_C \frac{f(z)}{z^{n+1}} dz
$$
I believe this code takes the derivative of a function using the above contour integral.
import math
import numpy
import matplotlib.pyplot as plt
def F(x):
mean=10
return math.exp(mean*(x.real-1))
def p(n):
mean=10
return (math.pow(mean, n) * math.exp(-mean)) / math.factorial(n)
def integration(func, a, n, r, n_steps):
z = r * numpy.exp(2j * numpy.pi * numpy.arange(0, 1, 1. / n_steps))
return math.factorial(n) * numpy.mean(func(a + z) / z**n)
ns = list(range(20))
f2 = numpy.vectorize(F)
plt.plot(ns,[p(n) for n in ns], label='Actual')
plt.plot(ns,[integration(f2, a=0., n=n, r=1., n_steps=100).real/math.factorial(n) for n in ns], label='Numerical derivative')
plt.legend()
However, it is clear that the numerical derivative is completely off the actual values of the coefficients of the series. What am I doing wrong?
The formulas in the Mathematics Stack Exchange answer that you're using to derive the coefficients of the power series expansion of F are based on complex analysis - coming for example from Cauchy's residue theorem (though other derivations are possible). One of the assumptions necessary to make those formulas work is that you have a holomorphic (i.e., complex differentiable) function.
Your definition of F gives a function that's not holomorphic. (For one thing, it always gives a real result for any complex input, which isn't possible for a non-constant holomorphic function.) But it's easily fixed to be holomorphic, while continuing to return the same result for real inputs.
Here's a fixed version of F, which replaces x.real with x. Since the input to exp is now complex, it's also necessary to use cmath.exp instead of math.exp to avoid a TypeError:
import cmath
def F(x):
mean=10
return cmath.exp(mean*(x-1))
After that fix for F, if I run your code I get rather surprisingly accurate results. Here's the graph that I get. (I had to print out the values to double check that that graph really did show two lines on top of one another.)
I'm trying to write an inverse lognormal function in python:
import numpy as np
import scipy.stats as sp
from scipy.optimize import curve_fit
def lognorm1(x,s,scale):
ANS = sp.lognorm(s,scale=scale).ppf(x)
return ANS
curve_fit(lognorm1,x,y)
I have no troubles fitting the curve, however the scale paramater is the exponential of what LOGNORM.INV function is on excel. I know I can just log the scale parameter at the end, but is there anyway to rewrite the function so that I don't have to do this everytime?
Indeed, SciPy documentation says
A common parametrization for a lognormal random variable Y is in terms of the mean, mu, and standard deviation, sigma, of the unique normally distributed random variable X such that exp(X) = Y. This parametrization corresponds to setting s = sigma and scale = exp(mu).
So let's set it as such:
def lognorm1(x, mu, sigma):
ANS = sp.lognorm(s=sigma, scale=exp(mu)).ppf(x)
return ANS
curve_fit(lognorm1, x, y)
Now the parameters returned by curve_fit have the meaning of the mean and standard deviation of the underlying normal distribution.
I'm trying to make some non-linear fittings with python which involve an integral, and the limits of the integral depends on the independent variable. The code is the following:
import numpy as np
import scipy as sc
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.integrate import quad
T,M=np.genfromtxt("zfc.txt", unpack=True, skiprows = 0) #here I load the data to fit
plt.plot(T,M,'o')
def arg_int1(x,sigma,Ebm):
return (1/(np.sqrt(2*np.pi)*sigma*Ebm))*np.exp(-(np.log(x/float(Ebm))**2)/(2*sigma**2))
def arg_int2(x,sigma,Ebm):
return (1/(np.sqrt(2*np.pi)*sigma*x))*np.exp(-(np.log(x/float(Ebm))**2)/(2*sigma**2))
def zfc(x,k1,k2,k3):
Temp=x*k2*27/float(k2/1.36e-16)
#Temp=k2*27/float(k2/1.36e-16) #apparently x can't be fitted with curve_fit if appears as well in the integral limits
A=sc.integrate.quad(arg_int1,0,Temp,args=(k3,k2))[0]
B=sc.integrate.quad(arg_int2,Temp,10*k2,args=(k3,k2))[0]
M=k1*(k2/1.36e-16*A/x+B)
return M
T_fit=np.linspace(1,301,301)
popt, pcov = curve_fit(zfc,T,M,p0=(0.5,2.802e-13,0.46))
M_fit=np.zeros(301)
M_fit[0]=zfc(100,0.5,2.8e-13,0.46)
for i in range (1,301):
M_fit[i-1]=zfc(i,popt[0],popt[1],popt[2])
plt.plot(T_fit,M_fit,'g')
The eror that I get is:
File "C:\Users\usuario\Anaconda\lib\site-packages\scipy\integrate\quadpack.py", line 329, in _quad
if (b != Inf and a != -Inf):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
which I don't understand, since the function is well defined. I know that the solution of my problem is the feeded parameters (i have made the fit with mathematica). I have tried to look for fitting for the Bloch-Gruneisen function (where the independent variable defines the integral limits as well) but I have not found any clue.
The problem is that scipy.optimize.curve_fit expects zfc to work on array arguments, i.e. given an n-array of x-values and 3 n-arrays of k1,k2,k3 values the zfc(x,k1,k2,k3) should return an n-array containing the corresponding values of the function. This can however easily be achieved by creating a wrapper for the function using np.vectorize:
zfc_wrapper = np.vectorize(zfc)
popt, pcov = curve_fit(zfc_wrapper,T,M,p0=(0.5,2.802e-13,0.46))
Next time it would be nice if you could provide some sample input data. I managed to run it with test data from some arbitrary function, but this may not always be the case.
Cheers.