I want to integrate a function that takes array arguments using an efficient (vectorised/parallelised) method.
I can get the desired output using unwanted loops in the code as in the following (reduced complexity) example:
import numpy as np
from scipy import integrate
c = np.array([1, 2])
r = np.array([2, 1])
def fun(p, c):
p1 = np.array(np.zeros(c.shape))
mask = np.array(np.sqrt(np.pi/(2*c)) < 1)
p1[mask] = np.arccos(np.sqrt(np.pi/(2*c[mask])))
d = np.zeros(c.shape)
mask = np.abs(p) <= p1
d[mask] = 1/(np.pi/(2*c[mask]**2) + np.cos(p))
mask = np.logical_and(np.abs(p) > p1, np.abs(p) <= np.pi/2)
d[mask] = 1/(np.pi/(2*c[mask]**2) +
((np.cos(p1[mask]) - np.cos(p))/2))
return(d)
def intgd(p, r, c):
A = np.ones((np.size(r), np.size(c)))
s = np.sin(r) - np.sin(p)
A[s != 0] = np.sin(c[s != 0]*s[s != 0])/(c[s != 0]*s[s != 0])
return 1/fun(p, c)**2*(A**2)
res = np.zeros((np.size(r), np.size(c)))
for ii in range(0, np.size(r)):
for jj in range(0, np.size(c)):
res[ii, jj], err = integrate.quad(intgd, -np.pi/2, np.pi/2,
epsabs=1e-10, limit=100,
args=(r[ii], c[jj]))
However, my real function needs to handle much larger array inputs, which leads to unacceptably lengthy calculation duration.
I have tried variations along the following, and gained the knowledge that (as noted in comments on this question), the vec_func=True option for scipy.integrate.quadrature does not in fact enable vector-valued arguments to be passed as parameters into the function being integrated. [Aside: this makes it quite different to the MATLAB integral function, for which the option ArrayValued, true does enable that functionality, which results in much faster, apparently parallelised, evaluation of the integral.]
import numpy as np
from scipy import integrate
c = np.array([1, 2], ndmin=2)
r = np.array([2, 1])
r = r[:, np.newaxis]
def fun(p, c):
p1 = np.zeros(c.shape)
mask = np.array(np.sqrt(np.pi/(2*c)) < 1, ndmin=2)
p1[mask] = np.arccos(np.sqrt(np.pi/(2*c[mask])))
d = np.zeros(c.shape)
mask = np.abs(p) <= p1
d[mask] = 1/(np.pi/(2*c[mask]**2) + np.cos(p))
mask = np.logical_and(np.abs(p) > p1, np.abs(p) <= np.pi/2)
d[mask] = 1/(np.pi/(2*c[mask]**2) +
((np.cos(p1[mask]) - np.cos(p))/2))
return(d)
def intgd(p, r, c):
A = np.ones((np.size(r), np.size(c)))
c_bcr = np.broadcast_to(c, (np.size(r), np.size(c)))
r_bcc = np.broadcast_to(r, (np.size(r), np.size(c)))
s = np.sin(r_bcc) - np.sin(p)
A[s != 0] = np.sin(c_bcr[s != 0]*s[s != 0])/(c_bcr[s != 0]*s[s != 0])
return 1/fun(p, c)**2*(A**2)
res, err = integrate.quadrature(intgd, -np.pi/2, np.pi/2,
args=(r, c), tol=1e-10, vec_func=True)
How can I use Scipy to integrate array-argument functions without resorting to loops?
quadratureright, it integrates a scalar valued function.vec_func=Truemeans it can handle multiple values of the integration variable at once, returning a value for each. But it is still scalar integration, not multidimensional.nquadis for multidimensional integration.randc(broadcasted or not)?solve_ivp, stackoverflow.com/questions/54991056/….scipydoes not have all the wiz-bang features that MATLAB provides.