Analysis

f_analysis

Code

 1function [results, settings] = ...
 2    f_analysis(settings, analysis, model_folders, analysis_options, results)
 3% This function performs a specific analysis based on the given analysis
 4% options. The function calls the appropriate analysis functions
 5% (f_diagnostics, f_opt, f_sim_score, f_lsa, f_gsa, f_PL_m) depending on
 6% the selected analysis type. The analysis results are stored in an updated
 7% 'rst' structure, and the settings are updated as needed.
 8%
 9% Inputs:
10% - stg: Settings to use in the analysis
11% - analysis: Type of analysis to be performed
12% - mmf: Main model files
13% - analysis_options: List of possible analysis options
14% - rst: Structure for storing analysis results
15%
16% Outputs:
17% - rst: Updated structure containing the results of the analysis
18% - stg: Updated settings after the analysis
19%
20% Used Functions:
21% - f_diagnostics: Performs diagnostic analysis
22% - f_opt: Performs optimization analysis
23% - f_sim_score: Runs simulation for a given option
24% - f_lsa: Performs Local sensititivity Analysis (LSA)
25% - f_gsa: Performs Global ensititivity Analysis (GSA)
26% - f_PL_m: Performs Profile Likelihood Analysisa (PLA)
27%
28% Variables:
29% Initialized:
30% - matching_option_idx: Index of the selected analysis option
31% - sim_results: Cell array for storing simulation results
32% - valid_options: Array of valid optimization options
33%
34% Persistent: None
35% analysis
36% analysis_options
37% matching_option_idx
38matching_option_idx = find(contains(analysis_options, analysis), 1);
39
40% display start message
41disp("Starting " + {analysis_options(matching_option_idx)})
42
43% Perform the analysis based on the current analysis option index
44switch matching_option_idx
45    case 1
46        % run diagnostic analysis
47        results.diag = f_diagnostics(settings, model_folders);
48    case 2
49        % run optimization analysis
50        [results.opt, pa] = f_opt(settings, model_folders);
51        % find valid optimization options
52        valid_options = find(pa(:, 1) ~= 0);
53        % update settings with valid options
54        settings.pat = transpose(valid_options);
55        % create cell array for simulation results
56        sim_results = cell(length(valid_options), 1);
57        % parallelize simulation runs
58        for j = settings.pat
59            % run simulation for current valid option
60            [~, sim_results{j}, ~] = ...
61                f_sim_score(pa(j, :), settings, model_folders, 0, 0);
62        end
63        % store simulation results in results structure
64        results.diag(settings.pat) = [sim_results{:}];
65    case 3
66        % run LSA analysis
67        results.lsa = f_lsa(settings, model_folders);
68    case 4
69        % run GSA analysis
70        results.gsa = f_gsa(settings, model_folders);
71    case 5
72        % run PLA analysis
73        results.PLA = f_PL_m(settings, model_folders);
74end
75
76% display completion message
77disp(analysis_options(matching_option_idx) + " completed successfully")
78end

The f_analysis function performs a specific analysis based on the given analysis options. It takes settings, analysis type, main model files, a list of possible analysis options, and a structure for storing analysis results as input arguments. The function outputs the results of the analysis and updates the settings as needed. The function calls the appropriate analysis functions (f_diagnostics, f_opt, f_sim_score, f_lsa, f_gsa, f_PL_m) depending on the selected analysis type.

Inputs

  • stg: Settings to use in the analysis.

  • analysis: Type of analysis to be performed.

  • mmf: Main model files.

  • analysis_options: List of possible analysis options.

  • rst: Structure for storing analysis results.

Outputs

  • rst: Updated structure containing the results of the analysis.

  • stg: Updated settings after the analysis.

Usage

[rst, stg] = f_analysis(stg, analysis, mmf, analysis_options, rst)

Example

settings = :ref:`stg<stg>`;
analysis_type = 'diagnostics';
model_files = 'path/to/model/files';
options = ["Diagnostics","Parameter Estimation",...
 "Local Sensitivity Analysis","Global Sensitivity Analysis",...
 "Profile Likelihood Analysis","Reproduce a previous analysis",...
 "Reproduce the plots of a previous analysis","Import model files"];
results = :ref:`stg<stg>`;

[results, settings] = f_analysis(settings, analysis_type, model_files, options, results);

Diagnostics

f_diagnostics

Code

 1function rst = f_diagnostics(stg, mmf)
 2% Description: This function runs a model for given parameters and
 3% settings, and computes the scores for fitness, both in multi-core and
 4% single-core mode. The function uses f_sim_score() to obtain the score for
 5% each parameter array. The mode (multi-core or single-core) is determined
 6% by the 'optmc' field in the 'stg' input.
 7%
 8% Inputs:
 9% - stg: A structure containing settings for the model, such as:
