% defining initial valus for the parameters:
% mdl1) (4 parameters) 'mdl1': (1) one alpha (2) one constant weight: w
% (3) painerror (4) experror
load('table_pain_4mdls.mat');
subj_num_new=unique(table_pain.src_subject_id);
% for ii=1:length(subj_num_new)
% 0) get the data matrix for the subject:
Data=table_pain(table_pain.src_subject_id==sub_no,:);
Data_lowpain=Data(strcmp(Data.param_stimulus_type, 'low_stim'),:);
Data_medpain=Data(strcmp(Data.param_stimulus_type, 'med_stim'),:);
Data_highpain=Data(strcmp(Data.param_stimulus_type, 'high_stim'),:);
% 1) Calculate the Noxious inputs (1, 2, or 3) for each of the participants based on the average ratings of that participant on each of those real Noxious inputs. For example, to calculate the subjective Noxious 1 (48) for participant 1, we average the ratings on the trials that the participant has got 48-degree heat (and the pain rating is reported on 0-180 scale).
% From pain rating vector:
N(ii,1)=nanmean(Data_lowpain.event04_actual_angle);
N(ii,2)=nanmean(Data_medpain.event04_actual_angle);
N(ii,3)=nanmean(Data_highpain.event04_actual_angle);
% Noxious input vecotr for each participant:
Noxious{ii} = zeros(length(Data.param_stimulus_type),1);
Noxious{ii}(strcmp(Data.param_stimulus_type,'low_stim')) = N(ii,1);
Noxious{ii}(strcmp(Data.param_stimulus_type,'med_stim')) = N(ii,2);
Noxious{ii}(strcmp(Data.param_stimulus_type,'high_stim')) = N(ii,3);
% High_Cue vector for each participant:
High_Cue{ii} = strcmp(Data.param_cue_type,'high_cue');
% 2, 3, 4) Calculate the Expectation and Pain matrices based on each model
% Calculate the nll based on the above equation
% For jj=1:20 % run 20 times to avoid being stuck in the
% Optimize the nll function and get the parameters and nll for each model and pick the best parameter
% 5) Calculate aic, bic, and nll or each model and compare different models together
Noxious_subj=Noxious{ii};
High_Cue_subj=High_Cue{ii};
fit_fun=@(xpar)NLL(xpar, Data, model_type{1},Noxious_subj,High_Cue_subj);
initpar=lb{m}+rand(1,length(lb{m})).*(ub{m}-lb{m});
[qpar{ii,m,jj}, nll(ii,m,jj), bic(ii,m,jj), nlike, aic(ii,m,jj)]=fit_fun_2(Data,fit_fun,initpar,lb{m},ub{m});
function [negloglike]=NLL(xpar, Data, model_type,Noxious, High_Cue)
%PURPOSE: NLL calculation for parameter estimation, called by fit_fun().
% xpar: input parameters depending on the model type
% Data: Data for each subject
% model_type: 'mdl1: one alpha, constant weight', 'mdl2: two alpha, constant weight',
% 'mdl3: one alpha, changing weight' ,'mdl4: two alpa, chanigng weight',
% 'mdl5: one alpha, changing weight (w on N)' ,'mdl6: two alpa, chanigng weight (w on N)'
% Noxious: a vector of the Noxious inputs for different trials
% High_Cue: a vector of cues (high (1) vs low(0)) for different trials
% negloglike: negaive log likelihood
num_trial=size(Data,1); % number of trials
NLLs = zeros(1,num_trial);
NLLs_pain = zeros(1,num_trial);
NLLs_exp = zeros(1,num_trial);
rng('shuffle'); % random number generator shuffle
idx_ExpH = find(strcmp(Data.param_cue_type, 'high_cue'), 1);
idx_ExpL = find(strcmp(Data.param_cue_type, 'low_cue'), 1);
first_exp_high_cue = Data.event02_expect_angle(idx_ExpH);
first_exp_low_cue = Data.event02_expect_angle(idx_ExpL);
% mdl1: one alpha, constant weight:
% defingin expectations and pains:
ExpectationH=first_exp_high_cue*ones(num_trial+1,1); % Expectation_highCue matrix
ExpectationL=first_exp_low_cue*ones(num_trial+1,1); % Expectation_lowCue matrix
Pain=zeros(num_trial+1,1); % Pain matrix
PE=zeros(num_trial+1,1); % Pain predcition error matrix
PainH=zeros(num_trial+1,1); % Pain matrix for high cues
PainL=zeros(num_trial+1,1); % Pain matrix for low cues
Pain(ii,1)=(1-w)*Noxious(ii,1)+w*ExpectationH(ii,1);
PE(ii,1)=Pain(ii,1)-ExpectationH(ii,1);
ExpectationH(ii+1,1)=ExpectationH(ii,1)+alpha*PE(ii,1);
ExpectationL(ii+1,1)=ExpectationL(ii,1);
NLLs_pain(ii) = -log(max(realmin,normpdf(Data.event04_actual_angle(ii),Pain(ii,1),painerror))); % pain log likelihood
NLLs_exp(ii) = -log(max(realmin,normpdf(Data.event02_expect_angle(ii),ExpectationH(ii,1),experror))); % exp log likelihood
NLLs(ii)=NLLs_pain(ii)+NLLs_exp(ii);
Pain(ii,1)=(1-w)*Noxious(ii,1)+w*ExpectationL(ii,1);
PE(ii,1)=Pain(ii,1)-ExpectationL(ii,1);
ExpectationL(ii+1,1)=ExpectationL(ii,1)+alpha*PE(ii,1);
ExpectationH(ii+1,1)=ExpectationH(ii,1);
NLLs_pain(ii) = -log(max(realmin,normpdf(Data.event04_actual_angle(ii),Pain(ii,1),painerror))); % pain log likelihood
NLLs_exp(ii) = -log(max(realmin,normpdf(Data.event02_expect_angle(ii),ExpectationL(ii,1),experror))); % exp log likelihood
NLLs(ii)=NLLs_pain(ii)+NLLs_exp(ii);
% calculating NLL (based on NLL_exp and NLL_pain)
negloglike=negloglike+NLLs(ii); % calculate log likelihood
% 'mdl2: two alpha, constant weight':
% defingin expectations and pains:
ExpectationH=first_exp_high_cue*ones(num_trial+1,1); % Expectation_highCue matrix
ExpectationL=first_exp_low_cue*ones(num_trial+1,1); % Expectation_lowCue matrix
Pain=zeros(num_trial+1,1); % Pain matrix
PE=zeros(num_trial+1,1); % Pain predcition error matrix
PainH=zeros(num_trial+1,1); % Pain matrix for high cues
PainL=zeros(num_trial+1,1); % Pain matrix for low cues
Pain(ii,1)=(1-w)*Noxious(ii,1)+w*ExpectationH(ii,1);
PE(ii,1)=Pain(ii,1)-ExpectationH(ii,1);
ExpectationH(ii+1,1)=ExpectationH(ii,1)+alpha*PE(ii,1);
ExpectationL(ii+1,1)=ExpectationL(ii,1);
NLLs_pain(ii) = -log(max(realmin,normpdf(Data.event04_actual_angle(ii),Pain(ii,1),painerror))); % pain log likelihood
NLLs_exp(ii) = -log(max(realmin,normpdf(Data.event02_expect_angle(ii),ExpectationH(ii,1),experror))); % exp log likelihood
NLLs(ii)=NLLs_pain(ii)+NLLs_exp(ii);
Pain(ii,1)=(1-w)*Noxious(ii,1)+w*ExpectationL(ii,1);
PE(ii,1)=Pain(ii,1)-ExpectationL(ii,1);
ExpectationL(ii+1,1)=ExpectationL(ii,1)+alpha*PE(ii,1);
ExpectationH(ii+1,1)=ExpectationH(ii,1);
NLLs_pain(ii) = -log(max(realmin,normpdf(Data.event04_actual_angle(ii),Pain(ii,1),painerror))); % pain log likelihood
NLLs_exp(ii) = -log(max(realmin,normpdf(Data.event02_expect_angle(ii),ExpectationL(ii,1),experror))); % exp log likelihood
NLLs(ii)=NLLs_pain(ii)+NLLs_exp(ii);
% calculating sum of squared errors (based on the sum of errors for Pain and expectation)
negloglike=negloglike+NLLs(ii); % calculate log likelihood
error('Unexpected model type')
function [qpar, negloglike, bic, nlike, aic]=fit_fun_2(Data,fit_fun,initpar,lb,ub)
%PURPOSE: Fit the choice behavior to a model using maximum likelihood estimate
%AUTHORS: AC Kwan 170518, Ethan Trepka 210220, Aryan Yazdanpanah
% stats: stats of the game
% fit_fun: the model to fit
% initpar: initial values for the parameters
% lb: lower bound values for the parameters (if used)
% ub: upper bound values for the parameters (if used)
% qpar: extracted parameters
% negloglike: negative log likelihood
% bic: Bayesian information criterion
% nlike: normalized likelihood
op=optimset('fminsearch');
% func_handle = str2func(fit_fun);
% [qpar, negloglike, exitflag]=fminsearch(func_handle, initpar, op, [stats.c stats.r]);
% [qpar, negloglike, exitflag]=fmincon(func_handle, initpar, [], [], [], [], lb, ub, [], op, [stats.c stats.r]);
[qpar, negloglike, exitflag]=fmincon(fit_fun, initpar, [], [], [], [], lb, ub, [], op);
qpar=nan(size(qpar)); %did not converge to a solution, so return NaN
%% BIC, bayesian information criterion
%L = negative log-likelihood, k = number of parameters, N = number of trials
%larger BIC value is worse because more parameters is worse, obviously
bic = 2*negloglike + numel(initpar)*log(size(Data,1));
%% AIC, Aikake information crieterion
%L = negative log-likelihood, k = number of parameters
aic = 2*negloglike + numel(initpar)*2;
%(Ito and Doya, PLoS Comp Biol, 2015)
nlike = exp(-1*negloglike)^(1/(size(Data,1)));