THE AUDITORY MODELING TOOLBOX

Applies to version: 1.6.0

View the help

Go to function

frambi_likelihood
Log-likelihood of observed responses given the model parameters

Program code:

function loglik = frambi_likelihood(data, par, agent, environment, options)
%frambi_likelihood Log-likelihood of observed responses given the model parameters
%
%   Usage: loglik = frambi_likelihood(data, par, agent, environment, options)
%
%   Input parameters:
%     data       : Structure describing model variables and behavioral data:
% 
%                  - variables*: Matrix with independent variables (in columns) for each trial (in rows).
% 
%                  - variablenames*: Cell array with names of the variables.
% 
%                  - responses*: Matrix with behavioral responses (in columns) for each trial (in rows).
%                    The responses in each row must have been obtained in the experiment condition
%                    described by the same row in variables.
%
%     par        : Matrix with parameter values to be used by the agent. 
%                  This matrix is usually created by the optimizer.
%                  The values might be transformed according to agent.state.parameters.
%
%     agent      : Structure representing the FrAMBI agent. See the general description of 
%                  FrAMBI for more details. See EXP_BARUMERLI2024 for an example. 
%
%     environment: Structure representing the FrAMBI environment. See the general description 
%                  of FrAMBI for more details. See EXP_BARUMERLI2024 for an example. 
%
%     options    : Structure representing the FrAMBI options. It needs to contain
%                  the structure sample with the following fields:
%
%                  - iterations*: Number of simulations of the same trial 
%                    used to estimate the response distribution;
% 
%                  - bandwidth*: Bandwidth (in the same units as data.responses*) 
%                    of the distribution smoothing. If zero, smoothing is disabled. 
% 
%
%   Output parameters:
%     loglik  : Log-likelihood of the model given the data and parameters.
%
%
%   FRAMBI_LIKELIHOOD(..) calculates the log-likelihood of data given the parameter 
%   values par of the agent. It handles parameter transformations, assigns 
%   parameter values to the agent, and uses unique conditions to optimize computation. 
%   
%   If options.sample.bandwidth is larger than 0, a kernel-based method 
%   is used for the simulation, i.e., FRAMBI_SIMULATE is called 
%   options.sample.iterations times. 
%   If options.sample.bandwidth is not larger than 0, a direct sampling 
%   is done by performing simulations with FRAMBI_SAMPLE. 
%
%   FRAMBI_LIKELIHOOD(..) is usually called by the optimizer, which setup 
%   is done in FRAMBI_ESTIMATE. 
%
%   *Note:* In the current implementation, only scalar responses from FRAMBI_SIMULATE 
%   can be processed. 
%
%   See also: frambi_estimate frambi_simulate frambi_sample exp_barumerli2024
%
%   References:
%     R. Barumerli and P. Majdak. FrAMBI: A Software Framework for Auditory
%     Modeling Based on Bayesian Inference. under review at Neuroinformatics,
%     2024.
%     
%
%   Url: http://amtoolbox.org/amt-1.6.0/doc/frambi/frambi_likelihood.php


%   #Requirements: MATLAB M-Signal
%   #Author: Roberto Barumerli (2023): Original implementation. 
%   #Author: Roberto Barumerli (2024): Integration in the AMT.
%   #Author: Michael Mihocic (2024): Documentation fixed. 
%   #Author: Piotr Majdak (2024): Adaptations for the AMT 1.6.

% This file is licensed unter the GNU General Public License (GPL) either 
% version 3 of the license, or any later version as published by the Free Software 
% Foundation. Details of the GPLv3 can be found in the AMT directory "licences" and 
% at <https://www.gnu.org/licenses/gpl-3.0.html>. 
% You can redistribute this file and/or modify it under the terms of the GPLv3. 
% This file is distributed without any warranty; without even the implied warranty 
% of merchantability or fitness for a particular purpose. 


% initialize some stuff
par = par(:);
iterations = options.sample.iterations;
sim_resp = zeros(iterations, 1);

% handle free parameters
parameter_names = fieldnames(agent.state.parameters);
free_params = structfun(@(p) p.fit, agent.state.parameters); 
parameter_names(~free_params) = [];
par = frambi_parameters('itransform', ...
                    agent.state.parameters, parameter_names, par);
agent.state.parameters = frambi_parameters('assign', ...
                    agent.state.parameters, parameter_names, par);

% initialize agent
agent.state = agent.model.initialize(agent.state);
options.initialization = false;
responses = data.responses;
assert(~isempty(responses), 'response column from data is empty')

% Optimize if a single variable: remove repetitions
if size(data.variables, 2) == 1
    % overwrite data.variables with only unique values 
    levels = data.variables;
    levels_u = unique(levels);
    data.variables = levels_u; 
end

N = size(data.variables, 1);
loglik_trial = zeros(N, 1);
for ii = 1:N
    % assign parameter values
    for jj = 1:length(data.variablenames)
        environment.state.parameters.(data.variablenames{jj}).value = data.variables(ii, jj); 
    end
    
    environment.state = environment.model.initialize(environment.state);
    
    if options.sample.bandwidth > 0 % Calculations with smoothing applied
        % simulate individual responses
        for m=1:iterations
            sim_resp(m,1) = frambi_simulate(agent, environment, options);
        end
        
        % select responses
        if size(data.variables, 2) == 1
            resps = responses(levels_u(ii, 1) == levels);
        else
            resps = responses(ii,1);
        end

        % compute likelihood with actual response 
        % (equivalent to filter the histogram with a gaussian kernel, but faster because not requiring histcounts)
        loglik_trial(ii, 1) = sum(log(mean(normpdf(resps', sim_resp, options.sample.bandwidth), 1)+eps));
        
        else % Calculations without smoothing
            % use sample to simulate response distribution
            [~, distribution, support] = frambi_sample(agent, environment, options);
            % select responses
            if size(data.variables, 2) == 1
                resps = responses(levels_u(ii, 1) == levels);
            else
                resps = responses(ii,1);
            end
            
            for r = 1:length(resps)
                [~, m] = min(abs(support-resps(r)));
                loglik_trial(ii, 1) = loglik_trial(ii, 1) + log(distribution(m)+eps);
            end
    end
end

loglik = sum(loglik_trial);
assert(~isnan(loglik));