10%        * pa: An array of parameter arrays to be tested.
11%        * pat: Indices of the parameter arrays to be tested.
12%        * optmc: A Boolean flag, if true the function runs in multi-core
13%          mode, otherwise it runs in single-core mode.
14% - mmf: A custom memory-mapped file object, used for reading or writing 
15% data.
16% 
17% Outputs:
18% - rst: A vector containing the computed scores for each parameter array.
19%
20% Used Functions:
21% - f_sim_score: A function that takes a parameter array, model settings
22% (stg), and a custom memory-mapped file object (mmf) as inputs and returns
23% the computed score for that parameter array.
24%
25% Variables:
26% Loaded:
27% (None)
28%
29% Initialized:
30% - par_array_idx: The current index of the parameter array being tested.
31%
32% Persistent:
33% (None)
34
35% Run the model and obtain scores for fitness Multi Core
36pa = stg.pa(stg.pat, :);
37if stg.optmc
38    disp("Running the model and obtaining Scores (Multicore)")
39    
40    % Iterate over the parameter arrays to be tested
41    parfor par_array_idx = 1:length(stg.pat)
42        [~, rst(par_array_idx), ~] = ...
43            f_sim_score(pa(par_array_idx, :), stg, mmf, 0, 0);
44    end
45        
46    % Run the model and obtain scores for fitness single Core
47else
48    disp("Running the model and obtaining Scores (Singlecore)")
49    
50    % Iterate over the parameter arrays to be tested
51    for par_array_idx = 1:length(stg.pat)
52        [~, rst(par_array_idx), ~] = ...
53            f_sim_score(pa(par_array_idx, :), stg, mmf, 0, 0);
54    end
55end
56rst(stg.pat(1:length(stg.pat))) = rst(1:length(stg.pat));
57end
Used to understand the effects of different parameters sets on model behaviour or in comparing different parameters sets.
It loads the user defined configurations, performs all the needed simulations, and calculates scores of the error functions either per experimental output, per experiment, or in total (check results).

Optimization

f_opt

Code

  1function [result,parameter_array] = f_opt(settings,model_folders)
  2% This function performs optimization on a given objective function using
  3% different optimization algorithms. The available algorithms include
  4% fmincon, Simulated annealing, Pattern search, Genetic algorithm, Particle
  5% swarm, and Surrogate optimization. It iterates through the
  6% optimization_algorithms cell array and calls the appropriate optimization
  7% algorithm if its flag is set in the 'settings'.
  8%
  9% Inputs:
 10% - settings: A struct containing settings for the optimization algorithms.
 11% - model_folders: An array containing information about the models to be
 12% optimized.
 13%
 14% Outputs:
 15% - result: A vector containing the results of each optimization algorithm.
 16% - parameter_array: A matrix containing the parameters for each
 17% optimization algorithm result.
 18%
 19% Used Functions:
 20% - run_algorithm: Handles the execution of a specific optimization
 21% algorithm.
 22% - optimize_algorithm: Sets up the optimization algorithm and executes it.
 23% - run_optimization: Runs the selected optimization algorithm with
 24% appropriate settings.
 25% - run_optimizer: Executes the chosen optimization algorithm.
 26% - prepare_optimization_options: Prepares the optimization options based
 27% on the algorithm.
 28% - get_plot_functions: Returns the appropriate plotting function for the
 29% optimization algorithm.
 30% - f_opt_start: Returns the starting point and the starting population for
 31% optimization algorithms.
 32% 
 33% Loaded Variables:
 34% - optimization_algorithms: A cell array containing optimization algorithm
 35% names and flags.
 36% - algorithm_flag: The flag corresponding to the optimization algorithm.
 37% - objective_function: A function handle to the objective function for
 38% optimization.
 39% - options: A struct containing options for the optimization algorithm.
 40% - plot_functions: A cell array containing plot functions specific to each
 41% optimization algorithm.
 42
 43% settings.optt = 60*60;
 44% settings.popsize = 720;
 45% Set the random seed for reproducibility
 46rng(settings.rseed);
 47
 48% Initialize the cell array containing optimization algorithm names and
 49% corresponding settings flags
 50optimization_algorithms = {
 51    'Genetic algorithm', 'ga';
 52    'fmincon', 'fmincon';
 53    'Simulated annealing', 'sa';
 54    'Pattern search', 'psearch';
 55   'Particle swarm', 'pswarm';
 56    'Surrogate optimization', 'sopt'};
 57
 58p = gcp(); % If no pool, do not create new one.
 59poolsize = p.NumWorkers;
 60
 61settings.pat_original = settings.pat;
 62settings.pa_original = settings.pa;
 63
 64
 65settings.pat = 1:poolsize;
 66for n = 1:poolsize
 67settings.pa(n, :) = settings.pa(1, :);
 68end
 69parfor n = 1:poolsize
 70[~] = f_diagnostics(settings, model_folders);
 71end
 72
 73% Iterate through the optimization_algorithms cell array and call the
 74% corresponding algorithms if their flag is set in the settings
 75for i = 1:size(optimization_algorithms, 1)
 76    % algorithm_func = optimization_algorithms{i, 1};
 77    algorithm_flag = optimization_algorithms{i, 2};
 78opt_time = tic;
 79    if settings.(algorithm_flag)
 80        [result(i), parameter_array(i, :)] = ...
 81            run_algorithm(@optimize_algorithm,...
 82            optimization_algorithms{i, 1}, settings, model_folders);
 83    end
 84disp(convertCharsToStrings(optimization_algorithms{i, 2}) + ": " + ...
 85    toc(opt_time))
 86end
 87end
 88
 89function [algorithm_result, algorithm_parameter_array] =...
 90    run_algorithm(algorithm_func, optimization_algorithms_names,...
 91    settings, model_folders)
 92
 93% Define the objective function
 94objective_function = @(x)f_sim_score(x, settings, model_folders, 0, 0);
 95
 96% Call the optimization algorithm
 97algorithm_result = algorithm_func(optimization_algorithms_names,...
 98    settings, objective_function);
 99
