function DEM_demo_dendrite
% Free-energy and the single neuron:
%__________________________________________________________________________
% This demo illustrates the use of Lotka-Volterra form SHCs (Stable
% heteroclinic channels) to prescribe active sampling (inference). In this
% example, we assume that neurons self-organise to minimise a free energy
% bound on the informational surprise in the pre-synaptic inputs that are 
% sampled.  We model this as a selective pruning of post-synaptic spines 
% that are expressed on the dendritic tree.  This pruning occurs when the 
% (optimised) post-synaptic gain falls to small values.  Crucially, post-
% synaptic gain (encoding the precision of the neuron's prediction errors 
% about its pre-synaptic inputs) is itself optimised with respect to free-
% energy.  Furthermore, the pruning itself suppresses free-energy as the 
% neuron selects post-synaptic specialisations that conform to its 
% expectations.  This provide a principled account of how neurons organise 
% and selectively sample the myriad of potential pre-synaptic inputs they 
% are exposed to, but it also connects elemental neuronal (dendritic) 
% processing to generic schemes in statistics and machine learning:
% such as Bayesian model selection and automatic relevance determination. 
% The demonstration of this scheme simulates direction selectivity in post
% synaptic transients and (see notes after 'return') spike-timing dependent
% plasticity.
%__________________________________________________________________________
 
% Karl Friston
% Copyright (C) 2008-2022 Wellcome Centre for Human Neuroimaging
 
% preliminaries
%==========================================================================
rng('default')
DemoMode = 0;
 
% generative model - Stable heteroclinic channel of pre-synaptic neurons
%==========================================================================
fx  = inline('spm_lotka_volterra(x,exp(v))','x','v','P');
gx  = inline('x(P.w)','x','v','P');
 
% connection weights: NB synapses are changed by switching Y (not P.w)
%--------------------------------------------------------------------------
np  = 5;                              % number of pre-synaptic neurons
ns  = 4;                              % number of dendritic segments
ny  = ns*np;                          % number of synapses

w   = kron((1:np),ones(1,ns));        % prior contacts
g   = rem(randperm(ns*np),np) + 1;    % initial contacts
 
P.w = w';                             % synaptic weights 
Q.w = g';                             % synaptic weights
 
 
% level 1
%--------------------------------------------------------------------------
M(1).x  = sparse(1,1,np,np,1) - np;
M(1).f  = fx;
M(1).g  = gx;
M(1).pE = P;
M(1).Q  = spm_Ce(ones(1,ny));          % error components
M(1).hE = -2;                          % log-precision prior mean
M(1).hC = 2;                           % log-precision prior covariance
M(1).W  = exp(16);                     % error precision
 
% level 2
%--------------------------------------------------------------------------
M(2).v  = 0;
M(2).V  = exp(16);
 
 
% generate presynaptic inputs
%--------------------------------------------------------------------------
M(1).E.nE = 4;
M(1).E.s  = 1;
 
N       = 128;
U       = log(sparse(1,N) + 1/2);
DEM     = spm_DEM_generate(M,U,Q,{2 16},{8});
DEM.U   = U; 
 
% Synaptic pruning
%==========================================================================
if DemoMode
    
    load DEM_Dendrites
    
else
    
    % Synaptic pruning
    %======================================================================
    for i = 1:64
        
        % integrate
        %------------------------------------------------------------------
        DEM   = spm_DEM(DEM);
        
        % Get priors and posteriors
        % -----------------------------------------------------------------
        qE    = DEM.qH.h{1};
        qC    = DEM.qH.C;
        pE    = DEM.M(1).hE;
        pC    = DEM.M(1).hC;
                
        % record
        %------------------------------------------------------------------
        G(:,i) = Q.w;
        F(1,i) = DEM.F(end);
        H(:,i) = qE;
        
        % model search over new prior without the i-th parameter
        % -----------------------------------------------------------------
        for k = 1:ny
            rE    = pE;
            rE(k) = 4;
            Z(k)  = spm_log_evidence(qE,qC,pE,pC,rE,pC);
        end
        
        % change synaptic locations based on optimized precision
        %------------------------------------------------------------------
        j     = Z < 0;
        
        % and generate new presynaptic inputs
        %------------------------------------------------------------------
        r     = rem(randperm(ns*np),np) + 1;
        g(j)  = r(j);
        Q.w   = g';
        GEN   = spm_DEM_generate(DEM.M,U,Q,{2 16},{8});
        DEM.Y = GEN.Y;
        
        
        % report
        %==================================================================
        spm_figure('Getwin','Figure 1');
        
        D     = sparse(1:ny,G(:,i),exp(H(:,i)),ny,np);
        subplot(2,1,1)
        imagesc(-D)
        axis image, axis off
        title('synaptic connections','FontSize',16)
        
        subplot(2,2,3)
        plot(F - F(1))
        xlabel('iterations')
        axis square, box off
        title('Free-energy','FontSize',16)
        
        subplot(2,2,4)
        imagesc(-exp(H))
        xlabel('iterations')
        ylabel('synaptic strength')
        axis square
        title('Precision','FontSize',16)
        drawnow
        
    end
    
    % save 
    %----------------------------------------------------------------------
    save DEM_Dendrites
 
