function spm_dcm_search(P)
% Post hoc optimisation of DCMs (under Laplace approximation)
% FORMAT spm_dcm_search(P)
%
% P         -  character/cell array of DCM filenames
%
%--------------------------------------------------------------------------
% spm_dcm_search operates on different DCMs of the same data to identify
% the best model. It will invert the full model whose free-parameters are
% the union (superset) of all free parameters in each model specified. The
% routine then uses a post hoc selection procedure to evaluate the log-
% evidence and conditional density over free-parameters of each model
% specified.
%
% The DCM specified does not need to be estimated. spm_dcm_search will 
% invert the requisite (full DCM) automatically.
%
% The outputs of this routine are graphics reporting the model space search
% (optimisation) and a DCM_optimum (in the first DCMs directory) for the
% best DCM. The structural and function (spectral embedding) graphs are
% based on this DCM.
%
% DCM_optimum  contains the fields:
%        DCM.P   - character/cell array of DCM filenames
%        DCM.PF  - their associated free energies
%        DCM.PP  - and posterior (model) probabilities
%
% In addition, the free energies and posterior estimates of each DCM in P 
% are saved for subsequent searches over different partitions of model 
% space.
%
% See also: spm_dcm_post_hoc.m
%__________________________________________________________________________

% Karl Friston
% Copyright (C) 2008-2022 Wellcome Centre for Human Neuroimaging


% get filenames
%--------------------------------------------------------------------------
try
    P;
catch
    [P, sts] = spm_select([2 Inf],'^DCM.*\.mat$','Select DCM*.mat files');
    if ~sts, return; end
end
 
if ischar(P), P = cellstr(P); end
N = numel(P);
 
%-Check models are compatible in terms of their data
%==========================================================================
for j = 1:N
    
    % get prior covariances
    %----------------------------------------------------------------------
    load(P{j});
    
    % and compare it with the first model
    %----------------------------------------------------------------------
    if j == 1
        Y = DCM.Y.y;
    else
        try
            if any(any(Y - DCM.Y.y))
                fprintf('Please check model %i for compatibility',j)
                return
            end
        catch
            fprintf('Please check model %i for compatibility',j)
            return
        end
    end
    
    % accumate model in terms of which parameters are free
    %----------------------------------------------------------------------
    A = DCM.a;
    B = DCM.b;
    C = DCM.c;
    D = DCM.d;
    
    % Get full models free parameters
    %----------------------------------------------------------------------
    if j == 1
        a    = A;
        b    = B;
        c    = C;
        d    = D;
    else
        a    = a | A;
        b    = b | B;
        c    = c | C;
        d    = d | D;
    end
end
 
%-Estimate full model
%==========================================================================
DCM.a = a;
DCM.b = b;
DCM.c = c;
DCM.d = d;
 
DCM.name = 'optimum';
 
% Get full priors and posteriors
% -------------------------------------------------------------------------
FUL   = spm_dcm_estimate(DCM);
 
qE    = FUL.Ep;
qC    = FUL.Cp;
pE    = FUL.M.pE;
pC    = FUL.M.pC;
 
