Matlab: getting rid of nested loops

02 Jun 2020

Ever seen matlab code that looks like this? Maybe you are doing a grid search?

for x = 1:10
  for y = 1:5
    for z = 1:100
      for t = 1:2
        do_something(x, y, z, t)
      end
    end
  end
end

This is pretty nasty, can we make this look better?

How about something like this?

params.X = 1:10;
params.Y = [2, 4, 8];

vectorized_parameters = build_vecd_params(params);
N = size(vectorised_parameters, 1);
for x = 1:N
    candidate = get_row_as_struct(params, vectorised_parameters, x);
    do_something(candidate);
end

This looks much nicer, we don’t have nested loops and adding a new parameter to loop over is much cleaner.

We can even randomise the looping order pretty easily:

N = size(vectorised_parameters, 1);
iteration_order = randperm(N);
for x = 1:N
    idx = iteration_order(x);
    candidate = get_row_as_struct(params, vectorised_parameters, idx);
    do_something(candidate);
end

How it works

We need to create the two functions:

  • build_vecd_params: to create a matrix where each row is a single permutation of all variables we want to loop over
  • get_row_as_struct: to extract a row of the matrix and give us back a struct we can use to easily access each looping variable
function [ vectorised_parameters ] = build_vecd_params(params)
% params is a struct like
% params.X = 1:10;
% params.Y = [2, 4, 8];

all_ranges = struct2cell(params);
N = numel(all_ranges);
[grid{1:N}] = ndgrid(all_ranges{:});
vectorised_parameters = reshape(cat(N + 1, grid{:}), [], N);
end

function [ candidate ] = get_row_as_struct(params, vectorised_parameters, idx)
% return a `candidate` permutation of our loop variables
row = vectorised_parameters(idx, :);
fns = fieldnames(params)';
assert(numel(row) == numel(fns)); % sanity check
c = 1;
for fn_ = fns
    fn = char(fn_);
    candidate.(fn) = row(c);
    c = c + 1;
end
assert(c - 1 == numel(row)) % sanity check
end

How to use it

Just copy the two functions above into your matlab file and you’re off to the races.