function spm_mfm_demo
% Demo routine for mean-field models
%==========================================================================
% 
% This demo compares and contrasts neural-mass and mean-field models of a 
% single population, using the model described in Marreiros et al 2008). 
% We start by comparing the impulse response of an small ensemble that has 
% some (but not marked) finite size effects) with that of the mean-field
% and neural-mass approximations.  The key difference between these models
% is that the means-field has states that describe the change in dispersion 
% or covariance among the first-order states (current and voltage).
% 
% We then move on to comparing responses to inputs that are transient and 
% sustained, to show that the mean-field model retains key nonlinearities 
% and can show [plausible] bifurcations, as sustained input levels are
% increased.  This is characterised using Fourier transforms, which are %
% plotted alongside spiking responses.  See Marreiros et al:
% 
% Population dynamics under the Laplace assumption.
% 
% A Marreiros, J Daunizeau, S Kiebel, L Harrison & Karl Friston
% 
% Abstract
% In this paper, we describe a generic approach to modelling dynamics in
% neuronal populations.  This approach retains a full density on the states
% of neuronal populations but resolves the problem of solving
% high-dimensional problems by re-formulating density dynamics in terms of
% ordinary differential equations on the sufficient statistics of the
% densities considered.  The particular form for the population density we
% adopt is a Gaussian density (c.f., a Laplace assumption). This means
% population dynamics are described completely by equations governing the
% evolution of the population’s mean and covariance.  We derive these
% equations from the Fokker-Planck formalism and illustrate their
% application to a reasonably simple conductance-based model of neuronal
% exchanges.  One interesting aspect of this formulation is that we can
% uncouple the mean and covariance to furnish a neural-mass model, which
% rests only on the populations mean.  This enables to compare equivalent
% mean-field and neural-mass models of the same populations and evaluate,
% quantitatively, the contribution of population variance to the expected
% dynamics.  The mean-field model presented here will form the basis of a
% dynamic causal model of observed electromagnetic signals in future work.
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging
 
% Karl Friston
% $Id: spm_mfm_demo.m 5939 2014-04-06 17:13:50Z karl $
 
 
% number of regions in coupled map lattice
%--------------------------------------------------------------------------
rng('default')
n     = 1;
 
% extrinsic network connections
%--------------------------------------------------------------------------
if n > 1
A{1}  = diag(ones(n - 1,1),-1);
else
    A{1} = 0;
end
A{2}  = A{1}';
A{3}  = sparse(n,n);
B     = {};
C     = sparse(1,1,1,n,1);
 
 
% get connectivity and other priors
%--------------------------------------------------------------------------
pE    = spm_nmm_priors(A,B,C);
 
% initialise states
%--------------------------------------------------------------------------
[x M] = spm_x_mfm(pE);
 
 
% create exogenous inputs
%==========================================================================
dt    = 2;
t     = (1:dt:256)';
N     = length(t);
U.dt  = dt/1000;
U.u   = 128*(exp(-(t - 64).^2/8) + rand(N,1)/exp(32));
 
 
% Integrate system to see Transient response - ensemble
%==========================================================================
np    = 8;                                % number of particles in ensemble
 
% initialise ensemble
%--------------------------------------------------------------------------
for i = 1:np
    X(i,:,:,:) = x{1};
end
 
% diffusion
%--------------------------------------------------------------------------
D    = diag([1/32 1 1]);
dfdw = kron(spm_sqrtm(D/2),speye(np*n*3));


 
% integrate ensemble Y(time, particle, source, population, state)
%==========================================================================
fprintf('integrating ensemble\n')

% burn in
%--------------------------------------------------------------------------
for i = 1:16
    [dfdx f]     = spm_diff('spm_fx_mfm_ensemble',X,U.u(1),pE,1);
    dX           = spm_sde_dx(dfdx,dfdw,f,U.dt);
    X            = spm_unvec(dX + spm_vec(X),X);
    fprintf('time-step (burn in) %i (%i)\n',i,N)
end
 
% integrate over peristimulus time
%--------------------------------------------------------------------------
for i = 1:N
 
    [dfdx f]     = spm_diff('spm_fx_mfm_ensemble',X,U.u(i),pE,1);
    dX           = spm_sde_dx(dfdx,dfdw,f,U.dt);
    X            = spm_unvec(dX + spm_vec(X),X);
    Y(i,:,:,:,:) = X;
    
    fprintf('time-step %i (%i)\n',i,N)
