-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpcm_likelihoodIndivid.m
165 lines (146 loc) · 6.32 KB
/
pcm_likelihoodIndivid.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
function [negLogLike,dnl,d2nl] = pcm_likelihoodIndivid(theta,YY,M,Z,X,P,varargin);
% function [negLogLike,dnl,d2nl] = pcm_likelihoodIndivid(theta,YY,M,Z,X,P,varargin);
% Returns negative log likelihood for the model, and the derivatives in
% respect to the model parameters for an individual subject / dataset
% It works very similar to pcm_likelihood, only that a run-effect can be
% specified to be modelled as an extra random effect
%
% INPUT:
% theta: Vector of (log-)model parameters: These include model
% parameters, noise parameter, and (optional) run parameter
% YY: NxN Matrix of outer product of the activity data (Y*Y')
% M: Model structure with fields
% Model.type: fixed, component, feature, nonlinear
% Model.numGparams: Number of model parameters (without the noise or run parameter)
% ...
% Z: NxK Design matrix - relating the trials (N) to the random effects (K)
% X: Fixed effects design matrix - will be accounted for by ReML
% P: Number of voxels
% VARARGIN:
% 'runEffect',B: design matrices for the run effect,
% which is modelled as a individual subject-specific random effect.
% 'S': Explicit noise covariance matrix structure matrix. The For speed,
% this is a cell array that contains
% S.S: Structure of noise
% S.invS: inverse of the noise covariance matrix
% 'fitScale': Scale parameter for each subject?
% 'scalePrior': Variance of Gaussian prior on scale parameter (if
% used)
%
% OUTPUT:
% negLogLike: Negative Log likelihood of all subject's data
% We use the negative log liklihood to be able to plug the function into
% minimize or other optimisation routines.
% dnl : Derivative of the negative log-likelihood in respect to
% the parameters
% d2nl : Expected second derivative of the negative
% log-likelihood
%
% Joern Diedrichsen & Atsushi Yokoi, 6/2016, [email protected]
%
N = size(YY,1);
K = size(Z,2);
OPT.S = [];
OPT.runEffect =[];
OPT.fitScale =0;
OPT=pcm_getUserOptions(varargin,OPT,{'S','runEffect','fitScale'});
% Get G-matrix and derivative of G-matrix in respect to parameters
if (isstruct(M))
[G,dGdtheta] = pcm_calculateG(M,theta(1:M.numGparams));
else
G=M;
M=[];
M.numGparams=0;
end
% If Run effect is to ne modelled as a random effect - add to G and
% design matrix
noiseParam = theta(M.numGparams+1);
if (OPT.fitScale)
scaleParam = theta(M.numGparams+2);
else
scaleParam = 0;
end
Gs = G*exp(scaleParam); % Scale the subject G matrix up by individual scale parameter
if (~isempty(OPT.runEffect))
numRuns = size(OPT.runEffect,2);
runParam = theta(M.numGparams+2+OPT.fitScale); % Subject run effect parameter
Gs = pcm_blockdiag(Gs,eye(numRuns)*exp(runParam)); % Include run effect in G
Z = [Z OPT.runEffect]; % Include run effect in design matrix
else
numRuns = 0; % No run effects modelled
end;
% Find the inverse of V - while dropping the zero dimensions in G
Gs = (Gs+Gs')/2; % Symmetrize
[u,s] = eig(Gs);
dS = diag(s);
idx = dS>(10*eps); % Increased to 10*eps from 2*eps
Zu = Z*u(:,idx);
% Apply the matrix inversion lemma. The following statement is the same as
% V = (Z*Gs*Z' + S.S*exp(noiseParam));
% iV = pinv(V);
if (isempty(OPT.S))
iV = (eye(N)-Zu/(diag(1./dS(idx))*exp(noiseParam)+Zu'*Zu)*Zu')./exp(noiseParam); % Matrix inversion lemma
else
iV = (OPT.S.invS-OPT.S.invS*Zu/(diag(1./dS(idx))*exp(noiseParam)+Zu'*OPT.S.invS*Zu)*Zu'*OPT.S.invS)./exp(noiseParam); % Matrix inversion lemma
end
iV = real(iV); % sometimes iV gets complex
% For ReML, compute the modified inverse iVr
if (~isempty(X))
iVX = iV * X;
iVr = iV - iVX*((X'*iVX)\iVX');
else
iVr = iV;
end
% Computation of (restricted) likelihood
ldet = -2* sum(log(diag(chol(iV)))); % Safe computation of the log determinant (V) Thanks to code from D. lu
l = -P/2*(ldet)-0.5*traceABtrans(iVr,YY);
if (~isempty(X)) % Correct for ReML estimates
l = l - P*sum(log(diag(chol(X'*iV*X)))); % - P/2 log(det(X'V^-1*X));
end
negLogLike = -l; % Invert sign
% Calculate the first derivative
if (nargout>1)
A = iVr*Z; % Precompute some matrices
B = YY*iVr;
% Get the derivatives for all the parameters
for i = 1:M.numGparams
iVdV{i} = A*pcm_blockdiag(dGdtheta(:,:,i),zeros(numRuns))*Z'*exp(scaleParam);
dLdtheta(i,1) = -P/2*trace(iVdV{i})+1/2*traceABtrans(iVdV{i},B);
end
% Get the derivatives for the Noise parameters
i = M.numGparams+1; % Which number parameter is it?
if (isempty(OPT.S))
dVdtheta{i} = eye(N)*exp(noiseParam);
else
dVdtheta{i} = OPT.S.S*exp(noiseParam);
end;
iVdV{i} = iVr*dVdtheta{i};
dLdtheta(i,1) = -P/2*trace(iVdV{i})+1/2*traceABtrans(iVdV{i},B);
% Get the derivatives for the scaling parameters
if (OPT.fitScale)
i = M.numGparams+2; % Which number parameter is it?
iVdV{i} = A*pcm_blockdiag(G,zeros(numRuns))*Z'*exp(scaleParam);
dLdtheta(i,1) = -P/2*trace(iVdV{i})+1/2*traceABtrans(iVdV{i},B);
% dLdtheta(i,1) = dLdtheta(i,s) - scaleParam/OPT.scalePrior; % add prior to scale parameter
end;
% Get the derivatives for the block parameter
if (~isempty(OPT.runEffect) && ~isempty(OPT.runEffect))
i = M.numGparams+2+OPT.fitScale; % Which number parameter is it?
%C = A*pcm_blockdiag(zeros(size(G,1)),eye(numRuns));
iVdV{i} = A*pcm_blockdiag(zeros(K),eye(numRuns))*Z'*exp(runParam);
dLdtheta(i,1) = -P/2*trace(iVdV{i})+1/2*traceABtrans(iVdV{i},B);
end;
% invert sign
dnl = -dLdtheta;
numTheta=i;
end;
% Calculate expected second derivative?
if (nargout>2)
for i=1:numTheta
for j=i:numTheta;
d2nl(i,j)=-P/2*traceAB(iVdV{i},iVdV{j}); % Fixed 11/2020
d2nl(j,i)=d2nl(i,j);
end;
end;
d2nl=-d2nl;
end;