% POP_RUNICA - Run an ICA decomposition of an EEG dataset using RUNICA,
% BINICA, or another ICA or other linear decomposition.
% Usage:
% >> OUT_EEG = pop_runica( EEG ); % pops-up a data entry window
% >> OUT_EEG = pop_runica( EEG, 'key', 'val' ); % no pop_up
%
% Graphic interface:
% "ICA algorithm to use" - [edit box] The ICA algorithm to use for
% ICA decomposition. Command line equivalent: 'icatype'
% "Commandline options" - [edit box] Command line options to forward
% to the ICA algorithm. Command line equivalent: 'options'
% Inputs:
% EEG - input EEG dataset or array of datasets
%
% Optional inputs:
% 'icatype' - ['runica'|'binica'|'jader'|'fastica'] ICA algorithm
% to use for the ICA decomposition. The nature of any
% differences in the results of these algorithms have
% not been well characterized. {default: BINICA, if
% found, else RUNICA}
% 'dataset' - [integer array] dataset index or indices.
% 'chanind' - [integer array or cell array] subset of channel indices
% for running the ICA decomposition. Alternatively, you may
% also enter channel types here in a cell array.
% 'concatenate' - ['on'|'off'] 'on' concatenate all input datasets
% (assuming there are several). 'off' run ICA independently
% on each dataset. Default is 'off'.
% 'concatcond' - ['on'|'off'] 'on' concatenate conditions for input datasets
% of the same sessions and the same subject. Default is 'off'.
% 'reorder' - ['on'|'off'] re-order components by variance if that's not
% already the case. Default is 'on'.
% 'key','val' - ICA algorithm options (see ICA routine help messages).
%
% Adding a new algorithm:
% Add the algorithm to the list of algorithms line 366 to 466, for example
%
% case 'myalgo', [EEG.icaweights] = myalgo( tmpdata, g.options{:} );
%
% where "myalgo" is the name of your algorithm (and Matlab function).
% tmpdata is the 2-D array containing the EEG data (channels x points) and
% g.options{} contains custom options for your algorithm (there is no
% predetermined format for these options). The output EEG.icaweights is the
% mixing matrix (or inverse of the unmixing matrix).
%
% Note:
% 1) Infomax (runica, binica) is the ICA algorithm we use most. It is based
% on Tony Bell's infomax algorithm as implemented for automated use by
% Scott Makeig et al. using the natural gradient of Amari et al. It can
% also extract sub-Gaussian sources using the (recommended) 'extended' option
% of Lee and Girolami. Function RUNICA is the all-Matlab version; function
% BINICA calls the (1.5x faster) binary version (a separate download)
% translated into C from RUNICA by Sigurd Enghoff.
% 2) JADER calls the JADE algorithm of Jean-Francois Cardoso. This is
% included in the EEGLAB toolbox by his permission. See >> help jader
% 3) To run FASTICA, download the fastICA toolbox from its website,
% http://www.cis.hut.fi/projects/ica/fastica/, and make it available
% in your Matlab path. According to its authors, default parameters
% are not optimal: Try args 'approach', 'sym' to estimate components
% in parallel.
%
% Outputs:
% OUT_EEG = The input EEGLAB dataset with new fields icaweights, icasphere
% and icachansind (channel indices).
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2001
%
% See also: RUNICA, BINICA, JADER, SOBI, FASTICA.
% Copyright (C) 2001 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.
% 01-25-02 reformated help & license -ad
% 03-07-02 add the eeglab options -ad
% 03-18-02 add other decomposition options -ad
% 03-19-02 text edition -sm
function [ALLEEG, com] = pop_runica( ALLEEG, varargin )
com = '';
if nargin < 1
help pop_runica;
return;
end
% find available algorithms
% -------------------------
allalgs(1).name = 'runica';
allalgs(1).description = 'Extended Infomax (runica.m; default)';
allalgs(1).options = '''extended'', 1, ''rndreset'', ''yes''';
allalgs(end+1).name = 'runica';
allalgs(end).description = 'Robust Extended Infomax (runica.m; slow)';
allalgs(end).options = '''extended'', 1, ''lrate'', 1e-5, ''maxstep'', 2000';
allalgs(end).help = 'See this <a href="https://sccn.ucsd.edu/wiki/Makoto%27s_useful_EEGLAB_code#How_to_obtain_practically_reproducible_ICA_results_.2809.2F26.2F2022_added.29">reference</a> for ICA conservative parameters ';
allalgs(end+1).name = 'amica';
allalgs(end).description = 'AMICA (slowest; best)';
allalgs(end).options = '''maxiter'', 2000';
allalgs(end).help = 'See this <a href="https://github.com/sccn/amica/wiki/AMICA">reference</a> for AMICA';
allalgs(end+1).name = 'picard';
allalgs(end).description = 'Infomax picard.m';
allalgs(end).options = '''maxiter'', 500, ''mode'', ''standard''';
allalgs(end).help = 'See this <a href="https://github.com/pierreablin/picard">page</a> for Picard parameters ';
allalgs(end+1).name = 'picard';
allalgs(end).description = 'FastICA picard.m (fastest)';
allalgs(end).options = '''maxiter'', 500';
allalgs(end).help = 'See this <a href="https://github.com/pierreablin/picard">page</a> for Picard parameters ';
allalgs(end+1).name = 'sobi'; allalgs(end).description = 'SOBI (sobi.m function)';
allalgs(end+1).name = 'acsobiro'; allalgs(end).description = 'SOBI (acsobiro.m function)';
allalgs(end+1).name = 'pearson_ica'; allalgs(end).description = 'Pearson (pearson_ica.m function)';
allalgs(end+1).name = 'jader'; allalgs(end).description = 'Jade (jader.m function)';
allalgs(end+1).name = 'jadeop'; allalgs(end).description = 'Jade (jadeop.m function)';
allalgs(end+1).name = 'jade_td_p'; allalgs(end).description = 'Jade (jade_td_p.m function)';
allalgs(end+1).name = 'MatlabshibbsR'; allalgs(end).description = 'MatlabshibbsR';
allalgs(end+1).name = 'fastica'; allalgs(end).description = 'FastICA (fastica.m function)';
allalgs(end+1).name = 'tica'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'simbec'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'unica'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'amuse'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'fobi'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'evd'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'evd24'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'sons'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'ng_ol'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'acrsobibpf'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'egld_ica'; allalgs(end).description = allalgs(end).name; % do not use egld_ica => too slow
allalgs(end+1).name = 'eeA'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'tfbss'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'icaML'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'icaMS'; allalgs(end).description = allalgs(end).name;
allalgs(end+1).name = 'binica'; allalgs(end).description = 'Executable runica';
defaultopts = allalgs(1).options;
for index = length(allalgs):-1:1
if ~exist(allalgs(index).name, 'file')
allalgs(index) = [];
end
end
% special AMICA (deprecated use the pop_runamica function)
% -------------
selectamica = 0;
if nargin > 1
if ischar(varargin{1})
if strcmpi(varargin{1}, 'selectamica')
selectamica = 1;
allalgs = { 'amica' allalgs{:} };
defaultopts = sprintf('''outdir'', ''%s''', fullfile(pwd, 'amicaout'));
elseif strcmpi(varargin{1}, 'selectamicaloc')
selectamica = 1;
allalgs = { 'amica' allalgs{:} };
defaultopts = sprintf('''outdir'', ''%s'', ''qsub'', ''off''', fullfile(pwd, 'amicaout'));
end
end
end
% popup window parameters
% -----------------------
fig = [];
if nargin < 2 || selectamica
commandchans = [ 'tmpchans = get(gcbf, ''userdata'');' ...
'tmpchans = tmpchans{1};' ...
'set(findobj(gcbf, ''tag'', ''chantype''), ''string'', ' ...
' int2str(pop_chansel( tmpchans )));' ...
'clear tmpchans;' ];
commandtype = ['tmptype = get(gcbf, ''userdata'');' ...
'tmptype = tmptype{2};' ...
'if ~isempty(tmptype),' ...
' [tmps,tmpv, tmpstr] = listdlg2(''PromptString'',''Select type(s)'', ''ListString'', tmptype);' ...
' if tmpv' ...
' set(findobj(''parent'', gcbf, ''tag'', ''chantype''), ''string'', tmpstr);' ...
' end;' ...
'else,' ...
' warndlg2(''No channel type'', ''No channel type'');' ...
'end;' ...
'clear tmps tmpv tmpstr tmptype tmpchans;' ];
cb_ica = ['tmpalgos = get(gcbf, ''userdata'');' ...
'tmpalgos = tmpalgos{3};' ...
'set(findobj(gcbf, ''tag'', ''params''), ''string'', tmpalgos( get(gcbo, ''value'') ).options );' ...
'disp(char(tmpalgos( get(gcbo, ''value'') ).help));' ...
'clear tmpalgos;' ];
% [ 'if get(gcbo, ''value'') < 3, ' ...
% ' set(findobj(gcbf, ''tag'', ''params''), ''string'', ''''''extended'''', 1'');' ...
% 'else,' ...
% ' tmpStr = get(gcbo, ''string'');' ....
% ' tmpAlgo = tmpStr(get(gcbo, ''value''));' ...
% ' if strcmpi(tmpAlgo, ''picard''),' ...
% ' set(findobj(gcbf, ''tag'', ''params''), ''string'', ''''''maxiter'''', 500'');' ...
% ' else,' ...
% ' set(findobj(gcbf, ''tag'', ''params''), ''string'', '''');' ...
% ' end;' ...
% 'end;' ];
promptstr = { { 'style' 'text' 'string' [ 'ICA algorithm to use (click to select)' 10 ] } ...
{ 'style' 'listbox' 'string' { allalgs.description } 'callback', cb_ica } ...
{ 'style' 'text' 'string' 'Commandline options (See help messages)' } ...
{ 'style' 'edit' 'string' defaultopts 'tag' 'params' } ...
{ 'style' 'checkbox' 'string' 'Reorder components by variance (if that''s not already the case)' 'value' 1 } ...
{ 'style' 'text' 'string' 'Use only channel type(s) or indices' } ...
{ 'style' 'edit' 'string' '' 'tag' 'chantype' } ...
{ 'style' 'pushbutton' 'string' '... types' 'callback' commandtype } ...
{ 'style' 'pushbutton' 'string' '... channels' 'callback' commandchans } };
geometry = { [2 1.5] [2 1.5] [1] [2 1 1 1] };
geomvert = [ 3 1 1 1];
if length(ALLEEG) > 1
cb1 = 'set(findobj(''parent'', gcbf, ''tag'', ''concat2''), ''value'', 0);';
cb2 = 'set(findobj(''parent'', gcbf, ''tag'', ''concat1''), ''value'', 0);';
promptstr = { promptstr{:}, ...
{ 'style' 'text' 'string' 'Concatenate all datasets (check=yes; uncheck=run ICA on each dataset)?' }, ...
{ 'style' 'checkbox' 'string' '' 'value' 0 'tag' 'concat1' 'callback' cb1 }, ...
{ 'style' 'text' 'string' 'Concatenate datasets for the same subject and session (check=yes)?' }, ...
{ 'style' 'checkbox' 'string' '' 'value' 1 'tag' 'concat2' 'callback' cb2 } };
geometry = { geometry{:} [ 2 0.2 ] [ 2 0.2 ]};
geomvert = [ geomvert 1 1];
end
% channel types
% -------------
if isfield(ALLEEG(1).chanlocs, 'type')
tmpchanlocs = ALLEEG(1).chanlocs;
alltypes = { tmpchanlocs.type };
indempty = cellfun('isempty', alltypes);
alltypes(indempty) = '';
try
alltypes = unique_bc(alltypes);
catch
alltypes = '';
end
else
alltypes = '';
end
% channel labels
% --------------
if ~isempty(ALLEEG(1).chanlocs)
tmpchanlocs = ALLEEG(1).chanlocs;
alllabels = { tmpchanlocs.labels };
else
for index = 1:ALLEEG(1).nbchan
alllabels{index} = int2str(index);
end
end
% gui
% ---
result = inputgui( 'geometry', geometry, 'geomvert', geomvert, 'uilist', promptstr, ...
'helpcom', 'pophelp(''pop_runica'')', ...
'title', 'Run ICA decomposition -- pop_runica()', 'userdata', { alllabels alltypes allalgs } );
if isempty(result)
return;
end
options = { 'icatype' allalgs(result{1}).name 'dataset' [1:length(ALLEEG)] 'options' eval( [ '{' result{2} '}' ]) 'reorder' fastif(result{3}, 'on', 'off') };
if ~isempty(result{4})
if ~isempty(str2num(result{4})), options = { options{:} 'chanind' str2num(result{4}) };
else options = { options{:} 'chanind' parsetxt(result{4}) };
end
end
if length(result) > 4
options = { options{:} 'concatenate' fastif(result{5}, 'on', 'off') };
options = { options{:} 'concatcond' fastif(result{6}, 'on', 'off') };
end
else
if mod(length(varargin),2) == 1
options = { 'icatype' varargin{1:end} };
else
options = varargin;
end
end
% decode input arguments
% ----------------------
[ g, addoptions ] = finputcheck( options, { 'icatype' 'string' unique({ allalgs.name }) 'runica'; ...
'dataset' 'integer' [] [1:length(ALLEEG)];
'options' 'cell' [] {};
'concatenate' 'string' { 'on','off' } 'off';
'concatcond' 'string' { 'on','off' } 'off';
'reorder' 'string' { 'on','off' } 'on';
'chanind' { 'cell','integer' } { [] [] } [];}, ...
'pop_runica', 'ignore');
if ischar(g), error(g); end
if ~isempty(addoptions), g.options = { g.options{:} addoptions{:}}; end
% select datasets, create new big dataset if necessary
% ----------------------------------------------------
if length(g.dataset) == 1
EEG = ALLEEG(g.dataset);
EEG = eeg_checkset(EEG, 'loaddata');
if isfield(EEG.etc, 'ic_classification')
EEG.etc = rmfield(EEG.etc, 'ic_classification');
end
elseif length(ALLEEG) > 1 && ~strcmpi(g.concatenate, 'on') && ~strcmpi(g.concatcond, 'on')
[ ALLEEG, com ] = eeg_eval( 'pop_runica', ALLEEG, 'warning', 'off', 'params', ...
{ 'icatype' g.icatype 'options' g.options 'chanind' g.chanind } );
return;
elseif length(ALLEEG) > 1 && strcmpi(g.concatcond, 'on')
allsubjects = { ALLEEG.subject };
allsessions = { ALLEEG.session };
alltags = zeros(1,length(allsubjects));
if any(cellfun('isempty', allsubjects))
errordlg2( [ 'Aborting: Subject names missing from at least one dataset file.' 10 ...
'Subject names must be stored within the datasets. To do so,' 10 ...
'use the STUDY > Edit STUDY Info menu and check the box' 10 ...
'"Dataset info (condition, group, ...) differs from study info..."' ]);
return
end
dats = {};
for index = 1:length(allsubjects)
if ~alltags(index)
allinds = strmatch(allsubjects{index}, allsubjects, 'exact');
rmind = [];
% if we have different sessions they will not be concatenated
for tmpi = setdiff_bc(allinds,index)'
if ~isequal(allsessions(index), allsessions(tmpi))
rmind = [rmind tmpi];
end
end
allinds = setdiff_bc(allinds, rmind);
fprintf('Found %d datasets for subject ''%s'' session %d\n', length(allinds), allsubjects{index}, allsessions{index});
dats = { dats{:} allinds };
alltags(allinds) = 1;
end
end
fprintf('**************************\nNOW RUNNING ALL DECOMPOSITIONS\n****************************\n');
eeglab_options;
if option_parallel
for index = 1:length(dats)
TMPEEG{index} = ALLEEG(dats{index});
end
parfor index = 1:length(dats)
TMPEEG{index} = pop_runica(TMPEEG{index}, 'icatype', g.icatype, ...
'options', g.options, 'chanind', g.chanind, 'concatenate', 'on');
end
for index = 1:length(dats)
ALLEEG(dats{index}) = TMPEEG{index};
for idat = 1:length(dats{index})
ALLEEG(dats{index}(idat)).saved = 'no';
pop_saveset(ALLEEG(dats{index}(idat)), 'savemode', 'resave');
ALLEEG(dats{index}(idat)).saved = 'yes';
end
end
else
for index = 1:length(dats)
ALLEEG(dats{index}) = pop_runica(ALLEEG(dats{index}), 'icatype', g.icatype, ...
'options', g.options, 'chanind', g.chanind, 'concatenate', 'on');
for idat = 1:length(dats{index})
ALLEEG(dats{index}(idat)).saved = 'no';
pop_saveset(ALLEEG(dats{index}(idat)), 'savemode', 'resave');
ALLEEG(dats{index}(idat)).saved = 'yes';
ALLEEG(dats{index}(idat)) = update_datafield(ALLEEG(dats{index}(idat)));
end
end
end
com = sprintf('EEG = pop_runica(EEG, %s);', ...
vararg2str({ 'icatype' g.icatype 'concatcond' 'on' 'options' g.options }) );
return;
else
% copy data
% ---------
if all([ALLEEG(g.dataset).trials] == 1)
EEG = eeg_checkset(ALLEEG, 'loaddata');
EEG = pop_mergeset(EEG, 1:length(EEG));
else
disp('Concatenating datasets...');
EEG = ALLEEG(g.dataset(1));
cpnts = 1;
% compute total data size
% -----------------------
totalpnts = 0;
for i = g.dataset
totalpnts = totalpnts+ALLEEG(g.dataset(i)).pnts*ALLEEG(g.dataset(i)).trials;
end
EEG.data = zeros(EEG.nbchan, totalpnts);
for i = g.dataset
tmplen = ALLEEG(g.dataset(i)).pnts*ALLEEG(g.dataset(i)).trials;
TMP = eeg_checkset(ALLEEG(g.dataset(i)), 'loaddata');
EEG.data(:,cpnts:cpnts+tmplen-1) = reshape(TMP.data, size(TMP.data,1), size(TMP.data,2)*size(TMP.data,3));
cpnts = cpnts+tmplen;
end
end
EEG.icaweights = [];
EEG.trials = 1;
EEG.pnts = size(EEG.data,2);
EEG.saved = 'no';
if isfield(EEG.etc, 'ic_classification')
EEG.etc = rmfield(EEG.etc, 'ic_classification');
end
end
% Store and then remove current EEG ICA weights and sphere
% ---------------------------------------------------
fprintf('\n');
if ~isempty(EEG.icaweights)
fprintf('Saving current ICA decomposition in "EEG.etc.oldicaweights" (etc.).\n');
if ~isfield(EEG,'etc'), EEG.etc = []; end
if ~isfield(EEG.etc,'oldicaweights')
EEG.etc.oldicaweights = {};
EEG.etc.oldicasphere = {};
EEG.etc.oldicachansind = {};
end
tmpoldicaweights = EEG.etc.oldicaweights;
tmpoldicasphere = EEG.etc.oldicasphere;
tmpoldicachansind = EEG.etc.oldicachansind;
EEG.etc.oldicaweights = { EEG.icaweights tmpoldicaweights{:} };
EEG.etc.oldicasphere = { EEG.icasphere tmpoldicasphere{:} };
EEG.etc.oldicachansind = { EEG.icachansind tmpoldicachansind{:} };
fprintf(' Decomposition saved as entry %d.\n',length(EEG.etc.oldicaweights));
end
EEG.icaweights = [];
EEG.icasphere = [];
EEG.icawinv = [];
EEG.icaact = [];
% select sub_channels
% -------------------
if isempty(g.chanind)
g.chanind = 1:EEG.nbchan;
end
if iscell(g.chanind)
datatype = {EEG.chanlocs.type};
tmpChanInd = [];
for iChan = 1:length(datatype)
if ~isempty(datatype{iChan}) && ~isempty(strmatch(datatype{iChan}, g.chanind))
tmpChanInd = [ tmpChanInd iChan ];
end
end
g.chanind = tmpChanInd;
end
EEG.icachansind = g.chanind;
% is pca already an option?
% -------------------------
pca_opt = 0;
for i = 1:length(g.options)
if ischar(g.options{i})
if strcmpi(g.options{i}, 'pca')
pca_opt = 1;
pca_ind = i;
end
end
end
if pca_opt
fprintf([ 'Warning: you have used PCA to reduce dimensionality so ICA\n' ...
' is not modeling the entire data, only the PCA-reduced data.\n' ]);
end
%------------------------------
% compute ICA on a definite set
% -----------------------------
tmpdata = reshape( EEG.data(g.chanind,:,:), length(g.chanind), EEG.pnts*EEG.trials);
tmprank = getrank(double(tmpdata(:,1:min(3000, size(tmpdata,2)))), pca_opt);
tmpdata = tmpdata - repmat(mean(tmpdata,2), [1 size(tmpdata,2)]); % zero mean
if ~strcmpi(g.icatype, 'binica')
try
disp('Attempting to convert data matrix to double precision for more accurate ICA results.')
tmpdata = double(tmpdata);
tmpdata = tmpdata - repmat(mean(tmpdata,2), [1 size(tmpdata,2)]); % zero mean (more precise than single precision)
catch
disp('*************************************************************')
disp('Not enough memory to convert data matrix to double precision.')
disp('All computations will be done in single precision. Matlab 7.x')
disp('under 64-bit Linux and others is imprecise in this mode.')
disp('We advise use of "binica" instead of "runica."')
disp('*************************************************************')
end
end
switch lower(g.icatype)
case 'runica'
% make sure we are using the correct ICA function
runicaCurrentLoc = fileparts(which('runica'));
runicaDesiredLoc = fullfile(fileparts(fileparts(which('pop_runica'))), 'sigprocfunc');
if ~isequal(runicaCurrentLoc, runicaDesiredLoc)
addpath(runicaDesiredLoc); % put back to the beginning of the path
end
try
if ismatlab && nargin < 2, g.options = { g.options{:}, 'interrupt', 'on' }; end
catch, end
if tmprank == size(tmpdata,1) || pca_opt
[EEG.icaweights,EEG.icasphere] = runica( tmpdata, 'lrate', 0.001, g.options{:} );
else
if nargin < 2
uilist = { { 'style' 'text' 'string' [ 'EEGLAB has detected that the rank of your data matrix' 10 ...
'is lower the number of input data channels. This might' 10 ...
'be because you are including a reference channel or' 10 ...
'because you are running a second ICA decomposition.' 10 ...
sprintf('The proposed dimension for ICA is %d (out of %d channels).', tmprank, size(tmpdata,1)) 10 ...
'Rank computation may be inaccurate so you may edit this' 10 ...
'number below. If you do not understand, simply press OK.' ] } { } ...
{ 'style' 'text' 'string' 'Proposed rank:' } ...
{ 'style' 'edit' 'string' num2str(tmprank) } };
res = inputgui('uilist', uilist, 'geometry', { [1] [1] [1 1] }, 'geomvert', [6 1 1]);
if isempty(res), return; end
tmprank = str2num(res{1});
g.options = [g.options { 'pca' tmprank }];
else
g.options = [g.options {'pca' tmprank }]; % automatic for STUDY (batch processing)
end
disp(['Data rank (' int2str(tmprank) ') is smaller than the number of channels (' int2str(size(tmpdata,1)) ').']);
[EEG.icaweights,EEG.icasphere] = runica( tmpdata, 'lrate', 0.001, g.options{:} );
end
case 'binica'
icadefs;
fprintf(['Warning: If the binary ICA function does not work, check that you have added the\n' ...
'binary file location (in the EEGLAB directory) to your Unix /bin directory (.cshrc file)\n']);
if exist(ICABINARY) ~= 2
error('Pop_runica(): binary ICA executable not found. Edit icadefs.m file to specify the ICABINARY location');
end
tmprank = getrank(tmpdata(:,1:min(3000, size(tmpdata,2))));
if tmprank == size(tmpdata,1) || pca_opt
[EEG.icaweights,EEG.icasphere] = binica( tmpdata, 'lrate', 0.001, g.options{:} );
else
disp(['Data rank (' int2str(tmprank) ') is smaller than the number of channels (' int2str(size(tmpdata,1)) ').']);
[EEG.icaweights,EEG.icasphere] = binica( tmpdata, 'lrate', 0.001, 'pca', tmprank, g.options{:} );
end
case 'amica'
if ~exist('pop_runamica')
if nargin < 2
errordlg2('You must install the AMICA plugin first to use AMICA');
else
error('You must install the AMICA plugin first to use AMICA');
end
end
EEG = pop_runamica(EEG, g.options{:});
case 'picard'
if ~exist('picard')
if nargin < 2
errordlg2('You must install the picard plugin first to use Picard');
else
error('You must install the picard plugin first to use Picard');
end
end
options2 = g.options;
if pca_opt
if g.options{pca_ind+1} < 0
fprintf('Decreasing data dimension from %d to %d using PCA...\n', size(tmpdata,1), size(tmpdata,1)+g.options{pca_ind+1});
[tmpdata,eigvec] = runpca(tmpdata, size(tmpdata,1)+g.options{pca_ind+1});
else
fprintf('Decreasing data dimension from %d to %d using PCA...\n', size(tmpdata,1), g.options{pca_ind+1});
[tmpdata,eigvec] = runpca(tmpdata, g.options{pca_ind+1});
end
options2(pca_ind:pca_ind+1) = [];
end
[~, EEG.icaweights] = picard( tmpdata, 'verbose', true, options2{:});
if pca_opt
EEG.icaweights = EEG.icaweights*pinv(eigvec);
end
case 'pearson_ica'
if isempty(g.options)
disp('Warning: EEGLAB default for pearson ICA is 1000 iterations and epsilon=0.0005');
[~, EEG.icaweights] = pearson_ica( tmpdata, 'maxNumIterations', 1000,'epsilon',0.0005);
else
[~, EEG.icaweights] = pearson_ica( tmpdata, g.options{:});
end
case 'egld_ica', disp('Warning: This algorithm is very slow!!!');
[~, EEG.icaweights] = egld_ica( tmpdata, g.options{:} );
case 'tfbss'
if isempty(g.options)
[~, EEG.icaweights] = tfbss( tmpdata, size(tmpdata,1), 8, 512 );
else
[~, EEG.icaweights] = tfbss( tmpdata, g.options{:} );
end
case 'jader', [EEG.icaweights] = jader( tmpdata, g.options{:} );
case 'matlabshibbsr', [EEG.icaweights] = MatlabshibbsR( tmpdata, g.options{:} );
case 'eea', [EEG.icaweights] = eeA( tmpdata, g.options{:} );
case 'icaml', [~, EEG.icawinv] = icaML( tmpdata, g.options{:} );
case 'icams', [~, EEG.icawinv] = icaMS( tmpdata, g.options{:} );
case 'fastica', [~, EEG.icawinv, EEG.icaweights] = fastica( tmpdata, 'displayMode', 'off', g.options{:} );
case { 'tica' 'erica' 'simbec' 'unica' 'amuse' 'fobi' 'evd' 'sons' ...
'jadeop' 'jade_td_p' 'evd24' 'sobi' 'ng_ol' 'acsobiro' 'acrsobibpf' }
fig = figure('tag', 'alg_is_run', 'visible', 'off');
if isempty(g.options), g.options = { size(tmpdata,1) }; end
switch lower(g.icatype)
case 'tica', EEG.icaweights = tica( tmpdata, g.options{:} );
case 'erica', EEG.icaweights = erica( tmpdata, g.options{:} );
case 'simbec', EEG.icaweights = simbec( tmpdata, g.options{:} );
case 'unica', EEG.icaweights = unica( tmpdata, g.options{:} );
case 'amuse', EEG.icaweights = amuse( tmpdata );
case 'fobi', [~, EEG.icaweights] = fobi( tmpdata, g.options{:} );
case 'evd', EEG.icaweights = evd( tmpdata, g.options{:} );
case 'sons', EEG.icaweights = sons( tmpdata, g.options{:} );
case 'jadeop', EEG.icaweights = jadeop( tmpdata, g.options{:} );
case 'jade_td_p',EEG.icaweights = jade_td_p( tmpdata, g.options{:} );
case 'evd24', EEG.icaweights = evd24( tmpdata, g.options{:} );
case 'sobi', EEG.icawinv = sobi( tmpdata, g.options{:} );
case 'ng_ol', [~, EEG.icaweights] = ng_ol( tmpdata, g.options{:} );
case 'acsobiro', EEG.icawinv = acsobiro( tmpdata, g.options{:} );
case 'acrsobibpf', EEG.icawinv = acrsobibpf( tmpdata, g.options{:} );
end
clear tmp;
close(fig);
otherwise, error('Pop_runica: unrecognized algorithm');
end
% update weight and inverse matrices etc...
% -----------------------------------------
if ~isempty(fig), try close(fig); catch, end; end
if isempty(EEG.icaweights)
EEG.icaweights = pinv(EEG.icawinv);
end
if isempty(EEG.icasphere)
EEG.icasphere = eye(size(EEG.icaweights,2));
end
if isempty(EEG.icawinv)
EEG.icawinv = pinv(EEG.icaweights*EEG.icasphere); % a priori same result as inv
end
% Reorder components by variance
% ------------------------------
meanvar = sum(EEG.icawinv.^2).*sum(transpose((EEG.icaweights * EEG.icasphere)*EEG.data(EEG.icachansind,:)).^2)/((length(EEG.icachansind)*EEG.pnts)-1);
[~, windex] = sort(meanvar);
windex = windex(end:-1:1); % order large to small
meanvar = meanvar(windex);
EEG.icaweights = EEG.icaweights(windex,:);
EEG.icawinv = pinv( EEG.icaweights * EEG.icasphere );
if ~isempty(EEG.icaact)
EEG.icaact = EEG.icaact(windex,:,:);
end
% copy back data to datasets if necessary
% ---------------------------------------
if length(g.dataset) > 1
for i = g.dataset
ALLEEG(i).icaweights = EEG.icaweights;
ALLEEG(i).icasphere = EEG.icasphere;
ALLEEG(i).icawinv = EEG.icawinv;
ALLEEG(i).icachansind = g.chanind;
end
ALLEEG = eeg_checkset(ALLEEG);
else
EEG = eeg_checkset(EEG);
ALLEEG = eeg_store(ALLEEG, EEG, g.dataset);
end
if nargin < 2 || selectamica
if ~isempty(g.options)
com = sprintf('EEG = pop_runica(EEG, ''icatype'', ''%s'', %s);', g.icatype, vararg2str(g.options) ); %vararg2str({ 'icatype' g.icatype 'dataset' g.dataset 'options' g.options }) );
else
com = sprintf('EEG = pop_runica(EEG, ''icatype'', ''%s'');',g.icatype );
end
end
return;
function tmprank2 = getrank(tmpdata, pca_opt)
tmprank = rank(tmpdata);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Here: alternate computation of the rank by Sven Hoffman
%tmprank = rank(tmpdata(:,1:min(3000, size(tmpdata,2)))); old code
covarianceMatrix = cov(tmpdata', 1);
[~, D] = eig (covarianceMatrix);
rankTolerance = 1e-7;
tmprank2=sum (diag (D) > rankTolerance);
if tmprank ~= tmprank2
if nargin >= 2 && pca_opt ~= 0
fprintf('Warning: fixing rank computation inconsistency (%d vs %d) most likely because running under Linux 64-bit Matlab, taking the minimum\n', tmprank, tmprank2);
end
tmprank2 = min(tmprank, tmprank2);
end
function EEG = update_datafield(EEG)
if ~isfield(EEG, 'datfile'), EEG.datfile = ''; end
if ~isempty(EEG.datfile)
EEG.data = EEG.datfile;
else
EEG.data = 'in set file';
end
EEG.icaact = [];