function c=children(t,j)
%CHILDEREN Child nodes.
%   C=CHILDREN(T) returns an N-by-2 array C containing the numbers of the
%   child nodes for each node in the tree T, where N is the number of
%   nodes in the tree.  The children for leaf nodes (those with no children)
%   are returned as 0.
%
%   P=CHILDREN(T,J) takes an array J of node numbers and returns the children
%   for the specified nodes.
%
%   See also CLASSREGTREE, CLASSREGTREE/NUMNODES, CLASSREGTREE/PARENT.

%   Copyright 2006-2007 The MathWorks, Inc.
%   $Revision: 1.1.6.2 $  $Date: 2007/02/15 21:48:00 $

if nargin>=2 && ~validatenodes(t,j)
    error('stats:classregtree:children:InvalidNode',...
          'J must be an array of node numbers or a logical array of the proper size.');
end

if nargin<2
    c = t.children;
else
    c = t.children(j,:);
end


function [traj, infStates] = tapas_hgf_ar1_mab(r, p, varargin)
% Calculates the trajectories of the agent's representations under the AR(1)-HGF in a multi-armed
% bandit task
%
% This function can be called in two ways:
%
% (1) tapas_hgf_ar1_mab(r, p)
%
%     where r is the structure generated by tapas_fitModel and p is the parameter vector in native space;
%
% (2) tapas_hgf_ar1_mab(r, ptrans, 'trans')
%
%     where r is the structure generated by tapas_fitModel, ptrans is the parameter vector in
%     transformed space, and 'trans' is a flag indicating this.
%
% --------------------------------------------------------------------------------------------------
% Copyright (C) 2013 Christoph Mathys, TNU, UZH & ETHZ
%
% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public
% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version). For further details, see the file
% COPYING or <http://www.gnu.org/licenses/>.

% Transform paramaters back to their native space if needed
if ~isempty(varargin) && strcmp(varargin{1},'trans');
    p = tapas_hgf_ar1_mab_transp(r, p);
end

% Number of levels
try
    l = r.c_prc.n_levels;
catch
    l = length(p)/6;

    if l ~= floor(l)
        error('tapas:hgf:UndetNumLevels', 'Cannot determine number of levels');
    end
end

% Number of bandits
try
    b = r.c_prc.n_bandits;
catch
    error('tapas:hgf:NumOfBanditsConfig', 'Number of bandits has to be configured in r.c_prc.n_bandits.');
end

% Unpack parameters
mu_0 = p(1:l);
sa_0 = p(l+1:2*l);
phi  = p(2*l+1:3*l);
m    = p(3*l+1:4*l);
ka   = p(4*l+1:5*l-1);
om   = p(5*l:6*l-2);
th   = exp(p(6*l-1));
al   = p(6*l);

% Add dummy "zeroth" trial
u = [0; r.u(:,1)];
y = [0; r.u(:,2)];

% Number of trials (including prior)
n = size(u,1);

% Construct time axis
if r.c_prc.irregular_intervals
    if size(u,2) > 1
        t = [0; r.u(:,end)];
    else
        error('tapas:hgf:InputSingleColumn', 'Input matrix must contain more than one column if irregular_intervals is set to true.');
    end
else
    t = ones(n,1);
end

% Initialize updated quantities

% Representations
mu = NaN(n,l,b);
pi = NaN(n,l,b);

% Other quantities
muhat = NaN(n,l,b);
pihat = NaN(n,l,b);
v     = NaN(n,l);
w     = NaN(n,l-1);
da    = NaN(n,l);
dau   = NaN(n,1);

% Representation priors
% Note: first entries of the other quantities remain
% NaN because they are undefined and are thrown away
% at the end; their presence simply leads to consistent
% trial indices.
mu(1,:,:) = repmat(mu_0,[1 1 b]);
pi(1,:,:) = repmat(1./sa_0,[1 1 b]);

% Representation update loop
% Pass through trials
for k = 2:1:n
    if not(ismember(k-1, r.ign))

        %%%%%%%%%%%%%%%%%%%%%%
        % Effect of input u(k)
        %%%%%%%%%%%%%%%%%%%%%%

        % 1st level
        % ~~~~~~~~~
        % Prediction
        muhat(k,1,:) = mu(k-1,1,:) +t(k) *phi(1) *(m(1) -mu(k-1,1,:));

        % Precision of prediction
        pihat(k,1,:) = 1/(1/pi(k-1,1,:) +t(k) *exp(ka(1) *mu(k-1,2,:) +om(1)));

        % Input prediction error
        dau(k) = u(k) -muhat(k,1,y(k));

        % Updates
        pi(k,1,:) = pihat(k,1,:);
        pi(k,1,y(k)) = pi(k,1,y(k)) +1/al;

        mu(k,1,:) = muhat(k,1,:);
        mu(k,1,y(k)) = mu(k,1,y(k)) +1/pihat(k,1,y(k)) *1/(1/pihat(k,1,y(k)) +al) *dau(k);

        % Volatility prediction error
        da(k,1) = (1/pi(k,1,y(k)) +(mu(k,1,y(k)) -muhat(k,1,y(k)))^2) *pihat(k,1,y(k)) -1;

        if l > 2
            % Pass through higher levels
            % ~~~~~~~~~~~~~~~~~~~~~~~~~~
            for j = 2:l-1
                % Prediction
                muhat(k,j,:) = mu(k-1,j,:) +t(k) *phi(j) *(m(j) -mu(k-1,j,:));

                % Precision of prediction
                pihat(k,j,:) = 1/(1/pi(k-1,j,:) +t(k) *exp(ka(j) *mu(k-1,j+1,:) +om(j)));

                % Weighting factor
                v(k,j-1) = t(k) *exp(ka(j-1) *mu(k-1,j,y(k)) +om(j-1));
                w(k,j-1) = v(k,j-1) *pihat(k,j-1,y(k));

                % Updates
                pi(k,j,:) = pihat(k,j,:) +1/2 *ka(j-1)^2 *w(k,j-1) *(w(k,j-1) +(2 *w(k,j-1) -1) *da(k,j-1));

                if pi(k,j,1) <= 0
                    error('tapas:hgf:NegPostPrec', 'Negative posterior precision. Parameters are in a region where model assumptions are violated.');
                end

                mu(k,j,:) = muhat(k,j,:) +1/2 *1/pi(k,j,:) *ka(j-1) *w(k,j-1) *da(k,j-1);

                % Volatility prediction error
                da(k,j) = (1/pi(k,j,y(k)) +(mu(k,j,y(k)) -muhat(k,j,y(k)))^2) *pihat(k,j,y(k)) -1;
            end
        end

        % Last level
        % ~~~~~~~~~~
        % Prediction
        muhat(k,l,:) = mu(k-1,l,:) +t(k) *phi(l) *(m(l) -mu(k-1,l,:));

        % Precision of prediction
        pihat(k,l,:) = 1/(1/pi(k-1,l,:) +t(k) *th);

        % Weighting factor
        v(k,l)   = t(k) *th;
        v(k,l-1) = t(k) *exp(ka(l-1) *mu(k-1,l,y(k)) +om(l-1));
        w(k,l-1) = v(k,l-1) *pihat(k,l-1,y(k));

        % Updates
        pi(k,l,:) = pihat(k,l,:) +1/2 *ka(l-1)^2 *w(k,l-1) *(w(k,l-1) +(2 *w(k,l-1) -1) *da(k,l-1));

        if pi(k,l,1) <= 0
            error('tapas:hgf:NegPostPrec', 'Negative posterior precision. Parameters are in a region where model assumptions are violated.');
        end

        mu(k,l,:) = muhat(k,l,:) +1/2 *1/pi(k,l,:) *ka(l-1) *w(k,l-1) *da(k,l-1);

        % Volatility prediction error
        da(k,l) = (1/pi(k,l,y(k)) +(mu(k,l,y(k)) -muhat(k,l,y(k)))^2) *pihat(k,l,y(k)) -1;
    else

        mu(k,:,:) = mu(k-1,:,:);
        pi(k,:,:) = pi(k-1,:,:);

        muhat(k,:,:) = muhat(k-1,:,:);
        pihat(k,:,:) = pihat(k-1,:,:);

        v(k,:)  = v(k-1,:);
        w(k,:)  = w(k-1,:);
        da(k,:) = da(k-1,:);

    end
end

% Remove representation priors
mu(1,:,:)  = [];
pi(1,:,:)  = [];

% Check validity of trajectories
if any(isnan(mu(:))) || any(isnan(pi(:)))
    error('tapas:hgf:VarApproxInvalid', 'Variational approximation invalid. Parameters are in a region where model assumptions are violated.');
else
    % Check for implausible jumps in trajectories
    dmu = diff(mu);
    dpi = diff(pi);
    rmdmu = repmat(sqrt(mean(dmu.^2)),length(dmu),1);
    rmdpi = repmat(sqrt(mean(dpi.^2)),length(dpi),1);

    jumpTol = 256;
    if any(abs(dmu(:)) > jumpTol*rmdmu(:)) || any(abs(dpi(:)) > jumpTol*rmdpi(:))
        error('tapas:hgf:VarApproxInvalid', 'Variational approximation invalid. Parameters are in a region where model assumptions are violated.');
    end
end

% Remove other dummy initial values
muhat(1,:,:) = [];
pihat(1,:,:) = [];
v(1,:)       = [];
w(1,:)       = [];
da(1,:)      = [];
dau(1)       = [];

% Create result data structure
traj = struct;

traj.mu     = mu;
traj.sa     = 1./pi;

traj.muhat  = muhat;
traj.sahat  = 1./pihat;

traj.v      = v;
traj.w      = w;
traj.da     = da;
traj.dau    = dau;

% Updates with respect to prediction
traj.ud = mu -muhat;

