function [DCM] = spm_dcm_fnirs_estimate(P) % Estimate parameters of a DCM for fNIRS data % FORMAT [DCM] = spm_dcm_fnirs_estimate(P) % % P - Name of DCM file % % DCM - DCM structure % % Expects %-------------------------------------------------------------------------- % DCM.a % switch on endogenous connections % DCM.b % switch on bilinear modulations % DCM.c % switch on exogenous connections % DCM.d % switch on nonlinear modulations % DCM.U % exogenous inputs % DCM.Y.y % responses % DCM.Y.X0 % confounds % DCM.Y.Q % array of precision components % DCM.n % number of regions % DCM.v % number of scans % % Options %-------------------------------------------------------------------------- % DCM.options.two_state % two regional populations (E and I) % DCM.options.stochastic % fluctuations on hidden states % DCM.options.centre % mean-centre inputs % DCM.options.nonlinear % interactions among hidden states % DCM.options.nograph % graphical display % DCM.options.induced % switch for CSD data features % DCM.options.P % starting estimates for parameters % DCM.options.hidden % indices of hidden regions % DCM.options.nmax % maximum number of (effective) nodes % DCM.options.nN % maximum number of iterations % % Evaluates: %-------------------------------------------------------------------------- % DCM.M % Model structure % DCM.Ep % Condition means (parameter structure) % DCM.Cp % Conditional covariances % DCM.Vp % Conditional variances % DCM.Pp % Conditional probabilities % DCM.H1 % 1st order hemodynamic kernels % DCM.H2 % 2nd order hemodynamic kernels % DCM.K1 % 1st order neuronal kernels % DCM.K2 % 2nd order neuronal kernels % DCM.R % residuals % DCM.y % predicted data % DCM.T % Threshold for Posterior inference % DCM.Ce % Error variance for each region % DCM.F % Free-energy bound on log evidence % DCM.ID % Data ID % DCM.AIC % Akaike Information criterion % DCM.BIC % Bayesian Information criterion % % Note: This code % (i) shows best results with spm_nlsi_GN.m (version 6481), % (ii) is based on spm_dcm_estimate.m by Karl Friston. %__________________________________________________________________________ % Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % Will Penny & Sungho Tak % $Id: spm_dcm_fnirs_estimate.m 7060 2017-04-18 16:44:46Z will $ %-------------------------------------------------------------------------- %-Load DCM structure if ~nargin %---------------------------------------------------------------------- %-Display model details Finter = spm_figure('GetWin','Interactive'); set(Finter,'name','Dynamic Causal Modelling') %---------------------------------------------------------------------- %-Get DCM [P, sts] = spm_select(1,'^DCM.*\.mat$','select DCM_???.mat'); if ~sts, DCM = []; return; end spm('Pointer','Watch') spm('FigName','Estimation in progress'); end if isstruct(P) DCM = P; P = ['DCM-' date '.mat']; else load(P) end %-------------------------------------------------------------------------- % check options try, DCM.options.two_state; catch, DCM.options.two_state = 0; end try, DCM.options.stochastic; catch, DCM.options.stochastic = 0; end try, DCM.options.nonlinear; catch, DCM.options.nonlinear = 0; end try, DCM.options.centre; catch, DCM.options.centre = 0; end try, DCM.options.nmax; catch, DCM.options.nmax = 8; end try, DCM.options.nN; catch, DCM.options.nN = 32; end try, DCM.options.hidden; catch, DCM.options.hidden = []; end try, DCM.n; catch, DCM.n = size(DCM.a,1); end try, DCM.v; catch, DCM.v = size(DCM.Y.y,1); end try, M.nograph = DCM.options.nograph; catch, M.nograph = spm('CmdLine');end %-------------------------------------------------------------------------- % Unpack U = DCM.U; % inputs Y = DCM.Y; % responses v = DCM.v; % number of scans n = DCM.n; % number of sources of interest m = size(U.u,2); % number of inputs %-------------------------------------------------------------------------- % load fNIRS measurements ny = size(Y.P.fname.nirs, 1); nch = size(Y.P.rois, 2); for i = 1:ny raw = load(deblank(Y.P.fname.nirs{i, 1})); y{i,1} = reshape(raw.Y.od(:, Y.P.rois, :), raw.P.ns, nch * size(Y.P.wav, 2)); end Y.P.nch = nch; %-------------------------------------------------------------------------- % apply temporal filtering addpath(fullfile(spm('Dir'), 'external', 'fieldtrip', 'preproc')); forder = 5; ftype = 'but'; fdir = 'twopass'; if strcmpi(Y.K.type, 'Butterworth IIR') nf = size(Y.K.cutoff, 1); for i = 1:ny for j = 1:nf if Y.K.cutoff(j, 1) == 0 % high pass filter y{i,1} = ft_preproc_highpassfilter(y{i,1}', Y.P.fs, Y.K.cutoff(j, 2), forder, ftype, fdir)'; else % band-stop filter y{i,1} = ft_preproc_bandstopfilter(y{i,1}', Y.P.fs , Y.K.cutoff(j, :), forder, ftype, fdir)'; end end end end %-------------------------------------------------------------------------- % concatenate the time series y = cell2mat(y); %-------------------------------------------------------------------------- % average time series over trials if strcmpi(Y.W.type, 'y') nc = size(Y.W.onsets, 2); for i = 1:nc, wy{i,1} = spm_fnirs_wavg(y, round(Y.W.onsets{i}.*Y.P.fs)+1, round(Y.W.durations(i).*Y.P.fs)); wy{i,1} = wy{i,1} - ones(size(wy{i,1},1),1) * wy{i,1}(1,:); end y = cell2mat(wy); % remove very slow drift which may exist in averaged time series hcutoff = 0.008; % default cutoff freq [Hz] y = ft_preproc_highpassfilter(y', Y.P.fs, hcutoff, forder, ftype, fdir)'; end Y.P.ns = size(y, 1); nout = size(Y.P.wav, 2) * nch; Y.y = y; DCM.Y = Y; % update %-------------------------------------------------------------------------- % detrend outputs (and inputs) % Y.y = spm_detrend(Y.y); if DCM.options.centre U.u = spm_detrend(U.u); end % generate regressors for confounding effects %-------------------------------------------------------------------------- X0 = []; switch Y.C.type case 'DCT' nf = size(Y.C.period, 1); for i = 1:nf period = Y.C.period(i,:); Lorder = fix(2*(v./Y.P.fs)/period(1,2) + 1); Horder = fix(2*(v./Y.P.fs)/period(1,1) + 1); X0_t = spm_dctmtx(v, Horder); X0_t = X0_t(:, Lorder:end); X0 = [X0 X0_t]; end case 'User' X0 = [X0 Y.C.X0]; end X0 = [X0 ones(v, 1)]; Y.X0 = X0; % calculate sensitivity matrix using precalculated Green's functions %-------------------------------------------------------------------------- A = spm_fnirs_sensitivity(DCM); %-------------------------------------------------------------------------- % create priors %-------------------------------------------------------------------------- % check DCM.d (for nonlinear DCMs) try DCM.options.nonlinear = logical(size(DCM.d,3)); catch DCM.d = zeros(n,n,0); DCM.options.nonlinear = 0; end %-------------------------------------------------------------------------- % specify parameters for spm_int_D (ensuring updates every second or so) if DCM.options.nonlinear M.IS = 'spm_int_D'; M.nsteps = round(max(Y.dt,1)); M.states = 1:n; else M.IS = 'spm_int'; end %-------------------------------------------------------------------------- % check for endogenous DCMs, with no exogenous driving effects if isempty(DCM.c) || isempty(U.u) DCM.c = zeros(n,1); DCM.b = zeros(n,n,1); U.u = zeros(v,1); U.name = {'null'}; end if ~any(spm_vec(U.u)) || ~any(spm_vec(DCM.c)) DCM.options.stochastic = 1; end %-------------------------------------------------------------------------- % priors (and initial states) [pE,pC,x] = spm_dcm_fnirs_priors(DCM); try, M.P = DCM.options.P; end % initial parameters try, pE = DCM.options.pE; end % prior expectation try, pC = DCM.options.pC; end % prior covariance %-------------------------------------------------------------------------- % eigenvector constraints on pC for large models if n > DCM.options.nmax % This routine will be skipped. %---------------------------------------------------------------------- % remove confounds and find principal (nmax) modes y = Y.y - Y.X0*(pinv(Y.X0)*Y.y); V = spm_svd(y'); V = V(:,1:DCM.options.nmax); %---------------------------------------------------------------------- % remove minor modes from priors on A j = 1:(n*n); V = kron(V*V',V*V'); pC(j,j) = V*pC(j,j)*V'; end %-------------------------------------------------------------------------- % hyperpriors over precision - expectation and covariance hE = sparse(nout,1) + 6; hC = speye(nout,nout)/128; i = DCM.options.hidden; hE(i) = -4; hC(i,i) = exp(-16); Y.Q = spm_Ce(ones(1,nout)*v); % error precision components %-------------------------------------------------------------------------- % complete model specification M.rs = DCM.options.rs; % radius of source M.acoef= Y.P.acoef; % molar absorption coefficients [1/(mM*cm)] M.f = 'spm_fx_fnirs'; % hemodynamic state equation M.g = 'spm_gx_state_fnirs'; % optics equation M.x = x; M.A = A; % sensitivity matrix M.pE = pE; % prior expectation (parameters) M.pC = pC; % prior covariance (parameters) M.m = size(U.u,2); M.n = size(M.x(:),1); M.nwav = size(Y.P.wav, 2); M.nch = nch; % number of channels M.l = nout; M.hE = hE; % prior expectation (precisions) M.hC = hC; % prior covariance (precisions) M.N = 32; M.dt = 16/M.N; M.ns = v; %-------------------------------------------------------------------------- % nonlinear system identification (nlsi) if ~DCM.options.stochastic %---------------------------------------------------------------------- % nonlinear system identification (Variational EM) - deterministic DCM [Ep,Cp,Eh,F] = spm_nlsi_GN(M,U,Y); %---------------------------------------------------------------------- % predicted responses (y) and residuals (R) y = feval(M.IS,Ep,M,U); R = Y.y - y; R = R - Y.X0*spm_inv(Y.X0'*Y.X0)*(Y.X0'*R); Ce = exp(-Eh); end %-------------------------------------------------------------------------- % Bilinear representation and first-order hemodynamic kernel [M0,M1,L1,L2] = spm_bireduce(M,Ep); [H0,H1] = spm_kernels(M0,M1,L1,L2,M.N,M.dt); %-------------------------------------------------------------------------- % and neuronal kernels L = sparse(1:n,(1:n) + 1,1,n,length(M0)); [K0,K1] = spm_kernels(M0,M1,L,M.N,M.dt); %-------------------------------------------------------------------------- % Bayesian inference and variance {threshold: prior mean plus T = 0} T = full(spm_vec(pE)); sw = warning('off','SPM:negativeVariance'); Pp = spm_unvec(1 - spm_Ncdf(T,abs(spm_vec(Ep)),diag(Cp)),Ep); Vp = spm_unvec(full(diag(Cp)),Ep); warning(sw); try, M = rmfield(M,'nograph'); end %-------------------------------------------------------------------------- % Store parameter estimates DCM.M = M; DCM.Y = Y; DCM.U = U; DCM.Ce = Ce; DCM.Ep = Ep; DCM.Cp = Cp; DCM.Pp = Pp; DCM.Vp = Vp; DCM.H1 = H1; DCM.K1 = K1; DCM.R = R; DCM.y = y; DCM.T = 0; %-------------------------------------------------------------------------- % Data ID and log-evidence if isfield(M,'FS') try ID = spm_data_id(feval(M.FS,Y.y,M)); catch ID = spm_data_id(feval(M.FS,Y.y)); end else ID = spm_data_id(Y.y); end %-------------------------------------------------------------------------- % Save approximations to model evidence: negative free energy, AIC, BIC evidence = spm_dcm_evidence(DCM); DCM.F = F; DCM.ID = ID; DCM.AIC = evidence.aic_overall; DCM.BIC = evidence.bic_overall; %-------------------------------------------------------------------------- %-Save DCM if ~isstruct(P) save(P, 'DCM', 'F', 'Ep', 'Cp', spm_get_defaults('mat.format')); end if ~nargin spm('Pointer','Arrow'); spm('FigName','Done'); end