%-Loop through models and get log-evidences
%==========================================================================
for j = 1:N
    
    load(P{j});
    
    % Fix for endogenous DCM (to match spm_dcm_estimate)
    % ---------------------------------------------------------------------
    if isempty(DCM.c) || isempty(U.u)
        DCM.c  = zeros(DCM.n,1);
        DCM.b  = zeros(DCM.n,DCM.n,1);
    end
    
    % Get model (priors) and evaluate (reduced) free-energy and posteriors
    % ---------------------------------------------------------------------
    [rE,rC]   = spm_dcm_fmri_priors(DCM.a,DCM.b,DCM.c,DCM.d,DCM.options);
    [F,Ep,Cp] = spm_log_evidence(qE,qC,pE,pC,rE,rC);
    
    % Put reduced conditional estimates in DCM
    % =====================================================================
    
    % Bayesian inference and variance
    %----------------------------------------------------------------------
    sw       = warning('off','SPM:negativeVariance');
    Pp       = spm_unvec(1 - spm_Ncdf(0,abs(spm_vec(Ep)),diag(Cp)),Ep);
    Vp       = spm_unvec(diag(Cp),Ep);
    warning(sw);
    
    
    % Store parameter estimates
    %----------------------------------------------------------------------
    DCM.M.pC = rC;
    DCM.Ep   = Ep;
    DCM.Cp   = Cp;
    DCM.Pp   = Pp;
    DCM.Vp   = Vp;
    DCM.T    = 0;
    
    % Store predictions of states from full model for simplicity
    %----------------------------------------------------------------------
    DCM.Ce   = FUL.Ce;
    DCM.H1   = FUL.H1;
    DCM.K1   = FUL.K1;
    DCM.R    = FUL.R;
    DCM.y    = FUL.y;
    
    % Save approximations to model evidence: negative free energy, AIC, BIC
    %----------------------------------------------------------------------
    evidence = spm_dcm_evidence(DCM);
    DCM.F    = F;
    DCM.AIC  = evidence.aic_overall;
    DCM.BIC  = evidence.bic_overall;
    
    % Save DCM
    %======================================================================
    save(P{j},'DCM','F','Ep','Cp', spm_get_defaults('mat.format'));
    
    % Record free-energy
    %----------------------------------------------------------------------
    G(j) = F;
    
end
 
% Model evidences and best model
% =========================================================================
G     = G - min(G);
p     = exp(G - max(G));
p     = p/sum(p);
 
% Get selected model
%--------------------------------------------------------------------------
[q,j] = max(p);
load(P{j});
 
i   = spm_fieldindices(DCM.Ep,'A','B','C','D');
qE  = spm_vec(FUL.Ep);
Ep  = spm_vec(DCM.Ep);
qC  = DCM.Cp;
Cp  = DCM.Cp;
F   = DCM.F;
 
% Show results
% -------------------------------------------------------------------------
spm_figure('Getwin','Graphics'); clf
 
subplot(2,2,1)
if length(P) > 32, plot(G,'k'), else bar(G,'c'), end
title('log-posterior','FontSize',16)
xlabel('model','FontSize',12)
ylabel('log-probability','FontSize',12)
axis square
 
subplot(2,2,2)
if length(P) > 32, plot(p,'k'), else, bar(p,'r'), end
title('model posterior','FontSize',16)
xlabel('model','FontSize',12)
ylabel('probability','FontSize',12)
axis square
 
% Show full and reduced conditional estimates (for optimum DCM)
%--------------------------------------------------------------------------
subplot(2,2,3)
spm_plot_ci(qE(i),qC(i,i))
title('MAP connections (full)','FontSize',16)
axis square
a   = axis;
 
subplot(2,2,4)
spm_plot_ci(Ep(i),Cp(i,i))
title('MAP connections (optimum)','FontSize',16)
axis square
axis(a)
 
% Show structural and functional graphs
%--------------------------------------------------------------------------
spm_figure('Getwin','Graph'); clf
 
spm_dcm_graph(DCM.xY,DCM.Ep.A)
 
 
%-Save optimum and full DCM
%==========================================================================
DCM.P  = P;
DCM.PF = G;
DCM.PP = p;
 
% Reduced model (optimum)
%--------------------------------------------------------------------------
pth      = fileparts(P{1});
filename = fullfile(pth,'DCM_optimum.mat');
save(filename,'DCM','F','Ep','Cp', spm_get_defaults('mat.format'));
 
% Full model
%--------------------------------------------------------------------------
DCM      = FUL;
Ep       = FUL.Ep;
Cp       = FUL.Cp;
F        = FUL.F;
filename = fullfile(pth,'DCM_full');
save(filename,'DCM','F','Ep','Cp', spm_get_defaults('mat.format'));