% Psi (precision weights on prediction errors)
psi      = NaN(n-1,l);
pi1      = squeeze(pi(:,1,:));
pi1obs   = pi1(sub2ind(size(pi1), (1:size(pi1,1))', y));
psi(:,1) = 1./(al*pi1obs);
for i=2:l
    pihati    = squeeze(pihat(:,i-1,:));
    pihatiobs = pihati(sub2ind(size(pihati), (1:size(pihati,1))', y));
    pii       = squeeze(pi(:,i,:));
    piiobs    = pii(sub2ind(size(pii), (1:size(pii,1))', y));
    psi(:,i)  = pihatiobs./piiobs;
end
traj.psi = psi;

% Epsilons (precision-weighted prediction errors)
epsi        = NaN(n-1,l);
epsi(:,1)   = psi(:,1) .*dau;
epsi(:,2:l) = psi(:,2:l) .*da(:,1:l-1);
traj.epsi   = epsi;

% Full learning rate (full weights on prediction errors)
wt        = NaN(n-1,l);
wt(:,1)   = psi(:,1);
wt(:,2:l) = 1/2 *(v(:,1:l-1) *diag(ka(1:l-1))) .*psi(:,2:l);
traj.wt   = wt;

% Create matrices for use by the observation model
infStates = NaN(n-1,l,b,4);
infStates(:,:,:,1) = traj.muhat;
infStates(:,:,:,2) = traj.sahat;
infStates(:,:,:,3) = traj.mu;
infStates(:,:,:,4) = traj.sa;

return;


function [MDP] = spm_MDP_game_KL(MDP,varargin)
% action selection using active inference (KL formulation)
% FORMAT [MDP] = spm_MDP_game_KL(MDP,[EU])
%
% EU              - optional flag to invoke expected utility only
%
% MDP.T           - process depth (the horizon)
% MDP.N           - number of variational iterations (default 4)
% MDP.S(N,1)      - true initial state
%
% MDP.A(O,N)      - Likelihood of O outcomes given N hidden states
% MDP.B{M}(N,N)   - transition probabilities among hidden states (priors)
% MDP.C(N,1)      - terminal cost probabilities (prior over hidden states)
% MDP.D(N,1)      - initial prior probabilities (prior over hidden states)
%
% MDP.V(T,P)      - P allowable policies (control sequences over T times)
%
% optional:
% MDP.s(1 x T)    - vector of true states  - for deterministic solutions
% MDP.o(1 x T)    - vector of observations - for deterministic solutions
% MDP.a(1 x T)    - vector of action       - for deterministic solutions
% MDP.w(1 x T)    - vector of precisions   - for deterministic solutions
%
% MDP.B{T,M}(N,N) - model transition probabilities for each time point
% MDP.G{T,M}(N,N) - true  transition probabilities for each time point
%                   (default: MDP.G{T,M} = MDP.G{M} = MDP.B{M})
%
% MDP.plot        - switch to suppress graphics: (default: [0])
% MDP.alpha       - upper bound on precision (Gamma hyperprior - shape [8])
% MDP.beta        - precision over precision (Gamma hyperprior - rate  [1])
%
% produces:
%
% MDP.P(M,T)   - probability of emitting an action 1,...,M at time 1,...,T
% MDP.Q(N,T)   - an array of conditional (posterior) expectations over
%                N hidden states and time 1,...,T
% MDP.O(O,T)   - a sparse matrix of ones encoding outcomes at time 1,...,T
% MDP.S(N,T)   - a sparse matrix of ones encoding states at time 1,...,T
% MDP.U(M,T)   - a sparse matrix of ones encoding action at time 1,...,T
% MDP.W(1,T)   - posterior expectations of precision
% MDP.d        - simulated dopamine responses
%
% This routine provides solutions of active inference (minimisation of
% variational free energy) using a generative model based upon a Markov
% decision process. This model and inference scheme is formulated
% in discrete space and time. This means that the generative model (and
% process) are  finite state  machines or hidden Markov models whose
% dynamics are given by transition probabilities among states and the
% likelihood corresponds to a particular outcome conditioned upon
% hidden states. For simplicity, this routine assumes that action
% and hidden controls are isomorphic. If the dynamics of transition
% probabilities of the true process are not provided, this routine will use
% the equivalent probabilities from the generative model.
%
% This particular scheme is designed for any allowable policies or control
% sequences specified in MDP.V. Constraints on allowable policies can limit
% the numerics or combinatorics considerable. For example, situations in
% which one action can be selected at one time can be reduced to T polices
% - with one (shift) control being emitted at all possible time points.
% This specification of polices simplifies the generative model, allowing a
% fairly exhaustive model of potential outcomes - eschewing a mean field
% approximation over successive control states. In brief, the agent simply
% represents the current state and states in the immediate and distant
% future.
%
% The transition probabilities are a cell array of probability transition
% matrices corresponding to each (discrete) the level of the control state.
%
% Mote that the conditional expectations are functions of time but also
% contain expectations about fictive states over time at each time point.
% To create time dependent transition probabilities, one can specify a
% function in place of the transition probabilities under different levels
% of control.
%
% Partially observed Markov decision processes can be modelled by
% specifying a likelihood (as part of a generative model) and absorbing any
% probabilistic mapping between (isomorphic) hidden states and outcomes
% into the transition probabilities G.
%
% See also: spm_MDP, which uses multiple future states and a mean field
% approximation for control states - but allows for different actions
% at all times (as in control problems).
%
% See also: spm_MDP_game, which generalises this scheme and replaces prior
% beliefs about KL control with minimisation of expected free energy.
%__________________________________________________________________________
% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_MDP_game_KL.m 7679 2019-10-24 15:54:07Z spm $

% set up and preliminaries
%==========================================================================

% options and precision defaults
%--------------------------------------------------------------------------
try, PLOT  = MDP.plot;  catch, PLOT  = 0; end
try, alpha = MDP.alpha; catch, alpha = 8; end
try, beta  = MDP.beta;  catch, beta  = 1; end
try, N     = MDP.N;     catch, N     = 4; end

% set up figure if necessary
%--------------------------------------------------------------------------
if PLOT
    if ishandle(PLOT)
        figure(PLOT); clf
        PLOT = 2;
    else
        spm_figure('GetWin','MDP'); clf
    end
end

% generative model and initial states
%--------------------------------------------------------------------------
T     = MDP.T;                     % process depth (the horizon)
Ns    = size(MDP.B{1},1);          % number of hidden states
Nb    = size(MDP.B,1);             % number of time-dependent probabilities
Nu    = size(MDP.B,2);             % number of hidden controls
p0    = eps;                       % smallest probability

% likelihood model (for a partially observed MDP implicit in G)
%--------------------------------------------------------------------------
try
    A  = MDP.A + p0;
    No = size(MDP.A,1);           % number of outcomes
catch
    A  = speye(Ns,Ns) + p0;
    No = Ns;
end
A     = A*diag(1./sum(A));
lnA   = log(A);

% transition probabilities (priors)
%--------------------------------------------------------------------------
for i = 1:T
    for j = 1:Nu
        if i == 1 || Nb == T
            B{i,j}   = MDP.B{i,j} + p0;
            B{i,j}   = B{i,j}*diag(1./sum(B{i,j}));
        else
            B{i,j}   = B{1,j};
        end
    end
end

% terminal probabilities (priors)
%--------------------------------------------------------------------------
try
    C = spm_vec(MDP.C) + p0;
catch
    C = ones(Ns,1);
end
C     = C/sum(C);
lnC   = log(C);

% intital probabilities (priors)
%--------------------------------------------------------------------------
try
    D = spm_vec(MDP.D) + p0;
catch
    D = ones(Ns,1);
end
D     = D/sum(D);
lnD   = log(D);

% generative process (assume the true process is the same as the model)
%--------------------------------------------------------------------------
try
    G = MDP.G;
catch
    G = MDP.B;
end
Ng    = size(G,1);
for i = 1:T
    for j = 1:Nu
        if i == 1 || Ng == T
            G{i,j} = G{i,j} + p0;
            G{i,j} = G{i,j}*diag(1./sum(G{i,j}));
        else
            G{i,j} = G{1,j};
        end
    end
end

% policies and their expectations
%--------------------------------------------------------------------------
V      = MDP.V;
u      = zeros(size(V,2),1);
Np     = size(V,2);                % number of allowable policies
w      = 1:Np;                     % indices of allowable policies

% initial states and outcomes
%--------------------------------------------------------------------------
[p q]  = max(A*MDP.S(:,1));        % initial outcome (index)
s      = find( MDP.S(:,1));        % initial state   (index)
o      = sparse(1,1,q,1,T);        % observations    (index)
S      = sparse(s,1,1,Ns,T);       % states sampled  (1 in K vector)
O      = sparse(q,1,1,No,T);       % states observed (1 in K vector)
a      = sparse(1, T);             % action (index)
U      = sparse(Nu,T);             % action selected (1 in K vector)
P      = sparse(Nu,T);             % posterior beliefs about control
E      = sparse(T,Np);             % posterior beliefs about policies
W      = sparse(1,T);              % posterior precision

% sufficient statistics of hidden states (past, current and last)
%--------------------------------------------------------------------------
gamma  = [];                       % simulated dopamine responses
x      = zeros(Ns,T);

% solve
%==========================================================================
for t  = 1:T

    % allowable policies
    %----------------------------------------------------------------------
    if t > 1

        % record posterior expectations over policies
        %------------------------------------------------------------------
        E(t - 1,w) = u;

        % retain allowable policies (that are consistent with last action)
        %------------------------------------------------------------------
        j = ismember(V(t - 1,:),a(t - 1));
        V = V(:,j);
        u = u(j);
        w = w(j);

    end

    % conditional KL divergence (under allowable policies)
    %======================================================================
    Np    = size(V,2);                % number of allowable policies
    Q     = zeros(Np,Ns);             % value of policies x current state
    for k = 1:Np

        % compositon of future states
        %------------------------------------------------------------------
        Bj = 1;
        for j = t:T
            Bj = B{j,V(j,k)}*Bj;
        end

        % divergence or information gain
        %------------------------------------------------------------------
        if nargin > 1
            Q(k,:) = lnC'*Bj;
        else
            Q(k,:) = lnC'*Bj - sum(Bj.*log(Bj));
        end

    end

    % Variational iterations (assuming precise inference about past action)
    %======================================================================
    for i  = 1:N

        % present state (x)
        %------------------------------------------------------------------
        if t == 1
            v  = lnD;
        else
            v  = log(B{t - 1,a(t - 1)}*x(:,t - 1));
        end
        v      = v + lnA(o(t),:)' + W(t)*Q'*u;
        x(:,t) = spm_softmax(v);

        % precision (W)
        %------------------------------------------------------------------
        if isfield(MDP,'w')
            W(t) = MDP.w(t);
        else
            v    = beta - u'*Q*x(:,t);
            W(t) = alpha/v;
        end

        % policy (u)
        %------------------------------------------------------------------
        v      = W(t)*Q*x(:,t);
        u      = spm_softmax(v);
        E(t,w) = u;

        % re-compute precision for first iteration
        %------------------------------------------------------------------
        if t == 1
            v    = beta - u'*Q*x(:,t);
            W(t) = alpha/v;
        end

        % simulated dopamine responses (precision as each iteration)
        %------------------------------------------------------------------
        gamma(end + 1,1) = W(t);

    end

    % posterior expectations (control)
    %======================================================================
    for j = 1:Nu
        for k = t:T
            P(j,k) = sum(u(ismember(V(k,:),j)));
        end
    end

    % next action (the action that minimises expected free energy)
    %------------------------------------------------------------------
    try
        a(t) = MDP.a(t);
    catch
        try
            a(t) = find(rand < cumsum(P(:,t)),1);
        catch
            error('there are no more allowable policies')
        end
    end

    % save action
    %------------------------------------------------------------------
    U(a(t),t) = 1;

    % sampling of next state (outcome)
    %======================================================================
    if t < T

        % next sampled state
        %------------------------------------------------------------------
        try
            s(t + 1) = MDP.s(t + 1);
        catch
            s(t + 1) = find(rand < cumsum(G{t,a(t)}(:,s(t))),1);
        end

        % next obsverved state
        %------------------------------------------------------------------
        try
            o(t + 1) = MDP.o(t + 1);
        catch
            o(t + 1) = find(rand < cumsum(A(:,s(t + 1))),1);
        end

        % save outcome and state sampled
        %------------------------------------------------------------------
        W(1,t + 1)        = W(t);
        O(o(t + 1),t + 1) = 1;
        S(s(t + 1),t + 1) = 1;

    end

    % plot
    %======================================================================
    if PLOT > 0

        % posterior beliefs about hidden states
        %------------------------------------------------------------------
        subplot(4,2,1)
        imagesc(1 - [x C*max(max(x))/max(C)])
        if size(x,1) > 128
            hold on, spm_spy(x,16,1), hold off
        end
        title('Inferred states (and utility)','FontSize',14)
        xlabel('Time','FontSize',12)
        ylabel('Hidden state','FontSize',12)

        % posterior beliefs about control states
        %==================================================================
        subplot(4,2,2)

        % make previous plots dotted lines
        %------------------------------------------------------------------
        if T > 2
            h     = get(gca,'Children'); hold on
            for i = 1:length(h)
                set(h(i),'LineStyle',':');
            end
            plot(P')
            title('Inferred policy','FontSize',14)
            xlabel('Time','FontSize',12)
            ylabel('Control state','FontSize',12)
            spm_axis tight
        else
            bar(P)
            title('Inferred policy','FontSize',14)
            xlabel('Contol state','FontSize',12)
            ylabel('Posterior expectation','FontSize',12)
        end

        % policies
        %------------------------------------------------------------------
        subplot(4,2,3)
        imagesc(MDP.V')
        title('Allowable policies','FontSize',14)
        ylabel('Policy','FontSize',12)
        xlabel('Time','FontSize',12)

        % expectations over policies
        %------------------------------------------------------------------
        subplot(4,2,4)
        imagesc(E')
        title('Posterior probability','FontSize',14)
        ylabel('Policy','FontSize',12)
        xlabel('Time','FontSize',12)

        % true state (outcome)
        %------------------------------------------------------------------
        subplot(4,2,5)
        if size(S,1) > 128
            spm_spy(S,16)
        else
            imagesc(1 - S)
        end
        title('True states','FontSize',14)
        ylabel('State','FontSize',12)

        % sample (observation)
        %------------------------------------------------------------------
        subplot(4,2,7)
        if size(O,1) > 128
            spm_spy(O,16,1)
        else
            imagesc(1 - O)
        end
        title('Observed states','FontSize',14)
        xlabel('Time','FontSize',12)
        ylabel('State','FontSize',12)

        % action sampled (selected)
        %------------------------------------------------------------------
        subplot(4,2,6)
        if size(U,1) > 128
            spm_spy(U,16,1)
        else
            imagesc(1 - U)
        end
        title('Selected action','FontSize',14)
        ylabel('Action','FontSize',12)

        % expected action
        %------------------------------------------------------------------
        subplot(4,2,8)
        plot((1:length(gamma))/N,gamma)
        title('Expected precision (confidence)','FontSize',14)
        xlabel('Time','FontSize',12)
        ylabel('Precision','FontSize',12)
        spm_axis tight
        drawnow

    end

end

% deconvolve to simulate dopamine responses
%--------------------------------------------------------------------------
da     = pinv( tril(toeplitz(exp(-((1:length(gamma)) - 1)'/8))) )*gamma;

% assemble results and place in NDP structure
%--------------------------------------------------------------------------
MDP.P  = P;              % probability of action at time 1,...,T - 1
MDP.Q  = x;              % conditional expectations over N hidden states
MDP.O  = O;              % a sparse matrix, encoding outcomes at 1,...,T
MDP.S  = S;              % a sparse matrix, encoding the states
MDP.U  = U;              % a sparse matrix, encoding the action
MDP.W  = W;              % posterior expectations of precision
MDP.d  = gamma;          % simulated dopamine responses
MDP.da = da;             % simulated dopamine responses (deconvolved)


function [U] = spm_dcm_eeg_channelmodes(dipfit,Nm,xY)
% Returns the channel eigenmodes
% FORMAT [U] = spm_dcm_eeg_channelmodes(dipfit,Nm)
% FORMAT [U] = spm_dcm_eeg_channelmodes(dipfit,Nm,xY)
% dipfit  - spatial model specification
% Nm      - number of modes required (upper bound)
% xY      - data structure
% U       - channel eigenmodes
%__________________________________________________________________________
%
% Uses SVD (an eigensolution) to identify the patterns with the greatest
% prior covariance; assuming independent source activity in the specified
% spatial (forward) model.
%
% if xY is specifed a CVA (a generalised eigensolution) will be used to
% find the spatial modes that are best by the spatial model
%
% U is scaled to ensure trace(U'*L*L'*U) = Nm
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_dcm_eeg_channelmodes.m 6806 2016-06-08 20:04:30Z karl $

% number of channels and modes
%--------------------------------------------------------------------------
if nargin < 2, Nm = 8; end

% Spatial modes
%--------------------------------------------------------------------------
[pE pC] = spm_L_priors(dipfit);

% evaluate eigenmodes of gain of covariance in sensor space
%--------------------------------------------------------------------------
dGdg  = spm_diff('spm_erp_L',pE,dipfit,1,'nocat');
L     = spm_cat(dGdg(find(spm_vec(pC))));

% eigen-mode reduction
%--------------------------------------------------------------------------
[U S] = spm_svd(L*L',exp(-8));
S     = diag(S);

if nargin > 2

    % reduce lead field to 32 or less d.f.
    %----------------------------------------------------------------------
    n    = min(size(U,2),32);
    L    = U(:,1:n)*diag(sqrt(S(1:n)));

    % response variable
    %----------------------------------------------------------------------
    for i = 1:length(xY.y)
        Y{i} = xY.y{i};
        Y{i} = Y{i}';
    end
    Y     = spm_cat(Y);
    CVA   = spm_cva(Y,L);
    U     = spm_en(CVA.w);
    S     = U'*(L*L')*U;
    S     = diag(S);
end

% eigen-mode reduction
%--------------------------------------------------------------------------
try
    U = U(:,1:Nm);
    S = S(  1:Nm);
end

% re-scale spatial projector
%--------------------------------------------------------------------------
U     = U/sqrt(mean(S));


% tracker_ensemble: Correlation filter tracking with convolutional features
%
% Input:
%   - video_path:          path to the image sequence
%   - img_files:           list of image names
%   - pos:                 intialized center position of the target in (row, col)
%   - target_sz:           intialized target size in (Height, Width)
% 	- padding:             padding parameter for the search area
%   - lambda:              regularization term for ridge regression
%   - output_sigma_factor: spatial bandwidth for the Gaussian label
%   - interp_factor:       learning rate for model update
%   - cell_size:           spatial quantization level
%   - show_visualization:  set to True for showing intermediate results
% Output:
%   - positions:           predicted target position at each frame
%   - time:                time spent for tracking
%
%   It is provided for educational/researrch purpose only.
%   If you find the software useful, please consider cite our paper.
%
%   Hierarchical Convolutional Features for Visual Tracking
%   Chao Ma, Jia-Bin Huang, Xiaokang Yang, and Ming-Hsuan Yang
%   IEEE International Conference on Computer Vision, ICCV 2015
%
% Contact:
%   Chao Ma (chaoma99@gmail.com), or
%   Jia-Bin Huang (jbhuang1@illinois.edu).

function [positions, time] = tracker_ensemble(video_path, img_files, pos, target_sz, ...
    padding, lambda, output_sigma_factor, interp_factor, cell_size, show_visualization)

% ================================================================================
% Environment setting
% ================================================================================

indLayers = [37, 28, 19];   % The CNN layers Conv5-4, Conv4-4, and Conv3-4 in VGG Net
nweights  = [1, 0.5, 0.25]; % Weights for combining correlation filter responses
numLayers = length(indLayers);

% Get image size and search window size
im_sz     = size(imread([video_path img_files{1}]));
window_sz = get_search_window(target_sz, im_sz, padding);

% Compute the sigma for the Gaussian function label
output_sigma = sqrt(prod(target_sz)) * output_sigma_factor / cell_size;

%create regression labels, gaussian shaped, with a bandwidth
%proportional to target size    d=bsxfun(@times,c,[1 2]);

l1_patch_num = floor(window_sz/ cell_size);

% Pre-compute the Fourier Transform of the Gaussian function label
yf = fft2(gaussian_shaped_labels(output_sigma, l1_patch_num));

% Pre-compute and cache the cosine window (for avoiding boundary discontinuity)
cos_window = hann(size(yf,1)) * hann(size(yf,2))';

% Create video interface for visualization
if(show_visualization)
    update_visualization = show_video(img_files, video_path);
end

% Initialize variables for calculating FPS and distance precision
time      = 0;
rects = zeros(numel(img_files), 4);
nweights  = reshape(nweights,1,1,[]);

% Note: variables ending with 'f' are in the Fourier domain.
model_xf     = cell(1, numLayers);
model_alphaf = cell(1, numLayers);

current_scale_factor=1;

% ================================================================================
% Start tracking
% ================================================================================
for frame = 1:numel(img_files),
    im = imread([video_path img_files{frame}]); % Load the image at the current frame
    if ismatrix(im)
        im = cat(3, im, im, im);
    end

    tic();
    % ================================================================================
    % Predicting the object position from the learned object model
    % ================================================================================
    if frame > 1
        % Extracting hierarchical convolutional features
        feat = extractFeature(im, pos, window_sz, cos_window, indLayers);
        % Predict position
        pos  = predictPosition(feat, pos, indLayers, nweights, cell_size, l1_patch_num, ...
            model_xf, model_alphaf);

        % Scale estimation
        current_scale_factor = estimate_scale( rgb2gray(im), pos, current_scale_factor);
    else
        init_scale_para(rgb2gray(im), target_sz, pos);
    end

    % ================================================================================
    % Learning correlation filters over hierarchical convolutional features
    % ================================================================================
    % Extracting hierarchical convolutional features
    feat  = extractFeature(im, pos, window_sz, cos_window, indLayers);
    % Model update
    [model_xf, model_alphaf] = updateModel(feat, yf, interp_factor, lambda, frame, ...
        model_xf, model_alphaf);

    % ================================================================================
    % Save predicted position and timing
    % ================================================================================
    % positions(frame,:) = pos;

    target_sz_t=target_sz*current_scale_factor;
    box = [pos([2,1]) - target_sz_t([2,1])/2, target_sz_t([2,1])];
    rects(frame,:)=box;

    time = time + toc();

    % Visualization
    if show_visualization,
        %box = [pos([2,1]) - target_sz([2,1])/2, target_sz([2,1])];
        stop = update_visualization(frame, box);
        if stop, break, end  %user pressed Esc, stop early
        drawnow
        % 			pause(0.05)  % uncomment to run slower
    end
end

end

function pos = predictPosition(feat, pos, indLayers, nweights, cell_size, l1_patch_num, ...
    model_xf, model_alphaf)

% ================================================================================
% Compute correlation filter responses at each layer
% ================================================================================
res_layer = zeros([l1_patch_num, length(indLayers)]);

for ii = 1 : length(indLayers)
    zf = fft2(feat{ii});
    kzf=sum(zf .* conj(model_xf{ii}), 3) / numel(zf);

    temp= real(fftshift(ifft2(model_alphaf{ii} .* kzf)));  %equation for fast detection
    res_layer(:,:,ii)=temp/max(temp(:));
end

% Combine responses from multiple layers (see Eqn. 5)
response = sum(bsxfun(@times, res_layer, nweights), 3);

% ================================================================================
% Find target location
% ================================================================================
% Target location is at the maximum response. we must take into
% account the fact that, if the target doesn't move, the peak
% will appear at the top-left corner, not at the center (this is
% discussed in the KCF paper). The responses wrap around cyclically.
[vert_delta, horiz_delta] = find(response == max(response(:)), 1);
vert_delta  = vert_delta  - floor(size(zf,1)/2);
horiz_delta = horiz_delta - floor(size(zf,2)/2);

% Map the position to the image space
pos = pos + cell_size * [vert_delta - 1, horiz_delta - 1];

end

function [model_xf, model_alphaf] = updateModel(feat, yf, interp_factor, lambda, frame, ...
    model_xf, model_alphaf)

numLayers = length(feat);

% ================================================================================
% Initialization
% ================================================================================
xf       = cell(1, numLayers);
alphaf   = cell(1, numLayers);

% ================================================================================
% Model update
% ================================================================================
for ii=1 : numLayers
    xf{ii} = fft2(feat{ii});
    kf = sum(xf{ii} .* conj(xf{ii}), 3) / numel(xf{ii});
    alphaf{ii} = yf./ (kf+ lambda);   % Fast training
end

% Model initialization or update
if frame == 1,  % First frame, train with a single image
    for ii=1:numLayers
        model_alphaf{ii} = alphaf{ii};
        model_xf{ii} = xf{ii};
    end
else
    % Online model update using learning rate interp_factor
    for ii=1:numLayers
        model_alphaf{ii} = (1 - interp_factor) * model_alphaf{ii} + interp_factor * alphaf{ii};
        model_xf{ii}     = (1 - interp_factor) * model_xf{ii}     + interp_factor * xf{ii};
    end
end

end

function feat  = extractFeature(im, pos, window_sz, cos_window, indLayers)

% Get the search window from previous detection
patch = get_subwindow(im, pos, window_sz);
% Extracting hierarchical convolutional features
feat  = get_features(patch, cos_window, indLayers);

end


% Change to your downloaded location
clear
addpath('C:\liblinear\matlab')
addpath('../training_code')
addpath('../utilities')

%% load shared definitions and AU data
shared_defs;

% Set up the hyperparameters to be validated
hyperparams.c = 10.^(-7:1:1);
hyperparams.e = 10.^(-3);

hyperparams.validate_params = {'c', 'e'};

% Set the training function
svr_train = @svm_train_linear;

% Set the test function (the first output will be used for validation)
svr_test = @svm_test_linear;

%%
for a=1:numel(aus)

    au = aus(a);

    rest_aus = setdiff(all_aus, au);

    % make sure validation data's labels are balanced
    [users_train, users_valid] = get_balanced_fold(DISFA_dir, users, au, 1/3, 1);

    % need to split the rest
    [train_samples, train_labels, valid_samples, valid_labels, ~, PC, means, scaling, valid_ids, valid_success] = Prepare_HOG_AU_data_generic_dynamic(users_train, users_valid, au, rest_aus, DISFA_dir, hog_data_dir);

    train_labels(train_labels > 1) = 1;
    valid_labels(valid_labels > 1) = 1;

    train_samples = sparse(train_samples);
    valid_samples = sparse(valid_samples);

    %% Validate here
    hyperparams.success = valid_success;

    [ best_params, ~ ] = validate_grid_search_no_par(svr_train, svr_test, false, train_samples, train_labels, valid_samples, valid_labels, hyperparams);

    model = svr_train(train_labels, train_samples, best_params);

    [~, prediction] = svr_test(valid_labels, valid_samples, model);

    name = sprintf('classifiers/AU_%d_dyn.mat', au);

    [ accuracies, F1s, corrs, ccc, rms, classes ] = evaluate_regression_results( prediction, valid_labels );

    save(name, 'model', 'accuracies', 'F1s', 'corrs', 'rms', 'ccc', 'prediction', 'valid_labels');

    name = sprintf('classifiers/AU_%d_dyn.dat', au);

    pos_lbl = model.Label(1);
    neg_lbl = model.Label(2);

    w = model.w(1:end-1)';
    b = model.w(end);

    svs = bsxfun(@times, PC, 1./scaling') * w;
    write_lin_svm(name, means, svs, b, pos_lbl, neg_lbl);
end


function res = spm_eeg_regressors_movement_ctf(S)
% Generate movement regressors for CTF MEG data
% S                     - input structure
% fields of S:
%    S.D                - M/EEG object
%
%    Additional parameters can be defined specific for each plugin
% Output:
%  res -
%   If no input is provided the plugin returns a cfg branch for itself
%
%   If input is provided the plugin returns
%______________________________________________________________________________________
% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak

SVNrev = '$Rev: 6186 $';

if nargin == 0

    Dmov        = cfg_files;
    Dmov.tag    = 'Dmov';
    Dmov.name   = 'Movement dataset name';
    Dmov.filter = 'mat';
    Dmov.num    = [1 1];
    Dmov.help   = {'Select the M/EEG mat file containing continuous head localisation data.',...
        'This might or might not be the same as the dataset being analysed.'};

    movement_ctf = cfg_branch;
    movement_ctf.tag = 'movement_ctf';
    movement_ctf.name = 'CTF head movements';
    movement_ctf.val = {Dmov};

    res = movement_ctf;

    return
end

%read HLC-channels
%HLC0011 HLC0012 HLC0013 x, y, z coordinates of nasion-coil in m.
%HLC0021 HLC0022 HLC0023 x, y, z coordinates of lpa-coil in m.
%HLC0031 HLC0032 HLC0033 x, y, z coordinates of rpa-coil in m.
hlc_chan_label = {'HLC0011' 'HLC0012' 'HLC0013'...
    'HLC0021' 'HLC0022' 'HLC0023'...
    'HLC0031' 'HLC0032' 'HLC0033'};

%-Startup
%--------------------------------------------------------------------------
spm('sFnBanner', mfilename, SVNrev);
spm('FigName','CTF movement regressors');

if iscell(S.Dmov)
    S.Dmov = char(S.Dmov);
end

Dmov = spm_eeg_load(S.Dmov);
D    = spm_eeg_load(S.D);

hlc_chan_ind = Dmov.indchannel(hlc_chan_label);

if length(hlc_chan_ind) ~= 9
    error('Nine CTF HLC channels should be present to define movement regressors.');
end

if ~isequal(Dmov.transformtype, 'time')
    error('The movement dataset should be in the time domain.');
end

if isequal(D.type, 'continuous')

    if ~isequal(Dmov.type, 'continuous') || (D.time(1) < Dmov.time(1)) || (D.time(end)>Dmov.time(end))
        error('All times of the input dataset should be within the movement dataset.');
    end

    data = Dmov(hlc_chan_ind, :);

    if D.fsample ~= Dmov.fsample
        [data, alpha] = spm_timeseries_resample(data, D.fsample/Dmov.fsample);
    else
        alpha = 1;
    end

    start = round(alpha*Dmov.indsample(D.time(1)));

    data = data(:, start:(start+D.nsamples-1));

else
    if D.ntrials ~= Dmov.ntrials
        error('Trial numbers should be equal between input and movement dataset.');
    end

    data = Dmov(hlc_chan_ind, :, :);

    if S.summarise
        data = spm_squeeze(mean(data, 2), 2);
    else
        data = reshape(data, size(data, 1), []);
    end

end

res.R     = hpi2mov(data);

res.names = {'x', 'y', 'z', 'pitch', 'roll', 'yaw'};

spm('FigName','CTF movement regressors: done');

function P = hpi2mov(data)

% John Ashburner

ref   = reshape(data(:,1),3,3)*1000;
muRef = mean(ref,2);
ref0  = ref - repmat(muRef, 1, 3);
P     = zeros(size(data,2),6);

ns   = size(data, 2);

spm_progress_bar('Init', ns, 'Computing movement parameters'); drawnow;
if ns > 100, Ibar = floor(linspace(1, ns,100));
else Ibar = 1:ns; end

for i=1:ns
    cur     = reshape(data(:,i),3,3)*1000;
    muCur   = mean(cur,2);
    cur0    = cur - repmat(muCur, 1, 3);
    [U,SS,V] = svd(cur0*ref0');
    Ri      = U*V';
    if det(Ri)<0, V(:,end)=-V(:,end); Ri=U*V'; end
    p       = spm_imatrix([Ri muCur-Ri*muRef; 0 0 0 1]);
    P(i,:)  = p(1:6);

    if ismember(i, Ibar)
        spm_progress_bar('Set', i); drawnow;
    end
end

spm_progress_bar('Clear');

P = detrend(P, 'constant');
P = P./repmat(std(P), size(P, 1), 1);


classdef PerimeterInteriorCircleExperiment < handle

    methods (Access = public)

        function obj = PerimeterInteriorCircleExperiment()
            obj.computeGradientVariationWithRadius();
            obj.computeGradientVariationWithBoundary();
            obj.computeGradientSurfPerimeterComputer();
        end

    end

    methods (Access = private)

        function computeGradientVariationWithBoundary(obj)
            s = obj.createParams();
            g = GradientVariationWithBoundaryExperiment(s);
            g.compute();
        end

        function computeGradientVariationWithRadius(obj)
            s = obj.createParams();
            g = GradientVariationWithRadiusExperiment(s);
            g.compute();
        end

        function computeGradientSurfPerimeterComputer(obj)
            s = obj.createParams();
            g = GradientSurfPerimeterComputer(s);
            g.compute();
        end

    end

    methods (Access = private, Static)

        function s = createParams()
            radius = 0.25;
            s.circleCase = 'interior';
            s.levelSetParams.fracRadius = radius;
            s.levelSetParams.type = 'circle';
            L = 1;
            halfSide = L/2;
            s.levelSetParams.fracRadius = radius/halfSide;
            s.curvature = 1/radius;
            s.circleCase = 'interior';
            s.nameCase = 'GradientCirclePerimeterExperiment';
            nameRoot   = 'SquareMacroTriangle';
            s.inputFiles = {nameRoot; [nameRoot,'Fine'];[nameRoot,'FineFine']};
            s.outputFolder = '/home/alex/git-repos/Perimeter/AllImages/';
        end

    end

end


function varargout = min(varargin)
%MIN   Minimum value of a DISKFUN in one direction.
%   MIN(f) returns a chebfun representing the minimum of the DISKFUN along
%   the latitude direction, i.e, MIN(f) = @(lambda) max( f ( lambda, : ) ).
%
%   MIN(f, [], dim) returns a CHEBFUN representing the minimum of f along
%   direction DIM. DIM = 1 is used to compute max along the radial-direction
%   and DIM = 2 along the angular-direction.
%
%   WARNING: This function is not always accurate to the expected precision.
%
%   For the global minimum use MIN2.
%
% See also DISKFUN/MAX, DISKFUN/MAX2, DISKFUN/MIN2, DISKFUN/MINANDMAX2.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

[varargout{1:nargout}] = min@separableApprox(varargin{:});

end


function [tests,pass,perf]=test_mat2wfdb(varargin)

  %Generate 3 different signals and convert them to signed 16 bit in WFDB format
str1=['N=1024;Fs=48000;tm=[0:1/Fs:(N-1)/Fs]'';adu=''V/mV/V'';' ...
    'info=''Example 1'';sig1=double(int16(linspace(-2^15,2^15,N)''));'...
    'sig2=double(int8(sin(2*pi*tm*1000).*(2^7)))./(2^7);' ...
    'sig3=(rand(N,1) > 0.97)*2 -1 + 2^16;sig=[sig1 sig2 sig3];' ...
    'mat2wfdb(sig,''Ex1'',Fs,[],adu,info);'];

cln1=['delete([pwd filesep ''Ex1*'']);'];

inputs={'verbose'};
verbose=0;
for n=1:nargin
    if(~isempty(varargin{n}))
        eval([inputs{n} '=varargin{n};']);
    end
end

%Test the examples
test_string={str1};
clean_up={cln1};

[tests,pass,perf]=test_wrapper(test_string,clean_up,verbose);


function h = draw_3d(A,b,C,d,obstacles,lb,ub)
  import iris.drawing.drawPolyFromVertices;
  import iris.thirdParty.polytopes.*;

  h = figure(2);
  cla
  hold on
  if ~isempty(obstacles)
    for j = 1:numel(obstacles)
      drawPolyFromVertices(obstacles{j},'k','FaceAlpha',0.5);
    end
  end

  if ~isempty(A)
    V = lcon2vert(A, b);
    drawPolyFromVertices(V', 'r');
  end
  th = linspace(0,2*pi,20);
  y = [cos(th);sin(th);zeros(size(th))];
  for phi = linspace(0,pi,10)
    T = makehgtform('xrotate', phi);
    R = T(1:3,1:3);
    y = [y, R * y];
  end
  x = bsxfun(@plus, C*y, d);
  drawPolyFromVertices(x, 'b', 'FaceAlpha', 1)
  xlim([lb(1),ub(1)])
  ylim([lb(2),ub(2)])
  zlim([lb(3),ub(3)])
  camtarget(0.5*(lb+ub))
  campos([lb(1)-2,lb(2)-2,ub(3)])
%   axis off;
end


function [varargout]=padLinDim(varargin)

% function [XP,indOriginal]=padLinDim(X,numPad,padDim)
% -----------------------------------------------------------------------
% Pad arrays (including vectors) allong dimension padDim with numPad
% entries which are linearly extrapolated allong that direction.
%
% -----------------------------------------------------------------------

%% Parse input

switch nargin
    case 1
        X=varargin{1};
        numPad=1;
        padDim=1;
        dirOpt='both';
    case 2
        X=varargin{1};
        numPad=varargin{2};
        padDim=1;
        dirOpt='both';
    case 3
        X=varargin{1};
        numPad=varargin{2};
        padDim=varargin{3};
        dirOpt='both';
    case 4
        X=varargin{1};
        numPad=varargin{2};
        padDim=varargin{3};
        dirOpt=varargin{4};
end

switch dirOpt
    case 'both'
        [XP,indOriginal]=padLinDimStart(X,numPad,padDim);
        XP=flip(XP,padDim);
        XP=padLinDimStart(XP,numPad,padDim);
        XP=flip(XP,padDim);
    case 'start'
        [XP,indOriginal]=padLinDimStart(X,numPad,padDim);
    case 'end'
        X=flip(X,padDim);
        [XP,indOriginal]=padLinDimStart(X,numPad,padDim);
        XP=flip(XP,padDim);
end

varargout{1}=XP;
varargout{2}=indOriginal;

end

%%

function [varargout]=padLinDimStart(X,numPad,padDim)
numDims=ndims(X);
siz=size(X);

Q=1:1:numDims;
Q=Q(Q~=padDim);
n=prod(siz(Q));
A=zeros(n,numDims);
A(:,padDim)=1;
for q=Q(:)'
    s=siz(q);
    A(:,q)=repmat(1:1:s,1,n/s);
end
B=A;
B(:,padDim)=2;

indDiff_1=sub2indn(siz,A);
indDiff_2=sub2indn(siz,B);

sizD=siz;
sizD(padDim)=1;

X1=reshape(X(indDiff_1),sizD);
X2=reshape(X(indDiff_2),sizD);
dX=X2-X1;

sizn=siz;
sizn(padDim)=sizn(padDim)+numPad;

IND=1:1:prod(sizn);
A=ind2subn(sizn,IND);
logicOld=(A(:,padDim)-numPad)>0;
indOriginal=IND(logicOld);

XP=zeros(sizn);
XP(indOriginal)=X;

A=zeros(n,numDims);
for qq=Q(:)'
    s=sizn(qq);
    A(:,qq)=repmat(1:1:s,1,n/s);
end

for q=1:1:numPad
    A(:,padDim)=q;
    indSet=sub2indn(sizn,A);
    XP(indSet)=((numPad-q)+1);
    XP(indSet)=X1-(((numPad-q)+1).*dX);
end

varargout{1}=XP;
varargout{2}=indOriginal;
end
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [perimeterEdges,eulerCondition] = findLegalPerimeters2(mesh,perimDist)
%
%  [perimeterEdges,eulerCondition] = findLegalPerimeters2(mesh,perimDist)
%
% Given a mesh structure (nodes, edges, distances from start point) and a
% threshold distance this routine will return a list of edges that
% constitute separate perimeters at a distance of perimDist from the start
% point.
%
% Note that because of intrinsic curvature, there can be more than one
% perimeter at the required distance. This routine makes sure that it
% returns separate perimeters (ones with no common nodes).

% Find perims with simple thold
insideNodes=find(mesh.dist<=perimDist);
insideNodes=insideNodes(:);

insideNodes = removeHangingNodes(mesh,insideNodes); % Cleans up the mesh
[perimeterEdges,eulerCondition]=findGroupPerimeter(mesh,insideNodes); % Find perimeter(s) - there may be more than 1
badPerimNodes = findBadPerimNodes(mesh,perimeterEdges); % See if some perimeters are joined up.

numBadNodes=9999999; % Hope we don't get more than this...

while (numBadNodes>0)
    [perimeterEdges,eulerCondition]=findGroupPerimeter(mesh,insideNodes);
	length(perimeterEdges);
	length(unique(perimeterEdges,'rows'));
    fprintf('Euler number=%d\n',eulerCondition);

    badPerimNodes=findBadPerimNodes(mesh,perimeterEdges);
    numBadNodes=length(badPerimNodes);
    fprintf('There are %d bad perim nodes.\n',numBadNodes);

    if(numBadNodes)
		[insideNodes]=correctBadNodes(mesh,insideNodes,badPerimNodes); % Splits up joined perimeters
		insideNodes=removeHangingNodes(mesh,insideNodes); % Cleans up mesh again
    end
end

return;


function [M q]=modularity_probtune_und_sign(W,qtype,M,p)
%MODULARITY_PROBTUNE_UND_SIGN     Optimal community structure, modularity and degeneracy
%
%   Ci     = modularity_probtune_und_sign(W,   [],Ci0,0.01);
%   Ci     = modularity_probtune_und_sign(W,'sta',Ci0,0.01);
%   [Ci Q] = modularity_probtune_und_sign(W,'sta',Ci0,0.01);
%
%   The optimal community structure is a subdivision of the network into
%   nonoverlapping groups of nodes in a way that maximizes the number of
%   within-group edges, and minimizes the number of between-group edges.
%   The modularity is a statistic that quantifies the degree to which the
%   network may be subdivided into such clearly delineated groups.
%   High-modularity degeneracy is the presence of many topologically
%   distinct high-modularity partitions of the network.
%
%   This algorithm is inspired by the Kernighan-Lin fine-tuning algorithm
%   and is designed to probabilistically refine a previously detected
%   community by incorporating random node moves into a finetuning
%   algorithm.
%
%   Input:      W,      undirected (weighted or binary) connection matrix
%                       with positive and negative weights
%
%               qtype,  modularity type (see Rubinov and Sporns, 2011)
%                           'sta',  Q_* (default if qtype is not specified)
%                           'pos',  Q_+
%                           'smp',  Q_simple
%                           'gja',  Q_GJA
%                           'neg',  Q_-
%
%               Ci0,    initial community affiliation vector (optional)
%
%               p,      probability of random node moves
%
%
%   Output:     Ci,     refined community affiliation vector
%               Q,      modularity (qtype dependent)
%
%   Note: Ci and Q may vary from run to run, due to heuristics in the
%   algorithm. Consequently, it may be worth to compare multiple runs.
%
%   References:
%   Rubinov and Sporns (2011) NeuroImage.
%   Sun et al. (2008)  Europhysics Lett 86, 28004.
%
%
%   Mika Rubinov, UNSW, 2011

%   Modification History:
%   Mar 2011: Original

n=length(W);                                                %number of nodes/modules
if ~exist('qtype','var') || isempty(qtype);
    qtype = 'sta';
end
if isempty(M);
    M = 1:n;
else
    [dum dum M] = unique(M(:).');                           %align module indices
end

W0= W.*(W>0);                                               %positive weights matrix
W1=-W.*(W<0);                                               %negative weights matrix
s0=sum(W0(:));                                              %positive sum of weights
s1=sum(W1(:));                                              %negative sum of weights
Knm0=zeros(n,n);                                            %positive node-to-module degree
Knm1=zeros(n,n);                                            %negative node-to-module degree
for m=1:max(M)                                              %loop over modules
    Knm0(:,m)=sum(W0(:,M==m),2);
    Knm1(:,m)=sum(W1(:,M==m),2);
end
Kn0=sum(Knm0,2);                                            %positive node degree
Kn1=sum(Knm1,2);                                            %negative node degree
Km0=sum(Knm0,1);                                            %positive module degree
Km1=sum(Knm1,1);                                            %negative module degree

switch qtype
    case 'smp';  d0 = 1/s0;       d1 = 1/s1;                %dQ = dQ0/s0 - dQ1/s1;
    case 'gja';  d0 = 1/(s0+s1);  d1 = 1/(s0+s1);           %dQ = (dQ0 - dQ1)/(s0+s1);
    case 'sta';  d0 = 1/s0;       d1 = 1/(s0+s1);           %dQ = dQ0/s0 - dQ1/(s0+s1);
    case 'pos';  d0 = 1/s0;       d1 = 0;                   %dQ = dQ0/s0;
    case 'neg';  d0 = 0;          d1 = 1/s1;                %dQ = -dQ1/s1;
    otherwise; error('qtype unknown');
end
if ~s0                                                      %adjust for absent positive weights
    s0=1;
    d0=0;
end
if ~s1                                                      %adjust for absent negative weights
    s1=1;
    d1=0;
end

for u=randperm(n);                                          %loop over all nodes in random order
    ma = M(u);                                              %current module of u
    r=rand<p;
    if r                                                    %with probability p
        mb = ceil(rand*n);                                  %choose random new module
    else
        dQ0 = (Knm0(u,:)+W0(u,u)-Knm0(u,ma)) - Kn0(u).*(Km0+Kn0(u)-Km0(ma))/s0;     %positive dQ
        dQ1 = (Knm1(u,:)+W1(u,u)-Knm1(u,ma)) - Kn1(u).*(Km1+Kn1(u)-Km1(ma))/s1;     %negative dQ
        dQ = d0*dQ0 - d1*dQ1;                               %rescaled changes in modularity
        dQ(ma) = 0;                                         %no changes for same module
        [max_dQ mb] = max(dQ);                              %maximal increase in modularity and corresponding module
    end
    if r || (max_dQ>1e-10)                                  %if maximal increase is positive (equiv. dQ(mb)>dQ(ma))
        M(u) = mb;                                          %reassign module

        Knm0(:,mb)=Knm0(:,mb)+W0(:,u);
        Knm1(:,mb)=Knm1(:,mb)+W1(:,u);
        Knm0(:,ma)=Knm0(:,ma)-W0(:,u);
        Knm1(:,ma)=Knm1(:,ma)-W1(:,u);
        Km0(mb)=Km0(mb)+Kn0(u);
        Km1(mb)=Km1(mb)+Kn1(u);
        Km0(ma)=Km0(ma)-Kn0(u);
        Km1(ma)=Km1(ma)-Kn1(u);
    end
end

[dum dum M]=unique(M(:).');                                 %realign module indices
if nargout==2                                               %compute modularity
    m = M(ones(1,n),:);
    Q0 = (W0-(Kn0*Kn0.')/s0).*(m==m.');
    Q1 = (W1-(Kn1*Kn1.')/s1).*(m==m.');
    q = d0*sum(Q0(:)) - d1*sum(Q1(:));
end


% This is material illustrating the methods from the book
% Financial Modelling  - Theory, Implementation and Practice with Matlab
% source
% Wiley Finance Series
% ISBN 978-0-470-74489-5
%
% Date: 02.05.2012
%
% Authors:  Joerg Kienitz
%           Daniel Wetterau
%
% Please send comments, suggestions, bugs, code etc. to
% kienitzwetterau_FinModelling@gmx.de
%
% (C) Joerg Kienitz, Daniel Wetterau
%
% Since this piece of code is distributed via the mathworks file-exchange
% it is covered by the BSD license
%
% This code is being provided solely for information and general
% illustrative purposes. The authors will not be responsible for the
% consequences of reliance upon using the code or for numbers produced
% from using the code.

function y = callsabr(k,f,limit)
% pricing a call using a density f and integration

    y = zeros(1,length(k));
    for j = 1:length(k)
        F = @(x) max(x-k(j),0) .* f(x);
        y(j) = quad(F,0,limit);
    end
end


function cae = caetrain(cae, x, opts)
    n = cae.inputkernel(1);
    cae.rL = [];
    for m = 1 : opts.rounds
        tic;
        disp([num2str(m) '/' num2str(opts.rounds) ' rounds']);
        i1 = randi(numel(x));
        l  = randi(size(x{i1}{1},1) - opts.batchsize - n + 1);
        x1{1} = double(x{i1}{1}(l : l + opts.batchsize - 1, :, :)) / 255;

        if n == 1   %Auto Encoder
            x2{1} = x1{1};
        else        %Predictive Encoder
            x2{1} = double(x{i1}{1}(l + n : l + n + opts.batchsize - 1, :, :)) / 255;
        end
        %  Add noise to input, for denoising stacked autoenoder
        x1{1} = x1{1} .* (rand(size(x1{1})) > cae.noise);

        cae = caeup(cae, x1);
        cae = caedown(cae);
        cae = caebp(cae, x2);
        cae = caesdlm(cae, opts, m);
%         caenumgradcheck(cae,x1,x2);
        cae = caeapplygrads(cae);

        if m == 1
            cae.rL(1) = cae.L;
        end
%         cae.rL(m + 1) = 0.99 * cae.rL(m) + 0.01 * cae.L;
        cae.rL(m + 1) = cae.L;
%         if cae.sv < 1e-10
%             disp('Converged');
%             break;
%         end
        toc;
    end

end


function [merged_sample, new_sample, merged_sample_id, new_sample_id, distance_matrix, gram_matrix, prior_weights] = ...
    update_sample_space_model(samplesf, new_train_sample, distance_matrix, gram_matrix, prior_weights,...
    num_training_samples,params)

% Updates the sample space model
% There are 4 possible cases
% 1: Memory is not full. In this case the new train sample is placed in the
% next empty slot.
%
% 2: Memory is full, one of the samples is outdated. In this case, the new
% train sample replaces the outdated sample.
%
% 3: Memory is full, the min distance of the new train sample to the existing samples
% is less than the min distance amongst any of the existing samples. The
% new train sample is merged with the nearest existing sample
%
% 4: Memory is full, the min distance of the new train sample to the existing samples
% is more than the min distance amongst any of the existing samples. The
% closest existing samples are merged. New train sample is placed in the
% free slot

num_feature_blocks = numel(new_train_sample);

% Find the inner product of the new sample with existing samples
gram_vector = find_gram_vector(samplesf, new_train_sample, num_training_samples, params);

% Find the distance of the new sample with the existing samples
% Note: Since getting the 'exact' distance between the samples is not important,
% the distance computation is done by using only the half spectrum for
% efficiency. In practice the error incurred by this is negligible. Also
% since we wannt to merge samples that are similar, and finding the best
% match is not important, small error in the distance computation doesn't
% matter
new_train_sample_norm =  zeros(1,'like', params.data_type);

for k = 1:num_feature_blocks
    new_train_sample_norm = new_train_sample_norm + real(2*(new_train_sample{k}(:)' * new_train_sample{k}(:)));
end

dist_vector = max(new_train_sample_norm + diag(gram_matrix) - 2*gram_vector,0);
dist_vector(num_training_samples+1:end) = inf;

merged_sample = [];
new_sample = [];
merged_sample_id = -1;
new_sample_id = -1;

% Check if we have filled the memory
if num_training_samples == params.nSamples

    % Check if any sample weight is too low
    [min_sample_weight, min_sample_id] = min(prior_weights);

    if min_sample_weight < params.minimum_sample_weight
        % If any prior weight is less than the minimum allowed weight,
        % replace that sample with the new sample

        % Update distance matrix and the gram matrix
        [distance_matrix, gram_matrix] = update_distance_matrix(distance_matrix, gram_matrix, gram_vector, new_train_sample_norm, min_sample_id, -1, 0, 1);

        % Normalise the prior weights so that the new sample gets weight as
        % the learning rate
        prior_weights(min_sample_id) = 0;
        prior_weights = prior_weights*(1 - params.learning_rate)/sum(prior_weights);
        prior_weights(min_sample_id) = params.learning_rate;

        % Set the new sample and new sample position in the samplesf
        new_sample_id = min_sample_id;
        new_sample = new_train_sample;
    else
        % If no sample has low enough prior weight, then we either merge
        % the new sample with an existing sample, or merge two of the
        % existing samples and insert the new sample in the vacated
        % position

        % Find sample closest to the new sample
        [new_sample_min_dist, closest_sample_to_new_sample] = min(dist_vector);

        % Find the closest pair amongst existing samples
        [existing_samples_min_dist, closest_existing_sample_pair] = min(distance_matrix(:));
        [closest_existing_sample1,closest_existing_sample2] = ind2sub(size(distance_matrix),closest_existing_sample_pair);

        if closest_existing_sample1 == closest_existing_sample2
            error('Score matrix diagonal filled wrongly');
        end

        if new_sample_min_dist < existing_samples_min_dist
            % If the min distance of the new sample to the existing samples
            % is less than the min distance amongst any of the existing
            % samples, we merge the new sample with the nearest existing
            % sample

            % Renormalize prior weights
            prior_weights = prior_weights*(1 - params.learning_rate);

            % Set the position of the merged sample
            merged_sample_id = closest_sample_to_new_sample;

            % Extract the existing sample to merge
            existing_sample_to_merge = cell(1, 1, num_feature_blocks);

            for k = 1:num_feature_blocks
                existing_sample_to_merge{k} = samplesf{k}(merged_sample_id,:,:,:);
            end

            % Merge the new_train_sample with existing sample
            merged_sample = merge_samples(existing_sample_to_merge, new_train_sample, prior_weights(merged_sample_id), params.learning_rate, params.sample_merge_type);

            % Update distance matrix and the gram matrix
            [distance_matrix, gram_matrix] = update_distance_matrix(distance_matrix, gram_matrix, gram_vector, new_train_sample_norm, merged_sample_id, -1, prior_weights(merged_sample_id), params.learning_rate);

            % Update the prior weight of the merged sample
            prior_weights(closest_sample_to_new_sample) = prior_weights(closest_sample_to_new_sample) + params.learning_rate;
        else
            % If the min distance amongst any of the existing
            % samples is less than the min distance of the new sample to the existing samples,
            % we merge the nearest existing samples and insert the new
            % sample in the vacated position

            % Renormalize prior weights
            prior_weights = prior_weights*(1 - params.learning_rate);

            % Ensure that the sample with higher prior weight is assigned
            % id1. This is only relevant if the sample_update_criteria is
            % 'replace'
            if prior_weights(closest_existing_sample2) > prior_weights(closest_existing_sample1)
                temp = closest_existing_sample1;
                closest_existing_sample1 = closest_existing_sample2;
                closest_existing_sample2 = temp;
            end

            sample_to_merge1 = cell(1, 1, num_feature_blocks);
            sample_to_merge2 = cell(1, 1, num_feature_blocks);

            % Extract the old sample
            for k = 1:num_feature_blocks
                sample_to_merge1{k} = samplesf{k}(closest_existing_sample1,:,:,:);
                sample_to_merge2{k} = samplesf{k}(closest_existing_sample2,:,:,:);
            end

            % Merge the existing closest samples
            merged_sample = merge_samples(sample_to_merge1, sample_to_merge2, prior_weights(closest_existing_sample1), prior_weights(closest_existing_sample2), params.sample_merge_type);

            % Update distance matrix and the gram matrix
            [distance_matrix, gram_matrix] = update_distance_matrix(distance_matrix, gram_matrix, gram_vector, new_train_sample_norm, closest_existing_sample1,closest_existing_sample2, ...
                prior_weights(closest_existing_sample1),prior_weights(closest_existing_sample2));

            % Update prior weights for the merged sample and the new sample
            prior_weights(closest_existing_sample1) = prior_weights(closest_existing_sample1) + prior_weights(closest_existing_sample2);
            prior_weights(closest_existing_sample2) = params.learning_rate;

            % Set the merged sample position and new sample position
            merged_sample_id = closest_existing_sample1;
            new_sample_id = closest_existing_sample2;

            new_sample = new_train_sample;
        end
    end
else
    % If the memory is not full, insert the new sample in the next empty
    % location

    sample_position = num_training_samples + 1;

    % Update the distance matrix and the gram matrix
    [distance_matrix, gram_matrix] = update_distance_matrix(distance_matrix, gram_matrix, gram_vector, new_train_sample_norm, sample_position, -1, 0, 1);

    % Update the prior weight
    if sample_position == 1
        prior_weights(sample_position) = 1;
    else
        prior_weights = prior_weights*(1 - params.learning_rate);
        prior_weights(sample_position) = params.learning_rate;
    end

    new_sample_id = sample_position;
    new_sample = new_train_sample;
end

% Ensure that prior weights always sum to 1
if (abs(1 - sum(prior_weights)) > 1e-5)
    error('Weights not properly updated');
end
end


% WAVELET_LAYER_1D Compute the one-dimensional wavelet transform from
% the modulus wavelet coefficients of the previous layer.
%
% Usages
%    [U_phi , U_psi] = wavelet_layer_1d(U, filters)
%
%    [U_phi , U_psi] = wavelet_layer_1d(U, filters, scat_opt)
%
%    [U_phi , U_psi] = wavelet_layer_1d(U, filters, scat_opt, wavelet)
%
% Input
%    U (struct): The input layer to be transformed.
%    filters (cell): The filters of the wavelet transform.
%    scat_opt (struct): The options that are transferred to the wavelet
%    function handle.
%    wavelet (function handle): the wavelet transform function (default is
%    @wavelet_1d).
%
% Output
%    U_phi The coefficients of in, lowpass-filtered (scattering
%       coefficients).
%    U_psi: The wavelet transform coefficients.
%
% Description
%    This function has a pivotal role between WAVELET_1D (which computes a
%    single wavelet transform), and WAVELET_FACTORY_1D (which creates the
%    whole cascade). Given inputs modulus wavelet coefficients
%    corresponding to a layer, WAVELET_LAYER_1D computes the wavelet
%    transform coefficients of the next layer using WAVELET_1D.
% See also
%   WAVELET_1D, WAVELET_FACTORY_1D, WAVELET_LAYER_2D

function [U_phi, U_psi] = wavelet_layer_1dave(U, filters, scat_opt, wavelet)
	if nargin < 3
		scat_opt = struct();
	end

	if nargin < 4
		wavelet = @wavelet_1d;
	end

	calc_U = (nargout>=2);

	[psi_xi,psi_bw,phi_bw] = filter_freq(filters.meta);

	if ~isfield(U.meta, 'bandwidth'), U.meta.bandwidth = 2*pi; end
	if ~isfield(U.meta, 'resolution'), U.meta.resolution = 0; end

	U_phi.signal = {};
	U_phi.meta.bandwidth = [];
	U_phi.meta.resolution = [];
	U_phi.meta.j = zeros(size(U.meta.j,1),0);

	U_psi.signal = {};
	U_psi.meta.bandwidth = [];
	U_psi.meta.resolution = [];
	U_psi.meta.j = zeros(size(U.meta.j,1)+1,0);

	r = 1;
	for p1 = 1:length(U.signal)
		psi_mask = calc_U&(U.meta.bandwidth(p1)>psi_xi);

		scat_opt.x_resolution = U.meta.resolution(p1);
		scat_opt.psi_mask = psi_mask;
		[x_phi, x_psi, meta_phi, meta_psi] = ...
			wavelet(U.signal{p1}, filters, scat_opt);

		U_phi.signal{1,p1} = mean(U.signal{p1}(:));
		U_phi.meta = map_meta(U.meta,p1,U_phi.meta,p1);
		U_phi.meta.bandwidth(1,p1) = meta_phi.bandwidth;
		U_phi.meta.resolution(1,p1) = meta_phi.resolution;

		ind = r:r+sum(psi_mask)-1;
		U_psi.signal(1,ind) = x_psi(1,psi_mask);
		U_psi.meta = map_meta(U.meta,p1,U_psi.meta,ind,{'j'});
		U_psi.meta.bandwidth(1,ind) = meta_psi.bandwidth(1,psi_mask);
		U_psi.meta.resolution(1,ind) = meta_psi.resolution(1,psi_mask);
		U_psi.meta.j(:,ind) = [U.meta.j(:,p1)*ones(1,length(ind)); ...
			meta_psi.j(1,psi_mask)];

		r = r+length(ind);
	end
end


function [M,scal] = spm_affreg(VG,VF,flags,M,scal)
% Affine registration using least squares.
% FORMAT [M,scal] = spm_affreg(VG,VF,flags,M0,scal0)
%
% VG        - Vector of template volumes.
% VF        - Source volume.
% flags     - a structure containing various options.  The fields are:
%             WG       - Weighting volume for template image(s).
%             WF       - Weighting volume for source image
%                        Default to [].
%             sep      - Approximate spacing between sampled points (mm).
%                        Defaults to 5.
%             regtype  - regularisation type.  Options are:
%                        'none'  - no regularisation
%                        'rigid' - almost rigid body
%                        'subj'  - inter-subject registration (default).
%                        'mni'   - registration to ICBM templates
%             globnorm - Global normalisation flag (1)
% M0        - (optional) starting estimate. Defaults to eye(4).
% scal0     - (optional) starting estimate.
%
% M         - affine transform, such that voxels in VF map to those in
%             VG by   VG.mat\M*VF.mat
% scal      - scaling factors for VG
%
% When only one template is used, then the cost function is approximately
% symmetric, although a linear combination of templates can be used.
% Regularisation is based on assuming a multi-normal distribution for the
% elements of the Henckey Tensor. See:
% "Non-linear Elastic Deformations". R. W. Ogden (Dover), 1984.
% Weighting for the regularisation is determined approximately according
% to:
% "Incorporating Prior Knowledge into Image Registration"
% J. Ashburner, P. Neelin, D. L. Collins, A. C. Evans & K. J. Friston.
% NeuroImage 6:344-352 (1997).
%
%_______________________________________________________________________
% @(#)spm_affreg.m	2.3 John Ashburner 03/02/18

if nargin<5, scal = ones(length(VG),1); end;
if nargin<4, M    = eye(4);             end;

def_flags = struct('sep',5, 'regtype','subj','WG',[],'WF',[],'globnorm',1,'debug',0);
if nargin < 2 | ~isstruct(flags),
	flags = def_flags;
else,
	fnms = fieldnames(def_flags);
	for i=1:length(fnms),
		if ~isfield(flags,fnms{i}),
			flags = setfield(flags,fnms{i},getfield(def_flags,fnms{i}));
		end;
	end;
end;

% Check to ensure inputs are valid...
% ---------------------------------------------------------------
if length(VF)>1, error('Can not use more than one source image'); end;
if ~isempty(flags.WF),
	if length(flags.WF)>1,
		error('Can only use one source weighting image');
	end;
	if any(any(VF.mat-flags.WF.mat)),
		error('Source and its weighting image must have same orientation');
	end;
	if any(any(VF.dim(1:3)-flags.WF.dim(1:3))),
		error('Source and its weighting image must have same dimensions');
	end;
end;
if ~isempty(flags.WG),
	if length(flags.WG)>1,
		error('Can only use one template weighting image');
	end;
	tmp = reshape(cat(3,VG(:).mat,flags.WG.mat),16,length(VG)+length(flags.WG));
else,
	tmp = reshape(cat(3,VG(:).mat),16,length(VG));
end;
if any(any(diff(tmp,1,2))),
	error('Reference images must all have the same orientation');
end;
if ~isempty(flags.WG),
	tmp = cat(1,VG(:).dim,flags.WG.dim);
else,
	tmp = cat(1,VG(:).dim);
end;
if any(any(diff(tmp(:,1:3),1,1))),
	error('Reference images must all have the same dimensions');
end;
% ---------------------------------------------------------------

% Generate points to sample from, adding some jitter in order to
% make the cost function smoother.
% ---------------------------------------------------------------
rand('state',0); % want the results to be consistant.
dg   = VG(1).dim(1:3);
df   = VF(1).dim(1:3);

if length(VG)==1,
	skip = sqrt(sum(VG(1).mat(1:3,1:3).^2)).^(-1)*flags.sep;
	[x1,x2,x3]=ndgrid(1:skip(1):dg(1)-.5, 1:skip(2):dg(2)-.5, 1:skip(3):dg(3)-.5);
	x1   = x1 + rand(size(x1))*0.5; x1 = x1(:);
	x2   = x2 + rand(size(x2))*0.5; x2 = x2(:);
	x3   = x3 + rand(size(x3))*0.5; x3 = x3(:);
end;

skip = sqrt(sum(VF(1).mat(1:3,1:3).^2)).^(-1)*flags.sep;
[y1,y2,y3]=ndgrid(1:skip(1):df(1)-.5, 1:skip(2):df(2)-.5, 1:skip(3):df(3)-.5);
y1   = y1 + rand(size(y1))*0.5; y1 = y1(:);
y2   = y2 + rand(size(y2))*0.5; y2 = y2(:);
y3   = y3 + rand(size(y3))*0.5; y3 = y3(:);
% ---------------------------------------------------------------

if flags.globnorm,
	% Scale all images approximately equally
	% ---------------------------------------------------------------
	for i=1:length(VG),
		VG(i).pinfo(1:2,:) = VG(i).pinfo(1:2,:)/spm_global(VG(i));
	end;
	VF(1).pinfo(1:2,:) = VF(1).pinfo(1:2,:)/spm_global(VF(1));
end;
% ---------------------------------------------------------------

if length(VG)==1,
	[G,dG1,dG2,dG3]  = spm_sample_vol(VG(1),x1,x2,x3,1);
	if ~isempty(flags.WG),
		WG = abs(spm_sample_vol(flags.WG,x1,x2,x3,1))+eps;
	end;
end;

[F,dF1,dF2,dF3]  = spm_sample_vol(VF(1),y1,y2,y3,1);
if ~isempty(flags.WF),
	WF = abs(spm_sample_vol(flags.WF,y1,y2,y3,1))+eps;
end;
% ---------------------------------------------------------------
n_main_its = 0;
ss         = Inf;
W          = [Inf Inf Inf];
est_smo    = 1;
% ---------------------------------------------------------------

for iter=1:256,
	pss   = ss;
	p0    = [0 0 0  0 0 0  1 1 1  0 0 0];

	% Initialise the cost function and its 1st and second derivatives
	% ---------------------------------------------------------------
	n     = 0;
	ss    = 0;
	Beta  = zeros(12+length(VG),1);
	Alpha = zeros(12+length(VG));

	if length(VG)==1,
		% Make the cost function symmetric
		% ---------------------------------------------------------------

		% Build a matrix to rotate the derivatives by, converting from
		% derivatives w.r.t. changes in the overall affine transformation
		% matrix, to derivatives w.r.t. the parameters p.
		% ---------------------------------------------------------------
		dt  = 0.0001;
		R   = eye(13);
		MM0 = inv(VG.mat)*inv(spm_matrix(p0))*VG.mat;
		for i1=1:12,
			p1          = p0;
			p1(i1)      = p1(i1)+dt;
			MM1         = (inv(VG.mat)*inv(spm_matrix(p1))*(VG.mat));
			R(1:12,i1)  = reshape((MM1(1:3,:)-MM0(1:3,:))/dt,12,1);
		end;
		% ---------------------------------------------------------------
		[t1,t2,t3] = coords((M*VF(1).mat)\VG(1).mat,x1,x2,x3);
		msk        = find((t1>=1 & t1<=df(1) & t2>=1 & t2<=df(2) & t3>=1 & t3<=df(3)));
		if length(msk)<32, error_message; end;
		t1         = t1(msk);
		t2         = t2(msk);
		t3         = t3(msk);
		t          = spm_sample_vol(VF(1), t1,t2,t3,1);

		% Get weights
		% ---------------------------------------------------------------
		if ~isempty(flags.WF) | ~isempty(flags.WG),
			if isempty(flags.WF),
				wt = WG(msk);
			else,
				wt = spm_sample_vol(flags.WF(1), t1,t2,t3,1)+eps;
				if ~isempty(flags.WG), wt = 1./(1./wt + 1./WG(msk)); end;
			end;
			wt = sparse(1:length(wt),1:length(wt),wt);
		else,
			wt = speye(length(msk));
			wt = [];
		end;
		% ---------------------------------------------------------------
		clear t1 t2 t3

		% Update the cost function and its 1st and second derivatives.
		% ---------------------------------------------------------------
		[AA,Ab,ss1,n1] = costfun(x1,x2,x3,dG1,dG2,dG3,msk,scal^(-2)*t,G(msk)-(1/scal)*t,wt);
		Alpha = Alpha + R'*AA*R;
		Beta  = Beta  + R'*Ab;
		ss    = ss    + ss1;
		n     = n     + n1;
		t     = G(msk) - (1/scal)*t;
	end;

	if 1,
		% Build a matrix to rotate the derivatives by, converting from
		% derivatives w.r.t. changes in the overall affine transformation
		% matrix, to derivatives w.r.t. the parameters p.
		% ---------------------------------------------------------------
		dt = 0.0001;
		R  = eye(12+length(VG));
		MM0 = inv(M*VF.mat)*spm_matrix(p0)*M*VF.mat;
		for i1=1:12,
			p1          = p0;
			p1(i1)      = p1(i1)+dt;
			MM1         = (inv(M*VF.mat)*spm_matrix(p1)*M*VF.mat);
			R(1:12,i1)  = reshape((MM1(1:3,:)-MM0(1:3,:))/dt,12,1);
		end;
		% ---------------------------------------------------------------
		[t1,t2,t3] = coords(VG(1).mat\M*VF(1).mat,y1,y2,y3);
		msk        = find((t1>=1 & t1<=dg(1) & t2>=1 & t2<=dg(2) & t3>=1 & t3<=dg(3)));
		if length(msk)<32, error_message; end;

		if length(msk)<32, error_message; end;
		t1 = t1(msk);
		t2 = t2(msk);
		t3 = t3(msk);
		t  = zeros(length(t1),length(VG));

		% Get weights
		% ---------------------------------------------------------------
		if ~isempty(flags.WF) | ~isempty(flags.WG),
			if isempty(flags.WG),
				wt = WF(msk);
			else,
				wt = spm_sample_vol(flags.WG(1), t1,t2,t3,1)+eps;
				if ~isempty(flags.WF), wt = 1./(1./wt + 1./WF(msk)); end;
			end;
			wt = sparse(1:length(wt),1:length(wt),wt);
		else,
			wt = speye(length(msk));
		end;
		% ---------------------------------------------------------------

		if est_smo,
			% Compute derivatives of residuals in the space of F
			% ---------------------------------------------------------------
			[ds1,ds2,ds3] = transform_derivs(VG(1).mat\M*VF(1).mat,dF1(msk),dF2(msk),dF3(msk));
			for i=1:length(VG),
				[t(:,i),dt1,dt2,dt3] = spm_sample_vol(VG(i), t1,t2,t3,1);
				ds1   = ds1 - dt1*scal(i); clear dt1
				ds2   = ds2 - dt2*scal(i); clear dt2
				ds3   = ds3 - dt3*scal(i); clear dt3
			end;
			dss   = [ds1'*wt*ds1 ds2'*wt*ds2 ds3'*wt*ds3];
			clear ds1 ds2 ds3
		else,
			for i=1:length(VG),
				t(:,i)= spm_sample_vol(VG(i), t1,t2,t3,1);
			end;
		end;

		clear t1 t2 t3

		% Update the cost function and its 1st and second derivatives.
		% ---------------------------------------------------------------
		[AA,Ab,ss2,n2] = costfun(y1,y2,y3,dF1,dF2,dF3,msk,-t,F(msk)-t*scal,wt);
		Alpha = Alpha  + R'*AA*R;
		Beta  = Beta   + R'*Ab;
		ss    = ss     + ss2;
		n     = n      + n2;
	end;

	if est_smo,
		% Compute a smoothness correction from the residuals and their
		% derivatives.  This is analagous to the one used in:
		% 	"Analysis of fMRI Time Series Revisited"
		% 	Friston KJ, Holmes AP, Poline JB, Grasby PJ, Williams SCR,
		% 	Frackowiak RSJ, Turner R.  Neuroimage 2:45-53 (1995).
		% ---------------------------------------------------------------
		vx     = sqrt(sum(VG(1).mat(1:3,1:3).^2));
		pW     = W;
		W      = (2*dss/ss2).^(-.5).*vx;
		W      = min(pW,W);
		if flags.debug, fprintf('\nSmoothness FWHM:  %.3g x %.3g x %.3g mm\n', W*sqrt(8*log(2))); end;
		if length(VG)==1, dens=2; else, dens=1; end;
		smo    = prod(min(dens*flags.sep/sqrt(2*pi)./W,[1 1 1]));
		est_smo=0;
		n_main_its = n_main_its + 1;
	end;

	% Update the parameter estimates
	% ---------------------------------------------------------------
	nu      = n*smo;
	sig2    = ss/nu;
	[d1,d2] = reg(M,12+length(VG),flags.regtype);

	soln    = (Alpha/sig2+d2)\(Beta/sig2-d1);
	scal    = scal - soln(13:end);
	M       = spm_matrix(p0 + soln(1:12)')*M;

	if flags.debug,
		fprintf('%d\t%g\n', iter, ss/n);
		piccies(VF,VG,M,scal,b)
	end;

	% If cost function stops decreasing, then re-estimate smoothness
	% and try again.  Repeat a few times.
	% ---------------------------------------------------------------
	ss = ss/n;
	if iter>1, spm_chi2_plot('Set',ss); end;
	if (pss-ss)/pss < 1e-6,
		est_smo = 1;
	end;
	if n_main_its>3, break; end;

end;
return;
%_______________________________________________________________________

%_______________________________________________________________________
function [X1,Y1,Z1] = transform_derivs(Mat,X,Y,Z)
% Given the derivatives of a scalar function, return those of the
% affine transformed function
%_______________________________________________________________________

t1 = Mat(1:3,1:3);
t2 = eye(3);
if sum((t1(:)-t2(:)).^2) < 1e-12,
        X1 = X;Y1 = Y; Z1 = Z;
else,
        X1    = Mat(1,1)*X + Mat(1,2)*Y + Mat(1,3)*Z;
        Y1    = Mat(2,1)*X + Mat(2,2)*Y + Mat(2,3)*Z;
        Z1    = Mat(3,1)*X + Mat(3,2)*Y + Mat(3,3)*Z;
end;
return;
%_______________________________________________________________________

%_______________________________________________________________________
function [d1,d2] = reg(M,n,typ)
% Analytically compute the first and second derivatives of a penalty
% function w.r.t. changes in parameters.

if nargin<3, typ = 'subj'; end;
if nargin<2, n   = 13;     end;

[mu,isig] = priors(typ);
ds  = 0.000001;
d1  = zeros(n,1);
d2  = zeros(n);
p0  = [0 0 0  0 0 0  1 1 1  0 0 0];
h0  = penalty(p0,M,mu,isig);
for i=7:12, % derivatives are zero w.r.t. rotations and translations
	p1    = p0;
	p1(i) = p1(i)+ds;
	h1    = penalty(p1,M,mu,isig);
	d1(i) = (h1-h0)/ds; % First derivative
	for j=7:12,
		p2    = p0;
		p2(j) = p2(j)+ds;
		h2    = penalty(p2,M,mu,isig);
		p3    = p1;
		p3(j) = p3(j)+ds;
		h3    = penalty(p3,M,mu,isig);
		d2(i,j) = ((h3-h2)/ds-(h1-h0)/ds)/ds; % Second derivative
	end;
end;
return;
%_______________________________________________________________________

%_______________________________________________________________________
function h = penalty(p,M,mu,isig)
% Return a penalty based on the elements of an affine transformation,
% which is given by:
% 	spm_matrix(p)*M
%
% The penalty is based on the 6 unique elements of the Hencky tensor
% elements being multinormally distributed.
%_______________________________________________________________________

% Unique elements of symmetric 3x3 matrix.
els = [1 2 3 5 6 9];

T = spm_matrix(p)*M;
T = T(1:3,1:3);
T = 0.5*logm(T'*T);
T = T(els)' - mu;
h = T'*isig*T;
return;
%_______________________________________________________________________

%_______________________________________________________________________
function [mu,isig] = priors(typ)
% The parameters for this distribution were derived empirically from 227
% scans, that were matched to the ICBM space.
%_______________________________________________________________________

mu   = zeros(6,1);
isig = zeros(6);
switch deblank(lower(typ)),

case 'mni', % For registering with MNI templates...
	mu   = [0.0667 0.0039 0.0008 0.0333 0.0071 0.1071]';
	isig = 1e4 * [
	    0.0902   -0.0345   -0.0106   -0.0025   -0.0005   -0.0163
	   -0.0345    0.7901    0.3883    0.0041   -0.0103   -0.0116
	   -0.0106    0.3883    2.2599    0.0113    0.0396   -0.0060
	   -0.0025    0.0041    0.0113    0.0925    0.0471   -0.0440
	   -0.0005   -0.0103    0.0396    0.0471    0.2964   -0.0062
	   -0.0163   -0.0116   -0.0060   -0.0440   -0.0062    0.1144];

case 'rigid', % Constrained to be almost rigid...
	mu   = zeros(6,1);
	isig = eye(6)*1e9;

case 'isochoric', % Volume preserving...
	error('Not implemented');

case 'isotropic', % Isotropic zoom in all directions...
	error('Not implemented');

case 'subj', % For inter-subject registration...
	mu   = zeros(6,1);
	isig = 1e3 * [
	    0.8876    0.0784    0.0784   -0.1749    0.0784   -0.1749
	    0.0784    5.3894    0.2655    0.0784    0.2655    0.0784
	    0.0784    0.2655    5.3894    0.0784    0.2655    0.0784
	   -0.1749    0.0784    0.0784    0.8876    0.0784   -0.1749
	    0.0784    0.2655    0.2655    0.0784    5.3894    0.0784
	   -0.1749    0.0784    0.0784   -0.1749    0.0784    0.8876];

case 'none', % No regularisation...
	mu   = zeros(6,1);
	isig = zeros(6);

otherwise,
	error(['"' typ '" not recognised as type of regularisation.']);
end;
return;

%_______________________________________________________________________
function [y1,y2,y3]=coords(M,x1,x2,x3)
% Affine transformation of a set of coordinates.
%_______________________________________________________________________

y1 = M(1,1)*x1 + M(1,2)*x2 + M(1,3)*x3 + M(1,4);
y2 = M(2,1)*x1 + M(2,2)*x2 + M(2,3)*x3 + M(2,4);
y3 = M(3,1)*x1 + M(3,2)*x2 + M(3,3)*x3 + M(3,4);
return;
%_______________________________________________________________________

%_______________________________________________________________________
function A = make_A(x1,x2,x3,dG1,dG2,dG3,t)
% Generate part of a design matrix using the chain rule...
% df/dm = df/dy * dy/dm
% where
% 	df/dm is the rate of change of intensity w.r.t. affine parameters
% 	df/dy is the gradient of the image f
% 	dy/dm crange of position w.r.t. change of parameters
%_______________________________________________________________________

A  = [x1.*dG1 x1.*dG2 x1.*dG3 ...
	  x2.*dG1 x2.*dG2 x2.*dG3 ...
	  x3.*dG1 x3.*dG2 x3.*dG3 ...
	      dG1     dG2     dG3    t];
return;
%_______________________________________________________________________

%_______________________________________________________________________
function [AA,Ab,ss,n] = costfun(x1,x2,x3,dG1,dG2,dG3,msk,lastcols,b,wt)
chunk = 10240;
lm    = length(msk);
AA    = zeros(12+size(lastcols,2));
Ab    = zeros(12+size(lastcols,2),1);
ss    = 0;
n     = 0;

for i=1:ceil(lm/chunk),
	ind  = (((i-1)*chunk+1):min(i*chunk,lm))';
	msk1 = msk(ind);

	A1   = make_A(x1(msk1),x2(msk1),x3(msk1),dG1(msk1),dG2(msk1),dG3(msk1),lastcols(ind,:));
	b1   = b(ind);
	if ~isempty(wt),
		wt1   = wt(ind,ind);
		AA    = AA  + A1'*wt1*A1;
		%Ab   = Ab  + A1'*wt1*b1;
		Ab    = Ab  + (b1'*wt1*A1)';
		ss    = ss  + b1'*wt1*b1;
		n     = n   + trace(wt1);
		clear wt1
	else,
		AA    = AA  + spm_atranspa(A1);
		%Ab   = Ab  + A1'*b1;
		Ab    = Ab  + (b1'*A1)';
		ss    = ss  + b1'*b1;
		n     = n   + length(msk1);
	end;
	clear A1 b1 msk1 ind
end;
return;
%_______________________________________________________________________

%_______________________________________________________________________
function error_message
% Display an error message for when things go wrong.
str = {	'There is not enough overlap in the images',...
	'to obtain a solution.',...
	' ',...
	'Please check that your header information is OK.'};
spm('alert*',str,mfilename,sqrt(-1));
error('insufficient image overlap')
return
%_______________________________________________________________________

%_______________________________________________________________________
function piccies(VF,VG,M,scal,b)
% This is for debugging purposes.
% It shows the linear combination of template images, the affine
% transformed source image, the residual image and a histogram of the
% residuals.
%_______________________________________________________________________

figure(2);
Mt = spm_matrix([0 0 (VG(1).dim(3)+1)/2]);
M  = (M*VF(1).mat)\VG(1).mat;
t  = zeros(VG(1).dim(1:2));
for i=1:length(VG);
	t  = t + spm_slice_vol(VG(i),  Mt,VG(1).dim(1:2),1)*scal(i);
end;
u  = spm_slice_vol(VF(1),M*Mt,VG(1).dim(1:2),1);
subplot(2,2,1);imagesc(t');axis image xy off
subplot(2,2,2);imagesc(u');axis image xy off
subplot(2,2,3);imagesc(u'-t');axis image xy off
subplot(2,2,4);hist(b,50); % Entropy of residuals may be a nice cost function?
drawnow;
return;
%_______________________________________________________________________


function alpha = calculateNoiseType(readings)
%this function calculates the dominate noise type at a given tau value.
%variable d is to track number of difference oppperations for identifying
%noise types **************. d should start at 0 and go no higher than 2
%(not sure purpose of d at this point).

%the following is the algorithm for calculating noise type
done = 0;
d = 0;
z = readings;

while done == 0

    r1 = rOneCalculation(z);

    delta = r1 / (r1 + 1);

    if d >= 0 && (delta < .25 || d >= 2)
        p = -2*(delta+d);
        alpha = p + 2; %for time data
        done = 1;
    else
        z = calculateDiffArray(z);
        d = d + 1;
    end
end


function result = MaskRecon(Data, v)

for i = 1 : size(Data, 1)
    dis(i) = norm(v-Data(i, :));
end
[mindis, ind] = min(dis);
if ind < 11
    result = ind-1;
else
    result = ind-11;
end


function x = l1_rand(mu,sigma,M)
error('this function was just for testing')
nu = 1;
u = gamrnd(nu*1,2/nu,M);
mean_invu = mean(1./u)
x = normrnd(mu,sigma.*u,M);


function outputImage = rms(this, varargin)
% Computes root mean square along specified dimension, i.e. sqrt(mean(Y.^2))
%
%
%   Y = MrImage()
%   Y.rms(applicationDimension)
%
% This is a method of class MrImage.
%
% IN
%   applicationDimension    image dimension along which operation is
%                           performed (e.g. 4 = time, 3 = slices)
%                           default: The last dimension with more than one
%                           value is chosen
%                           (i.e. 3 for 3D image, 4 for 4D image)
%
% OUT
%   outputImage             rms of all images along application dimension
%
% EXAMPLE
%   rms
%
%   See also MrImage MrImage.perform_unary_operation

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-12-23
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if nargin > 1
    applicationDimension = varargin{1};
    outputImage = mean(this.^2, applicationDimension).^(1/2);
else
    outputImage = mean(this.^2).^(1/2);
end


%% Economic Parameters

% Preferences
coefrra = 1;
frisch = 0.5;
adjfricshgridfrac = 0.85;
meanlabeff = 3; 	%so that at h=1/3 outout will be approximately = 1;
labdisutil = meanlabeff ./ ( (0.75 .^(-coefrra)) * ((1.0/3.0).^(1.0/frisch))); %guess labdisutil so that at average wages and average consumption hours =1/3 (sets C/Y = 0.75);
maxhours = 1;

% Production
ceselast = 10;							 % elasticity of substitution / demand
priceadjust = 100;

% Policy parameters
taylor_inflation = 1.25;	%taylor rule coefficient on inflation
taylor_outputgap = 0;		%taylor rule coefficient on output
labtax = 0.2;			% marginal tax rate
govbondtarget = 6;		%multiple of quarterly GDP
lumptransferpc = 0.06;	 %6% of quarterly GDP in steady state
govbcrule_fixnomB = 0.0;

% Some aggregates
Y_SS = 1;
B_SS = govbondtarget .* Y_SS;
m_SS = (ceselast-1)/ceselast;
w_SS = m_SS;
N_SS = 1/3;						    % steady state hours: so that quartelry GDP = 1 in s.s
profit_SS = (1 - m_SS) * Y_SS;
lumptransfer = lumptransferpc .* Y_SS;

% Aggregate shocks
ssigma_MP = sqrt(0.05);
ttheta_MP = 0.25;

%% Approximation Parameters
% HJB
maxit_HJB = 500;
tol_HJB  = 1.0e-8;
Delta_HJB = 1.0e6;

maxit_KFE = 1000;
tol_KFE  = 1.0e-12;
Delta_KFE = 1.0e6;

crit  = 10^(-6);

% steady state r
r0 = 0.005;
rmax = 0.08;
rmin = 0.001;
Ir = 100;

% steady state rho iteration
rrho0 = 0.02;
rhomax = 0.05;
rhomin = 0.005;
crit_S = 10^(-5);

% hours loop
niter_hours = 10;

%% Set Grids

%% Asset
I          = 100;
agridparam = 1; % 1 for linear
amin       = 0;
amax       = 40;

a          = linspace(0,1,I)';
a          = a.^(1/agridparam);
a          = amin + (amax-amin).*a;

%% Income
J = 2;

ygrid_combined = [0.2; 1];
ymarkov_combined = [-0.5 0.5; 0.0376 -0.0376];

% Compute stationary income distribution
AT = ymarkov_combined';
g_z = ones(J,1)/J;
for n = 1 : 50
	g_z_new = (speye(J) - AT * 1000) \ g_z;
	diff = max(abs(g_z_new - g_z));
	if diff < 10e-6
		break
	end
	g_z = g_z_new;
end

z = exp(ygrid_combined);

% scale so that mean = meanlabeff
z  	  = meanlabeff.*z./sum(z .* g_z);
z_bar = sum(z .* g_z);
z     = z';
zz = ones(I,1)*z;

%% Kronecker to matrix
aa = a*ones(1,J);

daf = [a(2:I)-a(1:I-1); a(I)-a(I-1)];
dazf = repmat(daf,1,J);
dab = [a(2)-a(1); a(2:I)-a(1:I-1)];
dazb = repmat(dab,1,J);

adelta = zeros(size(a));
adelta(1) = 0.5.*daf(1);
adelta(2:I-1) = 0.5.*(daf(1:I-2)+daf(2:I-1));
adelta(I) = 0.5*daf(I-1);

azdelta = repmat(adelta,1,J);
azdelta_mat = spdiags(azdelta(:),0,I*J,I*J);

Aswitch = kron(ymarkov_combined,speye(I));

%% Others
IterateR = 0;
IterateRho = 1;


function GdDTPA
% GdDTPA reaction advection steady state
%    using MATLAB fzero for solution of implicit equation
%
%   $Ekkehard Holzbecher  $Date: 2006/05/03 $
%--------------------------------------------------------------------------
xdata = [0.22 0.42 0.84 1.66 3.33 5 10 15 20 25 30];
ydata = [54.7 54.3 54.2 54.0 52.8 52.4 51.0 49.9 48.7 47.6 46.2];
cin = 55;
aeta = 4304;
beta = 5937;
for i=1:size(xdata,2)
    yfunc(i) = fzero (@GD,cin,odeset,cin,beta,aeta,xdata(i));
end
plot (xdata,ydata,'o',xdata,yfunc);
xlabel ('distance [m]'); ylabel ('Gd concentration [ng/ml]');

function y = GD(c,cin,beta,aeta,x);
y = x+(c+2*beta*log(c)-beta*beta/c-cin-2*beta*log(cin)+beta*beta/cin)/aeta;


function [Fdual,objdual,y,X] = primalize(F,obj)
% PRIMALIZE Create the dual of an SDP given in dual form
%
% [Fd,objd,y] = primalize(F,obj)
%
% Input
%  F   : Primal constraint in form C-Ay > 0, Fy = g
%  obj : Primal cost maximize b'y
%
% Output
%  Fd  : Dual constraints in form X>0, Trace(AiX)==bi+dt
%  obj : Dual cost trace(CX)
%  y   : The detected primal free variables
%
% Example
%  See the HTML help.
%
% See also DUAL, SOLVESDP, SDPVAR, DUALIZE

err = 0;

if isa(F,'constraint')
    F = (F);
end

% It's general, but not insanely general...
if ~(islinear(F) & islinear(obj))
    if nargout == 6
        Fdual = ([]);objdual = [];y = []; err = 1;
    else
        error('Can only primalize linear problems');
    end
end
if any(is(F,'socc'))
    if nargout == 6
        Fdual = ([]);objdual = [];y = []; err = 1;
    else
        error('Cannot primalize second order cone constraints');
    end
end
if isa(obj,'sdpvar')
    if any(is(F,'complex')) | is(obj,'complex')
    if nargout == 6
        Fdual = ([]);objdual = [];y = []; X = []; t = []; err = 1;
    else
        error('Cannot primalize complex-valued problems');
    end
    end
end
if any(is(F,'integer')) | any(is(F,'binary'))
    if nargout == 6
        Fdual = ([]);objdual = [];y = []; err = 1;
    else
        error('Cannot primalize discrete problems');
    end
end

% Create model using the standard code
[model,~,diagnostic] = export(F,obj,sdpsettings('solver','sedumi'),[],[],1);
if isempty(model)
    if isfield(diagnostic,'problem') && isfield(diagnostic,'info');
        error("export failed: model is empty, problem=%d,info=%s", ...
            diagnostic.problem,diagnostic.info);
    else
        error("export failed: model is empty");
    end
end

Fdual = ([]);
xvec = [];
if any(model.K.f)
    t = sdpvar(model.K.f,1);
    xvec = [xvec;t];
end

if any(model.K.l)
    x = sdpvar(model.K.l,1);
    xvec = [xvec;x];
    Fdual = Fdual + (x>=0);
end

if any(model.K.q)
    for i = 1:length(model.K.q)
        x = sdpvar(model.K.q(i),1);
        xvec = [xvec;x];
        Fdual = Fdual + (cone(x(2:end),x(1)));
    end
end

if any(model.K.s)
    for i = 1:length(model.K.s)
        X{i} = sdpvar(model.K.s(i),model.K.s(i));
        xvec = [xvec;X{i}(:)];
        Fdual = Fdual + (X{i}>=0);
    end
end

objdual = model.C(:)'*xvec;
Fdual = Fdual + (-model.b == model.A'*xvec);

yvars = union(getvariables(F),getvariables(obj));
y = recover(yvars);

yalmip('associatedual',getlmiid(Fdual(end)),y);


function L = ngaussNoiseLikelihood(noise, mu, varsigma, y)

% NGAUSSNOISELIKELIHOOD Likelihood of the data under the NGAUSS noise model.
% FORMAT
% DESC returns the likelihood of a data set under the  noiseless Gaussian noise model.
% ARG noise : the noise structure for which the likelihood is required.
% ARG mu : input mean locations for the likelihood.
% ARG varSigma : input variance locations for the likelihood.
% ARG y : target locations for the likelihood.
%
% SEEALSO : ngaussNoiseParamInit, ngaussNoiseLogLikelihood, noiseLikelihood
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005

% NOISE

L = gaussianNoiseLikelihood(noise, mu, varsigma, y);


function [roc_avg, pr_avg] = DTINet(seed, nFold, interaction, drug_feat, prot_feat, dim_imc)
	rng(seed);
	Pint = find(interaction); % pair of interaction
	Nint = length(Pint);
	Pnoint = find(~interaction);
	Pnoint = Pnoint(randperm(length(Pnoint), Nint * 1));
	Nnoint = length(Pnoint);

	posFilt = crossvalind('Kfold', Nint, nFold);
	negFilt = crossvalind('Kfold', Nnoint, nFold);

	AUROC = zeros(nFold, 1);
	AUPRC = zeros(nFold, 1);

	for foldID = 1 : nFold
		train_posIdx = Pint(posFilt ~= foldID);
		train_negIdx = Pnoint(negFilt ~= foldID);
		train_idx = [train_posIdx; train_negIdx];
		Ytrain = [ones(length(train_posIdx), 1); zeros(length(train_negIdx), 1)];
		fprintf('Train data: %d positives, %d negatives\n', sum(Ytrain == 1), sum(Ytrain == 0));

		test_posIdx = Pint(posFilt == foldID);
		test_negIdx = Pnoint(negFilt == foldID);
		test_idx = [test_posIdx; test_negIdx];
		Ytest = [ones(length(test_posIdx), 1); zeros(length(test_negIdx), 1)];
		fprintf('Test data: %d positives, %d negatives\n', sum(Ytest == 1), sum(Ytest == 0));

		[I, J] = ind2sub(size(interaction), train_idx);
		Xtrain = sparse(I, J, Ytrain, size(interaction, 1), size(interaction, 2));

		[W, H, ~] = train_mf(Xtrain, sparse(drug_feat), sparse(prot_feat), ...
						[' -l ' num2str(1) ' -k ' num2str(dim_imc) ' -t 10' ' -s ' num2str(10)]);
		Zscore = drug_feat * W' * H * prot_feat';
		Ypred = Zscore(test_idx);

		[trainroc, trainpr] = auc(Ytrain, Zscore(train_idx), 1e-6);
		[testroc, testpr] = auc(Ytest, Ypred, 1e-6);
		AUROC(foldID) = testroc;
		AUPRC(foldID) = testpr;
		fprintf('Fold %d, Train: AUROC=%f AUPR=%f; Test: AUROC=%f, AUPR=%f\n', foldID, trainroc, trainpr, testroc, testpr);
	end
	roc_avg = mean(AUROC);
    pr_avg = mean(AUPRC);
end