I find myself computing an integral (using integrate_1d) of the same function with several different upper limits. I would like to speed this process up.
To be concrete, here is a code snippet:
for (i in 1:n_trial) {
log_integ[i] = log(integrate_1d(integrand, lb, t_stim[i], {mu_antic, sig_antic}, {0.0}, {0}));
}
Here, lb and t_stim are data, and mu_antic and sig_antic are scalar parameters. In case it matters, the integrand function is a simple wrapper around normal_cdf().
Intuitively, it seems like I ought to be able to save on computation somehow, but I’m not familiar enough with the iterative quadrature algorithm to know for sure.
Initially, I was thinking I could sort t_stim and do something like trapezoid integration using the points of t_stim plus any additional points to ensure some maximum distance between evaluation points. But that feels very unsophisticated compared to what integrate_1d is doing.
I suppose I could use the built-in integrator, but instead of the current approach, I could just compute the integral between subsequent pairs of t_stim points. This would mean calling integrate_1d just as many times, but each call might be faster because it’s over a smaller range of x values (so a lower resolution approximation would still be good).
Update: I did a quick test of this sorting approach, and it cut runtime in half. So that’s a pretty good win. I’d still be happy to know if anyone has suggestions for other approaches to try.
The code I used to test the relative speed
functions {
real integrand(real x, real xc, real[] theta, real[] x_r, int[] x_i) {
return normal_cdf(x, theta[1], theta[2]);
}
}
data {
int<lower=0> N;
vector<lower=0>[N] x;
real mu;
real<lower=0> sigma;
}
parameters {}
model {}
generated quantities {
vector[N] method_one;
vector[N] method_two;
// Approach 1, the straightforward way.
profile ("Method 1") {
for (i in 1:N) {
method_one[i] = log(integrate_1d(integrand, 0, x[i], {mu, sigma}, {0.0}, {0}));
}
}
// Approach 2, sort then increment
profile ("Method 2") {
array[N] int x_idx = sort_indices_asc(x);
vector[N] sx = x[x_idx];
vector[N] sorted_m2;
for (i in 1:N) {
real lb;
real ub = sx[i];
real increment;
real prev_val;
if (i==1) {
lb = 0;
prev_val = 0;
} else {
lb = sx[i-1];
prev_val = sorted_m2[i-1];
}
increment = integrate_1d(integrand, lb, ub, {mu, sigma}, {0.0}, {0});
sorted_m2[i] = prev_val + increment;
}
method_two[x_idx] = log(sorted_m2);
}
}