1

The following code works, but if I change for into parfor, it gave an error

Index exceeds matrix dimensions

This is my code

a=zeros(3,1);
for t=1:2
  ind=randsample(3,2)
  a=pf(a,ind)
end

function a=pf(a,ind)
 a(ind)=a(ind)+2;
end

How can I get this code working without the error?

Adriaan
  • 17,741
  • 7
  • 42
  • 75
Alicia
  • 37
  • 7
  • What do you expect the `parfor` to do with your loop? And please don't answer "speed it up". – Andras Deak -- Слава Україні Jun 25 '17 at 18:53
  • Hi, this is the simplified code. The real code has a large matrix and I want to use parfor to speed it up. – Alicia Jun 25 '17 at 18:54
  • OK, I checked and I can reproduce your error (deleted my previous comment). The problem is probably that the parfor in this context is meaningless. Do you understand what parfor does? – Andras Deak -- Слава Україні Jun 25 '17 at 18:59
  • I need 'parfor' for a large matrix in the real code. Parfor lets several workers do the work simultaneously – Alicia Jun 25 '17 at 19:00
  • 2
    OK, so what is "this work"? One typically uses `parfor` to compute independent slices of a large array. Like a Fourier transform. In these cases each loop iteration is independent. In your case, every loop iteration is doing something to the whole array (incrementing it, to be specific). If the operation was something more complicated and not just increments, even the order of iterations would matter! This is clearly not something `parfor` can be used for, out of the box. You need to rewrite it so that it makes sense: for instance by adding a new dimension and collecting increments in the end. – Andras Deak -- Слава Україні Jun 25 '17 at 19:03
  • 1
    Please read [this answer of mine](https://stackoverflow.com/a/32146700/5211833) on what `parfor` actually does and which operations it can and cannot speed up. – Adriaan Jun 25 '17 at 19:30

2 Answers2

2

You are seeing the error because you are misusing parfor in your code. You haven't read the relevant documentation enough, and you seem to believe that parfor is magic fairy dust that makes your computation faster, regardless of computation. Well, I have bad news.

Let's take a closer look at your example:

a = zeros(3,1);
% usual for
disp('before for')
for t=1:2
  ind = randsample(3,2);
  a = pf(a,ind);
  disp(a); % add printing line
end

% parfor
disp('before parfor')
parfor t=1:2
  ind = randsample(3,2);
  a = pf(a,ind);
  disp(a); % add printing line
end

The output:

before for
     2
     2
     0

     2
     4
     2

before parfor
Error: The variable a is perhaps intended as a reduction variable, but is actually an uninitialized temporary.
See Parallel for Loops in MATLAB, "Temporary Variables Intended as Reduction Variables".

As you can see, in the latter case there are no prints inside the parfor, so it doesn't even get run. See also the warning about the type of variables. The variable a is being misidentified by the execution engine because what you are doing to it doesn't make any sense.

So what to do instead? You need to formulate your problem in a way that is compatible with parfor. This will, alas, depend on what exactly you're doing to your matrix. For your specific case of incrementing random elements, I suggest that you gather the increments separately in the loop, and sum them up afterwards:

a = zeros(3,1); % only needed for size; assumed that it exists already
numiters = 2;
increments = zeros([size(a), numiters]); % compatible with a proper 2d array too
parfor t=1:numiters
  ind = randsample(3,2);
  % create an auxiliary increment array so that we can use a full slice of 'increments'
  new_contrib = zeros(size(a));
  new_contrib(ind) = 2;
  increments(:,t) = new_contrib;
  disp(increments(:,t)); % add printing line
end

% collect increments along last axis
a = sum(increments,ndims(increments));
disp(a)

Output:

     2
     0
     2

     2
     2
     0


     4
     2
     2

Note the lack of warnings and the presence of a meaningful answer. Refactoring the loop this way transparently signals MATLAB what the variables are doing, and that increments is being filled up by independent iterations of the parfor loop. This is the way in which parfor can "speed up calculations", a very specific and controlled way that implies restrictions on the logistics used inside the loop.

  • 1
    Thank you for your help. Very useful.I made a little simplification to your code. Instead of using 'increments', I add 'new_contrib' directly to 'a' in each loop: a=a+new_contrib. I have to do this because it's a large matrix in the real code. – Alicia Jun 25 '17 at 22:02
  • @ChuchuXiang I don't expect that to work, but if it does, then it's all good. Thanks for the feedback. – Andras Deak -- Слава Україні Jun 25 '17 at 22:03
  • 1
    I tried that. It works and gave the same answer. You just need to change 'new_contrib = zeros(size(a))' into constant size :'new_contrib = zeros(3,1)'. Usually in 'parfor' the loop iterations must be independent, but the only exception is reduction (accumulates a value). Thank you – Alicia Jun 25 '17 at 22:13
1
n = 2;
a=zeros(3,1);
ind=zeros(3,2,n);
for ii = 1:n
    ind(:,:,ii) = randsample(3,2);
end

for t=1:n
    a=pf(a,ind(:,:,t));
end

function a=pf(a,ind)
    a(ind)=a(ind)+2;
end

The above gets the randsample out of the loop, which is probably the issue here. Note that randsample does not support direct 3D matrix creation, so I initialised that in a loop.

Adriaan
  • 17,741
  • 7
  • 42
  • 75