100% Find the minimum value and its corresponding parameters
101[~, min_index] = min(algorithm_result.fval);
102algorithm_parameter_array = algorithm_result.x(min_index, :);
103end
104
105function result_opt = optimize_algorithm(algorithm_name,...
106    settings, objective_function)
107p = gcp(); % If no pool, do not create new one.
108
109% poolsize = p.NumWorkers;
110% mst = settings.mst;
111% optt = settings.optt = 60*10;
112% if contains(algorithm_name, {'Pattern search', 'Surrogate optimization'})
113% settings.mst = true;
114% settings.msts = poolsize;
115% elseif contains(algorithm_name, {'fmincon', 'Simulated annealing'})
116% settings.mst = true;
117% settings.msts = poolsize*5;
118% settings.optt = optt/5;
119% else
120% settings.mst = mst;
121% settings.msts = poolsize;
122% settings.optt = optt;
123% end
124
125% Determine the starting point for the optimization
126[startpoint, ~, spop] = f_opt_start(settings);
127
128% Prepare optimization options based on settings and algorithm_name
129options = prepare_optimization_options(settings, algorithm_name);
130
131% Execute the optimization algorithm
132[x, fval, exitflag, output] = run_optimization(settings, algorithm_name,...
133    objective_function, startpoint, spop, settings.lb, settings.ub,...
134     options);
135
136% Save optimization results
137result_opt.name = algorithm_name;
138result_opt.x = x;
139result_opt.fval = fval;
140result_opt.exitflag = exitflag;
141result_opt.output = output;
142end
143
144function [x, fval, exitflag, output] = run_optimization(settings,...
145    algorithm_name, objective_function, startpoint, spop, lb, ub,...
146    options)
147
148% Run the optimization algorithm using either multiple starting points (mst)
149% or a single starting point (the average of the lower and upper bounds)
150if settings.mst
151    parfor n = 1:settings.msts
152        [x(n,:), fval(n), exitflag(n), output(n)] = ...
153            run_optimizer(algorithm_name, objective_function,...
154            startpoint(n,:), spop{n}, lb, ub, options);
155    end
156else
157
158    [x(1,:), fval(1), exitflag(1), output(1)] = ...
159        run_optimizer(algorithm_name, objective_function,...
160        (lb + ub) / 2, spop, lb, ub, options);
161
162    % [x(1,:), fval(1), exitflag(1), output(1)] = ...
163    %     run_optimizer(algorithm_name, objective_function,...
164    %     settings.bestpa, spop, lb, ub, options);
165end
166end
167
168function [x, fval, exitflag, output] = ...
169    run_optimizer(algorithm_name, objective_function, startpoint, spop,...
170    lb, ub, options)
171% Execute the appropriate optimization algorithm based on algorithm_name
172switch algorithm_name
173    case 'fmincon'
174        [x, fval, exitflag, output] = ...
175            fmincon(objective_function, startpoint, ...
176            [], [], [], [], lb, ub, [], options);
177    case 'Simulated annealing'
178        [x, fval, exitflag, output] = ...
179        simulannealbnd(objective_function, startpoint, lb, ub, options);
180    case 'Pattern search'
181        
182        [x, fval, exitflag, output] = ...
183            patternsearch(objective_function, startpoint, ...
184            [], [], [], [], lb, ub, [], options);
185    case 'Genetic algorithm'
186        parnum = length(lb);
187        [x, fval,exitflag,output] = ga(objective_function, parnum, ...
188            [], [], [], [], lb, ub, [], options);
189    case 'Particle swarm'
190        parnum = length(lb);
191        [x, fval, exitflag, output] = ...
192            particleswarm(objective_function, parnum, lb, ub, options);
193    case 'Surrogate optimization'
194        options.InitialPoints = spop;
195        [x, fval, exitflag, output] = ...
196        surrogateopt(objective_function, lb, ub, options);
197    otherwise
198        error('Unsupported optimization algorithm.');
199end
200end
201
202function options = prepare_optimization_options(settings, algorithm_name)
203
204% Set options for each algorithm
205switch algorithm_name
206    case 'fmincon'
207        options = settings.fm_options;
208        options.UseParallel = settings.optmc;
209    case 'Simulated annealing'
210        options =  settings.sa_options;
211        options.MaxTime = settings.optt;
212    case 'Pattern search'
213        options = settings.psearch_options;
214        options.MaxTime = settings.optt;
215        options.UseParallel = settings.optmc;
216    case 'Genetic algorithm'
217
218        % test = settings.pa_original(settings.pat_original, :);
219
220        options = settings.ga_options;
221        options.MaxTime = settings.optt;
222        % options.PopulationSize = settings.popsize+length(test);
223        options.PopulationSize = settings.popsize;
224        options.UseParallel = settings.optmc;
225        [~, startpop, ~] = f_opt_start(settings);
226        options.InitialPopulationMatrix = startpop;
227    case 'Particle swarm'
228        options = settings.pswarm_options;
229        options.MaxTime = settings.optt;
230        options.SwarmSize = settings.popsize;
231        options.UseParallel = settings.optmc;
232        [~, startpop, ~] = f_opt_start(settings);
233        options.InitialSwarmMatrix = startpop;
234    case 'Surrogate optimization'
235        options = settings.sopt_options;
236        options.MaxTime = settings.optt;
237        options.UseParallel = settings.optmc;
238    otherwise
239        error('Unsupported optimization algorithm.');
240end
241
242% Enable plots if chosen in settings
243if settings.optplots
244    % Set the appropriate PlotFcn based on the optimization algorithm
245    options.PlotFcn = get_plot_functions(algorithm_name);
246end
247
248% Enable console messages if chosen in settings
249if settings.optcsl
250    options.Display = 'iter';
251end
252end
253
254function plot_functions = get_plot_functions(algorithm_name)
255
256% Set the appropriate PlotFcn based on the optimization algorithm
257switch algorithm_name
258    case 'fmincon'
259        plot_functions = ...
260            {@optimplotx, @optimplotfunccount, @optimplotfval, ...
261            @optimplotstepsize, @optimplotfirstorderopt};
262    case 'Simulated annealing'
263        plot_functions = ...
264            {@saplotbestf, @saplottemperature, @saplotf, ...
265            @saplotstopping, @saplotx};
266    case 'Pattern search'
267        plot_functions = ...
268            {@psplotbestf, @psplotfuncount, @psplotmeshsize, @psplotbestx};
269    case 'Genetic algorithm'
270        plot_functions = ...
271            {@gaplotbestf, @gaplotexpectation, @gaplotrange, ...
272            @gaplotscorediversity, @gaplotstopping, ...
273            @gaplotscores, @gaplotdistance, @gaplotselection, @gaplotbestindiv};
274    case 'Particle swarm'
275        plot_functions = {@pswplotbestf};
276    case 'Surrogate optimization'
277        plot_functions = {@surrogateoptplot};
278    otherwise
279        error('Unsupported optimization algorithm.');
280end
281end
282
283function [spoint,spop_mc,spop_sc] = f_opt_start(settings)
284% Set the randomm seed for reproducibility
285rng(settings.rseed);
286
287test = settings.pa_original(settings.pat_original, :);
288
289% Optimization Start method 1
290if settings.osm == 1
291% stg.pat_original
292% stg.pa_original
293% for n = stg.pat_original
294%     n
295%     stg.pa_original(n,:)
296
297% end
298% test
299% lhsdesign(stg.popsize,stg.parnum).*(stg.ub-stg.lb)+stg.lb
300
301% test = stg.pa_original(stg.pat_original,:);
302    % Get a random starting point or group of starting points, if using
303    % multistart, inside the bounds
304    spoint = lhsdesign(settings.msts, settings.parnum) .* ...
305    (settings.ub - settings.lb) + settings.lb;
306
307    % Get a group of ramdom starting points inside the bounds
308    spop_mc = lhsdesign(settings.popsize, settings.parnum) .* ...
309        (settings.ub - settings.lb) + settings.lb;
310    % spop_mc = [lhsdesign(settings.popsize,settings.parnum).*(settings.ub-stg.lb)+settings.lb;test];
311
312    % Get a group of ramdom starting points inside the bounds
313    for n = 1:settings.msts
314    spop_sc{n} = lhsdesign(ceil(settings.popsize / 36), ...
315        settings.parnum) .* (settings.ub-  settings.lb) + settings.lb;
316    end
317    % Optimization Start method 2
318elseif settings.osm == 2
319    
320    % Get a random starting point or group of starting points, if using
321    % multistart, near the best point
322    spoint = settings.bestpa - settings.dbpa + ...
323        (settings.dbpa * 2 * lhsdesign(settings.msts, settings.parnum));
324    
325    spoint = max(min(5,spoint), -7);
326
327    % Get a group of random starting points near the best point
328    spop_mc = settings.bestpa - settings.dbpa + ...
329        (settings.dbpa * 2 * lhsdesign(settings.popsize, settings.parnum));
330
331    spop_mc = max(min(settings.ub,spop_mc),settings.lb);
332    spop_mc = [spop_mc;test];
333    for n = 1:settings.msts
334    spop_sc{n} = settings.bestpa - settings.dbpa + ...
335        (settings.dbpa * 2 * lhsdesign(ceil(settings.popsize / 36), ...
336        settings.parnum));
337
338    spop_sc{n} = max(min(settings.ub,spop_sc{n}), settings.lb);
339    end
340end
341end