end
 
% input
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 1'); clf

pop   = {'stellate', 'interneurons', 'pyramidal'};
state = {'Voltage', 'excitatory conductance', 'inhibitory conductance'};

p  = 3;                                    % default population (pyramidal)
T  = t;                                    % perstimulus time (ms)
c  = [1 1 1]*.9;                           % default colour for plots
subplot(3,1,1)
plot(T,U.u)
title('input')
axis square
xlabel('time (ms)')
 
% plot
%--------------------------------------------------------------------------
subplot(3,1,2)
plot(t,squeeze(Y(:,:,1,p,1)),':')
title('Pyramidal depolarisation')
axis square
xlabel('time (ms)')
 
% plot moments
%--------------------------------------------------------------------------
m   = mean(Y(:,:,1,p,1),2)';
s   = std(Y(:,:,1,p,1),0,2)'*1.64;
subplot(3,1,3)
plot(t,m*0,'k:'),hold on
patch([t(:)' fliplr(t(:)')],[(m(:)' - s(:)') fliplr(m(:)' + s(:)')],c)
plot(t,m,'r','LineWidth',2), hold off
title('Pyramidal depolarisation')
axis square
xlabel('time (ms)')
drawnow

 
% plot
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 2'); clf
for i = 1:3
    for j = 1:3
        subplot(3,3,(i - 1)*3 + j)
        plot(t,squeeze(Y(:,:,1,i,j)),'-','color',[1 1 1]*.7)
        title([pop{i} ': ' state{j}])
        xlabel('time (ms)')
    end
end
drawnow

 
% Integrate system to see Transient response - MFM
%==========================================================================
NM    = M;                              % neural mass model
NM.x  = M.x{1};                         % remove second-order moments
MFM   = spm_int_B(pE,M, U);
NMM   = spm_int_B(pE,NM,U);

% LFP - ensemble
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 3'); clf

m   = mean(Y(:,:,1,p,1),2)';
s   =  std(Y(:,:,1,p,1),0,2)'*1.64;
subplot(3,1,1)
plot(t,m*0,'k:'),hold on
patch([t(:)' fliplr(t(:)')],[(m(:)' - s(:)') fliplr(m(:)' + s(:)')],c)
plot(T,m,'r','LineWidth',2), hold off
title([pop{p} ': Ensemble'])
axis square
xlabel('time (ms)')


% LFP - MFM
%--------------------------------------------------------------------------
subplot(3,1,2)
m   = MFM(:,p)';
s   = sqrt(MFM(:,p*9 + 1)')*1.64;
plot(t,m*0,'k:'),hold on
patch([t(:)' fliplr(t(:)')],[(m(:)' - s(:)') fliplr(m(:)' + s(:)')],c)
plot(t,m,'r','LineWidth',2), hold off
title([pop{p} ': MFM'])
axis square
xlabel('time (ms)')

 
% LFP - NMM
%--------------------------------------------------------------------------
subplot(3,1,3)
m   = NMM(:,p)';
s   = sqrt(64)*1.64;
plot(t,m*0,'k:'),hold on
patch([t(:)' fliplr(t(:)')],[(m(:)' - s(:)') fliplr(m(:)' + s(:)')],c)
plot(t,m,'r','LineWidth',2), hold off
title([pop{p} ': NNM'])
axis square
xlabel('time (ms)')
drawnow

% create exogenous inputs for responses to transient and sustained input
%==========================================================================
dt    = 2;
t     = (1:dt:512)';
N     = length(t);
U.dt  = dt/1000;
 
 
% responses to different inputs - spikes
%==========================================================================
clear YMF YNM
p     = 3;                                               % pyramidal cells
u     = [1:32];                                          % peak input
for i = 1:length(u)
    
    % create exogenous inputs
    %----------------------------------------------------------------------
    U.u   = u(i)*(exp(-(t - 64).^2/8) + randn(N,1)*exp(-16));
 
    % Integrate systems
    %----------------------------------------------------------------------
    MFM   = spm_int_B(pE,M, U);
    NMM   = spm_int_B(pE,NM,U);
 
    YMF(:,:,i) = MFM(:,1:3);
    YNM(:,:,i) = NMM(:,1:3);
    
    fprintf('input level (spike) %i (%i)\n',i,length(u))
 
