I have a "super-nested" loop in Matlab to create an array gamma. The loop takes a lot of time to execute. I would like your help to vectorise the loop.
This is the code with explanations:
rng default
clear
%%%%%%%%%%%%%%%%%Parameters
L=2;
K=3;
n_draws=10^6;
mu_V=zeros(1, L 1);
Sigma_V=eye(L 1);
v_draws=mvnrnd(mu_V,Sigma_V,n_draws); %n_drawsx(L 1)
payoff=randn(n_draws, L 1); %n_drawsx(L 1)
v_upp=5;
v_low=-5;
%%%%%%%%%%%%%%%%Fill the array gamma
gamma=zeros(K 1,K 1,K 1,L 1,L 1); %allocate space for the array gamma
for k1=1:K 1
for k2=1:K 1
for k3=1:K 1
for y=1:L 1
for y1=1:L 1
tic
integr=((nchoosek(K, (k1-1))*(((v_draws(:,1)-v_low).^(k1-1).*(v_upp-v_draws(:,1)).^(K-(k1-1)))./((v_upp-v_low).^K))).*...
(nchoosek(K, (k2-1))*(((v_draws(:,2)-v_low).^(k2-1).*(v_upp-v_draws(:,2)).^(K-(k2-1)))./((v_upp-v_low).^K))).*...
(nchoosek(K, (k3-1))*(((v_draws(:,3)-v_low).^(k3-1).*(v_upp-v_draws(:,3)).^(K-(k3-1)))./((v_upp-v_low).^K)))).*...
mvnpdf(v_draws,mu_V,Sigma_V).*...
(payoff(:,y)-payoff(:,y1)); %n_drawsx1
gamma(k1,k2,k3,y,y1)=sum(integr)/n_draws; %average of elements of integr
toc
end
end
end
end
end
For example, with K=3, it takes around 20 sec to execute. In my actual exercise, K=50 and it takes forever.
CodePudding user response:
You can save ~95% runtime from my testing by applying two main types of improvement
- Compute things in the outermost loop possible, so that equivalent terms are not computed multiple times.
- Vectorise the inner
y1loop to remove it
All of the deltas between v_upp, v_low, and v_draws are constant because these vectors do not change in the loops, so can all be computed outside of the loops. So can the mvnpdf.
The code then looks something like this:
gamma=zeros(K 1,K 1,K 1,L 1,L 1); %allocate space for the array gamma
v_delta = v_upp-v_low;
v_delta_upp1 = v_upp-v_draws(:,1);
v_delta_upp2 = v_upp-v_draws(:,2);
v_delta_upp3 = v_upp-v_draws(:,3);
v_delta_low1 = v_draws(:,1)-v_low;
v_delta_low2 = v_draws(:,2)-v_low;
v_delta_low3 = v_draws(:,3)-v_low;
MVN = mvnpdf(v_draws,mu_V,Sigma_V);
y1=1:L 1;
for k1=1:K 1
term1 = (nchoosek(K, (k1-1))*((v_delta_low1.^(k1-1).*v_delta_upp1.^(K-(k1-1)))./(v_delta.^K)));
for k2=1:K 1
term2 = (nchoosek(K, (k2-1))*((v_delta_low2.^(k2-1).*v_delta_upp2.^(K-(k2-1)))./(v_delta.^K)));
for k3=1:K 1
term3 = (nchoosek(K, (k3-1))*((v_delta_low3.^(k3-1).*v_delta_upp3.^(K-(k3-1)))./(v_delta.^K)));
integr = term1 .* term2 .* term3 .* MVN;
for y=1:L 1
integrp = integr .* (payoff(:,y)-payoff(:,y1)); %n_drawsx1
gamma(k1,k2,k3,y,y1)=sum(integrp)/n_draws; %average of elements of integr
end
end
end
end
You could further improve this by noticing things like
- You're calculating
nchoosekof1:K 1inside every loop, it might be quicker to calculate an array of thenchoosekvalues up front and just index into it when looping. - You're dividing by
n_drawsand multiplying by themvnpdfterm every loop iteration, is it quicker to just do these after the loop?
You'll get diminishing returns looking at things like this though.
