Hello,
I’m having a bit of trouble speeding up my code.
For my model I need the peak output of a 5th order leaky integrator to a series of square pulses.
To get this I’m computing some step responses in the function ‘LPF_SinglePulse’. The expression looks long, since its the analytic solution. I initially used FFT in StanCMD, but computation was slow.
In a separate function named ‘Leaky_Output’ I obtain the maximum absolute value, which is then used to model each response (‘S_par’ in model section).
This parameters is important in my model since it determines an integration time constant for different stimuli.
The problem is that, even though I avoided convolution and switched to multiplication, running this takes a lot of time for a single subject.
Sampling from the priors is fast, but as soon as I include the calculation of ‘S_par’, the whole code runs very slow.
It is worth mentioning that each pulse (‘y1’,‘y2’, … in ‘LPF_SinglePulse’) has 2000 samples (this is the parameter ‘nsamples’).
The number of responses (‘y’) per subject is 440 (this is ‘Ntotal’=440)
I’m wondering if there’s a way to speed up these type of calculations.
functions{
vector LPF_SinglePulse(vector Step1,vector Step2,vector Step3,vector Step4,vector Step5,vector Step6,vector Step7,vector Step8, real t1, real t2, real t3, real t4, real t5, real t6, real t7, real t8, real tau, vector t, int nsamples) {
// This Function compues the response of a 5th order Leaky integrator to a pulse
vector [nsamples] y1;
vector [nsamples] y2;
vector [nsamples] y3;
vector [nsamples] y4;
vector [nsamples] y5;
vector [nsamples] y6;
vector [nsamples] y7;
vector [nsamples] y8;
// Initial implementation using a for loop in RStan, switched to StanCMD
//for(k in 1:nsamples){
//y1[k] = Step1[k]*( (1.0) - (exp(-(t[k]-t1)/tau)*(1/(tgamma(1))*(pow((t[k]-t1)/tau,0)) + 1/(tgamma(2))*(pow((t[k]-t1)/tau,1)) + 1/(tgamma(3))*(pow((t[k]-t1)/tau,2)) + 1/(tgamma(4))*(pow((t[k]-t1)/tau,3)) + 1/(tgamma(5))*(pow((t[k]-t1)/tau,4)))));
//y2[k] = Step2[k]*( (1.0) - (exp(-(t[k]-t2)/tau)*(1/(tgamma(1))*(pow((t[k]-t2)/tau,0)) + 1/(tgamma(2))*(pow((t[k]-t2)/tau,1)) + 1/(tgamma(3))*(pow((t[k]-t2)/tau,2)) + 1/(tgamma(4))*(pow((t[k]-t2)/tau,3)) + 1/(tgamma(5))*(pow((t[k]-t2)/tau,4)))));
// }
y1 = Step1.*( (1.0) - (exp(-(t-t1)/tau).*(1/(tgamma(1)).*(pow((t-t1)/tau,0)) + 1/(tgamma(2)).*(pow((t-t1)/tau,1)) + 1/(tgamma(3)).*(pow((t-t1)/tau,2)) + 1/(tgamma(4)).*(pow((t-t1)/tau,3)) + 1/(tgamma(5)).*(pow((t-t1)/tau,4)))));
y2 = Step2.*( (1.0) - (exp(-(t-t2)/tau).*(1/(tgamma(1)).*(pow((t-t2)/tau,0)) + 1/(tgamma(2)).*(pow((t-t2)/tau,1)) + 1/(tgamma(3)).*(pow((t-t2)/tau,2)) + 1/(tgamma(4)).*(pow((t-t2)/tau,3)) + 1/(tgamma(5)).*(pow((t-t2)/tau,4)))));
y3 = Step3.*( (1.0) - (exp(-(t-t3)/tau).*(1/(tgamma(1)).*(pow((t-t3)/tau,0)) + 1/(tgamma(2)).*(pow((t-t3)/tau,1)) + 1/(tgamma(3)).*(pow((t-t3)/tau,2)) + 1/(tgamma(4)).*(pow((t-t3)/tau,3)) + 1/(tgamma(5)).*(pow((t-t3)/tau,4)))));
y4 = Step4.*( (1.0) - (exp(-(t-t4)/tau).*(1/(tgamma(1)).*(pow((t-t4)/tau,0)) + 1/(tgamma(2)).*(pow((t-t4)/tau,1)) + 1/(tgamma(3)).*(pow((t-t4)/tau,2)) + 1/(tgamma(4)).*(pow((t-t4)/tau,3)) + 1/(tgamma(5)).*(pow((t-t4)/tau,4)))));
y5 = Step5.*( (1.0) - (exp(-(t-t5)/tau).*(1/(tgamma(1)).*(pow((t-t5)/tau,0)) + 1/(tgamma(2)).*(pow((t-t5)/tau,1)) + 1/(tgamma(3)).*(pow((t-t5)/tau,2)) + 1/(tgamma(4)).*(pow((t-t5)/tau,3)) + 1/(tgamma(5)).*(pow((t-t5)/tau,4)))));
y6 = Step6.*( (1.0) - (exp(-(t-t6)/tau).*(1/(tgamma(1)).*(pow((t-t6)/tau,0)) + 1/(tgamma(2)).*(pow((t-t6)/tau,1)) + 1/(tgamma(3)).*(pow((t-t6)/tau,2)) + 1/(tgamma(4)).*(pow((t-t6)/tau,3)) + 1/(tgamma(5)).*(pow((t-t6)/tau,4)))));
y7 = Step7.*( (1.0) - (exp(-(t-t7)/tau).*(1/(tgamma(1)).*(pow((t-t7)/tau,0)) + 1/(tgamma(2)).*(pow((t-t7)/tau,1)) + 1/(tgamma(3)).*(pow((t-t7)/tau,2)) + 1/(tgamma(4)).*(pow((t-t7)/tau,3)) + 1/(tgamma(5)).*(pow((t-t7)/tau,4)))));
y8 = Step8.*( (1.0) - (exp(-(t-t8)/tau).*(1/(tgamma(1)).*(pow((t-t8)/tau,0)) + 1/(tgamma(2)).*(pow((t-t8)/tau,1)) + 1/(tgamma(3)).*(pow((t-t8)/tau,2)) + 1/(tgamma(4)).*(pow((t-t8)/tau,3)) + 1/(tgamma(5)).*(pow((t-t8)/tau,4)))));
return( (y1-y2) + (y3-y4) + (y5-y6) + (y7-y8));
}
real Leaky_Output(vector Step1,vector Step2, vector Step3,vector Step4, vector Step5,vector Step6, vector Step7,vector Step8, real t1, real t2, real t3, real t4, real t5, real t6, real t7, real t8, real tauA, real tauC, real polarity, vector t, int nsamples) {
// This Function computes the peak output of a 5th order Leaky integrator to a pulse
real S;
real tau;
vector[nsamples] P;
// Determine time constant to be used (-1 or 1 is the input to polarity)
tau = tauA * ((1+polarity)/2) + tauC * ((polarity-1)/(-2));
// Compute Step response per pulse
P = LPF_SinglePulse( Step1, Step2, Step3 , Step4 ,Step5, Step6, Step7 , Step8 ,t1, t2, t3, t4, t5, t6, t7, t8, tau, t, nsamples);
// Get the maximum value
S = max(abs(P));
return(S);
}
} // e.o. functions
data {
int<lower=1> Nsubj ; // subjects
int<lower=1> Ntotal ; // Number of measurements
int<lower=1> nsamples ; // Number of samples in time vector
real<lower=0> y[Ntotal] ; // Response vector
int<lower=1> subj[Ntotal] ; // subject vector
real polarity[Ntotal] ; // polarity vector (either 1 or -1)
real Amp[Ntotal] ; // Stimulus amplitude vector
int<lower=1> nipi[Ntotal]; // sample inter-pulse interval, per stimulus
vector[nsamples] t ; // time vector
matrix[Ntotal,nsamples] p1; // Matrix with stimulus data for pulse 1
matrix[Ntotal,nsamples] p2; // Matrix with stimulus data for pulse 2
matrix[Ntotal,nsamples] p3; // Matrix with stimulus data for pulse 3
matrix[Ntotal,nsamples] p4; // Matrix with stimulus data for pulse 4
matrix[Ntotal,nsamples] p5; // Matrix with stimulus data for pulse 5
matrix[Ntotal,nsamples] p6; // Matrix with stimulus data for pulse 6
matrix[Ntotal,nsamples] p7; // Matrix with stimulus data for pulse 7
matrix[Ntotal,nsamples] p8; // Matrix with stimulus data for pulse 8
matrix[Ntotal,8] t0; // Matrix with time onsets for each pulse
} // e.o. data
parameters {
// Subject dependent parameters
real<lower=1> tauA[Nsubj]; // Time constant for polarity A
real<lower=1> tauC[Nsubj]; // Time constant for polarity C
real<lower=0.001> A[Nsubj]; // Parameter A (ceiling for y)
real<lower=0.001> theta[Nsubj]; // Parameter theta (half-width promptness)
real<lower=0.001> omega[Nsubj]; // Parameter omega (width)
real<lower=0.001> sigma[Nsubj]; // Parameter sigma (std of promptness)
} // e.o. parameters
model{
// loop for all stimuli presented (and responses)
for ( i in 1:Ntotal ) {
// Time constants follow uniform distributions here just for testing
tauA[subj[i]] ~ uniform(1, 100);
tauC[subj[i]] ~ uniform(1, 100);
// Priors for omega, theta, A defined as Gamma functions for testing
omega[subj[i]] ~ gamma(1, 0.001);
theta[subj[i]] ~ gamma(1, 0.001);
A[subj[i]] ~ gamma(1, 0.001);
sigma[subj[i]] ~ gamma(1, 0.001);
// We compute the peak output of the stimulus passing through a filter, this affects our response
// S_par takes too much time to compute!!!!
real S_par = Leaky_Output(p1[i]', p2[i]', p3[i]', p4[i]', p5[i]', p6[i]', p7[i]', p8[i]', t0[i,1], t0[i,2], t0[i,3], t0[i,4], t0[i,5], t0[i,6], t0[i,7], t0[i,8], tauA[subj[i]], tauC[subj[i]], polarity[i], t, nsamples);
// Response: y ~ normal(mu, sigma);
//y[i] ~ normal( A[subj[i]] / ( 1 + exp( ( (-2*log(9))/omega[subj[i]] ) * (S_par - theta[subj[i]]) ) ) + 0.25 , sigma[subj[i]] );
}
} // e.o. model
Thanks for the help!
Hope I was clear enough and sorry for the sloppiness in my code, I’m taking my first steps in STAN.
Kind regards,
Ignacio