I'm trying to efficiently evaluate multiple instances of a python function that takes in 4 arguments, but I can't find how to map the function to several input values in a way that doesn't involve native python's slow for loops.
Here is an example of where I call the function just once (i.e. for one observation).
from scipy.stats.mvn import mvnun
lower_bounds_one_obs = np.full(5, -1)
upper_bounds_one_obs = np.full(5, 1)
cov_mtx = np.eye(5)
means = np.zeros(5)
single_result = mvnun(lower_bounds_one_obs, upper_bounds_one_obs, means, cov_mtx)[0]
print(single_result)
For the sake of context, the mvnun
function calculates the rectangular CDF of a multivariate normal distribution between lower and upper bounds. In this specific case, the lower and upper bounds have 5 elements in them.
What I want to do is to calculate mvnun
for an array of lower and upper bounds, but always using the same means
and cov_mtx
.
In other words, I want to have two main input arrays, lower_bounds
and lower_bounds
, both with shape (1000 x 5). Then I would go through the process 1000 times, calculating mvnun
for lower_bounds[0]
and upper_bounds[0]
, then lower_bounds[1]
and upper_bounds[1]
, and so on and so forth until lower_bounds[999]
and upper_bounds[999]
.
In scipy's documentation, I found that I could vectorize/map this function, but only for the upper bounds, making the lower bound fixed. This can be done like so:
lower_fixed = np.full(5, -np.inf)
upper_bounds = np.random.rand(5 * 1000).reshape((1000, 5))
func1d = lambda upper_slice: mvnun(lower_fixed, upper_slice, means, cov_mtx)[0]
out = np.apply_along_axis(func1d, -1, upper_bounds)
This way, mvnun
is evaluated multiple times with a fixed lower bound and iterating over all 1000 rows of upper_bound
.
Does anyone know how to adjust the snippet of code above so that I can use an array of lower bounds instead of fixed lower bounds?
Thanks!
PS: This question is a bit of an update on this. There are also some interesting insights here too, but the solutions they present are pretty slow because they use regular python loops. More ideas here too.