function [qE,qC,Q] = spm_dcm_loo(DCM,M,field)
% Leave-one-out cross-validation for empirical Bayes and DCM
% FORMAT [qE,qC,Q] = spm_dcm_loo(DCM,M,field)
%
% DCM   - {N [x M]} structure DCM array of (M) DCMs from (N) subjects
% -------------------------------------------------------------------
%     DCM{i}.M.pE   - prior expectation of parameters
%     DCM{i}.M.pC   - prior covariances of parameters
%     DCM{i}.Ep     - posterior expectations
%     DCM{i}.Cp     - posterior covariance
%
% M.X       - second level design matrix, where X(:,1) = ones(N,1) [default]
% field     - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}]
%             'All' will invoke all fields
% 
% qE        - posterior predictive expectation (group effect)
% qC        - posterior predictive covariances (group effect)
% Q         - posterior probability over unique levels of X(:,2)
% 
% This routine uses the posterior predictive density over the coefficients
% of between-subject effects encoded by a design matrix X. It is assumed
% that the second column of X contains classification or predictor
% variables. A cross-validation scheme is used to estimate the mixture of
% parameters at the first (within-subject) level that are conserved over
% subjects in terms of a constant (first column of X) and differences
% (second column of X). Using a leave-one-out scheme, the predictive
% posterior density of the predictive variable is used to assess
% cross-validation accuracy. For multiple models, this procedure is
% repeated for each model in the columns of the DCM array.
% 
% See also: spm_dcm_peb.m and spm_dcm_ppd.m
%__________________________________________________________________________
 
% Karl Friston
% Copyright (C) 2015-2022 Wellcome Centre for Human Neuroimaging


% Set up
%==========================================================================

% parameter fields
%--------------------------------------------------------------------------
if nargin < 3
    try
        field = DCM{1}.field;
    catch
        field = {'A','B'};
    end
end
if strcmpi(field,'all')
    field = fieldnames(DCM{1}.M.pE);
end
if isnumeric(M)
    M = struct('X',M);
end

% Repeat for each column if TEST is an array
%==========================================================================
if size(DCM,2) > 1
    
    % loop over models in each column
    %----------------------------------------------------------------------
    for i = 1:size(DCM,2)
        [p,q,r] = spm_dcm_loo(DCM(:,i),M,field);
        qE{i}   = p;
        qC{i}   = q;
        Q{i}    = r;
    end
    return
end

% Leave-one-out scheme
%==========================================================================
Ns    = numel(DCM);
for i = 1:Ns
    
    % get posterior predictive density for each subject
    %----------------------------------------------------------------------
    j         = 1:Ns;
    j(i)      = [];
    [Ep,Cp,P] = spm_dcm_ppd(DCM(i),DCM(j,1),M.X(i,:),M.X(j,:),field);
    qE(i)     = Ep;
    qC(i)     = Cp;
    Q(:,i)    = P;

end

% Show results
%==========================================================================
if spm_get_defaults('cmdline'), return; end

spm_figure('GetWin','LOO cross-validation');clf
subplot(2,2,1), spm_plot_ci(qE,qC), hold on
plot(M.X(:,2),':'), hold off
xlabel('subject','FontSize',12), ylabel('group effect','FontSize',12)
title('Out of sample estimates','FontSize',16)
spm_axis tight, axis square

% classical inference on predictive accuracy
%--------------------------------------------------------------------------
[T,df] = spm_ancova(M.X(:,1:2),[],qE(:),[0;1]);
r      = corrcoef(qE(:),M.X(:,2));
r      = full(r(1,2));

if isnan(T)
    p = NaN;
else
    p = spm_Tcdf(-T,df(2));
end

% correlation (c.f., out-of-sample variance explained)
%--------------------------------------------------------------------------
str = sprintf('corr(df:%-2.0f) = %-0.2f: p = %-0.5f',df(2),r,p);

subplot(2,2,2)
plot(M.X(:,2),qE,'o','Markersize',8)
xlabel('group effect','FontSize',12), ylabel('estimate','FontSize',12)
title(str,'FontSize',16)
axis square

if size(Q,1) > 2
    
    % Continuous prediction (c.f., regression)
    %----------------------------------------------------------------------
    subplot(2,1,2), imagesc(1:Ns,unique(M.X(:,2)),Q), hold on
    plot(M.X(:,2),'.c','MarkerSize',32), hold off
    xlabel('Subject','FontSize',12);
    ylabel('levels of group effect','FontSize',12)
    title('Predictive posterior (and true values)','FontSize',16)
    axis square xy
    
else

    % discrete prediction (c.f., classification)
    %----------------------------------------------------------------------
    subplot(4,1,3), bar(Q(1,:))
    xlabel('subject','FontSize',12);
    ylabel('posterior probability','FontSize',12);
    title('Group effect (group 1)','FontSize',16);
    axis([0 (Ns + 1) 0 1]);
    line([0 Ns+0.5],[0.95 0.95],'LineStyle','--','Color','r');
    
    subplot(4,1,4), bar(Q(2,:))
    xlabel('subject','FontSize',12);
    ylabel('posterior probability','FontSize',12);
    title('Group effect (group 2)','FontSize',16);
    axis([0 (Ns + 1) 0 1]);
    line([0 Ns+0.5],[0.95 0.95],'LineStyle','--','Color','r');
    
end
