-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpcm_fitModelIndivid.m
227 lines (208 loc) · 9.43 KB
/
pcm_fitModelIndivid.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
function [T,theta_hat,G_pred,INFO]=pcm_fitModelIndivid(Y,M,partitionVec,conditionVec,varargin);
% function [T,theta_hat,G_pred]=pcm_fitModelIndivid(Y,M,partitionVec,conditionVec,varargin);
% Fits pattern component model(s) specified by M to data from a number of
% subjects.
% The model parameters are all individually fit.
%==========================================================================
% INPUT:
% Y: {#Subjects}.[#Conditions x #Voxels]
% Observed/estimated beta regressors from each subject.
% Preferably multivariate noise-normalized beta regressors.
%
% M: {#Models} Cell array with structure that defines model(s). Each
% may contain the fields
% .type: Type of the model to be fitted
% 'fixed': Fixed structure without parameter (except scale for each subject)
% 'component': G is a sum of linear components
% 'feature': G=A*A', with A a linear sum of weighted feature components
% 'nonlinear': Nonlinear model with own function to return derivatives
% 'freechol': Free model in Cholesky form
% .numGparams: Scalar that defines the number of parameters
% included in model.
% .theta0: Vector of starting theta parameters to calculate predicted
% model G.
% for more fields see the manual for model specification.
%
% partitionVec: {#Subjects} Cell array with partition assignment vector
% for each subject. Rows of partitionVec{subj} define
% partition assignment of rows of Y{subj}.
% Commonly these are the scanning run #s for beta
% regressors.
% If a single vector is provided, it is assumed to be the
% same for all subjects
%
% conditionVec: {#Subjects} Cell array with condition assignment vector
% for each subject. Rows of conditionVec{subj} define
% condition assignment of rows of Y{subj}.
% If a single vector is provided, it is assumed to me the
% same for all subjects.
% If the (elements of) conditionVec are matrices, it is
% assumed to be the design matrix Z, allowing the
% specification individualized models.
%
%--------------------------------------------------------------------------
% OPTION:
% 'runEffect': How to deal with effects that may be specific to different
% imaging runs:
% 'random': Models variance of the run effect for each subject
% as a seperate random effects parameter.
% 'fixed': Consider run effect a fixed effect, will be removed
% implicitly using ReML.
% 'none': No modeling of the run Effect (not recommended for fMRI data)
%
% 'isCheckDeriv: Check the derivative accuracy of theta params. Done using
% 'checkderiv'. This function compares input to finite
% differences approximations. See function documentation.
%
% 'fitAlgorithm': Either 'NR' or 'minimize' - provides over-write for
% model specific algorithms
% 'maxIteration': Number of max minimization iterations. Default is 1000.
%
% 'likeThres': Threshold for minimal change in the log-likelihood to
% stop iterations
% 'verbose': Optional flag to show display message in the command
% line. Default is 1. Setting to 2 gives more detailed
% feedback on pcm_NR
%
% 'S': Optional specific covariance structure of the noise
% {#Subjects} = cell array of N_s x N_s matrices
% or
% S(#Subjects).S and .invS: Structure of the N_s x N_s
% normal and inverse covariances matrices
%
% 'theta0': Cell array of starting values (same format as theta{m})
% 'fitScale': Fit a additional scale parameter for each subject?
% (0/1). Default is set to 0.
%--------------------------------------------------------------------------
% OUTPUT:
% T: Structure with following subfields:
% SN: Subject number
% likelihood: log-likelihood
% scale: Scale parameter (if fitscale = 1)-exp(theta_s)
% noise: Noise parameter- exp(theta_eps)
% run: Run parameter (if run = 'random')
% iterations: Number of interations for model fit
% time: Elapsed time in sec
%
% theta{m} Cell array of estimated model parameters, each a
% #params x #numSubj matrix
% G_pred{m} Cell array of estimated G-matrices under the model
runEffect = 'fixed';
isCheckDeriv = 0;
maxIteration = []; % MaxIteration - otherwise algorithm-specific defaults
likeThres = []; % if log-likelihood decreases less that this value, it stops (for pcm_NR)
verbose = 1; % 1: Indicating the subject 2: detailed feedback
S = [];
fitAlgorithm = [];
theta0 = [];
fitScale = 0;
pcm_vararginoptions(varargin,{'runEffect','isCheckDeriv','maxIteration',...
'verbose','S','fitAlgorithm','theta0','fitScale','likeThres'});
numSubj = numel(Y);
% Set options for fitting algorithm
fitOPT.verbose = verbose;
if (~isempty(maxIteration))
fitOPT.maxIteration = maxIteration;
end
if (~isempty(likeThres))
fitOPT.likeThres = likeThres;
end
% Determine number of models
if (~iscell(M))
M={M};
end
numModels = numel(M);
% Preallocate output structure
T.SN = [1:numSubj]';
% Determine optimal algorithm for each of the models
if (~isempty(fitAlgorithm))
for m=1:numModels
M{m}.fitAlgorithm = fitAlgorithm;
end
end
M = pcm_optimalAlgorithm(M);
% Set up all parameters for the upcoming fit
[Z,B,X,YY,S,N,P,G_hat,noise0,run0]=...
pcm_setUpFit(Y,partitionVec,conditionVec,'runEffect',runEffect,'S',S);
% Loop over subject and provide inidivdual fits
for s = 1:numSubj
% Now loop over models
for m = 1:length(M)
if (verbose)
if isfield(M{m},'name');
fprintf('Fitting Subj: %d model:%s\n',s,M{m}.name);
else
fprintf('Fitting Subj: %d model:%d\n',s,m);
end
end
tic;
% Get starting guess for theta0 is not provided
if (~isfield(M{m},'theta0'))
M{m}.theta0=pcm_getStartingval(M{m},G_hat(:,:,s));
end
% If naive noise ceiling model, use G_hat
if strcmp(M{m}.type,'freedirect')
G0 = pcm_makePD(mean(G_hat,3));
M{m}.numGparams=0;
M{m}.Gc = G0;
else
G0 = pcm_calculateG(M{m},M{m}.theta0);
end
g0 = G0(:);
% If fitting scale parameterm get initial estimate using OLS
if (fitScale)
g_hat = G_hat(:,:,s);
g_hat = g_hat(:);
scaling = (g0'*g_hat)/(g0'*g0);
if ((scaling<10e-5)||~isfinite(scaling)); scaling = 10e-5; end % Enforce positive scaling
scale0(s,m) = log(scaling);
end
if (numel(theta0)<m || size(theta0{m},2)<s)
th0m = [M{m}.theta0;noise0(s)];
if(fitScale)
th0m = [th0m;scale0(s,m)];
end
if (strcmp(runEffect,'random'))
th0m = [th0m;run0(s)];
end
theta0{m}(:,s)=th0m;
end;
% Set options for likelihood function
OPT.fitScale = fitScale;
OPT.runEffect=B{s};
if (~isempty(S))
OPT.S = S(s);
end;
% Now do the fitting, using the preferred optimization routine
switch (M{m}.fitAlgorithm)
case 'minimize' % Use minimize to find maximum liklhood estimate runEffect',B{s});
fcn = @(x) pcm_likelihoodIndivid(x,YY{s},M{m},Z{s},X{s},P(s),OPT);
if (isempty(maxIteration))
maxIteration=1000;
end
[theta_hat{m}(:,s),fX,T.iterations(s,m)] = ...
minimize(theta0{m}(:,s), fcn, maxIteration);
T.likelihood(s,m) = -fX(end); %invert the sign
case 'NR'
fcn = @(x) pcm_likelihoodIndivid(x,YY{s},M{m},Z{s},X{s},P(s),OPT);
[theta_hat{m}(:,s),T.likelihood(s,m),T.iterations(s,m),T.reg(s,m),INFO.regH{s,m},INFO.thetaH{s,m}]= ...
pcm_NR(theta0{m}(:,s),fcn,fitOPT);
end
G_pred{m}(:,:,s) = pcm_calculateG(M{m},theta_hat{m}(1:M{m}.numGparams,s));
T.noise(s,m) = exp(theta_hat{m}(M{m}.numGparams+1,s));
if (fitScale)
T.scale(s,m) = exp(theta_hat{m}(M{m}.numGparams+2,s));
end
if strcmp(runEffect,'random')
T.run(s,m) = exp(theta_hat{m}(M{m}.numGparams+2+fitScale,s));
end
T.time(s,m) = toc;
% This is an optional check if the dervivate calculation is correct
if (isCheckDeriv)
d = pcm_checkderiv(fcn,theta_hat{m}(:,s)-0.001,0.00001);
fprintf('discrepency :%d\n',d);
keyboard;
end
end % for each model
end % for each subject
INFO.theta0=theta0;