function [P,F] = spm_fmin(fun,Q,C,varargin)
% Objective function minimisation
% FORMAT [P,F] = spm_fmin('fun',P,C,varargin)
%
% fun - function or inline function f - fun(P,varargin)
% P   - free parameters (prior mean)
% C   - prior covariance
%
% P   - optimised parameters
% f   - optimised value of fun(P)
%
%--------------------------------------------------------------------------
% spm_fmin is a slow but robust function minimiser that uses a stochastic
% sampling of the objective function to be minimised (supplemented by a
% line search along the principal eigenvariate at the current sampling
% density. The sampling density is approximated with a Gaussian (first and
% second order moments) using that the sampling density is:
%
%           p(P) = (1/Z)*exp(-fun(P)/T)
%
% where the temperature; T is the sample standard deviation of the sampled
% objective function.
%__________________________________________________________________________

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


% stochastic search
%--------------------------------------------------------------------------
P     = spm_vec(Q);
pmin  = P;
n     = length(P);                            % number of parameters
try
    C;                                        % prior covariance
catch
    C = speye(n,n);
end
C     = C + speye(n,n)*exp(-16);

% Optimise sampling distribution iteratively
%==========================================================================
spm_figure('GetWin','FMIN');
for k = 1:8

    % report - current sample density
    %----------------------------------------------------------------------
    p1    = linspace(-3*sqrt(C(1,1)),3*sqrt(C(2,2)),64);
    p2    = linspace(-3*sqrt(C(2,2)),3*sqrt(C(2,2)),64);
    iC    = inv(C(1:2,1:2));
    for i = 1:64
        for j = 1:64
            d(i,j) = exp(-[p1(i) p2(j)]*iC*[p1(i) p2(j)]'/2);
        end
    end
    subplot(3,2,1)
    imagesc(p2 + P(2),p1 + P(1),d)
    xlabel('2nd parameter','FontSize',12)
    ylabel('1st parameter','FontSize',12)
    title(sprintf('iteration %i',k - 1),'FontSize',16)
    axis square xy

    % sample objective function using N(P,C)
    %----------------------------------------------------------------------
    if k == 1
        N = 256;                       % number of samples
    else
        N = 32;
    end
    p      = P*ones(1,N) + spm_sqrtm(C)*randn(n,N);
    p(:,1) = pmin;
    F      = sparse(N,1);
    for i = 1:N
        F(i) = feval(fun,spm_unvec(p(:,i),Q),varargin{:});
    end
    [m,i] = min(F);
    pmin  = p(:,i);

    % supplement with line search along principal eigenvector
    %----------------------------------------------------------------------
    [U,S] = spm_svd(C);
    U     = U(:,1);
    S     = S(1);
    x     = linspace(-3*sqrt(S),3*sqrt(S),16 + 1);
    for i = 1:(16 + 1)
        p(:,end + 1) = pmin + U*x(i);
        F(end + 1,1) = feval(fun,spm_unvec(p(:,end),Q),varargin{:});
    end
    [m,i]  = min(F);
    pmin   = p(:,i);
    M(k)   = m;
    R(:,k) = pmin;

    % plot objective function along eigenvariate and sampled values
    %----------------------------------------------------------------------
    subplot(3,2,3)
    plot(x,F(end - 16:end),'.')
    xlabel('first eigenvariate','FontSize',12)
    title('objective function','FontSize',16)
    axis square

    subplot(3,2,5)
    plot(sort(F))
    xlabel('sample','FontSize',12)
    title('ranked samples','FontSize',16)
    axis square


    % Laplace approximation: p(P)
    %======================================================================

    % temperature
    %----------------------------------------------------------------------
    T      = sqrt(2)*std(F);

    % mean (P)
    %----------------------------------------------------------------------
    q      = exp(-(F - mean(F))/T);
    q      = q/sum(q);
    P      = p*q;

    % dispersion
    %----------------------------------------------------------------------
    for i = 1:n
        for j = 1:n
            C(i,j) = ((p(i,:) - P(i)).*(p(j,:) - P(j)))*q;
        end
    end

    % report - updated sampling density
    %----------------------------------------------------------------------
    subplot(3,2,2)
    iC      = inv(C(1:2,1:2));
    for i = 1:64
        for j = 1:64
            d(i,j) = exp(-[p1(i) p2(j)]*iC*[p1(i) p2(j)]'/2);
        end
    end
    imagesc(p2 + P(2),p1 + P(1),d)
    title(sprintf('iteration %i',k),'FontSize',16)
    axis square xy

    % superimpose line search and plot means and min(F)
    %----------------------------------------------------------------------
    subplot(3,2,1),hold on
    plot(p(2,end - 16:end),p(1,end - 16:end),'r'), hold off
    subplot(3,2,2),hold on
    plot(p(2,end - 16:end),p(1,end - 16:end),'r'), hold off

    subplot(3,2,4)
    plot(R')
    xlabel('iteration','FontSize',12)
    title('parameter values','FontSize',16)
    axis square

    subplot(3,2,6)
    bar(M)
    xlabel('iteration','FontSize',12)
    title('minimum','FontSize',16)
    axis square
    drawnow

end

% minimiser
%--------------------------------------------------------------------------
P     = spm_unvec(pmin,Q);