Calls the correct optmizer or optimizers that have been chosen in the settings file.

f_opt_start

Code

Creates the starting parameter set or sets of the optimizations, if single or multistart selected in settings file.
It supports two different random distributions for the starting points.

f_opt_fmincon/sa/psearch/ga/pswarm/sopt

Code

f_opt_fmincon

f_opt_sa

f_opt_psearch

f_opt_ga

f_opt_pswarm

f_opt_sopt

These functions call built in MATLAB® functions that perform parameter optimization . For furher information relating to how these optimizers work please follow the links to the MATLAB® documentation. Optimizers used:

Global Sensitivity Analysis

f_gsa

Code

 1function results = f_gsa(settings, model_folders)
 2% f_gsa - Performs global sensitivity analysis (GSA) using the
 3% Sobol-Saltelli method on a given model
 4%
 5% This function conducts a GSA based on the Sobol-Saltelli method for a
 6% given model, as inspired by Geir Halnes et al.'s 2009 paper (J. comp.
 7% neuroscience 27.3 (2009): 471). The function takes two inputs:
 8% 'settings', a structure containing various settings for the analysis, and
 9% 'model_folders', the model folders structure. The function returns
10% 'results', a structure containing the GSA results, including sensitivity
11% indices (Si) and total sensitivity indices (SiT) for each score type.
12%
13% Inputs:
14% - settings: A structure containing settings for the GSA, such as
15% parameter bounds, sampling mode, and random seed.
16% - model_folders: A structure containing the necessary model folder paths.
17%
18% Outputs:
19% - results: A structure containing the GSA results, including sensitivity
20% indices (Si) and total sensitivity indices (SiT) for each score type.
21%
22% Functions called:
23% - f_make_par_samples(settings): Generates sample matrices of model
24% parameters.
25% - f_make_output_sample(results, settings, model_folders): Calculates the
26% outputs for three different matrices (GSA M1, GSA M2, and GSA N) based on
27% the input parameters, settings, and mathematical model.
28% - f_calc_sensitivities(results, settings): Removes simulation errors
29% from the data and calculates Si and SiT using the bootstrap method.
30
31results = f_make_par_samples(settings);
32
33results = f_make_output_sample(results,settings,model_folders);
34
35results = f_calc_sensitivities(results,settings);
36end

