% std_spec() - Returns the ICA component spectra for a dataset. Updates the EEG structure
%              in the Matlab environment and in the .set file as well. Saves the spectra
%              in a file.
% Usage:
%           >> [spec freqs] = std_spec(EEG, 'key', 'val', ...);
%
%              Computes the mean spectra of the activites of specified components of the
%              supplied dataset. The spectra are saved in a Matlab file. If such a file
%              already exists, loads the spectral information from this file.
%              Options (below) specify which components to use, and the desired frequency
%              range. There is also an option to specify other spectopo() input variables
%              (see >> help spectopo for details).
%
%              Returns the removed mean spectra of the selected ICA components in the
%              requested frequency range. If the spectra were computed previously but a
%              different frequency range is selected, there is an overwrite option.
%              so. The function will load previously computed log spectra, if any, and
%              will remove the mean from the requested frequency range. The frequencies
%              vector is also returned.
% Inputs:
%   EEG - a loaded epoched EEG dataset structure.
%
% Optional inputs:
%   'components' - [numeric vector] components of the EEG structure for which
%                  activation ERPs will be computed. Note that because
%                  computation of component spectra is relatively fast, all
%                  components spectra are computed and saved. Only selected
%                  component are returned by the function to Matlab
%                  {default|[] -> all}
%   'channels'   - [cell array] channels of the EEG structure for which
%                  activation spectrum will be computed. Note that because
%                  computation of spectrum is relatively fast, all channels
%                  spectrum are computed and saved. Only selected channels
%                  are returned by the function to Matlab
%                  {default|[] -> none}
%   'specmode'   - ['psd'|'fft'] method to compute spectral
%                  decomposition. 'psd' uses the spectopo function. 'fft'
%                  uses a simple fft on each trial.
%   'epochlim'   - [min max] for FFT on continuous data, extract data
%                  epochs with specific epoch limits in seconds (see also
%                  'epochrecur' below). Default is [0 1].
%   'epochrecur' - [float] for FFT on continuous data, set the automatic
%                  epoch extraction recurence interval (default is 1 second).
%   'timerange'  - [min max] use data within a specific time range before
%                  computing the data spectrum. For instance, for evoked
%                  data trials, it is recommended to use the baseline time
%                  period.
%   'freqrange'  - [minhz maxhz] frequency range (in Hz) within which to
%                  return the spectrum {default|[]: [0 sample rate/2]}.
%   'recompute'  - ['on'|'off'] force recomputing ERP file even if it is
%                  already on disk.
%
% Other optional spectral parameters:
%   All optional parameters to the spectopo function may be provided to this function
%   as well.
%
% Outputs:
%   spec      - the mean spectra (in dB) of the requested ICA components in the selected
%               frequency range (with the mean of each spectrum removed).
%   freqs     - a vector of frequencies at which the spectra have been computed.
%
% Files output or overwritten for ICA:
%               [dataset_filename].icaspec,   % raw spectrum of ICA components
%               [dataset_filename].icaspecm   % spectrum with the mean baseline removed
% Files output or overwritten for data:
%               [dataset_filename].datspec,
%               [dataset_filename].datspecm
%
% See also  spectopo(), std_erp(), std_ersp(), std_map(), std_preclust()
%
% Authors: Arnaud Delorme, SCCN, INC, UCSD, January, 2005

% Defunct:      0 -> if frequency range is different from saved spectra, ask via a
%                    pop-up window whether to keep existing spectra or to overwrite them.

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, October 11, 2004, arno@sccn.ucsd.edu
%
% 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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [X, f, overwrt] = std_spec(EEG, varargin)

overwrt = 1; % deprecated
if nargin < 1
    help std_spec;
    return;
end;

% decode inputs
% -------------
if ~isempty(varargin)
    if ~isstr(varargin{1})
        varargin = { varargin{:} [] [] };
        if all(varargin{1} > 0)
            options = { 'components' varargin{1} 'freqrange' varargin{2} };
        else
            options = { 'channels' -varargin{1} 'freqrange' varargin{2} };
        end;
    else
        options = varargin;
    end;
else
    options = varargin;
end;

[g spec_opt] = finputcheck(options, { 'components' 'integer' []         [];
                                      'channels'   'cell'    {}         {};
                                      'timerange'  'float'   []         [];
                                      'specmode'   'string'  {'fft' 'psd' 'pmtm' 'pburg'} 'psd';
                                      'recompute'  'string'  { 'on' 'off' } 'off';
                                      'savetrials' 'string'  { 'on' 'off' } 'off';
                                      'epochlim'   'real'    []         [0 1];
                                      'epochrecur' 'real'    []         1;
                                      'rmcomps'    'cell'    []         cell(1,length(EEG));
                                      'nw'         'float'   []         4;
                                      'fileout'    'string'  []         '';
                                      'burgorder'  'integer' []         20;
                                      'interp'     'struct'  { }        struct([]);
                                      'nfft'       'integer' []         [];
                                      'freqrange'  'real'    []         [] }, 'std_spec', 'ignore');
if isstr(g), error(g); end;
if isfield(EEG,'icaweights')
   numc = size(EEG(1).icaweights,1);
else
   error('EEG.icaweights not found');
end
if isempty(g.components)
    g.components = 1:numc;
end

EEG_etc = [];

% filename
% --------
if isempty(g.fileout), g.fileout = fullfile(EEG(1).filepath, EEG(1).filename(1:end-4)); end;
if ~isempty(g.channels)
    filename = [ g.fileout '.datspec'];
    prefix = 'chan';
else
    filename = [ g.fileout '.icaspec'];
    prefix = 'comp';
end;

% SPEC information found in datasets
% ---------------------------------
if exist(filename) & strcmpi(g.recompute, 'off')

    fprintf('File "%s" found on disk, no need to recompute\n', filename);
    setinfo.filebase = g.fileout;
    if strcmpi(prefix, 'comp')
        [X tmp f] = std_readfile(setinfo, 'components', g.components, 'freqlimits', g.freqrange, 'measure', 'spec');
    else
        [X tmp f] = std_readfile(setinfo, 'channels', g.channels, 'freqlimits', g.freqrange, 'measure', 'spec');
    end;
    if ~isempty(X), return; end;
end

oritrials = EEG.trials;
if ~strcmpi(g.specmode, 'psd')
    if EEG(1).trials == 1,
        EEG = eeg_checkset(EEG, 'loaddata');
        EEG = eeg_regepochs(EEG, g.epochrecur, g.epochlim);
        g.trialindices = { [1:EEG(1).trials] };
        disp('Warning: continuous data, extracting 1-second epochs');
    end;
end;

% No SPEC information found
% ------------------------
options = {};
if ~isempty(g.rmcomps), options = { options{:} 'rmcomps' g.rmcomps }; end;
if ~isempty(g.interp),  options = { options{:} 'interp' g.interp }; end;
X          = [];
boundaries = [];
for dat = 1:length(EEG)
    if strcmpi(prefix, 'comp')
        tmpdata = eeg_getdatact(EEG(dat), 'component', [1:size(EEG(dat).icaweights,1)], 'trialindices', g.trialindices{dat} );
    else
        EEG(dat).data = eeg_getdatact(EEG(dat), 'channel', [1:EEG(dat).nbchan], 'rmcomps', g.rmcomps{dat}, 'trialindices', g.trialindices{dat});
        EEG(dat).trials = size(EEG(dat).data,3);
        EEG(dat).event  = [];
        EEG(dat).epoch  = [];
        if ~isempty(g.interp),
            EEG(dat) = eeg_interp(EEG(dat), g.interp, 'spherical');
        end;
        tmpdata = EEG(dat).data;
    end;
    if all([ EEG.trials ] > 1)
        if size(X,2) ~= size(tmpdata,2) && size(X,3) ~= 1, error('Datasets to be concatenated do not have the same number of time points'); end;
        if isempty(X),
            X = tmpdata;
        else
            if size(X,1) ~= size(tmpdata,1), error('Datasets to be concatenated do not have the same number of channels'); end;
            X(:,:,end+1:end+size(tmpdata,3)) = tmpdata; % concatenating trials
        end;
    else
        % get boundaries for continuous data
        if ~isempty(EEG(dat).event) && isfield(EEG(dat).event, 'type') && ischar(EEG(dat).event(1).type)
             tmpevent = EEG(dat).event;
             tmpbound = strmatch('boundary', lower({ tmpevent.type }));
             if ~isempty(tmpbound)
                 boundaries = [boundaries size(X,2) [ tmpevent(tmpbound).latency ]-0.5+size(X,2) ];
             end;
        else
        end;
        if isempty(X),
            X = tmpdata;
        else
            if size(X,1) ~= size(tmpdata,1), error('Datasets to be concatenated do not have the same number of channels or components'); end;
            X(:,end+1:end+size(tmpdata,2)) = tmpdata;
        end;
    end;
end;
if ~isempty(boundaries), boundaries = [boundaries size(X,2)]; end;

% get specific time range for epoched and continuous data
if ~isempty(g.timerange)
    if oritrials > 1
        timebef  = find(EEG(1).times >= g.timerange(1) & EEG(1).times < g.timerange(2) );
        X        = X(:,timebef,:);
        EEG(1).pnts = length(timebef);
    else
        disp('warning: ''timerange'' option cannot be used with continuous data');
    end;
end;

% compute spectral decomposition
if strcmpi(g.specmode, 'psd')
    [X, f] = spectopo(X, size(X,2), EEG(1).srate, 'plot', 'off', 'boundaries', boundaries, 'nfft', g.nfft, spec_opt{:});
    if strcmpi(g.savetrials, 'on')
        disp('Cannot save trials using ''psd'' specmode option');
    end;
elseif strcmpi(g.specmode, 'pmtm')
    if all([ EEG.trials ] == 1) && ~isempty(boundaries), disp('Warning: multitaper does not take into account boundaries in continuous data'); end;
    fprintf('Computing multitaper:');
    for cind = 1:size(X,1)
        fprintf('.');
        for tind = 1:size(X,3)
            [tmpdat f] = pmtm(X(cind,:,tind), g.nw, g.nfft, EEG.srate);
            if cind == 1 && tind == 1
                X2 = zeros(size(X,1), length(tmpdat), size(X,3));
            end;
            X2(cind,:,tind) = tmpdat;
        end;
    end;
    fprintf('\n');
    X     = 10*log10(X2);
    if strcmpi(g.savetrials, 'off')
        X = mean(X,3);
    end;
elseif strcmpi(g.specmode, 'pburg')
    if all([ EEG.trials ] == 1) && ~isempty(boundaries), disp('Warning: pburg does not take into account boundaries in continuous data'); end;
    for cind = 1:size(X,1)
        fprintf('.');
        for tind = 1:size(X,3)
            [tmpdat f] = pburg(X(cind,:,tind), g.burgorder, g.nfft, EEG.srate);
            if cind == 1 && tind == 1
                X2 = zeros(size(X,1), length(tmpdat), size(X,3));
            end;
            X2(cind,:,tind) = tmpdat;
        end;
    end;
    fprintf('\n');
    if strcmpi(g.savetrials, 'off')
        X = mean(X,3);
    end;
else % fft mode
    if all([ EEG.trials ] == 1) && ~isempty(boundaries), disp('Warning: fft does not take into account boundaries in continuous data'); end;
    tmp   = fft(X, g.nfft, 2);
    f     = linspace(0, EEG(1).srate/2, floor(size(tmp,2)/2));
    f     = f(2:end); % remove DC (match the output of PSD)
    tmp   = tmp(:,2:floor(size(tmp,2)/2),:);
    X     = 10*log10(abs(tmp).^2);
    if strcmpi(g.savetrials, 'off')
        X = mean(X,3);
    end;
end;

% Save SPECs in file (all components or channels)
% ----------------------------------
options = { options{:} spec_opt{:} 'timerange' g.timerange 'nfft' g.nfft 'specmode' g.specmode };
if strcmpi(prefix, 'comp')
    savetofile( filename, f, X, 'comp', 1:size(X,1), options);
