Parfor issues with population generation - matlab

I'm running a genetic algorithm and i'm trying to parallelize the population generation.
My actual code:
Q = [];
parfor i=1:halfPop
pa = P(select(f),:);
pb = P(select(f),:);
...
Q = [Q; pa; pb];
end
Matlab gives me the error The temporary variable Q in a parfor is uninitialized.
So i rewrote it like this:
Q = [];
parfor i=1:halfPop
pa = P(select(f),:);
pb = P(select(f),:);
Q(i,:) pa;
Q(i+halfPop,:) pb;
end
But now i get error The variable Q in a parfor cannot be classified.. In the editor MATLAB tells me i cannot index in two differnt ways the same matrix inside a parfor.
What could i do?

parfor tries to split Q into as many slices as there are iterations, so each iteration gets one slice of Q. In your case, each iteration should get two slices, which parfor cannot handle (yet).
To fix this, you can define two variables, Qa and Qb, which you can combine after the end of the parfor loop.
[Qa,Qb] = deal(zeros(halfPop,size(P,2)));
parfor i=1:halfPop
pa = P(select(f),:);
pb = P(select(f),:);
Qa(i,:) = pa;
Qb(i,:) = pb;
end
Q = zeros(2*halfPop,size(P,2));
Q(1:2:end,:) = Qa;
Q(2:2:end,:) = Qb;

The " concatenation reduction" you're attempting should work if you make the concatenation with a single operand like so
tmp = [pa; pb];
Q = [Q; tmp];