Calls the global sensitivity analysis functions in the correct order.

f_make_par_samples

Code

 1function results = f_make_par_samples(settings)
 2% This function creates sample matrices of model parameters according to
 3% specified distributions and settings, based on Geir Halnes et al.'s 2009
 4% paper (J. comp. neuroscience 27.3 (2009): 471). It generates two sample
 5% matrices M1 and M2, and a 3D matrix N, where N(:,:,i) is M2 with its i:th
 6% column replaced by M1(:,i).
 7%
 8% Inputs:
 9% - settings: A structure containing fields for generating samples,
10% including sansamples, parnum, rseed, sasamplemode, lb, ub, bestpa, and
11% sasamplesigma.
12%
13% Outputs:
14% - results: A structure containing the following fields:
15% - M1: Sample matrix 1.
16% - M2: Sample matrix 2.
17% - N: A 3D matrix where N(:,:,i) is M2 with its i:th column replaced by
18% M1(:,i).
19%
20% Functions called:
21% - makedist: Create a probability distribution object.
22% - truncate: Truncate a probability distribution.
23% - random: Generate random numbers from a probability distribution.
24%
25% Variables:
26% Initialized:
27% - M1: Sample matrix 1.
28% - M2: Sample matrix 2.
29% - N: A 3D matrix where N(:,:,i) is M2 with its i:th column replaced by
30% M1(:,i).
31% - pd: An array of probability distribution objects for each parameter.
32%
33% Loaded (from settings structure):
34% - sansamples: Number of samples to generate for each parameter.
35% - parnum: Number of model parameters.
36% - rseed: Seed for the random number generator.
37% - sasamplemode: Sampling mode.
38% - lb: Vector containing lower bounds for each parameter.
39% - ub: Vector containing upper bounds for each parameter.
40% - bestpa: Vector containing the best parameter values.
41% - sasamplesigma: Standard deviation for normal distribution-based
42% sampling modes.
43
44% Pre-allocate memory for sample matrices
45M1 = zeros(settings.sansamples, settings.parnum);
46M2 = zeros(settings.sansamples, settings.parnum);
47N = zeros(settings.sansamples, settings.parnum, settings.parnum);
48rng(settings.rseed)
49
50% Loop through each parameter and create a distribution based on the
51% specified settings(Note that the sampling is being performed in log space
52% as the parameters and its bounds are in log space)
53for i = 1:settings.parnum
54    % Initialize distribution parameters depending on the sample mode
55    switch settings.sasamplemode
56        case 0
57            % Uniform distribution with bounds defined by parameter limits
58            lb = settings.lb(i);
59            ub = settings.ub(i);
60        case {1, 2}
61            % Normal distribution centered at the best parameter value with
62            % specified standard deviation
63            mu = settings.bestpa(i);
64            sigma = settings.sasamplesigma;
65        case {3, 4}
66            % Normal distribution centered at the mean of parameter bounds
67            % with specified standard deviation
68            mu = settings.lb(i) + (settings.ub(i) - settings.lb(i)) / 2;
69            sigma = settings.sasamplesigma;
70    end
71    % Generate samples based on the distribution parameters
72    if settings.sasamplemode == 0
73        % Uniform distribution
74        M1(:, i) = lb + (ub - lb) .* rand(1, settings.sansamples);
75        M2(:, i) = lb + (ub - lb) .* rand(1, settings.sansamples);
76    else
77        % Normal distribution
78        pd(i) = makedist('Normal', 'mu', mu, 'sigma', sigma);
79
80        % Truncate the distribution if specified
81        if ismember(settings.sasamplemode, [1, 3])
82            pd(i) = truncate(pd(i), settings.lb(i), settings.ub(i));
83        end
84        M1(:, i) = random(pd(i), settings.sansamples, 1);
85        M2(:, i) = random(pd(i), settings.sansamples, 1);
86    end
87end
88% Replace the i:th column in M2 by the i:th column from M1 to create N
89% matrices
90for i=1:settings.parnum
91    N(:, :, i) = M2;
92    N(:, i, i) = M1(:, i);
93end
94
95% Store resulting matrices in the output structure
96results.M1 = M1;
97results.M2 = M2;
98results.N = N;
99end

