function [MCI] = spm_mci_mfx (MCI)
% Mixed Effects Inference 
% FORMAT [MCI] = spm_mci_mfx (MCI)
%
% MCI               Data structure containing fields:
%
% .M{n}             Model for nth of N replications (e.g. subjects)
% .U{n}             Inputs for nth replication
% .Y{n}             Data for nth replication
% .S                Second level model describing population mean, m, and 
%                   precision, Lambda. The parameters in S.prior
%                   define the sufficient statistics of p(Lambda) (.a and .B)
%                   and p(m|Lambda) (.beta and.m)
%
% .inference        'amc' or 'lgv' (default)
% .total_its        Total number of samples per subject
% .rinit            Proportion of samples to collect prior to use of
%                   Empirical (group) prior
% .verbose          Show progress of optimisation 
% .update_obs_noise Update observation noise ? [yes/no] (1/0), default=1
%
% The output fields are: 
%
% POSTERIOR SAMPLES:
% .sm               [Nw x Nsamples] group random effect means, m
% .sw               [Nw x N x Nsamples] subject random effects, w
% .Ce               [Ny x Ny x Nsamples] Obs noise covariance samples
% .postind          Indices for posterior (ie. excluding burn-in)
%
% POSTERIOR MEANS:
% .sm_mean          [Nw x 1] posterior mean over m
% .sw_mean          [Nw x N] posterior mean over w
%
% SUFFICIENT STATISTICS:
% .noise            Parameters of p(Gamma|Y,w,v): .c0,.D0,.cN,.DN
% .S.post           Parameters of p(Lambda|w) (.a and.B) 
%                   and p(m|Lambda,w) (.beta and .m)
%
% W.Penny, M Klein-Flugge and B Sengupta (2015) Mixed Effects Langevin
% Monte Carlo, Submitted, 2015.
%__________________________________________________________________________

% Will Penny
% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging

try, verbose=MCI.verbose; catch, verbose=0; end
try, inference=MCI.inference; catch, inference='lgv'; end

% Update observation noise ?
try, update_obs_noise=MCI.update_obs_noise; catch, update_obs_noise=1; end

try, total_its=MCI.total_its; catch, total_its=1024; end
try, rinit=MCI.rinit; catch, rinit=0.25; end

rfx_its=2;
mfx_its=ceil((1-rinit)*total_its);
rfx1_its=total_its-mfx_its;

M=MCI.M;U=MCI.U;Y=MCI.Y;
Np=length(spm_vec(M{1}.pE));

% Observation noise
Ny=size(Y{1}.y,2);
noise.c0=Ny;
noise.D0=eye(Ny);
   
% Prior over random effects (second level model)
try
    S=MCI.S;
catch
    S.prior.P=Np;
    S.prior.a=Np/2;
    S.prior.B=eye(Np);
    S.prior.beta=1;
    S.prior.m=zeros(Np,1);
    S.N=length(M);
end
% Prior mean and cov of random effects from second level model
m=S.prior.m;
C=S.prior.B/S.prior.a;
rfx_prior.pE=m;
rfx_prior.pC=C;
MCI.S=S;

% Initialisation of each model
subj_mcmc.verbose=verbose;
subj_mcmc.maxits=rfx1_its;
for n=1:S.N,
    subj_mcmc.init=M{n}.pE;
    if rfx1_its > 0
        switch inference,
            case 'lgv',
                Psamp = spm_mci_random (subj_mcmc,rfx_prior,[],M{n},U{n},Y{n});
                
            case 'amc',
                M{n}.pE=rfx_prior.pE;
                M{n}.pC=rfx_prior.pC;
                Nscale=0;
                Nsamp=ceil(0.5*rfx1_its);
                Ntune=Nsamp;
                amc = spm_mci_popdef (Nscale,Ntune,Nsamp);
                amc.verbose=verbose;
                Mc{1}=M{n};Uc{1}=U{n};
                Ptmp=spm_mci_pop (amc,Mc,Uc,Y{n});
                Psamp=Ptmp{1}.theta;
                
            otherwise
                disp('Unknown inference method in spm_mci_mfx.m');
        end
        sw(n,:,:) = Psamp;
        w(:,n) = Psamp(:,end);
    else
        sw(n,:,1) = M{n}.pE;
        w(:,n) = M{n}.pE;
    end
end
sw=permute(sw,[2 1 3]);
sm=squeeze(mean(sw,2));

if isfield(M{1},'Ce')
    gauss_noise=1;
    Ce(:,:,1:rfx1_its)=M{1}.Ce;
else
    gauss_noise=0;
end
subj_mcmc.maxits=rfx_its;


% Main Loop
for it=1:mfx_its,
    
    if verbose
        disp(sprintf('RFX iteration %d',it));
    end
        
    if it>1   
        % Update second level params
        S = spm_nwpost (S,w);
        [m,Lambda,C] = spm_nwrnd (S.post,1);
    end
    
    % Updated prior on random effects
    rfx_prior.pE=m;
    rfx_prior.pC=C;
    
    % 1. Update estimates of subject random effects
    if verbose, disp('Updating random effects'); end
    
    for n=1:S.N,
        % Start sampling where left off
        subj_mcmc.init=w(:,n);
        
        switch inference,
            case 'lgv',
                Psamp = spm_mci_random (subj_mcmc,rfx_prior,[],M{n},U{n},Y{n});
                
            case 'amc',
                M{n}.pE=rfx_prior.pE;
                M{n}.pC=rfx_prior.pC;
                Nscale=0; Ntune=0; Nsamp=rfx_its;
                amc = spm_mci_popdef (Nscale,Ntune,Nsamp);
                amc.verbose=verbose;
                Mc{1}=M{n};Uc{1}=U{n};
                Ptmp=spm_mci_pop (amc,Mc,Uc,Y{n});
                Psamp=Ptmp{1}.theta;
                
            otherwise
                disp('Unknown inference method in spm_mci_mfx.m');
        end
        w(:,n) = Psamp(:,end);
    end
    
    % Update observation noise precision
    if update_obs_noise && gauss_noise
        if verbose, disp('Updating observation noise precision'); end
        [noise,M] = spm_mci_obsnoise (w,[],[],noise,M,U,Y);
    end
    if gauss_noise, Ce(:,:,rfx1_its+it)=M{1}.Ce; end
        
    % Store samples
    sm(:,rfx1_its+it)=m;
    sw(:,:,rfx1_its+it)=w;
    
end

% Define post burn-in
total_its=size(sm,2); 
burn_in=round(0.3*total_its);
post_ind=[burn_in+1:total_its];

% Return samples
MCI.sm=sm;
MCI.sw=sw;

% Return posterior means
MCI.sm_mean=mean(sm(:,post_ind),2);
MCI.sw_mean=mean(sw(:,:,post_ind),3);
MCI.post_ind=post_ind;

MCI.M=M;
MCI.S=S;
if gauss_noise
    MCI.noise=noise;
    MCI.Ce=Ce; 
end
