3

This is a follow-up of the question All possible combinations of many parameters MATLAB

In addition to all possible combinations of my parameter set, I also have a conditional parameter. For example, I need to include the parameter named 'lambda' only when the parameter 'corrAs' is set to 'objective'.

Do achieve this, right now I am doing the following

%% All posible 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'};  

% If corrAs is 'objective', then also set lambda
params.lambda = {0.01, 0.1, 1, 10, 100};

%%%%%%%%%%%%% The solution posted on the link %%%%%%%%%%%
%% Get current parameter and evaluate
fields = fieldnames(params);
nFields = numel(fields);
sz = NaN(nFields, 1);

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

% Loop for every combination of parameters
idx = cell(1,nFields);
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:nFields

        %%%%%%%%%%% My addition for conditional parameter %%%%%%%%%%%
        % lambda is valid only when corrAs is 'objective'
        if isfield(currentParam, 'corrAs') && strcmp(fields{jj}, 'lambda') && ~strcmp(currentParam.corrAs, 'objective')
            continue;
        end
        currentParam.(fields{jj}) = params.(fields{jj}){idx{jj}};
    end

    %% Do something with currentParam

end

It works but, the number of iterations for the main for loop also includes the lambda parameter even when corrAs is not 'objective'. So, I end up evaluating with the same currentParam many times than I am supposed to.

How can I do it more efficiently?

zeeshan khan
  • 376
  • 4
  • 12

1 Answers1

0

An easy way to think about this is by breaking the code up to be more function-based

In the below code, I've simply put the combination processing code into a function paramProcessing. This function is called twice -

  1. When params.corrAs is 'constraint' only, all combinations will be processed, with no lambda field.

  2. When params.corrAs is 'objective' only, all combinations will be processed with the additional lambda field.

You can have an output for the paramProcessing function if there is one from the looping.

This means you're only doing the combinations you want. From your question, it seems like each combination is independent, so it should be irrelevant that you're covering the combinations in separate loops. The function usage means you don't have to have the new condition in the loop, and the distinct possible values for params.corrAs each time ensure no overlap.

The paramProcessing function can be a local function in a main function file, as shown, local in a script (for newer MATLAB versions), or in its own .m file on your path.

Code:

function main()
    %% All posible parameters, corrA is 'constraint' only.
    params.corrAs = {'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'};  

    % First processing call, no 'lambda' field exists in 'params'
    paramProcessing( params );

    % Cover the cases where corrAs is 'objective', with 'lambda' field
    params.corrAs = {'objective'};
    params.lambda = {0.01, 0.1, 1, 10, 100};

    % Second processing call, with new settings
    paramsProcessing( params );    
end
function paramProcessing( params )
    %% Get current parameter and evaluate
    fields = fieldnames(params);
    nFields = numel(fields);
    sz = NaN(nFields, 1);

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

    % Loop for every combination of parameters
    idx = cell(1,nFields);
    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:nFields
            currentParam.(fields{jj}) = params.(fields{jj}){idx{jj}};
        end

        %% Do something with currentParam

    end
end    
Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • Yeah. But, that requires human intervention. Any way to automate it? – zeeshan khan Jan 09 '19 at 10:30
  • I don't understand how this is any more human intervention than the minimum? You're always going to have to specify which `param` conditions exist (/ which combinations are exceptions), this is just a way to separate those conditions to reduce unnecessary loops. – Wolfie Jan 09 '19 at 10:33