Creates parameter sets samples with specific parameter distributions that are used to perform the global sensitivity analysis.

Code inspired by Geir Halnes et al. 2009 paper.

f_make_output_sample

Code

  1function results = f_make_output_sample(results,settings,model_folders)
  2% This function calculates the outputs for three different matrices (GSA
  3% M1, GSA M2, and GSA N) based on the input parameters, settings, and a
  4% mathematical model. This code is inspired by Geir Halnes et al. 2009
  5% paper.
  6%
  7% Inputs:
  8% - results: A structure containing M1, M2, and N matrices
  9% - settings: A structure with settings, including sansamples and parnum
 10% - model_folders: A variable containing folders for the models
 11%
 12% Outputs:
 13% - results: A structure containing fM1, fM2, and fN fields, each
 14% containing computed outputs for corresponding GSA methods
 15%
 16% Used Functions :
 17% - compute_f: Compute the results for GSA M1, GSA M2, and GSA N methods
 18% - f_sim_score: Simulation score calculation function
 19% - progress_track: Track progress of the calculations
 20%
 21% Variables
 22% Loaded:
 23% - nSamples: Number of samples (loaded from settings.sansamples)
 24% - nPars: Number of parameters (loaded from settings.parnum)
 25%
 26% Initialized:
 27% - time_begin: The start time of the function
 28% - f_out: Structure holding the computed values for fM1, fM2, and fN
 29%
 30% Persistent:
 31% - current_sample: Keeps track of the current sample number for progress
 32% tracking
 33% - last_time: Keeps track of the last time progress was printed
 34
 35% Number of samples
 36nSamples = settings.sansamples;
 37
 38% Number of parameters
 39nPars = settings.parnum;
 40
 41% Start time of the function
 42time_begin = datetime;
 43
 44% Compute the results for GSA M1, GSA M2, and GSA N methods
 45results.fM1 = ...
 46    compute_f(results.M1, nSamples, settings, model_folders, nPars, ...
 47    time_begin, "GSA M1", "fM");
 48results.fM2 = ...
 49    compute_f(results.M2, nSamples, settings, model_folders, nPars, ...
 50    time_begin, "GSA M2", "fM");
 51results.fN = ...
 52    compute_f(results.N, nSamples, settings, model_folders, nPars, ...
 53    time_begin, "GSA N", "fN");
 54end
 55
 56function [f_out] =...
 57    compute_f(parameter_array, nSamples, settings, model_folders, nPars, ...
 58    time_begin, task_name, matrix_type)
 59
 60% Create a DataQueue for parallel processing
 61D = parallel.pool.DataQueue;
 62
 63% Set up the progress tracker for the DataQueue
 64afterEach(D, @progress_track_GSA);
 65
 66clear R RN
 67
 68% Choose the matrix type and perform the calculations accordingly
 69switch matrix_type
 70    case 'fM'
 71        R = cell(nSamples, 1);
 72      
 73        % Parallel loop for computing the output
 74        parfor i = 1:nSamples
 75            [~, ~, R{i}] = ...
 76                f_sim_score(parameter_array(i,:), settings, ...
 77                model_folders, i, 1);
 78            send(D, {task_name, 0, time_begin, nSamples, nPars});
 79        end
 80        % Extract the computed values
 81
 82        sd = zeros(nSamples, size(R{1}.sd, 1) * size(R{1}.sd, 2));
 83        se = zeros(nSamples, size(R{1}.se, 1));
 84        st = zeros(nSamples, size(R{1}.st, 1));
 85        xfinal = zeros(nSamples, size([R{1}.xfinal{:}], 2));
 86
 87        for i = 1:nSamples
 88            sd(i, :) = reshape(R{i}.sd(:, :), 1, []);
 89            se(i, :) = R{i}.se(:);
 90            st(i, :) = R{i}.st;
 91            xfinal(i, :) = [R{i}.xfinal{:}];
 92        end
 93    case 'fN'
 94        % Nested parallel loop for computing the output
 95        RN = cell(nSamples, nPars);
 96
 97        parfor i = 1:nSamples
 98            for j = 1:nPars
 99                % disp("i: " + i + " j: " + j)
