JeschkeLab / DeerLab-Matlab

Data analysis and method development toolbox for dipolar EPR spectroscopy
MIT License
4 stars 2 forks source link

Built-in models information interface #102

Closed luisfabib closed 4 years ago

luisfabib commented 4 years ago

All parametric models in DeerLab when called without inputs return a info structure which contains the essentials (start values, boundaries, parameter names,...) needed to setup optimization with built-in models and constraints.

Structure

At the moment info looks like this, e.g. for the dd_gauss model it is defined as:

    info.model  = 'Single Gaussian distribution';
    info.nparam  = nParam;

    info.parameters(1).name = 'Center r0';
    info.parameters(1).range = [1 20];
    info.parameters(1).default = 3.5;
    info.parameters(1).units = 'nm';

    info.parameters(2).name = 'FWHM w';
    info.parameters(2).range = [0.2 5];
    info.parameters(2).default = 0.5;
    info.parameters(2).units = 'nm';

and calling the model function returns

  struct with fields:

         model: 'Single Gaussian distribution'
        nparam: 2
    parameters: [1×2 struct]

While it is a simple data type, extracting the information on the parameters is cumbersome, since for each parameter there is an associated nested structure:

  1×2 struct array with fields:

    name
    range
    default
    units

First there is no clear oversight of the parameters due to the nested nature of these structures. Second, extracting the different default values, ranges, and so on for all parameters is cumbersome:

info = dd_gauss();
% Get array of start values
par0 = [info.parameters(:).default]; 
% Get lower/upper bounds
ranges = [info.parameters(:).range];
upper = ranges(2:2:end);
lower = ranges(1:2:end-1);

Here I propose an alternative interface.

Tables

This data fundamental data type from MATLAB provides an interesting candidate for the interface of these models. A table for the dd_gauss can be defined similarly as above:

structInfo(1,1).Index = 1;
structInfo(1,1).Parameter = 'Center r0';
structInfo(1,1).Units = 'nm';
structInfo(1,1).Start = 3.5;
structInfo(1,1).Upper = 20;
structInfo(1,1).Lower = 1;

structInfo(2,1).Index = 2;
structInfo(2,1).Parameter = 'FWHM w';
structInfo(2,1).Units = 'nm';
structInfo(2,1).Start = 0.5;
structInfo(2,1).Upper = 0.2;
structInfo(2,1).Lower = 5;

info = struct2table(structInfo);

but now calling the model function returns a full (and well formatted) table with the full summary of the model parameters:

info =

  2×6 table

    Index      Parameter      Units     Start    Upper    Lower
    _____    _____________    ______    _____    _____    _____

      1      {'Center r0'}    {'nm'}     3.5       20       1  
      2      {'FWHM w'   }    {'nm'}     0.5      0.2       5  

The retrieval of information would also be largely simplified. For example to reproduce the code above to retrieve the start, upper and lower bound values we need:

par0 = info.Start;
lower = info.Lower;
upper = info.Upper;

The main difference is that the additional fields nparam and model, containing the number of parameters and model name cannot be included in the table. The loss of nparam is irrelevant as it can be obtained from the number of rows, but the model name cannot be hard-coded into the table.

Therefore, tables appear to be a potential candidate for this interface. Although I would need more thought before implementing this.

laenan8466 commented 4 years ago

Just my two cents, feel free to ignore: Why not using classes for that? ;-) Then it would be possible to inherit from a base 'model' class to all models and also combining of models could be easier, if a destinguished method is implemented.