end
 
 
% Illustrate selection of synaptic connections
%==========================================================================
spm_figure('Getwin','Figure 1');
 
subplot(2,2,1)
imagesc(-exp(H))
xlabel('cycles')
ylabel('synaptic strength')
axis square
title('Precision','FontSize',16)
 
subplot(2,2,2)
plot(F - F(1))
xlabel('cycles')
axis square, box off
title('Free-energy','FontSize',16)
spm_axis tight
 
% show connectivity at selected time points
%--------------------------------------------------------------------------
T     = [1 2 32 48];
for i = 1:length(T)
    subplot(2,4,4 + i)
    imagesc(-sparse(1:ny,G(:,T(i)),exp(H(:,T(i))),ny,np))
    axis image
    title(sprintf('Cycle %i',T(i)),'FontSize',16)
end
    
 
% Illustrate synaptic selection function (cf post hoc model selection)
%==========================================================================
spm_figure('Getwin','Figure 2');
 
% Get priors and posteriors
% -------------------------------------------------------------------------
qE    = DEM.qH.h{1};
qC    = DEM.qH.C;
pE    = DEM.M(1).hE;
pC    = DEM.M(1).hC;
 
% model search over new prior without the i-th parameter
% -------------------------------------------------------------------------
QE    = linspace(-2,4,32);
for k = 1:length(QE)
    rE    = pE;
    rE(1) = 4;
    qE(1) = QE(k);
    L     = spm_log_evidence(qE,qC,pE,pC,rE,pC);
    p(k)  = exp(L)/(exp(L) + 1);
end
 
spm_figure('Getwin','Figure 2');
subplot(2,1,1)
plot(QE,p,QE,QE*0 + 1/2,':');
ylabel('conditional probability')
xlabel('synaptic strength (log-precision)')
title('Synaptic selection function','FontSize',16)
axis square
 
 
% make move of synaptic re-organization
%--------------------------------------------------------------------------
subplot(2,1,2),cla
for i = 1:size(G,2)
    imagesc(-sparse(1:ny,G(:,i),exp(H(:,i)),ny,np))
    axis image
    MM(i) = getframe(gca);
end
 
title('synaptic connections','FontSize',16)
xlabel('(left) click image for movie','FontSize',16)
 
% set ButtonDownFcn
%--------------------------------------------------------------------------
h = findobj(gca,'type','image');
set(h(1),'Userdata',{MM,16})
set(h(1),'ButtonDownFcn','spm_DEM_ButtonDownFcn')
 
return


