function ADEM_salience
% Saccadic eye movements under active inference
%__________________________________________________________________________
% This demo illustrates exploration or visual search in terms of optimality
% principles based on straightforward ergodic or allostatic principles.
% In other words, to maintain the constancy of our external milieu, it is
% sufficient to expose ourselves to predicted and predictable stimuli.
% Being able to predict what is currently seen also enables us to predict
% fictive sensations that we will experience from another viewpoint. This
% provides a principled way in which to explore and sample the world for
% example with visual searches using saccadic eye movements. These
% theoretical considerations are remarkably consistent with a number
% of compelling heuristics; most notably the Infomax principle or the
% principle of minimum redundancy, signal detection theory and recent
% formulations of salience in terms of Bayesian surprise. The example
% here uses saliency (the posterior precision associated with fictive
% sampling of sensory data) to simulate saccadic eye movements under
% active inference.
%__________________________________________________________________________

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


pth = fileparts(mfilename('fullpath'));


% hidden causes and states
%==========================================================================
% x    - hidden states:
%   o(1) - oculomotor angle
%   o(2) - oculomotor angle
%   x(1) - relative amplitude of visual hypothesis 1
%   x(2) - relative amplitude of visual hypothesis 2
%   x(3) - ...
%
% v    - hidden causes
%
% g    - sensations:
%   g(1) - oculomotor angle (proprioception - x)
%   g(2) - oculomotor angle (proprioception - y)
%   g(3) - retinal input - channel 1
%   g(4) - retinal input - channel 2
%   g(5) - ...
%--------------------------------------------------------------------------



% mapp images and get hypotheses
%--------------------------------------------------------------------------
global STIM
DEMO = 0;

try
    
    STIM.H{1}   = spm_vol(fullfile(pth,'face_R.nii'));
    STIM.H{2}   = spm_vol(fullfile(pth,'face_rot_R.nii'));
    STIM.H{3}   = spm_vol(fullfile(pth,'face_inv_R.nii'));
    
    STIM.S{1}   = spm_vol(fullfile(pth,'face.nii'));
    STIM.S{2}   = spm_vol(fullfile(pth,'face_rot.nii'));
    STIM.S{3}   = spm_vol(fullfile(pth,'face_inv.nii'));
    
catch
    
    error('Images not found.');
    
end


% set-up:
%--------------------------------------------------------------------------
dim    = 16;                                  % dimension of visual sample
ns     = dim*dim;                             % number of sensory channels
nh     = length(STIM.H);                      % number of hypotheses
STIM.R = spm_hanning(dim)*spm_hanning(dim)';  % Retinal precision

STIM.V = spm_vol(fullfile(pth,'Nefertiti_R.nii')); % Stimulus (filtered)
STIM.U = spm_vol(fullfile(pth,'Nefertiti.nii'));   % Stimulus (unfiltered)

STIM.V = spm_vol(fullfile(pth,'face_R.nii'));      % Stimulus (filtered)
STIM.U = spm_vol(fullfile(pth,'face.nii'));        % Stimulus (unfiltered)


% hidden states
%--------------------------------------------------------------------------
x.o    = [0;0];                               % oculomotor angle
x.x    = -log(nh)*ones(nh,1);                 % hypotheses


% Recognition model
%==========================================================================
M(1).E.s = 1/2;                               % smoothness
M(1).E.n = 4;                                 % order of
M(1).E.d = 2;                                 % generalised motion


% level 1: Displacement dynamics and mapping to sensory/proprioception
%--------------------------------------------------------------------------
M(1).f  = 'spm_fx_dem_salience';              % plant dynamics
M(1).g  = 'spm_gx_dem_salience';              % prediction

M(1).x  = x;                                  % hidden states
M(1).V  = exp([8 8 4*ones(1,ns)]);            % error precision (g)
M(1).W  = exp(8);                             % error precision (f)


% level 2:
%--------------------------------------------------------------------------
M(2).v  = [0;0];                              % priors
M(2).V  = exp(16);


% generative model
%==========================================================================

% first level
%--------------------------------------------------------------------------
G(1).f  = 'spm_fx_adem_salience';
G(1).g  = 'spm_gx_adem_salience';
G(1).x  = [0;0];                              % hidden states
G(1).V  = exp(16);                            % error precision
G(1).W  = exp(8);                             % error precision
G(1).U  = [exp(8) exp(8) zeros(1,ns)];        % gain