Many different option depending on whther pa and pb are consistently the same size. Matlab wants to make sure there is no risk of data "overwrite" (for lack of a better term), and it is unable to ascertain that in this case. Essentially, you can only write to the matrix once per iteration, and has be done within what it believes are "good practices".
My solution:
Q = zeros(halfPop,size([pa pb]);
parfor i=1:halfPop
pa = P(select(f),:);
pb = P(select(f),:);
Q(i,:)= [pa pb];
end

Related

How do I properly "slice" a 4D matrix in Matlab in a parfor loop?

I am trying to make a portion of my code run faster in MatLab, and I'd like to use parfor. When I try to, I get the following error about one of my variables D_all.
"The PARFOR loop cannot run because of the way D_all is used".
Here is a sample of my code.
M = 161;
N = 24;
P = 161;
parfor n=1:M*N*P
[j,i,k] = ind2sub([N,M,P],n);
r0 = Rw(n,1:3);
R0 = repmat(r0,M*N*P,1);
delta = sqrt(dXnd(i)^2 + dZnd(k)^2);
d = R_prime - R0;
inS = Rw_prime(find(sqrt(sum(d.^2,2))<0.8*delta),:);
if isempty(inS)
D_all(j,i,k,tj) = D_all(j,i,k,tj-1);
else
y0 = r0(2);
inC = inS(find(inS(:,2)==y0),:);
dw = sqrt(sum(d(find(sqrt(sum(d.^2,2))<0.8*delta & d(:,2)==0),:).^2,2));
V_avg = sum(dw.^(-1).*inC(:,4))/sum(dw.^(-1));
D_all(j,i,k,tj) = V_avg;
end
end
I'm not very familiar with parallel computing, and I've looked at the guides online and don't really understand how to apply them to my situation. I guess I need to "slice" D_all but I don't know how to do that.
EDIT: I think I understand that the major problem is that when using D_all I have tj and tj-1.
EDIT 2: I don't show this above, it probably would have been helpful, but I defined D_all(:,:,:,1) = V_1; where V_1 corresponds to a previous time step. I tried making multiple variables V_2, V_3, etc. for each step and replacing D_all(j,i,k,tj-1) with V_1(j,i,k). This still led to the same error I am seeing with D_all.
"Valid indices for D_all are restricted for PARFOR loops"

"Not enough input arguments" when trying to implement simple FOR loop using GPU

I'm trying to calculate the probability that a point (a nxn matrix) uniformly distributed in R^(n^2) exclusively has eigenvalues with negative real part, but I keep getting the following error:
Not enough input arguments.
Error in probability_n (line 4)
for i = 1:num_pts
Here is my code:
N = 10^2;
num_pts = 10^4;
n = 2;
n = n*ones(N,1,'gpuArray');
k = arrayfun(probability_n,n,num_pts);
and the function called is
function k = probability_n(n,num_pts)
k = 0;
for i = 1:num_pts
R = reshape(randsphere(1,n^2,1),n,n);
if all(real(eig(R))<0)
k = k+1;
end
end
end
function P = randsphere(m,n,r)
P = randn(m,n);
s2 = sum(P.^2,2);
P = P.*repmat(r*(gammainc(s2/2,n/2).^(1/n))./sqrt(s2),1,n);
end
Why is this happening? I suspect it is something very simple to do with a syntax error, since this is my first time trying to use my GPU for MATLAB. The GPU is an Nvidia GeForce GTX 580. Thanks.
In general, it's best to test things in vanilla MATLAB (without GPU or parallel processing) if you experience issues to see if the issue is specific to the GPU or parallel processing or whether it's something else. If you do that, you'll see that your code still doesn't work.
This is because you need to pass a function handle for probability_n to arrayfun, as you have it written, probability_n is implicitly called with no input arguments (you don't need the () to invoke a function). You receive the error you do when MATLAB tries to access num_pts from within probability_n and it hasn't been provided.
k = arrayfun(#probability_n, n, num_pts);
Note that passing the scalar num_pts as the third input only works when the first input to arrayfun is a gpuarray object. Otherwise, you'll want to create an anonymous function which passes num_pts to probability_n

Sweeping initial conditions for a set of ODEs using parfor

I am currently trying to use parfor to sweep across a range of initial conditions for a set of differential equations solved by ode45. The code works fine using two nested for loops but I was hoping parfor could make the process more efficient. Unfortunately, I have run into an issue where the solver is able to solve one of the combinations in the matrix representing initial conditions across a range of variables, but the others seem to have their initial values all set at 0, instead of the values specified by the initial conditions. It may have something to do with the fact that I need to create a matrix of zeros ('P') that the results will be written into, perhaps overwriting the initial conditions(?) Any help would be greatly appreciated.
Thanks,
Kyle
function help(C, R)
A = 0.01;
B = 0.00;
C = [0.001,0.01];
D = 0.00;
R = [1e-10,1e-9];
[CGrid,RGrid] = meshgrid(C,R);
parfor ij = 1 : numel(CGrid)
c2 = [A; B; CGrid(ij); D; RGrid(ij)];
[t,c] = ode45('ode_sys',[0:1:300],c2);
for k=1:length([0:1:300])
for l=1:length(c2)
if c(k,l)<0
c(k,l)=0;
end
end
end
P = zeros(301,5,numel(R),numel(C));
temp = zeros(301,5);
temp(:,1) = c(:,1);
temp(:,2) = c(:,2);
temp(:,3) = c(:,3);
temp(:,4) = c(:,4);
temp(:,5) = c(:,5);
P(:,:,ij)=temp;
parsave('data.mat', P);
end
end
You have one error, and a few opportunities to simplify the code.
In the parfor loop, you have this line P = zeros(301,5,numel(R),numel(C)); which overwrites P with all zeros at each iteration. Put this before the parfor loop.
The first double-for loop that makes negative elements of c zero can be done using max(c,0), which should be more efficient. You can also do P(:,:,ij)=c(:,1:5) directly.
So you can replace your parfor loop with
P = zeros(301,5,numel(R),numel(C));
for ij = 1 : numel(CGrid)
c2 = [A; B; CGrid(ij); D; RGrid(ij)];
[t,c] = ode45('ode_sys',0:300,c2);
c = max(c,0);
P(:,:,ij) = c(:,1:5);
parsave('data.mat',P);
end

parfor loop has wrong sliced variables

Can anyone explain to me, why the following gives an error for u but nor for h
max_X = 100;
max_Y = 100;
h = ones(max_Y,max_X);
u = zeros(max_Y,max_X);
parfor l=1:max_X*max_Y
i = mod(l-1,max_X) + 1;
j = floor((l-1)/max_Y) + 1;
for k=1:9
m = i + floor((k-1)/3) - 1;
n = j + mod(k,-3) + 1;
h_average(k) = sqrt(h(i,j)*h(m,n));
u_average(k) = (u(i,j)*sqrt(h(i,j)) + u(m,n)*sqrt(h(m,n)))/(sqrt(h(i,j)) + sqrt(h(m,n)));
end
end
I can now substitute (i,j) with (l), but even if I try to calculate the related variable, let's call it p, according to (m,n), and write u(p) instead of u(m,n) it gives me an error message.
It only underlines the u(m,n), resp. u(p) but not the h(m,n).
MATLAB says:
Explanation:
For MATLAB to execute parfor loops efficiently, the amount of data sent to the MATLAB workers must be minimal. One of the ways MATLAB achieves this is by restricting the way variables can be indexed in parfor iterations. The indicated variable is indexed in a way that is incompatible with parfor.
Suggested Action
Fix the indexing. For a description of the indexing restrictions, see “Sliced Variables” in the Parallel Computing Toolbox documentation
Any idea, what's wrong here?
The problems with u and h are that they are both being sent as broadcast variables to the PARFOR loop. This is not an error - it's just a warning indicating that more data than might otherwise be necessary is being sent.
The PARFOR loop cannot run because you're indexing but not slicing u_average and h_average. It's not clear what outputs you want from this loop since you're overwriting u_average and h_average each time, therefore the PARFOR loop is pointless.

Avoiding for-loops in Matlab

I have a function as follows:
My optimized matlab code is:
function gamma = calcGamma(H, d, delta, f)
s= size(H);
Nv = s(1);
Ne = s(2);
gamma = zeros(Ne,1);
for e =1:Ne
hue = H(:,e);
sdu=f./sqrt(d);
mHUE = repmat(hue',Nv,1);
mHVE = repmat(hue,1,Nv);
mSDU = repmat(sdu',Nv,1);
mSd = repmat(sdu,1,Nv);
ss1 = mHUE .* mHVE/delta(e) .* (mSDU-mSd).^2;
gamma(e) = sum(ss1(:));
end
However, since Ne is very big, it takes quite a long time to calculate the function.
I see an similar question with a good solution, but I do not understand how it is derived.
Any solution to avoid the for-loop? Thanks
I am not sure if this will be faster, but instead of your trick with repmat, you can use bsxfun. This trick is pretty similar to the array broadcasting that is standard in numpy and saves the creation of the large intermediate matrices like mHUE.
Another thing you should always do is to move everything out of the loop that does not depend on e. It seems to me that the calculation of sdu is totally constant, so do that once before the loop. And I guess that delta(e) is a scalar, so instead of dividing the big matrices by that, do it once afterwards:
s= size(H);
Nv = s(1);
Ne = s(2);
gamma = zeros(Ne,1);
sdu = f./sqrt(d);
M = bsxfun(#minus, sdu', sdu).^2;
for e =1:Ne
hue = H(:,e);
ss1 = bsxfun(#times, hue', hue) .* M;
gamma(e) = sum(ss1(:));
end
gamma = gamma ./ delta;
It might be possible to remove the for-loop, but it is doubtful that that will increase speed, since you have just a single loop and your calculation time is anyhow dominated by the multiplication of the big matrices. Moreover, removing the for-loop will just make your function very hard to understand, so just leave it as is.