100                % settings.i = i;
101                % setting.j = j;
102                [~,~,RN{i,j}] = ...
103                    f_sim_score(parameter_array(i, :, j), settings, ...
104                    model_folders, i, j);
105            end
106            send(D, {task_name, 0, time_begin, nSamples, nPars});
107        end
108
109        sd = ...
110            zeros(nSamples, size(RN{1, 1}.sd, 1) * ...
111            size(RN{1, 1}.sd, 2), nPars);
112        se = zeros(nSamples, size(RN{1, 1}.se, 1), nPars);
113        st = zeros(nSamples, size(RN{1, 1}.st, 1), nPars);
114        xfinal = zeros(nSamples, size([RN{1, 1}.xfinal{:}], 2), nPars);
115
116        for i = 1:nSamples
117            for j = 1:nPars
118                sd(i, :, j) = reshape(RN{i, j}.sd, 1, []);
119                se(i, :, j) = RN{i, j}.se;
120                st(i, :, j) = RN{i, j}.st;
121                xfinal(i, :, j) = [RN{i, j}.xfinal{:}];
122            end
123        end
124end
125
126f_out.sd = sd;
127f_out.se = se;
128f_out.st = st;
129f_out.xfinal = xfinal;
130
131% Display the runtime information for the task
132disp(task_name + " Runtime: " + string(datetime - time_begin) +...
133    "  All " + nSamples + " samples executed")
134end
135
136% Function to track progress of the GSA
137function progress_track_GSA(arg)
138persistent current_sample
139persistent last_time
140
141% Initialize or update the current sample counter
142if isempty(current_sample) || current_sample == arg{4}
143    current_sample = arg{2};
144end
145
146% Retrieve other required arguments
147task_name = arg{1};
148start_time = arg{3};
149num_samples = arg{4};
150par_n = arg{5};
151current_sample = current_sample + 1;
152
153% Print progress information at each step
154if mod(current_sample, ceil(num_samples / 10)) == 0 &&...
155        current_sample ~= num_samples
156    % 0
157    % num_samples
158    % current_sample
159    % ((num_samples-current_sample)/num_samples)*10
160    % ((num_samples-(ceil(num_samples/10)))/num_samples)*10
161    if ((num_samples - current_sample) / num_samples) * 10 <...
162            ((num_samples - (ceil(num_samples / 10))) / num_samples) * 10
163        % Calculate the remaining time
164        % datetime
165        % last_time
166        dt = (datetime-last_time);
167       % 1
168        remaining_time = seconds(dt);
169% 2
170        remaining_time = ...
171            remaining_time * ceil((num_samples - current_sample) / ...
172            num_samples * 10);
173% 3
174        remaining_time = seconds(remaining_time);
175% 4
176        remaining_time.Format = 'hh:mm:ss';
177     % 5
178        % Print the progress and remaining time
179        fprintf('%s Runtime: %s  Time to finish: %s  Samples: %d/%d\n', ...
180            task_name, string(datetime - start_time), string(remaining_time), ...
181            current_sample, num_samples);
182        % 6
183    else
184% 7
185        % Print the progress without remaining time
186        fprintf('%s Runtime: %s  Samples: %d/%d\n', ...
187            task_name, string(datetime - start_time), ...
188            current_sample, num_samples);
189        % 8
190    end
191% 9
192    last_time = datetime;
193end
194end

For each parameter set given in the matrices M1, M2, and N it runs the function f_sim_score generating new matrices fM1, fM2, and fN respectively.

Code inspired by Geir Halnes et al. 2009 paper.

f_calc_sensitivities

Code

  1function results = f_calc_sensitivities(results,settings)
  2% Computes the first-order (Si) and total-order (SiT) sensitivity indices
  3% for each score type present in the input data using the bootstrap method.
  4% This function takes in the raw data and various settings for the
  5% calculation process, and returns the results structure with calculated Si
  6% and SiT for each score type.
  7%
  8% Inputs:
  9% - results: a structure containing raw data (fM1, fM2, and fN) for
 10% different score types
 11% - settings: a structure containing settings for the calculation, such as
 12% bootstrap size, random seed, and error score
 13%
 14% Outputs:
 15% - results: a modified version of the input results structure containing
 16% the computed Si and SiT for each score type
 17%
 18% Functions called:
 19% - remove_sim_error: removes simulation errors from the input data
 20% - bootstrap: calculates Si and SiT using the bootstrap method
 21% - bootstrap_q: creates bootstrapped samples and calculates Si and SiT for
 22% the bootstrapped samples
 23% - calcSobolSaltelli: calculates Si and SiT using the Sobol-Saltelli
 24% method
 25% 
 26% Loaded variables:
 27% - None
 28%
 29% Initialized variables:
 30% - fM1: a structure containing the first-order score data for each score 
 31% type
 32% - fM2: a structure containing the total-order score data for each score 
 33% type
 34% - fN: a structure containing the score data for each score type and 
 35% parameter
 36% - settings: a structure containing settings for the calculation
 37%
 38% Persistent variables:
 39% - None
 40
 41% Remove simulation errors from the input data
 42results = remove_sim_error(results, settings);
 43
 44% Calculate Si and SiT using the bootstrap method and store them in the
 45% results structure
 46[results.SiQ,results.SiTQ, results.Si, results.SiT] = ...
 47    bootstrap(results, settings);
 48end
 49
 50function [SiQ,SiTQ,Si,SiT]=bootstrap(results,settings)
 51% calculates confidence intervals for the sensitivity indices using the
 52% bootstrap method.
 53
 54% Initialize variables
 55fM1 = results.fM1;
 56fM2 = results.fM2;
 57fN = results.fN;
 58
 59% Set the number of resamples for bootstrapping if not provided
 60if (isempty(settings.gsabootstrapsize))
 61    settings.gsabootstrapsize = ceil(sqrt(size(fM1.sd, 2)));
 62end%if
 63
 64% Define the list of scores to be used in the calculation
 65scores_list = ["sd","se","st","xfinal"];
 66
 67% Generate random indices for bootstrapping
 68for j = 1: settings.gsabootstrapsize
 69    rng(j * settings.rseed)
 70    I(j, :) = ceil(rand(size(fM1.sd, 1), 1) * size(fM1.sd, 1));
 71end
 72
 73% Calculate Si and SiT for each score type and perform bootstrapping
 74for n = 1:size(scores_list,2)
 75    fM1h = fM1.(scores_list(n));
 76    fM2h = fM2.(scores_list(n));
 77    fNh = fN.(scores_list(n));
 78
 79    % Calculate Si and SiT without bootstrapping
 80    [Sih,SiTh] = calcSobolSaltelli(fM1h, fM2h, fNh, settings);
 81    Si.(scores_list(n)) = Sih;
 82    SiT.(scores_list(n)) = SiTh;
 83
 84    % Initialize variables for bootstrapped Si and SiT
 85    SiQh = cell(1, settings.gsabootstrapsize);
 86    SiTQh = cell(1, settings.gsabootstrapsize);
 87
 88    % Perform bootstrapping for the current score type
 89    parfor j = 1:settings.gsabootstrapsize
 90        [SiQh{j},SiTQh{j}] = bootstrap_q(fM1h, fM2h, fNh, settings, j, I);
 91    end%parfor
 92
 93    % Store bootstrapped Si and SiT values in the output structures
 94    for j=1:settings.gsabootstrapsize
 95        SiQ.(scores_list(n))(j, :, :) = SiQh{j};
 96        SiTQ.(scores_list(n))(j, :, :) = SiTQh{j};
 97    end
 98end
 99end%function
