Search code examples
matlaboptimizationvectorizationnested-loops

Vectorize a for loop to fill row


I attempted to vectorize the following:

for i = 2:n
    mat = NaN(m,i);
    for j = 1:m
        mat(j,:) = getVal(i);
    end
    num(i) = max(mat(:));
end

into

for i = 2:n
    mat = NaN(m,i);
    j = 1:m;
    mat(j,:) = getVal(i);
    num(i) = max(mat(:));
end

But, MATLAB is giving me an error: "Subscripted assignment dimension mismatch" I'm not sure where my code went wrong?

Also, is it possible to vectorize the whole nested for loop, instead of just the inner loop?

Thanks!

Edit: My apologies, the function getVal() returns a random vector of size 'i'. This is used to fill the matrix's rows in the code provided, hence this post's question title "Vectorize a for loop to fill row". Sorry if that wasn't clear.

Sample Data: For i = 2, the output would be as follows: Matrix Output 1 The rows of the matrix are filled one at a time in the inner loop. For i = 3, it'd be the same as above, with 3 columns instead: Matrix Output 2

Hope that's clear?

Edit 2: I've just tried using repmat as advised by Cris Luengo's solution. This is my output: Matrix Output with repmat. I need the function getVal to be run each time, instead of simply running it once and copying its contents down the rows.

Edit 3: Alright, here is my function getVal(i):

function [A] = getVal(m)
    X = normrnd(0,1,[m,m]);
    A = triu(X) + triu(X)';
    A = A - diag(diag(A))/2;
    A = eig(A)';
end

Solution

  • From your examples, getVal(i) doesn't keep returning same outputs for repeated calls to it. As I don't have your code, I simulated that behaviour using:

    function out = getVal(i)
       out = rand(1, i);
    end
    

    Now, if you cannot touch getVal but want to have everything vectorized, I would use arrayfun (https://www.mathworks.com/help/matlab/ref/arrayfun.html)

    mat = cell2mat(arrayfun(@getVal, repmat(i, m, 1), 'UniformOutput', false)); % UniformOutput is needed because output isn't a scalar. Cell2mat is used to convert to the same matrix as previously.
    

    The rest of your code is the same. Note that everything works as before, you just don't have loops.

    But you still have the outer loop. Another arrayfun handles that:

    num = arrayfun(@(i, m) max(max(cell2mat(arrayfun(@getVal, repmat(i, m, 1), 'UniformOutput', false)))), 1:n, repmat(m, 1, n));
    

    Note that this does not calculate mat, but I assume it isn't really needed given it keeps getting rewritten for every i.

    But ... it isn't faster. I timed this with n=1000, m=1000 and your code with 2 loops gives me 7.3s, the first vectorized approach is done in 10.8s and the final one is done in 10.7s, and again the 2 loop approach ends in 7.1s this time.

    The obvious step to really speed up the code is to vectorize getVal - something I cannot help you with, as long as I have no idea what exactly it does. But if this upper rand approach gets done in about 5s if it returns the whole mat in a single step.