% second level
%--------------------------------------------------------------------------
G(2).v  = 0;                                  % exogenous forces
G(2).a  = [0; 0];                             % action forces
G(2).V  = exp(16);


% generate and invert
%==========================================================================
N     = 16;                                   % length of data sequence
nr    = 32;                                   % size of salience map
a     = 1/2;                                  % autoregression for salience
s     = STIM.V.dim(1)/16/16;                  % width of IOR
R     = sparse(nr*nr,1);                      % salience map with IOR

DEM.G = G;
DEM.M = M;
DEM.C = sparse(1,N);
DEM.U = sparse(2,N);

if DEMO
    
    % (k) saccades
    %----------------------------------------------------------------------
    for k = 1:8
        
        % solve and save saccade
        %------------------------------------------------------------------
        DEM     = spm_ADEM(DEM);
        DEM     = spm_ADEM_update(DEM);
        
        % overlay true values
        %------------------------------------------------------------------
        spm_DEM_qU(DEM.qU,DEM.pU)
        
        % compute salience
        %------------------------------------------------------------------
        [S L]   = spm_salience_map(DEM.M,nr);
        
        % optimise prior belief
        %------------------------------------------------------------------
        S       = (S - min(S)).*(1 - R);
        [i j]   = max(S);
        DEM.U   = L(:,j)*ones(1,N);
        DEM.S   = reshape(S,nr,nr);
        
        % inhibition of return (IOR)
        %------------------------------------------------------------------
        D       = exp(-sum((L - L(:,j)*ones(1,nr*nr)).^2)/(2*s*s))';
        R       = a*R + D;
        
        % store
        %------------------------------------------------------------------
        ADEM{k} = DEM;
        
    end
    
    % save
    %----------------------------------------------------------------------
    save ADEM_saccades ADEM
    
end

% load
%--------------------------------------------------------------------------
load ADEM_saccades
    
    
% create movie in extrinsic and intrinsic coordinates
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 1');
spm_dem_search_plot(ADEM(1:end))

% create movie in extrinsic and intrinsic coordinates
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 2');
spm_dem_search_trajectory(ADEM)

% create movie in extrinsic and intrinsic coordinates
%--------------------------------------------------------------------------
spm_figure('GetWin','Figure 3');
STIM.R = spm_hanning(32)*spm_hanning(32)';
spm_dem_search_movie(ADEM)


return


% illustrate salience for
%==========================================================================
nr          = 64;
STIM.H{1}   = spm_vol(fullfile(pth,'Nefertiti_R.nii'));
M(1).x.x    = 0;
S           = spm_salience_map(M,nr);

subplot(2,1,1)
imagesc(reshape(exp(S/6),nr,nr))
axis image
subplot(2,1,2)
imagesc(spm_read_vols(STIM.H{1}))
axis image



% create images for (memory mapped) sampling - for a given image F
%==========================================================================
fname = 'Nefertiti'
DIM = [size(F) 1];
M   = eye(4,4);
F   = F/max(max(F));

% write volume structure
%--------------------------------------------------------------------------
V   = struct(...
    'fname',  fullfile(pth,[fname '.nii']),...
    'dim',    DIM,...
    'mat',    M,...
    'pinfo',  [1 0 0]',...
    'descrip',fname);
V   = spm_create_vol(V);
V   = spm_write_plane(V,F,1);


% create images for (memory mapped) sampling - with Gabor filtering
%==========================================================================
for i = 1:length(H)
    
    fname = spm_file(H{i}.fname,'suffix','_R');
    s     = spm_read_vols(H{i});
    s     = s/max(max(s));
    s     = (spm_conv(s,1) - spm_conv(s,4));
    DIM   = [size(s) 1];
    M     = eye(4,4);
    
    % write volume structure
    %----------------------------------------------------------------------
    V   = struct(...
        'fname',  fname,...
        'dim',    DIM,...
        'mat',    M,...
        'pinfo',  [1 0 0]',...
        'descrip',spm_file(fname,'basename'));
    V   = spm_create_vol(V);
    V   = spm_write_plane(V,s,1);
    
end