else
    if ~isempty(g.interp)
        savetofile( filename, f, X, 'chan', 1:size(X,1), options, { g.interp.labels });
    else
        tmpchanlocs = EEG(1).chanlocs;
        savetofile( filename, f, X, 'chan', 1:size(X,1), options, { tmpchanlocs.labels });
    end;
end;
return;

% -------------------------------------
% saving SPEC information to Matlab file
% -------------------------------------
function savetofile(filename, f, X, prefix, comps, params, labels);

    disp([ 'Saving SPECTRAL file ''' filename '''' ]);
    allspec = [];
    for k = 1:length(comps)
        allspec = setfield( allspec, [ prefix int2str(comps(k)) ], squeeze(X(k,:,:)));
    end;
    if nargin > 6
        allspec.labels = labels;
    end;
    allspec.freqs      = f;
    allspec.parameters = params;
    allspec.datatype   = 'SPECTRUM';
    allspec.average_spec = mean(X,1);
    std_savedat(filename, allspec);


% PSWARM  Solve a Global NLP using PSWARM
%
% THIS IS A LOW LEVEL FUNCTION - USE opti_pswarm() INSTEAD!
%
% pswarm uses the Pattern and Particle Swarm Optimization library.
%
%   [x,fval,exitflag,iter,feval] = pswarm(fun,x0,lb,ub,A,b,opts)
%
%   Input arguments:
%       fun - nonlinear function handle
%       x0 - initial solution guess
%       lb - decision variable lower bounds
%       ub - decision variable upper bounds
%       A - linear inequality matrix (dense)
%       b - linear inequality rhs
%       opts - solver options (see below)
%
%   Return arguments:
%       x - solution vector
%       fval - objective value at the solution
%       exitflag - exit status (see below)
%       iter - number of iterations taken by the solver
%       feval - number of function evaluations taken by the solver
%
%   Option Fields (all optional - see pswarmset):
%       display - solver display level [0,1,2]
%       tolfun - function tolerance
%       maxiter - maximum solver iterations
%       maxfeval - Maximum number of function evaluations
%       maxtime - Maximum solver execution time
%       swarm_size - Swarm Size
%       vectorized - Objective function is vectorized
%       mu - Cognitial Parameter
%       nu - Social Parameter
%       iweight - Initial Weight
%       fweight - Final Weight
%       delta - Initial Delta
%       idelta - Increase Delta
%       ddelta - Decrease Delta
%
%   Return Status:
%       1 - converged
%       0 - maximum iterations / function evaluations exceeded
%      -1 - abnormal exit
%      -2 - memory error
%      -3 - population error
%
%
%   Copyright (C) 2012 Jonathan Currie (IPL)


%IM_MEASURE Fixed mapping computating features by DIP_Image
%
%		F = IM_MEASURE(A,GRAY,FEATURES)
%
% INPUT
%   A        Dataset with binary object images dataset (possibly multi-band)
%   GRAY     Gray-valued images (matched with A, optional)
%   FEATURES Features to be computed
%
% OUTPUT
%   F        Dataset with computed features
%
% In each image of the measurement set GRAY the features given in FEATURES
% are measured. In A a segmented version of GRAY has to be supplied.
% When no GRAY is supplied, the binary images in A are used. Only
% the largest object in each image is considered.
%
% The following features may be computed:
%    'dimension','mean','stddev','gravity','size','center','max','min',
%    'maxval','minval','feret'','inertia','ccbendingenergy'.
% Note that some features like 'mean' (mean image intensity) and 'stddev'
% (standard deviation of image intensity) are not useful for binary images.
% Run MEASUREHELP to get some information on these measures.
%
% Use FEATURES = 'all' for computing all features.
% Use MEASUREHELP for some description of the features.
% Use IM_FEATURES for a set of features not based on DIP_Image
%
% SEE ALSO (PRTools Guide)
% DATASETS, DATAFILES, MEASURE, MEASUREHELP

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function b = im_measure(a,gray,features)

	checktoolbox('diplib');
	if nargin < 3  features = []; end
	if nargin < 2  gray = []; end
	%if nargin < 2 | isempty(gray), gray = a; end

	if nargin < 1 | isempty(a)
		b = prmapping(mfilename,'fixed',{gray,features});
		b = setname(b,'DIP measurements');
	elseif isdataset(a)
		if (nargin < 3 | isempty(features)) & ~isdataset(gray)
			features = gray;
			gray = a;
		end
		if ~isdataset(gray)
			error('Binary and gray images should be both datasets')
		end
		fsize = getfeatsize(a);
		if any(getfeatsize(gray) ~= fsize)
			error('Image structures of binary and gray images should be identical')
		end
		if length(fsize) == 2, fsize = [fsize 1]; end
		if size(a,1) ~= size(gray,1)
			error('Same number of binary and gray images expected')
		end
		out = [];
		binim = data2im(a);
		grim = data2im(gray);
		nim = size(a,1)*fsize(3);
		s = sprintf('Measuring %i images',nim);
		prwaitbar(nim,s);
		for i=1:size(a,1)
			for j=1:fsize(3)
				prwaitbar(nim,(i-1)*fsize(3)+j);
				f = feval(mfilename,binim(:,:,j,i),grim(:,:,j,i),features);
				if isempty(out)
					out = repmat(f(:)',[size(a,1),1,fsize(3)]);
					%out = reshape(f(:)',[size(a,1),1,fsize(3)]);
				else
					out(i,:,j) = f;
				end
			end
		end
		prwaitbar(0);
		b = setdat(a,out);
		b = setfeatsize(b,[length(f),fsize(3)]);
		b = setfeatlab(b,getfeaturelabels(features));
	elseif isdatafile(a)
		if nargin < 3 | isempty(features) & ~isdatafile(gray)
			features = gray;
			gray = a;
		end
		if ~isdatafile(gray)
			error('Binary and gray images should be both datafiles')
		end
		b = dyadic(a,mfilename,gray,{features});
		%b = setfeatlab(b,getfeaturelabels(features));
  elseif isa(a,'double') | isa(a,'dip_image') % here we have a single image
		if isempty(features), features = 'dimension'; end
		gray = 1.0*dip_image(gray);
		%labim = label(dip_image(im_select_blob(a),'bin'));
		labim = label(dip_image(a,'bin'));
    c = measure(labim,gray,'size',[],2);
    labid = c.id;
    sz = c.size;
    [bb,mm] = max(sz);
    labid = labid(mm);
		if strcmp(features,'all')
			features = {'dimension','mean','stddev','gravity',...
			'size','center','max','min', 'maxval','minval',...
			'feret','inertia', 'ccbendingenergy'};
		end
		b = measure(labim,gray,features,labid,2);
    b = double(b);
	else
		error('Wrong input')
	end

return

function names = getfeaturelabels(features)

names = {};
for i=1:length(features)
	switch features{i}
	case 'dimension'
		names{end+1} = 'imagewidth';
		names{end+1} = 'imageheight';
	case 'mean'
		names{end+1} = 'mean int';
	case {'mean', 'sum'}
		names{end+1} = 'mass';
	case 'stddev'
		names{end+1} = 'standard dev.';
	case 'gravity'
		names{end+1} = 'gravity x';
		names{end+1} = 'gravity y';
	case 'size'
		names{end+1} = 'size';
	case 'center'
		names{end+1} = 'center x';
		names{end+1} = 'center y';
	case 'max'
		names{end+1} = 'max x coord';
		names{end+1} = 'max y coord';
	case 'min'
		names{end+1} = 'min x coord';
		names{end+1} = 'min y coord';
	case 'maxval'
		names{end+1} = 'max int';
	case 'minval'
		names{end+1} = 'min int';
	case 'perimeter'
		names{end+1} = 'perimeter';
	case 'feret'
		names{end+1} = 'max diameter';
		names{end+1} = 'min diameter';
		names{end+1} = 'max perp. diameter';
	case 'inertia'
		names{end+1} = 'inertia moment 1';
		names{end+1} = 'inertia moment 2';
	case 'ccbendingenergy'
		names{end+1} = 'bending energy perimeter';
	otherwise
		error('I do not know feature %s.',features{i});
	end
end

return


function str = VBA_summaryMFX(out)
% writes a summary string from output of VBA_MFX model inversion
% function str = VBA_summaryMFX(out)
% IN:
%   - out: the 'out' structure of VBA_MFX inversion routine
% OUT:
%   - str: a cell array of strings, which summarize the VBA inversion

str{1} = sprintf(['Date: ',datestr(out.date)]);
s0 = ['VB converged in ',num2str(out.it),' iterations'];
try
    if floor(out.dt./60) == 0
        timeString = [num2str(floor(out.dt)),' sec'];
    else
        timeString = [num2str(floor(out.dt./60)),' min'];
    end
    str{2} = sprintf([s0,' (took ~',timeString,')']);
catch
    str{2} = sprintf(s0);
end
str{3} = sprintf(['Dimensions of the MFX analysis:','\n ',...
    '    - subjects: ns=',num2str(out.options.dim.ns),'\n ',...
    '    - data: p=',num2str(out.options.dim.p),'\n ',...
    '    - hidden states: n=',num2str(out.options.dim.n),'\n ',...
    '    - evolution parameters: n_theta=',num2str(out.options.dim.n_theta),'\n ',...
    '    - observation parameters: n_phi=',num2str(out.options.dim.n_phi)]);
str{4} = sprintf('\n     Within-subject generative model:\n');
if isa(out.options.g_fname,'function_handle')
    gfn = func2str(out.options.g_fname);
else
    gfn = out.options.g_fname;
end
if out.options.dim.n >= 1
    if isa(out.options.f_fname,'function_handle')
        ffn = func2str(out.options.f_fname);
    else
        ffn = out.options.f_fname;
    end
    str{4} = sprintf([str{4},...
        '       - observation function: ',gfn,'\n',...
        '       - evolution function: ',ffn]);
else
    str{4} = sprintf([str{4},'     - observation function: ',gfn]);
end
mF = mean(out.within_fit.F(:));
sF = std(out.within_fit.F(:));
mF0 = mean(out.within_fit.LLH0(:));
sF0 = std(out.within_fit.LLH0(:));
str{5} = sprintf([...
    '\n     - Bayesian log model evidences: <log p(y|m)> = ',num2str(mF,'%4.3e'),' +/- ',num2str(sF,'%4.3e'),'\n',...
    '     - Bayesian log evidences under the null: <log p(y|H0)> = ',num2str(mF0,'%4.3e'),' +/- ',num2str(sF0,'%4.3e')]);

if any([out.options.sources.type]==0)
    R2str = 'coefficient of determination: <R2>';
    mR = mean(out.within_fit.R2);
    sR = std(out.within_fit.R2);
    str{6} = sprintf([...
        '\n     - ',R2str,' = ',num2str(mR,'%4.3f '),' +/- ',num2str(sR,'%4.3e ')]);
end

if any([out.options.sources.type]>0)
    R2str = 'balanced classification accuracy <Acc>';
    mR = mean(out.within_fit.R2);
    sR = std(out.within_fit.R2);
    str{7} = sprintf([...
        '\n     - ',R2str,' = ',num2str(mR,'%4.3f '),' +/- ',num2str(sR,'%4.3f ')]);
end


function [legal] = isLegal(v)
legal = sum(any(imag(v(:))))==0 & sum(isnan(v(:)))==0 & sum(isinf(v(:)))==0;


function writeKaldiNetworks_FeedForward(layer, kaldiFileName)

fprintf('Save DNN into Kaldi network format: %s!\n', kaldiFileName);

FID = fopen(kaldiFileName, 'w');
fprintf(FID, '<Nnet>\n');

for i=1:length(layer)
    fprintf('Write layer %d - %s - %s\n', i, layer{i}.name, datestr(now));
    switch lower(layer{i}.name)
        case 'affine'
            [outputSize, inputSize] = size(layer{i}.W);
            fprintf(FID, '<AffineTransform> %d %d\n', outputSize, inputSize);
            fprintf(FID, '<LearnRateCoef> 1 <BiasLearnRateCoef> 1 <MaxNorm> 0  [\n');

            for j=1:outputSize-1
                fprintf(FID, '%f ', layer{i}.W(j,:));
                fprintf(FID,'\n');
            end
            fprintf(FID, '%f ', layer{i}.W(end,:));
            fprintf(FID,']\n');

            fprintf(FID, '[ ');
            fprintf(FID, '%f ', layer{i}.b);
            fprintf(FID, ']\n');
        case 'sigmoid'
            fprintf(FID, '<Sigmoid> %d %d\n', outputSize, outputSize);
        case 'softmax'
            fprintf(FID, '<Softmax> %d %d\n', outputSize, outputSize);
    end
end

fprintf(FID, '</Nnet>\n');
fclose(FID);


function DAt = getDAtm(A,Ablkjc,dense,DAtdenq,d,K)
% DAt = getDAtm(A,Ablkjc,dense,DAtdenq,d,K)
%
% GETDATM  Computes d[k]'*Aj[k] for each lorentz block k and constraint j.
%
% ******************** INTERNAL FUNCTION OF SEDUMI ********************
%
% See also sedumi, getada2.

% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% 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 2 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, write to the Free Software
% Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA
%
nq = length(K.q);
DAt.q = extractA(A,Ablkjc,1,2,K.mainblks(1),K.mainblks(2));
if nq > 0
    DAt.q = spdiags(d.q1,0,nq,nq) * DAt.q;
    DAt.q = DAt.q + ddot(d.q2, A, K.qblkstart, Ablkjc);
end
DAt.denq = adendotd(dense,d,DAt.q(dense.q,:)',DAtdenq,K.qblkstart);
if ~isempty(dense.q)
    DAt.q(dense.q,:) = 0.0;
end


classdef lds < dml.method
%LDS linear dynamical system.
%
%   DESCRIPTION
%   data X is represented as trials x features x timepoints
%
%   State can be partially observed/unobserved during training.
%   partial observability of multiple observations is also supported
%   observations are normally distributed conditional on the state.
%   multiple observation sequences are supported
%
%   bias term is *NOT* automatically added to the model
%
%   K = number of states
%   M = number of observations
%   T = number of timesteps
%
%   X and Y are swapped wrt the Kalman filter conventions
%
%   EXAMPLE
%   rand('seed',2); randn('seed',2);
%
%   nsamples = 1000; ncov = 2; ncycles = 10; ntrials = 2;
%   Y = sin(ncycles * 2 * pi * (1:nsamples) ./ nsamples);
%   Y = repmat(reshape(Y,[1 1 numel(Y)]),[ntrials 1 1]);
%   X = repmat(Y,[1 ncov 1]) + 0.5*randn(ntrials,ncov,nsamples);
%
%   k = dml.lds('inference','smooth','verbose',true,'indims',[ncov nsamples]);
%   k = k.train(X,Y);
%   Z = k.test(X);
%   figure
%   plot(squeeze(Y(1,:,:))','k');
%   hold on;
%   plot(squeeze(Z(1,:,:))','r');
%   legend('real','predicted');
%
%   k = dml.lds('verbose',true);
%   k = k.train(X,nan(size(Y))); % hidden state estimation
%   U = repmat(Y,[1 ncov 1]) + 0.1*randn(ntrials,ncov,nsamples);
%   Z = k.test(U);
%   figure
%   plot(squeeze(Y(1,:,:))','k--','LineWidth',2);
%   hold on;
%   plot(zscore(squeeze(Z(1,:,:))'),'r','LineWidth',2);
%   plot(squeeze(X(1,:,:))','bo');
%   legend('real','predicted','observed');
%
%   k = dml.lds('inference','smooth','verbose',true);
%   k = k.train(X,[Y nan(size(Y))]); % mixture of hidden + observed states
%   U = repmat(Y,[1 ncov 1]) + 0.1*randn(ntrials,ncov,nsamples);
%   Z = k.test(X);
%   figure
%   plot(zscore(squeeze(Y(1,:,:))'),'k');
%   hold on;
%   plot(zscore(squeeze(Z(1,:,:))'),'r');
%   plot(zscore(squeeze(X(1,:,:))'),'bo');
%   legend('real','predicted','observed');
%
%   REFERENCES
%   Pattern Recognition and Machine Learning, Bishop
%   A unifying review of linear dynamical systems, Gharamani
%
%   DEVELOPER
%   Marcel van Gerven (m.vangerven@donders.ru.nl)
%   Ali Bahramisharif (ali@cs.ru.nl)

  properties

    maxiter = 100; % maximum number of EM iterations
    thresh = 1e-4; % EM convergence threshold
    diagQ = 0; % regularize state noise to diagonal (0 <= diagQ <=1)
    diagR = 0; % regularize measurement noise to diagonal (0 <= diagR <=1)

    inference = 'smooth'; % filter / smooth

    epsilon = 0 % added to the diagonal of the covariance matrices for numerical stability (e.g. 1e-7);

    A % K x K transition matrix for the unobservable state
    C % M x K emission matrix for the observations
    mu0 % K x 1 the initial mean of the hidden state
    V0 % K x K the initial hidden noise covariance
    R % M x M measurement noise covariance
    Q % K x K state noise covariance

    loglik % log likelihood

    nhidden = 1 % number of hidden states; automatically determined if Y is given

  end

  methods

    function obj = lds(varargin)

      obj = obj@dml.method(varargin{:});
    end

    function obj = train(obj,X,Y)

      % resize data if indims is specified
      if ~isempty(obj.indims)
        X = reshape(X,[size(X,1) obj.indims]);
      end

      % cast to cell array (multiple observation sequences)
      % representation as variables * timepoints
      if ~iscell(X)
        if ndims(X) ~= 3, error('input should be of size trials x features x time'); end
        Xt = cell(1,size(X,1));
        for c=1:length(Xt)
          Xt{c} = reshape(X(c,:,:),[size(X,2) size(X,3)]);
        end
        X = Xt; clear Xt;
      else
        for c=1:length(X)
          X{c} = X{c};
        end
      end

      if nargin < 3

        % assume nhidden signals underlying the observations
        Y = cell(1,length(X));
        for c=1:length(X)
          Y{c} = nan(obj.nhidden,size(X{c},2));
        end

      else

        if ~iscell(Y)
          if ndims(Y) ~= 3, error('input should be of size trials x features x time'); end
          Yt = cell(1,size(Y,1));
          for c=1:length(Yt)
            Yt{c} = reshape(Y(c,:,:),[size(Y,2) size(Y,3)]);
          end
          Y = Yt; clear Yt;
        else
          for c=1:length(Y)
            Y{c} = Y{c}';
          end
        end

      end

      % remove empty elements
      eidx = cellfun(@(x)(isempty(x)),X);
      eidx = eidx & cellfun(@(x)(isempty(x)),Y);
      X = X(~eidx);
      Y = Y(~eidx);

      K = size(Y{1},1);
      M = size(X{1},1);
      N = length(X);

      if isempty(obj.A), obj.A = 1e-3*randn(K,K); end
      if isempty(obj.C), obj.C = 1e-3*randn(M,K); end
      if isempty(obj.mu0), obj.mu0 = 1e-3*randn(K,1); end
      if isempty(obj.V0), obj.V0 = 1e-3*eye(K); end
      if isempty(obj.R), obj.R = 1e-3*eye(M); end
      if isempty(obj.Q), obj.Q = 1e-3*eye(K); end

%       % DEBUG
%       [obj.A, obj.C, obj.Q, obj.R, obj.mu0, obj.V0, obj.loglik] = learn_kalman(X,obj.A,obj.C,obj.Q,obj.R,obj.mu0,obj.V0);
%       return;

      oldLL = inf;
      LL = 0;
      loglik = [];
      iter = 0;
      while abs(LL - oldLL) > obj.thresh && iter < obj.maxiter

        oldLL = LL;

        % E step
        LL=0;
        for c=1:N % iterate over sequences

          [G1t,G2t,G3t,G4t,G5t,G6t,mu0t,V0t,LLt] = obj.Estep(X{c},Y{c});

          if c==1

            G1=G1t;
            G2=G2t;
            G3=G3t;
            G4=G4t;
            G5=G5t;
            G6=G6t;
            mu0=mu0t;
            V0=V0t + mu0t*mu0t';
            LL=LLt;

          else

            G1 = G1 + G1t;
            G2 = G2 + G2t;
            G3 = G3 + G3t;
            G4 = G4 + G4t;
            G5 = G5 + G5t;
            G6 = G6 + G6t;

            mu0 = cat(2,mu0,mu0t);
            V0 = V0 + V0t + mu0t*mu0t';

            LL = LL + LLt;

          end

        end

        loglik = [loglik LL];

        if obj.verbose && ~isnan(LL)
          fprintf('EM step: %d; log likelihood: %g\n',iter,LL);
        end

        if iter && (LL < oldLL), fprintf('non-decreasing log likelihood!\n'); end

        % M step
        T = sum(cellfun(@(x)(size(x,2)),X));

        obj.C = G6 / G1;

        obj.R = (G5 - obj.C * G6') ./ T;
        obj.R = (obj.R + obj.R') ./ 2;

        if obj.diagR
          obj.R = (1-obj.diagR) * obj.R + obj.diagR * diag(diag(obj.R));
        end

        obj.A = G4 / G3;

        obj.Q = (G2 - obj.A * G4') ./ (T-N);
        obj.Q = (obj.Q + obj.Q') ./ 2;

        if obj.diagQ
          obj.Q = (1-obj.diagQ) * obj.Q + obj.diagQ * diag(diag(obj.Q));
        end
        obj.mu0 = mean(mu0,2);
        obj.V0 = V0/N - obj.mu0*obj.mu0'+(mu0-repmat(obj.mu0,[1 N]))*(mu0-repmat(obj.mu0,[1 N]))'/N;

        % symmetricize and make positive semidefinite
        obj.V0 = (obj.V0 + obj.V0') ./ 2;
        obj.V0 = obj.V0 + obj.epsilon*eye(size(obj.V0));

        iter = iter + 1;

        if ~any(isnan(Y{1}(:)))
          break;
        end

      end

      if obj.verbose
        fprintf('EM step: %d; log likelihood: %g\n',iter,LL);
      end

      obj.loglik = loglik(2:end);

    end

    function [mu,V,loglik] = test(obj,X)
     % LDS inference

     if ~iscell(X)
       if ndims(X) ~= 3, error('input should be of size trials x features x time'); end
       Xt = cell(1,size(X,1));
       for c=1:length(Xt)
         Xt{c} = squeeze(X(c,:,:));
       end
       X = Xt; clear Xt;
     end

     nsets = length(X);

     mu = cell(1,nsets);
     for c=1:nsets

       [m,V,loglik] = obj.filter(X{c});

       if strcmp(obj.inference,'smooth')
         [m,V] = obj.smooth(m,V);
       end

       mu{c} = m';

     end

     % reshape if all sequences are of the same size
     if numel(unique(cellfun(@(x)(size(x,1)),mu)))==1
      Z = zeros(length(mu),size(mu{1},2),size(mu{1},1));
      for z=1:length(mu)
        Z(z,:,:) = mu{z}';
      end
      mu = Z;
     end

    end

    function [mu,V,J,VP] = smooth(obj,mu1,V1,Y)
      % Kalman smoother; uses filtered means and variances

      T = numel(V1);

      nandim=[];
      dim=[];
      if exist('Y','var')
          for i=1:size(Y,1)
              if isnan(Y(i,1))
                  nandim=[nandim i];
              else
                  dim=[dim,i];
              end
          end

          A = obj.A(nandim,nandim);
          C = obj.C(:,nandim);
          Q = obj.Q(nandim,nandim);
      else

          A   = obj.A;
          Q   = obj.Q;
          C   = obj.C;
      end
      % P(t,t-1) at horizon
      P = A * V1{T-1} * A' + Q;
      PC = P * C';
      K = PC / (C * PC + obj.R);
      VP=cell(1,T);
      KC=K*C;
      VP{T}=(eye(size(KC))-KC)*A*V1{T-1};

      % RTS equations

      mu = mu1;
      V = V1;
      J = cell(1,T-1); % needed to compute transition matrix A in EM
      for n=T:-1:2

          P = A * V1{n-1} * A' + Q;
        if det(P)<0
            disp('bad predictive covariance')
            lambda=max(svd(P));
            P=P+(lambda+obj.epsilon)*eye(size(P));
        end
        if ~all(V{n}(:)==0)

          J{n-1} = (V1{n-1} * A') / P;

        if numel(nandim)>0
          mu(:,n-1) = mu1(:,n-1) + J{n-1} * (mu(:,n) - A * mu1(:,n-1)-obj.A(nandim,dim)*Y(dim,n-1));
        else
          mu(:,n-1) = mu1(:,n-1) + J{n-1} * (mu(:,n) - A * mu1(:,n-1));
        end

          V{n-1} = V1{n-1} + J{n-1} * (V{n} - P)*J{n-1}';

          if n<T
            VP{n}=V1{n}*J{n-1}'+J{n}*(VP{n+1}-A*V1{n})*J{n-1}';
          end

        else

          % fix when variance is zero (deterministic state)
          J{n} = zeros(size(V{n}));
          mu(:,n) = mu1(:,n);
          V{n} = zeros(size(V{n}));
          VP{n} = zeros(size(V{n}));

        end

        % symmetricize and make positive semidefinite
        V{n} = (V{n} + V{n}') ./ 2;
        V{n} = V{n} + obj.epsilon*eye(size(V{n}));

      end

    end

    function [mu,V,LL] = filter(obj,X,varargin)
      % Kalman filter algorithm; returns mean mu and covariance matrix V of
      % the states, the log likelihood and the observations Xobs with missing
      % values imputed (the latter remains to be done)

      nandim=[];
      dim=[];
      if ~isempty(varargin)
          Y=varargin{1};
      end
      if exist('Y','var')
          for i=1:size(Y,1)
              if isnan(Y(i,1))
                  nandim=[nandim i];
              else
                  dim=[dim,i];
              end
          end

          A = obj.A(nandim,nandim);
          C = obj.C(:,nandim);
          mu0 = obj.mu0(nandim);
          V0 = obj.V0(nandim,nandim);
          R = obj.R;
          Q = obj.Q(nandim,nandim);
          X1=X;
          X=X-obj.C(:,dim)*Y(dim,:);
      else
          A = obj.A;
          C = obj.C;
          mu0 = obj.mu0;
          V0 = obj.V0;
          R = obj.R;
          Q = obj.Q;
          Y=zeros(size(A,1),size(X,2));
          X1=X;
      end

      K = size(A,1);
      T = size(X,2);

      mu = nan(K,T);
      V = cell(1,T);
      I = eye(K);

      Xpred=mu;
      Vpred=V;

      % take measurements into account
      if isempty(X) || all(isnan(X(:,1)))

        mu(:,1) = mu0;
        V{1} = V0;

      else
        Xpred(:,1)=mu0;
        Vpred{1}=V0;
        obs = ~isnan(X(:,1));
        Cb = C(obs,:); % emission matrix for observed measurements

        % Kalman gain matrix
        K = (V0 * Cb') / (Cb * V0 * Cb' + R(obs,obs));

        mu(:,1) = mu0 + K * (X(obs,1) - Cb * mu0);
        V{1}  = (I - K * Cb) * V0;

      end

      % symmetricize and make positive semidefinite
      V{1} = (V{1} + V{1}') ./ 2;
      V{1} = V{1} + obj.epsilon*eye(size(V{1}));

      for t=2:T

        if numel(nandim)>0
          AM = A * mu(:,t-1)+obj.A(nandim,dim)*Y(dim,t-1);
        else
            AM = A * mu(:,t-1);
        end
        P = A * V{t-1} * A' + Q;

        if isempty(X) || all(isnan(X(:,t)))

          mu(:,t) = AM;
          V{t} = P;

        else
            Xpred(:,t)=AM;
            Vpred{t}=P;
          obs = ~isnan(X(:,t));
          Cb = C(obs,:); % emission matrix for observed measurements

          PC = P * Cb';

          K = PC / (Cb * PC + R(obs,obs));

          mu(:,t) = AM + K * (X(obs,t) - Cb * AM);
          V{t}  = (I - K * Cb) * P;

        end

        % symmetricize and make positive semidefinite
        V{t} = (V{t} + V{t}') ./ 2;
        V{t} = V{t} + obj.epsilon*eye(size(V{t}));

      end

      % compute log likelihood using error decomposition ignoring constant terms
      if nargout >= 3
          V1 = cell(1,T);
          K=size(obj.A,1);
          Y1=zeros(K,T);
          Y1(dim,:)=Y(dim,:);
          if numel(nandim)<1
              Y1=mu;
              V1=V;
          else
              Y1(nandim,:)=Xpred;
              i=1;
              V1{i}=zeros(K);
              V1{i}(dim,dim)=obj.Q(dim,dim);
              V1{i}(dim,nandim)=obj.Q(dim,nandim);
              V1{i}(nandim,dim)=obj.Q(nandim,dim);
              V1{i}(nandim,nandim)=Vpred{i};
              for i=2:T
                  V1{i}=zeros(K);
                  V1{i}(dim,dim)=obj.Q(dim,dim);
                  V1{i}(dim,nandim)=obj.Q(dim,nandim);
                  V1{i}(nandim,dim)=obj.Q(nandim,dim);
                  V1{i}(nandim,nandim)=Vpred{i};
              end
          end
          LL = obj.compute_loglik(X1,Y1,V1);
      end

    end

    function ll = likelihood(obj,X)
      % returns log likelihood of a sequence of observations

      if ~iscell(X)
        if ndims(X) ~= 3, error('input should be of size trials x features x time'); end
        Xt = cell(1,size(X,1));
        for c=1:length(Xt)
          Xt{c} = squeeze(X(c,:,:));
        end
        X = Xt; clear Xt;
      end

      ll = nan(length(X),1);

      for c=1:length(X)

        [mu,V,ll(c)] = obj.filter(X{c});

      end

    end

  end

  methods(Access=protected)

    function [G1,G2,G3,G4,G5,G6,mu0,V0,LL] = Estep(obj,X,Y)

      K = size(Y,1);
      M = size(X,1);
      T = size(X,2);

      nandim=[];
      dim=[];
      if exist('Y','var')
          for i=1:K
              if isnan(Y(i,1))
                  nandim=[nandim i];
              else
                  dim=[dim,i];
              end
          end
      end

      if any(isnan(Y(:))) % hidden state

          [mu1,V1,LL] = obj.filter(X,Y);
          [Y1,V1,J1,VP1] = obj.smooth(mu1,V1,Y);

          Y(nandim,:)=Y1;
          VP=cell(1,T);
          V = cell(1,T);
          %J = cell(1,T-1);
          for i=1:T
              if i>1
                  VP{i}=zeros(K);
                  VP{i}(nandim,nandim)=VP1{i};
              end
              V{i}=zeros(K);
              V{i}(dim,dim)=V{i}(dim,dim)+obj.epsilon*eye(length(dim));
              V{i}(nandim,nandim)=V1{i};
              %if i<T
              %    J{i}=zeros(K);
              %    J{i}(nandim,nandim)=J1{i};
              %end
          end

      else

        V = repmat({0},size(Y));
        %J = repmat({0},[size(Y,1) size(Y,2)-1]);
        VP = repmat({0},size(Y));
        LL = nan;

      end

      G1 = zeros(K,K);
      G4 = zeros(K,K);
      for t=1:T

        G1  = G1 + V{t} + Y(:,t)*Y(:,t)';

        if t>1

          G4 = G4 + Y(:,t)*Y(:,t-1)' + VP{t};
          %G4 = G4 + J{t-1}*V{t} + Y(:,t)*Y(:,t-1)'; % bishop

        end

      end

      G2 = G1 - Y(:,1) * Y(:,1)' - V{1};
      G3 = G1 - Y(:,T) * Y(:,T)' - V{T};

      mu0 = Y(:,1);
      V0 = V{1};

      % deal with partially observed observations
      if any(isnan(X(:)))

        C = obj.C;
        R = obj.R;

        G5 = zeros(M,M);
        for t=1:T

          XX = X(:,t) * X(:,t)';
          nidx = isnan(XX(:));
          if any(nidx)
            E = X(:,t) * Y(:,t)' * C' + R;
            XX(nidx) = E(nidx);
          end
          nidx = isnan(XX(:));
          if any(nidx)
            E = C * Y(:,t) * X(:,t)' + R;
            XX(nidx) = E(nidx);
          end
          nidx = isnan(XX(:));
          if any(nidx)
            E = C * (V{t} + Y(:,t)*Y(:,t)') * C' + R;
            XX(nidx) = E(nidx);
          end

          G5 =  G5 + XX;

        end

        G6 = zeros(M,K);
        for t=1:T

          XY = X(:,t) * Y(:,t)';
          EXY = C * (V{t} + Y(:,t)*Y(:,t)');
          nidx = isnan(XY(:));
          XY(nidx) = EXY(nidx);
          G6 = G6 + XY;

        end

      else
        G5 = X * X';
        G6 = X * Y';
      end

    end

    function LL = compute_loglik(obj,X,Y,V)

      T = size(X,2);

      C = obj.C;
      R = obj.R;

      LL = 0;
      for t=1:T

        obs = ~isnan(X(:,t));

        e = X(obs,t) - C(obs,:)*Y(:,t); % innovation (measurement error)
        S = C(obs,:)*V{t}*C(obs,:)' + R(obs,obs); % covariance of the innovation
        LL = LL - log(det(S)) ./ 2 - ((e' / S) * e) ./ 2;
        if ~isreal(LL)
            LL=real(LL);
            disp('bad likelihood')
        end
        LL = LL - (numel(obs)/2) * log(2*pi); %length of the feature vector

      end

    end

  end

end


function month_name = month_to_month_name_republican ( m )

%*****************************************************************************80
%
%% MONTH_TO_MONTH_NAME_REPUBLICAN returns the name of a Republican month.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the month index.
%
%    Output, string MONTH_NAME, the month name.
%
  name = [ ...
    'Vendemaire    '; 'Brumaire      '; 'Frimaire      '; 'Nivose        '; ...
    'Pluviose      '; 'Ventose       '; 'Germinal      '; 'Floreal       '; ...
    'Prairial      '; 'Messidor      '; 'Thermidor     '; 'Fructidor     '; ...
    'Sansculottides' ];

  if ( m < 1 || 13 < m )

    month_name = '??????????????';

  else

    month_name = name(m,:);

  end

  return
end


verts = Target.vertices;
normals = Target.normals;
faceMap = [find(verts(:,2) < 0)'; 1:numel(find(verts(:,2) < 0))]';
newVerts = verts(verts(:,2) < 0, :);
newNormals = normals(verts(:,2) < 0, :);

flatFaces = reshape(Target.faces, numel(Target.faces), 1);
flatFacesNew = zeros(size(flatFaces));
% Flattens by cols
for v=1:numel(flatFaces)
    oldVert = flatFaces(v);
    newVert = faceMap(faceMap(:,1) == oldVert, 2);
    if isempty(newVert)
        flatFacesNew(v) = NaN;
    else
        flatFacesNew(v) = newVert;
    end
    disp(v);
end
newFaces = reshape(flatFacesNew, numel(flatFacesNew)/3, 3);
newFaces = newFaces(~any(isnan(newFaces),2),:);

TargetMissing.vertices = newVerts;
TargetMissing.faces = newFaces;
TargetMissing.normals = newNormals;

save('data/faceTargetMissing.mat', 'TargetMissing')


function [dat, roi_val, maskdat, beta_dat, beta_roi_val] = canlab_connectivity_preproc(dat, varargin)

% This function prepares data for connectivity analysis by removing nuisance
% variables and temporal filtering (high, low, or bandpass filter). This also
% can extract values from given masks and return averaged activity or pattern
% expression values.
%
% :Usage:
% ::
%
%    [preprocessed_dat, roi_val, maskdat] = canlab_connectivity_preproc(dat, varargin)
%    [preprocessed_dat, roi_val, maskdat, (optional) beta_dat, beta_roi_val] = canlab_connectivity_preproc(dat, varargin)
%
% :Features:
%
%   - can regress out nuisance variables with any additional nuisance matrix
%   - can remove signal from ventricle and white matter (default: use
%     canonical ventricle and white matter masks)
%   - can do temporal filtering, including high-pass, low-pass, or bandpass
%     filtering (it uses conn_filter.m from conn toolbox, which is using
%     fast fourier transform; see subfunction below)
%   - can extract data from given ROIs, and return averaged value or pattern
%     expression value (dot-product).
%   - Subsequent nuisance regression step is orthogonalized with respect to
%     pass-filter, consistent with principles in Lindquist 2019
%   - It is possible to extract ROI averages or linear patterns, save them,
%     and run this function afterwards. This will give the same result as
%     filtering each  voxel and then calculating the ROI average/pattern
%     response.
%   - (optional) can run additional GLM model with additional regressors if they are
%     specified. This runs the GLM *additionally*, therefore do not change
%     residualized image data or roi_vals. This GLM uses the same nuisance
%     matrix from above. output: beta_dat
%
% *Steps in order [with defaults]:*
%   1. Remove nuisance covariates (and linear trend if requested)
%   2. Remove ventricle and white matter - default: uses canonical images
%   3. High/low/bandpass filter on BOTH data and covs before regression (plot)
%   4. Residualization using regression (plot)
%   5. (optional) Run additional GLM using the same additional preprocessing
%   6. Windsorize based on distribution of full data matrix (plot)
%   7. Extract region-by-region average ROI or pattern expression data
%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2014  Wani Woo
%
%     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/>.
% ..
%
% :Inputs:
%
%   **dat:**
%        fmri_data object with data
%
%   **dat.covariate:**
%        basic nuisance matrix
%
% :Optional Inputs:
%
%   **additional_nuisance**
%        When you have additional nuisance variables that you want
%        regress out from the data, you can use this option. This
%        option should be followed by a nuisance matrix (or values).
%        The matrix should have the same number of rows with the
%        number of images.
%
%   **vw**
%        When you want to regress out signals from ventricle and
%        white matter, you can use this option. Default is using canonical
%        white matter and ventricle images (but eroded versions).
%
%        You can also choose what to use to remove ventricle and
%        white matter signal between raw data or top 5 PCA
%        components (default). You can just put 'raw' if you want to
%        use raw signal than PCA compoenents.
%        also see: canlab_extract_ventricle_wm_timeseries.m
%        canlab_create_wm_ventricle_masks.m)
%        - *Example:* 'vw','raw'
%
%   **windsorize:**
%        Windsorizing entire data matrix to k x STD.
%        - *Example:* 'windsorize', 5 (windsorize to 5 STD)
%
%   **linear_trend:**
%        This option will include the linear trend to nuisance variables.
%
%   **hpf', 'lpf', or 'bpf:**
%        This option will do temporal filtering.
%        - 'hpf': high pass filter. This option should be followed by
%                 the lower bound of the frequency (e.g., .01 Hz [= 100
%                 sec]), then TR
%        - 'lpf': low pass filter. This option should be followed by
%                 the upper bound of the frequency (e.g., .25 Hz [= 4 sec]), then TR.
%        - 'bpf': bandpass filter. This should be followed by lower
%                 and upper bounds of the frequency (e.g., [.01 .25]), then TR.
%                 In all cases, after the frequency value, you need to provide TR in sec.
%        - *Example:* 'hpf', .01, TR
%                     'bpf', [.01 .25], TR
%
%   **regressors:**
%        - This option will run additional GLM with the regressors specified
%          by the experimenter. The number of rows should be the number of
%          images, and the number of columns are the number of regressors.
%        - with extract_roi, the function will extract roi values from beta
%          images as well.
%        - Example: 'regressors', X (you can build your own design matrix,
%                                   e.g., hrf convolution using onset and
%                                   duration, using onsets2fmridesign.m)
%
%   **extract_roi:**
%        This option will extract data from ROIs specified. This
%        option should be followed by one or more masks.
%        For one mask (potentially multiple ROIs, enter a char array with the mask name.
%        For multiple masks (1 or more), enter in a cell array of mask names.
%        You can specify methods with 'roi_methods' option.
%        - 'average_over' (default): calculate averaged value across the ROIs.
%        - 'pattern_expression': calculate dot-products between
%                                pattern mask and data
%        - 'unique_mask_values' (default): will divide a mask into
%                                          multiple regions that have different discrete values.
%        - 'contiguous_regions': will divide a mask into multiple
%                                contiguous regions.
%        - 'whole': will do average_over or pattern_expression across
%                   all the voxels within the mask.
%        - *Example:* 'extract_roi', mask, 'contiguous_regions'
%                     'extract_roi', mask, 'pattern_expression'
%
%   **no_preproc:**
%        If you want to skip the preprocessing part, and want to
%        extract ROI values only, you can use this option.
%
%   **no_plots:**
%        To suppress plots, enter 'no_plots'
%
% :Outputs:
%
%  **preprocessed_dat:**
%        fmri_data object after removing nuisance variables and
%        filtering temporal confounds.
%
%  **roi_val:**
%        returns values extracted from ROIs in cell arrays (if there are many different ROIs).
%        Each cell will have roi_val.dat, roi_val.mask_name, and roi_val.methods.
%
%  **maskdat:**
%        returns resampled roi (or pattern) masks
%
%  **beta_dat:**
%        returns beta images from the additional GLM (see 'regressors' option)
%
%
% :Examples:
% ::
%
%     roi_masks = which('weights_NSF_grouppred_cvpcr.img');
%     [preprocessed_dat, roi_val] = canlab_connectivity_preproc(dat, 'vw',
%               'bpf', [.008 .25], TR, 'extract_roi', roi_masks, 'pattern_expression');

% ..
%    PROGRAMMER'S NOTE
%    05/19/15 fixed a bug related to conn_filter
%    08/24/18 implemented simultaneous pass filter + regression
%    01/05/19: Tor and Marianne: Pass filter all covariates (even
%               spikes., Consistent with principles in Lindquist 2019
%               "sequential filtering" paper. See more notes below.
%    08/01/19: Wani reversed Tor and Marianne's revision above
%               after dicussion with Tor and others. Spikes should be removed
%               before "low-pass filtering" the nuisance covariates, and
%               added to the covariates later (see Ciric et al., 2018)
%               Lindquist et al., 2019 did not address the issue related to
%               the use of spike regressors combined with low pass
%               filtering. This issue should be further examined later.
%    08/01/19: Wani added an option for additional GLM
% ..

additional_R = [];
remove_vent_wm = false;
use_canonical_masks = true;
do_filter = false;
do_extract_roi = false;
do_preproc = true;
do_windsorize = false;
do_linear = false;
do_plots = true;
regressors = [];
do_additional_regress = false;

z = '===============================================================';
zz = '---------------------------------------------------------------';
fprintf('%s\n', z);

% parsing varargin
for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}
            case {'additional_nuisance', 'additional_R'}
                additional_R = varargin{i+1};

            case {'vw', 'ventricle_whitematter', 'remove_vw'}
                remove_vent_wm = true;

                % filter
            case {'hpf', 'lpf', 'bpf'}
                do_filter = true;
                TR = varargin{i+2};

                if any(strcmp(varargin, 'hpf')), filter(1) = varargin{i+1}; filter(2)= Inf; end
                if any(strcmp(varargin, 'lpf')), filter(1) = 0; filter(2) = varargin{i+1}; end
                if any(strcmp(varargin, 'bpf')), filter = varargin{i+1}; end

            case {'windsorize'}
                do_windsorize = true;
                k_std = varargin{i+1};

                % detrend
            case {'linear_trend'}
                do_linear = true;

                % extract roi values
            case {'extract_roi', 'roi', 'mask'}
                do_extract_roi = true;
                mask = varargin{i+1};

            case {'no_preproc'}
                do_preproc = false;

            case {'no_plots'}
                do_plots = false;

            case {'regressors'}
                regressors = varargin{i+1};
                do_additional_regress = true;
                regressors(:,all(regressors==1)) = []; % remove an intercept if any
                nreg = size(regressors,2);
        end
    end
end

%% Remove nuisance

if do_preproc

    tic;

    nobs = size(dat.dat, 2);
    if do_additional_regress
        if size(regressors,1) ~= nobs
            error('The number of rows of regressors is different from the number of images from dat. Please check.');
        end
    end

    fprintf('Canlab_connectivity_preproc: starting preprocessing ... \n');
    comb_R = [regressors dat.covariates additional_R]; % if all these are empty, comb_R should still be empty

    if do_additional_regress
        beta_dat = dat;
        beta_dat.dat = [];
    end

    dat.dat = double(dat.dat);  % enforce double to avoid data format problems
    dat.history{end+1} = 'Processed with canlab_connectivity_preproc';

    %% white matter, ventricle

    if do_plots
        create_figure('plot_qc',2,4);
        set(gcf, 'position', get(0,'screensize')); drawnow;
        plot_qc(dat,1,0,[]); drawnow;
    end

    if remove_vent_wm

        fprintf('::: removing ventricle & white matter signal ... \n');
        use_pca_comp_vw = true; % default

        if any(strcmp(varargin, 'raw')), use_pca_comp_vw = false; end

        if any(strcmp(varargin, 'datdir'))
            use_canonical_masks = false;
            subject_dir = varargin{find(strcmp(varargin, 'datdir'))+1};
        end

        if use_canonical_masks

            [value, components] = extract_gray_white_csf(dat, 'masks', ...
                {'gray_matter_mask.nii', 'canonical_white_matter_thrp5_ero1.nii', ...
                'canonical_ventricles_thrp5_ero1.nii'});
            wm_nuisance = value(:,2:3); % white, CSF
            wm_nuisance_comps = [components{2} components{3}]; % 5 components for white, CSF

        else

            if ~exist(fullfile(subject_dir, 'Structural/SPGR/white_matter.img'), 'file')...
                    && ~exist(fullfile(subject_dir, 'Structural/SPGR/ventricles.img'), 'file')
                wm_mask = filenames(fullfile(subject_dir, 'Structural/SPGR/wc2*.nii'), 'char', 'absolute');
                gm_mask = filenames(fullfile(subject_dir, 'Structural/SPGR/wc1*.nii'), 'char', 'absolute');
                canlab_create_wm_ventricle_masks(wm_mask, gm_mask, 'wm_thr', .9, 'vent_thr', .9);
            end

            [wm_nuisance, wm_nuisance_comps] = canlab_extract_ventricle_wm_timeseries(fullfile(subject_dir, 'Structural/SPGR'), dat, 'noplot');

        end

        if use_pca_comp_vw

            comb_R = [comb_R scale(wm_nuisance_comps)];

        elseif ~use_pca_comp_vw

            comb_R = [comb_R scale(wm_nuisance)];

        end

        dat.history{end+1} = 'Extracting white matter and ventricle signals';

    end

    %% linear trend
    if do_linear

        comb_R(:,end+1) = scale((1:nobs)');
        dat.history{end+1} = '(added linear detrending to nuisance covs)';

    end

    % (optional) global signal % not implemented

    if do_filter

            fprintf('::: temporal filtering brain data... \n');
            % conn_filter does mean-center by default.
            % We need mean back in

            % Pass-filter brain data
            % -------------------------------------------
            dat.dat = conn_filter(TR, filter, dat.dat', 'full')' ...
                + repmat(mean(dat.dat,2), 1, size(dat.dat,2));

            dat.history{end+1} = 'Done temporal filtering brain data';

            if do_plots
                plot_qc(dat,2,1,[]); drawnow;
            end
    end

    %% get residuals

    if ~isempty(comb_R)
        % Do regression/residualization

        if size(comb_R, 1) ~= nobs
            error('Nuisance covariates and data matrix have different nunmbers of cases.');
        end

        %% temporal filter (low and high): low pass, high pass, bandpass
        % Simultaneous temporal filtering and regression

        if do_filter

            fprintf('::: temporal filtering covariates ... \n');
            % conn_filter does mean-center by default.
            % We need mean back in

            comb_R = filter_cov_wo_spikes(comb_R, TR, filter);

            dat.history{end+1} = 'Done temporal filtering covariates';

        end

        % add intercept
        X = [comb_R ones(nobs,1)];

        if do_additional_regress

            % regression
            beta_dat.dat = data.dat * pinv(X)';
            beta_dat.dat = beta_dat.dat(:,1:nreg);
            beta_dat.covariates = X;
            X(:,1:nreg) = [];
        end

        % residualize
        dat.dat = dat.dat - dat.dat * pinv(X)' * X'; % this is more efficient than (dat.dat' - X*pinv(X)*dat.dat')'
        dat.history{end+1} = 'Regressed out nuisance covariates';

        if do_plots
            clim = plot_qc(dat,3,1,[]); drawnow;
        end

    else  % no regression

    end

    %% Windsorize entire data matrix to k STD
    if do_windsorize
        dat = windsorize(dat, k_std); % history added within the function
        if do_plots
            plot_qc(dat,4,0,clim); drawnow;
        end
    end

    t = toc;
    fprintf('Done: data preprocessing in %4.1d sec.\n\n', t);
    fprintf('%s\n\n', zz);
end

%% get ROI values

if do_extract_roi
    tic;
    fprintf('Canlab_preproc: extracting ROI signal ... \n');

    % default
    do_pattern = false;
    do_whole = false;
    unique_mask_values = true;
    contiguous_regions = false;

    if any(strcmp(varargin, 'pattern_expression')), do_pattern = true; unique_mask_values = false; end
    if any(strcmp(varargin, 'whole')), do_whole = true; unique_mask_values = false; end
    if any(strcmp(varargin, 'contiguous_regions')), contiguous_regions = true; unique_mask_values = false; end

    if ~iscell(mask)
        mask_temp = mask; clear mask;
        mask = cell(size(mask_temp,1),1); % preallocation
        for i = 1:size(mask_temp,1)
            mask{i} = mask_temp(i,:);
        end
    end

    [roi_val, maskdat] = extract_roi_sub(dat, mask, do_pattern, do_whole, unique_mask_values, contiguous_regions);

    if do_additional_regress
        beta_roi_val = extract_roi_sub(beta_dat, mask, do_pattern, do_whole, unique_mask_values, contiguous_regions);
    end

    % output: roi_val, maskdat
    % input: mask, dat, do_pattern, do_whole, unique_mask_values, contiguous_regions

    t2 = toc;
    fprintf('Done in %4.1d sec.\n', t2);
    fprintf('%s\n', zz);

    if do_preproc
        fprintf('Total time: 4.1%d sec.\n', t+t2);
        fprintf('%s\n', z);
    end
else
    roi_val = {};
end

end

%%  ---------------------------------------------------
%    Sub-functions
%   ---------------------------------------------------

function [y,fy]=conn_filter(rt, filter, x, option)

% from conn toolbox
% http://www.nitrc.org/projects/conn/

if nargin < 4, option='full'; end

fy=fft(x,[],1);
f=(0:size(x,1)-1);
f=min(f,size(x,1)-f);

switch(lower(option))
    case 'full'

        idx=find(f<filter(1)*(rt*size(x,1))|f>=filter(2)*(rt*size(x,1)));
        %idx=idx(idx>1);
        fy(idx,:)=0;
        y=real(ifft(fy,[],1))*2*size(x,1)*(min(.5,filter(2)*rt)-max(0,filter(1)*rt))/max(1,size(x,1)-numel(idx));

    case 'partial'

        idx=find(f>=filter(1)*(rt*size(x,1))&f<filter(2)*(rt*size(x,1)));
        %if ~any(idx==1), idx=[1,idx]; end
        y=fy(idx,:);

end

end

function obj = windsorize(obj, k)

% Windsorize entire data matrix to k x STD

m = sum(sum(obj.dat)) ./ numel(obj.dat); % save memory space

s = k .* std(obj.dat(:));

whbad = obj.dat < m - s;
obj.dat(whbad) = m - s;
whbad2 = obj.dat > m + s;
obj.dat(whbad2) = m + s;

nbad = sum(whbad(:)) + sum(whbad2(:));
percbad = 100 .* nbad ./ numel(obj.dat);

obj.history{end+1} = sprintf('Windsorized data matrix to %d STD; adjusted %3.0f values, %3.1f%% of values', k, nbad, percbad);
disp(obj.history{end});
end

function clim = plot_qc(dat, n, use7sd, clim)

% dat
% n: steps
% use7sd: use -6sd~+6sd as a range
% clim: specify color limit

titles = {'RAW:before Conn preproc', 'Pass filter', 'Residualize (Nuisance, VentWM, linear trend, if selected)', 'Windsorize'};

subplot(2, 4, n);
plot(mean(dat.dat));
set(gca, 'xlim', [0 size(dat.dat,2)]);
xlabel('timeseries');
ylabel('Global mean');
title(titles{n});

subplot(2, 4, n+4);

if use7sd
    m = nanmean(dat.dat(:));
    sd = nanstd(dat.dat(:));
    clim = [m-7*sd m+7*sd];
end

if isempty(clim)
    imagesc(dat.dat);
else
    imagesc(dat.dat, clim);
end

colormap(gray);
set(gca, 'xlim', [0 size(dat.dat,2)]);
xlabel('timeseries');
ylabel('voxels');
colorbar('southoutside');

end

function R = filter_cov_wo_spikes(R, TR, filter)

% Filter the covariates
% Eliminate bias from sequential filtering operations
% -------------------------------------------
% 1/15/2019: Tor and Marianne: Pass filter all covariates (even
% spikes), because filter transforms data in a way that matches
% what is done to spike regressors. So even if spike regressors
% "look funny", they adjust for the effects of "spikes" on the
% time series data appropriately, accounting for filtering.
%
% 8/1/2019: Wani reversed this to the previous version after
% dicussion with Tor and others. Spikes should be removed
% before "low-pass filtering" the nuisance covariates, and
% added to the covariates later (see Ciric et al., 2018)

% pass-filter covs (without outlier indices)
outlier_idx = sum(R==1)==1;
outliers = R(:,outlier_idx);
R(:,outlier_idx) = [];

R = conn_filter(TR, filter, R, 'full') ...
    + repmat(mean(R), size(R,1), 1);

% add outlier indices back in
R = [R outliers];

end

function [roi_val, maskdat] = extract_roi_sub(dat, mask, do_pattern, do_whole, unique_mask_values, contiguous_regions)

% subfunction for extract rois

% copy over apply_mask
maskdat = cell(numel(mask),1);
for i = 1:numel(mask)
    maskdat{i} = fmri_data(mask{i});
    isdiff = compare_space(dat, maskdat{i});

    if isdiff == 1 || isdiff == 2 % diff space, not just diff voxels
        maskdat{i} = resample_space(maskdat{i}, dat, 'nearest');

        if length(maskdat{i}.removed_voxels) == maskdat{i}.volInfo.nvox
            disp('Warning: resample_space returned illegal length for removed voxels. Fixing...');
            maskdat{i}.removed_voxels = maskdat{i}.removed_voxels(maskdat{i}.volInfo.wh_inmask);
        end
    end
end

roi_val = cell(numel(mask),1); % preallocation

for i = 1:numel(mask)
    if do_whole
        % single, whole-brain pattern
        if ~do_pattern

            roi_obj = apply_mask(dat, maskdat{i});
            roi_obj = remove_empty(roi_obj);
            roi_val{i}.dat = nanmean(roi_obj.dat)';
            roi_val{i}.mask_name = mask{i};
            roi_val{i}.methods = 'averaged_over using a whole mask';

        elseif do_pattern

            roi_val{i}.dat = apply_mask(dat, maskdat{i}, 'pattern_expression', 'ignore_missing');
            roi_val{i}.mask_name = mask{i};
            roi_val{i}.methods = 'pattern expression using a whole mask';

        end
    else
        % separate patterns for local regions

        if ~do_pattern
            if unique_mask_values

                roi_obj = extract_roi_averages(dat, maskdat{i});
                roi_val{i}.dat = cat(2, roi_obj(:).dat);

                % extract_roi_averages does not preserve missing cases: remove
                if any(dat.removed_images)
                    roi_val{i}.dat = roi_val{i}.dat(~dat.removed_images, :);
                end

                roi_val{i}.mask_name = mask{i};
                roi_val{i}.methods = 'averaged_over using unique_mask_values';

            elseif contiguous_regions

                roi_obj = extract_roi_averages(dat, maskdat{i}, 'contiguous_regions');
                roi_val{i}.dat = cat(2, roi_obj(:).dat);

                % extract_roi_averages does not preserve missing cases: remove
                if any(dat.removed_images)
                    roi_val{i}.dat = roi_val{i}.dat(~dat.removed_images, :);
                end

                roi_val{i}.mask_name = mask{i};
                roi_val{i}.methods = 'averaged_over using contiguous_regions';

            else
                warning('Unknown ROI extraction option. Not extracting ROI data...');
            end
        else
            % local patterns within each region

            roi_obj = extract_roi_averages(dat, maskdat{i}, 'pattern_expression', 'contiguous_regions');
            roi_val{i}.dat = cat(2, roi_obj(:).dat);

            % extract_roi_averages does not preserve missing cases: remove
            if any(dat.removed_images)
                roi_val{i}.dat = roi_val{i}.dat(~dat.removed_images, :);
            end

            roi_val{i}.mask_name = mask{i};
            roi_val{i}.methods = 'pattern expression using contiguous_regions';
        end
    end
end

end


function test_bug1833

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_plot_mesh ft_read_headshape

% at this moment (20 November 2012) this test script is known not to work
% and the bugzilla report is still open
return

% this test script borrows some data from another bug
cd(dccnpath('/home/common/matlab/fieldtrip/data/test/bug1820'));

mesh = ft_read_headshape('tet_4layer_127_127_127.1.node');
mesh.tet = mesh.tet(1:10000,:); % prune the number of elements
ft_plot_mesh(mesh, 'vertexcolor', 'none', 'edgecolor', 'none');

mesh = ft_read_headshape('cube2mm3layervorwerk_ns_127_127_127.v');
mesh.hex = mesh.hex(1:1000,:); % prune the number of elements
ft_plot_mesh(mesh, 'vertexcolor', 'none', 'edgecolor', 'none');


function [mu, s2, MM, VV] = simulLMGPmc(hyp, inffcn, meanfcn, covfcn, likfcn, input, target, targetvariance,...
    derivinput, derivtarget, derivvariance, xt, lag, Nsamples)
% simulLMGPmc - Simulation of the dynamic GP model with incorporated local models (LMGP models),
% where the output variance is propagated using the numerical Monte Carlo approximation
%
%% Syntax
%  [mu, s2, MM, VV] = simulLMGPmc(hyp, inf, mean, cov, lik, input, target, targetvariance,...
%     derivinput, derivtarget, derivvariance, xt, lag, Nsamples)
%
%% Description
% Idea: at every time step the output of GP model is approximated with
% the Gaussian distribution, from which we sample one value. We repeat the
% procedure Nsamples-times.
% Nsamples samples, which are used as the future inputs of the GP model.
% Samples are re-used if necessary (ie. y(k-1) for y(k-2) if lag=2 etc.)
% Currently it can be used only with the Gaussian covariance function and
% with the white noise model (sum of covSEard and covNoise) due to the gpSD00.
% Uses routine gpSD00.
% see K. Azman. Identifikacija dinamicnih sistemov z Gaussovimi procesi. PhD
% thesis, University of Ljubljana, Ljubljana, 2007 (in Slovene).
%
% Input:
% * hyp            ... the structure of hyperparameters
% * inf      	   ... the inference method 	      --> not used, for interface compatibility only
% * cov      	   ... the prior covariance function  --> not used, for interface compatibility only
% * mean    	   ... the prior mean function        --> not used, for interface compatibility only
% * lik      	   ... the likelihood function        --> not used, for interface compatibility only
% * input          ... the input part of the training data,  NxD matrix
% * target         ... the output part of the training data (ie. target), Nx1 vector
% * targetvariance ... the target variance, use NaN where not known
% * derivinput     ... the input part of the derivative training data, NEQxD matrix
% * derivtarget    ... target derivatives, NEQxD matrix
% * derivvariance  ... variances of the local model prameters, NEQxD matrix
% * xt             ... the input matrix for simulation, kxD vector, see
%                      construct_ARXsimul_input.m for more info
% * lag            ... the order of the model (number of used lagged outputs)
% * Nsamples       ... the number of samples used in algorithm (ie. runs of simulation)
%
% Output:
% * mu             ... the mean predicted output
% * s2             ... associated variances (including noise variance)
% * MM             ... the matrix of all predicted means, kxNsamples
% * VV             ... associated predicted variances
%
% See Also
% gpSD00.m, simulLMGPnaive.m, simulGPmc.m
%
% Examples
% demo_example_lmgp_simulation.m
%
%%
% * Written by K.Azman, 31.05.2005
% * Based on the work of C.E. Rasmussen and A. Girard.
%
%
% Changelog:
%
% 16.2.2015, Martin Stepancic:
%		 	-changed the function interface as gpml > 3.0
%			-removed the addition of autocovariance - this is now
%			 already included in gpSD00.m.
%

fun_name = 'simulLMGPmc';

[n, D]      = size(input);
fullinput  = [input; repmat(derivinput,D,1)];
fulltarget = [target; derivtarget(:)];
[N, D]      = size(fullinput);
[nD, D] = size(derivinput);

MM = zeros(Nsamples,size(xt,1));
VV = zeros(Nsamples,size(xt,1));

for jjj=1:Nsamples

    if(mod(jjj,10)==0)
        disp([fun_name, ', run:  ',int2str(jjj),'/',int2str(Nsamples)]);
    end

    % 1st point - input is "point"
    test = xt(1,:);
    [mu(1), s2(1)] = gpSD00(hyp, inffcn, meanfcn, covfcn, likfcn, input, target, ...
			targetvariance, derivinput, derivtarget, derivvariance, test);

    for k=2:length(xt)

        test = [xt(k, lag+1:end)];

        % For the NEXT prediction...
        % assumed normal distribution, for more accurate procedure see
        % simulGPmc

        % random sample from assumed distribution
        ysampled = mu(k-1) + randn(1)*sqrt(s2(k-1));
        % generate input for the GP model
        if (k>lag)
            test = [mu(k-lag:k-2) ysampled xt(k, lag+1:end)];
        elseif (k<=lag)
            test = [xt(k, 1:lag-k+1) mu(1:k-2) ysampled xt(k, lag+1:end)];
        end

        [mu(k), s2(k)] = gpSD00(hyp, inffcn, meanfcn, covfcn, likfcn, input, target, ...
			targetvariance, derivinput, derivtarget, derivvariance, test);
    end

    MM(jjj,:) = mu;
    VV(jjj,:) = s2;

end
% individual realisations saved in matrices MM and VV
% approximate all output distributions with Gaussian distribution
mu = mean(MM);
s2 = mean(VV) + mean((MM-repmat(mu,Nsamples,1)).^2);

mu = mu';
s2 = s2';

MM = MM';
VV = VV';

return


function tissueModel = INIT(model, weights, tol, runtime, logfile, epsilon)
% Use the INIT algorithm (`Agren et al., 2012`) to extract a context
% specific model using data. INIT algorithm find the optimal trade-off
% between inluding and removing reactions based on their given weights. If
% desired, accumulation of certain metabolites can be allowed or even
% forced.
%
% USAGE:
%
%    tissueModel = INIT(model, weights, tol, runtime, logfile)
%
% INPUTS:
%    model:               input model (COBRA model structure)
%    weights:             column with positive and negative weights for each reaction
%                         positive weights are reactions with high expression, negative
%                         weigths for reaction with low expression (must be same length
%                         as model.rxns)
%
% OPTIONAL INPUTS:
%    tol:                 minimum flux threshold for "expressed" reactions
%                         (default 1e-8)
%    logfile:             name of the file to save the MILP log (string)
%    runtime:             maximum solve time for the MILP (default value - 7200s)
%    epsilon:             value added/subtracted to upper/lower bounds
%                         (default 1)
%
% OUTPUTS:
%    tissueModel:         extracted model
%
% `Agren et al. (2012). Reconstruction of genome-scale active metabolic
% networks for 69 human cell types and 16 cancer types using INIT. PLoS
% Comput. Biol. 8, e1002518.`
%
% .. Authors:  - Implementation adapted from the cobra toolbox (createTissueSpecificModel.m) by S. Opdam and A. Richelle, May 2017

if isfield(model,'C') || isfield(model,'E')
    issueConfirmationWarning('INIT does not handle the additional constraints and variables defined in the model structure (fields .C and .E.)\n It will only use the stoichiometry provided.');
end

if nargin < 6 || isempty(epsilon)
    epsilon=1;
end
if nargin < 5 || isempty(runtime)
    runtime = 7200;
end
if nargin < 4 || isempty(logfile)
    logfile = 'MILPlog';
end
if nargin < 3 || isempty(tol)
    tol = 1e-8;
end

    RHindex = find(weights > 0);
    RLindex = find(weights < 0);

    %Weights of 0 will be handled the same as in iMAT

    S = model.S;
    lb = model.lb;
    ub = model.ub;

    % Creating A matrix
    A = sparse(size(S,1)+2*length(RHindex)+2*length(RLindex),size(S,2)+2*length(RHindex)+length(RLindex));
    [m,n,s] = find(S);
    for i = 1:length(m)
        A(m(i),n(i)) = s(i);
    end

    for i = 1:length(RHindex)
        A(i+size(S,1),RHindex(i)) = 1;
        A(i+size(S,1),i+size(S,2)) = lb(RHindex(i)) - epsilon;
        A(i+size(S,1)+length(RHindex),RHindex(i)) = 1;
        A(i+size(S,1)+length(RHindex),i+size(S,2)+length(RHindex)+length(RLindex)) = ub(RHindex(i)) + epsilon;
    end

    for i = 1:length(RLindex)
        A(i+size(S,1)+2*length(RHindex),RLindex(i)) = 1;
        A(i+size(S,1)+2*length(RHindex),i+size(S,2)+length(RHindex)) = lb(RLindex(i));
        A(i+size(S,1)+2*length(RHindex)+length(RLindex),RLindex(i)) = 1;
        A(i+size(S,1)+2*length(RHindex)+length(RLindex),i+size(S,2)+length(RHindex)) = ub(RLindex(i));
    end

    % Creating csense
    csense1(1:size(S,1)) = 'E';
    csense2(1:length(RHindex)) = 'G';
    csense3(1:length(RHindex)) = 'L';
    csense4(1:length(RLindex)) = 'G';
    csense5(1:length(RLindex)) = 'L';
    csense = [csense1 csense2 csense3 csense4 csense5];

    % Creating lb and ub
    lb_y = zeros(2*length(RHindex)+length(RLindex),1);
    ub_y = ones(2*length(RHindex)+length(RLindex),1);
    lb = [lb;lb_y];
    ub = [ub;ub_y];

    % Creating c
    c_v = zeros(size(S,2),1);
    c_y = ones(2*length(RHindex)+length(RLindex),1);
    c_w = [weights(RHindex);weights(RHindex);abs(weights(RLindex))];
    c = [c_v;c_w.*c_y];

    % Creating b
    b_s = zeros(size(S,1),1);
    lb_rh = lb(RHindex);
    ub_rh = ub(RHindex);
    lb_rl = lb(RLindex);
    ub_rl = ub(RLindex);
    b = [b_s;lb_rh;ub_rh;lb_rl;ub_rl];

    % Creating vartype
    vartype1(1:size(S,2),1) = 'C';
    vartype2(1:2*length(RHindex)+length(RLindex),1) = 'B';
    vartype = [vartype1;vartype2];

    MILPproblem.A = A;
    MILPproblem.b = b;
    MILPproblem.c = c;
    MILPproblem.lb = lb;
    MILPproblem.ub = ub;
    MILPproblem.csense = csense;
    MILPproblem.vartype = vartype;
    MILPproblem.osense = -1;
    MILPproblem.x0 = [];

    solution = solveCobraMILP(MILPproblem, 'timeLimit', runtime, 'logFile', logfile, 'printLevel', 3);

    x = solution.cont;
    rxnRemList = model.rxns(abs(x) < tol);
    tissueModel = removeRxns(model,rxnRemList);
end


function cvx_optval = matrix_frac( x,Y )

%MATRIX_FRAC   Internal cvx version.

narginchk(2,2);
if ndims( Y ) > 2 || size( Y, 1 ) ~= size( Y, 2 ), %#ok

    error( 'Second argument must be a square matrix.' );

elseif ndims( x ) > 2 || size( x, 2 ) > 1, %#ok

    error( 'First argument must be a column vector.' );

elseif size( x, 1 ) ~= size( Y, 1 ),

    error( 'Size of first argument (vector) must match size of second argument (matrix).' );

elseif cvx_isconstant( x ) && cvx_isconstant( Y ),

    cvx_optval = cvx( matrix_frac( cvx_constant( x ), cvx_constant(Y) ) );

elseif cvx_isaffine( x ) && cvx_isaffine( Y ),

    n = size( x, 1 );
    z = [];
    cvx_begin
        epigraph variable z
        [Y x; x' z] == semidefinite( n+1 ); %#ok
    cvx_end

else

    error( 'Disciplined convex programming error:\n    MATRIX_FRAC is convex and nonmonotonic, so its input must be affine.' );

end

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function []=ps_baseline_plot(linecolor)
%PS_BASELINE_PLOT plot single master baselines
%
%   Andy Hooper, March 2011
%
%   ======================================================================
%   ======================================================================
if nargin<1
    linecolor=[1 0 0];
end

load psver
psname=['ps',num2str(psver)];
ps=load(psname);

small_baseline_flag=getparm('small_baseline_flag');
if ~strcmpi(small_baseline_flag,'n')
   error('You are not in a PS directory')
end

m=ps.master_ix;

%clf

for i=1:length(ps.day)
    l=line([ps.day(i),ps.day(m)],[ps.bperp(i),ps.bperp(m)]);
    set(l,'color',linecolor,'linewidth',2)
end
hold on
set(gca,'fontsize',14)
p=plot(ps.day,ps.bperp,'k+');
set(p,'markersize',8,'linewidth',1)
hold off
datetick('x',10)
xlabel('Acquisition Date')
ylabel('Perpendicular Baseline (m)')


function [params, names] = kbrExtractParam(model,dim);

% KBREXTRACTPARAM Extract parameters from the KBR model structure.
% FORMAT
% DESC extracts parameters from the kernel based regression model
% structure into a vector of parameters for optimisation.
% ARG model : the model structure containing the parameters to be
% extracted.
% RETURN param : vector of parameters extracted from the model.
%
% DESC extracts parameters and parameter names from the kernel based regression model structure.
% ARG model : the model structure containing the parameters to be
% extracted.
% RETURN param : vector of parameters extracted from the model.
% RETURN names : cell array of strings containing names for each parameter.
%
%
%
% SEEALSO : KBRCREATE, KBREXPANDPARAM, MODELEXTRACTPARAM, SCG, CONJGRAD
%
% COPYRIGHT : Neil D. Lawrence, 2005, 2006, 2008
%
% MODIFICATIONS : Carl Henrik Ek, 2007

% MLTOOLS

if(nargin<2)
  params = [model.A(:)' model.bias];
else
  params = model.A(:,dim);
  params = [params(:)' model.bias(dim)];
end

if nargout > 1
  % Add names to parameters
  counter = 0;
  for i = 1:model.numData
    for j = 1:model.outputDim
      counter = counter + 1;
      names{counter} = ['A(' num2str(i) ', ' num2str(j) ')'];
    end
  end
  for j = 1:model.outputDim
    counter = counter + 1;
    names{counter} = ['bias(' num2str(j) ')'];
  end
end


function binary = sample_bernoulli(probabilities)
    global report_calls_to_sample_bernoulli
    if report_calls_to_sample_bernoulli,
        fprintf('sample_bernoulli() was called with a matrix of size %d by %d. ', size(probabilities, 1), size(probabilities, 2));
    end
    seed = sum(probabilities(:));
    binary = +(probabilities > a4_rand(size(probabilities), seed)); % the "+" is to avoid the "logical" data type, which just confuses things.
end


% @Author: amishkin
% @Date:   2018-06-07T18:38:11-07:00
% @Email:  amishkin@cs.ubc.ca
% @Last modified by:   amishkin
% @Last modified time: 2018-07-10T15:10:42-07:00

function [delta, algo_params] =  get_expt_params(dataset_name, method_name, trial_params);
    % this file returns expreiment parameters for each data, method pair

    % set experiment parameters
    mini_batch_size = trial_params(2);
    num_epochs = trial_params(3);
    switch dataset_name
    case {'murphy_synth'}
        delta = 100;
        data_set_size = 60;
    case {'breast_cancer_scale'}
        delta =  1;
        data_set_size = 341;
    case {'australian_scale'}
        data_set_size = 345;
        delta =  1e-5;
    case {'a1a'}
        data_set_size = 1605;
        delta =  2.8072;
    case {'usps_3vs5'}
        data_set_size = 770;
        delta =  25;
    case {'colon-cancer'}
        data_set_size = 31;
        delta =  596.3623;
    case {'covtype_binary_scale'}
        data_set_size = 290506;
        delta =  1e-5;
    otherwise
        error('no such dataset');
    end
    max_iter = floor((num_epochs * data_set_size) / mini_batch_size);

    switch method_name
    case {'mf_exact'}
        algo_params = struct(...
        'max_iters', num_epochs);
    otherwise
        algo_params = struct(...
        'max_iters', max_iter,...
        'num_epochs', num_epochs,...
        'beta', trial_params(4),...
        'alpha', trial_params(5), ...
        'decay_rate', trial_params(6), ...
        'num_samples', trial_params(1), ...
        'mini_batch_size', mini_batch_size);
    end
end


params.dictsize = length(wordsbi);
params.numcases = length(labels);

fprintf('CV using dataset l=%d, dictSize=%d, CVNUM=%d\n', ...
    length(allSNumBi), length(wordsbi), params.CVNUM)

% initial = 1.1;
randn('state', 0);
rand('state', 0);

allcounts = [];
allfps = []; allfns = [];
for i=[1:params.CVNUM]
    train_ind = cv_obj.training(i);
    test_ind = cv_obj.test(i);
    assert(0==sum(train_ind == test_ind))

    model = trainfuncp(allSNumBi(train_ind), labels(train_ind), params);
%
    [acc pred softpred] = testfuncp(model, ...
        allSNumBi(test_ind), labels(test_ind), params);

    nblbltst = labels(test_ind);
    fp = sum(nblbltst == 0 & pred == 1);
    fn = sum(nblbltst == 1 & pred == 0);
    allfps = [allfps fp];
    allfns = [allfns fn];
    allcounts = [allcounts acc];
end
allcounts
mean(allcounts)


function [p1,p2,P1_l,P2_l] = ahmLin2ahmPnts(l)

% AHMLIN2AHMPNTS  AHM line to two AHM points conversion.
%   [P1,P2] = AHMLIN2AHMPNTS(L) extracts the two endpoints of the AHM line
%   L in the form of two HMG points with the same anchor of that of the
%   line.
%
%   [p1,p2,P1_l,P2_l] = AHMLIN2AHMPNTS(...) returns the Jacobians wrt L.

%   Copyright 2009 Teresa Vidal.

p1 = l(1:7,:);
p2 = l([1:3 8:11],:);

if nargout > 2

    P1_l = [eye(7) zeros(7,4)];
    P2_l = [eye(3) zeros(3,8) ; zeros(4,7) eye(4)];

end

return

%% jac

syms x y z a1 b1 c1 n1 a2 b2 c2 n2 real
l = [x y z a1 b1 c1 n1 a2 b2 c2 n2]';

[p1,p2,P1_l,P2_l] = ahmLin2ahmPnts(l);

simplify(P1_l - jacobian(p1,l))
simplify(P2_l - jacobian(p2,l))

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function [s] = vector2struct( v, template )

  if isnumeric(template)

    s = reshape(v, template);

  elseif isfield(template, 'sz') && isfield(template, 'shapes')

    s = vector2cell(v, template);

  else
    count = 0;

    s = [];
    for fi = 1:length(template.fields)
      f = template.fields{fi};
      sz = template.shapes{fi};

      if isfield(sz, 'fields')

        sub_struct = vector2struct(v(count+1:end), sz);
        c = length(struct2vector(sub_struct));
        s.(f) = sub_struct;

      elseif isstruct(sz)

        c = sum(cellfun(@(x) prod(x), sz.shapes));
        if isempty(v)
          next = zeros(c,1);
        else
          next = v(count + [1:c]);
        end
        s.(f) = vector2cell(next, sz);

      else

        c = prod(sz);
        if isempty(v)
          next = zeros(c,1);
        else
          next = v(count + [1:c]);
        end
        s.(f) = reshape(next, sz);

      end

      count = count + c;
    end
  end
end


function [StopFlag, Status] = StopCriterion(grad, nhxk, Niter, Nmap, Ngmap, MaxNumIter, MaxNumMapEval, MaxNumGmapEval, T, TimeLimit, epsilon, nhx0, ngradx0, Stopping_Crit)
% `StopCriterion` is a function checking that one of the stopping criteria
% holds to terminate LLM and GLM. It perepare the status determining why
% the algorithm is stopped.
%
% USAGE:
%
%    [StopFlag, Status] = StopCriterion(grad, nhxk, Niter, Nmap, Ngmap, MaxNumIter, MaxNumMapEval, MaxNumGmapEval, T, TimeLimit, epsilon, nhx0, ngradx0, Stopping_Crit)
%
% INPUTS:
%    grad:              gradient of the merit funcrion
%    nhxk:              the norm 2 of `h(xk)`
%    MaxNumIter:        maximum number of iterations
%    MaxNumMapEval:     maximum number of function evaluations
%    MaxNumGmapEval:    maximum number of subgradient evaluations
%    TimeLimit:         maximum running time
%    epsilon:           accuracy parameter
%    Stopping_Crit:     stopping criterion
%
%                         1. stop if :math:`||grad|| \leq \epsilon`
%                         2. stop if :math:`||nhxk|| \leq \epsilon`
%                         3. stop if `MaxNumIter` is reached
%                         4. stop if `MaxNumMapEval` is reached
%                         5. stop if `MaxNumGmapEval` is reached
%                         6. stop if `TimeLimit` is reached
%                         7. stop if :math:`||grad|| \leq \textrm{max}(\epsilon, \epsilon^2 * ngradx0)`
%                         8. stop if :math:`||nhxk|| \leq \textrm{max}(\epsilon, \epsilon^2 * nhx0)`
%                         9. stop if (default) :math:`||hxk|| \leq \epsilon` or `MaxNumIter` is reached
%
% OUTPUTS:
%    StopFlag:          1: if one of the stopping criteria holds, 0: if none of the stopping criteria holds
%    Status:            the reason of the scheme termination

switch Stopping_Crit
  case 1
    if norm(grad) <= epsilon
      StopFlag = 1;
      Status   = 'Local (global) solution of merit function is found.';
    else
      StopFlag = 0;
      Status   = [];
    end
  case 2
    if nhxk <= epsilon
      StopFlag = 1;
      Status   = 'A solution of nonlinear system is found.';
    else
      StopFlag = 0;
      Status   = [];
    end
  case 3
    if Niter >= MaxNumIter
      StopFlag = 1;
      Status   = 'Maximum number of iterations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end
  case 4
    if Nmap >= MaxNumMapEval
      StopFlag = 1;
      Status   = 'Maximum number of mapping evaluations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end
  case 5
    if Ngmap >= MaxNumGmapEval
      StopFlag = 1;
      Status   = 'Maximum number of gradient evaluations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end
  case 6
    if T >= TimeLimit
      StopFlag = 1;
      Status   = 'Time limit is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 7
    if norm(grad) <= max(epsilon,epsilon^2*ngradx0)
      StopFlag = 1;
      Status   = 'A possible stationary point is found.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 8
    if nhxk <= max(epsilon,epsilon^2*nhx0)
      StopFlag = 1;
      Status   = 'A possible solution is found.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 9
    if (nhxk <= epsilon || Niter >= MaxNumIter)
      StopFlag = 1;
      if Niter < MaxNumIter
          Status = 'a solution is found.';
      else
          Status = 'Maximum number of iterations is reached.';
      end
    else
      StopFlag = 0;
      Status   = [];
    end

end

end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% End of StopCriterion.m %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function B = imageblocks(img, sz, st)

% imageblocks -- Extract blocks of specified size from image or
%                array of images
%
% Usage:
%       B = imageblocks(img, sz, st)
%
% Input:
%       img         Image from which to extract blocks
%       sz          Block size vector
%       st          Block step vector
%
% Output:
%       B           Array of image blocks
%
%
% Author: Brendt Wohlberg <brendt@lanl.gov>  Modified: 2014-10-20
%
% This file is part of the SPORCO library. Details of the copyright
% and user license can be found in the 'License' file distributed with
% the library.

if nargin < 3,
  st = [1 1];
end

Nbr = sz(1);
Nbc = sz(2);
[Nir Nic Nih] = size(img);
Nib = length(1:st(1):(Nir-Nbr+1))*length(1:st(2):(Nic-Nbc+1))*Nih;
B = zeros(Nbr, Nbc, Nib);

% Loop over all blocks in images, adding each one to block set
n = 1;
for k = 1:Nih,
  for l=1:st(1):(Nir-Nbr+1),
    for m=1:st(2):(Nic-Nbc+1),
      B(:,:,n) = img(l:(l+Nbr-1), m:(m+Nbc-1), k);
      n = n + 1;
    end
  end
end

return


function value = mono_000_3d ( n, x )

%*****************************************************************************80
%
%% MONO_000_3D evaluates X**0 Y**0 Z**0.
%
%  Modified:
%
%    25 May 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the spatial dimension.
%
%    Input, real X(N), the point at which the monomial is to be evaluated.
%
%    Output, real VALUE, the value of the monomial.
%
  value = 1.0E+00;

  return
end