% Illustrate sequence specificity
%==========================================================================
SIM   = DEM;
U     = -((1:N) - N/2).^2/(2*(N/8)^2);
SIM.M(1).E.nE =  1;
SIM.M(1).W    =  8;
SIM.M(1).hE   =  2;
SIM.M(2).v    = -1;
SIM.M(2).V    =  8;
n     = 3;
t     = 1:N;
Q     = P;
for i = 1:n
    
    
    % Integrate
    %----------------------------------------------------------------------  
    SIM   = spm_DEM_generate(SIM.M,U,Q,{2 16},{8});
    SIM   = spm_DEM(SIM);
    
    % plot
    %----------------------------------------------------------------------
    spm_figure('Getwin','Figure 3');
    
    subplot(n,2,(i - 1)*2 + 1)
    imagesc(SIM.Y)
    xlabel('time','FontSize',12)
    ylabel('synapse','FontSize',12)
    title('presynaptic input','FontSize',16)
    
    subplot(n,2,(i - 1)*2 + 2)
   
    m    = SIM.qU.v{2};
    ci   = 1.64* sqrt(spm_vec(SIM.qU.C)');
    cu   = exp(m + ci);
    cl   = exp(m - ci);
    m    = exp(m);
    fill([t fliplr(t)],[full(cu) fliplr(full(cl))],...
         [1 1 1]*.8,'EdgeColor',[1 1 1]*.8),hold on
    plot(t,m), hold off
    xlabel('time','FontSize',12)
    ylabel('activity','FontSize',12)
    title('postsynaptic response','FontSize',16)
    axis([1 N 0 2])
    
    % change presynaptic firing order
    %----------------------------------------------------------------------
    j     = randperm(np);
    Q.w   = kron(j,ones(1,ns));
    
end
 
% show response
%==========================================================================
spm_figure('Getwin','DEM');
spm_DEM_qU(SIM.qU,SIM.pU)
 

% Changing the speed (u) using IN and OUT sequences
%==========================================================================
SIM           = DEM;
 
SIM.M(1).E.nE =  1;
SIM.M(1).W    =  8;
SIM.M(1).hE   =  2;
SIM.M(2).v    = -1;
SIM.M(2).V    =  8;
 
IN    = 1:np;
OUT   = fliplr(IN);
u     = [1 2 4 6];
for i = 1:length(u)
    
    % Speed
    %----------------------------------------------------------------------
    U      = log(zeros(1,round(64/u(i))) + u(i));
        
    % Integrate IN
    %---------------------------------------------------------------------- 
    Q.w    = kron(IN,ones(1,ns));
    SIM    = spm_DEM_generate(SIM.M,U,Q,{2 16},{8});
    SIM    = spm_DEM(SIM);
    V(1,i) = mean(exp(SIM.qU.v{2}));
    
    % Integrate OUT
    %---------------------------------------------------------------------- 
    Q.w    = kron(OUT,ones(1,ns));
    SIM    = spm_DEM_generate(SIM.M,U,Q,{2 16},{8});
    SIM    = spm_DEM(SIM);
    V(2,i) = mean(exp(SIM.qU.v{2}));
    
end
 
% plot direction-selectivity
%--------------------------------------------------------------------------
spm_figure('Getwin','Figure 4');
 
subplot(2,1,1)
plot(u,V(1,:),'r',u,V(1,:),'or',u,V(2,:),'b',u,V(2,:),'ob')
xlabel('input velocity','FontSize',12)
ylabel('mean response','FontSize',12)
title('Velocity-dependent responses','FontSize',16)
axis square, box off

 
return


% Spike-timing dependent plasticity
%==========================================================================
U             = -((1:N) - N/2).^2/(2*(N/8)^2);
SIM           = spm_DEM_generate(DEM.M,U,P,{2 16},{8});
SIM.M(1).E.nE =  4;
SIM.M(1).W    =  8;
SIM.M(1).hE   =  0;
SIM.M(2).v    = -1;
SIM.M(2).V    =  8;
 
% time delay and synapse
%--------------------------------------------------------------------------
delay = -32:8:32;                   
j     = 10;
Y     = SIM.Y;
for i = 1:length(delay)
    
    % delay j-th synaptic input
    %----------------------------------------------------------------------
    y          = Y(j,:);
    y          = [(zeros(1,128) + y(1)) y (zeros(1,128) + y(end))];
    y          = y((1:N) + 128 - delay(i));
    SIM.Y(j,:) = y;
    
    % optimise precision
    %----------------------------------------------------------------------
    SIM        = spm_DEM(SIM);
    
    % record plasticity
    %----------------------------------------------------------------------
    qH(i)      = SIM.qH.h{1}(j);
        
end
 
% plot Spike-timing dependent plasticity
%--------------------------------------------------------------------------
spm_figure('Getwin','Figure 4');
 
subplot(2,1,2)
plot(delay,qH)
xlabel('delay','FontSize',12)
ylabel('change in synaptic precicion','FontSize',12)
title('Spike-timing dependent plasticity','FontSize',16)
axis square, box off