100
101function [Si,SiT] = bootstrap_q(fM1, fM2, fN, settings, j, I)
102    % Create bootstrapped samples using the generated indices
103    fM1q = fM1(I(j, :), :);
104    fM2q = fM2(I(j, :), :);
105    fNq = fN(I(j, :), :, :);
106
107    % Calculate Si and SiT for the bootstrapped samples
108    [Si, SiT] = calcSobolSaltelli(fM1q, fM2q, fNq, settings);
109end
110
111function [Si, SiT] = calcSobolSaltelli(fM1, fM2, fN, settings)
112% Calculates Si and SiT using the Sobol-Saltelli method.
113% Code inspired by Geir Halnes et al. 2009 paper. (Halnes, Geir, et al. J.
114% comp. neuroscience 27.3 (2009): 471.)
115
116% Get the dimensions of the input data
117[Nsamples, Nvars, Npars] = size(fN);
118
119% Optionally subtract the mean to stabilize the model
120if(settings.sasubmean)
121    fM1 = fM1 - mean(fM1, 1);
122    fM2 = fM2 - mean(fM2, 1);
123    for i = 1: Npars
124        fN(:, :, i) =  fN(:,:,i) - mean(fN(:, :, i), 1);
125    end
126end
127
128% Calculate EY2 and variances
129EY2 = mean(fM1 .* fM2); % Valid definition (see Halnes et. al. Appendix)
130VY = sum(fM1 .^ 2)/(Nsamples - 1) - EY2;
131VYT = sum(fM2 .^ 2)/(Nsamples - 1) - EY2;
132
133% Initialize Si and SiT arrays
134Si = zeros(Nvars, Npars);
135SiT= zeros(Nvars, Npars);
136
137% Calculate Si and SiT for each parameter
138for i = 1: Npars
139    Si(:, i) = (sum(fM1 .* fN(:, :, i))/(Nsamples - 1) - EY2) ./ VY;
140    SiT(:, i) = 1 - (sum(fM2 .* fN(:, :, i))/(Nsamples - 1) - EY2) ./ VYT;
141end
142end
143
144function results = remove_sim_error(results, settings)
145% Removes any simulation errors present in the input data.
146error_indices = any(results.fM1.sd >= settings.errorscore, 2) | ...
147                any(results.fM2.sd >= settings.errorscore, 2) | ...
148                any(any(results.fN.sd >= settings.errorscore, 3), 2);
149
150% Remove error rows from all fields of the input data
151fields = fieldnames(results.fM1);
152for i = 1:numel(fields)
153    results.fM1.(fields{i})(error_indices, :) = [];
154    results.fM2.(fields{i})(error_indices, :) = [];
155    results.fN.(fields{i})(error_indices, :, :) = [];
156end
157end

Takes the matrices fM1, fM2, and fN and calculates sensitivity indexes. It calculates indexes based on the following oputputs of the f_sim_score function:

Code modified from the Geir Halnes et al. 2009 paper.

References

Halnes, G., Ulfhielm, E., Ljunggren, E.E., Kotaleski, J.H. and Rospars, J.P., 2009. Modelling and sensitivity analysis of the reactions involving receptor, G-protein and effector in vertebrate olfactory receptor neurons. Journal of Computational Neuroscience, 27(3), p.471.