end

spm_figure('GetWin','Figure 4'); clf

subplot(2,2,1)
imagesc(u,t,squeeze(YMF(:,p,:)))
xlabel('input amplitude (spike)')
ylabel('time (ms)')
title([pop{p} ': MFM'])
 
subplot(2,2,2)
imagesc(u,t,squeeze(YNM(:,p,:)))
xlabel('input amplitude (spike)')
ylabel('time (ms)')
title([pop{p} ': NMM'])
drawnow
 
 
% responses to different inputs - sustained
%--------------------------------------------------------------------------
clear YMF YNM
for i = 1:length(u)
    
    % create exogenous inputs (after 64 ms)
    %----------------------------------------------------------------------
    U.u   = u(i)*(t > 64);
 
    % Integrate systems
    %----------------------------------------------------------------------
    MFM   = spm_int_B(pE,M, U);
    NMM   = spm_int_B(pE,NM,U);
 
    YMF(:,:,i) = MFM(:,1:3);
    YNM(:,:,i) = NMM(:,1:3);
    
    fprintf('input level (sustained) %i (%i)\n',i,length(u))
 
end
 
subplot(2,2,3)
imagesc(u,t,squeeze(YMF(:,p,:)))
xlabel('input amplitude (sustained)')
ylabel('time (ms)')
title([pop{p} ': MFM'])
 
subplot(2,2,4)
imagesc(u,t,squeeze(YNM(:,p,:)))
xlabel('input amplitude (sustained)')
ylabel('time (ms)')
title([pop{p} ': NMM'])
drawnow

 
% frequency responses
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 5'); clf
clear FMF SMF

p     = 3;
for i = 1:length(u)
    
    % Fourier transform
    %----------------------------------------------------------------------
    FMF(:,i) = abs(fft(YMF(:,p,i)));
    
    % mean spiking
    %----------------------------------------------------------------------
    for j = 1:N
        m        = spm_gx_mfm(YMF(j,:,i),0,pE,M);
        SMF(j,i) = m(p);
    end
    
end
 
 
Hz  = [0:N]/(N*U.dt);
i   = find(Hz > 2 & Hz < 64);
Hz  = Hz(i);
FMF = FMF(i,:);
 
subplot(2,2,1)
imagesc(u,Hz,FMF)
xlabel('input amplitude')
ylabel('Frequency (Hz)')
title([pop{p} ': MFM'])
 
subplot(2,2,2)
plot(Hz,FMF(:,[8 16]))
xlabel('Frequency (Hz)')
ylabel('frequency response')
title([pop{p} ': MFM'])
legend({sprintf('input amplitude %i',u(16)) sprintf('input amplitude %i',u(32))})
 
subplot(2,2,3)
plot(u,mean(SMF),'k')
xlabel('input amplitude')
ylabel('firing response')
title([pop{p} ': MFM'])
axis([u(1) u(end) 0 0.5])
 
subplot(2,2,4)
imagesc(u,t,SMF)
xlabel('input amplitude')
ylabel('time (ms)')
title([pop{p} ': MFM'])
drawnow
 
return
 
 

% NOTES:
% time-frequency
%==========================================================================
W     = 256;
w     = 4:1/4:32;
cpW   = w*W*U.dt;
subplot(2,2,3)
imagesc(t*1000,w,abs(spm_wft(LFP(:,7),cpW,W)).^2);
title('time-frequency response')
axis square xy
xlabel('time (ms)')
 
 
% Kernels
%==========================================================================
 
 
% augment and bi-linearise
%--------------------------------------------------------------------------
[M0,M1,L1,L2] = spm_bireduce(M,M.pE);
 
% compute kernels (over 64 ms)
%--------------------------------------------------------------------------
N          = 64;
dt         = 3/1000;
t          = [1:N]*dt*1000;
[K0,K1,K2] = spm_kernels(M0,M1,L1,L2,N,dt);
 
subplot(2,1,1)
plot(t,K1)
title('1st-order Volterra kernel')
axis square
xlabel('time (ms)')
 
subplot(2,1,2)
imagesc(t,t,K2(1:64,1:64,1))
title('2nd-order Volterra kernel')
axis square
xlabel('time (ms)')
