All possible combinations of many parameters MATLA

2019-05-26 19:35发布

问题:

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

回答1:

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:

  • I'm using dynamic field name references for indexing the fields
  • I'm passing multiple outputs into a cell array from ind2sub, so you can handle a variable number of field names when ind2sub has one output for each dimension (or field in this use-case).


回答2:

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.



标签: matlab struct