2

I have a list of parameters and I need to evaluate my method over this list. Right now, I am doing it this way

% Parameters
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};
params.k = {3,4,5,6};
params.constraintP = {'identity', 'none'};
params.Npoints_perJ = {2, 3};
params.sampling = {'hks', 'fps'};   

% Select the current parameter
for corrAs_iter = params.corrAs
    for size_iter = params.size
        for density_iter = params.density
            for k_iter = params.k
                for constraintP_iter = params.constraintP
                    for Npoints_perJ_iter = params.Npoints_perJ
                        for sampling_iter = params.sampling
                            currentParam.corrAs = corrAs_iter;
                            currentParam.size = size_iter;
                            currentParam.density = density_iter;
                            currentParam.k = k_iter;
                            currentParam.constraintP = constraintP_iter;
                            currentParam.Npoints_perJ = Npoints_perJ_iter;
                            currentParam.sampling = sampling_iter;
                            evaluateMethod(currentParam);
                        end
                    end
                end
            end
        end
    end
end

I know it looks ugly and if I want to add a new type of parameter, I have to write another for loop. Is there any way, I can vectorize this? Or maybe use 2 for loops instead of so many.

I tried the following but, it doesn't result in what I need.

for i = 1:numel(fields)
%     if isempty(params.(fields{i}))
    param.(fields{i}) = params.(fields{i})(1);
    params.(fields{i})(1) = [];
end
zeeshan khan
  • 376
  • 4
  • 12
  • 1
    While it doesn't answer your question as posed, the concept in this question may help with your underlying problem: https://stackoverflow.com/questions/21895335/generate-a-matrix-containing-all-combinations-of-elements-taken-from-n-vectors?noredirect=1&lq=1 – etmuse Jan 07 '19 at 10:52

2 Answers2

5

What you need is all combinations of your input parameters. Unfortunately, as you add more parameters the storage requirements will grow quickly (and you'll have to use a large indexing matrix).

Instead, here is an idea which uses linear indicies of a (never created) n1*n2*...*nm matrix, where ni is the number of elements in each field, for m fields.

It is flexible enough to cope with any amount of fields being added to params. Not performance tested, although as with any "all combinations" operation you should be wary of the non-linear increase in computation time as you add more fields to params, note prod(sz)!

The code I've shown is fast, but the performance will depend entirely on which operations you do in the loop.

% Add parameters here
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};

% Setup
f = fieldnames( params );
nf = numel(f);
sz = NaN( nf, 1 );

% Loop over all parameters to get sizes
for jj = 1:nf
    sz(jj) = numel( params.(f{jj}) );
end

% Loop for every combination of parameters
idx = cell(1,nf);
for ii = 1:prod(sz)
    % Use ind2sub to switch from a linear index to the combination set
    [idx{:}] = ind2sub( sz, ii );
    % Create currentParam from the combination indices
    currentParam = struct();
    for jj = 1:nf
        currentParam.(f{jj}) = params.(f{jj}){idx{jj}};
    end
    % Do something with currentParam here
    % ...
end

Asides:

Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • Thank you, Is there a possibility to incorporate conditional fields. For example, if strcmp(currentParam.corrAs, 'objective') currentParam.lambda = 100; % But, from params.lambda = {1, 10, 100, 1000}; end – zeeshan khan Jan 07 '19 at 20:04
  • @zee that's a bit unclear, perhaps you could ask a new question with your desired output – Wolfie Jan 07 '19 at 21:11
  • please find the follow-up question at https://stackoverflow.com/q/54098049/5984672 – zeeshan khan Jan 08 '19 at 19:00
3

Here is a vectorized solution :

names = fieldnames(params).';
paramGrid = cell(1,numel(names));

cp = struct2cell(params);

[paramGrid{:}] = ndgrid(cp{:});

ng = [names;paramGrid];
st = struct(ng{:});


for param = st(:).'
    currentParam = param;
end

Instead of nested loops we can use ndgrid to create the cartesian product of the cell entries so we can find all combinations of cell entries without loop.

rahnema1
  • 15,264
  • 3
  • 15
  • 27